[
  {
    "path": ".bazelrc",
    "content": "# Copied from https://github.com/tensorflow/serving/blob/master/.bazelrc\n# Some entries are commented to fit to ByteDance environment\n\n# Options used to build with CUDA.\nbuild:cuda --repo_env TF_NEED_CUDA=1\nbuild:cuda --crosstool_top=@local_config_cuda//crosstool:toolchain\nbuild:cuda --define=using_cuda=true --define=using_cuda_nvcc=true\n# build:cuda --action_env=TF_CUDA_COMPUTE_CAPABILITIES=\"sm_50,sm_60,sm_70,sm_75,compute_80\"\n# Just compile for V100 and T4 and A100 for development:\nbuild:cuda --action_env=TF_CUDA_COMPUTE_CAPABILITIES=\"sm_70,sm_75,compute_80\"\n\n# Explicitly specify \"local\" here to avoid sandboxed for local.\n# Use ./configure to create .monolith_configure.bazelrc to enable build from remote buildfarm.\nbuild --spawn_strategy=local\n\nbuild --define=grpc_no_ares=true\n\n# Sets the default Apple platform to macOS.\nbuild --apple_platform_type=macos\n\nbuild -c opt\n\n# LLVM, MLIR and TF require C++14.\nbuild --cxxopt=-std=c++14\nbuild --host_cxxopt=-std=c++14\n\n# preventing relocation overflow error\nbuild:dbg --copt=-gsplit-dwarf\n\n# dbg config, copied from tensorflow v2.4.0\nbuild:dbg -c dbg\n# for now, disable arm_neon. see: https://github.com/tensorflow/tensorflow/issues/33360\nbuild:dbg --cxxopt -DTF_LITE_DISABLE_X86_NEON\n# AWS SDK must be compiled in release mode. see: https://github.com/tensorflow/tensorflow/issues/37498\nbuild:dbg --copt -DDEBUG_BUILD\n\n# Adding \"--cxxopt=-D_GLIBCXX_USE_CXX11_ABI=0\" creates parity with TF\n# compilation options. It also addresses memory use due to\n# copy-on-write semantics of std::strings of the older ABI.\nbuild --cxxopt=-D_GLIBCXX_USE_CXX11_ABI=0\n\nbuild --experimental_repo_remote_exec\n### end ###\n\nfetch --experimental_repo_remote_exec\nquery --experimental_repo_remote_exec\nbuild --genrule_strategy=local\n\n# Make it default to TF2\nbuild --define=tf_api_version=2\nbuild --action_env=TF2_BEHAVIOR=1\n\n# Horovod requires dynamic load on shared object.\nbuild --define=framework_shared_object=true\n\n# Some optimization config.\nbuild --define=open_source_build=true\nbuild --define=use_fast_cpp_protos=true\nbuild --define=allow_oversize_protos=true\nbuild --define=with_xla_support=true\n\n# Some native optimizations\nbuild --copt=-O3\nbuild --copt=-mavx \nbuild --copt=-mavx2 \nbuild --copt=-mfma \nbuild --copt=-msse4.1 \nbuild --copt=-msse4.2\n\n# TF currently relies on some bazel deprecated behavior, removes theses options\n# once TF fixes bugs.\nbuild --noincompatible_remove_legacy_whole_archive --noincompatible_prohibit_aapt1\n\n# Import user configured options (e.g., like building cluster)\ntry-import %workspace%/.monolith_configure.bazelrc\n# Import bazel 4 compatible options\ntry-import %workspace%/.bazel4-compatible.bazelrc\n\n"
  },
  {
    "path": ".gitignore",
    "content": "# Ignore .DS_Store\n.DS_Store\nfid_mapping/.DS_Store\nfid_analysis/.DS_Store\n\n# Ignore some test data files\nfid_mapping/test_data.gz\nfid_mapping/downloaded/*\nfid_analysis/downloaded/*\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\n.env\nvenv/\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\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.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Bazel binary folders\n/bazel-*\n/.vscode\n\n# Jupyter noteobok\n.ipynb_checkpoints\n\n# go\nvendor/\n\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2022 ByteDance and/or its affiliates \n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. 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\n2. 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\n3. 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\n4. 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\n5. 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\n6. 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\n7. 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\n8. 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\n9. 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\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: 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\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n======\nMonolith project incorporates components or files from the following third project：\n===\nalibaba/x-deeplearning (https://github.com/alibaba/x-deeplearning)\nSPDX-License-Identifier: Apache-2.0\n===\nApache ZooKeeper\nCopyright: Copyright 2009-2022 The Apache Software Foundation\nSPDX-License-Identifier: Apache-2.0\n===\napache/singa\nCopyright: Copyright 2017 The Apache Software Foundation\nSPDX-License-Identifier: Apache-2.0\n===\nFolly (https://github.com/facebook/folly)\nSPDX-License-Identifier: Apache-2.0\n===\nLibcuckoo\nCopyright: \nSPDX-License-Identifier: Apache-2.0\n===\nLingvo\nCopyright: \nSPDX-License-Identifier: Apache-2.0\n===\nTensorFlow\nCopyright: \nSPDX-License-Identifier: Apache-2.0\n===\ntiny-dnn\nCopyright: \nAll contributions by Taiga Nomi\nCopyright (c) 2013, Taiga Nomi\nAll rights reserved.\nAll other contributions:\nCopyright (c) 2013-2016, the respective contributors.\nAll rights reserved.\nEach contributor holds copyright over their respective contributions.\nThe project versioning (Git) records all such contribution source information.\nSPDX-License-Identifier: BSD 3-Clause\n===\nbrotli \nCopyright (c) 2009, 2010, 2013-2016 by the Brotli Authors.\nSPDX-License-Identifier: MIT\n===\n"
  },
  {
    "path": "README.md",
    "content": " Monolith\n\n## What is it?\n\n[Monolith](https://arxiv.org/abs/2209.07663) is a deep learning framework for large scale recommendation modeling. It introduces two important features which are crucial for advanced recommendation system: \n* collisionless embedding tables guarantees unique represeantion for different id features\n* real time training captures the latest hotspots and help users to discover new intersts rapidly\n\nMonolith is built on the top of TensorFlow and supports batch/real-time training and serving.\n\n\n## Discussion Group\n\n### Join us at Discord\n\nhttps://discord.gg/QYTDeKxGMX\n\n## Quick start\n\n### Build from source\n\nCurrently, we only support compilation on the Linux.\n\nFirst, download bazel 3.1.0\n```bash\nwget https://github.com/bazelbuild/bazel/releases/download/3.1.0/bazel-3.1.0-installer-linux-x86_64.sh && \\\n  chmod +x bazel-3.1.0-installer-linux-x86_64.sh && \\\n  ./bazel-3.1.0-installer-linux-x86_64.sh && \\\n  rm bazel-3.1.0-installer-linux-x86_64.sh\n```\n\nThen, prepare a python environment\n```bash\npip install -U --user pip numpy wheel packaging requests opt_einsum\npip install -U --user keras_preprocessing --no-deps\n```\n\nFinally, you can build any target in the monolith.\nFor example,\n```bash\nbazel run //monolith/native_training:demo --output_filter=IGNORE_LOGS\n```\n\n### Demo and tutorials\n\nThere are a tutorial in [markdown/demo](markdown/demo) on how to run distributed async training, and few guides on how to use the `MonolithModel` API [here](markdown).  "
  },
  {
    "path": "WORKSPACE",
    "content": "load(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n    name = \"rules_python\",\n    sha256 = \"b6d46438523a3ec0f3cead544190ee13223a52f6a6765a29eae7b7cc24cc83a0\",\n    url = \"https://github.com/bazelbuild/rules_python/releases/download/0.1.0/rules_python-0.1.0.tar.gz\",\n)\n\nhttp_archive(\n    name = \"rules_foreign_cc\",\n    sha256 = \"c2cdcf55ffaf49366725639e45dedd449b8c3fe22b54e31625eb80ce3a240f1e\",\n    strip_prefix = \"rules_foreign_cc-0.1.0\",\n    url = \"https://github.com/bazelbuild/rules_foreign_cc/archive/0.1.0.zip\",\n)\n\nload(\"@rules_foreign_cc//:workspace_definitions.bzl\", \"rules_foreign_cc_dependencies\")\n\n# This sets up some common toolchains for building targets. For more details, please see\n# https://bazelbuild.github.io/rules_foreign_cc/0.1.0/#rules_foreign_cc_dependencies\nrules_foreign_cc_dependencies()\n\nload(\"//monolith:monolith_workspace.bzl\", \"monolith_workspace\")\n\nmonolith_workspace()\n\nload(\"@bazel_tools//tools/build_defs/repo:git.bzl\", \"git_repository\")\n\n# This is an unofficial boost build but it is useful.\ngit_repository(\n    name = \"com_github_nelhage_rules_boost\",\n    commit = \"1e3a69bf2d5cd10c34b74f066054cd335d033d71\",\n    remote = \"https://github.com/nelhage/rules_boost\",\n    shallow_since = \"1591047380 -0700\",\n)\n\nload(\"@com_github_nelhage_rules_boost//:boost/boost.bzl\", \"boost_deps\")\n\nboost_deps()\n\nhttp_archive(\n    name = \"org_tensorflow_serving\",\n    patch_args = [\"-p1\"],\n    patches = [\n        \"//third_party:org_tensorflow_serving/public_tf_serving.patch\",\n        \"//third_party:org_tensorflow_serving/support_diff_dim_size_inputs.patch\",\n    ],\n    sha256 = \"8c1a4d31ec7ab041b9302348a01422e21349507c7a6f0974639386c8901b721b\",\n    strip_prefix = \"serving-2.4.0\",\n    url = \"https://github.com/tensorflow/serving/archive/2.4.0.tar.gz\",\n)\n\n# To update TensorFlow to a new revision.\n# 1. Update the 'git_commit' args below to include the new git hash.\n# 2. Get the sha256 hash of the archive with a command such as...\n#    curl -L https://github.com/tensorflow/tensorflow/archive/<git hash>.tar.gz | sha256sum\n#    and update the 'sha256' arg with the result.\n# 3. Request the new archive to be mirrored on mirror.bazel.build for more\n#    reliable downloads.\nload(\"@org_tensorflow_serving//tensorflow_serving:repo.bzl\", \"tensorflow_http_archive\")\n\n# Tensorflow 2.4.0\ntensorflow_http_archive(\n    name = \"org_tensorflow\",\n    git_commit = \"582c8d236cb079023657287c318ff26adb239002\",\n    patch = \"//third_party:org_tensorflow/tf.patch\",\n    sha256 = \"9c94bfec7214853750c7cacebd079348046f246ec0174d01cd36eda375117628\",\n)\n\nhttp_archive(\n    name = \"rules_pkg\",\n    sha256 = \"352c090cc3d3f9a6b4e676cf42a6047c16824959b438895a76c2989c6d7c246a\",\n    url = \"https://github.com/bazelbuild/rules_pkg/releases/download/0.2.5/rules_pkg-0.2.5.tar.gz\",\n)\n\nload(\"@rules_pkg//:deps.bzl\", \"rules_pkg_dependencies\")\n\nrules_pkg_dependencies()\n\nload(\n    \"@org_tensorflow//third_party/toolchains/preconfig/generate:archives.bzl\",\n    \"bazel_toolchains_archive\",\n)\n\nbazel_toolchains_archive()\n\nload(\n    \"@bazel_toolchains//repositories:repositories.bzl\",\n    bazel_toolchains_repositories = \"repositories\",\n)\n\nbazel_toolchains_repositories()\n\n# START: Upstream TensorFlow dependencies\n# TensorFlow build depends on these dependencies.\n# Needs to be in-sync with TensorFlow sources.\nhttp_archive(\n    name = \"io_bazel_rules_closure\",\n    sha256 = \"5b00383d08dd71f28503736db0500b6fb4dda47489ff5fc6bed42557c07c6ba9\",\n    strip_prefix = \"rules_closure-308b05b2419edb5c8ee0471b67a40403df940149\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/bazelbuild/rules_closure/archive/308b05b2419edb5c8ee0471b67a40403df940149.tar.gz\",\n        \"https://github.com/bazelbuild/rules_closure/archive/308b05b2419edb5c8ee0471b67a40403df940149.tar.gz\",  # 2019-06-13\n    ],\n)\n\nhttp_archive(\n    name = \"bazel_skylib\",\n    sha256 = \"1dde365491125a3db70731e25658dfdd3bc5dbdfd11b840b3e987ecf043c7ca0\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/bazelbuild/bazel-skylib/releases/download/0.9.0/bazel_skylib-0.9.0.tar.gz\",\n        \"https://github.com/bazelbuild/bazel-skylib/releases/download/0.9.0/bazel_skylib-0.9.0.tar.gz\",\n    ],\n)  # https://github.com/bazelbuild/bazel-skylib/releases\n\n# END: Upstream TensorFlow dependencies\n\n# Please add all new TensorFlow Serving and Archon dependencies in workspace.bzl.\nload(\"//monolith:tf_serving_workspace.bzl\", \"tf_serving_workspace\")\n\ntf_serving_workspace()\n\n\nload(\"@com_google_protobuf//:protobuf_deps.bzl\", \"protobuf_deps\")\n\nprotobuf_deps()\n\n# Specify the minimum required bazel version.\nload(\"@org_tensorflow//tensorflow:version_check.bzl\", \"check_bazel_version_at_least\")\n\ncheck_bazel_version_at_least(\"3.0.0\")\n\n# GPRC deps, required to match TF's.  Only after calling tf_serving_workspace()\nload(\"@com_github_grpc_grpc//bazel:grpc_deps.bzl\", \"grpc_deps\")\n\ngrpc_deps()\n\nhttp_archive(\n    name = \"upb\",\n    patch_args = [\"-p1\"],\n    patches = [\"//third_party:upb.patch\"],\n    sha256 = \"61d0417abd60e65ed589c9deee7c124fe76a4106831f6ad39464e1525cef1454\",\n    strip_prefix = \"upb-9effcbcb27f0a665f9f345030188c0b291e32482\",\n    url = \"https://github.com/protocolbuffers/upb/archive/9effcbcb27f0a665f9f345030188c0b291e32482.tar.gz\",\n)\n\nload(\"@upb//bazel:repository_defs.bzl\", \"bazel_version_repository\")\n\nbazel_version_repository(name = \"bazel_version\")\n\n# Hedron's Compile Commands Extractor for Bazel\n# https://github.com/hedronvision/bazel-compile-commands-extractor\nhttp_archive(\n    name = \"hedron_compile_commands\",\n    strip_prefix = \"bazel-compile-commands-extractor-79f8dcae6b451abb97fe76853c867792ac9ac703\",\n\n    # Replace the commit hash in both places (below) with the latest, rather than using the stale one here.\n    # Even better, set up Renovate and let it do the work for you (see \"Suggestion: Updates\" in the README).\n    url = \"https://github.com/hedronvision/bazel-compile-commands-extractor/archive/79f8dcae6b451abb97fe76853c867792ac9ac703.tar.gz\",\n    # When you first run this tool, it'll recommend a sha256 hash to put here with a message like: \"DEBUG: Rule 'hedron_compile_commands' indicated that a canonical reproducible form can be obtained by modifying arguments sha256 = ...\"\n)\n\nload(\"@hedron_compile_commands//:workspace_setup.bzl\", \"hedron_compile_commands_setup\")\n\nhedron_compile_commands_setup()\n\n\nhttp_archive(\n    name = \"zstd\",\n    build_file = \"//third_party:zstd.BUILD\",\n    sha256 = \"a364f5162c7d1a455cc915e8e3cf5f4bd8b75d09bc0f53965b0c9ca1383c52c8\",\n    strip_prefix = \"zstd-1.4.4\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/facebook/zstd/archive/v1.4.4.tar.gz\",\n        \"https://github.com/facebook/zstd/archive/v1.4.4.tar.gz\",\n    ],\n)\n\nhttp_archive(\n    name = \"lz4\",\n    build_file = \"//third_party:lz4.BUILD\",\n    sha256 = \"658ba6191fa44c92280d4aa2c271b0f4fbc0e34d249578dd05e50e76d0e5efcc\",\n    strip_prefix = \"lz4-1.9.2\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/lz4/lz4/archive/v1.9.2.tar.gz\",\n        \"https://github.com/lz4/lz4/archive/v1.9.2.tar.gz\",\n    ],\n)\n\nhttp_archive(\n    name = \"kafka\",\n    build_file = \"//third_party:kafka.BUILD\",\n    patch_cmds = [\n        \"rm -f src/win32_config.h\",\n        # TODO: Remove the fowllowing once librdkafka issue is resolved.\n        \"\"\"sed -i.bak '\\\\|rd_kafka_log(rk,|,/ exceeded);/ s/^/\\\\/\\\\//' src/rdkafka_cgrp.c\"\"\",\n    ],\n    sha256 = \"f7fee59fdbf1286ec23ef0b35b2dfb41031c8727c90ced6435b8cf576f23a656\",\n    strip_prefix = \"librdkafka-1.5.0\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/edenhill/librdkafka/archive/v1.5.0.tar.gz\",\n        \"https://github.com/edenhill/librdkafka/archive/v1.5.0.tar.gz\",\n    ],\n)\n\nload(\"//third_party:repo.bzl\", \"tf_http_archive\")\n\ntf_http_archive(\n    name = \"cuCollections\",\n    build_file = \"//third_party:cuco.BUILD\",\n    patch_file = \"//third_party:cuCollections.patch\",\n    sha256 = \"2e059ea1ae18173c5cc3f00989b114c431af78c674f92e35bed56367a9b8b186\",\n    strip_prefix = \"cuCollections-1e3c5842c6e212e0bd7de9802af583e53009f4a6\",\n    urls = [\n        \"https://github.com/NVIDIA/cuCollections/archive/1e3c5842c6e212e0bd7de9802af583e53009f4a6.zip\",\n        \"https://github.com/NVIDIA/cuCollections/archive/1e3c5842c6e212e0bd7de9802af583e53009f4a6.zip\",\n    ],\n)\n\nhttp_archive(\n    name = \"arrow\",\n    build_file = \"//third_party:arrow.BUILD\",\n    sha256 = \"57e13c62f27b710e1de54fd30faed612aefa22aa41fa2c0c3bacd204dd18a8f3\",\n    strip_prefix = \"arrow-apache-arrow-7.0.0\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/arrow/archive/apache-arrow-7.0.0.tar.gz\",\n        \"https://github.com/apache/arrow/archive/apache-arrow-7.0.0.tar.gz\",\n    ],\n)\n\n# extra dependencies of arrow begin\nhttp_archive(\n    name = \"rapidjson\",\n    build_file = \"//third_party:rapidjson.BUILD\",\n    sha256 = \"30bd2c428216e50400d493b38ca33a25efb1dd65f79dfc614ab0c957a3ac2c28\",\n    strip_prefix = \"rapidjson-418331e99f859f00bdc8306f69eba67e8693c55e\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/miloyip/rapidjson/archive/418331e99f859f00bdc8306f69eba67e8693c55e.tar.gz\",\n        \"https://github.com/miloyip/rapidjson/archive/418331e99f859f00bdc8306f69eba67e8693c55e.tar.gz\",\n    ],\n)\n\nhttp_archive(\n    name = \"xsimd\",\n    build_file = \"//third_party:xsimd.BUILD\",\n    sha256 = \"21b4700e9ef70f6c9a86952047efd8272317df4e6fee35963de9394fd9c5677f\",\n    strip_prefix = \"xsimd-8.0.1\",\n    urls = [\n        \"https://github.com/xtensor-stack/xsimd/archive/refs/tags/8.0.1.tar.gz\",\n    ],\n)\n\nhttp_archive(\n    name = \"brotli\",\n    build_file = \"//third_party:brotli.BUILD\",\n    sha256 = \"4c61bfb0faca87219ea587326c467b95acb25555b53d1a421ffa3c8a9296ee2c\",\n    strip_prefix = \"brotli-1.0.7\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/google/brotli/archive/v1.0.7.tar.gz\",\n        \"https://github.com/google/brotli/archive/v1.0.7.tar.gz\",\n    ],\n)\n\nhttp_archive(\n    name = \"bzip2\",\n    build_file = \"//third_party:bzip2.BUILD\",\n    sha256 = \"ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269\",\n    strip_prefix = \"bzip2-1.0.8\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz\",\n        \"https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz\",\n    ],\n)\n\nhttp_archive(\n    name = \"thrift\",\n    build_file = \"//third_party:thrift.BUILD\",\n    sha256 = \"5da60088e60984f4f0801deeea628d193c33cec621e78c8a43a5d8c4055f7ad9\",\n    strip_prefix = \"thrift-0.13.0\",\n    urls = [\n        \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/apache/thrift/archive/v0.13.0.tar.gz\",\n        \"https://github.com/apache/thrift/archive/v0.13.0.tar.gz\",\n    ],\n)\n\n# extra dependencies of arrow end\n"
  },
  {
    "path": "conf/BUILD",
    "content": "package(\n    default_visibility = [\"//visibility:public\"],\n)\nfilegroup(\n    name = \"serving\",\n    srcs = glob([\n        \"*.properties\",\n        \"*.conf\",\n        \"*.cfg\",\n    ]),\n)"
  },
  {
    "path": "deploy/.dockerignore",
    "content": "# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file\n# Ignore all files which are not go type\n!**/*.go\n!**/*.mod\n!**/*.sum\n"
  },
  {
    "path": "deploy/.gitignore",
    "content": "\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\nbin\ntestbin/*\n\n# Test binary, build with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Kubernetes Generated files - skip generated files, except for vendored files\n\n!vendor/**/zz_generated.*\n\n# editor and IDE paraphernalia\n.idea\n*.swp\n*.swo\n*~\n"
  },
  {
    "path": "deploy/.golangci.yaml",
    "content": "# This file contains all available configuration options\n# with their default values.\n\n# options for analysis running\nrun:\n  # default concurrency is a available CPU number\n  concurrency: 4\n\n  # timeout for analysis, e.g. 30s, 5m, default is 1m\n  timeout: 5m\n\n  # exit code when at least one issue was found, default is 1\n  issues-exit-code: 1\n\n  # include test files or not, default is true\n  tests: false\n\n  # list of build tags, all linters use it. Default is empty list.\n  build-tags: []\n\n  # which dirs to skip: issues from them won't be reported;\n  # can use regexp here: generated.*, regexp is applied on full path;\n  # default value is empty list, but default dirs are skipped independently\n  # from this option's value (see skip-dirs-use-default).\n  # \"/\" will be replaced by current OS file path separator to properly work\n  # on Windows.\n  skip-dirs:\n    - common/model/api\n    - test\n\n  # default is true. Enables skipping of directories:\n  #   vendor$, third_party$, testdata$, examples$, Godeps$, builtin$\n  skip-dirs-use-default: true\n\n  # which files to skip: they will be analyzed, but issues from them\n  # won't be reported. Default value is empty list, but there is\n  # no need to include all autogenerated files, we confidently recognize\n  # autogenerated files. If it's not please let us know.\n  # \"/\" will be replaced by current OS file path separator to properly work\n  # on Windows.\n  skip-files:\n    - \".*_gen.go\"\n    - \"k-*.go\"\n\n  # Allow multiple parallel golangci-lint instances running.\n  # If false (default) - golangci-lint acquires file lock on start.\n  allow-parallel-runners: false\n\n\n# output configuration options\noutput:\n  # colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions\n  # default is \"colored-line-number\"\n  format: colored-line-number\n\n  # print lines of code with issue, default is true\n  print-issued-lines: true\n\n  # print linter name in the end of issue text, default is true\n  print-linter-name: true\n\n  # make issues output unique by line, default is true\n  uniq-by-line: true\n\n  # add a prefix to the output file references; default is no prefix\n  path-prefix: \"\"\n\n  # sorts results by: filepath, line and column\n  sort-results: false\n\n\nlinters-settings:\n  dupl:\n    threshold: 100\n  funlen:\n    lines: 120\n    statements: 70\n  goconst:\n    min-len: 2\n    min-occurrences: 2\n  gocritic:\n    enabled-tags:\n      - diagnostic\n      - experimental\n      - opinionated\n      - performance\n      - style\n    disabled-checks:\n      - dupImport # https://github.com/go-critic/go-critic/issues/845\n      - ifElseChain\n      - octalLiteral\n      - whyNoLint\n      - wrapperFunc\n  gocognit:\n    min-complexity: 15\n  gomnd:\n    settings:\n      mnd:\n        # don't include the \"operation\" and \"assign\"\n        checks: [argument, case, condition, return]\n  govet:\n    check-shadowing: true\n  lll:\n    line-length: 120\n  misspell:\n    locale: US\n  nolintlint:\n    allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)\n    allow-unused: false # report any unused nolint directives\n    require-explanation: false # don't require an explanation for nolint directives\n\nlinters:\n  # please, do not use `enable-all`: it's deprecated and will be removed soon.\n  # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint\n  disable-all: true\n  enable:\n    - bodyclose\n    - deadcode\n    - dogsled\n    - dupl\n    - errcheck\n    - exportloopref\n    - exhaustive\n    - funlen\n    - gochecknoinits\n    - goconst\n    - gocritic\n    - gofmt\n    - goimports\n    - gomnd\n    - goprintffuncname\n    - gosec\n    - gosimple\n    - govet\n    - ineffassign\n    - lll\n    - misspell\n    - nakedret\n    - noctx\n    - nolintlint\n    - rowserrcheck\n    - staticcheck\n    - structcheck\n    - stylecheck\n    - typecheck\n    - unconvert\n    - unparam\n    - unused\n    - varcheck\n    - whitespace\n    - wrapcheck\n    - gocognit\n    - asciicheck\n    - nestif\n    - sqlclosecheck\n    - prealloc\n\n  # don't enable:\n  # - scopelint\n  # - gochecknoglobals\n  # - godot\n  # - godox\n  # - goerr113\n  # - golint\n  # - interfacer\n  # - maligned\n  # - testpackage\n  # - revive\n  # - wsl\n\nissues:\n  # Excluding configuration per-path, per-linter, per-text and per-source\n  exclude-rules:\n    - path: _test\\.go\n      linters:\n        - gomnd\n\n    # https://github.com/go-critic/go-critic/issues/926\n    - linters:\n        - gocritic\n      text: \"unnecessaryDefer:\"\n\n    - linters:\n        - stylecheck\n      text: \"ST1003:\"\n"
  },
  {
    "path": "deploy/Dockerfile",
    "content": "# Build the manager binary\nFROM golang:1.15 as builder\n\nWORKDIR /workspace\n# Copy the Go Modules manifests\nCOPY go.mod go.mod\nCOPY go.sum go.sum\n# cache deps before building and copying source so that we don't need to re-download as much\n# and so that source changes don't invalidate our downloaded layer\nRUN go mod download\n\n# Copy the go source\nCOPY main.go main.go\nCOPY api/ api/\nCOPY controllers/ controllers/\n\n# Build\nRUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go\n\n# Use distroless as minimal base image to package the manager binary\n# Refer to https://github.com/GoogleContainerTools/distroless for more details\nFROM gcr.io/distroless/static:nonroot\nWORKDIR /\nCOPY --from=builder /workspace/manager .\nUSER 65532:65532\n\nENTRYPOINT [\"/manager\"]\n"
  },
  {
    "path": "deploy/Makefile",
    "content": "\n# Image URL to use all building/pushing image targets\nREGISTRY ?= ml-platform-cn-guilin-boe.cr.volces.com/ml-platform\nNAME ?= data.monolith.controller-manager\n# TAG ?= $(shell git describe --always --dirty)\nTAG ?= b85906ce01ef40a75ba48779efdd4e3f\n\n# IMG ?= controller:latest\nIMG ?= ${REGISTRY}/${NAME}:${TAG}\n\n# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)\nCRD_OPTIONS ?= \"crd:trivialVersions=true,preserveUnknownFields=false,generateEmbeddedObjectMeta=true\"\n\n# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)\nifeq (,$(shell go env GOBIN))\nGOBIN=$(shell go env GOPATH)/bin\nelse\nGOBIN=$(shell go env GOBIN)\nendif\n\n# Setting SHELL to bash allows bash commands to be executed by recipes.\n# This is a requirement for 'setup-envtest.sh' in the test target.\n# Options are set to exit when a recipe line exits non-zero or a piped command fails.\nSHELL = /usr/bin/env bash -o pipefail\n.SHELLFLAGS = -ec\n\nall: build\n\n##@ General\n\n# The help target prints out all targets with their descriptions organized\n# beneath their categories. The categories are represented by '##@' and the\n# target descriptions by '##'. The awk commands is responsible for reading the\n# entire set of makefiles included in this invocation, looking for lines of the\n# file as xyz: ## something, and then pretty-format the target and help. Then,\n# if there's a line with ##@ something, that gets pretty-printed as a category.\n# More info on the usage of ANSI control characters for terminal formatting:\n# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters\n# More info on the awk command:\n# http://linuxcommand.org/lc3_adv_awk.php\n\nhelp: ## Display this help.\n\t@awk 'BEGIN {FS = \":.*##\"; printf \"\\nUsage:\\n  make \\033[36m<target>\\033[0m\\n\"} /^[a-zA-Z_0-9-]+:.*?##/ { printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2 } /^##@/ { printf \"\\n\\033[1m%s\\033[0m\\n\", substr($$0, 5) } ' $(MAKEFILE_LIST)\n\n##@ Development\n\nmanifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.\n\t$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths=\"./...\" output:crd:artifacts:config=config/crd/bases\n\ngenerate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.\n\t$(CONTROLLER_GEN) object:headerFile=\"hack/boilerplate.go.txt\" paths=\"./...\"\n\nfmt: ## Run go fmt against code.\n\tgo fmt ./...\n\nvet: ## Run go vet against code.\n\tgo vet ./...\n\nENVTEST_ASSETS_DIR=$(shell pwd)/testbin\ntest: manifests generate fmt vet ## Run tests.\n\tmkdir -p ${ENVTEST_ASSETS_DIR}\n\ttest -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.2/hack/setup-envtest.sh\n\tsource ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out\n\n##@ Build\n\nbuild: generate fmt vet ## Build manager binary.\n\tgo build -o bin/manager main.go\n\nrun: manifests generate fmt vet ## Run a controller from your host.\n\tgo run ./main.go\n\ndocker-build: test ## Build docker image with the manager.\n\tdocker build -t ${IMG} .\n\ndocker-push: ## Push docker image with the manager.\n\tdocker push ${IMG}\n\n##@ Deployment\n\ninstall: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.\n\t$(KUSTOMIZE) build config/crd | kubectl apply -f -\n\nuninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config.\n\t$(KUSTOMIZE) build config/crd | kubectl delete -f -\n\ndeploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.\n\tcd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}\n\t$(KUSTOMIZE) build config/default | kubectl apply -f -\n\nundeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config.\n\t$(KUSTOMIZE) build config/default | kubectl delete -f -\n\n\nCONTROLLER_GEN = $(shell pwd)/bin/controller-gen\ncontroller-gen: ## Download controller-gen locally if necessary.\n\t$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.1)\n\nKUSTOMIZE = $(shell pwd)/bin/kustomize\nkustomize: ## Download kustomize locally if necessary.\n\t$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)\n\n# go-get-tool will 'go get' any package $2 and install it to $1.\nPROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))\ndefine go-get-tool\n@[ -f $(1) ] || { \\\nset -e ;\\\nTMP_DIR=$$(mktemp -d) ;\\\ncd $$TMP_DIR ;\\\ngo mod init tmp ;\\\necho \"Downloading $(2)\" ;\\\nGOBIN=$(PROJECT_DIR)/bin go get $(2) ;\\\nrm -rf $$TMP_DIR ;\\\n}\nendef\n"
  },
  {
    "path": "deploy/PROJECT",
    "content": "domain: volcengine.com\nlayout:\n- go.kubebuilder.io/v3\nprojectName: deploy\nrepo: code.byted.org/data/monolith/deploy\nresources:\n- api:\n    crdVersion: v1\n    namespaced: true\n  controller: true\n  domain: volcengine.com\n  group: mlplatform\n  kind: MLService\n  path: code.byted.org/data/monolith/deploy/api/v1\n  version: v1\nversion: \"3\"\n"
  },
  {
    "path": "deploy/README.md",
    "content": "# 项目介绍\n\n项目初始结构通过kubebuilder(https://github.com/kubernetes-sigs/kubebuilder) 生成\n\nkubebuilder使用文档：https://book.kubebuilder.io/cronjob-tutorial/cronjob-tutorial.html\n\n```\nkubebuilder init --domain volcengine.com --repo https://github.com/bytedance/monolith/blob/master/deploy --skip-go-version-check\n\nkubebuilder create api --group mlplatform --version v1 --kind MLService --controller --resource\n```\n\n# 安装部署\n\n配置好集群kubeconfig后执行以下命令：\n\n```\nmake deploy  # 安装CRD，部署controller\n```\n\n"
  },
  {
    "path": "deploy/api/v1/groupversion_info.go",
    "content": "/*\nCopyright 2023.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Package v1 contains API Schema definitions for the mlplatform v1 API group\n//+kubebuilder:object:generate=true\n//+groupName=mlplatform.volcengine.com\npackage v1\n\nimport (\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"sigs.k8s.io/controller-runtime/pkg/scheme\"\n)\n\nvar (\n\t// GroupVersion is group version used to register these objects\n\tGroupVersion = schema.GroupVersion{Group: \"mlplatform.volcengine.com\", Version: \"v1\"}\n\n\t// SchemeBuilder is used to add go types to the GroupVersionKind scheme\n\tSchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}\n\n\t// AddToScheme adds the types in this group-version to the given scheme.\n\tAddToScheme = SchemeBuilder.AddToScheme\n)\n"
  },
  {
    "path": "deploy/api/v1/mlservice_types.go",
    "content": "/*\nCopyright 2023.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage v1\n\nimport (\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\n// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.\n\n// ServicePortType is the data type of ServicePort Type\ntype ServicePortType string\n\nconst (\n\tServicePortTypeHttp    ServicePortType = \"HTTP\"\n\tServicePortTypeRpc     ServicePortType = \"RPC\"\n\tServicePortTypeMetrics ServicePortType = \"Metrics\"\n\tServicePortTypeOther   ServicePortType = \"Other\"\n)\n\n// DeploymentTemplateSpec defines the metadata and spec of a Deployment\ntype DeploymentTemplateSpec struct {\n\t// Standard object metadata.\n\t// +optional\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\n\t// Specification of the desired behavior of the Deployment.\n\tSpec appsv1.DeploymentSpec `json:\"spec\"`\n}\n\n// ServicePort contains information on service's port.\ntype ServicePort struct {\n\t// The type of this port within the service.\n\tType ServicePortType `json:\"type,omitempty\"`\n\t// The port that will be exposed by this service.\n\tPort int32 `json:\"port\"`\n}\n\n// ServiceSpec describes the attributes that a user creates on a service.\ntype ServiceSpec struct {\n\t// ServiceType defines which type of service need to be created\n\tServiceType corev1.ServiceType `json:\"serviceType,omitempty\"`\n\n\t// The list of ports that are exposed by this service.\n\t// More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies\n\tPorts []ServicePort `json:\"ports,omitempty\"`\n}\n\n// RoleSpec defines the desired state of a role in MLService\ntype RoleSpec struct {\n\t// Name of the role\n\tName string `json:\"name\"`\n\n\t// Number of shards for the role, each shard associated with a Deployment\n\tShardNum int32 `json:\"shardNum,omitempty\"`\n\n\t// Template of the DeploymentSpec\n\tTemplate DeploymentTemplateSpec `json:\"template\"`\n\n\tServiceSpec *ServiceSpec `json:\"serviceSpec,omitempty\"`\n}\n\n// MLServiceSpec defines the desired state of MLService\ntype MLServiceSpec struct {\n\t// selector is a label query over deployment.\n\t// It must match the deployment template's labels.\n\tSelector *metav1.LabelSelector `json:\"selector\"`\n\n\t// Roles defines desired state for each role in the service\n\tRoles []RoleSpec `json:\"roles\"`\n}\n\n// ServicePhase is a label for the condition of a MLService at the current time.\ntype ServicePhase string\n\nconst (\n\t// ServiceQueuing means the service is queuing, waiting to be scheduled\n\tServiceQueuing ServicePhase = \"Queuing\"\n\t// ServiceDeploying means pods of the service are scheduled and being initializing\n\tServiceDeploying ServicePhase = \"Deploying\"\n\t// ServiceRunning means all pods of the service are running\n\tServiceRunning ServicePhase = \"Running\"\n\t// ServiceAbnormal means some pods of the service are abnormal\n\tServiceAbnormal ServicePhase = \"Abnormal\"\n\t// ServiceDeleting means the service is being deleted\n\tServiceDeleting ServicePhase = \"Deleting\"\n\t// ServiceStopping means replicas of the service is being scaled down to 0\n\tServiceStopping ServicePhase = \"Stopping\"\n\t// ServiceStopped means replicas of the service has been scaled down to 0\n\tServiceStopped ServicePhase = \"Stopped\"\n)\n\n// MLServiceStatus defines the observed state of MLService\ntype MLServiceStatus struct {\n\t// Phase is a simple, high-level summary of where the Service is in its lifecycle.\n\t// +optional\n\tPhase ServicePhase `json:\"phase,omitempty\"`\n\n\t// RoleShardStatusMap shows the current status for all Deployments.\n\t// The key is Deployment name, value is its status info\n\tRoleShardStatusMap map[string]appsv1.DeploymentStatus `json:\"roleShardStatusMap,omitempty\"`\n\n\t// RoleShardStatusMap shows the current status for all Services.\n\t// The key is Service name, value is its status info\n\tRoleServiceStatusMap map[string]corev1.ServiceStatus `json:\"roleServiceStatusMap,omitempty\"`\n\n\t// RoleServiceClusterIps shows the cluster ip for all Services.\n\t// The key is Service name, value is its clusterIP\n\tRoleServiceClusterIps map[string]string `json:\"roleServiceClusterIps,omitempty\"`\n\n\t// LastTransitionTime is time the last Phase transitioned to current one.\n\t// +optional\n\tLastTransitionTime metav1.Time `json:\"lastTransitionTime,omitempty\"`\n\n\t// Unique, one-word, CamelCase reason for the phase's last transition.\n\t// +optional\n\tReason string `json:\"reason,omitempty\"`\n\n\t// Human-readable message indicating details about last transition.\n\t// +optional\n\tMessage string `json:\"message,omitempty\"`\n}\n\n// +kubebuilder:object:root=true\n// +kubebuilder:subresource:status\n// +kubebuilder:printcolumn:name=\"Age\",type=date,JSONPath=`.metadata.creationTimestamp`\n// +kubebuilder:printcolumn:name=\"Phase\",type=string,JSONPath=`.status.phase`\n// +kubebuilder:resource:path=mlservices,shortName=mlsvc\n\n// MLService is the Schema for the mlservices API\ntype MLService struct {\n\tmetav1.TypeMeta   `json:\",inline\"`\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\n\tSpec   MLServiceSpec   `json:\"spec,omitempty\"`\n\tStatus MLServiceStatus `json:\"status,omitempty\"`\n}\n\n//+kubebuilder:object:root=true\n\n// MLServiceList contains a list of MLService\ntype MLServiceList struct {\n\tmetav1.TypeMeta `json:\",inline\"`\n\tmetav1.ListMeta `json:\"metadata,omitempty\"`\n\tItems           []MLService `json:\"items\"`\n}\n\nfunc init() {\n\tSchemeBuilder.Register(&MLService{}, &MLServiceList{})\n}\n"
  },
  {
    "path": "deploy/api/v1/zz_generated.deepcopy.go",
    "content": "//go:build !ignore_autogenerated\n// +build !ignore_autogenerated\n\n/*\nCopyright 2023.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Code generated by controller-gen. DO NOT EDIT.\n\npackage v1\n\nimport (\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\truntime \"k8s.io/apimachinery/pkg/runtime\"\n)\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *DeploymentTemplateSpec) DeepCopyInto(out *DeploymentTemplateSpec) {\n\t*out = *in\n\tin.ObjectMeta.DeepCopyInto(&out.ObjectMeta)\n\tin.Spec.DeepCopyInto(&out.Spec)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentTemplateSpec.\nfunc (in *DeploymentTemplateSpec) DeepCopy() *DeploymentTemplateSpec {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(DeploymentTemplateSpec)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *MLService) DeepCopyInto(out *MLService) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ObjectMeta.DeepCopyInto(&out.ObjectMeta)\n\tin.Spec.DeepCopyInto(&out.Spec)\n\tin.Status.DeepCopyInto(&out.Status)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MLService.\nfunc (in *MLService) DeepCopy() *MLService {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(MLService)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.\nfunc (in *MLService) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *MLServiceList) DeepCopyInto(out *MLServiceList) {\n\t*out = *in\n\tout.TypeMeta = in.TypeMeta\n\tin.ListMeta.DeepCopyInto(&out.ListMeta)\n\tif in.Items != nil {\n\t\tin, out := &in.Items, &out.Items\n\t\t*out = make([]MLService, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MLServiceList.\nfunc (in *MLServiceList) DeepCopy() *MLServiceList {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(MLServiceList)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.\nfunc (in *MLServiceList) DeepCopyObject() runtime.Object {\n\tif c := in.DeepCopy(); c != nil {\n\t\treturn c\n\t}\n\treturn nil\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *MLServiceSpec) DeepCopyInto(out *MLServiceSpec) {\n\t*out = *in\n\tif in.Selector != nil {\n\t\tin, out := &in.Selector, &out.Selector\n\t\t*out = new(metav1.LabelSelector)\n\t\t(*in).DeepCopyInto(*out)\n\t}\n\tif in.Roles != nil {\n\t\tin, out := &in.Roles, &out.Roles\n\t\t*out = make([]RoleSpec, len(*in))\n\t\tfor i := range *in {\n\t\t\t(*in)[i].DeepCopyInto(&(*out)[i])\n\t\t}\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MLServiceSpec.\nfunc (in *MLServiceSpec) DeepCopy() *MLServiceSpec {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(MLServiceSpec)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *MLServiceStatus) DeepCopyInto(out *MLServiceStatus) {\n\t*out = *in\n\tif in.RoleShardStatusMap != nil {\n\t\tin, out := &in.RoleShardStatusMap, &out.RoleShardStatusMap\n\t\t*out = make(map[string]appsv1.DeploymentStatus, len(*in))\n\t\tfor key, val := range *in {\n\t\t\t(*out)[key] = *val.DeepCopy()\n\t\t}\n\t}\n\tif in.RoleServiceStatusMap != nil {\n\t\tin, out := &in.RoleServiceStatusMap, &out.RoleServiceStatusMap\n\t\t*out = make(map[string]corev1.ServiceStatus, len(*in))\n\t\tfor key, val := range *in {\n\t\t\t(*out)[key] = *val.DeepCopy()\n\t\t}\n\t}\n\tif in.RoleServiceClusterIps != nil {\n\t\tin, out := &in.RoleServiceClusterIps, &out.RoleServiceClusterIps\n\t\t*out = make(map[string]string, len(*in))\n\t\tfor key, val := range *in {\n\t\t\t(*out)[key] = val\n\t\t}\n\t}\n\tin.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MLServiceStatus.\nfunc (in *MLServiceStatus) DeepCopy() *MLServiceStatus {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(MLServiceStatus)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *RoleSpec) DeepCopyInto(out *RoleSpec) {\n\t*out = *in\n\tin.Template.DeepCopyInto(&out.Template)\n\tif in.ServiceSpec != nil {\n\t\tin, out := &in.ServiceSpec, &out.ServiceSpec\n\t\t*out = new(ServiceSpec)\n\t\t(*in).DeepCopyInto(*out)\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RoleSpec.\nfunc (in *RoleSpec) DeepCopy() *RoleSpec {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(RoleSpec)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServicePort) DeepCopyInto(out *ServicePort) {\n\t*out = *in\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePort.\nfunc (in *ServicePort) DeepCopy() *ServicePort {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServicePort)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n\n// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\nfunc (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) {\n\t*out = *in\n\tif in.Ports != nil {\n\t\tin, out := &in.Ports, &out.Ports\n\t\t*out = make([]ServicePort, len(*in))\n\t\tcopy(*out, *in)\n\t}\n}\n\n// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceSpec.\nfunc (in *ServiceSpec) DeepCopy() *ServiceSpec {\n\tif in == nil {\n\t\treturn nil\n\t}\n\tout := new(ServiceSpec)\n\tin.DeepCopyInto(out)\n\treturn out\n}\n"
  },
  {
    "path": "deploy/build.sh",
    "content": "#!/bin/bash\n\nmkdir output\ncd deploy && make build\ncp bin/manager ../output\n"
  },
  {
    "path": "deploy/config/crd/bases/mlplatform.volcengine.com_mlservices.yaml",
    "content": "\n---\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    controller-gen.kubebuilder.io/version: v0.6.1\n  creationTimestamp: null\n  name: mlservices.mlplatform.volcengine.com\nspec:\n  group: mlplatform.volcengine.com\n  names:\n    kind: MLService\n    listKind: MLServiceList\n    plural: mlservices\n    shortNames:\n    - mlsvc\n    singular: mlservice\n  scope: Namespaced\n  versions:\n  - additionalPrinterColumns:\n    - jsonPath: .metadata.creationTimestamp\n      name: Age\n      type: date\n    - jsonPath: .status.phase\n      name: Phase\n      type: string\n    name: v1\n    schema:\n      openAPIV3Schema:\n        description: MLService is the Schema for the mlservices API\n        properties:\n          apiVersion:\n            description: 'APIVersion defines the versioned schema of this representation\n              of an object. Servers should convert recognized schemas to the latest\n              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'\n            type: string\n          kind:\n            description: 'Kind is a string value representing the REST resource this\n              object represents. Servers may infer this from the endpoint the client\n              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'\n            type: string\n          metadata:\n            type: object\n          spec:\n            description: MLServiceSpec defines the desired state of MLService\n            properties:\n              roles:\n                description: Roles defines desired state for each role in the service\n                items:\n                  description: RoleSpec defines the desired state of a role in MLService\n                  properties:\n                    name:\n                      description: Name of the role\n                      type: string\n                    serviceSpec:\n                      description: ServiceSpec describes the attributes that a user\n                        creates on a service.\n                      properties:\n                        ports:\n                          description: 'The list of ports that are exposed by this\n                            service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies'\n                          items:\n                            description: ServicePort contains information on service's\n                              port.\n                            properties:\n                              port:\n                                description: The port that will be exposed by this\n                                  service.\n                                format: int32\n                                type: integer\n                              type:\n                                description: The type of this port within the service.\n                                type: string\n                            required:\n                            - port\n                            type: object\n                          type: array\n                        serviceType:\n                          description: ServiceType defines which type of service need\n                            to be created\n                          type: string\n                      type: object\n                    shardNum:\n                      description: Number of shards for the role, each shard associated\n                        with a Deployment\n                      format: int32\n                      type: integer\n                    template:\n                      description: Template of the DeploymentSpec\n                      properties:\n                        metadata:\n                          description: Standard object metadata.\n                          properties:\n                            annotations:\n                              additionalProperties:\n                                type: string\n                              type: object\n                            finalizers:\n                              items:\n                                type: string\n                              type: array\n                            labels:\n                              additionalProperties:\n                                type: string\n                              type: object\n                            name:\n                              type: string\n                            namespace:\n                              type: string\n                          type: object\n                        spec:\n                          description: Specification of the desired behavior of the\n                            Deployment.\n                          properties:\n                            minReadySeconds:\n                              description: Minimum number of seconds for which a newly\n                                created pod should be ready without any of its container\n                                crashing, for it to be considered available. Defaults\n                                to 0 (pod will be considered available as soon as\n                                it is ready)\n                              format: int32\n                              type: integer\n                            paused:\n                              description: Indicates that the deployment is paused.\n                              type: boolean\n                            progressDeadlineSeconds:\n                              description: The maximum time in seconds for a deployment\n                                to make progress before it is considered to be failed.\n                                The deployment controller will continue to process\n                                failed deployments and a condition with a ProgressDeadlineExceeded\n                                reason will be surfaced in the deployment status.\n                                Note that progress will not be estimated during the\n                                time a deployment is paused. Defaults to 600s.\n                              format: int32\n                              type: integer\n                            replicas:\n                              description: Number of desired pods. This is a pointer\n                                to distinguish between explicit zero and not specified.\n                                Defaults to 1.\n                              format: int32\n                              type: integer\n                            revisionHistoryLimit:\n                              description: The number of old ReplicaSets to retain\n                                to allow rollback. This is a pointer to distinguish\n                                between explicit zero and not specified. Defaults\n                                to 10.\n                              format: int32\n                              type: integer\n                            selector:\n                              description: Label selector for pods. Existing ReplicaSets\n                                whose pods are selected by this will be the ones affected\n                                by this deployment. It must match the pod template's\n                                labels.\n                              properties:\n                                matchExpressions:\n                                  description: matchExpressions is a list of label\n                                    selector requirements. The requirements are ANDed.\n                                  items:\n                                    description: A label selector requirement is a\n                                      selector that contains values, a key, and an\n                                      operator that relates the key and values.\n                                    properties:\n                                      key:\n                                        description: key is the label key that the\n                                          selector applies to.\n                                        type: string\n                                      operator:\n                                        description: operator represents a key's relationship\n                                          to a set of values. Valid operators are\n                                          In, NotIn, Exists and DoesNotExist.\n                                        type: string\n                                      values:\n                                        description: values is an array of string\n                                          values. If the operator is In or NotIn,\n                                          the values array must be non-empty. If the\n                                          operator is Exists or DoesNotExist, the\n                                          values array must be empty. This array is\n                                          replaced during a strategic merge patch.\n                                        items:\n                                          type: string\n                                        type: array\n                                    required:\n                                    - key\n                                    - operator\n                                    type: object\n                                  type: array\n                                matchLabels:\n                                  additionalProperties:\n                                    type: string\n                                  description: matchLabels is a map of {key,value}\n                                    pairs. A single {key,value} in the matchLabels\n                                    map is equivalent to an element of matchExpressions,\n                                    whose key field is \"key\", the operator is \"In\",\n                                    and the values array contains only \"value\". The\n                                    requirements are ANDed.\n                                  type: object\n                              type: object\n                            strategy:\n                              description: The deployment strategy to use to replace\n                                existing pods with new ones.\n                              properties:\n                                rollingUpdate:\n                                  description: 'Rolling update config params. Present\n                                    only if DeploymentStrategyType = RollingUpdate.\n                                    --- TODO: Update this to follow our convention\n                                    for oneOf, whatever we decide it to be.'\n                                  properties:\n                                    maxSurge:\n                                      anyOf:\n                                      - type: integer\n                                      - type: string\n                                      description: 'The maximum number of pods that\n                                        can be scheduled above the desired number\n                                        of pods. Value can be an absolute number (ex:\n                                        5) or a percentage of desired pods (ex: 10%).\n                                        This can not be 0 if MaxUnavailable is 0.\n                                        Absolute number is calculated from percentage\n                                        by rounding up. Defaults to 25%. Example:\n                                        when this is set to 30%, the new ReplicaSet\n                                        can be scaled up immediately when the rolling\n                                        update starts, such that the total number\n                                        of old and new pods do not exceed 130% of\n                                        desired pods. Once old pods have been killed,\n                                        new ReplicaSet can be scaled up further, ensuring\n                                        that total number of pods running at any time\n                                        during the update is at most 130% of desired\n                                        pods.'\n                                      x-kubernetes-int-or-string: true\n                                    maxUnavailable:\n                                      anyOf:\n                                      - type: integer\n                                      - type: string\n                                      description: 'The maximum number of pods that\n                                        can be unavailable during the update. Value\n                                        can be an absolute number (ex: 5) or a percentage\n                                        of desired pods (ex: 10%). Absolute number\n                                        is calculated from percentage by rounding\n                                        down. This can not be 0 if MaxSurge is 0.\n                                        Defaults to 25%. Example: when this is set\n                                        to 30%, the old ReplicaSet can be scaled down\n                                        to 70% of desired pods immediately when the\n                                        rolling update starts. Once new pods are ready,\n                                        old ReplicaSet can be scaled down further,\n                                        followed by scaling up the new ReplicaSet,\n                                        ensuring that the total number of pods available\n                                        at all times during the update is at least\n                                        70% of desired pods.'\n                                      x-kubernetes-int-or-string: true\n                                  type: object\n                                type:\n                                  description: Type of deployment. Can be \"Recreate\"\n                                    or \"RollingUpdate\". Default is RollingUpdate.\n                                  type: string\n                              type: object\n                            template:\n                              description: Template describes the pods that will be\n                                created.\n                              properties:\n                                metadata:\n                                  description: 'Standard object''s metadata. More\n                                    info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata'\n                                  properties:\n                                    annotations:\n                                      additionalProperties:\n                                        type: string\n                                      type: object\n                                    finalizers:\n                                      items:\n                                        type: string\n                                      type: array\n                                    labels:\n                                      additionalProperties:\n                                        type: string\n                                      type: object\n                                    name:\n                                      type: string\n                                    namespace:\n                                      type: string\n                                  type: object\n                                spec:\n                                  description: 'Specification of the desired behavior\n                                    of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'\n                                  properties:\n                                    activeDeadlineSeconds:\n                                      description: Optional duration in seconds the\n                                        pod may be active on the node relative to\n                                        StartTime before the system will actively\n                                        try to mark it failed and kill associated\n                                        containers. Value must be a positive integer.\n                                      format: int64\n                                      type: integer\n                                    affinity:\n                                      description: If specified, the pod's scheduling\n                                        constraints\n                                      properties:\n                                        nodeAffinity:\n                                          description: Describes node affinity scheduling\n                                            rules for the pod.\n                                          properties:\n                                            preferredDuringSchedulingIgnoredDuringExecution:\n                                              description: The scheduler will prefer\n                                                to schedule pods to nodes that satisfy\n                                                the affinity expressions specified\n                                                by this field, but it may choose a\n                                                node that violates one or more of\n                                                the expressions. The node that is\n                                                most preferred is the one with the\n                                                greatest sum of weights, i.e. for\n                                                each node that meets all of the scheduling\n                                                requirements (resource request, requiredDuringScheduling\n                                                affinity expressions, etc.), compute\n                                                a sum by iterating through the elements\n                                                of this field and adding \"weight\"\n                                                to the sum if the node matches the\n                                                corresponding matchExpressions; the\n                                                node(s) with the highest sum are the\n                                                most preferred.\n                                              items:\n                                                description: An empty preferred scheduling\n                                                  term matches all objects with implicit\n                                                  weight 0 (i.e. it's a no-op). A\n                                                  null preferred scheduling term matches\n                                                  no objects (i.e. is also a no-op).\n                                                properties:\n                                                  preference:\n                                                    description: A node selector term,\n                                                      associated with the corresponding\n                                                      weight.\n                                                    properties:\n                                                      matchExpressions:\n                                                        description: A list of node\n                                                          selector requirements by\n                                                          node's labels.\n                                                        items:\n                                                          description: A node selector\n                                                            requirement is a selector\n                                                            that contains values,\n                                                            a key, and an operator\n                                                            that relates the key and\n                                                            values.\n                                                          properties:\n                                                            key:\n                                                              description: The label\n                                                                key that the selector\n                                                                applies to.\n                                                              type: string\n                                                            operator:\n                                                              description: Represents\n                                                                a key's relationship\n                                                                to a set of values.\n                                                                Valid operators are\n                                                                In, NotIn, Exists,\n                                                                DoesNotExist. Gt,\n                                                                and Lt.\n                                                              type: string\n                                                            values:\n                                                              description: An array\n                                                                of string values.\n                                                                If the operator is\n                                                                In or NotIn, the values\n                                                                array must be non-empty.\n                                                                If the operator is\n                                                                Exists or DoesNotExist,\n                                                                the values array must\n                                                                be empty. If the operator\n                                                                is Gt or Lt, the values\n                                                                array must have a\n                                                                single element, which\n                                                                will be interpreted\n                                                                as an integer. This\n                                                                array is replaced\n                                                                during a strategic\n                                                                merge patch.\n                                                              items:\n                                                                type: string\n                                                              type: array\n                                                          required:\n                                                          - key\n                                                          - operator\n                                                          type: object\n                                                        type: array\n                                                      matchFields:\n                                                        description: A list of node\n                                                          selector requirements by\n                                                          node's fields.\n                                                        items:\n                                                          description: A node selector\n                                                            requirement is a selector\n                                                            that contains values,\n                                                            a key, and an operator\n                                                            that relates the key and\n                                                            values.\n                                                          properties:\n                                                            key:\n                                                              description: The label\n                                                                key that the selector\n                                                                applies to.\n                                                              type: string\n                                                            operator:\n                                                              description: Represents\n                                                                a key's relationship\n                                                                to a set of values.\n                                                                Valid operators are\n                                                                In, NotIn, Exists,\n                                                                DoesNotExist. Gt,\n                                                                and Lt.\n                                                              type: string\n                                                            values:\n                                                              description: An array\n                                                                of string values.\n                                                                If the operator is\n                                                                In or NotIn, the values\n                                                                array must be non-empty.\n                                                                If the operator is\n                                                                Exists or DoesNotExist,\n                                                                the values array must\n                                                                be empty. If the operator\n                                                                is Gt or Lt, the values\n                                                                array must have a\n                                                                single element, which\n                                                                will be interpreted\n                                                                as an integer. This\n                                                                array is replaced\n                                                                during a strategic\n                                                                merge patch.\n                                                              items:\n                                                                type: string\n                                                              type: array\n                                                          required:\n                                                          - key\n                                                          - operator\n                                                          type: object\n                                                        type: array\n                                                    type: object\n                                                  weight:\n                                                    description: Weight associated\n                                                      with matching the corresponding\n                                                      nodeSelectorTerm, in the range\n                                                      1-100.\n                                                    format: int32\n                                                    type: integer\n                                                required:\n                                                - preference\n                                                - weight\n                                                type: object\n                                              type: array\n                                            requiredDuringSchedulingIgnoredDuringExecution:\n                                              description: If the affinity requirements\n                                                specified by this field are not met\n                                                at scheduling time, the pod will not\n                                                be scheduled onto the node. If the\n                                                affinity requirements specified by\n                                                this field cease to be met at some\n                                                point during pod execution (e.g. due\n                                                to an update), the system may or may\n                                                not try to eventually evict the pod\n                                                from its node.\n                                              properties:\n                                                nodeSelectorTerms:\n                                                  description: Required. A list of\n                                                    node selector terms. The terms\n                                                    are ORed.\n                                                  items:\n                                                    description: A null or empty node\n                                                      selector term matches no objects.\n                                                      The requirements of them are\n                                                      ANDed. The TopologySelectorTerm\n                                                      type implements a subset of\n                                                      the NodeSelectorTerm.\n                                                    properties:\n                                                      matchExpressions:\n                                                        description: A list of node\n                                                          selector requirements by\n                                                          node's labels.\n                                                        items:\n                                                          description: A node selector\n                                                            requirement is a selector\n                                                            that contains values,\n                                                            a key, and an operator\n                                                            that relates the key and\n                                                            values.\n                                                          properties:\n                                                            key:\n                                                              description: The label\n                                                                key that the selector\n                                                                applies to.\n                                                              type: string\n                                                            operator:\n                                                              description: Represents\n                                                                a key's relationship\n                                                                to a set of values.\n                                                                Valid operators are\n                                                                In, NotIn, Exists,\n                                                                DoesNotExist. Gt,\n                                                                and Lt.\n                                                              type: string\n                                                            values:\n                                                              description: An array\n                                                                of string values.\n                                                                If the operator is\n                                                                In or NotIn, the values\n                                                                array must be non-empty.\n                                                                If the operator is\n                                                                Exists or DoesNotExist,\n                                                                the values array must\n                                                                be empty. If the operator\n                                                                is Gt or Lt, the values\n                                                                array must have a\n                                                                single element, which\n                                                                will be interpreted\n                                                                as an integer. This\n                                                                array is replaced\n                                                                during a strategic\n                                                                merge patch.\n                                                              items:\n                                                                type: string\n                                                              type: array\n                                                          required:\n                                                          - key\n                                                          - operator\n                                                          type: object\n                                                        type: array\n                                                      matchFields:\n                                                        description: A list of node\n                                                          selector requirements by\n                                                          node's fields.\n                                                        items:\n                                                          description: A node selector\n                                                            requirement is a selector\n                                                            that contains values,\n                                                            a key, and an operator\n                                                            that relates the key and\n                                                            values.\n                                                          properties:\n                                                            key:\n                                                              description: The label\n                                                                key that the selector\n                                                                applies to.\n                                                              type: string\n                                                            operator:\n                                                              description: Represents\n                                                                a key's relationship\n                                                                to a set of values.\n                                                                Valid operators are\n                                                                In, NotIn, Exists,\n                                                                DoesNotExist. Gt,\n                                                                and Lt.\n                                                              type: string\n                                                            values:\n                                                              description: An array\n                                                                of string values.\n                                                                If the operator is\n                                                                In or NotIn, the values\n                                                                array must be non-empty.\n                                                                If the operator is\n                                                                Exists or DoesNotExist,\n                                                                the values array must\n                                                                be empty. If the operator\n                                                                is Gt or Lt, the values\n                                                                array must have a\n                                                                single element, which\n                                                                will be interpreted\n                                                                as an integer. This\n                                                                array is replaced\n                                                                during a strategic\n                                                                merge patch.\n                                                              items:\n                                                                type: string\n                                                              type: array\n                                                          required:\n                                                          - key\n                                                          - operator\n                                                          type: object\n                                                        type: array\n                                                    type: object\n                                                  type: array\n                                              required:\n                                              - nodeSelectorTerms\n                                              type: object\n                                          type: object\n                                        podAffinity:\n                                          description: Describes pod affinity scheduling\n                                            rules (e.g. co-locate this pod in the\n                                            same node, zone, etc. as some other pod(s)).\n                                          properties:\n                                            preferredDuringSchedulingIgnoredDuringExecution:\n                                              description: The scheduler will prefer\n                                                to schedule pods to nodes that satisfy\n                                                the affinity expressions specified\n                                                by this field, but it may choose a\n                                                node that violates one or more of\n                                                the expressions. The node that is\n                                                most preferred is the one with the\n                                                greatest sum of weights, i.e. for\n                                                each node that meets all of the scheduling\n                                                requirements (resource request, requiredDuringScheduling\n                                                affinity expressions, etc.), compute\n                                                a sum by iterating through the elements\n                                                of this field and adding \"weight\"\n                                                to the sum if the node has pods which\n                                                matches the corresponding podAffinityTerm;\n                                                the node(s) with the highest sum are\n                                                the most preferred.\n                                              items:\n                                                description: The weights of all of\n                                                  the matched WeightedPodAffinityTerm\n                                                  fields are added per-node to find\n                                                  the most preferred node(s)\n                                                properties:\n                                                  podAffinityTerm:\n                                                    description: Required. A pod affinity\n                                                      term, associated with the corresponding\n                                                      weight.\n                                                    properties:\n                                                      labelSelector:\n                                                        description: A label query\n                                                          over a set of resources,\n                                                          in this case pods.\n                                                        properties:\n                                                          matchExpressions:\n                                                            description: matchExpressions\n                                                              is a list of label selector\n                                                              requirements. The requirements\n                                                              are ANDed.\n                                                            items:\n                                                              description: A label\n                                                                selector requirement\n                                                                is a selector that\n                                                                contains values, a\n                                                                key, and an operator\n                                                                that relates the key\n                                                                and values.\n                                                              properties:\n                                                                key:\n                                                                  description: key\n                                                                    is the label key\n                                                                    that the selector\n                                                                    applies to.\n                                                                  type: string\n                                                                operator:\n                                                                  description: operator\n                                                                    represents a key's\n                                                                    relationship to\n                                                                    a set of values.\n                                                                    Valid operators\n                                                                    are In, NotIn,\n                                                                    Exists and DoesNotExist.\n                                                                  type: string\n                                                                values:\n                                                                  description: values\n                                                                    is an array of\n                                                                    string values.\n                                                                    If the operator\n                                                                    is In or NotIn,\n                                                                    the values array\n                                                                    must be non-empty.\n                                                                    If the operator\n                                                                    is Exists or DoesNotExist,\n                                                                    the values array\n                                                                    must be empty.\n                                                                    This array is\n                                                                    replaced during\n                                                                    a strategic merge\n                                                                    patch.\n                                                                  items:\n                                                                    type: string\n                                                                  type: array\n                                                              required:\n                                                              - key\n                                                              - operator\n                                                              type: object\n                                                            type: array\n                                                          matchLabels:\n                                                            additionalProperties:\n                                                              type: string\n                                                            description: matchLabels\n                                                              is a map of {key,value}\n                                                              pairs. A single {key,value}\n                                                              in the matchLabels map\n                                                              is equivalent to an\n                                                              element of matchExpressions,\n                                                              whose key field is \"key\",\n                                                              the operator is \"In\",\n                                                              and the values array\n                                                              contains only \"value\".\n                                                              The requirements are\n                                                              ANDed.\n                                                            type: object\n                                                        type: object\n                                                      namespaces:\n                                                        description: namespaces specifies\n                                                          which namespaces the labelSelector\n                                                          applies to (matches against);\n                                                          null or empty list means\n                                                          \"this pod's namespace\"\n                                                        items:\n                                                          type: string\n                                                        type: array\n                                                      topologyKey:\n                                                        description: This pod should\n                                                          be co-located (affinity)\n                                                          or not co-located (anti-affinity)\n                                                          with the pods matching the\n                                                          labelSelector in the specified\n                                                          namespaces, where co-located\n                                                          is defined as running on\n                                                          a node whose value of the\n                                                          label with key topologyKey\n                                                          matches that of any node\n                                                          on which any of the selected\n                                                          pods is running. Empty topologyKey\n                                                          is not allowed.\n                                                        type: string\n                                                    required:\n                                                    - topologyKey\n                                                    type: object\n                                                  weight:\n                                                    description: weight associated\n                                                      with matching the corresponding\n                                                      podAffinityTerm, in the range\n                                                      1-100.\n                                                    format: int32\n                                                    type: integer\n                                                required:\n                                                - podAffinityTerm\n                                                - weight\n                                                type: object\n                                              type: array\n                                            requiredDuringSchedulingIgnoredDuringExecution:\n                                              description: If the affinity requirements\n                                                specified by this field are not met\n                                                at scheduling time, the pod will not\n                                                be scheduled onto the node. If the\n                                                affinity requirements specified by\n                                                this field cease to be met at some\n                                                point during pod execution (e.g. due\n                                                to a pod label update), the system\n                                                may or may not try to eventually evict\n                                                the pod from its node. When there\n                                                are multiple elements, the lists of\n                                                nodes corresponding to each podAffinityTerm\n                                                are intersected, i.e. all terms must\n                                                be satisfied.\n                                              items:\n                                                description: Defines a set of pods\n                                                  (namely those matching the labelSelector\n                                                  relative to the given namespace(s))\n                                                  that this pod should be co-located\n                                                  (affinity) or not co-located (anti-affinity)\n                                                  with, where co-located is defined\n                                                  as running on a node whose value\n                                                  of the label with key <topologyKey>\n                                                  matches that of any node on which\n                                                  a pod of the set of pods is running\n                                                properties:\n                                                  labelSelector:\n                                                    description: A label query over\n                                                      a set of resources, in this\n                                                      case pods.\n                                                    properties:\n                                                      matchExpressions:\n                                                        description: matchExpressions\n                                                          is a list of label selector\n                                                          requirements. The requirements\n                                                          are ANDed.\n                                                        items:\n                                                          description: A label selector\n                                                            requirement is a selector\n                                                            that contains values,\n                                                            a key, and an operator\n                                                            that relates the key and\n                                                            values.\n                                                          properties:\n                                                            key:\n                                                              description: key is\n                                                                the label key that\n                                                                the selector applies\n                                                                to.\n                                                              type: string\n                                                            operator:\n                                                              description: operator\n                                                                represents a key's\n                                                                relationship to a\n                                                                set of values. Valid\n                                                                operators are In,\n                                                                NotIn, Exists and\n                                                                DoesNotExist.\n                                                              type: string\n                                                            values:\n                                                              description: values\n                                                                is an array of string\n                                                                values. If the operator\n                                                                is In or NotIn, the\n                                                                values array must\n                                                                be non-empty. If the\n                                                                operator is Exists\n                                                                or DoesNotExist, the\n                                                                values array must\n                                                                be empty. This array\n                                                                is replaced during\n                                                                a strategic merge\n                                                                patch.\n                                                              items:\n                                                                type: string\n                                                              type: array\n                                                          required:\n                                                          - key\n                                                          - operator\n                                                          type: object\n                                                        type: array\n                                                      matchLabels:\n                                                        additionalProperties:\n                                                          type: string\n                                                        description: matchLabels is\n                                                          a map of {key,value} pairs.\n                                                          A single {key,value} in\n                                                          the matchLabels map is equivalent\n                                                          to an element of matchExpressions,\n                                                          whose key field is \"key\",\n                                                          the operator is \"In\", and\n                                                          the values array contains\n                                                          only \"value\". The requirements\n                                                          are ANDed.\n                                                        type: object\n                                                    type: object\n                                                  namespaces:\n                                                    description: namespaces specifies\n                                                      which namespaces the labelSelector\n                                                      applies to (matches against);\n                                                      null or empty list means \"this\n                                                      pod's namespace\"\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                  topologyKey:\n                                                    description: This pod should be\n                                                      co-located (affinity) or not\n                                                      co-located (anti-affinity) with\n                                                      the pods matching the labelSelector\n                                                      in the specified namespaces,\n                                                      where co-located is defined\n                                                      as running on a node whose value\n                                                      of the label with key topologyKey\n                                                      matches that of any node on\n                                                      which any of the selected pods\n                                                      is running. Empty topologyKey\n                                                      is not allowed.\n                                                    type: string\n                                                required:\n                                                - topologyKey\n                                                type: object\n                                              type: array\n                                          type: object\n                                        podAntiAffinity:\n                                          description: Describes pod anti-affinity\n                                            scheduling rules (e.g. avoid putting this\n                                            pod in the same node, zone, etc. as some\n                                            other pod(s)).\n                                          properties:\n                                            preferredDuringSchedulingIgnoredDuringExecution:\n                                              description: The scheduler will prefer\n                                                to schedule pods to nodes that satisfy\n                                                the anti-affinity expressions specified\n                                                by this field, but it may choose a\n                                                node that violates one or more of\n                                                the expressions. The node that is\n                                                most preferred is the one with the\n                                                greatest sum of weights, i.e. for\n                                                each node that meets all of the scheduling\n                                                requirements (resource request, requiredDuringScheduling\n                                                anti-affinity expressions, etc.),\n                                                compute a sum by iterating through\n                                                the elements of this field and adding\n                                                \"weight\" to the sum if the node has\n                                                pods which matches the corresponding\n                                                podAffinityTerm; the node(s) with\n                                                the highest sum are the most preferred.\n                                              items:\n                                                description: The weights of all of\n                                                  the matched WeightedPodAffinityTerm\n                                                  fields are added per-node to find\n                                                  the most preferred node(s)\n                                                properties:\n                                                  podAffinityTerm:\n                                                    description: Required. A pod affinity\n                                                      term, associated with the corresponding\n                                                      weight.\n                                                    properties:\n                                                      labelSelector:\n                                                        description: A label query\n                                                          over a set of resources,\n                                                          in this case pods.\n                                                        properties:\n                                                          matchExpressions:\n                                                            description: matchExpressions\n                                                              is a list of label selector\n                                                              requirements. The requirements\n                                                              are ANDed.\n                                                            items:\n                                                              description: A label\n                                                                selector requirement\n                                                                is a selector that\n                                                                contains values, a\n                                                                key, and an operator\n                                                                that relates the key\n                                                                and values.\n                                                              properties:\n                                                                key:\n                                                                  description: key\n                                                                    is the label key\n                                                                    that the selector\n                                                                    applies to.\n                                                                  type: string\n                                                                operator:\n                                                                  description: operator\n                                                                    represents a key's\n                                                                    relationship to\n                                                                    a set of values.\n                                                                    Valid operators\n                                                                    are In, NotIn,\n                                                                    Exists and DoesNotExist.\n                                                                  type: string\n                                                                values:\n                                                                  description: values\n                                                                    is an array of\n                                                                    string values.\n                                                                    If the operator\n                                                                    is In or NotIn,\n                                                                    the values array\n                                                                    must be non-empty.\n                                                                    If the operator\n                                                                    is Exists or DoesNotExist,\n                                                                    the values array\n                                                                    must be empty.\n                                                                    This array is\n                                                                    replaced during\n                                                                    a strategic merge\n                                                                    patch.\n                                                                  items:\n                                                                    type: string\n                                                                  type: array\n                                                              required:\n                                                              - key\n                                                              - operator\n                                                              type: object\n                                                            type: array\n                                                          matchLabels:\n                                                            additionalProperties:\n                                                              type: string\n                                                            description: matchLabels\n                                                              is a map of {key,value}\n                                                              pairs. A single {key,value}\n                                                              in the matchLabels map\n                                                              is equivalent to an\n                                                              element of matchExpressions,\n                                                              whose key field is \"key\",\n                                                              the operator is \"In\",\n                                                              and the values array\n                                                              contains only \"value\".\n                                                              The requirements are\n                                                              ANDed.\n                                                            type: object\n                                                        type: object\n                                                      namespaces:\n                                                        description: namespaces specifies\n                                                          which namespaces the labelSelector\n                                                          applies to (matches against);\n                                                          null or empty list means\n                                                          \"this pod's namespace\"\n                                                        items:\n                                                          type: string\n                                                        type: array\n                                                      topologyKey:\n                                                        description: This pod should\n                                                          be co-located (affinity)\n                                                          or not co-located (anti-affinity)\n                                                          with the pods matching the\n                                                          labelSelector in the specified\n                                                          namespaces, where co-located\n                                                          is defined as running on\n                                                          a node whose value of the\n                                                          label with key topologyKey\n                                                          matches that of any node\n                                                          on which any of the selected\n                                                          pods is running. Empty topologyKey\n                                                          is not allowed.\n                                                        type: string\n                                                    required:\n                                                    - topologyKey\n                                                    type: object\n                                                  weight:\n                                                    description: weight associated\n                                                      with matching the corresponding\n                                                      podAffinityTerm, in the range\n                                                      1-100.\n                                                    format: int32\n                                                    type: integer\n                                                required:\n                                                - podAffinityTerm\n                                                - weight\n                                                type: object\n                                              type: array\n                                            requiredDuringSchedulingIgnoredDuringExecution:\n                                              description: If the anti-affinity requirements\n                                                specified by this field are not met\n                                                at scheduling time, the pod will not\n                                                be scheduled onto the node. If the\n                                                anti-affinity requirements specified\n                                                by this field cease to be met at some\n                                                point during pod execution (e.g. due\n                                                to a pod label update), the system\n                                                may or may not try to eventually evict\n                                                the pod from its node. When there\n                                                are multiple elements, the lists of\n                                                nodes corresponding to each podAffinityTerm\n                                                are intersected, i.e. all terms must\n                                                be satisfied.\n                                              items:\n                                                description: Defines a set of pods\n                                                  (namely those matching the labelSelector\n                                                  relative to the given namespace(s))\n                                                  that this pod should be co-located\n                                                  (affinity) or not co-located (anti-affinity)\n                                                  with, where co-located is defined\n                                                  as running on a node whose value\n                                                  of the label with key <topologyKey>\n                                                  matches that of any node on which\n                                                  a pod of the set of pods is running\n                                                properties:\n                                                  labelSelector:\n                                                    description: A label query over\n                                                      a set of resources, in this\n                                                      case pods.\n                                                    properties:\n                                                      matchExpressions:\n                                                        description: matchExpressions\n                                                          is a list of label selector\n                                                          requirements. The requirements\n                                                          are ANDed.\n                                                        items:\n                                                          description: A label selector\n                                                            requirement is a selector\n                                                            that contains values,\n                                                            a key, and an operator\n                                                            that relates the key and\n                                                            values.\n                                                          properties:\n                                                            key:\n                                                              description: key is\n                                                                the label key that\n                                                                the selector applies\n                                                                to.\n                                                              type: string\n                                                            operator:\n                                                              description: operator\n                                                                represents a key's\n                                                                relationship to a\n                                                                set of values. Valid\n                                                                operators are In,\n                                                                NotIn, Exists and\n                                                                DoesNotExist.\n                                                              type: string\n                                                            values:\n                                                              description: values\n                                                                is an array of string\n                                                                values. If the operator\n                                                                is In or NotIn, the\n                                                                values array must\n                                                                be non-empty. If the\n                                                                operator is Exists\n                                                                or DoesNotExist, the\n                                                                values array must\n                                                                be empty. This array\n                                                                is replaced during\n                                                                a strategic merge\n                                                                patch.\n                                                              items:\n                                                                type: string\n                                                              type: array\n                                                          required:\n                                                          - key\n                                                          - operator\n                                                          type: object\n                                                        type: array\n                                                      matchLabels:\n                                                        additionalProperties:\n                                                          type: string\n                                                        description: matchLabels is\n                                                          a map of {key,value} pairs.\n                                                          A single {key,value} in\n                                                          the matchLabels map is equivalent\n                                                          to an element of matchExpressions,\n                                                          whose key field is \"key\",\n                                                          the operator is \"In\", and\n                                                          the values array contains\n                                                          only \"value\". The requirements\n                                                          are ANDed.\n                                                        type: object\n                                                    type: object\n                                                  namespaces:\n                                                    description: namespaces specifies\n                                                      which namespaces the labelSelector\n                                                      applies to (matches against);\n                                                      null or empty list means \"this\n                                                      pod's namespace\"\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                  topologyKey:\n                                                    description: This pod should be\n                                                      co-located (affinity) or not\n                                                      co-located (anti-affinity) with\n                                                      the pods matching the labelSelector\n                                                      in the specified namespaces,\n                                                      where co-located is defined\n                                                      as running on a node whose value\n                                                      of the label with key topologyKey\n                                                      matches that of any node on\n                                                      which any of the selected pods\n                                                      is running. Empty topologyKey\n                                                      is not allowed.\n                                                    type: string\n                                                required:\n                                                - topologyKey\n                                                type: object\n                                              type: array\n                                          type: object\n                                      type: object\n                                    automountServiceAccountToken:\n                                      description: AutomountServiceAccountToken indicates\n                                        whether a service account token should be\n                                        automatically mounted.\n                                      type: boolean\n                                    containers:\n                                      description: List of containers belonging to\n                                        the pod. Containers cannot currently be added\n                                        or removed. There must be at least one container\n                                        in a Pod. Cannot be updated.\n                                      items:\n                                        description: A single application container\n                                          that you want to run within a pod.\n                                        properties:\n                                          args:\n                                            description: 'Arguments to the entrypoint.\n                                              The docker image''s CMD is used if this\n                                              is not provided. Variable references\n                                              $(VAR_NAME) are expanded using the container''s\n                                              environment. If a variable cannot be\n                                              resolved, the reference in the input\n                                              string will be unchanged. The $(VAR_NAME)\n                                              syntax can be escaped with a double\n                                              $$, ie: $$(VAR_NAME). Escaped references\n                                              will never be expanded, regardless of\n                                              whether the variable exists or not.\n                                              Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'\n                                            items:\n                                              type: string\n                                            type: array\n                                          command:\n                                            description: 'Entrypoint array. Not executed\n                                              within a shell. The docker image''s\n                                              ENTRYPOINT is used if this is not provided.\n                                              Variable references $(VAR_NAME) are\n                                              expanded using the container''s environment.\n                                              If a variable cannot be resolved, the\n                                              reference in the input string will be\n                                              unchanged. The $(VAR_NAME) syntax can\n                                              be escaped with a double $$, ie: $$(VAR_NAME).\n                                              Escaped references will never be expanded,\n                                              regardless of whether the variable exists\n                                              or not. Cannot be updated. More info:\n                                              https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'\n                                            items:\n                                              type: string\n                                            type: array\n                                          env:\n                                            description: List of environment variables\n                                              to set in the container. Cannot be updated.\n                                            items:\n                                              description: EnvVar represents an environment\n                                                variable present in a Container.\n                                              properties:\n                                                name:\n                                                  description: Name of the environment\n                                                    variable. Must be a C_IDENTIFIER.\n                                                  type: string\n                                                value:\n                                                  description: 'Variable references\n                                                    $(VAR_NAME) are expanded using\n                                                    the previous defined environment\n                                                    variables in the container and\n                                                    any service environment variables.\n                                                    If a variable cannot be resolved,\n                                                    the reference in the input string\n                                                    will be unchanged. The $(VAR_NAME)\n                                                    syntax can be escaped with a double\n                                                    $$, ie: $$(VAR_NAME). Escaped\n                                                    references will never be expanded,\n                                                    regardless of whether the variable\n                                                    exists or not. Defaults to \"\".'\n                                                  type: string\n                                                valueFrom:\n                                                  description: Source for the environment\n                                                    variable's value. Cannot be used\n                                                    if value is not empty.\n                                                  properties:\n                                                    configMapKeyRef:\n                                                      description: Selects a key of\n                                                        a ConfigMap.\n                                                      properties:\n                                                        key:\n                                                          description: The key to\n                                                            select.\n                                                          type: string\n                                                        name:\n                                                          description: 'Name of the\n                                                            referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                            TODO: Add other useful\n                                                            fields. apiVersion, kind,\n                                                            uid?'\n                                                          type: string\n                                                        optional:\n                                                          description: Specify whether\n                                                            the ConfigMap or its key\n                                                            must be defined\n                                                          type: boolean\n                                                      required:\n                                                      - key\n                                                      type: object\n                                                    fieldRef:\n                                                      description: 'Selects a field\n                                                        of the pod: supports metadata.name,\n                                                        metadata.namespace, `metadata.labels[''<KEY>'']`,\n                                                        `metadata.annotations[''<KEY>'']`,\n                                                        spec.nodeName, spec.serviceAccountName,\n                                                        status.hostIP, status.podIP,\n                                                        status.podIPs.'\n                                                      properties:\n                                                        apiVersion:\n                                                          description: Version of\n                                                            the schema the FieldPath\n                                                            is written in terms of,\n                                                            defaults to \"v1\".\n                                                          type: string\n                                                        fieldPath:\n                                                          description: Path of the\n                                                            field to select in the\n                                                            specified API version.\n                                                          type: string\n                                                      required:\n                                                      - fieldPath\n                                                      type: object\n                                                    resourceFieldRef:\n                                                      description: 'Selects a resource\n                                                        of the container: only resources\n                                                        limits and requests (limits.cpu,\n                                                        limits.memory, limits.ephemeral-storage,\n                                                        requests.cpu, requests.memory\n                                                        and requests.ephemeral-storage)\n                                                        are currently supported.'\n                                                      properties:\n                                                        containerName:\n                                                          description: 'Container\n                                                            name: required for volumes,\n                                                            optional for env vars'\n                                                          type: string\n                                                        divisor:\n                                                          anyOf:\n                                                          - type: integer\n                                                          - type: string\n                                                          description: Specifies the\n                                                            output format of the exposed\n                                                            resources, defaults to\n                                                            \"1\"\n                                                          pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                          x-kubernetes-int-or-string: true\n                                                        resource:\n                                                          description: 'Required:\n                                                            resource to select'\n                                                          type: string\n                                                      required:\n                                                      - resource\n                                                      type: object\n                                                    secretKeyRef:\n                                                      description: Selects a key of\n                                                        a secret in the pod's namespace\n                                                      properties:\n                                                        key:\n                                                          description: The key of\n                                                            the secret to select from.  Must\n                                                            be a valid secret key.\n                                                          type: string\n                                                        name:\n                                                          description: 'Name of the\n                                                            referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                            TODO: Add other useful\n                                                            fields. apiVersion, kind,\n                                                            uid?'\n                                                          type: string\n                                                        optional:\n                                                          description: Specify whether\n                                                            the Secret or its key\n                                                            must be defined\n                                                          type: boolean\n                                                      required:\n                                                      - key\n                                                      type: object\n                                                  type: object\n                                              required:\n                                              - name\n                                              type: object\n                                            type: array\n                                          envFrom:\n                                            description: List of sources to populate\n                                              environment variables in the container.\n                                              The keys defined within a source must\n                                              be a C_IDENTIFIER. All invalid keys\n                                              will be reported as an event when the\n                                              container is starting. When a key exists\n                                              in multiple sources, the value associated\n                                              with the last source will take precedence.\n                                              Values defined by an Env with a duplicate\n                                              key will take precedence. Cannot be\n                                              updated.\n                                            items:\n                                              description: EnvFromSource represents\n                                                the source of a set of ConfigMaps\n                                              properties:\n                                                configMapRef:\n                                                  description: The ConfigMap to select\n                                                    from\n                                                  properties:\n                                                    name:\n                                                      description: 'Name of the referent.\n                                                        More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                        TODO: Add other useful fields.\n                                                        apiVersion, kind, uid?'\n                                                      type: string\n                                                    optional:\n                                                      description: Specify whether\n                                                        the ConfigMap must be defined\n                                                      type: boolean\n                                                  type: object\n                                                prefix:\n                                                  description: An optional identifier\n                                                    to prepend to each key in the\n                                                    ConfigMap. Must be a C_IDENTIFIER.\n                                                  type: string\n                                                secretRef:\n                                                  description: The Secret to select\n                                                    from\n                                                  properties:\n                                                    name:\n                                                      description: 'Name of the referent.\n                                                        More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                        TODO: Add other useful fields.\n                                                        apiVersion, kind, uid?'\n                                                      type: string\n                                                    optional:\n                                                      description: Specify whether\n                                                        the Secret must be defined\n                                                      type: boolean\n                                                  type: object\n                                              type: object\n                                            type: array\n                                          image:\n                                            description: 'Docker image name. More\n                                              info: https://kubernetes.io/docs/concepts/containers/images\n                                              This field is optional to allow higher\n                                              level config management to default or\n                                              override container images in workload\n                                              controllers like Deployments and StatefulSets.'\n                                            type: string\n                                          imagePullPolicy:\n                                            description: 'Image pull policy. One of\n                                              Always, Never, IfNotPresent. Defaults\n                                              to Always if :latest tag is specified,\n                                              or IfNotPresent otherwise. Cannot be\n                                              updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'\n                                            type: string\n                                          lifecycle:\n                                            description: Actions that the management\n                                              system should take in response to container\n                                              lifecycle events. Cannot be updated.\n                                            properties:\n                                              postStart:\n                                                description: 'PostStart is called\n                                                  immediately after a container is\n                                                  created. If the handler fails, the\n                                                  container is terminated and restarted\n                                                  according to its restart policy.\n                                                  Other management of the container\n                                                  blocks until the hook completes.\n                                                  More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'\n                                                properties:\n                                                  exec:\n                                                    description: One and only one\n                                                      of the following should be specified.\n                                                      Exec specifies the action to\n                                                      take.\n                                                    properties:\n                                                      command:\n                                                        description: Command is the\n                                                          command line to execute\n                                                          inside the container, the\n                                                          working directory for the\n                                                          command  is root ('/') in\n                                                          the container's filesystem.\n                                                          The command is simply exec'd,\n                                                          it is not run inside a shell,\n                                                          so traditional shell instructions\n                                                          ('|', etc) won't work. To\n                                                          use a shell, you need to\n                                                          explicitly call out to that\n                                                          shell. Exit status of 0\n                                                          is treated as live/healthy\n                                                          and non-zero is unhealthy.\n                                                        items:\n                                                          type: string\n                                                        type: array\n                                                    type: object\n                                                  httpGet:\n                                                    description: HTTPGet specifies\n                                                      the http request to perform.\n                                                    properties:\n                                                      host:\n                                                        description: Host name to\n                                                          connect to, defaults to\n                                                          the pod IP. You probably\n                                                          want to set \"Host\" in httpHeaders\n                                                          instead.\n                                                        type: string\n                                                      httpHeaders:\n                                                        description: Custom headers\n                                                          to set in the request. HTTP\n                                                          allows repeated headers.\n                                                        items:\n                                                          description: HTTPHeader\n                                                            describes a custom header\n                                                            to be used in HTTP probes\n                                                          properties:\n                                                            name:\n                                                              description: The header\n                                                                field name\n                                                              type: string\n                                                            value:\n                                                              description: The header\n                                                                field value\n                                                              type: string\n                                                          required:\n                                                          - name\n                                                          - value\n                                                          type: object\n                                                        type: array\n                                                      path:\n                                                        description: Path to access\n                                                          on the HTTP server.\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Name or number\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                      scheme:\n                                                        description: Scheme to use\n                                                          for connecting to the host.\n                                                          Defaults to HTTP.\n                                                        type: string\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                  tcpSocket:\n                                                    description: 'TCPSocket specifies\n                                                      an action involving a TCP port.\n                                                      TCP hooks not yet supported\n                                                      TODO: implement a realistic\n                                                      TCP lifecycle hook'\n                                                    properties:\n                                                      host:\n                                                        description: 'Optional: Host\n                                                          name to connect to, defaults\n                                                          to the pod IP.'\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Number or name\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                type: object\n                                              preStop:\n                                                description: 'PreStop is called immediately\n                                                  before a container is terminated\n                                                  due to an API request or management\n                                                  event such as liveness/startup probe\n                                                  failure, preemption, resource contention,\n                                                  etc. The handler is not called if\n                                                  the container crashes or exits.\n                                                  The reason for termination is passed\n                                                  to the handler. The Pod''s termination\n                                                  grace period countdown begins before\n                                                  the PreStop hooked is executed.\n                                                  Regardless of the outcome of the\n                                                  handler, the container will eventually\n                                                  terminate within the Pod''s termination\n                                                  grace period. Other management of\n                                                  the container blocks until the hook\n                                                  completes or until the termination\n                                                  grace period is reached. More info:\n                                                  https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'\n                                                properties:\n                                                  exec:\n                                                    description: One and only one\n                                                      of the following should be specified.\n                                                      Exec specifies the action to\n                                                      take.\n                                                    properties:\n                                                      command:\n                                                        description: Command is the\n                                                          command line to execute\n                                                          inside the container, the\n                                                          working directory for the\n                                                          command  is root ('/') in\n                                                          the container's filesystem.\n                                                          The command is simply exec'd,\n                                                          it is not run inside a shell,\n                                                          so traditional shell instructions\n                                                          ('|', etc) won't work. To\n                                                          use a shell, you need to\n                                                          explicitly call out to that\n                                                          shell. Exit status of 0\n                                                          is treated as live/healthy\n                                                          and non-zero is unhealthy.\n                                                        items:\n                                                          type: string\n                                                        type: array\n                                                    type: object\n                                                  httpGet:\n                                                    description: HTTPGet specifies\n                                                      the http request to perform.\n                                                    properties:\n                                                      host:\n                                                        description: Host name to\n                                                          connect to, defaults to\n                                                          the pod IP. You probably\n                                                          want to set \"Host\" in httpHeaders\n                                                          instead.\n                                                        type: string\n                                                      httpHeaders:\n                                                        description: Custom headers\n                                                          to set in the request. HTTP\n                                                          allows repeated headers.\n                                                        items:\n                                                          description: HTTPHeader\n                                                            describes a custom header\n                                                            to be used in HTTP probes\n                                                          properties:\n                                                            name:\n                                                              description: The header\n                                                                field name\n                                                              type: string\n                                                            value:\n                                                              description: The header\n                                                                field value\n                                                              type: string\n                                                          required:\n                                                          - name\n                                                          - value\n                                                          type: object\n                                                        type: array\n                                                      path:\n                                                        description: Path to access\n                                                          on the HTTP server.\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Name or number\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                      scheme:\n                                                        description: Scheme to use\n                                                          for connecting to the host.\n                                                          Defaults to HTTP.\n                                                        type: string\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                  tcpSocket:\n                                                    description: 'TCPSocket specifies\n                                                      an action involving a TCP port.\n                                                      TCP hooks not yet supported\n                                                      TODO: implement a realistic\n                                                      TCP lifecycle hook'\n                                                    properties:\n                                                      host:\n                                                        description: 'Optional: Host\n                                                          name to connect to, defaults\n                                                          to the pod IP.'\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Number or name\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                type: object\n                                            type: object\n                                          livenessProbe:\n                                            description: 'Periodic probe of container\n                                              liveness. Container will be restarted\n                                              if the probe fails. Cannot be updated.\n                                              More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                            properties:\n                                              exec:\n                                                description: One and only one of the\n                                                  following should be specified. Exec\n                                                  specifies the action to take.\n                                                properties:\n                                                  command:\n                                                    description: Command is the command\n                                                      line to execute inside the container,\n                                                      the working directory for the\n                                                      command  is root ('/') in the\n                                                      container's filesystem. The\n                                                      command is simply exec'd, it\n                                                      is not run inside a shell, so\n                                                      traditional shell instructions\n                                                      ('|', etc) won't work. To use\n                                                      a shell, you need to explicitly\n                                                      call out to that shell. Exit\n                                                      status of 0 is treated as live/healthy\n                                                      and non-zero is unhealthy.\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              failureThreshold:\n                                                description: Minimum consecutive failures\n                                                  for the probe to be considered failed\n                                                  after having succeeded. Defaults\n                                                  to 3. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              httpGet:\n                                                description: HTTPGet specifies the\n                                                  http request to perform.\n                                                properties:\n                                                  host:\n                                                    description: Host name to connect\n                                                      to, defaults to the pod IP.\n                                                      You probably want to set \"Host\"\n                                                      in httpHeaders instead.\n                                                    type: string\n                                                  httpHeaders:\n                                                    description: Custom headers to\n                                                      set in the request. HTTP allows\n                                                      repeated headers.\n                                                    items:\n                                                      description: HTTPHeader describes\n                                                        a custom header to be used\n                                                        in HTTP probes\n                                                      properties:\n                                                        name:\n                                                          description: The header\n                                                            field name\n                                                          type: string\n                                                        value:\n                                                          description: The header\n                                                            field value\n                                                          type: string\n                                                      required:\n                                                      - name\n                                                      - value\n                                                      type: object\n                                                    type: array\n                                                  path:\n                                                    description: Path to access on\n                                                      the HTTP server.\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Name or number of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                  scheme:\n                                                    description: Scheme to use for\n                                                      connecting to the host. Defaults\n                                                      to HTTP.\n                                                    type: string\n                                                required:\n                                                - port\n                                                type: object\n                                              initialDelaySeconds:\n                                                description: 'Number of seconds after\n                                                  the container has started before\n                                                  liveness probes are initiated. More\n                                                  info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                              periodSeconds:\n                                                description: How often (in seconds)\n                                                  to perform the probe. Default to\n                                                  10 seconds. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              successThreshold:\n                                                description: Minimum consecutive successes\n                                                  for the probe to be considered successful\n                                                  after having failed. Defaults to\n                                                  1. Must be 1 for liveness and startup.\n                                                  Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              tcpSocket:\n                                                description: 'TCPSocket specifies\n                                                  an action involving a TCP port.\n                                                  TCP hooks not yet supported TODO:\n                                                  implement a realistic TCP lifecycle\n                                                  hook'\n                                                properties:\n                                                  host:\n                                                    description: 'Optional: Host name\n                                                      to connect to, defaults to the\n                                                      pod IP.'\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Number or name of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                required:\n                                                - port\n                                                type: object\n                                              timeoutSeconds:\n                                                description: 'Number of seconds after\n                                                  which the probe times out. Defaults\n                                                  to 1 second. Minimum value is 1.\n                                                  More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                            type: object\n                                          name:\n                                            description: Name of the container specified\n                                              as a DNS_LABEL. Each container in a\n                                              pod must have a unique name (DNS_LABEL).\n                                              Cannot be updated.\n                                            type: string\n                                          ports:\n                                            description: List of ports to expose from\n                                              the container. Exposing a port here\n                                              gives the system additional information\n                                              about the network connections a container\n                                              uses, but is primarily informational.\n                                              Not specifying a port here DOES NOT\n                                              prevent that port from being exposed.\n                                              Any port which is listening on the default\n                                              \"0.0.0.0\" address inside a container\n                                              will be accessible from the network.\n                                              Cannot be updated.\n                                            items:\n                                              description: ContainerPort represents\n                                                a network port in a single container.\n                                              properties:\n                                                containerPort:\n                                                  description: Number of port to expose\n                                                    on the pod's IP address. This\n                                                    must be a valid port number, 0\n                                                    < x < 65536.\n                                                  format: int32\n                                                  type: integer\n                                                hostIP:\n                                                  description: What host IP to bind\n                                                    the external port to.\n                                                  type: string\n                                                hostPort:\n                                                  description: Number of port to expose\n                                                    on the host. If specified, this\n                                                    must be a valid port number, 0\n                                                    < x < 65536. If HostNetwork is\n                                                    specified, this must match ContainerPort.\n                                                    Most containers do not need this.\n                                                  format: int32\n                                                  type: integer\n                                                name:\n                                                  description: If specified, this\n                                                    must be an IANA_SVC_NAME and unique\n                                                    within the pod. Each named port\n                                                    in a pod must have a unique name.\n                                                    Name for the port that can be\n                                                    referred to by services.\n                                                  type: string\n                                                protocol:\n                                                  default: TCP\n                                                  description: Protocol for port.\n                                                    Must be UDP, TCP, or SCTP. Defaults\n                                                    to \"TCP\".\n                                                  type: string\n                                              required:\n                                              - containerPort\n                                              type: object\n                                            type: array\n                                            x-kubernetes-list-map-keys:\n                                            - containerPort\n                                            - protocol\n                                            x-kubernetes-list-type: map\n                                          readinessProbe:\n                                            description: 'Periodic probe of container\n                                              service readiness. Container will be\n                                              removed from service endpoints if the\n                                              probe fails. Cannot be updated. More\n                                              info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                            properties:\n                                              exec:\n                                                description: One and only one of the\n                                                  following should be specified. Exec\n                                                  specifies the action to take.\n                                                properties:\n                                                  command:\n                                                    description: Command is the command\n                                                      line to execute inside the container,\n                                                      the working directory for the\n                                                      command  is root ('/') in the\n                                                      container's filesystem. The\n                                                      command is simply exec'd, it\n                                                      is not run inside a shell, so\n                                                      traditional shell instructions\n                                                      ('|', etc) won't work. To use\n                                                      a shell, you need to explicitly\n                                                      call out to that shell. Exit\n                                                      status of 0 is treated as live/healthy\n                                                      and non-zero is unhealthy.\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              failureThreshold:\n                                                description: Minimum consecutive failures\n                                                  for the probe to be considered failed\n                                                  after having succeeded. Defaults\n                                                  to 3. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              httpGet:\n                                                description: HTTPGet specifies the\n                                                  http request to perform.\n                                                properties:\n                                                  host:\n                                                    description: Host name to connect\n                                                      to, defaults to the pod IP.\n                                                      You probably want to set \"Host\"\n                                                      in httpHeaders instead.\n                                                    type: string\n                                                  httpHeaders:\n                                                    description: Custom headers to\n                                                      set in the request. HTTP allows\n                                                      repeated headers.\n                                                    items:\n                                                      description: HTTPHeader describes\n                                                        a custom header to be used\n                                                        in HTTP probes\n                                                      properties:\n                                                        name:\n                                                          description: The header\n                                                            field name\n                                                          type: string\n                                                        value:\n                                                          description: The header\n                                                            field value\n                                                          type: string\n                                                      required:\n                                                      - name\n                                                      - value\n                                                      type: object\n                                                    type: array\n                                                  path:\n                                                    description: Path to access on\n                                                      the HTTP server.\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Name or number of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                  scheme:\n                                                    description: Scheme to use for\n                                                      connecting to the host. Defaults\n                                                      to HTTP.\n                                                    type: string\n                                                required:\n                                                - port\n                                                type: object\n                                              initialDelaySeconds:\n                                                description: 'Number of seconds after\n                                                  the container has started before\n                                                  liveness probes are initiated. More\n                                                  info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                              periodSeconds:\n                                                description: How often (in seconds)\n                                                  to perform the probe. Default to\n                                                  10 seconds. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              successThreshold:\n                                                description: Minimum consecutive successes\n                                                  for the probe to be considered successful\n                                                  after having failed. Defaults to\n                                                  1. Must be 1 for liveness and startup.\n                                                  Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              tcpSocket:\n                                                description: 'TCPSocket specifies\n                                                  an action involving a TCP port.\n                                                  TCP hooks not yet supported TODO:\n                                                  implement a realistic TCP lifecycle\n                                                  hook'\n                                                properties:\n                                                  host:\n                                                    description: 'Optional: Host name\n                                                      to connect to, defaults to the\n                                                      pod IP.'\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Number or name of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                required:\n                                                - port\n                                                type: object\n                                              timeoutSeconds:\n                                                description: 'Number of seconds after\n                                                  which the probe times out. Defaults\n                                                  to 1 second. Minimum value is 1.\n                                                  More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                            type: object\n                                          resources:\n                                            description: 'Compute Resources required\n                                              by this container. Cannot be updated.\n                                              More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'\n                                            properties:\n                                              limits:\n                                                additionalProperties:\n                                                  anyOf:\n                                                  - type: integer\n                                                  - type: string\n                                                  pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                  x-kubernetes-int-or-string: true\n                                                description: 'Limits describes the\n                                                  maximum amount of compute resources\n                                                  allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'\n                                                type: object\n                                              requests:\n                                                additionalProperties:\n                                                  anyOf:\n                                                  - type: integer\n                                                  - type: string\n                                                  pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                  x-kubernetes-int-or-string: true\n                                                description: 'Requests describes the\n                                                  minimum amount of compute resources\n                                                  required. If Requests is omitted\n                                                  for a container, it defaults to\n                                                  Limits if that is explicitly specified,\n                                                  otherwise to an implementation-defined\n                                                  value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'\n                                                type: object\n                                            type: object\n                                          securityContext:\n                                            description: 'Security options the pod\n                                              should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/\n                                              More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'\n                                            properties:\n                                              allowPrivilegeEscalation:\n                                                description: 'AllowPrivilegeEscalation\n                                                  controls whether a process can gain\n                                                  more privileges than its parent\n                                                  process. This bool directly controls\n                                                  if the no_new_privs flag will be\n                                                  set on the container process. AllowPrivilegeEscalation\n                                                  is true always when the container\n                                                  is: 1) run as Privileged 2) has\n                                                  CAP_SYS_ADMIN'\n                                                type: boolean\n                                              capabilities:\n                                                description: The capabilities to add/drop\n                                                  when running containers. Defaults\n                                                  to the default set of capabilities\n                                                  granted by the container runtime.\n                                                properties:\n                                                  add:\n                                                    description: Added capabilities\n                                                    items:\n                                                      description: Capability represent\n                                                        POSIX capabilities type\n                                                      type: string\n                                                    type: array\n                                                  drop:\n                                                    description: Removed capabilities\n                                                    items:\n                                                      description: Capability represent\n                                                        POSIX capabilities type\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              privileged:\n                                                description: Run container in privileged\n                                                  mode. Processes in privileged containers\n                                                  are essentially equivalent to root\n                                                  on the host. Defaults to false.\n                                                type: boolean\n                                              procMount:\n                                                description: procMount denotes the\n                                                  type of proc mount to use for the\n                                                  containers. The default is DefaultProcMount\n                                                  which uses the container runtime\n                                                  defaults for readonly paths and\n                                                  masked paths. This requires the\n                                                  ProcMountType feature flag to be\n                                                  enabled.\n                                                type: string\n                                              readOnlyRootFilesystem:\n                                                description: Whether this container\n                                                  has a read-only root filesystem.\n                                                  Default is false.\n                                                type: boolean\n                                              runAsGroup:\n                                                description: The GID to run the entrypoint\n                                                  of the container process. Uses runtime\n                                                  default if unset. May also be set\n                                                  in PodSecurityContext.  If set in\n                                                  both SecurityContext and PodSecurityContext,\n                                                  the value specified in SecurityContext\n                                                  takes precedence.\n                                                format: int64\n                                                type: integer\n                                              runAsNonRoot:\n                                                description: Indicates that the container\n                                                  must run as a non-root user. If\n                                                  true, the Kubelet will validate\n                                                  the image at runtime to ensure that\n                                                  it does not run as UID 0 (root)\n                                                  and fail to start the container\n                                                  if it does. If unset or false, no\n                                                  such validation will be performed.\n                                                  May also be set in PodSecurityContext.  If\n                                                  set in both SecurityContext and\n                                                  PodSecurityContext, the value specified\n                                                  in SecurityContext takes precedence.\n                                                type: boolean\n                                              runAsUser:\n                                                description: The UID to run the entrypoint\n                                                  of the container process. Defaults\n                                                  to user specified in image metadata\n                                                  if unspecified. May also be set\n                                                  in PodSecurityContext.  If set in\n                                                  both SecurityContext and PodSecurityContext,\n                                                  the value specified in SecurityContext\n                                                  takes precedence.\n                                                format: int64\n                                                type: integer\n                                              seLinuxOptions:\n                                                description: The SELinux context to\n                                                  be applied to the container. If\n                                                  unspecified, the container runtime\n                                                  will allocate a random SELinux context\n                                                  for each container.  May also be\n                                                  set in PodSecurityContext.  If set\n                                                  in both SecurityContext and PodSecurityContext,\n                                                  the value specified in SecurityContext\n                                                  takes precedence.\n                                                properties:\n                                                  level:\n                                                    description: Level is SELinux\n                                                      level label that applies to\n                                                      the container.\n                                                    type: string\n                                                  role:\n                                                    description: Role is a SELinux\n                                                      role label that applies to the\n                                                      container.\n                                                    type: string\n                                                  type:\n                                                    description: Type is a SELinux\n                                                      type label that applies to the\n                                                      container.\n                                                    type: string\n                                                  user:\n                                                    description: User is a SELinux\n                                                      user label that applies to the\n                                                      container.\n                                                    type: string\n                                                type: object\n                                              seccompProfile:\n                                                description: The seccomp options to\n                                                  use by this container. If seccomp\n                                                  options are provided at both the\n                                                  pod & container level, the container\n                                                  options override the pod options.\n                                                properties:\n                                                  localhostProfile:\n                                                    description: localhostProfile\n                                                      indicates a profile defined\n                                                      in a file on the node should\n                                                      be used. The profile must be\n                                                      preconfigured on the node to\n                                                      work. Must be a descending path,\n                                                      relative to the kubelet's configured\n                                                      seccomp profile location. Must\n                                                      only be set if type is \"Localhost\".\n                                                    type: string\n                                                  type:\n                                                    description: \"type indicates which\n                                                      kind of seccomp profile will\n                                                      be applied. Valid options are:\n                                                      \\n Localhost - a profile defined\n                                                      in a file on the node should\n                                                      be used. RuntimeDefault - the\n                                                      container runtime default profile\n                                                      should be used. Unconfined -\n                                                      no profile should be applied.\"\n                                                    type: string\n                                                required:\n                                                - type\n                                                type: object\n                                              windowsOptions:\n                                                description: The Windows specific\n                                                  settings applied to all containers.\n                                                  If unspecified, the options from\n                                                  the PodSecurityContext will be used.\n                                                  If set in both SecurityContext and\n                                                  PodSecurityContext, the value specified\n                                                  in SecurityContext takes precedence.\n                                                properties:\n                                                  gmsaCredentialSpec:\n                                                    description: GMSACredentialSpec\n                                                      is where the GMSA admission\n                                                      webhook (https://github.com/kubernetes-sigs/windows-gmsa)\n                                                      inlines the contents of the\n                                                      GMSA credential spec named by\n                                                      the GMSACredentialSpecName field.\n                                                    type: string\n                                                  gmsaCredentialSpecName:\n                                                    description: GMSACredentialSpecName\n                                                      is the name of the GMSA credential\n                                                      spec to use.\n                                                    type: string\n                                                  runAsUserName:\n                                                    description: The UserName in Windows\n                                                      to run the entrypoint of the\n                                                      container process. Defaults\n                                                      to the user specified in image\n                                                      metadata if unspecified. May\n                                                      also be set in PodSecurityContext.\n                                                      If set in both SecurityContext\n                                                      and PodSecurityContext, the\n                                                      value specified in SecurityContext\n                                                      takes precedence.\n                                                    type: string\n                                                type: object\n                                            type: object\n                                          startupProbe:\n                                            description: 'StartupProbe indicates that\n                                              the Pod has successfully initialized.\n                                              If specified, no other probes are executed\n                                              until this completes successfully. If\n                                              this probe fails, the Pod will be restarted,\n                                              just as if the livenessProbe failed.\n                                              This can be used to provide different\n                                              probe parameters at the beginning of\n                                              a Pod''s lifecycle, when it might take\n                                              a long time to load data or warm a cache,\n                                              than during steady-state operation.\n                                              This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                            properties:\n                                              exec:\n                                                description: One and only one of the\n                                                  following should be specified. Exec\n                                                  specifies the action to take.\n                                                properties:\n                                                  command:\n                                                    description: Command is the command\n                                                      line to execute inside the container,\n                                                      the working directory for the\n                                                      command  is root ('/') in the\n                                                      container's filesystem. The\n                                                      command is simply exec'd, it\n                                                      is not run inside a shell, so\n                                                      traditional shell instructions\n                                                      ('|', etc) won't work. To use\n                                                      a shell, you need to explicitly\n                                                      call out to that shell. Exit\n                                                      status of 0 is treated as live/healthy\n                                                      and non-zero is unhealthy.\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              failureThreshold:\n                                                description: Minimum consecutive failures\n                                                  for the probe to be considered failed\n                                                  after having succeeded. Defaults\n                                                  to 3. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              httpGet:\n                                                description: HTTPGet specifies the\n                                                  http request to perform.\n                                                properties:\n                                                  host:\n                                                    description: Host name to connect\n                                                      to, defaults to the pod IP.\n                                                      You probably want to set \"Host\"\n                                                      in httpHeaders instead.\n                                                    type: string\n                                                  httpHeaders:\n                                                    description: Custom headers to\n                                                      set in the request. HTTP allows\n                                                      repeated headers.\n                                                    items:\n                                                      description: HTTPHeader describes\n                                                        a custom header to be used\n                                                        in HTTP probes\n                                                      properties:\n                                                        name:\n                                                          description: The header\n                                                            field name\n                                                          type: string\n                                                        value:\n                                                          description: The header\n                                                            field value\n                                                          type: string\n                                                      required:\n                                                      - name\n                                                      - value\n                                                      type: object\n                                                    type: array\n                                                  path:\n                                                    description: Path to access on\n                                                      the HTTP server.\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Name or number of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                  scheme:\n                                                    description: Scheme to use for\n                                                      connecting to the host. Defaults\n                                                      to HTTP.\n                                                    type: string\n                                                required:\n                                                - port\n                                                type: object\n                                              initialDelaySeconds:\n                                                description: 'Number of seconds after\n                                                  the container has started before\n                                                  liveness probes are initiated. More\n                                                  info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                              periodSeconds:\n                                                description: How often (in seconds)\n                                                  to perform the probe. Default to\n                                                  10 seconds. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              successThreshold:\n                                                description: Minimum consecutive successes\n                                                  for the probe to be considered successful\n                                                  after having failed. Defaults to\n                                                  1. Must be 1 for liveness and startup.\n                                                  Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              tcpSocket:\n                                                description: 'TCPSocket specifies\n                                                  an action involving a TCP port.\n                                                  TCP hooks not yet supported TODO:\n                                                  implement a realistic TCP lifecycle\n                                                  hook'\n                                                properties:\n                                                  host:\n                                                    description: 'Optional: Host name\n                                                      to connect to, defaults to the\n                                                      pod IP.'\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Number or name of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                required:\n                                                - port\n                                                type: object\n                                              timeoutSeconds:\n                                                description: 'Number of seconds after\n                                                  which the probe times out. Defaults\n                                                  to 1 second. Minimum value is 1.\n                                                  More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                            type: object\n                                          stdin:\n                                            description: Whether this container should\n                                              allocate a buffer for stdin in the container\n                                              runtime. If this is not set, reads from\n                                              stdin in the container will always result\n                                              in EOF. Default is false.\n                                            type: boolean\n                                          stdinOnce:\n                                            description: Whether the container runtime\n                                              should close the stdin channel after\n                                              it has been opened by a single attach.\n                                              When stdin is true the stdin stream\n                                              will remain open across multiple attach\n                                              sessions. If stdinOnce is set to true,\n                                              stdin is opened on container start,\n                                              is empty until the first client attaches\n                                              to stdin, and then remains open and\n                                              accepts data until the client disconnects,\n                                              at which time stdin is closed and remains\n                                              closed until the container is restarted.\n                                              If this flag is false, a container processes\n                                              that reads from stdin will never receive\n                                              an EOF. Default is false\n                                            type: boolean\n                                          terminationMessagePath:\n                                            description: 'Optional: Path at which\n                                              the file to which the container''s termination\n                                              message will be written is mounted into\n                                              the container''s filesystem. Message\n                                              written is intended to be brief final\n                                              status, such as an assertion failure\n                                              message. Will be truncated by the node\n                                              if greater than 4096 bytes. The total\n                                              message length across all containers\n                                              will be limited to 12kb. Defaults to\n                                              /dev/termination-log. Cannot be updated.'\n                                            type: string\n                                          terminationMessagePolicy:\n                                            description: Indicate how the termination\n                                              message should be populated. File will\n                                              use the contents of terminationMessagePath\n                                              to populate the container status message\n                                              on both success and failure. FallbackToLogsOnError\n                                              will use the last chunk of container\n                                              log output if the termination message\n                                              file is empty and the container exited\n                                              with an error. The log output is limited\n                                              to 2048 bytes or 80 lines, whichever\n                                              is smaller. Defaults to File. Cannot\n                                              be updated.\n                                            type: string\n                                          tty:\n                                            description: Whether this container should\n                                              allocate a TTY for itself, also requires\n                                              'stdin' to be true. Default is false.\n                                            type: boolean\n                                          volumeDevices:\n                                            description: volumeDevices is the list\n                                              of block devices to be used by the container.\n                                            items:\n                                              description: volumeDevice describes\n                                                a mapping of a raw block device within\n                                                a container.\n                                              properties:\n                                                devicePath:\n                                                  description: devicePath is the path\n                                                    inside of the container that the\n                                                    device will be mapped to.\n                                                  type: string\n                                                name:\n                                                  description: name must match the\n                                                    name of a persistentVolumeClaim\n                                                    in the pod\n                                                  type: string\n                                              required:\n                                              - devicePath\n                                              - name\n                                              type: object\n                                            type: array\n                                          volumeMounts:\n                                            description: Pod volumes to mount into\n                                              the container's filesystem. Cannot be\n                                              updated.\n                                            items:\n                                              description: VolumeMount describes a\n                                                mounting of a Volume within a container.\n                                              properties:\n                                                mountPath:\n                                                  description: Path within the container\n                                                    at which the volume should be\n                                                    mounted.  Must not contain ':'.\n                                                  type: string\n                                                mountPropagation:\n                                                  description: mountPropagation determines\n                                                    how mounts are propagated from\n                                                    the host to container and the\n                                                    other way around. When not set,\n                                                    MountPropagationNone is used.\n                                                    This field is beta in 1.10.\n                                                  type: string\n                                                name:\n                                                  description: This must match the\n                                                    Name of a Volume.\n                                                  type: string\n                                                readOnly:\n                                                  description: Mounted read-only if\n                                                    true, read-write otherwise (false\n                                                    or unspecified). Defaults to false.\n                                                  type: boolean\n                                                subPath:\n                                                  description: Path within the volume\n                                                    from which the container's volume\n                                                    should be mounted. Defaults to\n                                                    \"\" (volume's root).\n                                                  type: string\n                                                subPathExpr:\n                                                  description: Expanded path within\n                                                    the volume from which the container's\n                                                    volume should be mounted. Behaves\n                                                    similarly to SubPath but environment\n                                                    variable references $(VAR_NAME)\n                                                    are expanded using the container's\n                                                    environment. Defaults to \"\" (volume's\n                                                    root). SubPathExpr and SubPath\n                                                    are mutually exclusive.\n                                                  type: string\n                                              required:\n                                              - mountPath\n                                              - name\n                                              type: object\n                                            type: array\n                                          workingDir:\n                                            description: Container's working directory.\n                                              If not specified, the container runtime's\n                                              default will be used, which might be\n                                              configured in the container image. Cannot\n                                              be updated.\n                                            type: string\n                                        required:\n                                        - name\n                                        type: object\n                                      type: array\n                                    dnsConfig:\n                                      description: Specifies the DNS parameters of\n                                        a pod. Parameters specified here will be merged\n                                        to the generated DNS configuration based on\n                                        DNSPolicy.\n                                      properties:\n                                        nameservers:\n                                          description: A list of DNS name server IP\n                                            addresses. This will be appended to the\n                                            base nameservers generated from DNSPolicy.\n                                            Duplicated nameservers will be removed.\n                                          items:\n                                            type: string\n                                          type: array\n                                        options:\n                                          description: A list of DNS resolver options.\n                                            This will be merged with the base options\n                                            generated from DNSPolicy. Duplicated entries\n                                            will be removed. Resolution options given\n                                            in Options will override those that appear\n                                            in the base DNSPolicy.\n                                          items:\n                                            description: PodDNSConfigOption defines\n                                              DNS resolver options of a pod.\n                                            properties:\n                                              name:\n                                                description: Required.\n                                                type: string\n                                              value:\n                                                type: string\n                                            type: object\n                                          type: array\n                                        searches:\n                                          description: A list of DNS search domains\n                                            for host-name lookup. This will be appended\n                                            to the base search paths generated from\n                                            DNSPolicy. Duplicated search paths will\n                                            be removed.\n                                          items:\n                                            type: string\n                                          type: array\n                                      type: object\n                                    dnsPolicy:\n                                      description: Set DNS policy for the pod. Defaults\n                                        to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet',\n                                        'ClusterFirst', 'Default' or 'None'. DNS parameters\n                                        given in DNSConfig will be merged with the\n                                        policy selected with DNSPolicy. To have DNS\n                                        options set along with hostNetwork, you have\n                                        to specify DNS policy explicitly to 'ClusterFirstWithHostNet'.\n                                      type: string\n                                    enableServiceLinks:\n                                      description: 'EnableServiceLinks indicates whether\n                                        information about services should be injected\n                                        into pod''s environment variables, matching\n                                        the syntax of Docker links. Optional: Defaults\n                                        to true.'\n                                      type: boolean\n                                    ephemeralContainers:\n                                      description: List of ephemeral containers run\n                                        in this pod. Ephemeral containers may be run\n                                        in an existing pod to perform user-initiated\n                                        actions such as debugging. This list cannot\n                                        be specified when creating a pod, and it cannot\n                                        be modified by updating the pod spec. In order\n                                        to add an ephemeral container to an existing\n                                        pod, use the pod's ephemeralcontainers subresource.\n                                        This field is alpha-level and is only honored\n                                        by servers that enable the EphemeralContainers\n                                        feature.\n                                      items:\n                                        description: An EphemeralContainer is a container\n                                          that may be added temporarily to an existing\n                                          pod for user-initiated activities such as\n                                          debugging. Ephemeral containers have no\n                                          resource or scheduling guarantees, and they\n                                          will not be restarted when they exit or\n                                          when a pod is removed or restarted. If an\n                                          ephemeral container causes a pod to exceed\n                                          its resource allocation, the pod may be\n                                          evicted. Ephemeral containers may not be\n                                          added by directly updating the pod spec.\n                                          They must be added via the pod's ephemeralcontainers\n                                          subresource, and they will appear in the\n                                          pod spec once added. This is an alpha feature\n                                          enabled by the EphemeralContainers feature\n                                          flag.\n                                        properties:\n                                          args:\n                                            description: 'Arguments to the entrypoint.\n                                              The docker image''s CMD is used if this\n                                              is not provided. Variable references\n                                              $(VAR_NAME) are expanded using the container''s\n                                              environment. If a variable cannot be\n                                              resolved, the reference in the input\n                                              string will be unchanged. The $(VAR_NAME)\n                                              syntax can be escaped with a double\n                                              $$, ie: $$(VAR_NAME). Escaped references\n                                              will never be expanded, regardless of\n                                              whether the variable exists or not.\n                                              Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'\n                                            items:\n                                              type: string\n                                            type: array\n                                          command:\n                                            description: 'Entrypoint array. Not executed\n                                              within a shell. The docker image''s\n                                              ENTRYPOINT is used if this is not provided.\n                                              Variable references $(VAR_NAME) are\n                                              expanded using the container''s environment.\n                                              If a variable cannot be resolved, the\n                                              reference in the input string will be\n                                              unchanged. The $(VAR_NAME) syntax can\n                                              be escaped with a double $$, ie: $$(VAR_NAME).\n                                              Escaped references will never be expanded,\n                                              regardless of whether the variable exists\n                                              or not. Cannot be updated. More info:\n                                              https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'\n                                            items:\n                                              type: string\n                                            type: array\n                                          env:\n                                            description: List of environment variables\n                                              to set in the container. Cannot be updated.\n                                            items:\n                                              description: EnvVar represents an environment\n                                                variable present in a Container.\n                                              properties:\n                                                name:\n                                                  description: Name of the environment\n                                                    variable. Must be a C_IDENTIFIER.\n                                                  type: string\n                                                value:\n                                                  description: 'Variable references\n                                                    $(VAR_NAME) are expanded using\n                                                    the previous defined environment\n                                                    variables in the container and\n                                                    any service environment variables.\n                                                    If a variable cannot be resolved,\n                                                    the reference in the input string\n                                                    will be unchanged. The $(VAR_NAME)\n                                                    syntax can be escaped with a double\n                                                    $$, ie: $$(VAR_NAME). Escaped\n                                                    references will never be expanded,\n                                                    regardless of whether the variable\n                                                    exists or not. Defaults to \"\".'\n                                                  type: string\n                                                valueFrom:\n                                                  description: Source for the environment\n                                                    variable's value. Cannot be used\n                                                    if value is not empty.\n                                                  properties:\n                                                    configMapKeyRef:\n                                                      description: Selects a key of\n                                                        a ConfigMap.\n                                                      properties:\n                                                        key:\n                                                          description: The key to\n                                                            select.\n                                                          type: string\n                                                        name:\n                                                          description: 'Name of the\n                                                            referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                            TODO: Add other useful\n                                                            fields. apiVersion, kind,\n                                                            uid?'\n                                                          type: string\n                                                        optional:\n                                                          description: Specify whether\n                                                            the ConfigMap or its key\n                                                            must be defined\n                                                          type: boolean\n                                                      required:\n                                                      - key\n                                                      type: object\n                                                    fieldRef:\n                                                      description: 'Selects a field\n                                                        of the pod: supports metadata.name,\n                                                        metadata.namespace, `metadata.labels[''<KEY>'']`,\n                                                        `metadata.annotations[''<KEY>'']`,\n                                                        spec.nodeName, spec.serviceAccountName,\n                                                        status.hostIP, status.podIP,\n                                                        status.podIPs.'\n                                                      properties:\n                                                        apiVersion:\n                                                          description: Version of\n                                                            the schema the FieldPath\n                                                            is written in terms of,\n                                                            defaults to \"v1\".\n                                                          type: string\n                                                        fieldPath:\n                                                          description: Path of the\n                                                            field to select in the\n                                                            specified API version.\n                                                          type: string\n                                                      required:\n                                                      - fieldPath\n                                                      type: object\n                                                    resourceFieldRef:\n                                                      description: 'Selects a resource\n                                                        of the container: only resources\n                                                        limits and requests (limits.cpu,\n                                                        limits.memory, limits.ephemeral-storage,\n                                                        requests.cpu, requests.memory\n                                                        and requests.ephemeral-storage)\n                                                        are currently supported.'\n                                                      properties:\n                                                        containerName:\n                                                          description: 'Container\n                                                            name: required for volumes,\n                                                            optional for env vars'\n                                                          type: string\n                                                        divisor:\n                                                          anyOf:\n                                                          - type: integer\n                                                          - type: string\n                                                          description: Specifies the\n                                                            output format of the exposed\n                                                            resources, defaults to\n                                                            \"1\"\n                                                          pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                          x-kubernetes-int-or-string: true\n                                                        resource:\n                                                          description: 'Required:\n                                                            resource to select'\n                                                          type: string\n                                                      required:\n                                                      - resource\n                                                      type: object\n                                                    secretKeyRef:\n                                                      description: Selects a key of\n                                                        a secret in the pod's namespace\n                                                      properties:\n                                                        key:\n                                                          description: The key of\n                                                            the secret to select from.  Must\n                                                            be a valid secret key.\n                                                          type: string\n                                                        name:\n                                                          description: 'Name of the\n                                                            referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                            TODO: Add other useful\n                                                            fields. apiVersion, kind,\n                                                            uid?'\n                                                          type: string\n                                                        optional:\n                                                          description: Specify whether\n                                                            the Secret or its key\n                                                            must be defined\n                                                          type: boolean\n                                                      required:\n                                                      - key\n                                                      type: object\n                                                  type: object\n                                              required:\n                                              - name\n                                              type: object\n                                            type: array\n                                          envFrom:\n                                            description: List of sources to populate\n                                              environment variables in the container.\n                                              The keys defined within a source must\n                                              be a C_IDENTIFIER. All invalid keys\n                                              will be reported as an event when the\n                                              container is starting. When a key exists\n                                              in multiple sources, the value associated\n                                              with the last source will take precedence.\n                                              Values defined by an Env with a duplicate\n                                              key will take precedence. Cannot be\n                                              updated.\n                                            items:\n                                              description: EnvFromSource represents\n                                                the source of a set of ConfigMaps\n                                              properties:\n                                                configMapRef:\n                                                  description: The ConfigMap to select\n                                                    from\n                                                  properties:\n                                                    name:\n                                                      description: 'Name of the referent.\n                                                        More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                        TODO: Add other useful fields.\n                                                        apiVersion, kind, uid?'\n                                                      type: string\n                                                    optional:\n                                                      description: Specify whether\n                                                        the ConfigMap must be defined\n                                                      type: boolean\n                                                  type: object\n                                                prefix:\n                                                  description: An optional identifier\n                                                    to prepend to each key in the\n                                                    ConfigMap. Must be a C_IDENTIFIER.\n                                                  type: string\n                                                secretRef:\n                                                  description: The Secret to select\n                                                    from\n                                                  properties:\n                                                    name:\n                                                      description: 'Name of the referent.\n                                                        More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                        TODO: Add other useful fields.\n                                                        apiVersion, kind, uid?'\n                                                      type: string\n                                                    optional:\n                                                      description: Specify whether\n                                                        the Secret must be defined\n                                                      type: boolean\n                                                  type: object\n                                              type: object\n                                            type: array\n                                          image:\n                                            description: 'Docker image name. More\n                                              info: https://kubernetes.io/docs/concepts/containers/images'\n                                            type: string\n                                          imagePullPolicy:\n                                            description: 'Image pull policy. One of\n                                              Always, Never, IfNotPresent. Defaults\n                                              to Always if :latest tag is specified,\n                                              or IfNotPresent otherwise. Cannot be\n                                              updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'\n                                            type: string\n                                          lifecycle:\n                                            description: Lifecycle is not allowed\n                                              for ephemeral containers.\n                                            properties:\n                                              postStart:\n                                                description: 'PostStart is called\n                                                  immediately after a container is\n                                                  created. If the handler fails, the\n                                                  container is terminated and restarted\n                                                  according to its restart policy.\n                                                  Other management of the container\n                                                  blocks until the hook completes.\n                                                  More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'\n                                                properties:\n                                                  exec:\n                                                    description: One and only one\n                                                      of the following should be specified.\n                                                      Exec specifies the action to\n                                                      take.\n                                                    properties:\n                                                      command:\n                                                        description: Command is the\n                                                          command line to execute\n                                                          inside the container, the\n                                                          working directory for the\n                                                          command  is root ('/') in\n                                                          the container's filesystem.\n                                                          The command is simply exec'd,\n                                                          it is not run inside a shell,\n                                                          so traditional shell instructions\n                                                          ('|', etc) won't work. To\n                                                          use a shell, you need to\n                                                          explicitly call out to that\n                                                          shell. Exit status of 0\n                                                          is treated as live/healthy\n                                                          and non-zero is unhealthy.\n                                                        items:\n                                                          type: string\n                                                        type: array\n                                                    type: object\n                                                  httpGet:\n                                                    description: HTTPGet specifies\n                                                      the http request to perform.\n                                                    properties:\n                                                      host:\n                                                        description: Host name to\n                                                          connect to, defaults to\n                                                          the pod IP. You probably\n                                                          want to set \"Host\" in httpHeaders\n                                                          instead.\n                                                        type: string\n                                                      httpHeaders:\n                                                        description: Custom headers\n                                                          to set in the request. HTTP\n                                                          allows repeated headers.\n                                                        items:\n                                                          description: HTTPHeader\n                                                            describes a custom header\n                                                            to be used in HTTP probes\n                                                          properties:\n                                                            name:\n                                                              description: The header\n                                                                field name\n                                                              type: string\n                                                            value:\n                                                              description: The header\n                                                                field value\n                                                              type: string\n                                                          required:\n                                                          - name\n                                                          - value\n                                                          type: object\n                                                        type: array\n                                                      path:\n                                                        description: Path to access\n                                                          on the HTTP server.\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Name or number\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                      scheme:\n                                                        description: Scheme to use\n                                                          for connecting to the host.\n                                                          Defaults to HTTP.\n                                                        type: string\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                  tcpSocket:\n                                                    description: 'TCPSocket specifies\n                                                      an action involving a TCP port.\n                                                      TCP hooks not yet supported\n                                                      TODO: implement a realistic\n                                                      TCP lifecycle hook'\n                                                    properties:\n                                                      host:\n                                                        description: 'Optional: Host\n                                                          name to connect to, defaults\n                                                          to the pod IP.'\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Number or name\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                type: object\n                                              preStop:\n                                                description: 'PreStop is called immediately\n                                                  before a container is terminated\n                                                  due to an API request or management\n                                                  event such as liveness/startup probe\n                                                  failure, preemption, resource contention,\n                                                  etc. The handler is not called if\n                                                  the container crashes or exits.\n                                                  The reason for termination is passed\n                                                  to the handler. The Pod''s termination\n                                                  grace period countdown begins before\n                                                  the PreStop hooked is executed.\n                                                  Regardless of the outcome of the\n                                                  handler, the container will eventually\n                                                  terminate within the Pod''s termination\n                                                  grace period. Other management of\n                                                  the container blocks until the hook\n                                                  completes or until the termination\n                                                  grace period is reached. More info:\n                                                  https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'\n                                                properties:\n                                                  exec:\n                                                    description: One and only one\n                                                      of the following should be specified.\n                                                      Exec specifies the action to\n                                                      take.\n                                                    properties:\n                                                      command:\n                                                        description: Command is the\n                                                          command line to execute\n                                                          inside the container, the\n                                                          working directory for the\n                                                          command  is root ('/') in\n                                                          the container's filesystem.\n                                                          The command is simply exec'd,\n                                                          it is not run inside a shell,\n                                                          so traditional shell instructions\n                                                          ('|', etc) won't work. To\n                                                          use a shell, you need to\n                                                          explicitly call out to that\n                                                          shell. Exit status of 0\n                                                          is treated as live/healthy\n                                                          and non-zero is unhealthy.\n                                                        items:\n                                                          type: string\n                                                        type: array\n                                                    type: object\n                                                  httpGet:\n                                                    description: HTTPGet specifies\n                                                      the http request to perform.\n                                                    properties:\n                                                      host:\n                                                        description: Host name to\n                                                          connect to, defaults to\n                                                          the pod IP. You probably\n                                                          want to set \"Host\" in httpHeaders\n                                                          instead.\n                                                        type: string\n                                                      httpHeaders:\n                                                        description: Custom headers\n                                                          to set in the request. HTTP\n                                                          allows repeated headers.\n                                                        items:\n                                                          description: HTTPHeader\n                                                            describes a custom header\n                                                            to be used in HTTP probes\n                                                          properties:\n                                                            name:\n                                                              description: The header\n                                                                field name\n                                                              type: string\n                                                            value:\n                                                              description: The header\n                                                                field value\n                                                              type: string\n                                                          required:\n                                                          - name\n                                                          - value\n                                                          type: object\n                                                        type: array\n                                                      path:\n                                                        description: Path to access\n                                                          on the HTTP server.\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Name or number\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                      scheme:\n                                                        description: Scheme to use\n                                                          for connecting to the host.\n                                                          Defaults to HTTP.\n                                                        type: string\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                  tcpSocket:\n                                                    description: 'TCPSocket specifies\n                                                      an action involving a TCP port.\n                                                      TCP hooks not yet supported\n                                                      TODO: implement a realistic\n                                                      TCP lifecycle hook'\n                                                    properties:\n                                                      host:\n                                                        description: 'Optional: Host\n                                                          name to connect to, defaults\n                                                          to the pod IP.'\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Number or name\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                type: object\n                                            type: object\n                                          livenessProbe:\n                                            description: Probes are not allowed for\n                                              ephemeral containers.\n                                            properties:\n                                              exec:\n                                                description: One and only one of the\n                                                  following should be specified. Exec\n                                                  specifies the action to take.\n                                                properties:\n                                                  command:\n                                                    description: Command is the command\n                                                      line to execute inside the container,\n                                                      the working directory for the\n                                                      command  is root ('/') in the\n                                                      container's filesystem. The\n                                                      command is simply exec'd, it\n                                                      is not run inside a shell, so\n                                                      traditional shell instructions\n                                                      ('|', etc) won't work. To use\n                                                      a shell, you need to explicitly\n                                                      call out to that shell. Exit\n                                                      status of 0 is treated as live/healthy\n                                                      and non-zero is unhealthy.\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              failureThreshold:\n                                                description: Minimum consecutive failures\n                                                  for the probe to be considered failed\n                                                  after having succeeded. Defaults\n                                                  to 3. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              httpGet:\n                                                description: HTTPGet specifies the\n                                                  http request to perform.\n                                                properties:\n                                                  host:\n                                                    description: Host name to connect\n                                                      to, defaults to the pod IP.\n                                                      You probably want to set \"Host\"\n                                                      in httpHeaders instead.\n                                                    type: string\n                                                  httpHeaders:\n                                                    description: Custom headers to\n                                                      set in the request. HTTP allows\n                                                      repeated headers.\n                                                    items:\n                                                      description: HTTPHeader describes\n                                                        a custom header to be used\n                                                        in HTTP probes\n                                                      properties:\n                                                        name:\n                                                          description: The header\n                                                            field name\n                                                          type: string\n                                                        value:\n                                                          description: The header\n                                                            field value\n                                                          type: string\n                                                      required:\n                                                      - name\n                                                      - value\n                                                      type: object\n                                                    type: array\n                                                  path:\n                                                    description: Path to access on\n                                                      the HTTP server.\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Name or number of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                  scheme:\n                                                    description: Scheme to use for\n                                                      connecting to the host. Defaults\n                                                      to HTTP.\n                                                    type: string\n                                                required:\n                                                - port\n                                                type: object\n                                              initialDelaySeconds:\n                                                description: 'Number of seconds after\n                                                  the container has started before\n                                                  liveness probes are initiated. More\n                                                  info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                              periodSeconds:\n                                                description: How often (in seconds)\n                                                  to perform the probe. Default to\n                                                  10 seconds. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              successThreshold:\n                                                description: Minimum consecutive successes\n                                                  for the probe to be considered successful\n                                                  after having failed. Defaults to\n                                                  1. Must be 1 for liveness and startup.\n                                                  Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              tcpSocket:\n                                                description: 'TCPSocket specifies\n                                                  an action involving a TCP port.\n                                                  TCP hooks not yet supported TODO:\n                                                  implement a realistic TCP lifecycle\n                                                  hook'\n                                                properties:\n                                                  host:\n                                                    description: 'Optional: Host name\n                                                      to connect to, defaults to the\n                                                      pod IP.'\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Number or name of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                required:\n                                                - port\n                                                type: object\n                                              timeoutSeconds:\n                                                description: 'Number of seconds after\n                                                  which the probe times out. Defaults\n                                                  to 1 second. Minimum value is 1.\n                                                  More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                            type: object\n                                          name:\n                                            description: Name of the ephemeral container\n                                              specified as a DNS_LABEL. This name\n                                              must be unique among all containers,\n                                              init containers and ephemeral containers.\n                                            type: string\n                                          ports:\n                                            description: Ports are not allowed for\n                                              ephemeral containers.\n                                            items:\n                                              description: ContainerPort represents\n                                                a network port in a single container.\n                                              properties:\n                                                containerPort:\n                                                  description: Number of port to expose\n                                                    on the pod's IP address. This\n                                                    must be a valid port number, 0\n                                                    < x < 65536.\n                                                  format: int32\n                                                  type: integer\n                                                hostIP:\n                                                  description: What host IP to bind\n                                                    the external port to.\n                                                  type: string\n                                                hostPort:\n                                                  description: Number of port to expose\n                                                    on the host. If specified, this\n                                                    must be a valid port number, 0\n                                                    < x < 65536. If HostNetwork is\n                                                    specified, this must match ContainerPort.\n                                                    Most containers do not need this.\n                                                  format: int32\n                                                  type: integer\n                                                name:\n                                                  description: If specified, this\n                                                    must be an IANA_SVC_NAME and unique\n                                                    within the pod. Each named port\n                                                    in a pod must have a unique name.\n                                                    Name for the port that can be\n                                                    referred to by services.\n                                                  type: string\n                                                protocol:\n                                                  default: TCP\n                                                  description: Protocol for port.\n                                                    Must be UDP, TCP, or SCTP. Defaults\n                                                    to \"TCP\".\n                                                  type: string\n                                              required:\n                                              - containerPort\n                                              type: object\n                                            type: array\n                                          readinessProbe:\n                                            description: Probes are not allowed for\n                                              ephemeral containers.\n                                            properties:\n                                              exec:\n                                                description: One and only one of the\n                                                  following should be specified. Exec\n                                                  specifies the action to take.\n                                                properties:\n                                                  command:\n                                                    description: Command is the command\n                                                      line to execute inside the container,\n                                                      the working directory for the\n                                                      command  is root ('/') in the\n                                                      container's filesystem. The\n                                                      command is simply exec'd, it\n                                                      is not run inside a shell, so\n                                                      traditional shell instructions\n                                                      ('|', etc) won't work. To use\n                                                      a shell, you need to explicitly\n                                                      call out to that shell. Exit\n                                                      status of 0 is treated as live/healthy\n                                                      and non-zero is unhealthy.\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              failureThreshold:\n                                                description: Minimum consecutive failures\n                                                  for the probe to be considered failed\n                                                  after having succeeded. Defaults\n                                                  to 3. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              httpGet:\n                                                description: HTTPGet specifies the\n                                                  http request to perform.\n                                                properties:\n                                                  host:\n                                                    description: Host name to connect\n                                                      to, defaults to the pod IP.\n                                                      You probably want to set \"Host\"\n                                                      in httpHeaders instead.\n                                                    type: string\n                                                  httpHeaders:\n                                                    description: Custom headers to\n                                                      set in the request. HTTP allows\n                                                      repeated headers.\n                                                    items:\n                                                      description: HTTPHeader describes\n                                                        a custom header to be used\n                                                        in HTTP probes\n                                                      properties:\n                                                        name:\n                                                          description: The header\n                                                            field name\n                                                          type: string\n                                                        value:\n                                                          description: The header\n                                                            field value\n                                                          type: string\n                                                      required:\n                                                      - name\n                                                      - value\n                                                      type: object\n                                                    type: array\n                                                  path:\n                                                    description: Path to access on\n                                                      the HTTP server.\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Name or number of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                  scheme:\n                                                    description: Scheme to use for\n                                                      connecting to the host. Defaults\n                                                      to HTTP.\n                                                    type: string\n                                                required:\n                                                - port\n                                                type: object\n                                              initialDelaySeconds:\n                                                description: 'Number of seconds after\n                                                  the container has started before\n                                                  liveness probes are initiated. More\n                                                  info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                              periodSeconds:\n                                                description: How often (in seconds)\n                                                  to perform the probe. Default to\n                                                  10 seconds. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              successThreshold:\n                                                description: Minimum consecutive successes\n                                                  for the probe to be considered successful\n                                                  after having failed. Defaults to\n                                                  1. Must be 1 for liveness and startup.\n                                                  Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              tcpSocket:\n                                                description: 'TCPSocket specifies\n                                                  an action involving a TCP port.\n                                                  TCP hooks not yet supported TODO:\n                                                  implement a realistic TCP lifecycle\n                                                  hook'\n                                                properties:\n                                                  host:\n                                                    description: 'Optional: Host name\n                                                      to connect to, defaults to the\n                                                      pod IP.'\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Number or name of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                required:\n                                                - port\n                                                type: object\n                                              timeoutSeconds:\n                                                description: 'Number of seconds after\n                                                  which the probe times out. Defaults\n                                                  to 1 second. Minimum value is 1.\n                                                  More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                            type: object\n                                          resources:\n                                            description: Resources are not allowed\n                                              for ephemeral containers. Ephemeral\n                                              containers use spare resources already\n                                              allocated to the pod.\n                                            properties:\n                                              limits:\n                                                additionalProperties:\n                                                  anyOf:\n                                                  - type: integer\n                                                  - type: string\n                                                  pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                  x-kubernetes-int-or-string: true\n                                                description: 'Limits describes the\n                                                  maximum amount of compute resources\n                                                  allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'\n                                                type: object\n                                              requests:\n                                                additionalProperties:\n                                                  anyOf:\n                                                  - type: integer\n                                                  - type: string\n                                                  pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                  x-kubernetes-int-or-string: true\n                                                description: 'Requests describes the\n                                                  minimum amount of compute resources\n                                                  required. If Requests is omitted\n                                                  for a container, it defaults to\n                                                  Limits if that is explicitly specified,\n                                                  otherwise to an implementation-defined\n                                                  value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'\n                                                type: object\n                                            type: object\n                                          securityContext:\n                                            description: SecurityContext is not allowed\n                                              for ephemeral containers.\n                                            properties:\n                                              allowPrivilegeEscalation:\n                                                description: 'AllowPrivilegeEscalation\n                                                  controls whether a process can gain\n                                                  more privileges than its parent\n                                                  process. This bool directly controls\n                                                  if the no_new_privs flag will be\n                                                  set on the container process. AllowPrivilegeEscalation\n                                                  is true always when the container\n                                                  is: 1) run as Privileged 2) has\n                                                  CAP_SYS_ADMIN'\n                                                type: boolean\n                                              capabilities:\n                                                description: The capabilities to add/drop\n                                                  when running containers. Defaults\n                                                  to the default set of capabilities\n                                                  granted by the container runtime.\n                                                properties:\n                                                  add:\n                                                    description: Added capabilities\n                                                    items:\n                                                      description: Capability represent\n                                                        POSIX capabilities type\n                                                      type: string\n                                                    type: array\n                                                  drop:\n                                                    description: Removed capabilities\n                                                    items:\n                                                      description: Capability represent\n                                                        POSIX capabilities type\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              privileged:\n                                                description: Run container in privileged\n                                                  mode. Processes in privileged containers\n                                                  are essentially equivalent to root\n                                                  on the host. Defaults to false.\n                                                type: boolean\n                                              procMount:\n                                                description: procMount denotes the\n                                                  type of proc mount to use for the\n                                                  containers. The default is DefaultProcMount\n                                                  which uses the container runtime\n                                                  defaults for readonly paths and\n                                                  masked paths. This requires the\n                                                  ProcMountType feature flag to be\n                                                  enabled.\n                                                type: string\n                                              readOnlyRootFilesystem:\n                                                description: Whether this container\n                                                  has a read-only root filesystem.\n                                                  Default is false.\n                                                type: boolean\n                                              runAsGroup:\n                                                description: The GID to run the entrypoint\n                                                  of the container process. Uses runtime\n                                                  default if unset. May also be set\n                                                  in PodSecurityContext.  If set in\n                                                  both SecurityContext and PodSecurityContext,\n                                                  the value specified in SecurityContext\n                                                  takes precedence.\n                                                format: int64\n                                                type: integer\n                                              runAsNonRoot:\n                                                description: Indicates that the container\n                                                  must run as a non-root user. If\n                                                  true, the Kubelet will validate\n                                                  the image at runtime to ensure that\n                                                  it does not run as UID 0 (root)\n                                                  and fail to start the container\n                                                  if it does. If unset or false, no\n                                                  such validation will be performed.\n                                                  May also be set in PodSecurityContext.  If\n                                                  set in both SecurityContext and\n                                                  PodSecurityContext, the value specified\n                                                  in SecurityContext takes precedence.\n                                                type: boolean\n                                              runAsUser:\n                                                description: The UID to run the entrypoint\n                                                  of the container process. Defaults\n                                                  to user specified in image metadata\n                                                  if unspecified. May also be set\n                                                  in PodSecurityContext.  If set in\n                                                  both SecurityContext and PodSecurityContext,\n                                                  the value specified in SecurityContext\n                                                  takes precedence.\n                                                format: int64\n                                                type: integer\n                                              seLinuxOptions:\n                                                description: The SELinux context to\n                                                  be applied to the container. If\n                                                  unspecified, the container runtime\n                                                  will allocate a random SELinux context\n                                                  for each container.  May also be\n                                                  set in PodSecurityContext.  If set\n                                                  in both SecurityContext and PodSecurityContext,\n                                                  the value specified in SecurityContext\n                                                  takes precedence.\n                                                properties:\n                                                  level:\n                                                    description: Level is SELinux\n                                                      level label that applies to\n                                                      the container.\n                                                    type: string\n                                                  role:\n                                                    description: Role is a SELinux\n                                                      role label that applies to the\n                                                      container.\n                                                    type: string\n                                                  type:\n                                                    description: Type is a SELinux\n                                                      type label that applies to the\n                                                      container.\n                                                    type: string\n                                                  user:\n                                                    description: User is a SELinux\n                                                      user label that applies to the\n                                                      container.\n                                                    type: string\n                                                type: object\n                                              seccompProfile:\n                                                description: The seccomp options to\n                                                  use by this container. If seccomp\n                                                  options are provided at both the\n                                                  pod & container level, the container\n                                                  options override the pod options.\n                                                properties:\n                                                  localhostProfile:\n                                                    description: localhostProfile\n                                                      indicates a profile defined\n                                                      in a file on the node should\n                                                      be used. The profile must be\n                                                      preconfigured on the node to\n                                                      work. Must be a descending path,\n                                                      relative to the kubelet's configured\n                                                      seccomp profile location. Must\n                                                      only be set if type is \"Localhost\".\n                                                    type: string\n                                                  type:\n                                                    description: \"type indicates which\n                                                      kind of seccomp profile will\n                                                      be applied. Valid options are:\n                                                      \\n Localhost - a profile defined\n                                                      in a file on the node should\n                                                      be used. RuntimeDefault - the\n                                                      container runtime default profile\n                                                      should be used. Unconfined -\n                                                      no profile should be applied.\"\n                                                    type: string\n                                                required:\n                                                - type\n                                                type: object\n                                              windowsOptions:\n                                                description: The Windows specific\n                                                  settings applied to all containers.\n                                                  If unspecified, the options from\n                                                  the PodSecurityContext will be used.\n                                                  If set in both SecurityContext and\n                                                  PodSecurityContext, the value specified\n                                                  in SecurityContext takes precedence.\n                                                properties:\n                                                  gmsaCredentialSpec:\n                                                    description: GMSACredentialSpec\n                                                      is where the GMSA admission\n                                                      webhook (https://github.com/kubernetes-sigs/windows-gmsa)\n                                                      inlines the contents of the\n                                                      GMSA credential spec named by\n                                                      the GMSACredentialSpecName field.\n                                                    type: string\n                                                  gmsaCredentialSpecName:\n                                                    description: GMSACredentialSpecName\n                                                      is the name of the GMSA credential\n                                                      spec to use.\n                                                    type: string\n                                                  runAsUserName:\n                                                    description: The UserName in Windows\n                                                      to run the entrypoint of the\n                                                      container process. Defaults\n                                                      to the user specified in image\n                                                      metadata if unspecified. May\n                                                      also be set in PodSecurityContext.\n                                                      If set in both SecurityContext\n                                                      and PodSecurityContext, the\n                                                      value specified in SecurityContext\n                                                      takes precedence.\n                                                    type: string\n                                                type: object\n                                            type: object\n                                          startupProbe:\n                                            description: Probes are not allowed for\n                                              ephemeral containers.\n                                            properties:\n                                              exec:\n                                                description: One and only one of the\n                                                  following should be specified. Exec\n                                                  specifies the action to take.\n                                                properties:\n                                                  command:\n                                                    description: Command is the command\n                                                      line to execute inside the container,\n                                                      the working directory for the\n                                                      command  is root ('/') in the\n                                                      container's filesystem. The\n                                                      command is simply exec'd, it\n                                                      is not run inside a shell, so\n                                                      traditional shell instructions\n                                                      ('|', etc) won't work. To use\n                                                      a shell, you need to explicitly\n                                                      call out to that shell. Exit\n                                                      status of 0 is treated as live/healthy\n                                                      and non-zero is unhealthy.\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              failureThreshold:\n                                                description: Minimum consecutive failures\n                                                  for the probe to be considered failed\n                                                  after having succeeded. Defaults\n                                                  to 3. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              httpGet:\n                                                description: HTTPGet specifies the\n                                                  http request to perform.\n                                                properties:\n                                                  host:\n                                                    description: Host name to connect\n                                                      to, defaults to the pod IP.\n                                                      You probably want to set \"Host\"\n                                                      in httpHeaders instead.\n                                                    type: string\n                                                  httpHeaders:\n                                                    description: Custom headers to\n                                                      set in the request. HTTP allows\n                                                      repeated headers.\n                                                    items:\n                                                      description: HTTPHeader describes\n                                                        a custom header to be used\n                                                        in HTTP probes\n                                                      properties:\n                                                        name:\n                                                          description: The header\n                                                            field name\n                                                          type: string\n                                                        value:\n                                                          description: The header\n                                                            field value\n                                                          type: string\n                                                      required:\n                                                      - name\n                                                      - value\n                                                      type: object\n                                                    type: array\n                                                  path:\n                                                    description: Path to access on\n                                                      the HTTP server.\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Name or number of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                  scheme:\n                                                    description: Scheme to use for\n                                                      connecting to the host. Defaults\n                                                      to HTTP.\n                                                    type: string\n                                                required:\n                                                - port\n                                                type: object\n                                              initialDelaySeconds:\n                                                description: 'Number of seconds after\n                                                  the container has started before\n                                                  liveness probes are initiated. More\n                                                  info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                              periodSeconds:\n                                                description: How often (in seconds)\n                                                  to perform the probe. Default to\n                                                  10 seconds. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              successThreshold:\n                                                description: Minimum consecutive successes\n                                                  for the probe to be considered successful\n                                                  after having failed. Defaults to\n                                                  1. Must be 1 for liveness and startup.\n                                                  Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              tcpSocket:\n                                                description: 'TCPSocket specifies\n                                                  an action involving a TCP port.\n                                                  TCP hooks not yet supported TODO:\n                                                  implement a realistic TCP lifecycle\n                                                  hook'\n                                                properties:\n                                                  host:\n                                                    description: 'Optional: Host name\n                                                      to connect to, defaults to the\n                                                      pod IP.'\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Number or name of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                required:\n                                                - port\n                                                type: object\n                                              timeoutSeconds:\n                                                description: 'Number of seconds after\n                                                  which the probe times out. Defaults\n                                                  to 1 second. Minimum value is 1.\n                                                  More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                            type: object\n                                          stdin:\n                                            description: Whether this container should\n                                              allocate a buffer for stdin in the container\n                                              runtime. If this is not set, reads from\n                                              stdin in the container will always result\n                                              in EOF. Default is false.\n                                            type: boolean\n                                          stdinOnce:\n                                            description: Whether the container runtime\n                                              should close the stdin channel after\n                                              it has been opened by a single attach.\n                                              When stdin is true the stdin stream\n                                              will remain open across multiple attach\n                                              sessions. If stdinOnce is set to true,\n                                              stdin is opened on container start,\n                                              is empty until the first client attaches\n                                              to stdin, and then remains open and\n                                              accepts data until the client disconnects,\n                                              at which time stdin is closed and remains\n                                              closed until the container is restarted.\n                                              If this flag is false, a container processes\n                                              that reads from stdin will never receive\n                                              an EOF. Default is false\n                                            type: boolean\n                                          targetContainerName:\n                                            description: If set, the name of the container\n                                              from PodSpec that this ephemeral container\n                                              targets. The ephemeral container will\n                                              be run in the namespaces (IPC, PID,\n                                              etc) of this container. If not set then\n                                              the ephemeral container is run in whatever\n                                              namespaces are shared for the pod. Note\n                                              that the container runtime must support\n                                              this feature.\n                                            type: string\n                                          terminationMessagePath:\n                                            description: 'Optional: Path at which\n                                              the file to which the container''s termination\n                                              message will be written is mounted into\n                                              the container''s filesystem. Message\n                                              written is intended to be brief final\n                                              status, such as an assertion failure\n                                              message. Will be truncated by the node\n                                              if greater than 4096 bytes. The total\n                                              message length across all containers\n                                              will be limited to 12kb. Defaults to\n                                              /dev/termination-log. Cannot be updated.'\n                                            type: string\n                                          terminationMessagePolicy:\n                                            description: Indicate how the termination\n                                              message should be populated. File will\n                                              use the contents of terminationMessagePath\n                                              to populate the container status message\n                                              on both success and failure. FallbackToLogsOnError\n                                              will use the last chunk of container\n                                              log output if the termination message\n                                              file is empty and the container exited\n                                              with an error. The log output is limited\n                                              to 2048 bytes or 80 lines, whichever\n                                              is smaller. Defaults to File. Cannot\n                                              be updated.\n                                            type: string\n                                          tty:\n                                            description: Whether this container should\n                                              allocate a TTY for itself, also requires\n                                              'stdin' to be true. Default is false.\n                                            type: boolean\n                                          volumeDevices:\n                                            description: volumeDevices is the list\n                                              of block devices to be used by the container.\n                                            items:\n                                              description: volumeDevice describes\n                                                a mapping of a raw block device within\n                                                a container.\n                                              properties:\n                                                devicePath:\n                                                  description: devicePath is the path\n                                                    inside of the container that the\n                                                    device will be mapped to.\n                                                  type: string\n                                                name:\n                                                  description: name must match the\n                                                    name of a persistentVolumeClaim\n                                                    in the pod\n                                                  type: string\n                                              required:\n                                              - devicePath\n                                              - name\n                                              type: object\n                                            type: array\n                                          volumeMounts:\n                                            description: Pod volumes to mount into\n                                              the container's filesystem. Cannot be\n                                              updated.\n                                            items:\n                                              description: VolumeMount describes a\n                                                mounting of a Volume within a container.\n                                              properties:\n                                                mountPath:\n                                                  description: Path within the container\n                                                    at which the volume should be\n                                                    mounted.  Must not contain ':'.\n                                                  type: string\n                                                mountPropagation:\n                                                  description: mountPropagation determines\n                                                    how mounts are propagated from\n                                                    the host to container and the\n                                                    other way around. When not set,\n                                                    MountPropagationNone is used.\n                                                    This field is beta in 1.10.\n                                                  type: string\n                                                name:\n                                                  description: This must match the\n                                                    Name of a Volume.\n                                                  type: string\n                                                readOnly:\n                                                  description: Mounted read-only if\n                                                    true, read-write otherwise (false\n                                                    or unspecified). Defaults to false.\n                                                  type: boolean\n                                                subPath:\n                                                  description: Path within the volume\n                                                    from which the container's volume\n                                                    should be mounted. Defaults to\n                                                    \"\" (volume's root).\n                                                  type: string\n                                                subPathExpr:\n                                                  description: Expanded path within\n                                                    the volume from which the container's\n                                                    volume should be mounted. Behaves\n                                                    similarly to SubPath but environment\n                                                    variable references $(VAR_NAME)\n                                                    are expanded using the container's\n                                                    environment. Defaults to \"\" (volume's\n                                                    root). SubPathExpr and SubPath\n                                                    are mutually exclusive.\n                                                  type: string\n                                              required:\n                                              - mountPath\n                                              - name\n                                              type: object\n                                            type: array\n                                          workingDir:\n                                            description: Container's working directory.\n                                              If not specified, the container runtime's\n                                              default will be used, which might be\n                                              configured in the container image. Cannot\n                                              be updated.\n                                            type: string\n                                        required:\n                                        - name\n                                        type: object\n                                      type: array\n                                    hostAliases:\n                                      description: HostAliases is an optional list\n                                        of hosts and IPs that will be injected into\n                                        the pod's hosts file if specified. This is\n                                        only valid for non-hostNetwork pods.\n                                      items:\n                                        description: HostAlias holds the mapping between\n                                          IP and hostnames that will be injected as\n                                          an entry in the pod's hosts file.\n                                        properties:\n                                          hostnames:\n                                            description: Hostnames for the above IP\n                                              address.\n                                            items:\n                                              type: string\n                                            type: array\n                                          ip:\n                                            description: IP address of the host file\n                                              entry.\n                                            type: string\n                                        type: object\n                                      type: array\n                                    hostIPC:\n                                      description: 'Use the host''s ipc namespace.\n                                        Optional: Default to false.'\n                                      type: boolean\n                                    hostNetwork:\n                                      description: Host networking requested for this\n                                        pod. Use the host's network namespace. If\n                                        this option is set, the ports that will be\n                                        used must be specified. Default to false.\n                                      type: boolean\n                                    hostPID:\n                                      description: 'Use the host''s pid namespace.\n                                        Optional: Default to false.'\n                                      type: boolean\n                                    hostname:\n                                      description: Specifies the hostname of the Pod\n                                        If not specified, the pod's hostname will\n                                        be set to a system-defined value.\n                                      type: string\n                                    imagePullSecrets:\n                                      description: 'ImagePullSecrets is an optional\n                                        list of references to secrets in the same\n                                        namespace to use for pulling any of the images\n                                        used by this PodSpec. If specified, these\n                                        secrets will be passed to individual puller\n                                        implementations for them to use. For example,\n                                        in the case of docker, only DockerConfig type\n                                        secrets are honored. More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod'\n                                      items:\n                                        description: LocalObjectReference contains\n                                          enough information to let you locate the\n                                          referenced object inside the same namespace.\n                                        properties:\n                                          name:\n                                            description: 'Name of the referent. More\n                                              info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                              TODO: Add other useful fields. apiVersion,\n                                              kind, uid?'\n                                            type: string\n                                        type: object\n                                      type: array\n                                    initContainers:\n                                      description: 'List of initialization containers\n                                        belonging to the pod. Init containers are\n                                        executed in order prior to containers being\n                                        started. If any init container fails, the\n                                        pod is considered to have failed and is handled\n                                        according to its restartPolicy. The name for\n                                        an init container or normal container must\n                                        be unique among all containers. Init containers\n                                        may not have Lifecycle actions, Readiness\n                                        probes, Liveness probes, or Startup probes.\n                                        The resourceRequirements of an init container\n                                        are taken into account during scheduling by\n                                        finding the highest request/limit for each\n                                        resource type, and then using the max of of\n                                        that value or the sum of the normal containers.\n                                        Limits are applied to init containers in a\n                                        similar fashion. Init containers cannot currently\n                                        be added or removed. Cannot be updated. More\n                                        info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/'\n                                      items:\n                                        description: A single application container\n                                          that you want to run within a pod.\n                                        properties:\n                                          args:\n                                            description: 'Arguments to the entrypoint.\n                                              The docker image''s CMD is used if this\n                                              is not provided. Variable references\n                                              $(VAR_NAME) are expanded using the container''s\n                                              environment. If a variable cannot be\n                                              resolved, the reference in the input\n                                              string will be unchanged. The $(VAR_NAME)\n                                              syntax can be escaped with a double\n                                              $$, ie: $$(VAR_NAME). Escaped references\n                                              will never be expanded, regardless of\n                                              whether the variable exists or not.\n                                              Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'\n                                            items:\n                                              type: string\n                                            type: array\n                                          command:\n                                            description: 'Entrypoint array. Not executed\n                                              within a shell. The docker image''s\n                                              ENTRYPOINT is used if this is not provided.\n                                              Variable references $(VAR_NAME) are\n                                              expanded using the container''s environment.\n                                              If a variable cannot be resolved, the\n                                              reference in the input string will be\n                                              unchanged. The $(VAR_NAME) syntax can\n                                              be escaped with a double $$, ie: $$(VAR_NAME).\n                                              Escaped references will never be expanded,\n                                              regardless of whether the variable exists\n                                              or not. Cannot be updated. More info:\n                                              https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell'\n                                            items:\n                                              type: string\n                                            type: array\n                                          env:\n                                            description: List of environment variables\n                                              to set in the container. Cannot be updated.\n                                            items:\n                                              description: EnvVar represents an environment\n                                                variable present in a Container.\n                                              properties:\n                                                name:\n                                                  description: Name of the environment\n                                                    variable. Must be a C_IDENTIFIER.\n                                                  type: string\n                                                value:\n                                                  description: 'Variable references\n                                                    $(VAR_NAME) are expanded using\n                                                    the previous defined environment\n                                                    variables in the container and\n                                                    any service environment variables.\n                                                    If a variable cannot be resolved,\n                                                    the reference in the input string\n                                                    will be unchanged. The $(VAR_NAME)\n                                                    syntax can be escaped with a double\n                                                    $$, ie: $$(VAR_NAME). Escaped\n                                                    references will never be expanded,\n                                                    regardless of whether the variable\n                                                    exists or not. Defaults to \"\".'\n                                                  type: string\n                                                valueFrom:\n                                                  description: Source for the environment\n                                                    variable's value. Cannot be used\n                                                    if value is not empty.\n                                                  properties:\n                                                    configMapKeyRef:\n                                                      description: Selects a key of\n                                                        a ConfigMap.\n                                                      properties:\n                                                        key:\n                                                          description: The key to\n                                                            select.\n                                                          type: string\n                                                        name:\n                                                          description: 'Name of the\n                                                            referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                            TODO: Add other useful\n                                                            fields. apiVersion, kind,\n                                                            uid?'\n                                                          type: string\n                                                        optional:\n                                                          description: Specify whether\n                                                            the ConfigMap or its key\n                                                            must be defined\n                                                          type: boolean\n                                                      required:\n                                                      - key\n                                                      type: object\n                                                    fieldRef:\n                                                      description: 'Selects a field\n                                                        of the pod: supports metadata.name,\n                                                        metadata.namespace, `metadata.labels[''<KEY>'']`,\n                                                        `metadata.annotations[''<KEY>'']`,\n                                                        spec.nodeName, spec.serviceAccountName,\n                                                        status.hostIP, status.podIP,\n                                                        status.podIPs.'\n                                                      properties:\n                                                        apiVersion:\n                                                          description: Version of\n                                                            the schema the FieldPath\n                                                            is written in terms of,\n                                                            defaults to \"v1\".\n                                                          type: string\n                                                        fieldPath:\n                                                          description: Path of the\n                                                            field to select in the\n                                                            specified API version.\n                                                          type: string\n                                                      required:\n                                                      - fieldPath\n                                                      type: object\n                                                    resourceFieldRef:\n                                                      description: 'Selects a resource\n                                                        of the container: only resources\n                                                        limits and requests (limits.cpu,\n                                                        limits.memory, limits.ephemeral-storage,\n                                                        requests.cpu, requests.memory\n                                                        and requests.ephemeral-storage)\n                                                        are currently supported.'\n                                                      properties:\n                                                        containerName:\n                                                          description: 'Container\n                                                            name: required for volumes,\n                                                            optional for env vars'\n                                                          type: string\n                                                        divisor:\n                                                          anyOf:\n                                                          - type: integer\n                                                          - type: string\n                                                          description: Specifies the\n                                                            output format of the exposed\n                                                            resources, defaults to\n                                                            \"1\"\n                                                          pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                          x-kubernetes-int-or-string: true\n                                                        resource:\n                                                          description: 'Required:\n                                                            resource to select'\n                                                          type: string\n                                                      required:\n                                                      - resource\n                                                      type: object\n                                                    secretKeyRef:\n                                                      description: Selects a key of\n                                                        a secret in the pod's namespace\n                                                      properties:\n                                                        key:\n                                                          description: The key of\n                                                            the secret to select from.  Must\n                                                            be a valid secret key.\n                                                          type: string\n                                                        name:\n                                                          description: 'Name of the\n                                                            referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                            TODO: Add other useful\n                                                            fields. apiVersion, kind,\n                                                            uid?'\n                                                          type: string\n                                                        optional:\n                                                          description: Specify whether\n                                                            the Secret or its key\n                                                            must be defined\n                                                          type: boolean\n                                                      required:\n                                                      - key\n                                                      type: object\n                                                  type: object\n                                              required:\n                                              - name\n                                              type: object\n                                            type: array\n                                          envFrom:\n                                            description: List of sources to populate\n                                              environment variables in the container.\n                                              The keys defined within a source must\n                                              be a C_IDENTIFIER. All invalid keys\n                                              will be reported as an event when the\n                                              container is starting. When a key exists\n                                              in multiple sources, the value associated\n                                              with the last source will take precedence.\n                                              Values defined by an Env with a duplicate\n                                              key will take precedence. Cannot be\n                                              updated.\n                                            items:\n                                              description: EnvFromSource represents\n                                                the source of a set of ConfigMaps\n                                              properties:\n                                                configMapRef:\n                                                  description: The ConfigMap to select\n                                                    from\n                                                  properties:\n                                                    name:\n                                                      description: 'Name of the referent.\n                                                        More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                        TODO: Add other useful fields.\n                                                        apiVersion, kind, uid?'\n                                                      type: string\n                                                    optional:\n                                                      description: Specify whether\n                                                        the ConfigMap must be defined\n                                                      type: boolean\n                                                  type: object\n                                                prefix:\n                                                  description: An optional identifier\n                                                    to prepend to each key in the\n                                                    ConfigMap. Must be a C_IDENTIFIER.\n                                                  type: string\n                                                secretRef:\n                                                  description: The Secret to select\n                                                    from\n                                                  properties:\n                                                    name:\n                                                      description: 'Name of the referent.\n                                                        More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                        TODO: Add other useful fields.\n                                                        apiVersion, kind, uid?'\n                                                      type: string\n                                                    optional:\n                                                      description: Specify whether\n                                                        the Secret must be defined\n                                                      type: boolean\n                                                  type: object\n                                              type: object\n                                            type: array\n                                          image:\n                                            description: 'Docker image name. More\n                                              info: https://kubernetes.io/docs/concepts/containers/images\n                                              This field is optional to allow higher\n                                              level config management to default or\n                                              override container images in workload\n                                              controllers like Deployments and StatefulSets.'\n                                            type: string\n                                          imagePullPolicy:\n                                            description: 'Image pull policy. One of\n                                              Always, Never, IfNotPresent. Defaults\n                                              to Always if :latest tag is specified,\n                                              or IfNotPresent otherwise. Cannot be\n                                              updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images'\n                                            type: string\n                                          lifecycle:\n                                            description: Actions that the management\n                                              system should take in response to container\n                                              lifecycle events. Cannot be updated.\n                                            properties:\n                                              postStart:\n                                                description: 'PostStart is called\n                                                  immediately after a container is\n                                                  created. If the handler fails, the\n                                                  container is terminated and restarted\n                                                  according to its restart policy.\n                                                  Other management of the container\n                                                  blocks until the hook completes.\n                                                  More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'\n                                                properties:\n                                                  exec:\n                                                    description: One and only one\n                                                      of the following should be specified.\n                                                      Exec specifies the action to\n                                                      take.\n                                                    properties:\n                                                      command:\n                                                        description: Command is the\n                                                          command line to execute\n                                                          inside the container, the\n                                                          working directory for the\n                                                          command  is root ('/') in\n                                                          the container's filesystem.\n                                                          The command is simply exec'd,\n                                                          it is not run inside a shell,\n                                                          so traditional shell instructions\n                                                          ('|', etc) won't work. To\n                                                          use a shell, you need to\n                                                          explicitly call out to that\n                                                          shell. Exit status of 0\n                                                          is treated as live/healthy\n                                                          and non-zero is unhealthy.\n                                                        items:\n                                                          type: string\n                                                        type: array\n                                                    type: object\n                                                  httpGet:\n                                                    description: HTTPGet specifies\n                                                      the http request to perform.\n                                                    properties:\n                                                      host:\n                                                        description: Host name to\n                                                          connect to, defaults to\n                                                          the pod IP. You probably\n                                                          want to set \"Host\" in httpHeaders\n                                                          instead.\n                                                        type: string\n                                                      httpHeaders:\n                                                        description: Custom headers\n                                                          to set in the request. HTTP\n                                                          allows repeated headers.\n                                                        items:\n                                                          description: HTTPHeader\n                                                            describes a custom header\n                                                            to be used in HTTP probes\n                                                          properties:\n                                                            name:\n                                                              description: The header\n                                                                field name\n                                                              type: string\n                                                            value:\n                                                              description: The header\n                                                                field value\n                                                              type: string\n                                                          required:\n                                                          - name\n                                                          - value\n                                                          type: object\n                                                        type: array\n                                                      path:\n                                                        description: Path to access\n                                                          on the HTTP server.\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Name or number\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                      scheme:\n                                                        description: Scheme to use\n                                                          for connecting to the host.\n                                                          Defaults to HTTP.\n                                                        type: string\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                  tcpSocket:\n                                                    description: 'TCPSocket specifies\n                                                      an action involving a TCP port.\n                                                      TCP hooks not yet supported\n                                                      TODO: implement a realistic\n                                                      TCP lifecycle hook'\n                                                    properties:\n                                                      host:\n                                                        description: 'Optional: Host\n                                                          name to connect to, defaults\n                                                          to the pod IP.'\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Number or name\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                type: object\n                                              preStop:\n                                                description: 'PreStop is called immediately\n                                                  before a container is terminated\n                                                  due to an API request or management\n                                                  event such as liveness/startup probe\n                                                  failure, preemption, resource contention,\n                                                  etc. The handler is not called if\n                                                  the container crashes or exits.\n                                                  The reason for termination is passed\n                                                  to the handler. The Pod''s termination\n                                                  grace period countdown begins before\n                                                  the PreStop hooked is executed.\n                                                  Regardless of the outcome of the\n                                                  handler, the container will eventually\n                                                  terminate within the Pod''s termination\n                                                  grace period. Other management of\n                                                  the container blocks until the hook\n                                                  completes or until the termination\n                                                  grace period is reached. More info:\n                                                  https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'\n                                                properties:\n                                                  exec:\n                                                    description: One and only one\n                                                      of the following should be specified.\n                                                      Exec specifies the action to\n                                                      take.\n                                                    properties:\n                                                      command:\n                                                        description: Command is the\n                                                          command line to execute\n                                                          inside the container, the\n                                                          working directory for the\n                                                          command  is root ('/') in\n                                                          the container's filesystem.\n                                                          The command is simply exec'd,\n                                                          it is not run inside a shell,\n                                                          so traditional shell instructions\n                                                          ('|', etc) won't work. To\n                                                          use a shell, you need to\n                                                          explicitly call out to that\n                                                          shell. Exit status of 0\n                                                          is treated as live/healthy\n                                                          and non-zero is unhealthy.\n                                                        items:\n                                                          type: string\n                                                        type: array\n                                                    type: object\n                                                  httpGet:\n                                                    description: HTTPGet specifies\n                                                      the http request to perform.\n                                                    properties:\n                                                      host:\n                                                        description: Host name to\n                                                          connect to, defaults to\n                                                          the pod IP. You probably\n                                                          want to set \"Host\" in httpHeaders\n                                                          instead.\n                                                        type: string\n                                                      httpHeaders:\n                                                        description: Custom headers\n                                                          to set in the request. HTTP\n                                                          allows repeated headers.\n                                                        items:\n                                                          description: HTTPHeader\n                                                            describes a custom header\n                                                            to be used in HTTP probes\n                                                          properties:\n                                                            name:\n                                                              description: The header\n                                                                field name\n                                                              type: string\n                                                            value:\n                                                              description: The header\n                                                                field value\n                                                              type: string\n                                                          required:\n                                                          - name\n                                                          - value\n                                                          type: object\n                                                        type: array\n                                                      path:\n                                                        description: Path to access\n                                                          on the HTTP server.\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Name or number\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                      scheme:\n                                                        description: Scheme to use\n                                                          for connecting to the host.\n                                                          Defaults to HTTP.\n                                                        type: string\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                  tcpSocket:\n                                                    description: 'TCPSocket specifies\n                                                      an action involving a TCP port.\n                                                      TCP hooks not yet supported\n                                                      TODO: implement a realistic\n                                                      TCP lifecycle hook'\n                                                    properties:\n                                                      host:\n                                                        description: 'Optional: Host\n                                                          name to connect to, defaults\n                                                          to the pod IP.'\n                                                        type: string\n                                                      port:\n                                                        anyOf:\n                                                        - type: integer\n                                                        - type: string\n                                                        description: Number or name\n                                                          of the port to access on\n                                                          the container. Number must\n                                                          be in the range 1 to 65535.\n                                                          Name must be an IANA_SVC_NAME.\n                                                        x-kubernetes-int-or-string: true\n                                                    required:\n                                                    - port\n                                                    type: object\n                                                type: object\n                                            type: object\n                                          livenessProbe:\n                                            description: 'Periodic probe of container\n                                              liveness. Container will be restarted\n                                              if the probe fails. Cannot be updated.\n                                              More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                            properties:\n                                              exec:\n                                                description: One and only one of the\n                                                  following should be specified. Exec\n                                                  specifies the action to take.\n                                                properties:\n                                                  command:\n                                                    description: Command is the command\n                                                      line to execute inside the container,\n                                                      the working directory for the\n                                                      command  is root ('/') in the\n                                                      container's filesystem. The\n                                                      command is simply exec'd, it\n                                                      is not run inside a shell, so\n                                                      traditional shell instructions\n                                                      ('|', etc) won't work. To use\n                                                      a shell, you need to explicitly\n                                                      call out to that shell. Exit\n                                                      status of 0 is treated as live/healthy\n                                                      and non-zero is unhealthy.\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              failureThreshold:\n                                                description: Minimum consecutive failures\n                                                  for the probe to be considered failed\n                                                  after having succeeded. Defaults\n                                                  to 3. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              httpGet:\n                                                description: HTTPGet specifies the\n                                                  http request to perform.\n                                                properties:\n                                                  host:\n                                                    description: Host name to connect\n                                                      to, defaults to the pod IP.\n                                                      You probably want to set \"Host\"\n                                                      in httpHeaders instead.\n                                                    type: string\n                                                  httpHeaders:\n                                                    description: Custom headers to\n                                                      set in the request. HTTP allows\n                                                      repeated headers.\n                                                    items:\n                                                      description: HTTPHeader describes\n                                                        a custom header to be used\n                                                        in HTTP probes\n                                                      properties:\n                                                        name:\n                                                          description: The header\n                                                            field name\n                                                          type: string\n                                                        value:\n                                                          description: The header\n                                                            field value\n                                                          type: string\n                                                      required:\n                                                      - name\n                                                      - value\n                                                      type: object\n                                                    type: array\n                                                  path:\n                                                    description: Path to access on\n                                                      the HTTP server.\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Name or number of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                  scheme:\n                                                    description: Scheme to use for\n                                                      connecting to the host. Defaults\n                                                      to HTTP.\n                                                    type: string\n                                                required:\n                                                - port\n                                                type: object\n                                              initialDelaySeconds:\n                                                description: 'Number of seconds after\n                                                  the container has started before\n                                                  liveness probes are initiated. More\n                                                  info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                              periodSeconds:\n                                                description: How often (in seconds)\n                                                  to perform the probe. Default to\n                                                  10 seconds. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              successThreshold:\n                                                description: Minimum consecutive successes\n                                                  for the probe to be considered successful\n                                                  after having failed. Defaults to\n                                                  1. Must be 1 for liveness and startup.\n                                                  Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              tcpSocket:\n                                                description: 'TCPSocket specifies\n                                                  an action involving a TCP port.\n                                                  TCP hooks not yet supported TODO:\n                                                  implement a realistic TCP lifecycle\n                                                  hook'\n                                                properties:\n                                                  host:\n                                                    description: 'Optional: Host name\n                                                      to connect to, defaults to the\n                                                      pod IP.'\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Number or name of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                required:\n                                                - port\n                                                type: object\n                                              timeoutSeconds:\n                                                description: 'Number of seconds after\n                                                  which the probe times out. Defaults\n                                                  to 1 second. Minimum value is 1.\n                                                  More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                            type: object\n                                          name:\n                                            description: Name of the container specified\n                                              as a DNS_LABEL. Each container in a\n                                              pod must have a unique name (DNS_LABEL).\n                                              Cannot be updated.\n                                            type: string\n                                          ports:\n                                            description: List of ports to expose from\n                                              the container. Exposing a port here\n                                              gives the system additional information\n                                              about the network connections a container\n                                              uses, but is primarily informational.\n                                              Not specifying a port here DOES NOT\n                                              prevent that port from being exposed.\n                                              Any port which is listening on the default\n                                              \"0.0.0.0\" address inside a container\n                                              will be accessible from the network.\n                                              Cannot be updated.\n                                            items:\n                                              description: ContainerPort represents\n                                                a network port in a single container.\n                                              properties:\n                                                containerPort:\n                                                  description: Number of port to expose\n                                                    on the pod's IP address. This\n                                                    must be a valid port number, 0\n                                                    < x < 65536.\n                                                  format: int32\n                                                  type: integer\n                                                hostIP:\n                                                  description: What host IP to bind\n                                                    the external port to.\n                                                  type: string\n                                                hostPort:\n                                                  description: Number of port to expose\n                                                    on the host. If specified, this\n                                                    must be a valid port number, 0\n                                                    < x < 65536. If HostNetwork is\n                                                    specified, this must match ContainerPort.\n                                                    Most containers do not need this.\n                                                  format: int32\n                                                  type: integer\n                                                name:\n                                                  description: If specified, this\n                                                    must be an IANA_SVC_NAME and unique\n                                                    within the pod. Each named port\n                                                    in a pod must have a unique name.\n                                                    Name for the port that can be\n                                                    referred to by services.\n                                                  type: string\n                                                protocol:\n                                                  default: TCP\n                                                  description: Protocol for port.\n                                                    Must be UDP, TCP, or SCTP. Defaults\n                                                    to \"TCP\".\n                                                  type: string\n                                              required:\n                                              - containerPort\n                                              type: object\n                                            type: array\n                                            x-kubernetes-list-map-keys:\n                                            - containerPort\n                                            - protocol\n                                            x-kubernetes-list-type: map\n                                          readinessProbe:\n                                            description: 'Periodic probe of container\n                                              service readiness. Container will be\n                                              removed from service endpoints if the\n                                              probe fails. Cannot be updated. More\n                                              info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                            properties:\n                                              exec:\n                                                description: One and only one of the\n                                                  following should be specified. Exec\n                                                  specifies the action to take.\n                                                properties:\n                                                  command:\n                                                    description: Command is the command\n                                                      line to execute inside the container,\n                                                      the working directory for the\n                                                      command  is root ('/') in the\n                                                      container's filesystem. The\n                                                      command is simply exec'd, it\n                                                      is not run inside a shell, so\n                                                      traditional shell instructions\n                                                      ('|', etc) won't work. To use\n                                                      a shell, you need to explicitly\n                                                      call out to that shell. Exit\n                                                      status of 0 is treated as live/healthy\n                                                      and non-zero is unhealthy.\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              failureThreshold:\n                                                description: Minimum consecutive failures\n                                                  for the probe to be considered failed\n                                                  after having succeeded. Defaults\n                                                  to 3. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              httpGet:\n                                                description: HTTPGet specifies the\n                                                  http request to perform.\n                                                properties:\n                                                  host:\n                                                    description: Host name to connect\n                                                      to, defaults to the pod IP.\n                                                      You probably want to set \"Host\"\n                                                      in httpHeaders instead.\n                                                    type: string\n                                                  httpHeaders:\n                                                    description: Custom headers to\n                                                      set in the request. HTTP allows\n                                                      repeated headers.\n                                                    items:\n                                                      description: HTTPHeader describes\n                                                        a custom header to be used\n                                                        in HTTP probes\n                                                      properties:\n                                                        name:\n                                                          description: The header\n                                                            field name\n                                                          type: string\n                                                        value:\n                                                          description: The header\n                                                            field value\n                                                          type: string\n                                                      required:\n                                                      - name\n                                                      - value\n                                                      type: object\n                                                    type: array\n                                                  path:\n                                                    description: Path to access on\n                                                      the HTTP server.\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Name or number of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                  scheme:\n                                                    description: Scheme to use for\n                                                      connecting to the host. Defaults\n                                                      to HTTP.\n                                                    type: string\n                                                required:\n                                                - port\n                                                type: object\n                                              initialDelaySeconds:\n                                                description: 'Number of seconds after\n                                                  the container has started before\n                                                  liveness probes are initiated. More\n                                                  info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                              periodSeconds:\n                                                description: How often (in seconds)\n                                                  to perform the probe. Default to\n                                                  10 seconds. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              successThreshold:\n                                                description: Minimum consecutive successes\n                                                  for the probe to be considered successful\n                                                  after having failed. Defaults to\n                                                  1. Must be 1 for liveness and startup.\n                                                  Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              tcpSocket:\n                                                description: 'TCPSocket specifies\n                                                  an action involving a TCP port.\n                                                  TCP hooks not yet supported TODO:\n                                                  implement a realistic TCP lifecycle\n                                                  hook'\n                                                properties:\n                                                  host:\n                                                    description: 'Optional: Host name\n                                                      to connect to, defaults to the\n                                                      pod IP.'\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Number or name of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                required:\n                                                - port\n                                                type: object\n                                              timeoutSeconds:\n                                                description: 'Number of seconds after\n                                                  which the probe times out. Defaults\n                                                  to 1 second. Minimum value is 1.\n                                                  More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                            type: object\n                                          resources:\n                                            description: 'Compute Resources required\n                                              by this container. Cannot be updated.\n                                              More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'\n                                            properties:\n                                              limits:\n                                                additionalProperties:\n                                                  anyOf:\n                                                  - type: integer\n                                                  - type: string\n                                                  pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                  x-kubernetes-int-or-string: true\n                                                description: 'Limits describes the\n                                                  maximum amount of compute resources\n                                                  allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'\n                                                type: object\n                                              requests:\n                                                additionalProperties:\n                                                  anyOf:\n                                                  - type: integer\n                                                  - type: string\n                                                  pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                  x-kubernetes-int-or-string: true\n                                                description: 'Requests describes the\n                                                  minimum amount of compute resources\n                                                  required. If Requests is omitted\n                                                  for a container, it defaults to\n                                                  Limits if that is explicitly specified,\n                                                  otherwise to an implementation-defined\n                                                  value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'\n                                                type: object\n                                            type: object\n                                          securityContext:\n                                            description: 'Security options the pod\n                                              should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/\n                                              More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/'\n                                            properties:\n                                              allowPrivilegeEscalation:\n                                                description: 'AllowPrivilegeEscalation\n                                                  controls whether a process can gain\n                                                  more privileges than its parent\n                                                  process. This bool directly controls\n                                                  if the no_new_privs flag will be\n                                                  set on the container process. AllowPrivilegeEscalation\n                                                  is true always when the container\n                                                  is: 1) run as Privileged 2) has\n                                                  CAP_SYS_ADMIN'\n                                                type: boolean\n                                              capabilities:\n                                                description: The capabilities to add/drop\n                                                  when running containers. Defaults\n                                                  to the default set of capabilities\n                                                  granted by the container runtime.\n                                                properties:\n                                                  add:\n                                                    description: Added capabilities\n                                                    items:\n                                                      description: Capability represent\n                                                        POSIX capabilities type\n                                                      type: string\n                                                    type: array\n                                                  drop:\n                                                    description: Removed capabilities\n                                                    items:\n                                                      description: Capability represent\n                                                        POSIX capabilities type\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              privileged:\n                                                description: Run container in privileged\n                                                  mode. Processes in privileged containers\n                                                  are essentially equivalent to root\n                                                  on the host. Defaults to false.\n                                                type: boolean\n                                              procMount:\n                                                description: procMount denotes the\n                                                  type of proc mount to use for the\n                                                  containers. The default is DefaultProcMount\n                                                  which uses the container runtime\n                                                  defaults for readonly paths and\n                                                  masked paths. This requires the\n                                                  ProcMountType feature flag to be\n                                                  enabled.\n                                                type: string\n                                              readOnlyRootFilesystem:\n                                                description: Whether this container\n                                                  has a read-only root filesystem.\n                                                  Default is false.\n                                                type: boolean\n                                              runAsGroup:\n                                                description: The GID to run the entrypoint\n                                                  of the container process. Uses runtime\n                                                  default if unset. May also be set\n                                                  in PodSecurityContext.  If set in\n                                                  both SecurityContext and PodSecurityContext,\n                                                  the value specified in SecurityContext\n                                                  takes precedence.\n                                                format: int64\n                                                type: integer\n                                              runAsNonRoot:\n                                                description: Indicates that the container\n                                                  must run as a non-root user. If\n                                                  true, the Kubelet will validate\n                                                  the image at runtime to ensure that\n                                                  it does not run as UID 0 (root)\n                                                  and fail to start the container\n                                                  if it does. If unset or false, no\n                                                  such validation will be performed.\n                                                  May also be set in PodSecurityContext.  If\n                                                  set in both SecurityContext and\n                                                  PodSecurityContext, the value specified\n                                                  in SecurityContext takes precedence.\n                                                type: boolean\n                                              runAsUser:\n                                                description: The UID to run the entrypoint\n                                                  of the container process. Defaults\n                                                  to user specified in image metadata\n                                                  if unspecified. May also be set\n                                                  in PodSecurityContext.  If set in\n                                                  both SecurityContext and PodSecurityContext,\n                                                  the value specified in SecurityContext\n                                                  takes precedence.\n                                                format: int64\n                                                type: integer\n                                              seLinuxOptions:\n                                                description: The SELinux context to\n                                                  be applied to the container. If\n                                                  unspecified, the container runtime\n                                                  will allocate a random SELinux context\n                                                  for each container.  May also be\n                                                  set in PodSecurityContext.  If set\n                                                  in both SecurityContext and PodSecurityContext,\n                                                  the value specified in SecurityContext\n                                                  takes precedence.\n                                                properties:\n                                                  level:\n                                                    description: Level is SELinux\n                                                      level label that applies to\n                                                      the container.\n                                                    type: string\n                                                  role:\n                                                    description: Role is a SELinux\n                                                      role label that applies to the\n                                                      container.\n                                                    type: string\n                                                  type:\n                                                    description: Type is a SELinux\n                                                      type label that applies to the\n                                                      container.\n                                                    type: string\n                                                  user:\n                                                    description: User is a SELinux\n                                                      user label that applies to the\n                                                      container.\n                                                    type: string\n                                                type: object\n                                              seccompProfile:\n                                                description: The seccomp options to\n                                                  use by this container. If seccomp\n                                                  options are provided at both the\n                                                  pod & container level, the container\n                                                  options override the pod options.\n                                                properties:\n                                                  localhostProfile:\n                                                    description: localhostProfile\n                                                      indicates a profile defined\n                                                      in a file on the node should\n                                                      be used. The profile must be\n                                                      preconfigured on the node to\n                                                      work. Must be a descending path,\n                                                      relative to the kubelet's configured\n                                                      seccomp profile location. Must\n                                                      only be set if type is \"Localhost\".\n                                                    type: string\n                                                  type:\n                                                    description: \"type indicates which\n                                                      kind of seccomp profile will\n                                                      be applied. Valid options are:\n                                                      \\n Localhost - a profile defined\n                                                      in a file on the node should\n                                                      be used. RuntimeDefault - the\n                                                      container runtime default profile\n                                                      should be used. Unconfined -\n                                                      no profile should be applied.\"\n                                                    type: string\n                                                required:\n                                                - type\n                                                type: object\n                                              windowsOptions:\n                                                description: The Windows specific\n                                                  settings applied to all containers.\n                                                  If unspecified, the options from\n                                                  the PodSecurityContext will be used.\n                                                  If set in both SecurityContext and\n                                                  PodSecurityContext, the value specified\n                                                  in SecurityContext takes precedence.\n                                                properties:\n                                                  gmsaCredentialSpec:\n                                                    description: GMSACredentialSpec\n                                                      is where the GMSA admission\n                                                      webhook (https://github.com/kubernetes-sigs/windows-gmsa)\n                                                      inlines the contents of the\n                                                      GMSA credential spec named by\n                                                      the GMSACredentialSpecName field.\n                                                    type: string\n                                                  gmsaCredentialSpecName:\n                                                    description: GMSACredentialSpecName\n                                                      is the name of the GMSA credential\n                                                      spec to use.\n                                                    type: string\n                                                  runAsUserName:\n                                                    description: The UserName in Windows\n                                                      to run the entrypoint of the\n                                                      container process. Defaults\n                                                      to the user specified in image\n                                                      metadata if unspecified. May\n                                                      also be set in PodSecurityContext.\n                                                      If set in both SecurityContext\n                                                      and PodSecurityContext, the\n                                                      value specified in SecurityContext\n                                                      takes precedence.\n                                                    type: string\n                                                type: object\n                                            type: object\n                                          startupProbe:\n                                            description: 'StartupProbe indicates that\n                                              the Pod has successfully initialized.\n                                              If specified, no other probes are executed\n                                              until this completes successfully. If\n                                              this probe fails, the Pod will be restarted,\n                                              just as if the livenessProbe failed.\n                                              This can be used to provide different\n                                              probe parameters at the beginning of\n                                              a Pod''s lifecycle, when it might take\n                                              a long time to load data or warm a cache,\n                                              than during steady-state operation.\n                                              This cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                            properties:\n                                              exec:\n                                                description: One and only one of the\n                                                  following should be specified. Exec\n                                                  specifies the action to take.\n                                                properties:\n                                                  command:\n                                                    description: Command is the command\n                                                      line to execute inside the container,\n                                                      the working directory for the\n                                                      command  is root ('/') in the\n                                                      container's filesystem. The\n                                                      command is simply exec'd, it\n                                                      is not run inside a shell, so\n                                                      traditional shell instructions\n                                                      ('|', etc) won't work. To use\n                                                      a shell, you need to explicitly\n                                                      call out to that shell. Exit\n                                                      status of 0 is treated as live/healthy\n                                                      and non-zero is unhealthy.\n                                                    items:\n                                                      type: string\n                                                    type: array\n                                                type: object\n                                              failureThreshold:\n                                                description: Minimum consecutive failures\n                                                  for the probe to be considered failed\n                                                  after having succeeded. Defaults\n                                                  to 3. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              httpGet:\n                                                description: HTTPGet specifies the\n                                                  http request to perform.\n                                                properties:\n                                                  host:\n                                                    description: Host name to connect\n                                                      to, defaults to the pod IP.\n                                                      You probably want to set \"Host\"\n                                                      in httpHeaders instead.\n                                                    type: string\n                                                  httpHeaders:\n                                                    description: Custom headers to\n                                                      set in the request. HTTP allows\n                                                      repeated headers.\n                                                    items:\n                                                      description: HTTPHeader describes\n                                                        a custom header to be used\n                                                        in HTTP probes\n                                                      properties:\n                                                        name:\n                                                          description: The header\n                                                            field name\n                                                          type: string\n                                                        value:\n                                                          description: The header\n                                                            field value\n                                                          type: string\n                                                      required:\n                                                      - name\n                                                      - value\n                                                      type: object\n                                                    type: array\n                                                  path:\n                                                    description: Path to access on\n                                                      the HTTP server.\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Name or number of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                  scheme:\n                                                    description: Scheme to use for\n                                                      connecting to the host. Defaults\n                                                      to HTTP.\n                                                    type: string\n                                                required:\n                                                - port\n                                                type: object\n                                              initialDelaySeconds:\n                                                description: 'Number of seconds after\n                                                  the container has started before\n                                                  liveness probes are initiated. More\n                                                  info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                              periodSeconds:\n                                                description: How often (in seconds)\n                                                  to perform the probe. Default to\n                                                  10 seconds. Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              successThreshold:\n                                                description: Minimum consecutive successes\n                                                  for the probe to be considered successful\n                                                  after having failed. Defaults to\n                                                  1. Must be 1 for liveness and startup.\n                                                  Minimum value is 1.\n                                                format: int32\n                                                type: integer\n                                              tcpSocket:\n                                                description: 'TCPSocket specifies\n                                                  an action involving a TCP port.\n                                                  TCP hooks not yet supported TODO:\n                                                  implement a realistic TCP lifecycle\n                                                  hook'\n                                                properties:\n                                                  host:\n                                                    description: 'Optional: Host name\n                                                      to connect to, defaults to the\n                                                      pod IP.'\n                                                    type: string\n                                                  port:\n                                                    anyOf:\n                                                    - type: integer\n                                                    - type: string\n                                                    description: Number or name of\n                                                      the port to access on the container.\n                                                      Number must be in the range\n                                                      1 to 65535. Name must be an\n                                                      IANA_SVC_NAME.\n                                                    x-kubernetes-int-or-string: true\n                                                required:\n                                                - port\n                                                type: object\n                                              timeoutSeconds:\n                                                description: 'Number of seconds after\n                                                  which the probe times out. Defaults\n                                                  to 1 second. Minimum value is 1.\n                                                  More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes'\n                                                format: int32\n                                                type: integer\n                                            type: object\n                                          stdin:\n                                            description: Whether this container should\n                                              allocate a buffer for stdin in the container\n                                              runtime. If this is not set, reads from\n                                              stdin in the container will always result\n                                              in EOF. Default is false.\n                                            type: boolean\n                                          stdinOnce:\n                                            description: Whether the container runtime\n                                              should close the stdin channel after\n                                              it has been opened by a single attach.\n                                              When stdin is true the stdin stream\n                                              will remain open across multiple attach\n                                              sessions. If stdinOnce is set to true,\n                                              stdin is opened on container start,\n                                              is empty until the first client attaches\n                                              to stdin, and then remains open and\n                                              accepts data until the client disconnects,\n                                              at which time stdin is closed and remains\n                                              closed until the container is restarted.\n                                              If this flag is false, a container processes\n                                              that reads from stdin will never receive\n                                              an EOF. Default is false\n                                            type: boolean\n                                          terminationMessagePath:\n                                            description: 'Optional: Path at which\n                                              the file to which the container''s termination\n                                              message will be written is mounted into\n                                              the container''s filesystem. Message\n                                              written is intended to be brief final\n                                              status, such as an assertion failure\n                                              message. Will be truncated by the node\n                                              if greater than 4096 bytes. The total\n                                              message length across all containers\n                                              will be limited to 12kb. Defaults to\n                                              /dev/termination-log. Cannot be updated.'\n                                            type: string\n                                          terminationMessagePolicy:\n                                            description: Indicate how the termination\n                                              message should be populated. File will\n                                              use the contents of terminationMessagePath\n                                              to populate the container status message\n                                              on both success and failure. FallbackToLogsOnError\n                                              will use the last chunk of container\n                                              log output if the termination message\n                                              file is empty and the container exited\n                                              with an error. The log output is limited\n                                              to 2048 bytes or 80 lines, whichever\n                                              is smaller. Defaults to File. Cannot\n                                              be updated.\n                                            type: string\n                                          tty:\n                                            description: Whether this container should\n                                              allocate a TTY for itself, also requires\n                                              'stdin' to be true. Default is false.\n                                            type: boolean\n                                          volumeDevices:\n                                            description: volumeDevices is the list\n                                              of block devices to be used by the container.\n                                            items:\n                                              description: volumeDevice describes\n                                                a mapping of a raw block device within\n                                                a container.\n                                              properties:\n                                                devicePath:\n                                                  description: devicePath is the path\n                                                    inside of the container that the\n                                                    device will be mapped to.\n                                                  type: string\n                                                name:\n                                                  description: name must match the\n                                                    name of a persistentVolumeClaim\n                                                    in the pod\n                                                  type: string\n                                              required:\n                                              - devicePath\n                                              - name\n                                              type: object\n                                            type: array\n                                          volumeMounts:\n                                            description: Pod volumes to mount into\n                                              the container's filesystem. Cannot be\n                                              updated.\n                                            items:\n                                              description: VolumeMount describes a\n                                                mounting of a Volume within a container.\n                                              properties:\n                                                mountPath:\n                                                  description: Path within the container\n                                                    at which the volume should be\n                                                    mounted.  Must not contain ':'.\n                                                  type: string\n                                                mountPropagation:\n                                                  description: mountPropagation determines\n                                                    how mounts are propagated from\n                                                    the host to container and the\n                                                    other way around. When not set,\n                                                    MountPropagationNone is used.\n                                                    This field is beta in 1.10.\n                                                  type: string\n                                                name:\n                                                  description: This must match the\n                                                    Name of a Volume.\n                                                  type: string\n                                                readOnly:\n                                                  description: Mounted read-only if\n                                                    true, read-write otherwise (false\n                                                    or unspecified). Defaults to false.\n                                                  type: boolean\n                                                subPath:\n                                                  description: Path within the volume\n                                                    from which the container's volume\n                                                    should be mounted. Defaults to\n                                                    \"\" (volume's root).\n                                                  type: string\n                                                subPathExpr:\n                                                  description: Expanded path within\n                                                    the volume from which the container's\n                                                    volume should be mounted. Behaves\n                                                    similarly to SubPath but environment\n                                                    variable references $(VAR_NAME)\n                                                    are expanded using the container's\n                                                    environment. Defaults to \"\" (volume's\n                                                    root). SubPathExpr and SubPath\n                                                    are mutually exclusive.\n                                                  type: string\n                                              required:\n                                              - mountPath\n                                              - name\n                                              type: object\n                                            type: array\n                                          workingDir:\n                                            description: Container's working directory.\n                                              If not specified, the container runtime's\n                                              default will be used, which might be\n                                              configured in the container image. Cannot\n                                              be updated.\n                                            type: string\n                                        required:\n                                        - name\n                                        type: object\n                                      type: array\n                                    nodeName:\n                                      description: NodeName is a request to schedule\n                                        this pod onto a specific node. If it is non-empty,\n                                        the scheduler simply schedules this pod onto\n                                        that node, assuming that it fits resource\n                                        requirements.\n                                      type: string\n                                    nodeSelector:\n                                      additionalProperties:\n                                        type: string\n                                      description: 'NodeSelector is a selector which\n                                        must be true for the pod to fit on a node.\n                                        Selector which must match a node''s labels\n                                        for the pod to be scheduled on that node.\n                                        More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/'\n                                      type: object\n                                    overhead:\n                                      additionalProperties:\n                                        anyOf:\n                                        - type: integer\n                                        - type: string\n                                        pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                        x-kubernetes-int-or-string: true\n                                      description: 'Overhead represents the resource\n                                        overhead associated with running a pod for\n                                        a given RuntimeClass. This field will be autopopulated\n                                        at admission time by the RuntimeClass admission\n                                        controller. If the RuntimeClass admission\n                                        controller is enabled, overhead must not be\n                                        set in Pod create requests. The RuntimeClass\n                                        admission controller will reject Pod create\n                                        requests which have the overhead already set.\n                                        If RuntimeClass is configured and selected\n                                        in the PodSpec, Overhead will be set to the\n                                        value defined in the corresponding RuntimeClass,\n                                        otherwise it will remain unset and treated\n                                        as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md\n                                        This field is alpha-level as of Kubernetes\n                                        v1.16, and is only honored by servers that\n                                        enable the PodOverhead feature.'\n                                      type: object\n                                    preemptionPolicy:\n                                      description: PreemptionPolicy is the Policy\n                                        for preempting pods with lower priority. One\n                                        of Never, PreemptLowerPriority. Defaults to\n                                        PreemptLowerPriority if unset. This field\n                                        is beta-level, gated by the NonPreemptingPriority\n                                        feature-gate.\n                                      type: string\n                                    priority:\n                                      description: The priority value. Various system\n                                        components use this field to find the priority\n                                        of the pod. When Priority Admission Controller\n                                        is enabled, it prevents users from setting\n                                        this field. The admission controller populates\n                                        this field from PriorityClassName. The higher\n                                        the value, the higher the priority.\n                                      format: int32\n                                      type: integer\n                                    priorityClassName:\n                                      description: If specified, indicates the pod's\n                                        priority. \"system-node-critical\" and \"system-cluster-critical\"\n                                        are two special keywords which indicate the\n                                        highest priorities with the former being the\n                                        highest priority. Any other name must be defined\n                                        by creating a PriorityClass object with that\n                                        name. If not specified, the pod priority will\n                                        be default or zero if there is no default.\n                                      type: string\n                                    readinessGates:\n                                      description: 'If specified, all readiness gates\n                                        will be evaluated for pod readiness. A pod\n                                        is ready when all its containers are ready\n                                        AND all conditions specified in the readiness\n                                        gates have status equal to \"True\" More info:\n                                        https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md'\n                                      items:\n                                        description: PodReadinessGate contains the\n                                          reference to a pod condition\n                                        properties:\n                                          conditionType:\n                                            description: ConditionType refers to a\n                                              condition in the pod's condition list\n                                              with matching type.\n                                            type: string\n                                        required:\n                                        - conditionType\n                                        type: object\n                                      type: array\n                                    restartPolicy:\n                                      description: 'Restart policy for all containers\n                                        within the pod. One of Always, OnFailure,\n                                        Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy'\n                                      type: string\n                                    runtimeClassName:\n                                      description: 'RuntimeClassName refers to a RuntimeClass\n                                        object in the node.k8s.io group, which should\n                                        be used to run this pod.  If no RuntimeClass\n                                        resource matches the named class, the pod\n                                        will not be run. If unset or empty, the \"legacy\"\n                                        RuntimeClass will be used, which is an implicit\n                                        class with an empty definition that uses the\n                                        default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md\n                                        This is a beta feature as of Kubernetes v1.14.'\n                                      type: string\n                                    schedulerName:\n                                      description: If specified, the pod will be dispatched\n                                        by specified scheduler. If not specified,\n                                        the pod will be dispatched by default scheduler.\n                                      type: string\n                                    securityContext:\n                                      description: 'SecurityContext holds pod-level\n                                        security attributes and common container settings.\n                                        Optional: Defaults to empty.  See type description\n                                        for default values of each field.'\n                                      properties:\n                                        fsGroup:\n                                          description: \"A special supplemental group\n                                            that applies to all containers in a pod.\n                                            Some volume types allow the Kubelet to\n                                            change the ownership of that volume to\n                                            be owned by the pod: \\n 1. The owning\n                                            GID will be the FSGroup 2. The setgid\n                                            bit is set (new files created in the volume\n                                            will be owned by FSGroup) 3. The permission\n                                            bits are OR'd with rw-rw---- \\n If unset,\n                                            the Kubelet will not modify the ownership\n                                            and permissions of any volume.\"\n                                          format: int64\n                                          type: integer\n                                        fsGroupChangePolicy:\n                                          description: 'fsGroupChangePolicy defines\n                                            behavior of changing ownership and permission\n                                            of the volume before being exposed inside\n                                            Pod. This field will only apply to volume\n                                            types which support fsGroup based ownership(and\n                                            permissions). It will have no effect on\n                                            ephemeral volume types such as: secret,\n                                            configmaps and emptydir. Valid values\n                                            are \"OnRootMismatch\" and \"Always\". If\n                                            not specified, \"Always\" is used.'\n                                          type: string\n                                        runAsGroup:\n                                          description: The GID to run the entrypoint\n                                            of the container process. Uses runtime\n                                            default if unset. May also be set in SecurityContext.  If\n                                            set in both SecurityContext and PodSecurityContext,\n                                            the value specified in SecurityContext\n                                            takes precedence for that container.\n                                          format: int64\n                                          type: integer\n                                        runAsNonRoot:\n                                          description: Indicates that the container\n                                            must run as a non-root user. If true,\n                                            the Kubelet will validate the image at\n                                            runtime to ensure that it does not run\n                                            as UID 0 (root) and fail to start the\n                                            container if it does. If unset or false,\n                                            no such validation will be performed.\n                                            May also be set in SecurityContext.  If\n                                            set in both SecurityContext and PodSecurityContext,\n                                            the value specified in SecurityContext\n                                            takes precedence.\n                                          type: boolean\n                                        runAsUser:\n                                          description: The UID to run the entrypoint\n                                            of the container process. Defaults to\n                                            user specified in image metadata if unspecified.\n                                            May also be set in SecurityContext.  If\n                                            set in both SecurityContext and PodSecurityContext,\n                                            the value specified in SecurityContext\n                                            takes precedence for that container.\n                                          format: int64\n                                          type: integer\n                                        seLinuxOptions:\n                                          description: The SELinux context to be applied\n                                            to all containers. If unspecified, the\n                                            container runtime will allocate a random\n                                            SELinux context for each container.  May\n                                            also be set in SecurityContext.  If set\n                                            in both SecurityContext and PodSecurityContext,\n                                            the value specified in SecurityContext\n                                            takes precedence for that container.\n                                          properties:\n                                            level:\n                                              description: Level is SELinux level\n                                                label that applies to the container.\n                                              type: string\n                                            role:\n                                              description: Role is a SELinux role\n                                                label that applies to the container.\n                                              type: string\n                                            type:\n                                              description: Type is a SELinux type\n                                                label that applies to the container.\n                                              type: string\n                                            user:\n                                              description: User is a SELinux user\n                                                label that applies to the container.\n                                              type: string\n                                          type: object\n                                        seccompProfile:\n                                          description: The seccomp options to use\n                                            by the containers in this pod.\n                                          properties:\n                                            localhostProfile:\n                                              description: localhostProfile indicates\n                                                a profile defined in a file on the\n                                                node should be used. The profile must\n                                                be preconfigured on the node to work.\n                                                Must be a descending path, relative\n                                                to the kubelet's configured seccomp\n                                                profile location. Must only be set\n                                                if type is \"Localhost\".\n                                              type: string\n                                            type:\n                                              description: \"type indicates which kind\n                                                of seccomp profile will be applied.\n                                                Valid options are: \\n Localhost -\n                                                a profile defined in a file on the\n                                                node should be used. RuntimeDefault\n                                                - the container runtime default profile\n                                                should be used. Unconfined - no profile\n                                                should be applied.\"\n                                              type: string\n                                          required:\n                                          - type\n                                          type: object\n                                        supplementalGroups:\n                                          description: A list of groups applied to\n                                            the first process run in each container,\n                                            in addition to the container's primary\n                                            GID.  If unspecified, no groups will be\n                                            added to any container.\n                                          items:\n                                            format: int64\n                                            type: integer\n                                          type: array\n                                        sysctls:\n                                          description: Sysctls hold a list of namespaced\n                                            sysctls used for the pod. Pods with unsupported\n                                            sysctls (by the container runtime) might\n                                            fail to launch.\n                                          items:\n                                            description: Sysctl defines a kernel parameter\n                                              to be set\n                                            properties:\n                                              name:\n                                                description: Name of a property to\n                                                  set\n                                                type: string\n                                              value:\n                                                description: Value of a property to\n                                                  set\n                                                type: string\n                                            required:\n                                            - name\n                                            - value\n                                            type: object\n                                          type: array\n                                        windowsOptions:\n                                          description: The Windows specific settings\n                                            applied to all containers. If unspecified,\n                                            the options within a container's SecurityContext\n                                            will be used. If set in both SecurityContext\n                                            and PodSecurityContext, the value specified\n                                            in SecurityContext takes precedence.\n                                          properties:\n                                            gmsaCredentialSpec:\n                                              description: GMSACredentialSpec is where\n                                                the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa)\n                                                inlines the contents of the GMSA credential\n                                                spec named by the GMSACredentialSpecName\n                                                field.\n                                              type: string\n                                            gmsaCredentialSpecName:\n                                              description: GMSACredentialSpecName\n                                                is the name of the GMSA credential\n                                                spec to use.\n                                              type: string\n                                            runAsUserName:\n                                              description: The UserName in Windows\n                                                to run the entrypoint of the container\n                                                process. Defaults to the user specified\n                                                in image metadata if unspecified.\n                                                May also be set in PodSecurityContext.\n                                                If set in both SecurityContext and\n                                                PodSecurityContext, the value specified\n                                                in SecurityContext takes precedence.\n                                              type: string\n                                          type: object\n                                      type: object\n                                    serviceAccount:\n                                      description: 'DeprecatedServiceAccount is a\n                                        depreciated alias for ServiceAccountName.\n                                        Deprecated: Use serviceAccountName instead.'\n                                      type: string\n                                    serviceAccountName:\n                                      description: 'ServiceAccountName is the name\n                                        of the ServiceAccount to use to run this pod.\n                                        More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/'\n                                      type: string\n                                    setHostnameAsFQDN:\n                                      description: If true the pod's hostname will\n                                        be configured as the pod's FQDN, rather than\n                                        the leaf name (the default). In Linux containers,\n                                        this means setting the FQDN in the hostname\n                                        field of the kernel (the nodename field of\n                                        struct utsname). In Windows containers, this\n                                        means setting the registry value of hostname\n                                        for the registry key HKEY_LOCAL_MACHINE\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\Tcpip\\\\Parameters\n                                        to FQDN. If a pod does not have FQDN, this\n                                        has no effect. Default to false.\n                                      type: boolean\n                                    shareProcessNamespace:\n                                      description: 'Share a single process namespace\n                                        between all of the containers in a pod. When\n                                        this is set containers will be able to view\n                                        and signal processes from other containers\n                                        in the same pod, and the first process in\n                                        each container will not be assigned PID 1.\n                                        HostPID and ShareProcessNamespace cannot both\n                                        be set. Optional: Default to false.'\n                                      type: boolean\n                                    subdomain:\n                                      description: If specified, the fully qualified\n                                        Pod hostname will be \"<hostname>.<subdomain>.<pod\n                                        namespace>.svc.<cluster domain>\". If not specified,\n                                        the pod will not have a domainname at all.\n                                      type: string\n                                    terminationGracePeriodSeconds:\n                                      description: Optional duration in seconds the\n                                        pod needs to terminate gracefully. May be\n                                        decreased in delete request. Value must be\n                                        non-negative integer. The value zero indicates\n                                        delete immediately. If this value is nil,\n                                        the default grace period will be used instead.\n                                        The grace period is the duration in seconds\n                                        after the processes running in the pod are\n                                        sent a termination signal and the time when\n                                        the processes are forcibly halted with a kill\n                                        signal. Set this value longer than the expected\n                                        cleanup time for your process. Defaults to\n                                        30 seconds.\n                                      format: int64\n                                      type: integer\n                                    tolerations:\n                                      description: If specified, the pod's tolerations.\n                                      items:\n                                        description: The pod this Toleration is attached\n                                          to tolerates any taint that matches the\n                                          triple <key,value,effect> using the matching\n                                          operator <operator>.\n                                        properties:\n                                          effect:\n                                            description: Effect indicates the taint\n                                              effect to match. Empty means match all\n                                              taint effects. When specified, allowed\n                                              values are NoSchedule, PreferNoSchedule\n                                              and NoExecute.\n                                            type: string\n                                          key:\n                                            description: Key is the taint key that\n                                              the toleration applies to. Empty means\n                                              match all taint keys. If the key is\n                                              empty, operator must be Exists; this\n                                              combination means to match all values\n                                              and all keys.\n                                            type: string\n                                          operator:\n                                            description: Operator represents a key's\n                                              relationship to the value. Valid operators\n                                              are Exists and Equal. Defaults to Equal.\n                                              Exists is equivalent to wildcard for\n                                              value, so that a pod can tolerate all\n                                              taints of a particular category.\n                                            type: string\n                                          tolerationSeconds:\n                                            description: TolerationSeconds represents\n                                              the period of time the toleration (which\n                                              must be of effect NoExecute, otherwise\n                                              this field is ignored) tolerates the\n                                              taint. By default, it is not set, which\n                                              means tolerate the taint forever (do\n                                              not evict). Zero and negative values\n                                              will be treated as 0 (evict immediately)\n                                              by the system.\n                                            format: int64\n                                            type: integer\n                                          value:\n                                            description: Value is the taint value\n                                              the toleration matches to. If the operator\n                                              is Exists, the value should be empty,\n                                              otherwise just a regular string.\n                                            type: string\n                                        type: object\n                                      type: array\n                                    topologySpreadConstraints:\n                                      description: TopologySpreadConstraints describes\n                                        how a group of pods ought to spread across\n                                        topology domains. Scheduler will schedule\n                                        pods in a way which abides by the constraints.\n                                        All topologySpreadConstraints are ANDed.\n                                      items:\n                                        description: TopologySpreadConstraint specifies\n                                          how to spread matching pods among the given\n                                          topology.\n                                        properties:\n                                          labelSelector:\n                                            description: LabelSelector is used to\n                                              find matching pods. Pods that match\n                                              this label selector are counted to determine\n                                              the number of pods in their corresponding\n                                              topology domain.\n                                            properties:\n                                              matchExpressions:\n                                                description: matchExpressions is a\n                                                  list of label selector requirements.\n                                                  The requirements are ANDed.\n                                                items:\n                                                  description: A label selector requirement\n                                                    is a selector that contains values,\n                                                    a key, and an operator that relates\n                                                    the key and values.\n                                                  properties:\n                                                    key:\n                                                      description: key is the label\n                                                        key that the selector applies\n                                                        to.\n                                                      type: string\n                                                    operator:\n                                                      description: operator represents\n                                                        a key's relationship to a\n                                                        set of values. Valid operators\n                                                        are In, NotIn, Exists and\n                                                        DoesNotExist.\n                                                      type: string\n                                                    values:\n                                                      description: values is an array\n                                                        of string values. If the operator\n                                                        is In or NotIn, the values\n                                                        array must be non-empty. If\n                                                        the operator is Exists or\n                                                        DoesNotExist, the values array\n                                                        must be empty. This array\n                                                        is replaced during a strategic\n                                                        merge patch.\n                                                      items:\n                                                        type: string\n                                                      type: array\n                                                  required:\n                                                  - key\n                                                  - operator\n                                                  type: object\n                                                type: array\n                                              matchLabels:\n                                                additionalProperties:\n                                                  type: string\n                                                description: matchLabels is a map\n                                                  of {key,value} pairs. A single {key,value}\n                                                  in the matchLabels map is equivalent\n                                                  to an element of matchExpressions,\n                                                  whose key field is \"key\", the operator\n                                                  is \"In\", and the values array contains\n                                                  only \"value\". The requirements are\n                                                  ANDed.\n                                                type: object\n                                            type: object\n                                          maxSkew:\n                                            description: 'MaxSkew describes the degree\n                                              to which pods may be unevenly distributed.\n                                              When `whenUnsatisfiable=DoNotSchedule`,\n                                              it is the maximum permitted difference\n                                              between the number of matching pods\n                                              in the target topology and the global\n                                              minimum. For example, in a 3-zone cluster,\n                                              MaxSkew is set to 1, and pods with the\n                                              same labelSelector spread as 1/1/0:\n                                              | zone1 | zone2 | zone3 | |   P   |   P   |       |\n                                              - if MaxSkew is 1, incoming pod can\n                                              only be scheduled to zone3 to become\n                                              1/1/1; scheduling it onto zone1(zone2)\n                                              would make the ActualSkew(2-0) on zone1(zone2)\n                                              violate MaxSkew(1). - if MaxSkew is\n                                              2, incoming pod can be scheduled onto\n                                              any zone. When `whenUnsatisfiable=ScheduleAnyway`,\n                                              it is used to give higher precedence\n                                              to topologies that satisfy it. It''s\n                                              a required field. Default value is 1\n                                              and 0 is not allowed.'\n                                            format: int32\n                                            type: integer\n                                          topologyKey:\n                                            description: TopologyKey is the key of\n                                              node labels. Nodes that have a label\n                                              with this key and identical values are\n                                              considered to be in the same topology.\n                                              We consider each <key, value> as a \"bucket\",\n                                              and try to put balanced number of pods\n                                              into each bucket. It's a required field.\n                                            type: string\n                                          whenUnsatisfiable:\n                                            description: 'WhenUnsatisfiable indicates\n                                              how to deal with a pod if it doesn''t\n                                              satisfy the spread constraint. - DoNotSchedule\n                                              (default) tells the scheduler not to\n                                              schedule it. - ScheduleAnyway tells\n                                              the scheduler to schedule the pod in\n                                              any location,   but giving higher precedence\n                                              to topologies that would help reduce\n                                              the   skew. A constraint is considered\n                                              \"Unsatisfiable\" for an incoming pod\n                                              if and only if every possible node assigment\n                                              for that pod would violate \"MaxSkew\"\n                                              on some topology. For example, in a\n                                              3-zone cluster, MaxSkew is set to 1,\n                                              and pods with the same labelSelector\n                                              spread as 3/1/1: | zone1 | zone2 | zone3\n                                              | | P P P |   P   |   P   | If WhenUnsatisfiable\n                                              is set to DoNotSchedule, incoming pod\n                                              can only be scheduled to zone2(zone3)\n                                              to become 3/2/1(3/1/2) as ActualSkew(2-1)\n                                              on zone2(zone3) satisfies MaxSkew(1).\n                                              In other words, the cluster can still\n                                              be imbalanced, but scheduler won''t\n                                              make it *more* imbalanced. It''s a required\n                                              field.'\n                                            type: string\n                                        required:\n                                        - maxSkew\n                                        - topologyKey\n                                        - whenUnsatisfiable\n                                        type: object\n                                      type: array\n                                      x-kubernetes-list-map-keys:\n                                      - topologyKey\n                                      - whenUnsatisfiable\n                                      x-kubernetes-list-type: map\n                                    volumes:\n                                      description: 'List of volumes that can be mounted\n                                        by containers belonging to the pod. More info:\n                                        https://kubernetes.io/docs/concepts/storage/volumes'\n                                      items:\n                                        description: Volume represents a named volume\n                                          in a pod that may be accessed by any container\n                                          in the pod.\n                                        properties:\n                                          awsElasticBlockStore:\n                                            description: 'AWSElasticBlockStore represents\n                                              an AWS Disk resource that is attached\n                                              to a kubelet''s host machine and then\n                                              exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'\n                                            properties:\n                                              fsType:\n                                                description: 'Filesystem type of the\n                                                  volume that you want to mount. Tip:\n                                                  Ensure that the filesystem type\n                                                  is supported by the host operating\n                                                  system. Examples: \"ext4\", \"xfs\",\n                                                  \"ntfs\". Implicitly inferred to be\n                                                  \"ext4\" if unspecified. More info:\n                                                  https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore\n                                                  TODO: how do we prevent errors in\n                                                  the filesystem from compromising\n                                                  the machine'\n                                                type: string\n                                              partition:\n                                                description: 'The partition in the\n                                                  volume that you want to mount. If\n                                                  omitted, the default is to mount\n                                                  by volume name. Examples: For volume\n                                                  /dev/sda1, you specify the partition\n                                                  as \"1\". Similarly, the volume partition\n                                                  for /dev/sda is \"0\" (or you can\n                                                  leave the property empty).'\n                                                format: int32\n                                                type: integer\n                                              readOnly:\n                                                description: 'Specify \"true\" to force\n                                                  and set the ReadOnly property in\n                                                  VolumeMounts to \"true\". If omitted,\n                                                  the default is \"false\". More info:\n                                                  https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'\n                                                type: boolean\n                                              volumeID:\n                                                description: 'Unique ID of the persistent\n                                                  disk resource in AWS (Amazon EBS\n                                                  volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore'\n                                                type: string\n                                            required:\n                                            - volumeID\n                                            type: object\n                                          azureDisk:\n                                            description: AzureDisk represents an Azure\n                                              Data Disk mount on the host and bind\n                                              mount to the pod.\n                                            properties:\n                                              cachingMode:\n                                                description: 'Host Caching mode: None,\n                                                  Read Only, Read Write.'\n                                                type: string\n                                              diskName:\n                                                description: The Name of the data\n                                                  disk in the blob storage\n                                                type: string\n                                              diskURI:\n                                                description: The URI the data disk\n                                                  in the blob storage\n                                                type: string\n                                              fsType:\n                                                description: Filesystem type to mount.\n                                                  Must be a filesystem type supported\n                                                  by the host operating system. Ex.\n                                                  \"ext4\", \"xfs\", \"ntfs\". Implicitly\n                                                  inferred to be \"ext4\" if unspecified.\n                                                type: string\n                                              kind:\n                                                description: 'Expected values Shared:\n                                                  multiple blob disks per storage\n                                                  account  Dedicated: single blob\n                                                  disk per storage account  Managed:\n                                                  azure managed data disk (only in\n                                                  managed availability set). defaults\n                                                  to shared'\n                                                type: string\n                                              readOnly:\n                                                description: Defaults to false (read/write).\n                                                  ReadOnly here will force the ReadOnly\n                                                  setting in VolumeMounts.\n                                                type: boolean\n                                            required:\n                                            - diskName\n                                            - diskURI\n                                            type: object\n                                          azureFile:\n                                            description: AzureFile represents an Azure\n                                              File Service mount on the host and bind\n                                              mount to the pod.\n                                            properties:\n                                              readOnly:\n                                                description: Defaults to false (read/write).\n                                                  ReadOnly here will force the ReadOnly\n                                                  setting in VolumeMounts.\n                                                type: boolean\n                                              secretName:\n                                                description: the name of secret that\n                                                  contains Azure Storage Account Name\n                                                  and Key\n                                                type: string\n                                              shareName:\n                                                description: Share Name\n                                                type: string\n                                            required:\n                                            - secretName\n                                            - shareName\n                                            type: object\n                                          cephfs:\n                                            description: CephFS represents a Ceph\n                                              FS mount on the host that shares a pod's\n                                              lifetime\n                                            properties:\n                                              monitors:\n                                                description: 'Required: Monitors is\n                                                  a collection of Ceph monitors More\n                                                  info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'\n                                                items:\n                                                  type: string\n                                                type: array\n                                              path:\n                                                description: 'Optional: Used as the\n                                                  mounted root, rather than the full\n                                                  Ceph tree, default is /'\n                                                type: string\n                                              readOnly:\n                                                description: 'Optional: Defaults to\n                                                  false (read/write). ReadOnly here\n                                                  will force the ReadOnly setting\n                                                  in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'\n                                                type: boolean\n                                              secretFile:\n                                                description: 'Optional: SecretFile\n                                                  is the path to key ring for User,\n                                                  default is /etc/ceph/user.secret\n                                                  More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'\n                                                type: string\n                                              secretRef:\n                                                description: 'Optional: SecretRef\n                                                  is reference to the authentication\n                                                  secret for User, default is empty.\n                                                  More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'\n                                                properties:\n                                                  name:\n                                                    description: 'Name of the referent.\n                                                      More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                      TODO: Add other useful fields.\n                                                      apiVersion, kind, uid?'\n                                                    type: string\n                                                type: object\n                                              user:\n                                                description: 'Optional: User is the\n                                                  rados user name, default is admin\n                                                  More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it'\n                                                type: string\n                                            required:\n                                            - monitors\n                                            type: object\n                                          cinder:\n                                            description: 'Cinder represents a cinder\n                                              volume attached and mounted on kubelets\n                                              host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'\n                                            properties:\n                                              fsType:\n                                                description: 'Filesystem type to mount.\n                                                  Must be a filesystem type supported\n                                                  by the host operating system. Examples:\n                                                  \"ext4\", \"xfs\", \"ntfs\". Implicitly\n                                                  inferred to be \"ext4\" if unspecified.\n                                                  More info: https://examples.k8s.io/mysql-cinder-pd/README.md'\n                                                type: string\n                                              readOnly:\n                                                description: 'Optional: Defaults to\n                                                  false (read/write). ReadOnly here\n                                                  will force the ReadOnly setting\n                                                  in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md'\n                                                type: boolean\n                                              secretRef:\n                                                description: 'Optional: points to\n                                                  a secret object containing parameters\n                                                  used to connect to OpenStack.'\n                                                properties:\n                                                  name:\n                                                    description: 'Name of the referent.\n                                                      More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                      TODO: Add other useful fields.\n                                                      apiVersion, kind, uid?'\n                                                    type: string\n                                                type: object\n                                              volumeID:\n                                                description: 'volume id used to identify\n                                                  the volume in cinder. More info:\n                                                  https://examples.k8s.io/mysql-cinder-pd/README.md'\n                                                type: string\n                                            required:\n                                            - volumeID\n                                            type: object\n                                          configMap:\n                                            description: ConfigMap represents a configMap\n                                              that should populate this volume\n                                            properties:\n                                              defaultMode:\n                                                description: 'Optional: mode bits\n                                                  used to set permissions on created\n                                                  files by default. Must be an octal\n                                                  value between 0000 and 0777 or a\n                                                  decimal value between 0 and 511.\n                                                  YAML accepts both octal and decimal\n                                                  values, JSON requires decimal values\n                                                  for mode bits. Defaults to 0644.\n                                                  Directories within the path are\n                                                  not affected by this setting. This\n                                                  might be in conflict with other\n                                                  options that affect the file mode,\n                                                  like fsGroup, and the result can\n                                                  be other mode bits set.'\n                                                format: int32\n                                                type: integer\n                                              items:\n                                                description: If unspecified, each\n                                                  key-value pair in the Data field\n                                                  of the referenced ConfigMap will\n                                                  be projected into the volume as\n                                                  a file whose name is the key and\n                                                  content is the value. If specified,\n                                                  the listed keys will be projected\n                                                  into the specified paths, and unlisted\n                                                  keys will not be present. If a key\n                                                  is specified which is not present\n                                                  in the ConfigMap, the volume setup\n                                                  will error unless it is marked optional.\n                                                  Paths must be relative and may not\n                                                  contain the '..' path or start with\n                                                  '..'.\n                                                items:\n                                                  description: Maps a string key to\n                                                    a path within a volume.\n                                                  properties:\n                                                    key:\n                                                      description: The key to project.\n                                                      type: string\n                                                    mode:\n                                                      description: 'Optional: mode\n                                                        bits used to set permissions\n                                                        on this file. Must be an octal\n                                                        value between 0000 and 0777\n                                                        or a decimal value between\n                                                        0 and 511. YAML accepts both\n                                                        octal and decimal values,\n                                                        JSON requires decimal values\n                                                        for mode bits. If not specified,\n                                                        the volume defaultMode will\n                                                        be used. This might be in\n                                                        conflict with other options\n                                                        that affect the file mode,\n                                                        like fsGroup, and the result\n                                                        can be other mode bits set.'\n                                                      format: int32\n                                                      type: integer\n                                                    path:\n                                                      description: The relative path\n                                                        of the file to map the key\n                                                        to. May not be an absolute\n                                                        path. May not contain the\n                                                        path element '..'. May not\n                                                        start with the string '..'.\n                                                      type: string\n                                                  required:\n                                                  - key\n                                                  - path\n                                                  type: object\n                                                type: array\n                                              name:\n                                                description: 'Name of the referent.\n                                                  More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                  TODO: Add other useful fields. apiVersion,\n                                                  kind, uid?'\n                                                type: string\n                                              optional:\n                                                description: Specify whether the ConfigMap\n                                                  or its keys must be defined\n                                                type: boolean\n                                            type: object\n                                          csi:\n                                            description: CSI (Container Storage Interface)\n                                              represents ephemeral storage that is\n                                              handled by certain external CSI drivers\n                                              (Beta feature).\n                                            properties:\n                                              driver:\n                                                description: Driver is the name of\n                                                  the CSI driver that handles this\n                                                  volume. Consult with your admin\n                                                  for the correct name as registered\n                                                  in the cluster.\n                                                type: string\n                                              fsType:\n                                                description: Filesystem type to mount.\n                                                  Ex. \"ext4\", \"xfs\", \"ntfs\". If not\n                                                  provided, the empty value is passed\n                                                  to the associated CSI driver which\n                                                  will determine the default filesystem\n                                                  to apply.\n                                                type: string\n                                              nodePublishSecretRef:\n                                                description: NodePublishSecretRef\n                                                  is a reference to the secret object\n                                                  containing sensitive information\n                                                  to pass to the CSI driver to complete\n                                                  the CSI NodePublishVolume and NodeUnpublishVolume\n                                                  calls. This field is optional, and  may\n                                                  be empty if no secret is required.\n                                                  If the secret object contains more\n                                                  than one secret, all secret references\n                                                  are passed.\n                                                properties:\n                                                  name:\n                                                    description: 'Name of the referent.\n                                                      More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                      TODO: Add other useful fields.\n                                                      apiVersion, kind, uid?'\n                                                    type: string\n                                                type: object\n                                              readOnly:\n                                                description: Specifies a read-only\n                                                  configuration for the volume. Defaults\n                                                  to false (read/write).\n                                                type: boolean\n                                              volumeAttributes:\n                                                additionalProperties:\n                                                  type: string\n                                                description: VolumeAttributes stores\n                                                  driver-specific properties that\n                                                  are passed to the CSI driver. Consult\n                                                  your driver's documentation for\n                                                  supported values.\n                                                type: object\n                                            required:\n                                            - driver\n                                            type: object\n                                          downwardAPI:\n                                            description: DownwardAPI represents downward\n                                              API about the pod that should populate\n                                              this volume\n                                            properties:\n                                              defaultMode:\n                                                description: 'Optional: mode bits\n                                                  to use on created files by default.\n                                                  Must be a Optional: mode bits used\n                                                  to set permissions on created files\n                                                  by default. Must be an octal value\n                                                  between 0000 and 0777 or a decimal\n                                                  value between 0 and 511. YAML accepts\n                                                  both octal and decimal values, JSON\n                                                  requires decimal values for mode\n                                                  bits. Defaults to 0644. Directories\n                                                  within the path are not affected\n                                                  by this setting. This might be in\n                                                  conflict with other options that\n                                                  affect the file mode, like fsGroup,\n                                                  and the result can be other mode\n                                                  bits set.'\n                                                format: int32\n                                                type: integer\n                                              items:\n                                                description: Items is a list of downward\n                                                  API volume file\n                                                items:\n                                                  description: DownwardAPIVolumeFile\n                                                    represents information to create\n                                                    the file containing the pod field\n                                                  properties:\n                                                    fieldRef:\n                                                      description: 'Required: Selects\n                                                        a field of the pod: only annotations,\n                                                        labels, name and namespace\n                                                        are supported.'\n                                                      properties:\n                                                        apiVersion:\n                                                          description: Version of\n                                                            the schema the FieldPath\n                                                            is written in terms of,\n                                                            defaults to \"v1\".\n                                                          type: string\n                                                        fieldPath:\n                                                          description: Path of the\n                                                            field to select in the\n                                                            specified API version.\n                                                          type: string\n                                                      required:\n                                                      - fieldPath\n                                                      type: object\n                                                    mode:\n                                                      description: 'Optional: mode\n                                                        bits used to set permissions\n                                                        on this file, must be an octal\n                                                        value between 0000 and 0777\n                                                        or a decimal value between\n                                                        0 and 511. YAML accepts both\n                                                        octal and decimal values,\n                                                        JSON requires decimal values\n                                                        for mode bits. If not specified,\n                                                        the volume defaultMode will\n                                                        be used. This might be in\n                                                        conflict with other options\n                                                        that affect the file mode,\n                                                        like fsGroup, and the result\n                                                        can be other mode bits set.'\n                                                      format: int32\n                                                      type: integer\n                                                    path:\n                                                      description: 'Required: Path\n                                                        is  the relative path name\n                                                        of the file to be created.\n                                                        Must not be absolute or contain\n                                                        the ''..'' path. Must be utf-8\n                                                        encoded. The first item of\n                                                        the relative path must not\n                                                        start with ''..'''\n                                                      type: string\n                                                    resourceFieldRef:\n                                                      description: 'Selects a resource\n                                                        of the container: only resources\n                                                        limits and requests (limits.cpu,\n                                                        limits.memory, requests.cpu\n                                                        and requests.memory) are currently\n                                                        supported.'\n                                                      properties:\n                                                        containerName:\n                                                          description: 'Container\n                                                            name: required for volumes,\n                                                            optional for env vars'\n                                                          type: string\n                                                        divisor:\n                                                          anyOf:\n                                                          - type: integer\n                                                          - type: string\n                                                          description: Specifies the\n                                                            output format of the exposed\n                                                            resources, defaults to\n                                                            \"1\"\n                                                          pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                          x-kubernetes-int-or-string: true\n                                                        resource:\n                                                          description: 'Required:\n                                                            resource to select'\n                                                          type: string\n                                                      required:\n                                                      - resource\n                                                      type: object\n                                                  required:\n                                                  - path\n                                                  type: object\n                                                type: array\n                                            type: object\n                                          emptyDir:\n                                            description: 'EmptyDir represents a temporary\n                                              directory that shares a pod''s lifetime.\n                                              More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'\n                                            properties:\n                                              medium:\n                                                description: 'What type of storage\n                                                  medium should back this directory.\n                                                  The default is \"\" which means to\n                                                  use the node''s default medium.\n                                                  Must be an empty string (default)\n                                                  or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'\n                                                type: string\n                                              sizeLimit:\n                                                anyOf:\n                                                - type: integer\n                                                - type: string\n                                                description: 'Total amount of local\n                                                  storage required for this EmptyDir\n                                                  volume. The size limit is also applicable\n                                                  for memory medium. The maximum usage\n                                                  on memory medium EmptyDir would\n                                                  be the minimum value between the\n                                                  SizeLimit specified here and the\n                                                  sum of memory limits of all containers\n                                                  in a pod. The default is nil which\n                                                  means that the limit is undefined.\n                                                  More info: http://kubernetes.io/docs/user-guide/volumes#emptydir'\n                                                pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                x-kubernetes-int-or-string: true\n                                            type: object\n                                          ephemeral:\n                                            description: \"Ephemeral represents a volume\n                                              that is handled by a cluster storage\n                                              driver (Alpha feature). The volume's\n                                              lifecycle is tied to the pod that defines\n                                              it - it will be created before the pod\n                                              starts, and deleted when the pod is\n                                              removed. \\n Use this if: a) the volume\n                                              is only needed while the pod runs, b)\n                                              features of normal volumes like restoring\n                                              from snapshot or capacity    tracking\n                                              are needed, c) the storage driver is\n                                              specified through a storage class, and\n                                              d) the storage driver supports dynamic\n                                              volume provisioning through    a PersistentVolumeClaim\n                                              (see EphemeralVolumeSource for more\n                                              \\   information on the connection between\n                                              this volume type    and PersistentVolumeClaim).\n                                              \\n Use PersistentVolumeClaim or one\n                                              of the vendor-specific APIs for volumes\n                                              that persist for longer than the lifecycle\n                                              of an individual pod. \\n Use CSI for\n                                              light-weight local ephemeral volumes\n                                              if the CSI driver is meant to be used\n                                              that way - see the documentation of\n                                              the driver for more information. \\n\n                                              A pod can use both types of ephemeral\n                                              volumes and persistent volumes at the\n                                              same time.\"\n                                            properties:\n                                              readOnly:\n                                                description: Specifies a read-only\n                                                  configuration for the volume. Defaults\n                                                  to false (read/write).\n                                                type: boolean\n                                              volumeClaimTemplate:\n                                                description: \"Will be used to create\n                                                  a stand-alone PVC to provision the\n                                                  volume. The pod in which this EphemeralVolumeSource\n                                                  is embedded will be the owner of\n                                                  the PVC, i.e. the PVC will be deleted\n                                                  together with the pod.  The name\n                                                  of the PVC will be `<pod name>-<volume\n                                                  name>` where `<volume name>` is\n                                                  the name from the `PodSpec.Volumes`\n                                                  array entry. Pod validation will\n                                                  reject the pod if the concatenated\n                                                  name is not valid for a PVC (for\n                                                  example, too long). \\n An existing\n                                                  PVC with that name that is not owned\n                                                  by the pod will *not* be used for\n                                                  the pod to avoid using an unrelated\n                                                  volume by mistake. Starting the\n                                                  pod is then blocked until the unrelated\n                                                  PVC is removed. If such a pre-created\n                                                  PVC is meant to be used by the pod,\n                                                  the PVC has to updated with an owner\n                                                  reference to the pod once the pod\n                                                  exists. Normally this should not\n                                                  be necessary, but it may be useful\n                                                  when manually reconstructing a broken\n                                                  cluster. \\n This field is read-only\n                                                  and no changes will be made by Kubernetes\n                                                  to the PVC after it has been created.\n                                                  \\n Required, must not be nil.\"\n                                                properties:\n                                                  metadata:\n                                                    description: May contain labels\n                                                      and annotations that will be\n                                                      copied into the PVC when creating\n                                                      it. No other fields are allowed\n                                                      and will be rejected during\n                                                      validation.\n                                                    properties:\n                                                      annotations:\n                                                        additionalProperties:\n                                                          type: string\n                                                        type: object\n                                                      finalizers:\n                                                        items:\n                                                          type: string\n                                                        type: array\n                                                      labels:\n                                                        additionalProperties:\n                                                          type: string\n                                                        type: object\n                                                      name:\n                                                        type: string\n                                                      namespace:\n                                                        type: string\n                                                    type: object\n                                                  spec:\n                                                    description: The specification\n                                                      for the PersistentVolumeClaim.\n                                                      The entire content is copied\n                                                      unchanged into the PVC that\n                                                      gets created from this template.\n                                                      The same fields as in a PersistentVolumeClaim\n                                                      are also valid here.\n                                                    properties:\n                                                      accessModes:\n                                                        description: 'AccessModes\n                                                          contains the desired access\n                                                          modes the volume should\n                                                          have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1'\n                                                        items:\n                                                          type: string\n                                                        type: array\n                                                      dataSource:\n                                                        description: 'This field can\n                                                          be used to specify either:\n                                                          * An existing VolumeSnapshot\n                                                          object (snapshot.storage.k8s.io/VolumeSnapshot)\n                                                          * An existing PVC (PersistentVolumeClaim)\n                                                          * An existing custom resource\n                                                          that implements data population\n                                                          (Alpha) In order to use\n                                                          custom resource types that\n                                                          implement data population,\n                                                          the AnyVolumeDataSource\n                                                          feature gate must be enabled.\n                                                          If the provisioner or an\n                                                          external controller can\n                                                          support the specified data\n                                                          source, it will create a\n                                                          new volume based on the\n                                                          contents of the specified\n                                                          data source.'\n                                                        properties:\n                                                          apiGroup:\n                                                            description: APIGroup\n                                                              is the group for the\n                                                              resource being referenced.\n                                                              If APIGroup is not specified,\n                                                              the specified Kind must\n                                                              be in the core API group.\n                                                              For any other third-party\n                                                              types, APIGroup is required.\n                                                            type: string\n                                                          kind:\n                                                            description: Kind is the\n                                                              type of resource being\n                                                              referenced\n                                                            type: string\n                                                          name:\n                                                            description: Name is the\n                                                              name of resource being\n                                                              referenced\n                                                            type: string\n                                                        required:\n                                                        - kind\n                                                        - name\n                                                        type: object\n                                                      resources:\n                                                        description: 'Resources represents\n                                                          the minimum resources the\n                                                          volume should have. More\n                                                          info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources'\n                                                        properties:\n                                                          limits:\n                                                            additionalProperties:\n                                                              anyOf:\n                                                              - type: integer\n                                                              - type: string\n                                                              pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                              x-kubernetes-int-or-string: true\n                                                            description: 'Limits describes\n                                                              the maximum amount of\n                                                              compute resources allowed.\n                                                              More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'\n                                                            type: object\n                                                          requests:\n                                                            additionalProperties:\n                                                              anyOf:\n                                                              - type: integer\n                                                              - type: string\n                                                              pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                              x-kubernetes-int-or-string: true\n                                                            description: 'Requests\n                                                              describes the minimum\n                                                              amount of compute resources\n                                                              required. If Requests\n                                                              is omitted for a container,\n                                                              it defaults to Limits\n                                                              if that is explicitly\n                                                              specified, otherwise\n                                                              to an implementation-defined\n                                                              value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/'\n                                                            type: object\n                                                        type: object\n                                                      selector:\n                                                        description: A label query\n                                                          over volumes to consider\n                                                          for binding.\n                                                        properties:\n                                                          matchExpressions:\n                                                            description: matchExpressions\n                                                              is a list of label selector\n                                                              requirements. The requirements\n                                                              are ANDed.\n                                                            items:\n                                                              description: A label\n                                                                selector requirement\n                                                                is a selector that\n                                                                contains values, a\n                                                                key, and an operator\n                                                                that relates the key\n                                                                and values.\n                                                              properties:\n                                                                key:\n                                                                  description: key\n                                                                    is the label key\n                                                                    that the selector\n                                                                    applies to.\n                                                                  type: string\n                                                                operator:\n                                                                  description: operator\n                                                                    represents a key's\n                                                                    relationship to\n                                                                    a set of values.\n                                                                    Valid operators\n                                                                    are In, NotIn,\n                                                                    Exists and DoesNotExist.\n                                                                  type: string\n                                                                values:\n                                                                  description: values\n                                                                    is an array of\n                                                                    string values.\n                                                                    If the operator\n                                                                    is In or NotIn,\n                                                                    the values array\n                                                                    must be non-empty.\n                                                                    If the operator\n                                                                    is Exists or DoesNotExist,\n                                                                    the values array\n                                                                    must be empty.\n                                                                    This array is\n                                                                    replaced during\n                                                                    a strategic merge\n                                                                    patch.\n                                                                  items:\n                                                                    type: string\n                                                                  type: array\n                                                              required:\n                                                              - key\n                                                              - operator\n                                                              type: object\n                                                            type: array\n                                                          matchLabels:\n                                                            additionalProperties:\n                                                              type: string\n                                                            description: matchLabels\n                                                              is a map of {key,value}\n                                                              pairs. A single {key,value}\n                                                              in the matchLabels map\n                                                              is equivalent to an\n                                                              element of matchExpressions,\n                                                              whose key field is \"key\",\n                                                              the operator is \"In\",\n                                                              and the values array\n                                                              contains only \"value\".\n                                                              The requirements are\n                                                              ANDed.\n                                                            type: object\n                                                        type: object\n                                                      storageClassName:\n                                                        description: 'Name of the\n                                                          StorageClass required by\n                                                          the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1'\n                                                        type: string\n                                                      volumeMode:\n                                                        description: volumeMode defines\n                                                          what type of volume is required\n                                                          by the claim. Value of Filesystem\n                                                          is implied when not included\n                                                          in claim spec.\n                                                        type: string\n                                                      volumeName:\n                                                        description: VolumeName is\n                                                          the binding reference to\n                                                          the PersistentVolume backing\n                                                          this claim.\n                                                        type: string\n                                                    type: object\n                                                required:\n                                                - spec\n                                                type: object\n                                            type: object\n                                          fc:\n                                            description: FC represents a Fibre Channel\n                                              resource that is attached to a kubelet's\n                                              host machine and then exposed to the\n                                              pod.\n                                            properties:\n                                              fsType:\n                                                description: 'Filesystem type to mount.\n                                                  Must be a filesystem type supported\n                                                  by the host operating system. Ex.\n                                                  \"ext4\", \"xfs\", \"ntfs\". Implicitly\n                                                  inferred to be \"ext4\" if unspecified.\n                                                  TODO: how do we prevent errors in\n                                                  the filesystem from compromising\n                                                  the machine'\n                                                type: string\n                                              lun:\n                                                description: 'Optional: FC target\n                                                  lun number'\n                                                format: int32\n                                                type: integer\n                                              readOnly:\n                                                description: 'Optional: Defaults to\n                                                  false (read/write). ReadOnly here\n                                                  will force the ReadOnly setting\n                                                  in VolumeMounts.'\n                                                type: boolean\n                                              targetWWNs:\n                                                description: 'Optional: FC target\n                                                  worldwide names (WWNs)'\n                                                items:\n                                                  type: string\n                                                type: array\n                                              wwids:\n                                                description: 'Optional: FC volume\n                                                  world wide identifiers (wwids) Either\n                                                  wwids or combination of targetWWNs\n                                                  and lun must be set, but not both\n                                                  simultaneously.'\n                                                items:\n                                                  type: string\n                                                type: array\n                                            type: object\n                                          flexVolume:\n                                            description: FlexVolume represents a generic\n                                              volume resource that is provisioned/attached\n                                              using an exec based plugin.\n                                            properties:\n                                              driver:\n                                                description: Driver is the name of\n                                                  the driver to use for this volume.\n                                                type: string\n                                              fsType:\n                                                description: Filesystem type to mount.\n                                                  Must be a filesystem type supported\n                                                  by the host operating system. Ex.\n                                                  \"ext4\", \"xfs\", \"ntfs\". The default\n                                                  filesystem depends on FlexVolume\n                                                  script.\n                                                type: string\n                                              options:\n                                                additionalProperties:\n                                                  type: string\n                                                description: 'Optional: Extra command\n                                                  options if any.'\n                                                type: object\n                                              readOnly:\n                                                description: 'Optional: Defaults to\n                                                  false (read/write). ReadOnly here\n                                                  will force the ReadOnly setting\n                                                  in VolumeMounts.'\n                                                type: boolean\n                                              secretRef:\n                                                description: 'Optional: SecretRef\n                                                  is reference to the secret object\n                                                  containing sensitive information\n                                                  to pass to the plugin scripts. This\n                                                  may be empty if no secret object\n                                                  is specified. If the secret object\n                                                  contains more than one secret, all\n                                                  secrets are passed to the plugin\n                                                  scripts.'\n                                                properties:\n                                                  name:\n                                                    description: 'Name of the referent.\n                                                      More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                      TODO: Add other useful fields.\n                                                      apiVersion, kind, uid?'\n                                                    type: string\n                                                type: object\n                                            required:\n                                            - driver\n                                            type: object\n                                          flocker:\n                                            description: Flocker represents a Flocker\n                                              volume attached to a kubelet's host\n                                              machine. This depends on the Flocker\n                                              control service being running\n                                            properties:\n                                              datasetName:\n                                                description: Name of the dataset stored\n                                                  as metadata -> name on the dataset\n                                                  for Flocker should be considered\n                                                  as deprecated\n                                                type: string\n                                              datasetUUID:\n                                                description: UUID of the dataset.\n                                                  This is unique identifier of a Flocker\n                                                  dataset\n                                                type: string\n                                            type: object\n                                          gcePersistentDisk:\n                                            description: 'GCEPersistentDisk represents\n                                              a GCE Disk resource that is attached\n                                              to a kubelet''s host machine and then\n                                              exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'\n                                            properties:\n                                              fsType:\n                                                description: 'Filesystem type of the\n                                                  volume that you want to mount. Tip:\n                                                  Ensure that the filesystem type\n                                                  is supported by the host operating\n                                                  system. Examples: \"ext4\", \"xfs\",\n                                                  \"ntfs\". Implicitly inferred to be\n                                                  \"ext4\" if unspecified. More info:\n                                                  https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk\n                                                  TODO: how do we prevent errors in\n                                                  the filesystem from compromising\n                                                  the machine'\n                                                type: string\n                                              partition:\n                                                description: 'The partition in the\n                                                  volume that you want to mount. If\n                                                  omitted, the default is to mount\n                                                  by volume name. Examples: For volume\n                                                  /dev/sda1, you specify the partition\n                                                  as \"1\". Similarly, the volume partition\n                                                  for /dev/sda is \"0\" (or you can\n                                                  leave the property empty). More\n                                                  info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'\n                                                format: int32\n                                                type: integer\n                                              pdName:\n                                                description: 'Unique name of the PD\n                                                  resource in GCE. Used to identify\n                                                  the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'\n                                                type: string\n                                              readOnly:\n                                                description: 'ReadOnly here will force\n                                                  the ReadOnly setting in VolumeMounts.\n                                                  Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk'\n                                                type: boolean\n                                            required:\n                                            - pdName\n                                            type: object\n                                          gitRepo:\n                                            description: 'GitRepo represents a git\n                                              repository at a particular revision.\n                                              DEPRECATED: GitRepo is deprecated. To\n                                              provision a container with a git repo,\n                                              mount an EmptyDir into an InitContainer\n                                              that clones the repo using git, then\n                                              mount the EmptyDir into the Pod''s container.'\n                                            properties:\n                                              directory:\n                                                description: Target directory name.\n                                                  Must not contain or start with '..'.  If\n                                                  '.' is supplied, the volume directory\n                                                  will be the git repository.  Otherwise,\n                                                  if specified, the volume will contain\n                                                  the git repository in the subdirectory\n                                                  with the given name.\n                                                type: string\n                                              repository:\n                                                description: Repository URL\n                                                type: string\n                                              revision:\n                                                description: Commit hash for the specified\n                                                  revision.\n                                                type: string\n                                            required:\n                                            - repository\n                                            type: object\n                                          glusterfs:\n                                            description: 'Glusterfs represents a Glusterfs\n                                              mount on the host that shares a pod''s\n                                              lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md'\n                                            properties:\n                                              endpoints:\n                                                description: 'EndpointsName is the\n                                                  endpoint name that details Glusterfs\n                                                  topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'\n                                                type: string\n                                              path:\n                                                description: 'Path is the Glusterfs\n                                                  volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'\n                                                type: string\n                                              readOnly:\n                                                description: 'ReadOnly here will force\n                                                  the Glusterfs volume to be mounted\n                                                  with read-only permissions. Defaults\n                                                  to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod'\n                                                type: boolean\n                                            required:\n                                            - endpoints\n                                            - path\n                                            type: object\n                                          hostPath:\n                                            description: 'HostPath represents a pre-existing\n                                              file or directory on the host machine\n                                              that is directly exposed to the container.\n                                              This is generally used for system agents\n                                              or other privileged things that are\n                                              allowed to see the host machine. Most\n                                              containers will NOT need this. More\n                                              info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath\n                                              --- TODO(jonesdl) We need to restrict\n                                              who can use host directory mounts and\n                                              who can/can not mount host directories\n                                              as read/write.'\n                                            properties:\n                                              path:\n                                                description: 'Path of the directory\n                                                  on the host. If the path is a symlink,\n                                                  it will follow the link to the real\n                                                  path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath'\n                                                type: string\n                                              type:\n                                                description: 'Type for HostPath Volume\n                                                  Defaults to \"\" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath'\n                                                type: string\n                                            required:\n                                            - path\n                                            type: object\n                                          iscsi:\n                                            description: 'ISCSI represents an ISCSI\n                                              Disk resource that is attached to a\n                                              kubelet''s host machine and then exposed\n                                              to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md'\n                                            properties:\n                                              chapAuthDiscovery:\n                                                description: whether support iSCSI\n                                                  Discovery CHAP authentication\n                                                type: boolean\n                                              chapAuthSession:\n                                                description: whether support iSCSI\n                                                  Session CHAP authentication\n                                                type: boolean\n                                              fsType:\n                                                description: 'Filesystem type of the\n                                                  volume that you want to mount. Tip:\n                                                  Ensure that the filesystem type\n                                                  is supported by the host operating\n                                                  system. Examples: \"ext4\", \"xfs\",\n                                                  \"ntfs\". Implicitly inferred to be\n                                                  \"ext4\" if unspecified. More info:\n                                                  https://kubernetes.io/docs/concepts/storage/volumes#iscsi\n                                                  TODO: how do we prevent errors in\n                                                  the filesystem from compromising\n                                                  the machine'\n                                                type: string\n                                              initiatorName:\n                                                description: Custom iSCSI Initiator\n                                                  Name. If initiatorName is specified\n                                                  with iscsiInterface simultaneously,\n                                                  new iSCSI interface <target portal>:<volume\n                                                  name> will be created for the connection.\n                                                type: string\n                                              iqn:\n                                                description: Target iSCSI Qualified\n                                                  Name.\n                                                type: string\n                                              iscsiInterface:\n                                                description: iSCSI Interface Name\n                                                  that uses an iSCSI transport. Defaults\n                                                  to 'default' (tcp).\n                                                type: string\n                                              lun:\n                                                description: iSCSI Target Lun number.\n                                                format: int32\n                                                type: integer\n                                              portals:\n                                                description: iSCSI Target Portal List.\n                                                  The portal is either an IP or ip_addr:port\n                                                  if the port is other than default\n                                                  (typically TCP ports 860 and 3260).\n                                                items:\n                                                  type: string\n                                                type: array\n                                              readOnly:\n                                                description: ReadOnly here will force\n                                                  the ReadOnly setting in VolumeMounts.\n                                                  Defaults to false.\n                                                type: boolean\n                                              secretRef:\n                                                description: CHAP Secret for iSCSI\n                                                  target and initiator authentication\n                                                properties:\n                                                  name:\n                                                    description: 'Name of the referent.\n                                                      More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                      TODO: Add other useful fields.\n                                                      apiVersion, kind, uid?'\n                                                    type: string\n                                                type: object\n                                              targetPortal:\n                                                description: iSCSI Target Portal.\n                                                  The Portal is either an IP or ip_addr:port\n                                                  if the port is other than default\n                                                  (typically TCP ports 860 and 3260).\n                                                type: string\n                                            required:\n                                            - iqn\n                                            - lun\n                                            - targetPortal\n                                            type: object\n                                          name:\n                                            description: 'Volume''s name. Must be\n                                              a DNS_LABEL and unique within the pod.\n                                              More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'\n                                            type: string\n                                          nfs:\n                                            description: 'NFS represents an NFS mount\n                                              on the host that shares a pod''s lifetime\n                                              More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'\n                                            properties:\n                                              path:\n                                                description: 'Path that is exported\n                                                  by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'\n                                                type: string\n                                              readOnly:\n                                                description: 'ReadOnly here will force\n                                                  the NFS export to be mounted with\n                                                  read-only permissions. Defaults\n                                                  to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'\n                                                type: boolean\n                                              server:\n                                                description: 'Server is the hostname\n                                                  or IP address of the NFS server.\n                                                  More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs'\n                                                type: string\n                                            required:\n                                            - path\n                                            - server\n                                            type: object\n                                          persistentVolumeClaim:\n                                            description: 'PersistentVolumeClaimVolumeSource\n                                              represents a reference to a PersistentVolumeClaim\n                                              in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'\n                                            properties:\n                                              claimName:\n                                                description: 'ClaimName is the name\n                                                  of a PersistentVolumeClaim in the\n                                                  same namespace as the pod using\n                                                  this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims'\n                                                type: string\n                                              readOnly:\n                                                description: Will force the ReadOnly\n                                                  setting in VolumeMounts. Default\n                                                  false.\n                                                type: boolean\n                                            required:\n                                            - claimName\n                                            type: object\n                                          photonPersistentDisk:\n                                            description: PhotonPersistentDisk represents\n                                              a PhotonController persistent disk attached\n                                              and mounted on kubelets host machine\n                                            properties:\n                                              fsType:\n                                                description: Filesystem type to mount.\n                                                  Must be a filesystem type supported\n                                                  by the host operating system. Ex.\n                                                  \"ext4\", \"xfs\", \"ntfs\". Implicitly\n                                                  inferred to be \"ext4\" if unspecified.\n                                                type: string\n                                              pdID:\n                                                description: ID that identifies Photon\n                                                  Controller persistent disk\n                                                type: string\n                                            required:\n                                            - pdID\n                                            type: object\n                                          portworxVolume:\n                                            description: PortworxVolume represents\n                                              a portworx volume attached and mounted\n                                              on kubelets host machine\n                                            properties:\n                                              fsType:\n                                                description: FSType represents the\n                                                  filesystem type to mount Must be\n                                                  a filesystem type supported by the\n                                                  host operating system. Ex. \"ext4\",\n                                                  \"xfs\". Implicitly inferred to be\n                                                  \"ext4\" if unspecified.\n                                                type: string\n                                              readOnly:\n                                                description: Defaults to false (read/write).\n                                                  ReadOnly here will force the ReadOnly\n                                                  setting in VolumeMounts.\n                                                type: boolean\n                                              volumeID:\n                                                description: VolumeID uniquely identifies\n                                                  a Portworx volume\n                                                type: string\n                                            required:\n                                            - volumeID\n                                            type: object\n                                          projected:\n                                            description: Items for all in one resources\n                                              secrets, configmaps, and downward API\n                                            properties:\n                                              defaultMode:\n                                                description: Mode bits used to set\n                                                  permissions on created files by\n                                                  default. Must be an octal value\n                                                  between 0000 and 0777 or a decimal\n                                                  value between 0 and 511. YAML accepts\n                                                  both octal and decimal values, JSON\n                                                  requires decimal values for mode\n                                                  bits. Directories within the path\n                                                  are not affected by this setting.\n                                                  This might be in conflict with other\n                                                  options that affect the file mode,\n                                                  like fsGroup, and the result can\n                                                  be other mode bits set.\n                                                format: int32\n                                                type: integer\n                                              sources:\n                                                description: list of volume projections\n                                                items:\n                                                  description: Projection that may\n                                                    be projected along with other\n                                                    supported volume types\n                                                  properties:\n                                                    configMap:\n                                                      description: information about\n                                                        the configMap data to project\n                                                      properties:\n                                                        items:\n                                                          description: If unspecified,\n                                                            each key-value pair in\n                                                            the Data field of the\n                                                            referenced ConfigMap will\n                                                            be projected into the\n                                                            volume as a file whose\n                                                            name is the key and content\n                                                            is the value. If specified,\n                                                            the listed keys will be\n                                                            projected into the specified\n                                                            paths, and unlisted keys\n                                                            will not be present. If\n                                                            a key is specified which\n                                                            is not present in the\n                                                            ConfigMap, the volume\n                                                            setup will error unless\n                                                            it is marked optional.\n                                                            Paths must be relative\n                                                            and may not contain the\n                                                            '..' path or start with\n                                                            '..'.\n                                                          items:\n                                                            description: Maps a string\n                                                              key to a path within\n                                                              a volume.\n                                                            properties:\n                                                              key:\n                                                                description: The key\n                                                                  to project.\n                                                                type: string\n                                                              mode:\n                                                                description: 'Optional:\n                                                                  mode bits used to\n                                                                  set permissions\n                                                                  on this file. Must\n                                                                  be an octal value\n                                                                  between 0000 and\n                                                                  0777 or a decimal\n                                                                  value between 0\n                                                                  and 511. YAML accepts\n                                                                  both octal and decimal\n                                                                  values, JSON requires\n                                                                  decimal values for\n                                                                  mode bits. If not\n                                                                  specified, the volume\n                                                                  defaultMode will\n                                                                  be used. This might\n                                                                  be in conflict with\n                                                                  other options that\n                                                                  affect the file\n                                                                  mode, like fsGroup,\n                                                                  and the result can\n                                                                  be other mode bits\n                                                                  set.'\n                                                                format: int32\n                                                                type: integer\n                                                              path:\n                                                                description: The relative\n                                                                  path of the file\n                                                                  to map the key to.\n                                                                  May not be an absolute\n                                                                  path. May not contain\n                                                                  the path element\n                                                                  '..'. May not start\n                                                                  with the string\n                                                                  '..'.\n                                                                type: string\n                                                            required:\n                                                            - key\n                                                            - path\n                                                            type: object\n                                                          type: array\n                                                        name:\n                                                          description: 'Name of the\n                                                            referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                            TODO: Add other useful\n                                                            fields. apiVersion, kind,\n                                                            uid?'\n                                                          type: string\n                                                        optional:\n                                                          description: Specify whether\n                                                            the ConfigMap or its keys\n                                                            must be defined\n                                                          type: boolean\n                                                      type: object\n                                                    downwardAPI:\n                                                      description: information about\n                                                        the downwardAPI data to project\n                                                      properties:\n                                                        items:\n                                                          description: Items is a\n                                                            list of DownwardAPIVolume\n                                                            file\n                                                          items:\n                                                            description: DownwardAPIVolumeFile\n                                                              represents information\n                                                              to create the file containing\n                                                              the pod field\n                                                            properties:\n                                                              fieldRef:\n                                                                description: 'Required:\n                                                                  Selects a field\n                                                                  of the pod: only\n                                                                  annotations, labels,\n                                                                  name and namespace\n                                                                  are supported.'\n                                                                properties:\n                                                                  apiVersion:\n                                                                    description: Version\n                                                                      of the schema\n                                                                      the FieldPath\n                                                                      is written in\n                                                                      terms of, defaults\n                                                                      to \"v1\".\n                                                                    type: string\n                                                                  fieldPath:\n                                                                    description: Path\n                                                                      of the field\n                                                                      to select in\n                                                                      the specified\n                                                                      API version.\n                                                                    type: string\n                                                                required:\n                                                                - fieldPath\n                                                                type: object\n                                                              mode:\n                                                                description: 'Optional:\n                                                                  mode bits used to\n                                                                  set permissions\n                                                                  on this file, must\n                                                                  be an octal value\n                                                                  between 0000 and\n                                                                  0777 or a decimal\n                                                                  value between 0\n                                                                  and 511. YAML accepts\n                                                                  both octal and decimal\n                                                                  values, JSON requires\n                                                                  decimal values for\n                                                                  mode bits. If not\n                                                                  specified, the volume\n                                                                  defaultMode will\n                                                                  be used. This might\n                                                                  be in conflict with\n                                                                  other options that\n                                                                  affect the file\n                                                                  mode, like fsGroup,\n                                                                  and the result can\n                                                                  be other mode bits\n                                                                  set.'\n                                                                format: int32\n                                                                type: integer\n                                                              path:\n                                                                description: 'Required:\n                                                                  Path is  the relative\n                                                                  path name of the\n                                                                  file to be created.\n                                                                  Must not be absolute\n                                                                  or contain the ''..''\n                                                                  path. Must be utf-8\n                                                                  encoded. The first\n                                                                  item of the relative\n                                                                  path must not start\n                                                                  with ''..'''\n                                                                type: string\n                                                              resourceFieldRef:\n                                                                description: 'Selects\n                                                                  a resource of the\n                                                                  container: only\n                                                                  resources limits\n                                                                  and requests (limits.cpu,\n                                                                  limits.memory, requests.cpu\n                                                                  and requests.memory)\n                                                                  are currently supported.'\n                                                                properties:\n                                                                  containerName:\n                                                                    description: 'Container\n                                                                      name: required\n                                                                      for volumes,\n                                                                      optional for\n                                                                      env vars'\n                                                                    type: string\n                                                                  divisor:\n                                                                    anyOf:\n                                                                    - type: integer\n                                                                    - type: string\n                                                                    description: Specifies\n                                                                      the output format\n                                                                      of the exposed\n                                                                      resources, defaults\n                                                                      to \"1\"\n                                                                    pattern: ^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$\n                                                                    x-kubernetes-int-or-string: true\n                                                                  resource:\n                                                                    description: 'Required:\n                                                                      resource to\n                                                                      select'\n                                                                    type: string\n                                                                required:\n                                                                - resource\n                                                                type: object\n                                                            required:\n                                                            - path\n                                                            type: object\n                                                          type: array\n                                                      type: object\n                                                    secret:\n                                                      description: information about\n                                                        the secret data to project\n                                                      properties:\n                                                        items:\n                                                          description: If unspecified,\n                                                            each key-value pair in\n                                                            the Data field of the\n                                                            referenced Secret will\n                                                            be projected into the\n                                                            volume as a file whose\n                                                            name is the key and content\n                                                            is the value. If specified,\n                                                            the listed keys will be\n                                                            projected into the specified\n                                                            paths, and unlisted keys\n                                                            will not be present. If\n                                                            a key is specified which\n                                                            is not present in the\n                                                            Secret, the volume setup\n                                                            will error unless it is\n                                                            marked optional. Paths\n                                                            must be relative and may\n                                                            not contain the '..' path\n                                                            or start with '..'.\n                                                          items:\n                                                            description: Maps a string\n                                                              key to a path within\n                                                              a volume.\n                                                            properties:\n                                                              key:\n                                                                description: The key\n                                                                  to project.\n                                                                type: string\n                                                              mode:\n                                                                description: 'Optional:\n                                                                  mode bits used to\n                                                                  set permissions\n                                                                  on this file. Must\n                                                                  be an octal value\n                                                                  between 0000 and\n                                                                  0777 or a decimal\n                                                                  value between 0\n                                                                  and 511. YAML accepts\n                                                                  both octal and decimal\n                                                                  values, JSON requires\n                                                                  decimal values for\n                                                                  mode bits. If not\n                                                                  specified, the volume\n                                                                  defaultMode will\n                                                                  be used. This might\n                                                                  be in conflict with\n                                                                  other options that\n                                                                  affect the file\n                                                                  mode, like fsGroup,\n                                                                  and the result can\n                                                                  be other mode bits\n                                                                  set.'\n                                                                format: int32\n                                                                type: integer\n                                                              path:\n                                                                description: The relative\n                                                                  path of the file\n                                                                  to map the key to.\n                                                                  May not be an absolute\n                                                                  path. May not contain\n                                                                  the path element\n                                                                  '..'. May not start\n                                                                  with the string\n                                                                  '..'.\n                                                                type: string\n                                                            required:\n                                                            - key\n                                                            - path\n                                                            type: object\n                                                          type: array\n                                                        name:\n                                                          description: 'Name of the\n                                                            referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                            TODO: Add other useful\n                                                            fields. apiVersion, kind,\n                                                            uid?'\n                                                          type: string\n                                                        optional:\n                                                          description: Specify whether\n                                                            the Secret or its key\n                                                            must be defined\n                                                          type: boolean\n                                                      type: object\n                                                    serviceAccountToken:\n                                                      description: information about\n                                                        the serviceAccountToken data\n                                                        to project\n                                                      properties:\n                                                        audience:\n                                                          description: Audience is\n                                                            the intended audience\n                                                            of the token. A recipient\n                                                            of a token must identify\n                                                            itself with an identifier\n                                                            specified in the audience\n                                                            of the token, and otherwise\n                                                            should reject the token.\n                                                            The audience defaults\n                                                            to the identifier of the\n                                                            apiserver.\n                                                          type: string\n                                                        expirationSeconds:\n                                                          description: ExpirationSeconds\n                                                            is the requested duration\n                                                            of validity of the service\n                                                            account token. As the\n                                                            token approaches expiration,\n                                                            the kubelet volume plugin\n                                                            will proactively rotate\n                                                            the service account token.\n                                                            The kubelet will start\n                                                            trying to rotate the token\n                                                            if the token is older\n                                                            than 80 percent of its\n                                                            time to live or if the\n                                                            token is older than 24\n                                                            hours.Defaults to 1 hour\n                                                            and must be at least 10\n                                                            minutes.\n                                                          format: int64\n                                                          type: integer\n                                                        path:\n                                                          description: Path is the\n                                                            path relative to the mount\n                                                            point of the file to project\n                                                            the token into.\n                                                          type: string\n                                                      required:\n                                                      - path\n                                                      type: object\n                                                  type: object\n                                                type: array\n                                            type: object\n                                          quobyte:\n                                            description: Quobyte represents a Quobyte\n                                              mount on the host that shares a pod's\n                                              lifetime\n                                            properties:\n                                              group:\n                                                description: Group to map volume access\n                                                  to Default is no group\n                                                type: string\n                                              readOnly:\n                                                description: ReadOnly here will force\n                                                  the Quobyte volume to be mounted\n                                                  with read-only permissions. Defaults\n                                                  to false.\n                                                type: boolean\n                                              registry:\n                                                description: Registry represents a\n                                                  single or multiple Quobyte Registry\n                                                  services specified as a string as\n                                                  host:port pair (multiple entries\n                                                  are separated with commas) which\n                                                  acts as the central registry for\n                                                  volumes\n                                                type: string\n                                              tenant:\n                                                description: Tenant owning the given\n                                                  Quobyte volume in the Backend Used\n                                                  with dynamically provisioned Quobyte\n                                                  volumes, value is set by the plugin\n                                                type: string\n                                              user:\n                                                description: User to map volume access\n                                                  to Defaults to serivceaccount user\n                                                type: string\n                                              volume:\n                                                description: Volume is a string that\n                                                  references an already created Quobyte\n                                                  volume by name.\n                                                type: string\n                                            required:\n                                            - registry\n                                            - volume\n                                            type: object\n                                          rbd:\n                                            description: 'RBD represents a Rados Block\n                                              Device mount on the host that shares\n                                              a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md'\n                                            properties:\n                                              fsType:\n                                                description: 'Filesystem type of the\n                                                  volume that you want to mount. Tip:\n                                                  Ensure that the filesystem type\n                                                  is supported by the host operating\n                                                  system. Examples: \"ext4\", \"xfs\",\n                                                  \"ntfs\". Implicitly inferred to be\n                                                  \"ext4\" if unspecified. More info:\n                                                  https://kubernetes.io/docs/concepts/storage/volumes#rbd\n                                                  TODO: how do we prevent errors in\n                                                  the filesystem from compromising\n                                                  the machine'\n                                                type: string\n                                              image:\n                                                description: 'The rados image name.\n                                                  More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'\n                                                type: string\n                                              keyring:\n                                                description: 'Keyring is the path\n                                                  to key ring for RBDUser. Default\n                                                  is /etc/ceph/keyring. More info:\n                                                  https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'\n                                                type: string\n                                              monitors:\n                                                description: 'A collection of Ceph\n                                                  monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'\n                                                items:\n                                                  type: string\n                                                type: array\n                                              pool:\n                                                description: 'The rados pool name.\n                                                  Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'\n                                                type: string\n                                              readOnly:\n                                                description: 'ReadOnly here will force\n                                                  the ReadOnly setting in VolumeMounts.\n                                                  Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'\n                                                type: boolean\n                                              secretRef:\n                                                description: 'SecretRef is name of\n                                                  the authentication secret for RBDUser.\n                                                  If provided overrides keyring. Default\n                                                  is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'\n                                                properties:\n                                                  name:\n                                                    description: 'Name of the referent.\n                                                      More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                      TODO: Add other useful fields.\n                                                      apiVersion, kind, uid?'\n                                                    type: string\n                                                type: object\n                                              user:\n                                                description: 'The rados user name.\n                                                  Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it'\n                                                type: string\n                                            required:\n                                            - image\n                                            - monitors\n                                            type: object\n                                          scaleIO:\n                                            description: ScaleIO represents a ScaleIO\n                                              persistent volume attached and mounted\n                                              on Kubernetes nodes.\n                                            properties:\n                                              fsType:\n                                                description: Filesystem type to mount.\n                                                  Must be a filesystem type supported\n                                                  by the host operating system. Ex.\n                                                  \"ext4\", \"xfs\", \"ntfs\". Default is\n                                                  \"xfs\".\n                                                type: string\n                                              gateway:\n                                                description: The host address of the\n                                                  ScaleIO API Gateway.\n                                                type: string\n                                              protectionDomain:\n                                                description: The name of the ScaleIO\n                                                  Protection Domain for the configured\n                                                  storage.\n                                                type: string\n                                              readOnly:\n                                                description: Defaults to false (read/write).\n                                                  ReadOnly here will force the ReadOnly\n                                                  setting in VolumeMounts.\n                                                type: boolean\n                                              secretRef:\n                                                description: SecretRef references\n                                                  to the secret for ScaleIO user and\n                                                  other sensitive information. If\n                                                  this is not provided, Login operation\n                                                  will fail.\n                                                properties:\n                                                  name:\n                                                    description: 'Name of the referent.\n                                                      More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                      TODO: Add other useful fields.\n                                                      apiVersion, kind, uid?'\n                                                    type: string\n                                                type: object\n                                              sslEnabled:\n                                                description: Flag to enable/disable\n                                                  SSL communication with Gateway,\n                                                  default false\n                                                type: boolean\n                                              storageMode:\n                                                description: Indicates whether the\n                                                  storage for a volume should be ThickProvisioned\n                                                  or ThinProvisioned. Default is ThinProvisioned.\n                                                type: string\n                                              storagePool:\n                                                description: The ScaleIO Storage Pool\n                                                  associated with the protection domain.\n                                                type: string\n                                              system:\n                                                description: The name of the storage\n                                                  system as configured in ScaleIO.\n                                                type: string\n                                              volumeName:\n                                                description: The name of a volume\n                                                  already created in the ScaleIO system\n                                                  that is associated with this volume\n                                                  source.\n                                                type: string\n                                            required:\n                                            - gateway\n                                            - secretRef\n                                            - system\n                                            type: object\n                                          secret:\n                                            description: 'Secret represents a secret\n                                              that should populate this volume. More\n                                              info: https://kubernetes.io/docs/concepts/storage/volumes#secret'\n                                            properties:\n                                              defaultMode:\n                                                description: 'Optional: mode bits\n                                                  used to set permissions on created\n                                                  files by default. Must be an octal\n                                                  value between 0000 and 0777 or a\n                                                  decimal value between 0 and 511.\n                                                  YAML accepts both octal and decimal\n                                                  values, JSON requires decimal values\n                                                  for mode bits. Defaults to 0644.\n                                                  Directories within the path are\n                                                  not affected by this setting. This\n                                                  might be in conflict with other\n                                                  options that affect the file mode,\n                                                  like fsGroup, and the result can\n                                                  be other mode bits set.'\n                                                format: int32\n                                                type: integer\n                                              items:\n                                                description: If unspecified, each\n                                                  key-value pair in the Data field\n                                                  of the referenced Secret will be\n                                                  projected into the volume as a file\n                                                  whose name is the key and content\n                                                  is the value. If specified, the\n                                                  listed keys will be projected into\n                                                  the specified paths, and unlisted\n                                                  keys will not be present. If a key\n                                                  is specified which is not present\n                                                  in the Secret, the volume setup\n                                                  will error unless it is marked optional.\n                                                  Paths must be relative and may not\n                                                  contain the '..' path or start with\n                                                  '..'.\n                                                items:\n                                                  description: Maps a string key to\n                                                    a path within a volume.\n                                                  properties:\n                                                    key:\n                                                      description: The key to project.\n                                                      type: string\n                                                    mode:\n                                                      description: 'Optional: mode\n                                                        bits used to set permissions\n                                                        on this file. Must be an octal\n                                                        value between 0000 and 0777\n                                                        or a decimal value between\n                                                        0 and 511. YAML accepts both\n                                                        octal and decimal values,\n                                                        JSON requires decimal values\n                                                        for mode bits. If not specified,\n                                                        the volume defaultMode will\n                                                        be used. This might be in\n                                                        conflict with other options\n                                                        that affect the file mode,\n                                                        like fsGroup, and the result\n                                                        can be other mode bits set.'\n                                                      format: int32\n                                                      type: integer\n                                                    path:\n                                                      description: The relative path\n                                                        of the file to map the key\n                                                        to. May not be an absolute\n                                                        path. May not contain the\n                                                        path element '..'. May not\n                                                        start with the string '..'.\n                                                      type: string\n                                                  required:\n                                                  - key\n                                                  - path\n                                                  type: object\n                                                type: array\n                                              optional:\n                                                description: Specify whether the Secret\n                                                  or its keys must be defined\n                                                type: boolean\n                                              secretName:\n                                                description: 'Name of the secret in\n                                                  the pod''s namespace to use. More\n                                                  info: https://kubernetes.io/docs/concepts/storage/volumes#secret'\n                                                type: string\n                                            type: object\n                                          storageos:\n                                            description: StorageOS represents a StorageOS\n                                              volume attached and mounted on Kubernetes\n                                              nodes.\n                                            properties:\n                                              fsType:\n                                                description: Filesystem type to mount.\n                                                  Must be a filesystem type supported\n                                                  by the host operating system. Ex.\n                                                  \"ext4\", \"xfs\", \"ntfs\". Implicitly\n                                                  inferred to be \"ext4\" if unspecified.\n                                                type: string\n                                              readOnly:\n                                                description: Defaults to false (read/write).\n                                                  ReadOnly here will force the ReadOnly\n                                                  setting in VolumeMounts.\n                                                type: boolean\n                                              secretRef:\n                                                description: SecretRef specifies the\n                                                  secret to use for obtaining the\n                                                  StorageOS API credentials.  If not\n                                                  specified, default values will be\n                                                  attempted.\n                                                properties:\n                                                  name:\n                                                    description: 'Name of the referent.\n                                                      More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names\n                                                      TODO: Add other useful fields.\n                                                      apiVersion, kind, uid?'\n                                                    type: string\n                                                type: object\n                                              volumeName:\n                                                description: VolumeName is the human-readable\n                                                  name of the StorageOS volume.  Volume\n                                                  names are only unique within a namespace.\n                                                type: string\n                                              volumeNamespace:\n                                                description: VolumeNamespace specifies\n                                                  the scope of the volume within StorageOS.  If\n                                                  no namespace is specified then the\n                                                  Pod's namespace will be used.  This\n                                                  allows the Kubernetes name scoping\n                                                  to be mirrored within StorageOS\n                                                  for tighter integration. Set VolumeName\n                                                  to any name to override the default\n                                                  behaviour. Set to \"default\" if you\n                                                  are not using namespaces within\n                                                  StorageOS. Namespaces that do not\n                                                  pre-exist within StorageOS will\n                                                  be created.\n                                                type: string\n                                            type: object\n                                          vsphereVolume:\n                                            description: VsphereVolume represents\n                                              a vSphere volume attached and mounted\n                                              on kubelets host machine\n                                            properties:\n                                              fsType:\n                                                description: Filesystem type to mount.\n                                                  Must be a filesystem type supported\n                                                  by the host operating system. Ex.\n                                                  \"ext4\", \"xfs\", \"ntfs\". Implicitly\n                                                  inferred to be \"ext4\" if unspecified.\n                                                type: string\n                                              storagePolicyID:\n                                                description: Storage Policy Based\n                                                  Management (SPBM) profile ID associated\n                                                  with the StoragePolicyName.\n                                                type: string\n                                              storagePolicyName:\n                                                description: Storage Policy Based\n                                                  Management (SPBM) profile name.\n                                                type: string\n                                              volumePath:\n                                                description: Path that identifies\n                                                  vSphere volume vmdk\n                                                type: string\n                                            required:\n                                            - volumePath\n                                            type: object\n                                        required:\n                                        - name\n                                        type: object\n                                      type: array\n                                  required:\n                                  - containers\n                                  type: object\n                              type: object\n                          required:\n                          - selector\n                          - template\n                          type: object\n                      required:\n                      - spec\n                      type: object\n                  required:\n                  - name\n                  - template\n                  type: object\n                type: array\n              selector:\n                description: selector is a label query over deployment. It must match\n                  the deployment template's labels.\n                properties:\n                  matchExpressions:\n                    description: matchExpressions is a list of label selector requirements.\n                      The requirements are ANDed.\n                    items:\n                      description: A label selector requirement is a selector that\n                        contains values, a key, and an operator that relates the key\n                        and values.\n                      properties:\n                        key:\n                          description: key is the label key that the selector applies\n                            to.\n                          type: string\n                        operator:\n                          description: operator represents a key's relationship to\n                            a set of values. Valid operators are In, NotIn, Exists\n                            and DoesNotExist.\n                          type: string\n                        values:\n                          description: values is an array of string values. If the\n                            operator is In or NotIn, the values array must be non-empty.\n                            If the operator is Exists or DoesNotExist, the values\n                            array must be empty. This array is replaced during a strategic\n                            merge patch.\n                          items:\n                            type: string\n                          type: array\n                      required:\n                      - key\n                      - operator\n                      type: object\n                    type: array\n                  matchLabels:\n                    additionalProperties:\n                      type: string\n                    description: matchLabels is a map of {key,value} pairs. A single\n                      {key,value} in the matchLabels map is equivalent to an element\n                      of matchExpressions, whose key field is \"key\", the operator\n                      is \"In\", and the values array contains only \"value\". The requirements\n                      are ANDed.\n                    type: object\n                type: object\n            required:\n            - roles\n            - selector\n            type: object\n          status:\n            description: MLServiceStatus defines the observed state of MLService\n            properties:\n              lastTransitionTime:\n                description: LastTransitionTime is time the last Phase transitioned\n                  to current one.\n                format: date-time\n                type: string\n              message:\n                description: Human-readable message indicating details about last\n                  transition.\n                type: string\n              phase:\n                description: Phase is a simple, high-level summary of where the Service\n                  is in its lifecycle.\n                type: string\n              reason:\n                description: Unique, one-word, CamelCase reason for the phase's last\n                  transition.\n                type: string\n              roleServiceClusterIps:\n                additionalProperties:\n                  type: string\n                description: RoleServiceClusterIps shows the cluster ip for all Services.\n                  The key is Service name, value is its clusterIP\n                type: object\n              roleServiceStatusMap:\n                additionalProperties:\n                  description: ServiceStatus represents the current status of a service.\n                  properties:\n                    conditions:\n                      description: Current service state\n                      items:\n                        description: \"Condition contains details for one aspect of\n                          the current state of this API Resource. --- This struct\n                          is intended for direct use as an array at the field path\n                          .status.conditions.  For example, type FooStatus struct{\n                          \\    // Represents the observations of a foo's current state.\n                          \\    // Known .status.conditions.type are: \\\"Available\\\",\n                          \\\"Progressing\\\", and \\\"Degraded\\\"     // +patchMergeKey=type\n                          \\    // +patchStrategy=merge     // +listType=map     //\n                          +listMapKey=type     Conditions []metav1.Condition `json:\\\"conditions,omitempty\\\"\n                          patchStrategy:\\\"merge\\\" patchMergeKey:\\\"type\\\" protobuf:\\\"bytes,1,rep,name=conditions\\\"`\n                          \\n     // other fields }\"\n                        properties:\n                          lastTransitionTime:\n                            description: lastTransitionTime is the last time the condition\n                              transitioned from one status to another. This should\n                              be when the underlying condition changed.  If that is\n                              not known, then using the time when the API field changed\n                              is acceptable.\n                            format: date-time\n                            type: string\n                          message:\n                            description: message is a human readable message indicating\n                              details about the transition. This may be an empty string.\n                            maxLength: 32768\n                            type: string\n                          observedGeneration:\n                            description: observedGeneration represents the .metadata.generation\n                              that the condition was set based upon. For instance,\n                              if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration\n                              is 9, the condition is out of date with respect to the\n                              current state of the instance.\n                            format: int64\n                            minimum: 0\n                            type: integer\n                          reason:\n                            description: reason contains a programmatic identifier\n                              indicating the reason for the condition's last transition.\n                              Producers of specific condition types may define expected\n                              values and meanings for this field, and whether the\n                              values are considered a guaranteed API. The value should\n                              be a CamelCase string. This field may not be empty.\n                            maxLength: 1024\n                            minLength: 1\n                            pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$\n                            type: string\n                          status:\n                            description: status of the condition, one of True, False,\n                              Unknown.\n                            enum:\n                            - \"True\"\n                            - \"False\"\n                            - Unknown\n                            type: string\n                          type:\n                            description: type of condition in CamelCase or in foo.example.com/CamelCase.\n                              --- Many .condition.type values are consistent across\n                              resources like Available, but because arbitrary conditions\n                              can be useful (see .node.status.conditions), the ability\n                              to deconflict is important. The regex it matches is\n                              (dns1123SubdomainFmt/)?(qualifiedNameFmt)\n                            maxLength: 316\n                            pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$\n                            type: string\n                        required:\n                        - lastTransitionTime\n                        - message\n                        - reason\n                        - status\n                        - type\n                        type: object\n                      type: array\n                      x-kubernetes-list-map-keys:\n                      - type\n                      x-kubernetes-list-type: map\n                    loadBalancer:\n                      description: LoadBalancer contains the current status of the\n                        load-balancer, if one is present.\n                      properties:\n                        ingress:\n                          description: Ingress is a list containing ingress points\n                            for the load-balancer. Traffic intended for the service\n                            should be sent to these ingress points.\n                          items:\n                            description: 'LoadBalancerIngress represents the status\n                              of a load-balancer ingress point: traffic intended for\n                              the service should be sent to an ingress point.'\n                            properties:\n                              hostname:\n                                description: Hostname is set for load-balancer ingress\n                                  points that are DNS based (typically AWS load-balancers)\n                                type: string\n                              ip:\n                                description: IP is set for load-balancer ingress points\n                                  that are IP based (typically GCE or OpenStack load-balancers)\n                                type: string\n                              ports:\n                                description: Ports is a list of records of service\n                                  ports If used, every port defined in the service\n                                  should have an entry in it\n                                items:\n                                  properties:\n                                    error:\n                                      description: 'Error is to record the problem\n                                        with the service port The format of the error\n                                        shall comply with the following rules: - built-in\n                                        error values shall be specified in this file\n                                        and those shall use   CamelCase names - cloud\n                                        provider specific error values must have names\n                                        that comply with the   format foo.example.com/CamelCase.\n                                        --- The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'\n                                      maxLength: 316\n                                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$\n                                      type: string\n                                    port:\n                                      description: Port is the port number of the\n                                        service port of which status is recorded here\n                                      format: int32\n                                      type: integer\n                                    protocol:\n                                      default: TCP\n                                      description: 'Protocol is the protocol of the\n                                        service port of which status is recorded here\n                                        The supported values are: \"TCP\", \"UDP\", \"SCTP\"'\n                                      type: string\n                                  required:\n                                  - port\n                                  - protocol\n                                  type: object\n                                type: array\n                                x-kubernetes-list-type: atomic\n                            type: object\n                          type: array\n                      type: object\n                  type: object\n                description: RoleShardStatusMap shows the current status for all Services.\n                  The key is Service name, value is its status info\n                type: object\n              roleShardStatusMap:\n                additionalProperties:\n                  description: DeploymentStatus is the most recently observed status\n                    of the Deployment.\n                  properties:\n                    availableReplicas:\n                      description: Total number of available pods (ready for at least\n                        minReadySeconds) targeted by this deployment.\n                      format: int32\n                      type: integer\n                    collisionCount:\n                      description: Count of hash collisions for the Deployment. The\n                        Deployment controller uses this field as a collision avoidance\n                        mechanism when it needs to create the name for the newest\n                        ReplicaSet.\n                      format: int32\n                      type: integer\n                    conditions:\n                      description: Represents the latest available observations of\n                        a deployment's current state.\n                      items:\n                        description: DeploymentCondition describes the state of a\n                          deployment at a certain point.\n                        properties:\n                          lastTransitionTime:\n                            description: Last time the condition transitioned from\n                              one status to another.\n                            format: date-time\n                            type: string\n                          lastUpdateTime:\n                            description: The last time this condition was updated.\n                            format: date-time\n                            type: string\n                          message:\n                            description: A human readable message indicating details\n                              about the transition.\n                            type: string\n                          reason:\n                            description: The reason for the condition's last transition.\n                            type: string\n                          status:\n                            description: Status of the condition, one of True, False,\n                              Unknown.\n                            type: string\n                          type:\n                            description: Type of deployment condition.\n                            type: string\n                        required:\n                        - status\n                        - type\n                        type: object\n                      type: array\n                    observedGeneration:\n                      description: The generation observed by the deployment controller.\n                      format: int64\n                      type: integer\n                    readyReplicas:\n                      description: Total number of ready pods targeted by this deployment.\n                      format: int32\n                      type: integer\n                    replicas:\n                      description: Total number of non-terminated pods targeted by\n                        this deployment (their labels match the selector).\n                      format: int32\n                      type: integer\n                    unavailableReplicas:\n                      description: Total number of unavailable pods targeted by this\n                        deployment. This is the total number of pods that are still\n                        required for the deployment to have 100% available capacity.\n                        They may either be pods that are running but not yet available\n                        or pods that still have not been created.\n                      format: int32\n                      type: integer\n                    updatedReplicas:\n                      description: Total number of non-terminated pods targeted by\n                        this deployment that have the desired template spec.\n                      format: int32\n                      type: integer\n                  type: object\n                description: RoleShardStatusMap shows the current status for all Deployments.\n                  The key is Deployment name, value is its status info\n                type: object\n            type: object\n        type: object\n    served: true\n    storage: true\n    subresources:\n      status: {}\nstatus:\n  acceptedNames:\n    kind: \"\"\n    plural: \"\"\n  conditions: []\n  storedVersions: []\n"
  },
  {
    "path": "deploy/config/crd/kustomization.yaml",
    "content": "# This kustomization.yaml is not intended to be run by itself,\n# since it depends on service name and namespace that are out of this kustomize package.\n# It should be run by config/default\nresources:\n- bases/mlplatform.volcengine.com_mlservices.yaml\n#+kubebuilder:scaffold:crdkustomizeresource\n\npatchesStrategicMerge:\n# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.\n# patches here are for enabling the conversion webhook for each CRD\n#- patches/webhook_in_mlservices.yaml\n#+kubebuilder:scaffold:crdkustomizewebhookpatch\n\n# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.\n# patches here are for enabling the CA injection for each CRD\n#- patches/cainjection_in_mlservices.yaml\n#+kubebuilder:scaffold:crdkustomizecainjectionpatch\n\n# the following config is for teaching kustomize how to do kustomization for CRDs.\nconfigurations:\n- kustomizeconfig.yaml\n"
  },
  {
    "path": "deploy/config/crd/kustomizeconfig.yaml",
    "content": "# This file is for teaching kustomize how to substitute name and namespace reference in CRD\nnameReference:\n- kind: Service\n  version: v1\n  fieldSpecs:\n  - kind: CustomResourceDefinition\n    version: v1\n    group: apiextensions.k8s.io\n    path: spec/conversion/webhook/clientConfig/service/name\n\nnamespace:\n- kind: CustomResourceDefinition\n  version: v1\n  group: apiextensions.k8s.io\n  path: spec/conversion/webhook/clientConfig/service/namespace\n  create: false\n\nvarReference:\n- path: metadata/annotations\n"
  },
  {
    "path": "deploy/config/crd/patches/cainjection_in_mlservices.yaml",
    "content": "# The following patch adds a directive for certmanager to inject CA into the CRD\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  annotations:\n    cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)\n  name: mlservices.mlplatform.volcengine.com\n"
  },
  {
    "path": "deploy/config/crd/patches/webhook_in_mlservices.yaml",
    "content": "# The following patch enables a conversion webhook for the CRD\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  name: mlservices.mlplatform.volcengine.com\nspec:\n  conversion:\n    strategy: Webhook\n    webhook:\n      clientConfig:\n        service:\n          namespace: system\n          name: webhook-service\n          path: /convert\n"
  },
  {
    "path": "deploy/config/default/kustomization.yaml",
    "content": "# Adds namespace to all resources.\nnamespace: monolith-system\n\n# Value of this field is prepended to the\n# names of all resources, e.g. a deployment named\n# \"wordpress\" becomes \"alices-wordpress\".\n# Note that it should also match with the prefix (text before '-') of the namespace\n# field above.\nnamePrefix: monolith-\n\n# Labels to add to all resources and selectors.\n#commonLabels:\n#  someName: someValue\n\nbases:\n- ../crd\n- ../rbac\n- ../manager\n# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in\n# crd/kustomization.yaml\n#- ../webhook\n# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.\n#- ../certmanager\n# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.\n#- ../prometheus\n\npatchesStrategicMerge:\n# Protect the /metrics endpoint by putting it behind auth.\n# If you want your controller-manager to expose the /metrics\n# endpoint w/o any authn/z, please comment the following line.\n- manager_auth_proxy_patch.yaml\n\n# Mount the controller config file for loading manager configurations\n# through a ComponentConfig type\n#- manager_config_patch.yaml\n\n# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in\n# crd/kustomization.yaml\n#- manager_webhook_patch.yaml\n\n# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.\n# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.\n# 'CERTMANAGER' needs to be enabled to use ca injection\n#- webhookcainjection_patch.yaml\n\n# the following config is for teaching kustomize how to do var substitution\nvars:\n# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.\n#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR\n#  objref:\n#    kind: Certificate\n#    group: cert-manager.io\n#    version: v1\n#    name: serving-cert # this name should match the one in certificate.yaml\n#  fieldref:\n#    fieldpath: metadata.namespace\n#- name: CERTIFICATE_NAME\n#  objref:\n#    kind: Certificate\n#    group: cert-manager.io\n#    version: v1\n#    name: serving-cert # this name should match the one in certificate.yaml\n#- name: SERVICE_NAMESPACE # namespace of the service\n#  objref:\n#    kind: Service\n#    version: v1\n#    name: webhook-service\n#  fieldref:\n#    fieldpath: metadata.namespace\n#- name: SERVICE_NAME\n#  objref:\n#    kind: Service\n#    version: v1\n#    name: webhook-service\n"
  },
  {
    "path": "deploy/config/default/manager_auth_proxy_patch.yaml",
    "content": "# This patch inject a sidecar container which is a HTTP proxy for the\n# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: controller-manager\n  namespace: system\nspec:\n  template:\n    spec:\n      containers:\n      - name: kube-rbac-proxy\n        image: ml-platform-cn-guilin-boe.cr.volces.com/ml-platform/kube-rbac-proxy:0.13.0\n        args:\n        - \"--secure-listen-address=0.0.0.0:8443\"\n        - \"--upstream=http://127.0.0.1:8080/\"\n        - \"--logtostderr=true\"\n        - \"--v=10\"\n        ports:\n        - containerPort: 8443\n          name: https\n      - name: manager\n        args:\n        - \"--health-probe-bind-address=:8081\"\n        - \"--metrics-bind-address=127.0.0.1:8080\"\n        - \"--leader-elect\"\n"
  },
  {
    "path": "deploy/config/default/manager_config_patch.yaml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: controller-manager\n  namespace: system\nspec:\n  template:\n    spec:\n      containers:\n      - name: manager\n        args:\n        - \"--config=controller_manager_config.yaml\"\n        volumeMounts:\n        - name: manager-config\n          mountPath: /controller_manager_config.yaml\n          subPath: controller_manager_config.yaml\n      volumes:\n      - name: manager-config\n        configMap:\n          name: manager-config\n"
  },
  {
    "path": "deploy/config/manager/controller_manager_config.yaml",
    "content": "apiVersion: controller-runtime.sigs.k8s.io/v1alpha1\nkind: ControllerManagerConfig\nhealth:\n  healthProbeBindAddress: :8081\nmetrics:\n  bindAddress: 127.0.0.1:8080\nwebhook:\n  port: 9443\nleaderElection:\n  leaderElect: true\n  resourceName: 183d5a48.volcengine.com\n"
  },
  {
    "path": "deploy/config/manager/kustomization.yaml",
    "content": "resources:\n- manager.yaml\n\ngeneratorOptions:\n  disableNameSuffixHash: true\n\nconfigMapGenerator:\n- files:\n  - controller_manager_config.yaml\n  name: manager-config\napiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\nimages:\n- name: controller\n  newName: ml-platform-cn-guilin-boe.cr.volces.com/ml-platform/data.monolith.controller-manager\n  newTag: b85906ce01ef40a75ba48779efdd4e3f\n"
  },
  {
    "path": "deploy/config/manager/manager.yaml",
    "content": "apiVersion: v1\nkind: Namespace\nmetadata:\n  labels:\n    control-plane: controller-manager\n  name: system\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: controller-manager\n  namespace: system\n  labels:\n    control-plane: controller-manager\nspec:\n  selector:\n    matchLabels:\n      control-plane: controller-manager\n  replicas: 1\n  template:\n    metadata:\n      labels:\n        control-plane: controller-manager\n    spec:\n      containers:\n      - command:\n        - ./manager\n        args:\n        - --leader-elect\n        image: controller:latest\n        name: manager\n        securityContext:\n          allowPrivilegeEscalation: false\n        livenessProbe:\n          httpGet:\n            path: /healthz\n            port: 8081\n          initialDelaySeconds: 15\n          periodSeconds: 20\n        readinessProbe:\n          httpGet:\n            path: /readyz\n            port: 8081\n          initialDelaySeconds: 5\n          periodSeconds: 10\n        resources:\n          limits:\n            cpu: 100m\n            memory: 30Mi\n          requests:\n            cpu: 100m\n            memory: 20Mi\n      serviceAccountName: controller-manager\n      terminationGracePeriodSeconds: 10\n"
  },
  {
    "path": "deploy/config/prometheus/kustomization.yaml",
    "content": "resources:\n- monitor.yaml\n"
  },
  {
    "path": "deploy/config/prometheus/monitor.yaml",
    "content": "\n# Prometheus Monitor Service (Metrics)\napiVersion: monitoring.coreos.com/v1\nkind: ServiceMonitor\nmetadata:\n  labels:\n    control-plane: controller-manager\n  name: controller-manager-metrics-monitor\n  namespace: system\nspec:\n  endpoints:\n    - path: /metrics\n      port: https\n      scheme: https\n      bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token\n      tlsConfig:\n        insecureSkipVerify: true\n  selector:\n    matchLabels:\n      control-plane: controller-manager\n"
  },
  {
    "path": "deploy/config/rbac/auth_proxy_client_clusterrole.yaml",
    "content": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: metrics-reader\nrules:\n- nonResourceURLs:\n  - \"/metrics\"\n  verbs:\n  - get\n"
  },
  {
    "path": "deploy/config/rbac/auth_proxy_role.yaml",
    "content": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: proxy-role\nrules:\n- apiGroups:\n  - authentication.k8s.io\n  resources:\n  - tokenreviews\n  verbs:\n  - create\n- apiGroups:\n  - authorization.k8s.io\n  resources:\n  - subjectaccessreviews\n  verbs:\n  - create\n"
  },
  {
    "path": "deploy/config/rbac/auth_proxy_role_binding.yaml",
    "content": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n  name: proxy-rolebinding\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: proxy-role\nsubjects:\n- kind: ServiceAccount\n  name: controller-manager\n  namespace: system\n"
  },
  {
    "path": "deploy/config/rbac/auth_proxy_service.yaml",
    "content": "apiVersion: v1\nkind: Service\nmetadata:\n  labels:\n    control-plane: controller-manager\n  name: controller-manager-metrics-service\n  namespace: system\nspec:\n  ports:\n  - name: https\n    port: 8443\n    targetPort: https\n  selector:\n    control-plane: controller-manager\n"
  },
  {
    "path": "deploy/config/rbac/kustomization.yaml",
    "content": "resources:\n# All RBAC will be applied under this service account in\n# the deployment namespace. You may comment out this resource\n# if your manager will use a service account that exists at\n# runtime. Be sure to update RoleBinding and ClusterRoleBinding\n# subjects if changing service account names.\n- service_account.yaml\n- role.yaml\n- role_binding.yaml\n- leader_election_role.yaml\n- leader_election_role_binding.yaml\n# Comment the following 4 lines if you want to disable\n# the auth proxy (https://github.com/brancz/kube-rbac-proxy)\n# which protects your /metrics endpoint.\n- auth_proxy_service.yaml\n- auth_proxy_role.yaml\n- auth_proxy_role_binding.yaml\n- auth_proxy_client_clusterrole.yaml\n"
  },
  {
    "path": "deploy/config/rbac/leader_election_role.yaml",
    "content": "# permissions to do leader election.\napiVersion: rbac.authorization.k8s.io/v1\nkind: Role\nmetadata:\n  name: leader-election-role\nrules:\n- apiGroups:\n  - \"\"\n  resources:\n  - configmaps\n  verbs:\n  - get\n  - list\n  - watch\n  - create\n  - update\n  - patch\n  - delete\n- apiGroups:\n  - coordination.k8s.io\n  resources:\n  - leases\n  verbs:\n  - get\n  - list\n  - watch\n  - create\n  - update\n  - patch\n  - delete\n- apiGroups:\n  - \"\"\n  resources:\n  - events\n  verbs:\n  - create\n  - patch\n"
  },
  {
    "path": "deploy/config/rbac/leader_election_role_binding.yaml",
    "content": "apiVersion: rbac.authorization.k8s.io/v1\nkind: RoleBinding\nmetadata:\n  name: leader-election-rolebinding\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: Role\n  name: leader-election-role\nsubjects:\n- kind: ServiceAccount\n  name: controller-manager\n  namespace: system\n"
  },
  {
    "path": "deploy/config/rbac/mlservice_editor_role.yaml",
    "content": "# permissions for end users to edit mlservices.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: mlservice-editor-role\nrules:\n- apiGroups:\n  - mlplatform.volcengine.com\n  resources:\n  - mlservices\n  verbs:\n  - create\n  - delete\n  - get\n  - list\n  - patch\n  - update\n  - watch\n- apiGroups:\n  - mlplatform.volcengine.com\n  resources:\n  - mlservices/status\n  verbs:\n  - get\n"
  },
  {
    "path": "deploy/config/rbac/mlservice_viewer_role.yaml",
    "content": "# permissions for end users to view mlservices.\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: mlservice-viewer-role\nrules:\n- apiGroups:\n  - mlplatform.volcengine.com\n  resources:\n  - mlservices\n  verbs:\n  - get\n  - list\n  - watch\n- apiGroups:\n  - mlplatform.volcengine.com\n  resources:\n  - mlservices/status\n  verbs:\n  - get\n"
  },
  {
    "path": "deploy/config/rbac/role.yaml",
    "content": "\n---\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  creationTimestamp: null\n  name: manager-role\nrules:\n- apiGroups:\n  - \"\"\n  resources:\n  - pods\n  verbs:\n  - get\n  - list\n- apiGroups:\n  - apps\n  resources:\n  - deployments\n  verbs:\n  - create\n  - delete\n  - get\n  - list\n  - patch\n  - update\n  - watch\n- apiGroups:\n  - apps\n  resources:\n  - replicasets\n  verbs:\n  - get\n  - list\n- apiGroups:\n  - \"\"\n  resources:\n  - services\n  verbs:\n  - create\n  - delete\n  - get\n  - list\n  - patch\n  - update\n  - watch\n- apiGroups:\n  - mlplatform.volcengine.com\n  resources:\n  - mlservices\n  verbs:\n  - create\n  - delete\n  - get\n  - list\n  - patch\n  - update\n  - watch\n- apiGroups:\n  - mlplatform.volcengine.com\n  resources:\n  - mlservices/finalizers\n  verbs:\n  - update\n- apiGroups:\n  - mlplatform.volcengine.com\n  resources:\n  - mlservices/status\n  verbs:\n  - get\n  - patch\n  - update\n"
  },
  {
    "path": "deploy/config/rbac/role_binding.yaml",
    "content": "apiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n  name: manager-rolebinding\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: manager-role\nsubjects:\n- kind: ServiceAccount\n  name: controller-manager\n  namespace: system\n"
  },
  {
    "path": "deploy/config/rbac/service_account.yaml",
    "content": "apiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: controller-manager\n  namespace: system\n"
  },
  {
    "path": "deploy/config/samples/mlplatform_v1_mlservice.yaml",
    "content": "apiVersion: mlplatform.volcengine.com/v1\nkind: MLService\nmetadata:\n  name: mlservice-demo\n  namespace: mlplatform-service\nspec:\n  selector:\n    matchLabels:\n      app: mlservice-demo\n  roles:\n  - name: \"Entry\"\n    shardNum: 1\n    serviceSpec:\n      serviceType: \"ClusterIP\"\n    template:\n      metadata:\n        labels:\n          app: mlservice-demo\n      spec:\n        progressDeadlineSeconds: 600\n        replicas: 1\n        selector:\n          matchLabels:\n            app: mlservice-demo\n        strategy:\n          rollingUpdate:\n            maxSurge: 25%\n            maxUnavailable: 25%\n          type: RollingUpdate\n        template:\n          metadata:\n            labels:\n              app: mlservice-demo\n          spec:\n            containers:\n            - command:\n              - sleep\n              - infinity\n              env:\n              - name: TEST\n                value: \"1\"\n              image: cr-cn-guilin-boe.volces.com/ml_platform/tfserving:8bf6def4f68f89fd07bce144723f7a97\n              imagePullPolicy: Always\n              name: mlservice-demo\n              ports:\n              - containerPort: 8500\n                protocol: TCP\n              - containerPort: 8501\n                protocol: TCP\n              resources:\n                limits:\n                  cpu: \"500m\"\n                  memory: 1Gi\n                requests:\n                  cpu: \"500m\"\n                  memory: 1Gi\n              terminationMessagePath: /dev/termination-log\n              terminationMessagePolicy: File\n            dnsPolicy: ClusterFirst\n            restartPolicy: Always\n            securityContext:\n              runAsNonRoot: false\n            terminationGracePeriodSeconds: 30\n  - name: \"PS\"\n    shardNum: 2\n    template:\n      metadata:\n        labels:\n          app: mlservice-demo\n      spec:\n        progressDeadlineSeconds: 600\n        replicas: 1\n        selector:\n          matchLabels:\n            app: mlservice-demo\n        strategy:\n          rollingUpdate:\n            maxSurge: 25%\n            maxUnavailable: 25%\n          type: RollingUpdate\n        template:\n          metadata:\n            labels:\n              app: mlservice-demo\n          spec:\n            containers:\n            - command:\n              - sleep\n              - infinity\n              env:\n              - name: TEST\n                value: \"1\"\n              image: cr-cn-guilin-boe.volces.com/ml_platform/tfserving:8bf6def4f68f89fd07bce144723f7a97\n              imagePullPolicy: Always\n              name: mlservice-demo\n              ports:\n              - containerPort: 8500\n                protocol: TCP\n              - containerPort: 8501\n                protocol: TCP\n              resources:\n                limits:\n                  cpu: \"500m\"\n                  memory: 1Gi\n                requests:\n                  cpu: \"500m\"\n                  memory: 1Gi\n              terminationMessagePath: /dev/termination-log\n              terminationMessagePolicy: File\n            dnsPolicy: ClusterFirst\n            restartPolicy: Always\n            securityContext:\n              runAsNonRoot: false\n            terminationGracePeriodSeconds: 30"
  },
  {
    "path": "deploy/controllers/constants.go",
    "content": "package controllers\n\nconst (\n\tModuleInference      = \"inference\"\n\tMLPlatformVolcPrefix = \"mlplatform.volcengine.com\"\n)\n\nconst (\n\tImmutableLabelServiceId = ModuleInference + \".\" + MLPlatformVolcPrefix + \"/service-id\"\n\tImmutableLabelRoleName  = ModuleInference + \".\" + MLPlatformVolcPrefix + \"/role-name\"\n\tImmutableLabelShardId   = ModuleInference + \".\" + MLPlatformVolcPrefix + \"/shard-id\"\n\tImmutableLabelShardNum  = ModuleInference + \".\" + MLPlatformVolcPrefix + \"/shard-num\"\n)\n\nconst (\n\tEnvShardId     = \"MLP_SHARD_ID\"\n\tEnvPodName     = \"MLP_POD_NAME\"\n\tEnvHostIp      = \"MLP_HOST_IP\"\n\tEnvShardNum    = \"MLP_SHARD_NUM\"\n\tEnvIdc         = \"MLP_IDC\"\n\tEnvServiceName = \"MLP_SERVICE_NAME\"\n\tEnvRoleName    = \"MLP_ROLE_NAME\"\n\tEnvPort        = \"MLP_%s_PORT\"\n)\n\n// kubelet\nconst (\n\tPodInitializing   = \"PodInitializing\"\n\tContainerCreating = \"ContainerCreating\"\n)\n\nconst (\n\tDefaultRpcPort  = 8500\n\tDefaultHttpPort = 8501\n)\n\nconst (\n\tContainerEvicted = \"Evicted\"\n)\n\n// reason for pod\nconst (\n\tReasonInsufficientClusterResources = \"InsufficientClusterResources\"\n\tReasonInProgress                   = \"\"\n\tReasonStatusNotFound               = \"StatusNotFound\"\n\tReasonEvicted                      = \"Evicted\"\n\tReasonServiceExceptionExited       = \"ExceptionExited\"\n)\n"
  },
  {
    "path": "deploy/controllers/deployment_handler.go",
    "content": "package controllers\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\tmonolithv1 \"code.byted.org/data/monolith/deploy/api/v1\"\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/api/equality\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log\"\n)\n\n// getDeploymentName returns Deployment name with pattern {mlsvcName}-{role}-{shardIdx}\nfunc getDeploymentName(mlsvcName, role string, shardIdx int) string {\n\treturn fmt.Sprintf(\"%s-%s-%d\", mlsvcName, strings.ToLower(role), shardIdx)\n}\n\n// DeploymentHandler handles with k8s Deployment resource,\n// make sure deployments owned by MLService in cluster match the desired state the MLService spec defines.\nfunc (r *MLServiceReconciler) DeploymentHandler(ctx context.Context, mlsvc *monolithv1.MLService) error {\n\tif mlsvc == nil {\n\t\treturn nil\n\t}\n\n\tlog := log.FromContext(ctx).WithName(\"DeploymentHandler\")\n\n\t// delete deployments if MLService is deleted\n\tmlsvcDeleting := !mlsvc.GetDeletionTimestamp().IsZero()\n\tif mlsvcDeleting {\n\t\treturn r.cleanOwnedDeployments(ctx, mlsvc)\n\t}\n\n\tfor roleIdx, role := range mlsvc.Spec.Roles {\n\t\tshardNum := int(role.ShardNum)\n\t\tif shardNum == 0 {\n\t\t\t// default value of ShardNum is 1\n\t\t\tshardNum = 1\n\t\t}\n\n\t\tfor shardIdx := 1; shardIdx <= shardNum; shardIdx++ {\n\t\t\tdeploy := &appsv1.Deployment{\n\t\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\t\tName:      getDeploymentName(mlsvc.Name, mlsvc.Spec.Roles[roleIdx].Name, shardIdx),\n\t\t\t\t\tNamespace: mlsvc.Namespace,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tif _, err := ctrl.CreateOrUpdate(ctx, r.Client, deploy, func() error {\n\t\t\t\ttemplate := mlsvc.Spec.Roles[roleIdx].Template.DeepCopy()\n\t\t\t\t// set additional labels,annotations,label selector for deployment\n\t\t\t\tif template.ObjectMeta.Labels == nil {\n\t\t\t\t\ttemplate.ObjectMeta.Labels = make(map[string]string, 0)\n\t\t\t\t}\n\t\t\t\tif template.ObjectMeta.Annotations == nil {\n\t\t\t\t\ttemplate.ObjectMeta.Annotations = make(map[string]string, 0)\n\t\t\t\t}\n\t\t\t\tif template.Spec.Selector.MatchLabels == nil {\n\t\t\t\t\ttemplate.Spec.Selector.MatchLabels = make(map[string]string, 0)\n\t\t\t\t}\n\t\t\t\tSetAdditionalKeyValuePairs(template.ObjectMeta.Labels, mlsvc.Name, role.Name, &shardIdx, &shardNum)\n\t\t\t\tSetAdditionalKeyValuePairs(template.ObjectMeta.Annotations, mlsvc.Name, role.Name, &shardIdx, &shardNum)\n\t\t\t\tSetAdditionalKeyValuePairs(template.Spec.Selector.MatchLabels, mlsvc.Name, role.Name, &shardIdx, &shardNum)\n\n\t\t\t\t// set additional labels for pod\n\t\t\t\tif template.Spec.Template.ObjectMeta.Labels == nil {\n\t\t\t\t\ttemplate.Spec.Template.ObjectMeta.Labels = make(map[string]string, 0)\n\t\t\t\t}\n\t\t\t\tSetAdditionalKeyValuePairs(template.Spec.Template.ObjectMeta.Labels, mlsvc.Name, role.Name, &shardIdx, &shardNum)\n\n\t\t\t\t// set additional Env to the container\n\t\t\t\tidc, _ := mlsvc.GetAnnotations()[EnvIdc]\n\t\t\t\tvar ports []corev1.ServicePort\n\t\t\t\tif mlsvc.Spec.Roles[roleIdx].ServiceSpec != nil {\n\t\t\t\t\tports = GetServicePorts(mlsvc.Spec.Roles[roleIdx].ServiceSpec.Ports)\n\t\t\t\t}\n\t\t\t\tfor i := range template.Spec.Template.Spec.Containers {\n\t\t\t\t\ttemplate.Spec.Template.Spec.Containers[i].Env = append(template.Spec.Template.Spec.Containers[i].Env,\n\t\t\t\t\t\tAdditionalEnvs(mlsvc.Name, role.Name, idc, shardIdx, int(shardNum), ports)...,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tdeploy.ResourceVersion = \"\"\n\n\t\t\t\t// set ObjectMeta.Labels\n\t\t\t\tif deploy.ObjectMeta.Labels == nil {\n\t\t\t\t\tdeploy.ObjectMeta.Labels = make(map[string]string)\n\t\t\t\t}\n\t\t\t\tfor k, v := range template.ObjectMeta.Labels {\n\t\t\t\t\tdeploy.ObjectMeta.Labels[k] = v\n\t\t\t\t}\n\n\t\t\t\t// set ObjectMeta.Annotations\n\t\t\t\tif deploy.ObjectMeta.Annotations == nil {\n\t\t\t\t\tdeploy.ObjectMeta.Annotations = make(map[string]string)\n\t\t\t\t}\n\t\t\t\tfor k, v := range template.ObjectMeta.Annotations {\n\t\t\t\t\tdeploy.ObjectMeta.Annotations[k] = v\n\t\t\t\t}\n\n\t\t\t\t// set Finalizers\n\t\t\t\tfor _, finalizer := range template.ObjectMeta.Finalizers {\n\t\t\t\t\tcontrollerutil.AddFinalizer(deploy, finalizer)\n\t\t\t\t}\n\n\t\t\t\t// set Spec\n\t\t\t\tdeploy.Spec = template.Spec\n\n\t\t\t\t// set the owner so that garbage collection can kicks in\n\t\t\t\tif err := ctrl.SetControllerReference(mlsvc, deploy, r.Scheme); err != nil {\n\t\t\t\t\tlog.Error(err, \"unable to set ownerReference from MLService to Deployment\")\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\t// end of ctrl.CreateOrUpdate\n\t\t\t\treturn nil\n\t\t\t}); err != nil {\n\t\t\t\t// error handling of ctrl.CreateOrUpdate\n\t\t\t\tlog.Error(err, \"unable to ensure deployment is correct\")\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (r *MLServiceReconciler) createDeployment(ctx context.Context, dp *appsv1.Deployment) error {\n\tlog := log.FromContext(ctx).WithValues(\"DeploymentName\", dp.Name)\n\tif err := r.Client.Create(ctx, dp); err != nil {\n\t\tlog.Error(err, \"failed to create Deployment resource\")\n\t\treturn err\n\t}\n\n\tlog.Info(\"created Deployment resource for MLService\")\n\treturn nil\n}\n\nfunc (r *MLServiceReconciler) updateDeployment(ctx context.Context, desired, existing *appsv1.Deployment) error {\n\tlog := log.FromContext(ctx).WithValues(\"DeploymentName\", existing.Name)\n\tif equality.Semantic.DeepEqual(existing, desired) {\n\t\treturn nil\n\t}\n\n\tif err := r.Client.Update(ctx, desired); err != nil {\n\t\tlog.Error(err, \"failed to update Deployment resource\")\n\t\treturn err\n\t}\n\tlog.Info(\"update Deployment resource for MLService\")\n\treturn nil\n}\n\nfunc (r *MLServiceReconciler) deleteDeployment(ctx context.Context, dp *appsv1.Deployment) error {\n\tlog := log.FromContext(ctx).WithValues(\"DeploymentName\", dp.Name)\n\tif err := r.Client.Delete(ctx, dp); err != nil {\n\t\tlog.Error(err, \"failed to delete Deployment resource\")\n\t\treturn err\n\t}\n\n\tlog.Info(\"delete deployment resource: \" + dp.Name)\n\treturn nil\n}\n\n// cleanOwnedDeployments will delete any existing Deployment resources that\n// were created for the given MLService\nfunc (r *MLServiceReconciler) cleanOwnedDeployments(ctx context.Context, mlsvc *monolithv1.MLService) error {\n\tlog := log.FromContext(ctx).WithValues(\"MLService\", mlsvc.Name)\n\tlog.Info(\"finding existing Deployments for MLService resource\")\n\n\t// list all deployment resources owned by this MLService\n\tdeployments, err := r.getOwnedDeployments(ctx, mlsvc)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, deployment := range deployments.Items {\n\t\tif !deployment.GetDeletionTimestamp().IsZero() {\n\t\t\t// deployment already deleted, ignore.\n\t\t\tcontinue\n\t\t}\n\n\t\t// delete deployment\n\t\tif err := r.Delete(ctx, &deployment); err != nil {\n\t\t\tlog.Error(err, \"failed to delete Deployment resource: \"+deployment.Name)\n\t\t\treturn err\n\t\t}\n\n\t\tlog.Info(\"delete deployment resource: \" + deployment.Name)\n\t}\n\n\treturn nil\n}\n\n// getOwnedDeployments return all deployments owned by the MLService\nfunc (r *MLServiceReconciler) getOwnedDeployments(ctx context.Context, mlsvc *monolithv1.MLService) (*appsv1.DeploymentList, error) {\n\tvar deployments appsv1.DeploymentList\n\tif err := r.List(ctx, &deployments, client.InNamespace(mlsvc.Namespace),\n\t\tclient.MatchingLabels(mlsvc.Spec.Selector.MatchLabels)); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &deployments, nil\n}\n\n// AdditionalEnvs return a list of EnvVar, these Envs will be injected to pod container\nfunc AdditionalEnvs(mlsvcName, roleName, idc string, shardIdx, shardNum int, ports []corev1.ServicePort) []corev1.EnvVar {\n\tenvs := []corev1.EnvVar{\n\t\t{\n\t\t\tName:  EnvShardId,\n\t\t\tValue: strconv.Itoa(shardIdx),\n\t\t},\n\t\t{\n\t\t\tName:  EnvShardNum,\n\t\t\tValue: strconv.Itoa(int(shardNum)),\n\t\t},\n\t\t{\n\t\t\tName:  EnvServiceName,\n\t\t\tValue: mlsvcName,\n\t\t},\n\t\t{\n\t\t\tName:  EnvRoleName,\n\t\t\tValue: roleName,\n\t\t},\n\t\t{\n\t\t\tName:  EnvIdc,\n\t\t\tValue: idc,\n\t\t},\n\t\t{\n\t\t\tName: EnvPodName,\n\t\t\tValueFrom: &corev1.EnvVarSource{\n\t\t\t\tFieldRef: &corev1.ObjectFieldSelector{\n\t\t\t\t\tFieldPath: \"metadata.name\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: EnvHostIp,\n\t\t\tValueFrom: &corev1.EnvVarSource{\n\t\t\t\tFieldRef: &corev1.ObjectFieldSelector{\n\t\t\t\t\tFieldPath: \"status.podIP\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, port := range ports {\n\t\tenvs = append(envs, corev1.EnvVar{\n\t\t\tName:  fmt.Sprintf(EnvPort, strings.ToUpper(string(port.Name))),\n\t\t\tValue: strconv.Itoa(int(port.Port)),\n\t\t})\n\t}\n\treturn envs\n}\n\n// SetAdditionalKeyValuePairs inserts additional labels to the existing Labels map\nfunc SetAdditionalKeyValuePairs(existing map[string]string, mlsvcName, roleName string, shardIdx, shardNum *int) {\n\tadditional := map[string]string{\n\t\tImmutableLabelServiceId: mlsvcName,\n\t\tImmutableLabelRoleName:  roleName,\n\t}\n\n\tif shardIdx != nil {\n\t\tadditional[ImmutableLabelShardId] = strconv.Itoa(*shardIdx)\n\t}\n\n\tif shardNum != nil {\n\t\tadditional[ImmutableLabelShardNum] = strconv.Itoa(*shardNum)\n\t}\n\n\tfor k, v := range additional {\n\t\texisting[k] = v\n\t}\n}\n"
  },
  {
    "path": "deploy/controllers/mlservice_controller.go",
    "content": "/*\nCopyright 2023.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage controllers\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"time\"\n\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\tappsclient \"k8s.io/client-go/kubernetes/typed/apps/v1\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log\"\n\n\tmonolithv1 \"code.byted.org/data/monolith/deploy/api/v1\"\n)\n\ntype MLSvcHandler func(ctx context.Context, mlsvc *monolithv1.MLService) error\n\nvar handlers []MLSvcHandler\n\n// MLServiceReconciler reconciles a MLService object\ntype MLServiceReconciler struct {\n\tappsclient.AppsV1Client\n\tclient.Client\n\tScheme *runtime.Scheme\n}\n\n//+kubebuilder:rbac:groups=mlplatform.volcengine.com,resources=mlservices,verbs=get;list;watch;create;update;patch;delete\n//+kubebuilder:rbac:groups=mlplatform.volcengine.com,resources=mlservices/status,verbs=get;update;patch\n//+kubebuilder:rbac:groups=mlplatform.volcengine.com,resources=mlservices/finalizers,verbs=update\n//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete\n//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete\n//+kubebuilder:rbac:groups=apps,resources=replicasets,verbs=get;list\n//+kubebuilder:rbac:groups=\"\",resources=pods,verbs=get;list\n\n// Reconcile is part of the main kubernetes reconciliation loop which aims to\n// move the current state of the cluster closer to the desired state.\n// TODO(user): Modify the Reconcile function to compare the state specified by\n// the MLService object against the actual cluster state, and then\n// perform operations to make the cluster state reflect the state specified by\n// the user.\n//\n// For more details, check Reconcile and its Result here:\n// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.7.2/pkg/reconcile\nfunc (r *MLServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {\n\tlog := log.FromContext(ctx).WithName(\"MLService\").WithValues(\"mlservice\", req.NamespacedName)\n\n\tvar mlsvc monolithv1.MLService\n\tif err := r.Get(ctx, req.NamespacedName, &mlsvc); err != nil {\n\t\tlog.Error(err, \"unable to fetch MLService\")\n\t\treturn ctrl.Result{}, client.IgnoreNotFound(err)\n\t}\n\n\tfor _, h := range handlers {\n\t\terr := h(ctx, &mlsvc)\n\t\tif err != nil {\n\t\t\tlog.Error(err, \"handler failed\")\n\t\t\treturn ctrl.Result{}, err\n\t\t}\n\t}\n\n\tif err := r.updateStatus(ctx, &mlsvc); err != nil {\n\t\tlog.Error(err, \"unable to update status\")\n\t\treturn ctrl.Result{}, err\n\t}\n\n\t// if phase is Stopping, chances are that there will be no events to trigger the Reconcile,\n\t// so requeue is needed.\n\tif mlsvc.Status.Phase == monolithv1.ServiceStopping {\n\t\treturn ctrl.Result{RequeueAfter: 2 * time.Second}, nil\n\t}\n\n\treturn ctrl.Result{}, nil\n}\n\n// SetupWithManager sets up the controller with the Manager.\nfunc (r *MLServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {\n\thandlers = []MLSvcHandler{\n\t\tr.DeploymentHandler,\n\t\tr.ServiceHandler,\n\t}\n\n\treturn ctrl.NewControllerManagedBy(mgr).\n\t\tFor(&monolithv1.MLService{}).\n\t\tOwns(&appsv1.Deployment{}).\n\t\tOwns(&corev1.Service{}).\n\t\tComplete(r)\n}\n\n// updateStatus update the status of MLService according to status of resources owned by this MLService\nfunc (r *MLServiceReconciler) updateStatus(ctx context.Context, mlsvc *monolithv1.MLService) error {\n\tlog := log.FromContext(ctx).WithName(\"MLService\").WithValues(\"mlservice\", mlsvc.Name)\n\n\t// List all deployment resources owned by this MLService\n\tdeployments, err := r.getOwnedDeployments(ctx, mlsvc)\n\tif err != nil {\n\t\tlog.Error(err, \"get owned deployments failed\")\n\t\treturn err\n\t}\n\n\t// Shard status map\n\tvar newRoleShardStatusMap map[string]appsv1.DeploymentStatus\n\tif len(deployments.Items) > 0 {\n\t\tnewRoleShardStatusMap = make(map[string]appsv1.DeploymentStatus)\n\t}\n\tfor _, dp := range deployments.Items {\n\t\tnewRoleShardStatusMap[dp.Name] = *dp.Status.DeepCopy()\n\t}\n\n\t// List all service resources owned by this MLService\n\tservices, err := r.getOwnedServices(ctx, mlsvc)\n\tif err != nil {\n\t\tlog.Error(err, \"get owned services failed\")\n\t\treturn err\n\t}\n\n\t// Service status map\n\tvar newRoleServiceStatusMap map[string]corev1.ServiceStatus\n\tif len(services.Items) > 0 {\n\t\tnewRoleServiceStatusMap = make(map[string]corev1.ServiceStatus)\n\t}\n\tfor _, svc := range services.Items {\n\t\tnewRoleServiceStatusMap[svc.Name] = *svc.Status.DeepCopy()\n\t}\n\n\t// Service ClusterIps\n\tvar newRoleServiceClusterIps map[string]string\n\tif len(services.Items) > 0 {\n\t\tnewRoleServiceClusterIps = make(map[string]string)\n\t}\n\tfor _, svc := range services.Items {\n\t\tnewRoleServiceClusterIps[svc.Name] = svc.Spec.ClusterIP\n\t}\n\n\t// phase, reason, message\n\tphase, reason, message, err := r.getMLServiceStatus(ctx, mlsvc)\n\tif err != nil {\n\t\tlog.Error(err, \"get MLService status failed\")\n\t\treturn err\n\t}\n\n\tif mlsvc.Status.Phase == phase && mlsvc.Status.Reason == reason && mlsvc.Status.Message == message &&\n\t\treflect.DeepEqual(newRoleShardStatusMap, mlsvc.Status.RoleShardStatusMap) &&\n\t\treflect.DeepEqual(newRoleServiceStatusMap, mlsvc.Status.RoleServiceStatusMap) &&\n\t\treflect.DeepEqual(newRoleServiceClusterIps, mlsvc.Status.RoleServiceClusterIps) {\n\t\tlog.Info(\"no changes of MLService status\")\n\t\treturn nil\n\t}\n\n\t// update MLService status\n\tlog.Info(\"MLService status\", \"phase\", phase, \"reason\", reason, \"message\", message)\n\tmlsvc.Status.RoleShardStatusMap = newRoleShardStatusMap\n\tmlsvc.Status.RoleServiceStatusMap = newRoleServiceStatusMap\n\tmlsvc.Status.RoleServiceClusterIps = newRoleServiceClusterIps\n\tmlsvc.Status.LastTransitionTime = metav1.Now()\n\tmlsvc.Status.Phase = phase\n\tmlsvc.Status.Reason = reason\n\tmlsvc.Status.Message = message\n\tif err := r.Status().Update(ctx, mlsvc); err != nil {\n\t\tlog.Error(err, \"unable to update MLService status\")\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "deploy/controllers/service_handler.go",
    "content": "package controllers\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\tmonolithv1 \"code.byted.org/data/monolith/deploy/api/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/util/intstr\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log\"\n)\n\n// getServiceName returns Service name with pattern {mlsvcName}-{role}\nfunc getServiceName(mlsvcName, role string) string {\n\treturn fmt.Sprintf(\"%s-%s\", mlsvcName, strings.ToLower(role))\n}\n\n// ServiceHandler handles with k8s Service resource,\n// make sure k8s service owned by MLService in cluster match the desired state the MLService spec defines.\nfunc (r *MLServiceReconciler) ServiceHandler(ctx context.Context, mlsvc *monolithv1.MLService) error {\n\tif mlsvc == nil {\n\t\treturn nil\n\t}\n\n\tlog := log.FromContext(ctx).WithName(\"ServiceHandler\")\n\n\t// delete sesrvice if MLService is deleted\n\tmlsvcDeleting := !mlsvc.GetDeletionTimestamp().IsZero()\n\tif mlsvcDeleting {\n\t\treturn r.cleanOwnedServices(ctx, mlsvc)\n\t}\n\n\tfor roleIdx, role := range mlsvc.Spec.Roles {\n\t\tif role.ServiceSpec == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif role.ServiceSpec.ServiceType != corev1.ServiceTypeClusterIP {\n\t\t\tmlsvc.Status.Phase = monolithv1.ServiceAbnormal\n\t\t\tmlsvc.Status.Message = \"Currently only ClusterIP type is supported\"\n\t\t\tlog.Info(\"invalid service type, set status to abnormal\", \"ServiceType\", role.ServiceSpec.ServiceType)\n\t\t\tif err := r.Status().Update(ctx, mlsvc); err != nil {\n\t\t\t\tlog.Error(err, \"unable to update MLService status\")\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn errors.New(mlsvc.Status.Message)\n\t\t}\n\n\t\tsvc := &corev1.Service{\n\t\t\tObjectMeta: metav1.ObjectMeta{\n\t\t\t\tName:      getServiceName(mlsvc.Name, mlsvc.Spec.Roles[roleIdx].Name),\n\t\t\t\tNamespace: mlsvc.Namespace,\n\t\t\t},\n\t\t}\n\n\t\tif _, err := ctrl.CreateOrUpdate(ctx, r.Client, svc, func() error {\n\t\t\tsvc.Spec = corev1.ServiceSpec{\n\t\t\t\tPorts:    GetServicePorts(role.ServiceSpec.Ports),\n\t\t\t\tSelector: map[string]string{},\n\t\t\t\tType:     corev1.ServiceTypeClusterIP,\n\t\t\t}\n\n\t\t\t// set service labels\n\t\t\tsvc.ObjectMeta.Labels = make(map[string]string, 0)\n\t\t\tSetAdditionalKeyValuePairs(svc.ObjectMeta.Labels, mlsvc.Name, role.Name, nil, nil)\n\t\t\tfor k, v := range mlsvc.Spec.Selector.MatchLabels {\n\t\t\t\tsvc.ObjectMeta.Labels[k] = v\n\t\t\t}\n\n\t\t\t// set selector for pods\n\t\t\tSetAdditionalKeyValuePairs(svc.Spec.Selector, mlsvc.Name, role.Name, nil, nil)\n\n\t\t\t// set the owner so that garbage collection can kicks in\n\t\t\tif err := ctrl.SetControllerReference(mlsvc, svc, r.Scheme); err != nil {\n\t\t\t\tlog.Error(err, \"unable to set ownerReference from MLService to Service\")\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\t// end of ctrl.CreateOrUpdate\n\t\t\treturn nil\n\t\t}); err != nil {\n\t\t\t// error handling of ctrl.CreateOrUpdate\n\t\t\tlog.Error(err, \"unable to ensure service is correct\")\n\t\t\treturn err\n\t\t}\n\n\t}\n\treturn nil\n}\n\n// cleanOwnedServices will delete any existing Service resources that\n// were created for the given MLService\nfunc (r *MLServiceReconciler) cleanOwnedServices(ctx context.Context, mlsvc *monolithv1.MLService) error {\n\tlog := log.FromContext(ctx).WithValues(\"MLService\", mlsvc.Name)\n\tlog.Info(\"finding existing Service for MLService resource\")\n\n\t// List all service resources owned by this MLService\n\tservices, err := r.getOwnedServices(ctx, mlsvc)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, svc := range services.Items {\n\t\tif !svc.GetDeletionTimestamp().IsZero() {\n\t\t\t// Service already deleted, ignore.\n\t\t\tcontinue\n\t\t}\n\n\t\t// Delete service\n\t\tif err := r.Delete(ctx, &svc); err != nil {\n\t\t\tlog.Error(err, \"failed to delete Service resource: \"+svc.Name)\n\t\t\treturn err\n\t\t}\n\n\t\tlog.Info(\"delete service resource: \" + svc.Name)\n\t}\n\n\treturn nil\n}\n\n// getOwnedServices return all services owned by the MLService\nfunc (r *MLServiceReconciler) getOwnedServices(ctx context.Context, mlsvc *monolithv1.MLService) (*corev1.ServiceList, error) {\n\tvar services corev1.ServiceList\n\tif err := r.List(ctx, &services, client.InNamespace(mlsvc.Namespace),\n\t\tclient.MatchingLabels(mlsvc.Spec.Selector.MatchLabels)); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &services, nil\n}\n\n// GetServicePorts return HTTP port and gRPC port\nfunc GetServicePorts(ports []monolithv1.ServicePort) []corev1.ServicePort {\n\tvar httpPort int32 = DefaultHttpPort\n\tvar rpcPort int32 = DefaultRpcPort\n\tfor _, port := range ports {\n\t\tif port.Type == monolithv1.ServicePortTypeHttp {\n\t\t\thttpPort = port.Port\n\t\t} else if port.Type == monolithv1.ServicePortTypeRpc {\n\t\t\trpcPort = port.Port\n\t\t} else {\n\t\t\t// ignore non-http and non-rpc port\n\t\t\tcontinue\n\t\t}\n\t}\n\treturn []corev1.ServicePort{\n\t\t{\n\t\t\tName: strings.ToLower(string(monolithv1.ServicePortTypeHttp)),\n\t\t\tPort: httpPort,\n\t\t\tTargetPort: intstr.IntOrString{\n\t\t\t\tType:   intstr.Int,\n\t\t\t\tIntVal: int32(httpPort),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: strings.ToLower(string(monolithv1.ServicePortTypeRpc)),\n\t\t\tPort: rpcPort,\n\t\t\tTargetPort: intstr.IntOrString{\n\t\t\t\tType:   intstr.Int,\n\t\t\t\tIntVal: int32(rpcPort),\n\t\t\t},\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "deploy/controllers/status.go",
    "content": "package controllers\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\tmonolithv1 \"code.byted.org/data/monolith/deploy/api/v1\"\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tutildeployment \"k8s.io/kubectl/pkg/util/deployment\"\n\t\"sigs.k8s.io/controller-runtime/pkg/client\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log\"\n)\n\n// getMLServiceStatus return the status of MLService along with a reason and message.\n// Queuing: at least one deployment owned by this MLService is in status Queuing\n// Deploying: at least one deployment owned by this MLService is in status Deploying\n// Running: all deployments owned by this MLService is in status Running\n// Abnormal: at least one deployment owned by this MLService is in status Abnormal\n// Deleting: MLService DeletionTimestamp is not zero\n// Stopping: at least one deployment owned by this MLService is in status Stopping\n// Stopped: all deployments owned by this MLService is in status Stopped\nfunc (r *MLServiceReconciler) getMLServiceStatus(ctx context.Context, mlsvc *monolithv1.MLService) (phase monolithv1.ServicePhase, reason, message string, err error) {\n\tlog := log.FromContext(ctx).WithName(\"MLService\").WithValues(\"mlservice\", mlsvc.Name)\n\t// the status of Deleting\n\tif !mlsvc.GetDeletionTimestamp().IsZero() {\n\t\tphase = monolithv1.ServiceDeleting\n\t\treturn\n\t}\n\n\t// List all deployment resources owned by this MLService\n\tdeployments, err := r.getOwnedDeployments(ctx, mlsvc)\n\tif err != nil {\n\t\tlog.Error(err, \"get owned deployments failed\")\n\t\treturn\n\t}\n\n\tdeploymentStatusCount := make(map[monolithv1.ServicePhase]int, 0)\n\tfor _, deployment := range deployments.Items {\n\t\tdeploymentPhase, deploymentReason, deploymentMessage, dErr := r.getDeploymentStatus(ctx, &deployment)\n\t\tif dErr != nil {\n\t\t\terr = dErr\n\t\t\tlog.Error(err, \"get status of deployment failed\", \"deployment\", deployment.Name)\n\t\t\treturn\n\t\t}\n\n\t\tdeploymentStatusCount[deploymentPhase]++\n\n\t\t// at least one is abnormal\n\t\tif deploymentPhase == monolithv1.ServiceAbnormal {\n\t\t\tphase = deploymentPhase\n\t\t\treason = deploymentReason\n\t\t\tif deploymentMessage != \"\" {\n\t\t\t\tmessage = fmt.Sprintf(\"[Deployment %s] %s\", deployment.Name, deploymentMessage)\n\t\t\t}\n\t\t\tlog.Info(\"at least one deployment is abnormal\", \"deployment\", deployment.Name, \"phase\", phase, \"reason\", reason, \"message\", message)\n\t\t\treturn\n\t\t}\n\n\t\t// at least one is queuing\n\t\tif deploymentPhase == monolithv1.ServiceQueuing {\n\t\t\tphase = deploymentPhase\n\t\t\tlog.Info(\"at least one deployment is queuing\", \"deployment\", deployment.Name, \"phase\", phase)\n\t\t\treturn\n\t\t}\n\n\t\t// at least one is stopping\n\t\tif deploymentPhase == monolithv1.ServiceStopping {\n\t\t\tphase = deploymentPhase\n\t\t\tlog.Info(\"at least one deployment is stopping\", \"deployment\", deployment.Name, \"phase\", phase)\n\t\t\treturn\n\t\t}\n\t}\n\n\t// all deployments Running\n\tif count, ok := deploymentStatusCount[monolithv1.ServiceRunning]; ok && count == len(deployments.Items) {\n\t\tphase = monolithv1.ServiceRunning\n\t\tlog.Info(\"all deployments are running\", \"phase\", phase)\n\t\treturn\n\t}\n\n\t// all deployments Stopped\n\tif count, ok := deploymentStatusCount[monolithv1.ServiceStopped]; ok && count == len(deployments.Items) {\n\t\tphase = monolithv1.ServiceStopped\n\t\tlog.Info(\"all deployments are stopped\", \"phase\", phase)\n\t\treturn\n\t}\n\n\tphase = monolithv1.ServiceDeploying\n\treturn\n}\n\n// getDeploymentStatus return the status of Deployment along with a reason and message.\n// status is generated based on the latest replicaset\n// Queuing: the latest ReplicaSet not exists, or it's status is Queuing\n// Deploying: status of the latest ReplicaSet is Deploying\n// Running: status of the latest ReplicaSet is Running\n// Abnormal: status of the latest ReplicaSet is Abnormal\n// Deleting: Deployment  DeletionTimestamp is not zero\n// Stopping: the latest ReplicaSet is Stopping\n// Stopped: the latest ReplicaSet is Stopped\nfunc (r *MLServiceReconciler) getDeploymentStatus(ctx context.Context, deployment *appsv1.Deployment) (phase monolithv1.ServicePhase, reason, message string, err error) {\n\tlog := log.FromContext(ctx).WithName(\"Deployment\").WithValues(\"deployment\", deployment.Name)\n\n\t// the status of Deleting\n\tif !deployment.GetDeletionTimestamp().IsZero() {\n\t\tphase = monolithv1.ServiceDeleting\n\t\treturn\n\t}\n\n\t// get all replicaset\n\tvar replicasets appsv1.ReplicaSetList\n\tif err = r.List(ctx, &replicasets, client.InNamespace(deployment.Namespace),\n\t\tclient.MatchingLabels(deployment.Spec.Selector.MatchLabels)); err != nil {\n\t\tlog.Error(err, \"list replicasets failed\")\n\t\treturn\n\t}\n\n\t// get latest replicaset\n\t_, _, latest, err := utildeployment.GetAllReplicaSets(deployment, &r.AppsV1Client)\n\tif latest == nil {\n\t\tlog.Info(\"latest replicaset not found, set phase to queuing\")\n\t\tphase = monolithv1.ServiceQueuing\n\t\treturn\n\t}\n\n\treturn r.getReplicaSetStatus(ctx, latest)\n}\n\n// getReplicaSetStatus return the status of ReplicaSet along with a reason and message.\n// Queuing: 1)  All Pods are in status Queuing 2)PodGroup is Pending;\n// Deploying: at least one Pod is in status Deploying\n// Running:  at least one Pod is in status Running\n// Abnormal: all Pods are in status Abnormal\n// Deleting: ReplicaSet DeletionTimestamp is not zero\n// Stopping: replicas is 0 but pods exits\n// Stopped: replicas is 0 and no pods exits\nfunc (r *MLServiceReconciler) getReplicaSetStatus(ctx context.Context, replicaset *appsv1.ReplicaSet) (phase monolithv1.ServicePhase, reason, message string, err error) {\n\tlog := log.FromContext(ctx).WithName(\"ReplicaSet\").WithValues(\"replicaset\", replicaset.Name)\n\n\t// list all pods of the replicaset\n\tvar podList corev1.PodList\n\tif err = r.List(ctx, &podList, client.InNamespace(replicaset.Namespace),\n\t\tclient.MatchingLabels(replicaset.Spec.Selector.MatchLabels)); client.IgnoreNotFound(err) != nil {\n\t\tlog.Error(err, \"list pods failed\")\n\t\treturn\n\t}\n\n\t// Stopping\n\tif *replicaset.Spec.Replicas == 0 && len(podList.Items) != 0 {\n\t\tphase = monolithv1.ServiceStopping\n\t\treturn\n\t}\n\n\t// Stopped\n\tif *replicaset.Spec.Replicas == 0 && len(podList.Items) == 0 {\n\t\tphase = monolithv1.ServiceStopped\n\t\treturn\n\t}\n\n\tpodStatusCount := make(map[monolithv1.ServicePhase]int, 0)\n\tvar abnormalReason, abnormalMessage string\n\tfor _, pod := range podList.Items {\n\t\tpodPhase, podReason, podMessage, dErr := r.getPodStatus(ctx, &pod)\n\t\tif dErr != nil {\n\t\t\terr = dErr\n\t\t\tlog.Error(err, \"get pod status failed\")\n\t\t\treturn\n\t\t}\n\n\t\tlog.Info(\"pod status\", \"pod\", pod.Name, \"phase\", podPhase, \"reason\", podReason, \"message\", podMessage)\n\t\tpodStatusCount[podPhase]++\n\n\t\t// at least one pod is deploying or running\n\t\tif podPhase == monolithv1.ServiceDeploying || podPhase == monolithv1.ServiceRunning {\n\t\t\tphase = podPhase\n\t\t\treason = podReason\n\t\t\tmessage = fmt.Sprintf(\"[Pod %s] %s\", pod.Name, podMessage)\n\t\t\tlog.Info(fmt.Sprintf(\"at least one pod is %s\", phase), \"pod\", pod.Name)\n\t\t\treturn\n\t\t}\n\n\t\tif podPhase == monolithv1.ServiceAbnormal && (podReason != \"\" || podMessage != \"\") {\n\t\t\tabnormalReason = podReason\n\t\t\tabnormalMessage = podMessage\n\t\t}\n\t}\n\n\t// all pods Queuing\n\tif count, ok := podStatusCount[monolithv1.ServiceQueuing]; ok && count == len(podList.Items) {\n\t\tlog.Info(\"all pods are queuing\")\n\t\tphase = monolithv1.ServiceQueuing\n\t\treturn\n\t}\n\n\t// all pods Abnormal\n\tif count, ok := podStatusCount[monolithv1.ServiceAbnormal]; ok && count == len(podList.Items) {\n\t\tlog.Info(\"all pods are abnormal\")\n\t\tphase = monolithv1.ServiceAbnormal\n\t\treason = abnormalReason\n\t\tmessage = abnormalMessage\n\t\treturn\n\t}\n\n\treturn\n}\n\n// getPodStatus return the status of Pod along with a reason and message.\n// Queuing: condition PodScheduled is False\n// Deploying: condition PodScheduled is True\n// Running:  condition Ready is True\n// Abnormal: Pod phase Succeeded、Failed、Unknown, Pending or Running but crash\n// Deleting: ReplicaSet  DeletionTimestamp is not zero\n// ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase\nfunc (r *MLServiceReconciler) getPodStatus(ctx context.Context, pod *corev1.Pod) (phase monolithv1.ServicePhase, reason, message string, err error) {\n\tlog := log.FromContext(ctx).WithName(\"Pod\").WithValues(\"pod\", pod.Name)\n\n\t// the status of Deleting\n\tif !pod.GetDeletionTimestamp().IsZero() {\n\t\tphase = monolithv1.ServiceDeleting\n\t\treturn\n\t}\n\n\t// Queuing\n\tif cond := getPodCondition(pod, corev1.PodScheduled); cond != nil && cond.Status != corev1.ConditionTrue {\n\t\tlog.Info(\"PodScheduled condition is false, set phase to queuing\")\n\t\tphase = monolithv1.ServiceQueuing\n\t\tif cond.Message == \"\" {\n\t\t\treason = ReasonInProgress\n\t\t} else {\n\t\t\treason = ReasonInsufficientClusterResources\n\t\t}\n\t\treturn\n\t}\n\n\t// Running\n\tif cond := getPodCondition(pod, corev1.PodReady); cond != nil && cond.Status == corev1.ConditionTrue {\n\t\tlog.Info(\"PodReady condition is true, set phase to running\")\n\t\tphase = monolithv1.ServiceRunning\n\t\treturn\n\t}\n\n\t// Abnormal\n\t// Abnormal case 1: pod Failure\n\tif pod.Status.Phase == corev1.PodFailed || pod.Status.Phase == corev1.PodSucceeded {\n\t\tlog.Info(fmt.Sprintf(\"pod Phase is %s, set phase to abnormal\", pod.Status.Phase))\n\t\tphase = monolithv1.ServiceAbnormal\n\t\tif pod.Status.Reason == ContainerEvicted {\n\t\t\treason = ReasonEvicted\n\t\t\tmessage = \"pod evicted\"\n\t\t} else {\n\t\t\treason = ReasonServiceExceptionExited\n\t\t\tmessage = \"pod exited unexpectedly\"\n\t\t}\n\t\treturn\n\t}\n\t// Abnormal case 2: pod status unknown\n\tif pod.Status.Phase == corev1.PodUnknown {\n\t\tlog.Info(\"pod Phase is Unknown, set phase to abnormal\")\n\t\tphase = monolithv1.ServiceAbnormal\n\t\treason = ReasonStatusNotFound\n\t\tmessage = \"pod in status Unknown\"\n\t\treturn\n\t}\n\t// Abnormal case 3: container creating error or exited\n\tif pod.Status.Phase == corev1.PodRunning || pod.Status.Phase == corev1.PodPending {\n\t\tfor _, status := range pod.Status.InitContainerStatuses {\n\t\t\tif tmpReason, tmpMessage := getContainerAbnormalMessage(status, true); tmpReason != \"\" {\n\t\t\t\tphase = monolithv1.ServiceAbnormal\n\t\t\t\treason = tmpReason\n\t\t\t\tmessage = tmpMessage\n\t\t\t\tlog.Info(fmt.Sprintf(\"pod Phase %s, but InitContainer is abnormal\", pod.Status.Phase), \"reason\", reason, \"message\", message)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tfor _, status := range pod.Status.ContainerStatuses {\n\t\t\tif tmpReason, tmpMessage := getContainerAbnormalMessage(status, false); tmpReason != \"\" {\n\t\t\t\tphase = monolithv1.ServiceAbnormal\n\t\t\t\treason = tmpReason\n\t\t\t\tmessage = tmpMessage\n\t\t\t\tlog.Info(fmt.Sprintf(\"pod Phase %s, but container is abnormal\", pod.Status.Phase), \"reason\", reason, \"message\", message)\n\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tlog.Info(\"assume phase is deploying in other cases\")\n\tphase = monolithv1.ServiceDeploying\n\treturn\n}\n\nfunc getContainerAbnormalMessage(status corev1.ContainerStatus, isInitContainer bool) (reason, message string) {\n\twaiting, terminated := status.State.Waiting, status.State.Terminated\n\tif waiting != nil && waiting.Reason != PodInitializing && waiting.Reason != ContainerCreating {\n\t\treturn waiting.Reason, waiting.Message\n\t}\n\tif terminated != nil {\n\t\tif isInitContainer && terminated.ExitCode != 0 {\n\t\t\treason = terminated.Reason\n\t\t\tmessage = terminated.Message\n\t\t}\n\n\t\tif !isInitContainer {\n\t\t\treason = terminated.Reason\n\t\t\tif terminated.Message == \"\" {\n\t\t\t\tmessage = \"Container terminated.\"\n\t\t\t} else {\n\t\t\t\tmessage = terminated.Message\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\treturn\n}\n\nfunc getPodCondition(pod *corev1.Pod, conditionType corev1.PodConditionType) *corev1.PodCondition {\n\tfor _, cond := range pod.Status.Conditions {\n\t\tif cond.Type == conditionType {\n\t\t\treturn &cond\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "deploy/go.mod",
    "content": "module code.byted.org/data/monolith/deploy\n\ngo 1.15\n\nrequire (\n\tgithub.com/onsi/ginkgo v1.16.5 // indirect\n\tgithub.com/onsi/gomega v1.18.1 // indirect\n\tk8s.io/api v0.23.5\n\tk8s.io/apimachinery v0.23.5\n\tk8s.io/client-go v0.23.5\n\tk8s.io/kubectl v0.20.6\n\tsigs.k8s.io/controller-runtime v0.10.2\n\n)\n\nreplace (\n\tsigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.8.3\n\tk8s.io/api => k8s.io/api v0.20.6\n\tk8s.io/apimachinery => k8s.io/apimachinery v0.20.6\n\tk8s.io/client-go => k8s.io/client-go v0.20.6\n)"
  },
  {
    "path": "deploy/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=\ncloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=\ncloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=\ncloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=\ncloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=\ncloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=\ncloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=\ncloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=\ncloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=\ncloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=\ncloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=\ncloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=\ncloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=\ngithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=\ngithub.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest v0.11.1 h1:eVvIXUKiTgv++6YnWb42DUA1YL7qDugnKP0HljexdnQ=\ngithub.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=\ngithub.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM=\ngithub.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE=\ngithub.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=\ngithub.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=\ngithub.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=\ngithub.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=\ngithub.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=\ngithub.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20220418222510-f25a4f6275ed/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=\ngithub.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=\ngithub.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=\ngithub.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=\ngithub.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=\ngithub.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=\ngithub.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=\ngithub.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=\ngithub.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=\ngithub.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=\ngithub.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=\ngithub.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=\ngithub.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=\ngithub.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=\ngithub.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=\ngithub.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\ngithub.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\ngithub.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=\ngithub.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=\ngithub.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=\ngithub.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=\ngithub.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=\ngithub.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=\ngithub.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=\ngithub.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=\ngithub.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=\ngithub.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=\ngithub.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=\ngithub.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=\ngithub.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=\ngithub.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=\ngithub.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=\ngithub.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=\ngithub.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=\ngithub.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=\ngithub.com/go-logr/logr v0.3.0 h1:q4c+kbcR0d5rSurhBR8dIgieOaYpXtsdTYfx22Cu6rs=\ngithub.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=\ngithub.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=\ngithub.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-logr/zapr v0.2.0 h1:v6Ji8yBW77pva6NkJKQdHLAJKrIJKRHz0RXwPqCHSR4=\ngithub.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU=\ngithub.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=\ngithub.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=\ngithub.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=\ngithub.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=\ngithub.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=\ngithub.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=\ngithub.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=\ngithub.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=\ngithub.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=\ngithub.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=\ngithub.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=\ngithub.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=\ngithub.com/golangplus/bytes v1.0.0/go.mod h1:AdRaCFwmc/00ZzELMWb01soso6W1R/++O1XL80yAn+A=\ngithub.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=\ngithub.com/golangplus/fmt v1.0.0/go.mod h1:zpM0OfbMCjPtd2qkTD/jX2MgiFCqklhSUFyDW44gVQE=\ngithub.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=\ngithub.com/golangplus/testing v1.0.0/go.mod h1:ZDreixUV3YzhoVraIDyOzHrr76p6NUh6k/pPg/Q3gYA=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=\ngithub.com/google/cel-go v0.12.5/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw=\ngithub.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=\ngithub.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=\ngithub.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=\ngithub.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=\ngithub.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=\ngithub.com/googleapis/gnostic v0.5.1 h1:A8Yhf6EtqTv9RMsU6MQTyrtV1TjWlR6xU9BsZIwuTCM=\ngithub.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=\ngithub.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=\ngithub.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=\ngithub.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=\ngithub.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=\ngithub.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=\ngithub.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=\ngithub.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=\ngithub.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=\ngithub.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=\ngithub.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=\ngithub.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=\ngithub.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=\ngithub.com/imdario/mergo v0.3.10 h1:6q5mVkdH/vYmqngx7kZQTjJ5HRsx+ImorDIEQ+beJgc=\ngithub.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=\ngithub.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=\ngithub.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=\ngithub.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=\ngithub.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=\ngithub.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=\ngithub.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=\ngithub.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=\ngithub.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=\ngithub.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=\ngithub.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=\ngithub.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=\ngithub.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=\ngithub.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=\ngithub.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=\ngithub.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=\ngithub.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=\ngithub.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=\ngithub.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=\ngithub.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=\ngithub.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=\ngithub.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=\ngithub.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=\ngithub.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc=\ngithub.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=\ngithub.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=\ngithub.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=\ngithub.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=\ngithub.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=\ngithub.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=\ngithub.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=\ngithub.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=\ngithub.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys=\ngithub.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=\ngithub.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=\ngithub.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=\ngithub.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=\ngithub.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=\ngithub.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=\ngithub.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=\ngithub.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=\ngithub.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=\ngithub.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=\ngithub.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=\ngithub.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=\ngithub.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=\ngithub.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=\ngithub.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=\ngithub.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=\ngithub.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=\ngithub.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=\ngo.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=\ngo.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=\ngo.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ=\ngo.etcd.io/etcd/client/v2 v2.305.5/go.mod h1:zQjKllfqfBVyVStbt4FaosoX2iYd8fV/GRy/PbowgP4=\ngo.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c=\ngo.etcd.io/etcd/pkg/v3 v3.5.5/go.mod h1:6ksYFxttiUGzC2uxyqiyOEvhAiD0tuIqSZkX3TyPdaE=\ngo.etcd.io/etcd/raft/v3 v3.5.5/go.mod h1:76TA48q03g1y1VpTue92jZLr9lIHKUNcYdZOOGyx8rI=\ngo.etcd.io/etcd/server/v3 v3.5.5/go.mod h1:rZ95vDw/jrvsbj9XpTqPrTAB9/kzchVdhRirySPkUBc=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=\ngo.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.25.0/go.mod h1:E5NNboN0UqSAki0Atn9kVwaN7I+l25gGxDqBueo/74E=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.0/go.mod h1:9NiG9I2aHTKkcxqCILhjtyNA1QEiCjdBACv4IvrFQ+c=\ngo.opentelemetry.io/otel v1.0.1/go.mod h1:OPEOD4jIT2SlZPMmwT6FqZz2C0ZNdQqiWcoK6M0SNFU=\ngo.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM=\ngo.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ=\ngo.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.0.1/go.mod h1:Kv8liBeVNFkkkbilbgWRpV+wWuu+H5xdOT6HAgd30iw=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.1/go.mod h1:xOvWoTOrQjxjW61xtOmD/WKGRYb/P4NzRo3bs65U6Rk=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0/go.mod h1:OfUCyyIiDvNXHWpcWgbF+MWvqPZiNa3YDEnivcnYsV0=\ngo.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A=\ngo.opentelemetry.io/otel/sdk v1.0.1/go.mod h1:HrdXne+BiwsOHYYkBE5ysIcv2bvdZstxzmCQhxTcZkI=\ngo.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE=\ngo.opentelemetry.io/otel/trace v1.0.1/go.mod h1:5g4i4fKLaX2BQpSBsxw8YYcgKpMMSW3x7ZTuYBr3sUk=\ngo.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4=\ngo.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.opentelemetry.io/proto/otlp v0.9.0/go.mod h1:1vKfU9rv61e9EVGthD1zNvUbiwPcimSsOPU9brfSHJg=\ngo.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=\ngo.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=\ngo.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=\ngo.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=\ngo.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=\ngo.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngo.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=\ngo.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=\ngo.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=\ngolang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=\ngolang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=\ngolang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=\ngolang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=\ngolang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=\ngolang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=\ngolang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=\ngolang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=\ngolang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=\ngolang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=\ngolang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 h1:Frnccbp+ok2GkUS2tC84yAq/U9Vg+0sIO7aRL3T4Xnc=\ngolang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=\ngolang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=\ngolang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=\ngolang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=\ngolang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=\ngolang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=\ngolang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=\ngolang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=\ngolang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=\ngolang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=\ngolang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=\ngolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=\ngolang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=\ngolang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=\ngolang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngomodules.xyz/jsonpatch/v2 v2.1.0 h1:Phva6wqu+xR//Njw6iorylFFgn/z547tw5Ne3HZPQ+k=\ngomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=\ngomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=\ngomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=\ngoogle.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=\ngoogle.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=\ngoogle.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=\ngoogle.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=\ngoogle.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=\ngoogle.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=\ngoogle.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=\ngoogle.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=\ngoogle.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=\ngoogle.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=\ngoogle.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=\ngoogle.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=\ngoogle.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=\ngoogle.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=\ngoogle.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=\ngoogle.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=\ngoogle.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=\ngoogle.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=\ngoogle.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=\ngoogle.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=\ngoogle.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=\ngoogle.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=\ngoogle.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=\ngoogle.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=\ngoogle.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=\ngoogle.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=\ngoogle.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=\ngoogle.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=\ngoogle.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=\ngoogle.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=\ngopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=\ngotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=\ngotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nk8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=\nk8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8=\nk8s.io/api v0.20.6 h1:bgdZrW++LqgrLikWYNruIKAtltXbSCX2l5mJu11hrVE=\nk8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=\nk8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA=\nk8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8=\nk8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I=\nk8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg=\nk8s.io/apiextensions-apiserver v0.20.1 h1:ZrXQeslal+6zKM/HjDXLzThlz/vPSxrfK3OqL8txgVQ=\nk8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk=\nk8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo=\nk8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ=\nk8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=\nk8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=\nk8s.io/apimachinery v0.20.6 h1:R5p3SlhaABYShQSO6LpPsYHjV05Q+79eBUR0Ut/f4tk=\nk8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=\nk8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0=\nk8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=\nk8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg=\nk8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=\nk8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=\nk8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84=\nk8s.io/cli-runtime v0.20.6/go.mod h1:JVERW478qcxWrUjJuWQSqyJeiz9QC4T6jmBznHFBC8w=\nk8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw=\nk8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY=\nk8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=\nk8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE=\nk8s.io/client-go v0.20.6 h1:nJZOfolnsVtDtbGJNCxzOtKUAu7zvXjB8+pMo9UNxZo=\nk8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=\nk8s.io/client-go v0.23.5 h1:zUXHmEuqx0RY4+CsnkOn5l0GU+skkRXKGJrhmE2SLd8=\nk8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4=\nk8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8=\nk8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg=\nk8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=\nk8s.io/code-generator v0.20.6/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU=\nk8s.io/code-generator v0.26.0/go.mod h1:OMoJ5Dqx1wgaQzKgc+ZWaZPfGjdRq/Y3WubFrZmeI3I=\nk8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=\nk8s.io/component-base v0.20.2/go.mod h1:pzFtCiwe/ASD0iV7ySMu8SYVJjCapNM9bjvk7ptpKh0=\nk8s.io/component-base v0.20.6 h1:G0inASS5vAqCpzs7M4Sp9dv9d0aElpz39zDHbSB4f4g=\nk8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=\nk8s.io/component-base v0.26.0 h1:0IkChOCohtDHttmKuz+EP3j3+qKmV55rM9gIFTXA7Vs=\nk8s.io/component-base v0.26.0/go.mod h1:lqHwlfV1/haa14F/Z5Zizk5QmzaVf23nQzCwVOQpfC8=\nk8s.io/component-helpers v0.20.6/go.mod h1:d4rFhZS/wxrZCxRiJJiWf1mVGVeMB5/ey3Yv8/rOp78=\nk8s.io/component-helpers v0.26.0/go.mod h1:jHN01qS/Jdj95WCbTe9S2VZ9yxpxXNY488WjF+yW4fo=\nk8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=\nk8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=\nk8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=\nk8s.io/gengo v0.0.0-20220902162205-c0856e24416d/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=\nk8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=\nk8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=\nk8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=\nk8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=\nk8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw=\nk8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=\nk8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=\nk8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=\nk8s.io/kms v0.26.0/go.mod h1:ReC1IEGuxgfN+PDCIpR6w8+XMmDE7uJhxcCwMZFdIYc=\nk8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=\nk8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=\nk8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4=\nk8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=\nk8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M=\nk8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=\nk8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=\nk8s.io/kubectl v0.20.6 h1:G0a3fJXvypzN1fDcO+clH131rpDxNtDZIgSuogSCtng=\nk8s.io/kubectl v0.20.6/go.mod h1:yTCGVrlkBuQhFbKA1R65+lQ9hH7XeyOqUd0FUPFicPg=\nk8s.io/kubectl v0.26.0 h1:xmrzoKR9CyNdzxBmXV7jW9Ln8WMrwRK6hGbbf69o4T0=\nk8s.io/kubectl v0.26.0/go.mod h1:eInP0b+U9XUJWSYeU9XZnTA+cVYuWyl3iYPGtru0qhQ=\nk8s.io/metrics v0.20.6/go.mod h1:d+OAIaXutom9kGWcBit/M8OkDpIzBKTsm47+KcUt7VI=\nk8s.io/metrics v0.26.0/go.mod h1:cf5MlG4ZgWaEFZrR9+sOImhZ2ICMpIdNurA+D8snIs8=\nk8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20210111153108-fddb29f9d009 h1:0T5IaWHO3sJTEmCP6mUlBvMukxPKUQWqiI/YuiBNMiQ=\nk8s.io/utils v0.0.0-20210111153108-fddb29f9d009/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20211116205334-6203023598ed h1:ck1fRPWPJWsMd8ZRFsWc6mh/zHp5fZ/shhbrgPUxDAE=\nk8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nk8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y=\nk8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=\nsigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33/go.mod h1:soWkSNf2tZC7aMibXEqVhCd73GOY5fJikn8qbdzemB0=\nsigs.k8s.io/controller-runtime v0.8.3 h1:GMHvzjTmaWHQB8HadW+dIvBoJuLvZObYJ5YoZruPRao=\nsigs.k8s.io/controller-runtime v0.8.3/go.mod h1:U/l+DUopBc1ecfRZ5aviA9JDmGFQKvLf5YkZNx2e0sU=\nsigs.k8s.io/controller-runtime v0.14.1 h1:vThDes9pzg0Y+UbCPY3Wj34CGIYPgdmspPm2GIpxpzM=\nsigs.k8s.io/controller-runtime v0.14.1/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU=\nsigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s=\nsigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=\nsigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=\nsigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=\nsigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=\nsigs.k8s.io/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM=\nsigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s=\nsigs.k8s.io/kustomize/cmd/config v0.10.9/go.mod h1:T0s850zPV3wKfBALA0dyeP/K74jlJcoP8Pr9ZWwE3MQ=\nsigs.k8s.io/kustomize/kustomize/v4 v4.5.7/go.mod h1:VSNKEH9D9d9bLiWEGbS6Xbg/Ih0tgQalmPvntzRxZ/Q=\nsigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk=\nsigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4=\nsigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=\nsigs.k8s.io/structured-merge-diff/v4 v4.0.3 h1:4oyYo8NREp49LBBhKxEqCulFjg26rawYKrnCmg+Sr6c=\nsigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=\nsigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=\nsigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=\nsigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\nsigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=\nsigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=\n"
  },
  {
    "path": "deploy/hack/boilerplate.go.txt",
    "content": "/*\nCopyright 2023.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/"
  },
  {
    "path": "deploy/main.go",
    "content": "/*\nCopyright 2023.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"os\"\n\n\t// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)\n\t// to ensure that exec-entrypoint and run can make use of them.\n\t_ \"k8s.io/client-go/plugin/pkg/client/auth\"\n\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\tutilruntime \"k8s.io/apimachinery/pkg/util/runtime\"\n\tclientgoscheme \"k8s.io/client-go/kubernetes/scheme\"\n\tctrl \"sigs.k8s.io/controller-runtime\"\n\t\"sigs.k8s.io/controller-runtime/pkg/healthz\"\n\t\"sigs.k8s.io/controller-runtime/pkg/log/zap\"\n\n\tmlplatformv1 \"code.byted.org/data/monolith/deploy/api/v1\"\n\t\"code.byted.org/data/monolith/deploy/controllers\"\n\tappsclient \"k8s.io/client-go/kubernetes/typed/apps/v1\"\n\t//+kubebuilder:scaffold:imports\n)\n\nvar (\n\tscheme   = runtime.NewScheme()\n\tsetupLog = ctrl.Log.WithName(\"setup\")\n)\n\nfunc init() {\n\tutilruntime.Must(clientgoscheme.AddToScheme(scheme))\n\n\tutilruntime.Must(mlplatformv1.AddToScheme(scheme))\n\t//+kubebuilder:scaffold:scheme\n}\n\nfunc main() {\n\tvar metricsAddr string\n\tvar enableLeaderElection bool\n\tvar probeAddr string\n\tflag.StringVar(&metricsAddr, \"metrics-bind-address\", \":8080\", \"The address the metric endpoint binds to.\")\n\tflag.StringVar(&probeAddr, \"health-probe-bind-address\", \":8081\", \"The address the probe endpoint binds to.\")\n\tflag.BoolVar(&enableLeaderElection, \"leader-elect\", false,\n\t\t\"Enable leader election for controller manager. \"+\n\t\t\t\"Enabling this will ensure there is only one active controller manager.\")\n\topts := zap.Options{\n\t\tDevelopment: true,\n\t}\n\topts.BindFlags(flag.CommandLine)\n\tflag.Parse()\n\n\tctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))\n\n\tmgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{\n\t\tScheme:                 scheme,\n\t\tMetricsBindAddress:     metricsAddr,\n\t\tPort:                   9443,\n\t\tHealthProbeBindAddress: probeAddr,\n\t\tLeaderElection:         enableLeaderElection,\n\t\tLeaderElectionID:       \"183d5a48.volcengine.com\",\n\t})\n\tif err != nil {\n\t\tsetupLog.Error(err, \"unable to start manager\")\n\t\tos.Exit(1)\n\t}\n\n\tappsClient, err := appsclient.NewForConfig(mgr.GetConfig())\n\tif err != nil {\n\t\tsetupLog.Error(err, \"unable to create AppsV1Client\")\n\t\tos.Exit(1)\n\t}\n\tif err = (&controllers.MLServiceReconciler{\n\t\tAppsV1Client: *appsClient,\n\t\tClient:       mgr.GetClient(),\n\n\t\tScheme: mgr.GetScheme(),\n\t}).SetupWithManager(mgr); err != nil {\n\t\tsetupLog.Error(err, \"unable to create controller\", \"controller\", \"MLService\")\n\t\tos.Exit(1)\n\t}\n\t//+kubebuilder:scaffold:builder\n\n\tif err := mgr.AddHealthzCheck(\"healthz\", healthz.Ping); err != nil {\n\t\tsetupLog.Error(err, \"unable to set up health check\")\n\t\tos.Exit(1)\n\t}\n\tif err := mgr.AddReadyzCheck(\"readyz\", healthz.Ping); err != nil {\n\t\tsetupLog.Error(err, \"unable to set up ready check\")\n\t\tos.Exit(1)\n\t}\n\n\tsetupLog.Info(\"starting manager\")\n\tif err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {\n\t\tsetupLog.Error(err, \"problem running manager\")\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "deploy/serving/agent.conf",
    "content": "bzid {{bzid}}\nbase_name {{base_name}}\nbase_path {{base_path}}\nnum_ps {{num_ps}}\nserver_type {{server_type}}\nzk_servers {{zk_servers}}\ndense_alone {{dense_alone}}\n\nupdate_model_status_interval 10\nenable_batching {{enable_batching}}\ntensorflow_session_parallelism 0\ntensorflow_intra_op_parallelism 0\ntensorflow_inter_op_parallelism 0\nper_process_gpu_memory_fraction 0\nnum_load_threads 0\nnum_unload_threads 0\nmax_num_load_retries 5\nload_retry_interval_micros 60 * 1000 * 1000\nfile_system_poll_wait_seconds 1\nfile_system_poll_wait_seconds_ps 0\nflush_filesystem_caches true\nsaved_model_tags none\ngrpc_channel_arguments none\ngrpc_max_threads 0\nenable_model_warmup true\nenable_signature_method_name_check false\nxla_cpu_compilation_enabled false\nenable_profiler true\naio_thread_num 20\ndc_aware true"
  },
  {
    "path": "deploy/serving/docker/Dockerfile",
    "content": "FROM debian:buster-20221219\n\nLABEL maintainer=\"Monolith\"\n\nARG PYPI_SOURCE=https://pypi.tuna.tsinghua.edu.cn/simple\n\n# pre install for tsinghua apt source\nRUN set -eux; \\\n    apt-get update; \\\n    apt-get install -y --no-install-recommends \\\n        apt-transport-https ca-certificates wget dirmngr gnupg \\\n        software-properties-common \\\n    ;\n\n# install java\nRUN set -eux; \\\n    wget -qO - https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public | apt-key add - ; \\\n    add-apt-repository --yes https://adoptopenjdk.jfrog.io/adoptopenjdk/deb/; \\\n    apt-get update; \\\n    apt-get install -y --no-install-recommends adoptopenjdk-8-hotspot\n\nENV JAVA_HOME /usr/lib/jvm/adoptopenjdk-8-hotspot-amd64/\nENV JRE_HOME ${JAVA_HOME}/jre\nENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib\nENV PATH ${JAVA_HOME}/bin:$PATH\n\n# copy assets\nADD deploy/serving/docker/assets /tmp/assets\n# Copy the service of dumping environment variables\nCOPY deploy/serving/docker/assets/configurator_dumpenv.sh /root/.system/\nCOPY deploy/serving/docker/assets/configurator_dumpenv.service /etc/systemd/system/\n# Configure bashrc\nCOPY deploy/serving/docker/assets/bashrc /root/.bashrc\n\n# Change mirrors in /etc/apt/sources.list to tsinghua mirrors\nRUN set -eux; \\\n    { \\\n        echo \"deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free\"; \\\n        echo \"deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free\"; \\\n        echo \"deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free\"; \\\n        echo \"deb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free\"; \\\n    } > /etc/apt/sources.list; \\\n    \\\n# Install required packages\n    apt-get update; \\\n    apt-get upgrade -y; \\\n    DEBIAN_FRONTEND=noninteractive \\\n    apt-get install -y --no-install-recommends \\\n        curl lsof procps locales tzdata less vim python \\\n        build-essential autoconf automake bzip2 file imagemagick libbz2-dev \\\n        libc6-dev openssl libcurl4-openssl-dev libdb-dev libevent-dev \\\n        libffi-dev libgdbm-dev libgeoip-dev libglib2.0-dev libjpeg-dev \\\n        libkrb5-dev liblzma-dev libmagickcore-dev libmagickwand-dev \\\n        libncurses-dev libpng-dev libpq-dev libreadline-dev libsqlite3-dev \\\n        default-libmysqlclient-dev libssl-dev libtool libwebp-dev libxml2 \\\n        libxml2-dev libxslt-dev libyaml-dev make patch xz-utils zlib1g-dev \\\n        tcl tk git rsync ssh net-tools iputils-ping pbzip2 python-dev netcat \\\n        libxslt1-dev libcap2-bin libjemalloc2 libjemalloc-dev libsnappy1v5 \\\n        libtcmalloc-minimal4 libzookeeper-mt2 lldpd libnss3 pv gnupg2 libaio1 \\\n        systemd systemd-sysv libsystemd0 netcat-openbsd rsyslog unscd \\\n        apt-utils cgroup-bin cmake gdb libncurses5-dev libnss3-dev \\\n        libprotobuf-dev linux-base linux-libc-dev linux-perf \\\n        openssh-client openssh-server protobuf-compiler  \\\n        python-pip python-pycurl python-setuptools python-wheel python3-dev \\\n        python3-pip python3-pycurl python3-setuptools python3-wheel sysstat \\\n        telnet tree libunwind-dev numactl unzip \\\n    ; \\\n# Remove apt cache\n    rm -rf /var/lib/apt/lists/*\n\n# RDMA Essentials\n# download from https://linux.mellanox.com/public/repo/mlnx_rdma_minimal/5.0-1.0.0.0/debian10.0/\nRUN set -eux; \\\n    apt-get update && apt-get install -y --no-install-recommends \\\n    libnl-3-200=3.4.0-1 libnl-3-dev libnl-route-3-200=3.4.0-1 libnl-route-3-dev; \\\n    dpkg -i /tmp/assets/rdma/librdmacm1_50mlnx1-1.50100.0_amd64.deb \\\n        /tmp/assets/rdma/rdmacm-utils_50mlnx1-1.50100.0_amd64.deb \\\n        /tmp/assets/rdma/libibverbs1_50mlnx1-1.50100.0_amd64.deb \\\n        /tmp/assets/rdma/ibverbs-utils_50mlnx1-1.50100.0_amd64.deb \\\n        /tmp/assets/rdma/libibverbs-dev_50mlnx1-1.50100.0_amd64.deb \\\n        /tmp/assets/rdma/ibverbs-providers_50mlnx1-1.50100.0_amd64.deb \\\n        /tmp/assets/rdma/libibumad3_50mlnx1-1.50100.0_amd64.deb\n\nRUN set -eux; \\\n# Create tiger account\n    if ! id -u tiger >/dev/null 2>&1 ; then \\\n        groupadd -f tiger; \\\n        useradd -u 1000 -g tiger -d /home/tiger -m -s /bin/bash tiger; \\\n    fi; \\\n    mkdir -p /home/tiger/.service/ /opt/tiger /opt/log/tiger /var/log/tiger; \\\n    chown tiger:tiger /home/tiger/.service/ /opt/tiger /opt/log/tiger /var/log/tiger; \\\n# Change timezone\n    echo \"Asia/Shanghai\" > /etc/timezone; \\\n    ln -sfn /usr/share/zoneinfo/Asia/Shanghai /etc/localtime; \\\n    \\\n# Generate locales\n    sed -i 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen; \\\n    sed -i 's/# zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/' /etc/locale.gen; \\\n    locale-gen; \\\n# Configure permissions\n    chmod 700 /root/.system/configurator_dumpenv.sh; \\\n# Install pip\n    tar -xf /tmp/assets/Python-3.8.6.tar.xz && cd Python-3.8.6 && \\\n    ./configure --enable-optimizations && make -j8 build_all && make altinstall && \\\n    update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.8 3 && \\\n    ln -s /usr/share/pyshared/lsb_release.py /usr/local/lib/python3.8/site-packages/lsb_release.py && \\\n    cd .. && rm -rf Python-3.8.* && \\\n    cd /usr/local/bin && ln -s pip3.8 pip3 && \\\n\n    cp /tmp/assets/pip.conf /etc/ && mkdir ~/.pip && cp /tmp/assets/pip.conf ~/.pip/ && \\\n    printf \"\\n. /etc/profile\\n\" >> /root/.bashrc && printf \"\\n. /etc/profile\\n\" >> /home/tiger/.bashrc && \\\n    sh /tmp/assets/build.sh && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* ~/.cache/pip && \\\n# Enable the service of dumping environment variables\n    systemctl enable configurator_dumpenv.service; \\\n# Configure systemd\n    sed -i '/#DefaultLimitNOFILE=/c\\DefaultLimitNOFILE=1048576:1048576' /etc/systemd/system.conf; \\\n    rm -fr /lib/systemd/system/multi-user.target.wants/* \\\n        /etc/systemd/system/*.wants/* \\\n        /lib/systemd/system/local-fs.target.wants/* \\\n        /lib/systemd/system/sockets.target.wants/*udev* \\\n        /lib/systemd/system/sockets.target.wants/*initctl* \\\n        /lib/systemd/system/sysinit.target.wants/systemd-tmpfiles-setup* \\\n        /lib/systemd/system/systemd-update-utmp* \\\n        /etc/systemd/system/-.mount \\\n        /lib/systemd/system/user-.slice.d/; \\\n# Remove cron.daily\n    rm -f /etc/cron.daily/*\n\n# Set locales to en_US.UTF-8\nENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8\nENV IS_DOCKER_ENV=true\n\n# Set the default mount path, the path /sys/fs/cgroup is necessary to systemd;\nVOLUME [\"/sys/fs/cgroup\",\"/run\",\"/run/lock\",\"/tmp\"]\n\n# Currently the service is managed by systemd. So systemd needs to be installed and set as the default command.\nCMD [\"/lib/systemd/systemd\"]\n\n# CUDA Essentials\nRUN apt-get update && \\\n    apt-get install -yq --no-install-recommends \\\n        gawk \\\n        binutils-dev \\\n        && \\\n    curl -fsSL http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub | apt-key add - && \\\n    echo \"deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 /\" > /etc/apt/sources.list.d/cuda.list && \\\n    curl -fsSL http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub | apt-key add - && \\\n    echo \"deb http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 /\" > /etc/apt/sources.list.d/nvidia-ml.list && \\\n    apt-get update && \\\n    apt-get install -yq --no-install-recommends --fix-missing \\\n        cuda-cudart-11-0=11.0.221-1 \\\n        cuda-compat-11-0 \\\n        cuda-libraries-11-0=11.0.3-1 \\\n        libnpp-11-0=11.1.0.245-1 \\\n        cuda-nvtx-11-0=11.0.167-1 \\\n        libcublas-11-0=11.2.0.252-1 \\\n        cuda-nvml-dev-11-0=11.0.167-1 \\\n        cuda-command-line-tools-11-0=11.0.3-1 \\\n        cuda-nvprof-11-0=11.0.221-1 \\\n        libnpp-dev-11-0=11.1.0.245-1 \\\n        cuda-libraries-dev-11-0=11.0.3-1 \\\n        cuda-minimal-build-11-0=11.0.3-1 \\\n        libcublas-dev-11-0=11.2.0.252-1 \\\n        libcusparse-11-0=11.1.1.245-1 \\\n        libcusparse-dev-11-0=11.1.1.245-1 \\\n        libcudnn8=8.0.5.39-1+cuda11.0 \\\n        libcudnn8-dev=8.0.5.39-1+cuda11.0 \\\n        libnccl2=2.8.3-1+cuda11.0 \\\n        libnccl-dev=2.8.3-1+cuda11.0 \\\n        libnvinfer7=7.2.1-1+cuda11.0 \\\n        libnvinfer-dev=7.2.1-1+cuda11.0 \\\n        libnvinfer-plugin7=7.2.1-1+cuda11.0 \\\n        libnvinfer-plugin-dev=7.2.1-1+cuda11.0 \\\n        libxml-sax-expat-perl libexpat1 libexpat1-dev \\\n        && \\\n    ln -s /usr/local/cuda-11.0 /usr/local/cuda && \\\n    find /usr/local/cuda-11.0/lib64/ -type f -name '*.a' -not -name 'libcudart_static.a' -not -name 'libcudadevrt.a' -delete && \\\n    rm /etc/alternatives/libcudnn_stlib && \\\n    rm /usr/lib/x86_64-linux-gnu/libcudnn_static.a && \\\n    rm /usr/lib/x86_64-linux-gnu/libcudnn_static_v8.a && \\\n    rm /usr/lib/x86_64-linux-gnu/libnccl_static.a && \\\n    rm /usr/lib/x86_64-linux-gnu/libnvinfer_static.a && \\\n    rm /usr/lib/x86_64-linux-gnu/libnvinfer_plugin_static.a && \\\n    rm /usr/lib/x86_64-linux-gnu/libmyelin_compiler_static.a && \\\n    rm /usr/lib/x86_64-linux-gnu/libmyelin_executor_static.a && \\\n    rm /usr/lib/x86_64-linux-gnu/libmyelin_pattern_library_static.a && \\\n    rm /usr/lib/x86_64-linux-gnu/libmyelin_pattern_runtime_static.a && \\\n    rm /etc/apt/sources.list.d/cuda.list && \\\n    rm /etc/apt/sources.list.d/nvidia-ml.list\n\nRUN python3.8 -m pip install ifstat==1.0.3 absl-py==0.12.0 kazoo==2.8.0 Flask-API==2.0 \\\n    dataclasses-json==0.5.2 numpy==1.23.4 psutil==5.8.0 msgpack==1.0.2 \\\n    pyinotify==0.9.6 Jinja2==2.11.3 requests==2.25.1 PyYAML==3.13 redis==3.5.1 \\\n    protobuf==3.12.4 grpcio==1.26.0 sqlalchemy==1.3.24 tensorflow-gpu==2.4.0\nRUN HOROVOD_NCCL_LINK=SHARED HOROVOD_WITHOUT_GLOO=1 HOROVOD_WITH_TENSORFLOW=1 HOROVOD_GPU_OPERATIONS=NCCL python3.8 -m pip install --no-cache-dir horovod==0.21.3\n\n# CUDA environment variables\nENV CUDA_HOME \"/usr/local/cuda\"\nENV PATH \"${CUDA_HOME}/bin:${PATH}\"\n# ENV LD_LIBRARY_PATH \"${CUDA_HOME}/compat:${CUDA_HOME}/lib64:${LD_LIBRARY_PATH}\"\nENV LD_LIBRARY_PATH \"${CUDA_HOME}/lib64:${LD_LIBRARY_PATH}\"\n\n# nvidia-container-runtime\nENV NVIDIA_VISIBLE_DEVICES all\nENV NVIDIA_DRIVER_CAPABILITIES compute,utility\nENV NVIDIA_REQUIRE_CUDA \"cuda>=11.0 brand=tesla,driver>=418,driver<419 brand=tesla,driver>=440,driver<441 brand=tesla,driver>=450,driver<451\"\n\n# hadoop\nRUN wget -q https://mirrors.aliyun.com/apache/hadoop/common/hadoop-3.3.2/hadoop-3.3.2.tar.gz && \\\n    tar -xzf hadoop-3.3.2.tar.gz && mv hadoop-3.3.2 /opt/tiger/hadoop && rm hadoop-3.3.2.tar.gz\n\nENV HADOOP_HOME /opt/tiger/hadoop/\nENV HDFS_JDK ${JAVA_HOME}\nENV LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:${HADOOP_HOME}/lib/native:${JAVA_HOME}/jre/lib/amd64/server\nENV CLASSPATH ${CLASSPATH}:`${HADOOP_HOME}/bin/hadoop classpath --glob`\nENV HADOOP_HDFS_HOME $HADOOP_HOME\nENV HADOOP_OPTS \"-Djava.library.path=$HADOOP_HOME/lib/native\"\nENV PATH $PATH:$HADOOP_HOME/sbin:$HADOOP_HOME/bin\n\nRUN printf \"\\nexport PATH=${JAVA_HOME}/bin:${CUDA_HOME}/bin:$HADOOP_HOME/sbin:$HADOOP_HOME/bin:$PATH\\n\" >> /root/.bashrc && \\\n    printf \"\\nexport PATH=${JAVA_HOME}/bin:${CUDA_HOME}/bin:$HADOOP_HOME/sbin:$HADOOP_HOME/bin:$PATH\\n\" >> /home/tiger/.bashrc\n\n\n# ADD monolith_serving /opt/tiger/monolith_serving\n"
  },
  {
    "path": "deploy/serving/docker/assets/bashrc",
    "content": "# ~/.bashrc: executed by bash(1) for non-login shells.\n# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)\n# for examples\n\n# If not running interactively, don't do anything\ncase $- in\n    *i*) ;;\n      *) return;;\nesac\n\n# don't put duplicate lines or lines starting with space in the history.\n# See bash(1) for more options\nHISTCONTROL=ignoreboth\n\n# append to the history file, don't overwrite it\nshopt -s histappend\n\n# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)\nHISTSIZE=1000\nHISTFILESIZE=2000\n\n# check the window size after each command and, if necessary,\n# update the values of LINES and COLUMNS.\nshopt -s checkwinsize\n\n# If set, the pattern \"**\" used in a pathname expansion context will\n# match all files and zero or more directories and subdirectories.\n#shopt -s globstar\n\n# make less more friendly for non-text input files, see lesspipe(1)\n#[ -x /usr/bin/lesspipe ] && eval \"$(SHELL=/bin/sh lesspipe)\"\n\n# set variable identifying the chroot you work in (used in the prompt below)\nif [ -z \"${debian_chroot:-}\" ] && [ -r /etc/debian_chroot ]; then\n    debian_chroot=$(cat /etc/debian_chroot)\nfi\n\n# set a fancy prompt (non-color, unless we know we \"want\" color)\ncase \"$TERM\" in\n    xterm-color) color_prompt=yes;;\nesac\n\n# uncomment for a colored prompt, if the terminal has the capability; turned\n# off by default to not distract the user: the focus in a terminal window\n# should be on the output of commands, not on the prompt\n#force_color_prompt=yes\n\nif [ -n \"$force_color_prompt\" ]; then\n    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then\n    # We have color support; assume it's compliant with Ecma-48\n    # (ISO/IEC-6429). (Lack of such support is extremely rare, and such\n    # a case would tend to support setf rather than setaf.)\n    color_prompt=yes\n    else\n    color_prompt=\n    fi\nfi\n\nunset color_prompt force_color_prompt\n\n# If this is an xterm set the title to user@host:dir\ncase \"$TERM\" in\nxterm*|rxvt*)\n    PS1=\"\\[\\e]0;${debian_chroot:+($debian_chroot)}\\u@\\h: \\W\\a\\]$PS1\"\n    ;;\n*)\n    ;;\nesac\n\n# enable color support of ls and also add handy aliases\nif [ -x /usr/bin/dircolors ]; then\n    test -r ~/.dircolors && eval \"$(dircolors -b ~/.dircolors)\" || eval \"$(dircolors -b)\"\n    alias ls='ls --color=auto'\n    #alias dir='dir --color=auto'\n    #alias vdir='vdir --color=auto'\n\n    #alias grep='grep --color=auto'\n    #alias fgrep='fgrep --color=auto'\n    #alias egrep='egrep --color=auto'\nfi\n\n# some more ls aliases\n#alias ll='ls -l'\n#alias la='ls -A'\n#alias l='ls -CF'\n\n# Alias definitions.\n# You may want to put all your additions into a separate file like\n# ~/.bash_aliases, instead of adding them here directly.\n# See /usr/share/doc/bash-doc/examples in the bash-doc package.\n\nif [ -f ~/.bash_aliases ]; then\n    . ~/.bash_aliases\nfi\n\n# enable programmable completion features (you don't need to enable\n# this, if it's already enabled in /etc/bash.bashrc and /etc/profile\n# sources /etc/bash.bashrc).\nif ! shopt -oq posix; then\n  if [ -f /usr/share/bash-completion/bash_completion ]; then\n    . /usr/share/bash-completion/bash_completion\n  elif [ -f /etc/bash_completion ]; then\n    . /etc/bash_completion\n  fi\nfi\n\nalias l=ls\nalias ll='ls -l --color=auto'\n\nalias cp='cp -i'\nalias mv='mv -i'\nalias m='more'\nalias ll='ls -l'\nalias lsl='ls -lrt'\nalias lm='ls -al|more'\nalias l='ls -lrt'\nalias c='cat'\nalias v='vi'\nalias cl='clear'\nalias pg='ps -ef| grep '\n\nexport TERM=xterm\n"
  },
  {
    "path": "deploy/serving/docker/assets/build.sh",
    "content": "#!/usr/bin/env bash\nset -ex\n\n# still make python2 as default, but python 3.8 is installed\n\nexport PYTHON_PIP_VERSION=20.1\ncurl -skSLf -o get-pip.py 'https://bootstrap.pypa.io/pip/2.7/get-pip.py'\n\npython get-pip.py \\\n    --disable-pip-version-check \\\n    --no-cache-dir \\\n    -i http://mirrors.aliyun.com/pypi/simple \\\n    --trusted-host mirrors.aliyun.com \\\n    \"pip==$PYTHON_PIP_VERSION\"\n\nfind /usr/local -depth \\\n    \\( \\\n        \\( -type d -a -name test -o -name tests \\) \\\n        -o \\\n        \\( -type f -a -name '*.pyc' -o -name '*.pyo' \\) \\\n    \\) -exec rm -rf '{}' +;\nrm -f get-pip.py\nrm -rf /usr/src/python\n\ncd /root/\n\npip uninstall -y paramiko pycrypto\npip install --no-cache-dir setuptools==44.1.0 virtualenv==15.1.0 cffi==1.12.3 paramiko==1.18.3\npip install --no-cache-dir -r /tmp/assets/requirements.txt\n\n# systemd\n[ -d /etc/systemd/system/user@1000.service.d ] || mkdir /etc/systemd/system/user@1000.service.d\necho \"[Service]\nRestart=always\" > /etc/systemd/system/user@1000.service.d/always.conf\necho \"[Service]\nLimitNOFILE=1000000\nLimitMEMLOCK=infinity\" > /etc/systemd/system/user@1000.service.d/limits.conf\n\n## for run systemd\ncd /lib/systemd/system/sysinit.target.wants/ && \\\n    ls | grep -v systemd-tmpfiles-setup | xargs rm -f $1 && \\\n    rm -f /lib/systemd/system/sockets.target.wants/*udev*\n\nsystemctl mask -- \\\n    apt-daily-upgrade.timer \\\n    apt-daily.timer \\\n    cgmanager.service \\\n    cgproxy.service \\\n    dev-mqueue.mount \\\n    getty-static.service \\\n    getty.target \\\n    swap.target \\\n    systemd-logind.service \\\n    systemd-remount-fs.service \\\n    systemd-timesyncd.service \\\n    systemd-tmpfiles-setup-dev.service \\\n    systemd-tmpfiles-setup.service \\\n    systemd-update-utmp-runlevel.service; \\\n    tmp.mount \\\n    etc-hostname.mount \\\n    etc-hosts.mount \\\n    etc-resolv.conf.mount \\\n    -.mount \\\n"
  },
  {
    "path": "deploy/serving/docker/assets/configurator_dumpenv.service",
    "content": "[Unit]\nDescription=Dump the Docker environment variables\n\n[Service]\nType=oneshot\nExecStart=/root/.system/configurator_dumpenv.sh\nTimeoutSec=0\n"
  },
  {
    "path": "deploy/serving/docker/assets/configurator_dumpenv.sh",
    "content": "#!/bin/bash - \n\nxargs -0 bash -c 'printf \"%q\\n\" \"$@\" ; systemctl set-environment \"$@\"' -- \\\n    < /proc/1/environ \\\n    > /var/docker_environment\n\nchmod 700 /var/docker_environment\n"
  },
  {
    "path": "deploy/serving/docker/assets/pip.conf",
    "content": "[global]\nindex-url=http://mirrors.aliyun.com/pypi/simple\ntrusted-host=mirrors.aliyun.com\ntimeout = 600\ndisable_pip_version_check = 1\n\n[install]\ntrusted-host=mirrors.aliyun.com\n"
  },
  {
    "path": "deploy/serving/docker/assets/requirements.txt",
    "content": "absl-py==0.7.1\nansible==2.2.1.0\nansicolors==1.0.2\nAPScheduler==3.5.1\nasn1crypto==0.24.0\nastor==0.8.0\nbackports.ssl-match-hostname==3.5.0.1\nbackports.weakref==1.0.post1\nbcrypt==3.1.6\nbpython==0.12\ncertifi==2017.7.27.1\ncffi==1.12.3\nchardet==2.3.0\ncityhash==0.2.3.post9\nclick==6.7\ncolorama==0.3.2\ncoloredlogs==10.0\ncryptography==2.6.1\nDBUtils==1.3\ndecorator==3.4.0\ndefusedxml==0.4.1\nDjango==1.11.3\ndocutils==0.12\necdsa==0.11\nenum34==1.1.6\nexecutor==21.3\nfasteners==0.14.1\nfilelock==2.0.11\nFlask==0.12.2\nfuncsigs==1.0.2\nfutures==3.2.0\ngast==0.2.2\nGeohash==1.0\ngevent==1.1.1\ngoogle-pasta==0.1.7\ngreenlet==0.4.10\ngrpcio==1.22.0\ngunicorn==19.9.0\nh5py==2.9.0\nhash-ring==1.3.1\nhtml5lib==0.999\nhttplib2==0.9\nhumanfriendly==4.18\nidna==2.0\nipaddress==1.0.22\nipython==2.3.0\nitsdangerous==0.24\njieba==0.42.1\nkafka-python==1.3.5\nlockfile==0.8\nlxml==3.4.0\nMarkupSafe==1.0\nmonotonic==1.5\nmsgpack-python==0.4.8\nmsgpack==0.6.1\nmysqlclient==1.4.6\nndg-httpsclient==0.4.2\nnumpy==1.12.1\npandas==0.24.2\nPillow==2.6.1\nproc==0.17\nproperty-manager==2.3.1\nprotobuf==3.11.3\npssh==2.3.1\npsutil==5.6.1\npyasn1==0.1.9\npycparser==2.19\nPygments==2.0.1\nPyNaCl==1.3.0\npyOpenSSL==19.0.0\nPySocks==1.7.0\npython-consul==0.4.0\npython-daemon==1.5.5\npython-dateutil==2.8.0\npython-decouple==3.0\npython-memcached==1.58\npython-utils==2.3.0\npytz==2019.1\nPyYAML==3.12\nredis==2.10.5\nrequests==2.11.1\nroman==2.0.0\nruamel.ordereddict==0.4.13\nruamel.yaml==0.15.85\nscipy==0.14.0\nsetuptools==44.0.0\nsimplejson==3.11.1\nsix==1.12.0\nSOAPpy==0.12.22\nsqlalchemy-migrate==0.12.0\nsqlalchemy==1.2.12\nsqlparse==0.3.0\ntabulate==0.7.7\nTempita==0.5.2\ntermcolor==1.1.0\nthrift==0.13.0\ntornado==4.1\ntransitions==0.7.1\nujson==1.35\nurllib3==1.16\nverboselogs==1.7\nWerkzeug==0.12.2\nwstools==0.4.3\nxcmd==0.0.3\nxmltodict==0.9.0\nzk-shell==1.1.3\nzstandard==0.13.0"
  },
  {
    "path": "deploy/serving/docker/run",
    "content": "#! /bin/bash\n\ndocker build --force-rm -t bytedance.monolith_pro.release:v1.0.0 ./\n"
  },
  {
    "path": "deploy/serving/open_source_serving.sh",
    "content": "#!/bin/bash\nset -eux\n\nexport SHARD_ID=`expr $MLP_SHARD_ID - 1`\necho \"The SHARD_ID {$SHARD_ID}\"\n\nexport MY_POD_NAME=$MLP_POD_NAME\necho \"The MY_POD_NAME {$MY_POD_NAME}\"\n\nexport byterec_host_shard_n=$MLP_SHARD_NUM\necho \"The byterec_host_shard_n {$byterec_host_shard_n}\"\n\nif [ $MLP_IDC ]; then\n    export TCE_INTERNAL_IDC=$MLP_IDC\nelse\n    export TCE_INTERNAL_IDC=\"cn-beijing-b\"\nfi\n\necho \"The TCE_INTERNAL_IDC {$TCE_INTERNAL_IDC}\"\n\n#export TCE_CLUSTER=$\nexport TCE_CLUSTER=default\necho \"The TCE_CLUSTER {$TCE_CLUSTER}\"\n\n# TCE_PSM for metrics\nPSM_PREFIX=\"data.tob.monolith_serving_\"\n\nSHELL_FOLDER=/opt/tiger/monolith_serving\nexport PATH=$SHELL_FOLDER:$PATH\n\nif [ $MLP_ROLE_NAME = 'PS' ]; then\n    export SERVER_TYPE='ps'\n    export ENABLE_BATCHING=false\n    export TCE_PSM=$PSM_PREFIX\"ps-\"$MLP_SERVICE_NAME\nelif [ $MLP_ROLE_NAME == 'Entry' ]; then\n    export SERVER_TYPE='entry'  \n    export ENABLE_BATCHING=false\n    export TCE_PSM=$PSM_PREFIX\"en-\"$MLP_SERVICE_NAME\nelif [ $MLP_ROLE_NAME == 'DenseNN' ]; then\n    export SERVER_TYPE='dense'\n    export CUDA_MPS_PIPE_DIRECTORY=/dev/shm\n    export ENABLE_BATCHING=true\n    export TCE_PSM=$PSM_PREFIX\"de-\"$MLP_SERVICE_NAME\n    nvidia-cuda-mps-control -d\nfi\necho \"THE SERVER_TYPE {$SERVER_TYPE}\"\necho \"THE ENABLE_BATCHING {$ENABLE_BATCHING}\"\necho \"The TCE_PSM {$TCE_PSM}\"\n\ncd $SHELL_FOLDER\necho \"The shell folder is {$SHELL_FOLDER}\"\nPYV=$(python -c \"import sys; print('{}.{}'.format(sys.version_info.major, sys.version_info.minor))\")\n\necho \"Using sparse_dense_serving: {$DENSE_ALONE}\"\n\ncat agent.conf | sed -e \"s/{{bzid}}/${BZID}/g\" -e \"s/{{base_name}}/${BASE_NAME}/g\" -e \"s?{{base_path}}?${BASE_PATH}?g\" -e \"s/{{num_ps}}/${NUM_PS}/g\" -e \"s/{{server_type}}/${SERVER_TYPE}/g\" \\\n\t-e \"s/{{zk_servers}}/${ZK_SERVERS}/g\" -e \"s/{{dense_alone}}/${DENSE_ALONE}/g\" -e \"s/{{enable_batching}}/${ENABLE_BATCHING}/g\" > render_agent.conf\n\n# add other conf parameter here\n#echo -e \"\\ndense_service_num 3\" >> render_agent.conf\n\ncd $SHELL_FOLDER/bin\n\nif [ $PYV = '3.8' ]; then\n  python run --bin_name=\"agent\" --conf /opt/tiger/monolith_serving/render_agent.conf\nelse\n  python3 run --bin_name=\"agent\" --conf /opt/tiger/monolith_serving/render_agent.conf\nfi\n"
  },
  {
    "path": "deploy/serving/scripts/build_serving.sh",
    "content": "#!/bin/bash\nset -eux\n\nscript_dir=`dirname $0`\nabs_script_dir=`realpath $script_dir`\n\nuse_gpu=\"${1:-false}\"\n\nrm -rf output\nmkdir -p output\n\nbazel --version\n\nif [ \"$use_gpu\" = \"true\" ]; then\n  bazel build \\\n    --output_filter=DONT_MATCH_ANYTHING \\\n    --define=framework_shared_object=false \\\n    --config=cuda \\\n    @org_tensorflow_serving//tensorflow_serving/model_servers:tensorflow_model_server\nelse\n  bazel build \\\n    --output_filter=DONT_MATCH_ANYTHING \\\n    --define=framework_shared_object=false \\\n    @org_tensorflow_serving//tensorflow_serving/model_servers:tensorflow_model_server\nfi\n\n# We can't compile archon in TensorFlow Py\n\nbazel build \\\n  --output_filter=DONT_MATCH_ANYTHING \\\n  --define=framework_shared_object=false \\\n  //monolith/agent_service:agent\n\nbazel build \\\n  --output_filter=DONT_MATCH_ANYTHING \\\n  --define=framework_shared_object=false \\\n  //monolith/agent_service:tfs_client\n\n# 1) prepare output\nmkdir -p output/bin/\nmkdir -p output/lib/\n\nfunction clear_external() {\n  runfiles_dir=\"$1\"\n  echo \"runfiles_dir: $runfiles_dir\"\n  pushd $runfiles_dir/__main__/external\n  for external_name in `ls .`;\n  do\n    if [ -d \"../../$external_name\" ]; then\n      rm -rf $external_name && ln -s ../../$external_name .\n    fi\n  done\n  popd\n}\n\ncp -frL bazel-bin/monolith/agent_service/agent.runfiles/ output/\nclear_external output/agent.runfiles\n\ncp -frL bazel-bin/monolith/agent_service/tfs_client.runfiles/ output/\nclear_external output/tfs_client.runfiles\n\ncp -frL bazel-bin/external/org_tensorflow_serving/tensorflow_serving/model_servers/tensorflow_model_server.runfiles/ output/\nrm output/tensorflow_model_server.runfiles/org_tensorflow_serving -rf\n\ncd output/bin\nln -s ../agent.runfiles/__main__/monolith/agent_service/agent .\nln -s ../tfs_client.runfiles/__main__/monolith/agent_service/tfs_client .\nln -s ../tensorflow_model_server.runfiles/__main__/external/org_tensorflow_serving/tensorflow_serving/model_servers/tensorflow_model_server .\ncd -\n\ncp -rL $abs_script_dir/run_server output/bin/\ncp -rL $abs_script_dir/conf output\n"
  },
  {
    "path": "deploy/serving/scripts/run_server",
    "content": "#!/bin/bash\n\n# need env: TCE_PSM, TCE_INTERNAL_IDC, TCE_CLUSTER, SHARD_ID\n# need arg: --conf agent.conf\npython3 agent $@\n"
  },
  {
    "path": "idl/BUILD",
    "content": "load(\"@com_google_protobuf//:protobuf.bzl\", \"cc_proto_library\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_test\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\n\nproto_library(\n    name = \"proto_parser_proto\",\n    srcs = [\n        \"matrix/proto/feature.proto\",\n        \"matrix/proto/line_id.proto\",\n        \"matrix/proto/proto_parser.proto\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_proto_library(\n    name = \"line_id_cc_proto\",\n    srcs = [\n        \"matrix/proto/line_id.proto\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n\npy_proto_library(\n    name = \"line_id_py_proto\",\n    srcs = [\n        \"matrix/proto/line_id.proto\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_proto_library(\n    name = \"proto_parser_cc_proto\",\n    srcs = [\n        \"matrix/proto/feature.proto\",\n        \"matrix/proto/proto_parser.proto\",\n    ],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":line_id_cc_proto\",\n    ],\n)\n\npy_proto_library(\n    name = \"proto_parser_py_proto\",\n    srcs = [\n        \"matrix/proto/feature.proto\",\n        \"matrix/proto/proto_parser.proto\",\n    ],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":line_id_py_proto\",\n    ],\n)\n\ncc_library(\n    name = \"compression_cc_float16\",\n    hdrs = [\"matrix/compression/float16.h\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\"//third_party/half_sourceforge_net:half\"],\n)\n\ncc_library(\n    name = \"compression\",\n    srcs = [\"matrix/compression/compression.cc\"],\n    hdrs = [\n        \"matrix/compression/compression.h\",\n        \"matrix/compression/compression_qtz8mm.h\",\n    ],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":compression_cc_float16\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_library(\n    name = \"compression_qtz8mm\",\n    srcs = [\"matrix/compression/compression_qtz8mm.cc\"],\n    hdrs = [\"matrix/compression/compression_qtz8mm.h\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":compression\",\n        \":compression_cc_float16\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\n\ncc_proto_library(\n    name = \"example_cc_proto\",\n    srcs = [\n        \"matrix/proto/example.proto\",\n    ],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":line_id_cc_proto\",\n    ],\n)\n\npy_proto_library(\n    name = \"example_py_proto\",\n    srcs = [\n        \"matrix/proto/example.proto\",\n    ],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":line_id_py_proto\",\n    ],\n)\n\n"
  },
  {
    "path": "idl/matrix/compression/compression.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <vector>\n\n#include \"glog/logging.h\"\n#include \"idl/matrix/compression/compression.h\"\n#include \"idl/matrix/compression/float16.h\"\n\nnamespace matrix {\nnamespace compression {\n\nusing matrix::compression::Float16;\n\nbool compress_float_list_f16(const char* raw_data, const size_t raw_size,\n                             char* out_buffer, size_t* out_size) {\n  if ((raw_size % sizeof(float)) != 0) {\n    LOG(ERROR) << \"compress_float_list_f16 got invalid input data\";\n    return false;\n  }\n  size_t num = raw_size / sizeof(float);\n  if (sizeof(Float16) * num > *out_size) {\n    LOG(ERROR) << \"compress_float_list_f16 out_buffer size not enough\";\n    return false;\n  }\n  const float* raw_floats = reinterpret_cast<const float*>(raw_data);\n  Float16* f16_buffer = reinterpret_cast<Float16*>(out_buffer);\n  *out_size = 0;\n  for (size_t i = 0; i < num; ++i) {\n    f16_buffer[i].set(raw_floats[i]);\n    (*out_size) += sizeof(Float16);\n  }\n  return true;\n}\n\nbool compress_float_list_f16(const char* raw_data, const size_t raw_size,\n                             std::string* out) {\n  if ((raw_size % sizeof(float)) != 0) {\n    LOG(ERROR) << \"compress_float_list_f16 got invalid input data\";\n    return false;\n  }\n  size_t num = raw_size / sizeof(float);\n  size_t out_size = num * sizeof(Float16);\n  out->resize(out_size);\n  const float* raw_floats = reinterpret_cast<const float*>(raw_data);\n  Float16* f16_buffer =\n      reinterpret_cast<Float16*>(const_cast<char*>(out->data()));\n  for (size_t i = 0; i < num; ++i) {\n    f16_buffer[i].set(raw_floats[i]);\n  }\n  return true;\n}\n\nbool decompress_float_list_f16(const char* compressed_data,\n                               size_t compressed_size, char* out_buffer,\n                               size_t* out_size) {\n  if ((compressed_size % sizeof(Float16)) != 0) {\n    LOG(ERROR) << \"decompress_float_list_f16 got invalid data\";\n    return false;\n  }\n  size_t num = compressed_size / sizeof(Float16);\n  if (sizeof(float) * num > *out_size) {\n    LOG(ERROR) << \"decompress_float_list_f16 got no enough out_buffer\";\n    return false;\n  }\n  const Float16* f16_buffer = reinterpret_cast<const Float16*>(compressed_data);\n  float* out_floats = reinterpret_cast<float*>(out_buffer);\n  *out_size = 0;\n  for (size_t i = 0; i < num; ++i) {\n    out_floats[i] = f16_buffer[i].get_m();\n    (*out_size) += sizeof(float);\n  }\n  return true;\n}\n\nbool decompress_float_list_f16(const char* compressed_data,\n                               size_t compressed_size, std::string* out) {\n  if ((compressed_size % sizeof(Float16)) != 0) {\n    LOG(ERROR) << \"decompress_float_list_f16 got invalid data\";\n    return false;\n  }\n  size_t num = compressed_size / sizeof(Float16);\n  size_t out_size = num * sizeof(float);\n  out->resize(out_size);\n  const Float16* f16_buffer = reinterpret_cast<const Float16*>(compressed_data);\n  float* out_floats = reinterpret_cast<float*>(const_cast<char*>(out->data()));\n  for (size_t i = 0; i < num; ++i) {\n    out_floats[i] = f16_buffer[i].get_m();\n  }\n  return true;\n}\n\nusing bfloat16 = uint16_t;\n\nbool compress_float_list_f16b(const char* raw_data, const size_t raw_size,\n                              std::string* out) {\n  if ((raw_size % sizeof(float)) != 0) {\n    LOG(ERROR) << \"compress_float_list_f16 got invalid input data\";\n    return false;\n  }\n  size_t num = raw_size / sizeof(float);\n  size_t out_size = num * sizeof(bfloat16);\n  out->resize(out_size);\n  const uint16_t* p = reinterpret_cast<const uint16_t*>(raw_data);\n  uint16_t* q = reinterpret_cast<uint16_t*>(const_cast<char*>(out->data()));\n  for (; num != 0; p += 2, q++, num--) {\n    *q = p[1];\n  }\n  return true;\n}\n\nbool decompress_float_list_f16b(const char* compressed_data,\n                                size_t compressed_size, std::string* out) {\n  if ((compressed_size % sizeof(bfloat16)) != 0) {\n    LOG(ERROR) << \"decompress_float_list_f16 got invalid data\";\n    return false;\n  }\n  size_t num = compressed_size / sizeof(bfloat16);\n  size_t out_size = num * sizeof(float);\n  out->resize(out_size);\n\n  const uint16_t* p = reinterpret_cast<const uint16_t*>(compressed_data);\n  uint16_t* q = reinterpret_cast<uint16_t*>(const_cast<char*>(out->data()));\n  for (; num != 0; p++, q += 2, num--) {\n    q[0] = 0;\n    q[1] = *p;\n  }\n  return true;\n}\n\n}  // end namespace compression\n}  // end namespace matrix\n"
  },
  {
    "path": "idl/matrix/compression/compression.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n\n#ifndef IDL_MATRIX_COMPRESSION_COMPRESSION_H_\n#define IDL_MATRIX_COMPRESSION_COMPRESSION_H_\n\n#include <string>\n\nnamespace matrix {\nnamespace compression {\n\nbool compress_float_list_f16(const char* raw_data, const size_t raw_size,\n                             char* out_buffer, size_t* out_size);\nbool compress_float_list_f16(const char* raw_data, const size_t raw_size,\n                             std::string* out);\nbool decompress_float_list_f16(const char* compressed_data,\n                               size_t compressed_size, char* out_buffer,\n                               size_t* out_size);\nbool decompress_float_list_f16(const char* compressed_data,\n                               size_t compressed_size, std::string* out);\nbool compress_float_list_f16b(const char* raw_data, const size_t raw_size,\n                              std::string* out);\nbool decompress_float_list_f16b(const char* compressed_data,\n                                size_t compressed_size, std::string* out);\n\n// qtz8mm, with min/max qtz8, by liuyizhou\n// 注意raw_size不是float的数量，而是buffer长度，所以是float数量*4\n// 前两个函数主要是给 wudi.yx 使用，对于一个vec的数据做操作\nbool compress_float_list_qtz8mm(const char* raw_data, const size_t raw_size,\n                                std::string* out);\nbool decompress_float_list_qtz8mm(const char* compressed_data,\n                                  size_t compressed_size, std::string* out);\n}  // end namespace compression\n}  // end namespace matrix\n\n#include \"idl/matrix/compression/compression_qtz8mm.h\"\n\n#endif  // IDL_MATRIX_COMPRESSION_COMPRESSION_H_\n"
  },
  {
    "path": "idl/matrix/compression/compression_qtz8mm.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"glog/logging.h\"\n\n#include \"idl/matrix/compression/compression.h\"\n#include \"idl/matrix/compression/compression_qtz8mm.h\"\n\nusing matrix::compression::Float16;\n\nnamespace matrix {\nnamespace compression {\n\nbool compress_float_list_qtz8mm(const char* raw_data, const size_t raw_size,\n                                std::string* out) {\n  if ((raw_size % sizeof(float)) != 0) {\n    LOG(ERROR) << \"compress_float_list_f16 got invalid input data\";\n    return false;\n  }\n  if (raw_size <= 4 * sizeof(float)) {\n    // 如果长度 <=\n    // 4时，由于min/max需要各占用2B，会导致qtz8mm相对于f16不节省内存，此时直接用f16\n    return compress_float_list_f16(raw_data, raw_size, out);\n  }\n\n  size_t num = raw_size / sizeof(float);\n  size_t out_size = 2 * sizeof(Float16) + num * sizeof(uint8_t);\n  out->resize(out_size);\n  char* qtz_buf_ptr = const_cast<char*>(out->data());\n\n  const float* raw_floats = reinterpret_cast<const float*>(raw_data);\n  Float16* w_ptr = reinterpret_cast<Float16*>(qtz_buf_ptr);\n  uint8_t* v_ptr =\n      reinterpret_cast<uint8_t*>(qtz_buf_ptr + 2 * sizeof(Float16));\n\n  set_to_qtz8mm(raw_floats, num, num, w_ptr, v_ptr);\n  return true;\n}\n\nbool decompress_float_list_qtz8mm(const char* compressed_data,\n                                  size_t compressed_size, std::string* out) {\n  if (compressed_size <= 4 * sizeof(Float16)) {\n    // 长度<=8时，原始的数据长度<=4，直接用f16反解\n    return decompress_float_list_f16(compressed_data, compressed_size, out);\n  }\n\n  size_t num = compressed_size - 2 * sizeof(Float16);\n  size_t out_size = num * sizeof(float);\n  out->resize(out_size);\n  const char* qtz_buf_ptr = static_cast<const char*>(compressed_data);\n\n  const Float16* w_ptr = reinterpret_cast<const Float16*>(qtz_buf_ptr);\n  const uint8_t* v_ptr =\n      reinterpret_cast<const uint8_t*>(qtz_buf_ptr + 2 * sizeof(Float16));\n  float* out_floats = reinterpret_cast<float*>(const_cast<char*>(out->data()));\n\n  get_from_qtz8mm(out_floats, num, w_ptr, v_ptr);\n  return true;\n}\n\n}  // end namespace compression\n}  // end namespace matrix\n"
  },
  {
    "path": "idl/matrix/compression/compression_qtz8mm.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef IDL_MATRIX_COMPRESSION_COMPRESSION_QTZ8MM_H_\n#define IDL_MATRIX_COMPRESSION_COMPRESSION_QTZ8MM_H_\n\n#include <string>\n#include <vector>\n#include \"idl/matrix/compression/float16.h\"\n\nnamespace matrix {\nnamespace compression {\n\ninline void get_from_qtz8mm(float* dest, const int& dim,\n                            const matrix::compression::Float16* w,\n                            const uint8_t* v) {\n  float min = w[0].get();\n  float max = w[1].get();\n  float step = (max - min) / 255;\n  for (int i = 0; i < dim; ++i) {\n    dest[i] = min + step * v[i];\n  }\n}\n\ninline void set_to_qtz8mm(const float* src_list,\n                          const int& data_size,  // data_size 可能<dim\n                          const int& dim, matrix::compression::Float16* w,\n                          uint8_t* v) {\n  float min = src_list[0];\n  float max = src_list[0];\n  for (int i = 0; i < data_size && i < dim; ++i) {\n    float tmp = src_list[i];\n    min = std::min(min, tmp);\n    max = std::max(max, tmp);\n  }\n  if (data_size < dim) {\n    min = std::min(min, 0.0f);\n    max = std::max(max, 0.0f);\n  }\n  float step = (max - min) / 255;\n  w[0] = min;\n  w[1] = max;\n  for (int i = 0; i < data_size && i < dim; ++i) {\n    float tmp = src_list[i];\n    v[i] = int(0.5f + (tmp - min) / step);\n  }\n  for (int i = data_size; i < dim; ++i) {\n    float tmp = 0.0f;\n    v[i] = int(0.5f + (tmp - min) / step);\n  }\n}\n\n}  // end namespace compression\n}  // end namespace matrix\n\n#endif  // IDL_MATRIX_COMPRESSION_COMPRESSION_QTZ8MM_H_\n"
  },
  {
    "path": "idl/matrix/compression/float16.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef FLOAT_16_H\n#define FLOAT_16_H\n\n#include <cmath>\n#include <cstdlib>\n#include <ctime>\n#include <iostream>\n#include <limits>\n#include \"third_party/half_sourceforge_net/half.hpp\"\n\nnamespace matrix {\nnamespace compression {\n\nclass Float16 {\n public:\n  Float16() {}\n\n  Float16(const Float16& other) : value(other.value) {}\n\n  Float16(float vf) { set(vf); }\n\n  void set(float vf) { value = vf; }\n\n  float get() const {\n    float val = value;\n    return std::isinf(val) ? ((val < 0) ? -65504 : 65504) : val;\n  }\n\n  /*\n   * get value with random rounding value\n   *\n   * explain:\n   *     we want to store 1.23456 in a 16 bit unit, but because of truncation\n   *     what we really store is 1.234\n   *\n   *     get_r() will return (1.234 + random(0, 1) * 0.001) to mitigate the\n   *     truncation error\n   */\n  float get_r() const { return get() + random_rounding_value(); }\n\n  /*\n   * get value with median rounding value\n   *\n   * explain:\n   *     we want to store 1.23456 in a 16 bit unit, but because of truncation\n   *     what we really store is 1.234\n   *\n   *     get_m() will return (1.234 + 0.0005) to mitigate the truncation error\n   */\n  float get_m() const {\n    if ((value.get_data() & 0x7FFF) == 0)\n      return 0;\n    else\n      return get() + median_rounding_value();\n  }\n\n  unsigned short get_raw_data() const { return value.get_data(); }\n\n private:\n  half_float::half value;\n\n  /*\n   * random make use of Marsaglia's xorshf generator to generator float\n   * number in [0, 1]\n   *\n   * About Marsaglia's xorshf generator, see\n   * [stackoverflow](http://stackoverflow.com/a/1640399)]\n   */\n  static float random(void) {\n    static unsigned long x = 123456789, y = 362436069, z = 521288629;\n\n    static unsigned long cnt = 0;\n\n    if (!(cnt = (cnt + 1) & 0xFFFFFFFF)) {\n      x = 123456789;\n      y = 362436069;\n      z = 521288629;\n    }\n\n    unsigned long t;\n    x ^= x << 16;\n    x ^= x >> 5;\n    x ^= x << 1;\n\n    t = x;\n    x = y;\n    y = z;\n    z = t ^ x ^ y;\n\n    return (double)(z & 0xFFFFFFFF) / (unsigned long)(0xFFFFFFFF);\n  }\n\n  float random_rounding_value() const {\n    static constexpr float v[64] = {\n        std::pow(2, -25),  std::pow(2, -24),  std::pow(2, -23),\n        std::pow(2, -22),  std::pow(2, -21),  std::pow(2, -20),\n        std::pow(2, -19),  std::pow(2, -18),  std::pow(2, -17),\n        std::pow(2, -16),  std::pow(2, -15),  std::pow(2, -14),\n        std::pow(2, -13),  std::pow(2, -12),  std::pow(2, -11),\n        std::pow(2, -10),  std::pow(2, -9),   std::pow(2, -8),\n        std::pow(2, -7),   std::pow(2, -6),   std::pow(2, -5),\n        std::pow(2, -4),   std::pow(2, -3),   std::pow(2, -2),\n        std::pow(2, -1),   std::pow(2, 0),    std::pow(2, 1),\n        std::pow(2, 2),    std::pow(2, 3),    std::pow(2, 4),\n        std::pow(2, 5),    std::pow(2, 6),    -std::pow(2, -25),\n        -std::pow(2, -24), -std::pow(2, -23), -std::pow(2, -22),\n        -std::pow(2, -21), -std::pow(2, -20), -std::pow(2, -19),\n        -std::pow(2, -18), -std::pow(2, -17), -std::pow(2, -16),\n        -std::pow(2, -15), -std::pow(2, -14), -std::pow(2, -13),\n        -std::pow(2, -12), -std::pow(2, -11), -std::pow(2, -10),\n        -std::pow(2, -9),  -std::pow(2, -8),  -std::pow(2, -7),\n        -std::pow(2, -6),  -std::pow(2, -5),  -std::pow(2, -4),\n        -std::pow(2, -3),  -std::pow(2, -2),  -std::pow(2, -1),\n        -std::pow(2, 0),   -std::pow(2, 1),   -std::pow(2, 2),\n        -std::pow(2, 3),   -std::pow(2, 4),   -std::pow(2, 5),\n        -std::pow(2, 6),\n    };\n    return v[value.get_data() >> 10] * random();\n  }\n\n  float median_rounding_value() const {\n    static constexpr float v[64] = {\n        std::pow(2, -26),  std::pow(2, -25),  std::pow(2, -24),\n        std::pow(2, -23),  std::pow(2, -22),  std::pow(2, -21),\n        std::pow(2, -20),  std::pow(2, -19),  std::pow(2, -18),\n        std::pow(2, -17),  std::pow(2, -16),  std::pow(2, -15),\n        std::pow(2, -14),  std::pow(2, -13),  std::pow(2, -12),\n        std::pow(2, -11),  std::pow(2, -10),  std::pow(2, -9),\n        std::pow(2, -8),   std::pow(2, -7),   std::pow(2, -6),\n        std::pow(2, -5),   std::pow(2, -4),   std::pow(2, -3),\n        std::pow(2, -2),   std::pow(2, -1),   std::pow(2, 0),\n        std::pow(2, 1),    std::pow(2, 2),    std::pow(2, 3),\n        std::pow(2, 4),    std::pow(2, 5),    -std::pow(2, -26),\n        -std::pow(2, -25), -std::pow(2, -24), -std::pow(2, -23),\n        -std::pow(2, -22), -std::pow(2, -21), -std::pow(2, -20),\n        -std::pow(2, -19), -std::pow(2, -18), -std::pow(2, -17),\n        -std::pow(2, -16), -std::pow(2, -15), -std::pow(2, -14),\n        -std::pow(2, -13), -std::pow(2, -12), -std::pow(2, -11),\n        -std::pow(2, -10), -std::pow(2, -9),  -std::pow(2, -8),\n        -std::pow(2, -7),  -std::pow(2, -6),  -std::pow(2, -5),\n        -std::pow(2, -4),  -std::pow(2, -3),  -std::pow(2, -2),\n        -std::pow(2, -1),  -std::pow(2, 0),   -std::pow(2, 1),\n        -std::pow(2, 2),   -std::pow(2, 3),   -std::pow(2, 4),\n        -std::pow(2, 5),\n    };\n    return v[value.get_data() >> 10];\n  }\n};\n\n}  // end namespace compression\n}  // end namespace matrix\n#endif /* FLOAT_16_H */"
  },
  {
    "path": "idl/matrix/proto/example.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto3\";\npackage monolith.io.proto;\noption cc_enable_arenas = true;\n\nimport \"idl/matrix/proto/line_id.proto\";\n\nmessage FidList {\n  repeated fixed64 value = 1;\n}\n\nmessage FidLists {\n  repeated FidList list = 1;\n}\n\nmessage FloatList {\n  repeated float value = 1;\n}\n\nmessage FloatLists {\n  repeated FloatList list = 1;\n}\n\nmessage DoubleList {\n  repeated double value = 1;\n}\n\nmessage DoubleLists {\n  repeated DoubleList list = 1;\n}\n\nmessage Int64List {\n  repeated int64 value = 1;\n}\n\nmessage Int64Lists {\n  repeated Int64List list = 1;\n}\n\nmessage BytesList {\n  repeated bytes value = 1;\n}\n\nmessage BytesLists {\n  repeated BytesList list = 1;\n}\n\n// Basic extracted features\nmessage Feature {\n  oneof type {\n    FidList fid_v1_list = 1;\n    FidList fid_v2_list = 2;\n    FloatList float_list = 3;\n    DoubleList double_list = 4;\n    Int64List int64_list = 5;\n    BytesList bytes_list = 6;\n\n    FidLists fid_v2_lists = 7;\n    FloatLists float_lists = 8;\n    DoubleLists double_lists = 9;\n    Int64Lists int64_lists = 10;\n    BytesLists bytes_lists = 11;\n\n    int64 int64_value = 12;\n    float float_value = 13;\n    double double_value = 14;\n    bytes bytes_value = 15;\n\n    FidLists fid_v1_lists = 16;\n  }\n}\n\n// Feature map for easy retrieval\nmessage FeatureMap {\n  map<string, Feature> feature_map = 1;\n}\n\n// Raw features, or intermediate results during extraction\nmessage RawFeature {\n  repeated Feature feature = 1;\n}\n\n// ---------ColumnMajor definitions----------\nenum FeatureListType {\n  INDIVIDUAL = 0;  // each example has its own value\n  SHARED = 1;      // all examples share the same value\n}\n\nmessage NamedFeatureList {\n  int32 id = 4;\n  string name = 1;\n  repeated Feature feature = 2;\n  FeatureListType type = 3;\n}\n\nmessage NamedRawFeatureList {\n  int32 id = 4;\n  string name = 1;\n  repeated RawFeature raw_feature = 2;\n  FeatureListType type = 3;\n}\n\n// column major examples\nmessage ExampleBatch {\n  repeated NamedFeatureList named_feature_list = 1;\n  repeated NamedRawFeatureList named_raw_feature_list = 2;\n  int32 batch_size = 3;\n\n  uint32 data_source_key = 100;\n}\n\n// ---------RowMajor definitions----------\nmessage NamedFeature {\n  int32 id = 3;\n  string name = 1;\n  Feature feature = 2;\n  int32 sorted_id = 6;\n}\n\nmessage NamedRawFeature {\n  int32 id = 3;\n  string name = 1;\n  RawFeature raw_feature = 2;\n}\n\n// Example for both online and offline\nmessage Example {\n  repeated NamedFeature named_feature = 1;\n  repeated NamedRawFeature named_raw_feature = 2;\n\n  idl.matrix.proto.LineId line_id = 100;\n  repeated float label = 101;\n  float instance_weight = 102;\n  uint32 data_source_key = 103;\n}\n\nmessage ExampleBatchRowMajor {\n  repeated NamedFeature shared_feature = 1;\n  repeated NamedRawFeature shared_raw_feature = 2;\n  repeated Example example = 3;\n}\n\nmessage FeatureData {\n  int64 gid = 1;\n  repeated int64 fids = 2;\n  repeated NamedFeature feature_columns = 3;\n  int64 origin_cnt = 4;\n  int64 sample_cnt = 5;\n}\n\nmessage ChannelCache {\n  int64 channel_id = 1;\n  repeated FeatureData feature_datas = 2;\n}\n\nmessage FilterValues {\n  oneof type {\n    FloatList float_list = 1;\n    Int64List int64_list = 2;\n    BytesList bytes_list = 3;\n  }\n}\n\nenum PoolingType {\n  SUM = 0;\n  MEAN = 1;\n  FIRSTN = 3;\n}\n\nenum OutType {\n  CONCAT = 0;\n  STACK = 1;\n  ADDN = 2;\n  NONE = 3;\n}\n\nmessage SliceConfig {\n  string feature_name = 1;\n  int32 start = 2;\n  int32 end = 3;\n  int32 feature_idx = 4;\n  int32 slice_idx = 5;\n  PoolingType pooling_type = 10;\n  int32 max_sequence_length = 11;\n}\n\nmessage TensorShape {\n  repeated int32 dims = 1;\n}\n\nmessage OutConfig {\n  repeated SliceConfig slice_configs = 1;\n  OutType out_type = 2;\n  repeated TensorShape shape = 3;\n}\n\nmessage FeatureConfig {\n  string table = 1;\n  PoolingType pooling_type = 2;\n  repeated int32 slice_dims = 3;\n  int32 max_sequence_length = 4;\n}\n\nmessage FeatureConfigs {\n  map<string, FeatureConfig> feature_configs = 1;\n  map<string, OutConfig> out_configs = 2;\n}\n"
  },
  {
    "path": "idl/matrix/proto/feature.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\npackage idl.matrix.proto;\noption java_outer_classname = \"FeatureProto\";\n\n// 序列特征，文档见下面链接\n// 离散序列特征\nmessage Fixed64List {\n  repeated fixed64 value = 1 [packed = true];\n}\n\n// 浮点型连续值序列特征\nmessage FloatList {\n  repeated float value = 1 [packed = true];\n}\n\n// 整型连续值序列特征\nmessage Int64List {\n  repeated int64 value = 1 [packed = true];\n}\n\n// 原始值序列特征\nmessage BytesList {\n  repeated bytes value = 1;\n}\n\nmessage Feature {\n  // feature column name\n  // 一定要有名字，否则无法使用。\n  // 名字是唯一的，以 fc_ 开头。\n  optional string name = 1;\n\n  // 以下字段只使用其中一个。离散值和连续值特征都是有序的。\n  // 如果要对 fid 赋权，需要分在两个 feature column 中，顺序对应。\n\n  // oneof {\n  // 离散 id 化特征\n  repeated fixed64 fid = 2 [packed = true];\n  // 连续值特征\n  repeated float float_value = 3 [packed = true];\n  repeated int64 int64_value = 4 [packed = true];\n  // 原始特征\n  repeated bytes bytes_value = 5;\n  // 以下为序列特征，表达一个序列对应的离散或连续特征\n  repeated Fixed64List fid_list = 6;\n  repeated FloatList float_list = 7;\n  repeated Int64List int64_list = 8;\n  repeated BytesList bytes_list = 9;\n  // } // oneof\n}\n"
  },
  {
    "path": "idl/matrix/proto/line_id.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\npackage idl.matrix.proto;\noption java_outer_classname = \"LineIdProto\";\n\nmessage MapStringFloatEntry {\n  optional string key = 1;\n  optional float value = 2;\n}\nmessage LineId {\n    optional fixed64 uid = 2;\n    optional int64 req_time = 3;\n    optional fixed64 item_id = 4;\n    repeated int32 actions = 6 [packed = true];\n    optional int64 chnid = 19;\n    repeated int32 pre_actions = 23 [packed = true];\n    optional float sample_rate = 27 [default = 1.0];\n    repeated int32 special_strategies = 39;\n    optional string device_type = 41;\n    optional string cid = 48;\n    optional string user_id = 49;\n    optional bool is_draw = 87;\n    optional int32 rank = 145;\n    optional string data_source_name = 235;\n}\n"
  },
  {
    "path": "idl/matrix/proto/proto_parser.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\npackage parser.proto;\nimport \"idl/matrix/proto/feature.proto\";\nimport \"idl/matrix/proto/line_id.proto\";\n\n\nmessage Instance\n{\n    repeated fixed64 fid = 1 [packed=true];\n    repeated float value = 2 [packed=true];\n    repeated float label = 3 [packed=true];\n    optional float instance_weight = 4;\n    optional idl.matrix.proto.LineId line_id = 5;\n    // deprecated, move to feature columns\n    repeated float dense = 6 [packed=true, deprecated=true];\n    repeated LabelTag label_tag = 7;\n    repeated fixed64 next_fid = 8 [packed=true];\n    // feature columns\n    repeated idl.matrix.proto.Feature feature = 9;\n\n    optional uint32 data_source_key = 100;\n}\n\nmessage InstanceWrapper\n{\n    // Serialized `Instance` message\n    optional bytes instance = 1;\n    // Which data source this Instance comes from.\n    optional string data_source = 2;\n}\n\nmessage LabelTag\n{\n    optional int32 key = 1;\n    optional float val = 2;\n}\n\nmessage Request\n{\n    optional string req_id = 1;\n    optional int32 ut = 2;\n    optional fixed64 uid = 3;\n    optional int64 req_time = 4;\n    repeated Instance instances = 5;\n    optional string user = 6;\n}\n"
  },
  {
    "path": "markdown/demo/AWS-EKS.md",
    "content": "# Distributed async training on EKS\n\nTo scale to multiple machines and handle failure recovery, we can utilize container orchestration frameworks such as yarn and kubernetes. Regradless what tool you use, as long as the `TF_CONFIG` environment variable is correctly set for each worker and ps, it will work just fine. \n\nIn this tutorial, we will show how to setup distributed training using kubernetes, kubeflow, and AWS's elastic kubernetes service (EKS). Kubeflow is used as the middleware that injects `TF_CONFIG` environment variable for each worker container. \n\n## Prerequisite\n\nSetup kubeflow on AWS by following the official guide. It will also help you to setup other tools such as aws cli and eksctl. Make sure to complete \n\n- Prerequisites\n- Create an EKS Cluster\n- Vanilla Installation\n\nhttps://awslabs.github.io/kubeflow-manifests/docs/deployment/\n\n\n## Prepare monolith docker\n\nTODO\n\n## Write Spec and launch training\n\nIf you have completed all the prerequisites, `kubectl` should be able to connect to your cluster on AWS. \n\nNow, create a spec file called `aws-tfjob.yaml`. \n\n```yaml\napiVersion: \"kubeflow.org/v1\"\nkind: \"TFJob\"\nmetadata:\n  name: \"monolith-train\"\n  namespace: kubeflow \nspec:\n  runPolicy:\n    cleanPodPolicy: None\n  tfReplicaSpecs:\n    Worker:\n      replicas: 4\n      restartPolicy: Never\n      template:\n        metadata:\n          annotations:\n            # solve RBAC permission problem\n            sidecar.istio.io/inject: \"false\"\n        spec:\n          containers:\n            - name: tensorflow\n              image: YOUR_IMAGE\n              args: \n                - --model_dir=/tmp/model\n    PS:\n      replicas: 4\n      restartPolicy: Never\n      template:\n        metadata:\n          annotations:\n            sidecar.istio.io/inject: \"false\"\n        spec:\n          containers:\n            - name: tensorflow\n              image: YOUR_IMAGE\n              args:\n                - --model_dir=/tmp/model\n```\n\nThen, launch training: \n\n```bash\nkubectl apply -f aws-tfjob.yaml\n```\n\nTo view the status of workers, you can use\n\n```bash\n# use this to list pods\nkubectl --namespace kubeflow get pods\n# use this get a log of a worker\nkubectl --namespace kubeflow logs monolith-train-worker-0\n```\n\nOf course, there are other middlewares built on top of kubeflow to better help you to keep track of the training progress. Monolith's compatibility with tensorflow means that tools that are built for tensorflow will likely work with Monolith too. "
  },
  {
    "path": "markdown/demo/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\npy_binary(\n    name = \"ml_dataset\",\n    srcs = [\"ml_dataset.py\"],\n    deps = [\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\n\npy_binary(\n    name = \"kafka_producer\",\n    srcs = [\"kafka_producer.py\"],\n    deps = [\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n        \":ml_dataset\",\n    ],\n)\n\npy_binary(\n    name = \"kafka_receiver\",\n    srcs = [\"kafka_receiver.py\"],\n    deps = [\n        \"//monolith/native_training:native_model\",\n    ],\n)\n\npy_binary(\n    name = \"demo_model\",\n    srcs = [\"demo_model.py\"],\n    deps = [\n        \":kafka_producer\",\n        \":kafka_receiver\",\n        \"//monolith/native_training:native_model\",\n    ],\n)\n\npy_binary(\n    name = \"demo_local_runner\",\n    srcs = [\"demo_local_runner.py\"],\n    deps = [\n        \":demo_model\",\n    ],\n)\n"
  },
  {
    "path": "markdown/demo/Batch.md",
    "content": "# Movie Ranking Batch Training\n\nThis tutorial demonstrates how to use Monolith to perform a movie ranking task. This tutorial is essentially the same as [Tensorflow's tutorial on movie ranking](https://www.tensorflow.org/recommenders/examples/basic_ranking), but with Monolith's API. Through this tutorial, you'll learn the similarity and differences between Monolith and native Tensorflow. Additionally, we'll showcase how batching training and stream training is done with Monolith.\n\n## Building the Model\n\nSource code: [kafka_producer.py](./kafka_producer.py)\n\n### Monolith Model API\n\n```python\nclass MovieRankingModel(MonolithModel):\n  def __init__(self, params):\n    super().__init__(params)\n    self.p = params\n    self.p.serving.export_when_saving = True\n\n  def input_fn(self, mode):\n    return dataset\n\n  def model_fn(self, features, mode):\n    # features = \n    return EstimatorSpec(...)\n    \n  def serving_input_receiver_fn(self):\n    return tf.estimator.export.ServingInputReceiver({...})\n```\n\nA monolith model follows the above template. `input_fn` returns an instance of tf.data.Dataset. `model_fn` builds the graph for the forward pass and returns an EstimatorSpec. The `features` argument is an item from the dataset returned by the `input_fn`. Finally, if you want to serve the model, you need to implement the `serving_input_receiver_fn`.\n\n### Prepare the dataset\n\nWe can use tfds to load dataset. Then, we select the features that we're going to use from the dataset, and do some preprocessing. In our case, we need to convert user ids and movie titles from strings to unique integer ids. \n\n```python\ndef get_preprocessed_dataset(size='100k') -> tf.data.Dataset:\n  ratings = tfds.load(f\"movielens/{size}-ratings\", split=\"train\")\n  # For simplicity, we map each movie_title and user_id to numbers\n  # by hashing. You can use other ways to number them to avoid \n  # collision and better leverage Monolith's collision-free hash tables.  \n  max_b = (1 << 63) - 1\n  return ratings.map(lambda x: {\n    'mov': tf.strings.to_hash_bucket_fast([x['movie_title']], max_b),\n    'uid': tf.strings.to_hash_bucket_fast([x['user_id']], max_b),\n    'label': tf.expand_dims(x['user_rating'], axis=0)\n  })\n```\n\n### Write input_fn for batch training\n\nTo enable distributed training, our `input_fn` first shard the dataset according to total number of workers, then batch. Note that Monolith requires sparse features to be ragged tensors, so a .map(to_ragged) is required if this isn't the case. \n\n```python\ndef to_ragged(x):\n  return {\n    'mov': tf.RaggedTensor.from_tensor(x['mov']),\n    'uid': tf.RaggedTensor.from_tensor(x['uid']),\n    'label': x['label']\n  }\n\ndef input_fn(self, mode):\n  env = json.loads(os.environ['TF_CONFIG'])\n  cluster = env['cluster']\n  worker_count = len(cluster.get('worker', [])) + len(cluster.get('chief', []))\n  dataset = get_preprocessed_dataset('25m')\n  dataset = dataset.shard(worker_count, env['task']['index'])\n  return dataset.batch(512, drop_remainder=True)\\\n    .map(to_ragged).prefetch(tf.data.AUTOTUNE)\n```\n\n### Build the model \n\n```python\ndef model_fn(self, features, mode):\n  # for sparse features, we declare an embedding table for each of them\n  for s_name in [\"mov\", \"uid\"]:\n    self.create_embedding_feature_column(s_name)\n\n  mov_embedding, user_embedding = self.lookup_embedding_slice(\n    features=['mov', 'uid'], slice_name='vec', slice_dim=32)\n  ratings = tf.keras.Sequential([\n    # Learn multiple dense layers.\n    tf.keras.layers.Dense(256, activation=\"relu\"),\n    tf.keras.layers.Dense(64, activation=\"relu\"),\n    # Make rating predictions in the final layer.\n    tf.keras.layers.Dense(1)\n  ])\n  rank = ratings(tf.concat((user_embedding, mov_embedding), axis=1))\n  label = features['label']\n  loss = tf.reduce_mean(tf.losses.mean_squared_error(rank, label))\n\n  optimizer = tf.compat.v1.train.AdagradOptimizer(0.05)\n\n  return EstimatorSpec(\n    label=label,\n    pred=rank,\n    head_name=\"rank\",\n    loss=loss, \n    optimizer=optimizer,\n    classification=False\n  )\n```\n\nIn `model_fn`, we use `self.create_embedding_feature_column(feature_name)` to declare a embedding table for each of the feature name that requires an embedding. In our case, they are `mov` and `uid`. Note that the these feature names must match what the `input_fn` provides. \n\nThen, we use `self.lookup_embedding_slice` to lookup the embeddings at once. If your features require different embedding length, then you can use multiple calls to `self.lookup_embedding_slice`. The rest is straightforward and is identical to how you do it in native tensorflow in graph mode. \n\nFinally, we return an `EstimatorSpec`. This `EstimatorSpec` is a wrapped version of `tf.estimator.EstimatorSpec` and thus has more fields. \n\n## Run distributed batch training locally\n\nThere're multiple ways to setup a distributed training. In this tutorial, we'll use the parameter server (PS) training strategy. In this strategy, model weights are partitioned across PS, and workers read data and pull weights from PS and do training. \n\nWhile we usually run distributed training on top of a job scheduler such as YARN and Kubernetes, it can be done locally too.\n\nTo launch a training, we start multiple processes, some of which are workers and some of which are PS. Tensorflow uses a `TF_CONFIG` variable to define a cluster and the role of the current process in the cluster. This environment variable also enables service discovery between worker and PS. Example of a `TF_CONFIG`:\n\n```python\nos.environ[\"TF_CONFIG\"] = json.dumps({\n    \"cluster\": {\n        \"worker\": [\"host1:port\", \"host2:port\", \"host3:port\"],\n        \"ps\": [\"host4:port\", \"host5:port\"]\n    },\n   \"task\": {\"type\": \"worker\", \"index\": 1}\n})\n```\n\nWe provide a script for this: [demo_local_runner.py](./demo_local_runner.py). To run batch training, simply do\n\n```bash\nbazel run //markdown/demo:demo_local_runner -- --training_type=batch\n```\n\n"
  },
  {
    "path": "markdown/demo/README.md",
    "content": "# Monolith demo model and tutorials\n\nThis is a 3-part tutorial for building monolith models and launch training. \n\n### [Part 1: building a model and launch distributed async batch training](./Batch.md)\n\n### [Part 2: training with streaming input data](./Stream.md)\n\n### [Part 3: launching distributed async training on the cloud](./AWS-EKS.md)"
  },
  {
    "path": "markdown/demo/Stream.md",
    "content": "# Stream training tutorial\n> This tutorial depends on the batching training tutorial. Please read it first if you haven't.\n\nMonolith supports reading input data from Kafka stream. To add stream training support to your model, simply change the `input_fn` and read data from a KafkaDataset. \n\n## Kafka producer\n\nSource code: [kafka_producer.py](./kafka_producer.py)\n\nLet's create a kafka producer for our movie-lens dataset. Kafka requires serializing everything to bytes, so we convert each data item in the dataset to String by putting them into the standard Tensorflow Example protobuf. \n\n```python\ndef serialize_one(data):\n  # serialize an training instance to string\n  return tf.train.Example(features=tf.train.Features(\n    feature={\n      'mov': tf.train.Feature(int64_list=tf.train.Int64List(value=data['mov'])),\n      'uid': tf.train.Feature(int64_list=tf.train.Int64List(value=data['uid'])),\n      'label': tf.train.Feature(float_list=tf.train.FloatList(value=data['label']))\n    }\n  )).SerializeToString() \n```\n\nThen, we create a KafkaProducer, iterate over the dataset, serializing each item and write it to the desired kafka topic. \n\n```python\nif __name__ == \"__main__\":\n  ds = get_preprocessed_dataset()\n  producer = KafkaProducer(bootstrap_servers=['127.0.0.1:9092'])\n  for count, val in tqdm(enumerate(ds), total=len(ds)):\n    # note: we omit error callback here for performance\n    producer.send(\n      \"movie-train\", key=str(count).encode('utf-8'), value=serialize_one(val), headers=[])\n  producer.flush()\n```\n\n## Kafka consumer in the input_fn\n\nSource code: [kafka_receiver.py](./kafka_receiver.py) and [demo_model.py](./demo_model.py)\n\nSince the kafka stream contains serialized `tf.train.Example`, we can use `tf.io.parse_example` to parse multiple of them at once. \n\n```python\ndef decode_example(v):\n    x = tf.io.parse_example(v, raw_feature_desc)\n    return to_ragged(x)\n```\n\nIn the `input_fn`, we use the Monolith's utility function to create a kafka dataset, and use the function above the decode. The parameter `poll_batch_size` determines the how many serialized `Example` we should batch before sending them to `decode_example`. It effectively means the training batch size. \n\n```python\ndef input_fn(self, mode):\n  dataset = create_plain_kafka_dataset(topics=[\"movie-train\"],\n      group_id=\"cgonline\",\n      servers=\"127.0.0.1:9092\",\n      stream_timeout=10000,\n      poll_batch_size=16,\n      configuration=[\n        \"session.timeout.ms=7000\",\n        \"max.poll.interval.ms=8000\"\n      ],\n  )\n  return dataset.map(lambda x: decode_example(x.message))\n```"
  },
  {
    "path": "markdown/demo/demo_local_runner.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport subprocess\nfrom typing import List\nimport time\nfrom absl import app\nfrom absl import flags\nimport os\nfrom monolith.native_training import yarn_runtime\nfrom socket import socket\nimport json\n\nflags.DEFINE_enum('training_type', 'batch', ['batch', 'stream'], \"type of training to launch\")\nFLAGS = flags.FLAGS\n\n\noccupied_ports = set()\n\ndef get_rand_port():\n  # this function returns a unique unused port\n  while True:\n    with socket() as s:\n      s.bind(('',0))\n      port = s.getsockname()[1]\n      if port not in occupied_ports:\n        occupied_ports.add(port)\n        return port\n\n\ndef launch_workers(num_ps: int, num_workers: int):\n  args = [\n    \"markdown/demo/demo_model\",\n    f\"--training_type={FLAGS.training_type}\",\n    \"--model_dir=/tmp/movie_lens_tutorial\",\n    \"--model_name=movie_lens_tutorial\"\n  ]\n  assert num_workers > 1, \"must have more than 1 workers\"\n  ip = yarn_runtime.get_local_host()\n  ps_addrs = [f'{ip}:{get_rand_port()}' for i in range(num_ps)]\n  worker_addrs = [f'{ip}:{get_rand_port()}' for i in range(num_workers)]\n  \n  env = os.environ.copy()\n  tf_config = {\n    \"cluster\": {\n      \"worker\": worker_addrs,\n      \"ps\": ps_addrs,\n    }\n  }\n  \n  processes = []\n  for i in range(num_ps):\n    tf_config['task'] = {\"type\": \"ps\", \"index\": i}\n    env['TF_CONFIG'] = json.dumps(tf_config)\n    processes.append(subprocess.Popen(args, env=env))\n\n  for i in range(num_workers):\n    tf_config['task'] = {\"type\": \"worker\", \"index\": i}\n    env['TF_CONFIG'] = json.dumps(tf_config)\n    processes.append(subprocess.Popen(args, env=env))\n    if i == 0:\n      time.sleep(2)\n  return processes\n\n\ndef main(_):\n  num_ps = 2\n  num_workers = 2\n  processes = launch_workers(\n    num_ps,\n    num_workers\n  )\n  try:\n    for p in processes:\n      p.wait()\n  finally:\n    for p in processes:\n      p.kill()\n\nif __name__ == \"__main__\":\n  app.run(main)\n"
  },
  {
    "path": "markdown/demo/demo_model.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport json\nimport os\nimport sys\n\nimport tensorflow as tf\nfrom kafka_receiver import decode_example, to_ragged\nfrom ml_dataset import get_preprocessed_dataset\n\nfrom monolith.native_training.estimator import EstimatorSpec, Estimator, RunnerConfig, ServiceDiscoveryType\nfrom monolith.native_training.native_model import MonolithModel\nfrom monolith.native_training.data.datasets import create_plain_kafka_dataset\n\nflags.DEFINE_enum('training_type', 'batch', ['batch', 'stream', 'stdin'], \"type of training to launch\")\nFLAGS = flags.FLAGS\n\ndef get_worker_count(env: dict):\n  cluster = env['cluster']\n  worker_count = len(cluster.get('worker', [])) + len(cluster.get('chief', []))\n  assert worker_count > 0\n  return worker_count\n\nclass MovieRankingModelBase(MonolithModel):\n  def __init__(self, params):\n    super().__init__(params)\n    self.p = params\n\n  def model_fn(self, features, mode):\n    # for sparse features, we declare an embedding table for each of them\n    for s_name in [\"mov\", \"uid\"]:\n      self.create_embedding_feature_column(s_name)\n\n    mov_embedding, user_embedding = self.lookup_embedding_slice(\n      features=['mov', 'uid'], slice_name='vec', slice_dim=32)\n    ratings = tf.keras.Sequential([\n      # Learn multiple dense layers.\n      tf.keras.layers.Dense(256, activation=\"relu\"),\n      tf.keras.layers.Dense(64, activation=\"relu\"),\n      # Make rating predictions in the final layer.\n      tf.keras.layers.Dense(1)\n    ])\n    concated = tf.concat((user_embedding, mov_embedding), axis=1)\n    rank = ratings(concated)\n    label = features['label']\n    loss = tf.reduce_mean(tf.losses.mean_squared_error(rank, label))\n\n    optimizer = tf.compat.v1.train.AdagradOptimizer(0.05)\n\n    return EstimatorSpec(\n      label=label,\n      pred=rank,\n      head_name=\"rank\",\n      loss=loss, \n      optimizer=optimizer,\n      classification=False\n    )\n    \n  def serving_input_receiver_fn(self):\n    # a dummy serving input receiver\n    return tf.estimator.export.ServingInputReceiver({})\n\nclass MovieRankingBatchTraining(MovieRankingModelBase):\n  def input_fn(self, mode):\n    env = json.loads(os.environ['TF_CONFIG'])\n    dataset = get_preprocessed_dataset('1m')\n    dataset = dataset.shard(get_worker_count(env), env['task']['index'])\n    return dataset.batch(512, drop_remainder=True)\\\n      .map(to_ragged).prefetch(tf.data.AUTOTUNE)\n\nclass MovieRankingStreamTraining(MovieRankingModelBase):\n  def input_fn(self, mode):\n    dataset = create_plain_kafka_dataset(topics=[\"movie-train\"],\n        group_id=\"cgonline\",\n        servers=\"127.0.0.1:9092\",\n        stream_timeout=10000,\n        poll_batch_size=16,\n        configuration=[\n          \"session.timeout.ms=7000\",\n          \"max.poll.interval.ms=8000\"\n        ],\n    )\n    return dataset.map(lambda x: decode_example(x.message))\n  \ndef read_stdin():\n  while True:\n    line = sys.stdin.readline()\n    if line:\n      tokens = line.strip().split(',')\n      yield {\n        'mov': [int(tokens[0])],\n        'uid': [int(tokens[1])],\n        'label': float(tokens[2])\n      }\n    else:\n      return\n  \nclass MovieRankingBatchTrainingStdin(MovieRankingModelBase):\n  def input_fn(self, mode):\n    return tf.data.Dataset.from_generator(\n      read_stdin,\n      output_signature={\n        'mov': tf.TensorSpec(shape=(1,), dtype=tf.int64),\n        'uid': tf.TensorSpec(shape=(1,), dtype=tf.int64),\n        'label': tf.TensorSpec(shape=(), dtype=tf.float32),\n      }\n    ).batch(512, drop_remainder=True)\\\n      .map(to_ragged).prefetch(tf.data.AUTOTUNE)\n\nFLAGS = flags.FLAGS\n\ndef main(_):\n  tf.compat.v1.disable_eager_execution()\n  raw_tf_conf = os.environ['TF_CONFIG']\n  tf_conf = json.loads(raw_tf_conf)\n  config = RunnerConfig(\n      discovery_type=ServiceDiscoveryType.PRIMUS,\n      tf_config=raw_tf_conf,\n      save_checkpoints_steps=10000,\n      enable_model_ckpt_info=True,\n      num_ps=len(tf_conf['cluster']['ps']),\n      num_workers=get_worker_count(tf_conf),\n      server_type=tf_conf['task']['type'],\n      index=tf_conf['task']['index']\n  )\n  if FLAGS.training_type == \"batch\":\n    params = MovieRankingBatchTraining.params().instantiate()\n  elif FLAGS.training_type == \"stdin\":\n    params = MovieRankingBatchTrainingStdin.params().instantiate()\n  else:\n    params = MovieRankingStreamTraining.params().instantiate()\n\n  estimator = Estimator(params, config)\n  estimator.train(max_steps=1000000)\n\nif __name__ == '__main__':\n  logging.set_verbosity(logging.INFO)\n  app.run(main)"
  },
  {
    "path": "markdown/demo/kafka_producer.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom ml_dataset import get_preprocessed_dataset, serialize_one\nfrom tqdm import tqdm\nfrom kafka import KafkaProducer\n\nif __name__ == \"__main__\":\n  ds = get_preprocessed_dataset()\n  producer = KafkaProducer(bootstrap_servers=['127.0.0.1:9092'])\n  for count, val in tqdm(enumerate(ds), total=len(ds)):\n    # note: we omit error callback here for performance\n    producer.send(\n      \"movie-train\", key=str(count).encode('utf-8'), value=serialize_one(val), headers=[])\n  producer.flush()\n"
  },
  {
    "path": "markdown/demo/kafka_receiver.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom monolith.native_training.data.datasets import create_plain_kafka_dataset\n\nraw_feature_desc = {\n  'mov': tf.io.FixedLenFeature([1], tf.int64),\n  'uid': tf.io.FixedLenFeature([1], tf.int64),\n  'label': tf.io.FixedLenFeature([], tf.float32)\n}\n\ndef to_ragged(x):\n  return {\n    'mov': tf.RaggedTensor.from_tensor(x['mov']),\n    'uid': tf.RaggedTensor.from_tensor(x['uid']),\n    'label': x['label']\n  }\n\n# corresponds to serailize_one in kafka_producer.py\ndef decode_example(v):\n    x = tf.io.parse_example(v, raw_feature_desc)\n    return to_ragged(x)\n\nif __name__ == \"__main__\":\n    dataset = create_plain_kafka_dataset(topics=[\"movie-train\"],\n        group_id=\"cgonline\",\n        servers=\"127.0.0.1:9092\",\n        stream_timeout=10000, # in milliseconds, to block indefinitely, set it to -1.\n        poll_batch_size=8,\n        configuration=[\n          \"session.timeout.ms=7000\",\n          \"max.poll.interval.ms=8000\"\n        ],\n    )\n    for x in dataset.map(lambda x: decode_example(x.message)):\n        print(x)\n"
  },
  {
    "path": "markdown/demo/kafka_utils/add_data_topics.sh",
    "content": "#!/bin/bash\nsource ./kafka_base.sh\n$KAFKA_PATH/bin/kafka-topics.sh --create --bootstrap-server 127.0.0.1:9092 --replication-factor 1 --partitions 6 --topic movie-train\n$KAFKA_PATH/bin/kafka-topics.sh --describe --bootstrap-server 127.0.0.1:9092 --topic movie-train\n"
  },
  {
    "path": "markdown/demo/kafka_utils/delete_topics.sh",
    "content": "#!/bin/bash\nsource ./kafka_base.sh\n$KAFKA_PATH/bin/kafka-topics.sh --delete --bootstrap-server 127.0.0.1:9092 --topic movie-train"
  },
  {
    "path": "markdown/demo/kafka_utils/kafka_base.sh",
    "content": "#!/bin/bash\nexport KAFKA_PATH=$HOME/kafka_2.13-2.8.1\n"
  },
  {
    "path": "markdown/demo/kafka_utils/start_broker.sh",
    "content": "#!/bin/bash\nsource ./kafka_base.sh\nbash $KAFKA_PATH/bin/zookeeper-server-start.sh -daemon $KAFKA_PATH/config/zookeeper.properties\nsleep 10\nbash $KAFKA_PATH/bin/kafka-server-start.sh -daemon $KAFKA_PATH/config/server.properties\n"
  },
  {
    "path": "markdown/demo/ml_dataset.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nimport tensorflow_datasets as tfds\nfrom tqdm import tqdm\nfrom multiprocessing import Process, cpu_count\n\ndef get_preprocessed_dataset(size='1m') -> tf.data.Dataset:\n  ratings = tfds.load(f\"movielens/{size}-ratings\", split=\"train\")\n  # For simplicity, we map each movie_title and user_id to numbers\n  # by Hashing. You can use other ways to number them to avoid \n  # collision and better leverage Monolith's collision-free hash tables.  \n  max_b = (1 << 63) - 1\n  return ratings.map(lambda x: {\n    'mov': tf.strings.to_hash_bucket_fast([x['movie_title']], max_b),\n    'uid': tf.strings.to_hash_bucket_fast([x['user_id']], max_b),\n    'label': tf.expand_dims(x['user_rating'], axis=0)\n  })\n\ndef serialize_one(data):\n  # serialize an training instance to string\n  return tf.train.Example(features=tf.train.Features(\n    feature={\n      'mov': tf.train.Feature(int64_list=tf.train.Int64List(value=data['mov'])),\n      'uid': tf.train.Feature(int64_list=tf.train.Int64List(value=data['uid'])),\n      'label': tf.train.Feature(float_list=tf.train.FloatList(value=data['label']))\n    }\n  )).SerializeToString()\n\n# serialize to human readable (csv) format\ndef serialize_hr(data):\n  return f\"{data['mov']},{data['uid']},{data['label']}\\n\"\n\ndef save_one_shard(total_shards, pid, start, end):\n  ds = get_preprocessed_dataset('1m').map(lambda x: {\n    'mov': tf.squeeze(x['mov']),\n    'uid': tf.squeeze(x['uid']),\n    'label': tf.squeeze(x['label'])\n  })\n  pbar = tqdm(position=pid, desc=\"[Serializing]\")\n  for i in range(start, end):\n    ds_shard = ds.shard(total_shards, i).as_numpy_iterator()\n    with open(f\"data_1m/part_{i}.csv\", \"w\") as f:\n      for item in ds_shard:\n        f.write(serialize_hr(item))\n        pbar.update()\n\nif __name__ == \"__main__\":\n  # just let TF download this dataset if it doesn't exist\n  ds = get_preprocessed_dataset('1m')\n  for _ in ds.take(1):\n    pass\n\n  total_shards = 4\n  num_process = min(max(cpu_count() // 4, 1), total_shards)\n  processes = []\n  shards_per_p = total_shards // num_process\n  for i in range(num_process):\n    # note: this multiprocessing is not very efficient because .shard needs to skip elements\n    p = Process(target=save_one_shard, args=(total_shards, i, shards_per_p * i, shards_per_p * (i + 1)))\n    p.start()\n    processes.append(p)\n\n  for p in processes:\n    p.join()\n"
  },
  {
    "path": "markdown/input_and_model_fn.md",
    "content": "# Monolith `input_fn` and `model_fn`\n\nThis is guide on how to setup `input_fn` and using Monolith's embedding hash table in `model_fn`\n\n## How to create an `input_fn`\n\nAn important part of `MonolithModel` is the input function. It has two requirements:\n1. It needs to return an instance of anything that inherits from `tf.data.Dataset`.\n2. When this instance is iterated over batch by batch, it yields a dict containing sparse ids and dense data. The keys should be feature names.\n3. Sparse ids must be instance of `tf.RaggedTensor` with dtype `tf.int64`, and the remaining values in the dict are treated as dense features\n\nThe reason sparse ids must be RaggedTensor is that they can vary in length bewteen different training instance. For example, consider a dataset like this\n\n```python\n{\n'user_id': 15,\n'gender': 0,\n'recently_liked_videos': [1, 2, 3]\n}\n```\n\nThe feature `recently_liked_videos` may vary in length, so when we batch these training instances, the resulting tensor is a RaggedTensor of 2 dimensions. The first dimension is the batch dimension, and the second dimension is ragged. \n\nA constant dataset returning a single **batch** of data where batch_size=2 may look like this\n\n```python\ndef input_fn(self, mode):\n    features = {\n        \"mov\": tf.ragged.constant([[155], [13]], dtype=tf.int64), # sparse feature\n        \"uid\": tf.ragged.constant([[324], [75]], dtype=tf.int64), # sparse feature\n        \"ratings\": tf.constant([5.0, 2.0], dtype=tf.float32) # dense feature\n    }\n    return tf.data.Dataset.from_tensors(features)\n```\n\n## `model_fn`\n\nThe model function's argument `features` is exactly what the dataset `input_fn` returns when iterated over. To lookup the embeddings corresponding to the sparse features, we first define the configuration for each embedding table by using `self.create_embedding_feature_column(sparse_feature_name)`, where `sparse_feature_name` is one of the sparse feature returned in the dataset. \n\n```python\ndef model_fn(self, features, mode):\n    for feature_name in [\"mov\", \"uid\"]:\n      self.create_embedding_feature_column(feature_name)\n```\n\nThen we lookup the embeddings corresponding to each sparse feature with `self.lookup_embedding_slice`. We can lookup embeddings from multiple tables at once by specifying the list of feature names. \n\n```python\n    mov_embedding, user_embedding = self.lookup_embedding_slice(\n      features=['mov', 'uid'], slice_name='vec', slice_dim=32)\n```\n\nNote that we do not use `features` directly to obtain the sparse ids here, as it is handled internally through `self.lookup_embedding_slice`. \n\nTo get dense features, simply use the `features` dictionary\n\n```python\nratings = features['ratings']\n```\n\n## TFRecordDataset\n\nIt is a common practice to prepare the dataset in `tf.train.Example` format, and then stored as a `TFRecordDataset`. In this way, the dataset can be parsed as easily as \n\n```python\ndef input_fn(self, mode):\n    raw_feature_desc = {\n        'mov': tf.io.VarLenFeature(tf.int64),\n        'uid': tf.io.VarLenFeature(tf.int64),\n        'label': tf.io.FixedLenFeature([], tf.float32)\n    }\n    def decode_example(v):\n        return tf.io.parse_example(v, raw_feature_desc)\n    return tf.data.TFRecordDataset([PATH_TO_YOUR_DATASET]).batch(BATCH_SIZE).map(decode_example)\n```\n\nWhere `tf.io.parse_example` automatically parses batches of `tf.train.Example`, converting `VarLenFeature` to ragged tensors and the remaining to regular tensors. \n\n## Final note\n\nAs long as your dataset adheres to the requirements above, it shouldn't be a issue. You can also leverage any kinds of dataset that tensorflow provides. For more informaiton, please refer to the official tensorflow documentation. "
  },
  {
    "path": "markdown/primus_demo/README.md",
    "content": "# Monolith x Primus Demo\n\n## Setup Primus\n\nFollow the primus quickstart guide to setup the primus baseline virtual machine: https://github.com/bytedance/primus/blob/master/docs/primus-quickstart.md\n\n## Setup Monolith\n\nIn your virtual machine, clone the open source monolith\n\n```bash\ncd\ngit clone https://github.com/bytedance/monolith\n```\n\n\n### Prepare monolith image\n\n```bash\ncd monolith/markdown/primus_demo\ndocker build -t monolith_ubuntu22_exec:1.0 -f monolith.Dockerfile .\n```\n\nThen, load this image to k8s cluster\n\n```bash\nkind load docker-image monolith_ubuntu22_exec:1.0\n```\n\n## Prepare the movie-lens dataset\n\nNow, we will convert the movie-lens dataset to CSV format, which is later feed to the model through Primus's input manager. This may take a while (a few hours) due to the size of the dataset, depending on the number of CPU cores you have. \n\n```bash\npip3 install tensorflow==2.4.0 tensorflow-datasets\ncd monolith/markdown/demo\nmkdir -p data_1m\npython3 ml_dataset.py\n```\n\nWhen the conversion finished, upload the data to HDFS\n\n```bash\n/usr/lib/hadoop/bin/hdfs dfs -put data_1m /primus/\n```\n\n## Launch training with Primus on k8s\n\nFirst, make sure that the `files` entry of `monolith/markdown/primus_demo/primus_monolith.json` matches the actual place where you clone monolith. Then, you can submit the training via\n\n```bash\n/usr/lib/primus-kubernetes/sbin/primus-submit --primus_conf primus_monolith.json\n```\n\n"
  },
  {
    "path": "markdown/primus_demo/main.sh",
    "content": "#!/bin/bash\n\nset -ex\n\n# setup env\nexport JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64\nexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/amd64/server/\nexport HADOOP_HDFS_HOME=/usr/lib/hadoop\nexport CLASSPATH=\"$(/usr/lib/hadoop/bin/hadoop classpath --glob)\"\n\npython3 demo/demo_model.py \\\n--model_dir=hdfs:///primus/model-checkpoints/movie_lens_tutorial \\\n--model_name=movie_lens_tutorial \\\n--training_type=stdin"
  },
  {
    "path": "markdown/primus_demo/monolith.Dockerfile",
    "content": "FROM hanzhi713/monolith:ubuntu22.04\n\n# Java will be mounted\nENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64\nENV PATH=$JAVA_HOME/bin:$PATH\n\n# Hadoop will be mounted\nENV HADOOP_HOME=/usr/lib/hadoop\nENV HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop\nENV PATH=$HADOOP_HOME/bin:$PATH\n\nENTRYPOINT [\"sleep\"]\nCMD [\"43200\"]\n"
  },
  {
    "path": "markdown/primus_demo/primus_monolith.json",
    "content": "{\n    \"name\": \"primus-monolith\",\n    \"files\": [\n      \"/home/ubuntu/monolith/markdown/demo\",\n      \"/home/ubuntu/monolith/markdown/primus_demo/main.sh\"\n    ],\n    \"role\": [\n      {\n        \"roleName\": \"worker\",\n        \"num\": 2,\n        \"vcores\": 1,\n        \"memoryMb\": 4096,\n        \"jvmMemoryMb\": 4096,\n        \"command\": \"env && bash main.sh\",\n        \"successPercent\": 100,\n        \"failover\": {\n          \"commonFailoverPolicy\": {\n            \"commonFailover\": {\n              \"restartType\": \"ON_FAILURE\",\n              \"maxFailureTimes\": 1,\n              \"maxFailurePolicy\": \"FAIL_ATTEMPT\"\n            }\n          }\n        },\n        \"inputPolicy\": \"STREAMING\"\n      },\n      {\n        \"roleName\": \"ps\",\n        \"num\": 2,\n        \"vcores\": 1,\n        \"memoryMb\": 4096,\n        \"jvmMemoryMb\": 4096,\n        \"command\": \"env && bash main.sh\",\n        \"successPercent\": 100,\n        \"failover\": {\n          \"commonFailoverPolicy\": {\n            \"commonFailover\": {\n              \"restartType\": \"ON_FAILURE\",\n              \"maxFailureTimes\": 1,\n              \"maxFailurePolicy\": \"FAIL_ATTEMPT\"\n            }\n          }\n        }\n      }\n    ],\n    \"inputManager\": {\n      \"fileConfig\": {\n        \"inputs\": [\n          {\n            \"name\": \"data\",\n            \"spec\": {\n              \"pathPattern\": \"/primus/data_1m/\",\n              \"namePattern\": \"part_*.csv\",\n              \"textInput\": {}\n            }\n          }\n        ],\n        \"stopPolicy\": {\n          \"taskSuccessPercent\": 100\n        }\n      },\n      \"workPreserve\": {\n        \"dumpIntervalSecs\": 5,\n        \"hdfsConfig\": {}\n      },\n      \"gracefulShutdown\": \"true\"\n    },  \n    \"runtimeConf\": {\n      \"kubernetesNativeConf\": {\n        \"executorPodConf\": {\n          \"mainContainerConf\": {\n            \"imageName\": \"monolith_ubuntu22_exec:1.0\"\n          }\n        }\n      }\n    }\n  }\n  "
  },
  {
    "path": "markdown/serving.md",
    "content": "# Serving\n\n## Understanding Hashtable Ckpt Format\n\n### Export Hashtable Ckpt\n\n```python\nimport tensorflow as tf\n\nfrom monolith.native_training import hash_table_ops\n\nwith tf.compat.v1.Session() as sess:\n  table1 = hash_table_ops.test_hash_table(4)\n  table1 = table1.assign(tf.convert_to_tensor([1], tf.int64), tf.convert_to_tensor([[0,1,2,3]], tf.float32))\n  table1 = table1.save(\"/tmp/save_restore\")\n  sess.run(table1.as_op())\n  \n```\n\n### Parse Hashtable Ckpt\n\n```python\nimport tensorflow as tf\n\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\ndataset = tf.data.TFRecordDataset(\"/tmp/save_restore-00000-of-00001\")\n\nfor raw_dump in dataset:\n  entry_dump = embedding_hash_table_pb2.EntryDump()\n  entry_dump.ParseFromString(raw_dump.numpy())\n  print(entry_dump)\n```\n\n```\n2022-10-19 07:09:30.406350: I external/org_tensorflow/tensorflow/compiler/jit/xla_cpu_device.cc:41] Not creating XLA devices, tf_xla_enable_xla_devices not set\n2022-10-19 07:09:30.406563: I external/org_tensorflow/tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX512F\nTo enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.\n2022-10-19 07:09:30.426969: I external/org_tensorflow/tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:116] None of the MLIR optimization passes are enabled (registered 2)\n2022-10-19 07:09:30.439333: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:112] CPU Frequency: 2300000000 Hz\nid: 1\nnum: 0.0\nnum: 1.0\nnum: 2.0\nnum: 3.0\nopt {\n  dump {\n    sgd {\n    }\n  }\n}\nlast_update_ts_sec: 0\n```\n\n## Model Serving\n\nMonolith uses tensorflow saved_model as servable format. There are two kinds of saved_model in monolith. One is entry, the other is PS.\nPS is a KV-Storage for embeddings.\nEntry accepts client calls, calls PS to fetch embeddings and runs the computation graph to get the target tensor value.\nPS is not callable from client directly, only entry should call PS.\n\n### Config saved_model export\nSaved_model exporting happens during ckpt saving stage during training, in order to enable that we need to have two changes. First set `self.p.serving.export_when_saving = True`, then implement `serving_input_receiver_fn` to parse serving request.\n```python\nclass DemoModel(MonolithModel):\n\n  def __init__(self, params):\n    super().__init__(params)\n    self.p = params\n    self.p.serving.export_when_saving = True\n    ......\n  \n  def serving_input_receiver_fn(self):\n    \n    input_placeholder = tf.compat.v1.placeholder(dtype=tf.string,\n                                                shape=(None,))\n    receiver_tensors = {'examples': input_placeholder}\n    raw_feature_desc = {\n      'mov': tf.io.FixedLenFeature([1], tf.int64),\n      'uid': tf.io.FixedLenFeature([1], tf.int64),\n      'label': tf.io.FixedLenFeature([], tf.float32)\n    }\n    examples = tf.io.parse_example(input_placeholder, raw_feature_desc)\n    parsed_features = {\n      'mov': tf.RaggedTensor.from_tensor(examples['mov']),\n      'uid': tf.RaggedTensor.from_tensor(examples['uid']),\n      'label': examples['label']\n    }\n    \n    return tf.estimator.export.ServingInputReceiver(receiver_tensors, parsed_features)\n```\n\n### Exported File Structure\n\nSuppose a training job has `hdfs:///user/xxx/model_checkpoint` as its model_dir, the saved_models for saving will reside in `hdfs:///user/xxx/model_checkpoint/exported_models`\nFor example\n```\n➜  hdfs dfs -ls hdfs:///user/xxx/model_checkpoint/exported_models        \n\ndrwxr-xr-x   - nnproxy supergroup          0 2022-07-14 07:38 hdfs:///user/xxx/model_checkpoint/exported_models/entry\ndrwxr-xr-x   - nnproxy supergroup          0 2022-07-14 07:38 hdfs:///user/xxx/model_checkpoint/exported_models/ps_0\ndrwxr-xr-x   - nnproxy supergroup          0 2022-07-14 07:38 hdfs:///user/xxx/model_checkpoint/exported_models/ps_1\ndrwxr-xr-x   - nnproxy supergroup          0 2022-07-14 07:38 hdfs:///user/xxx/model_checkpoint/exported_models/ps_2\n```\n\n### Serving Configuration\nUsing the above file structure as an example, saved_models are stored in hdfs:///user/xxx/model_checkpoint/exported_models\n\n#### Standalone serving\nFor standalone serving, we serve all saved_models of the same model in the same tf serving instance.\nWe can using the following configuration, save it as `demo.conf`\n```conf\nbzid monolith_serving_test # namespace\ndeploy_type unified # always unified\nzk_servers 10.*.91.73:2181,10.*.86.70:2181,10.*.126.131:2181,10.*.109.135:2181 \nbase_path hdfs:///user/xxx/model_checkpoint/exported_models\nlayout_filters entry; True\nlayout_filters ps_{i}; True\nagent_version 3 # always 3\n\n# tensorflow serving flags\nfetch_ps_timeout_ms 10000\nenable_batching false\ntensorflow_session_parallelism 0\ntensorflow_intra_op_parallelism 0\ntensorflow_inter_op_parallelism 0\nper_process_gpu_memory_fraction 0\nnum_load_threads 0\nnum_unload_threads 0\nmax_num_load_retries 5\nload_retry_interval_micros 60 * 1000 * 1000\nfile_system_poll_wait_seconds 60\nflush_filesystem_caches true\nsaved_model_tags none\ngrpc_channel_arguments none\ngrpc_max_threads 0\nenable_model_warmup true\nenable_signature_method_name_check false\nxla_cpu_compilation_enabled false\nenable_profiler true\n```\n\n#### Start TF Serving\nWe use the following command to start serving agent. It will start the tf serving process and register to the name service(zookeeper).\n```bash\nbazel run monolith/agent_service:agent -- --conf=`demo.conf` --tfs_log=tfs.std.log\n```\nWe can see the following log printed out, showing our saved_models are successfully loaded and registered to the name service.\n```\nI1101 05:55:59.951008 139897902933760 backends.py:222] available saved models updating, add: {test_ffm_model_2:ps_0, test_ffm_model_2:ps_1, test_ffm_model_2:ps_2, test_ffm_model_2:entry}, remove: set()\nI1101 05:55:59.973262 139897902933760 backends.py:230] available saved models updated: {test_ffm_model_2:ps_0, test_ffm_model_2:ps_1, test_ffm_model_2:ps_2, test_ffm_model_2:entry}\n```\n\n#### Distributed Serving\n\nThere are cases when our models are too large that they can not fit in one container.\n\nStill using `hdfs:///user/xxx/model_checkpoint/exported_models` as an example, now we want to have two machines to serve the models.\nFor machine 1, we want to load `entry` and `ps_1`, for machine 2 we want to load `ps_0` and `ps_2`.\n\n##### Conf for Machine 1\n```\n...\nbase_path hdfs:///user/xxx/model_checkpoint/exported_models\nlayout_filters entry; True\nlayout_filters ps_{i}; i % 2 == 1 # ps_1\n...\n```\n\n##### Conf for Machine 2\n```\n...\nbase_path hdfs:///user/xxx/model_checkpoint/exported_models\nlayout_filters ps_{i}; i % 2 == 0 # ps_0 and ps_2\n...\n```\nwe use layout_filters for a container to pick the saved_models. The pattern is `{match}:{filter}`. For example, ps_{i} will match ps_1 and assign i = 1, then i can be used in filter clause.\n\n\n\n\n\n\n\n"
  },
  {
    "path": "monolith/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\n\npy_library(\n    name = \"path_utils\",\n    srcs = [\"path_utils.py\"],\n)\n\npy_library(\n    name = \"utils\",\n    srcs = [\"utils.py\"],\n    deps = [\n        \":path_utils\",\n    ],\n)\n\npy_test(\n    name = \"utils_test\",\n    srcs = [\"utils_test.py\"],\n    deps = [\n        \":utils\",\n    ],\n)\n\npy_library(\n    name = \"init\",\n    srcs = [\"__init__.py\"],\n    deps = [\n        \"//monolith/native_training:entry\",\n        \"//monolith/native_training:estimator\",\n        \"//monolith/native_training:native_model\",\n        \"//monolith/native_training/data\",\n        \"//monolith/native_training/layers\",\n        \"//monolith/native_training/model_export\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n"
  },
  {
    "path": "monolith/__init__.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport sys\nfrom absl import logging\nimport importlib\n\nfrom tensorflow.python.tools import module_util as _module_util\n\nfrom monolith.native_training import data\nfrom monolith.native_training import layers\nfrom monolith.native_training import model_export\nfrom monolith.native_training import entry\nfrom monolith.native_training import native_model as base_model\nfrom monolith.native_training import estimator\nfrom monolith.utils import enable_monkey_patch\n\n\ndef add_module(module):\n  try:\n    if isinstance(module, str):\n      name = module.split('.')[-1]\n      module = importlib.import_module(module)\n    else:\n      name = module.__name__.split('.')[-1]\n\n    if name == 'native_model':\n      name = 'base_model'\n  except ImportError as e:\n    raise e\n  sys.modules[f'{__name__}.{name}'] = module\n\n\nadd_module(data)\nadd_module(layers)\nadd_module(model_export)\nadd_module(entry)\nadd_module(base_model)\nadd_module(estimator)\n\ntry:\n  enable_monkey_patch()\nexcept:\n  logging.error('enable_monkey_patch failed')\n"
  },
  {
    "path": "monolith/agent_service/BUILD",
    "content": "load(\"@com_github_grpc_grpc//bazel:cc_grpc_library.bzl\", \"cc_grpc_library\")\nload(\"@com_github_grpc_grpc//bazel:python_rules.bzl\", \"py_grpc_library\", \"py_proto_library\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"cc_proto_library\")\nload(\"@pip_deps//:requirements.bzl\", \"requirement\")\nload(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\n\npackage(default_visibility = [\n    \"//monolith/agent_service:__subpackages__\",\n    \"//monolith/integration_test:__subpackages__\",\n    \"//monolith/native_training:__subpackages__\",\n])\n\npy_library(\n    name = \"utils\",\n    srcs = [\"utils.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \"//idl:proto_parser_py_proto\",\n        \"@org_tensorflow//tensorflow/core:protos_all_py\",\n        \"@org_tensorflow//tensorflow/core/example:protos_all_py\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:model_service_proto_py_pb2\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_service_proto_py_pb2\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:session_service_proto_py_pb2\",\n        \"@org_tensorflow_serving//tensorflow_serving/config:platform_config_py_pb2\",\n        \"@org_tensorflow_serving//tensorflow_serving/servables/tensorflow:saved_model_bundle_source_adapter_py_pb2\",\n        \"@org_tensorflow_serving//tensorflow_serving/servables/tensorflow:session_bundle_config_py_pb2\",\n        requirement(\"dataclasses_json\"),\n        \":constants\",\n        \"//monolith/native_training:env_utils\",\n        \"//monolith/native_training:zk_utils\",\n    ],\n)\n\npy_test(\n    name = \"utils_test\",\n    srcs = [\"utils_test.py\"],\n    data = [\n        \"agent.conf\",\n        \"//monolith/agent_service/test_data\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n    ],\n)\n\npy_library(\n    name = \"backends\",\n    srcs = [\"backends.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        requirement(\"kazoo\"),\n        requirement(\"dataclasses_json\"),\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"backends_test\",\n    srcs = [\"backends_test.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":backends\",\n        \":mocked_zkclient\",\n        \":utils\",\n    ],\n)\n\npy_library(\n    name = \"constants\",\n    srcs = [\"constants.py\"],\n)\n\npy_binary(\n    name = \"agent_controller\",\n    srcs = [\n        \"agent_controller.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":backends\",\n        \":utils\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\n\npy_library(\n    name = \"replica_manager\",\n    srcs = [\"replica_manager.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":agent_service_py_pb2\",\n        \":backends\",\n        \":data_def\",\n        \":resource_utils\",\n        \":tfs_monitor\",\n        \":utils\",\n        \"//monolith/native_training/metric:cli\",\n        \"//monolith/native_training/model_export:export_state_utils\",\n        \"//monolith/native_training/runtime/parameter_sync:parameter_sync_py_proto\",\n        requirement(\"dataclasses_json\"),\n    ],\n)\n\npy_library(\n    name = \"model_manager\",\n    srcs = [\"model_manager.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \"//monolith/native_training/metric:cli\",\n    ],\n)\n\npy_test(\n    name = \"model_manager_test\",\n    srcs = [\"model_manager_test.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":model_manager\",\n    ],\n)\n\npy_test(\n    name = \"replica_manager_test\",\n    srcs = [\"replica_manager_test.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":constants\",\n        \":mocked_tfserving\",\n        \":mocked_zkclient\",\n        \":replica_manager\",\n        \":tfs_monitor\",\n        \":utils\",\n    ],\n)\n\npy_library(\n    name = \"agent_base\",\n    srcs = [\"agent_base.py\"],\n    srcs_version = \"PY3\",\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":utils\",\n    ],\n)\n\npy_library(\n    name = \"agent_v1\",\n    srcs = [\"agent_v1.py\"],\n    srcs_version = \"PY3\",\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":agent_base\",\n        \":agent_service\",\n        \":replica_manager\",\n        \":tfs_monitor\",\n    ],\n)\n\npy_library(\n    name = \"mocked_tfserving\",\n    srcs = [\n        \"mocked_tfserving.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"mocked_tfserving_test\",\n    srcs = [\n        \"mocked_tfserving_test.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":mocked_tfserving\",\n    ],\n)\n\npy_library(\n    name = \"mocked_zkclient\",\n    srcs = [\n        \"mocked_zkclient.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        requirement(\"kazoo\"),\n    ],\n)\n\npy_test(\n    name = \"mocked_zkclient_test\",\n    srcs = [\n        \"mocked_zkclient_test.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":mocked_zkclient\",\n    ],\n)\n\nproto_library(\n    name = \"agent_service_proto\",\n    srcs = [\"agent_service.proto\"],\n)\n\npy_proto_library(\n    name = \"agent_service_py_pb2\",\n    deps = [\":agent_service_proto\"],\n)\n\npy_grpc_library(\n    name = \"agent_service_py_pb2_grpc\",\n    srcs = [\":agent_service_proto\"],\n    deps = [\":agent_service_py_pb2\"],\n)\n\npy_library(\n    name = \"agent_service\",\n    srcs = [\"agent_service.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":agent_service_py_pb2\",\n        \":agent_service_py_pb2_grpc\",\n        \":data_def\",\n        \":replica_manager\",\n        \":resource_utils\",\n        \":utils\",\n        \":zk_mirror\",\n    ],\n)\n\npy_binary(\n    name = \"agent_client\",\n    srcs = [\"agent_client.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":agent_service_py_pb2\",\n        \":agent_service_py_pb2_grpc\",\n        \":utils\",\n        requirement(\"kazoo\"),\n        \":client\",\n        \":data_def\",\n        \":resource_utils\",\n    ],\n)\n\npy_test(\n    name = \"agent_service_test\",\n    srcs = [\n        \"agent_service_test.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":agent_service\",\n        \":mocked_zkclient\",\n        \":svr_client\",\n    ],\n)\n\npy_binary(\n    name = \"tfs_client\",\n    srcs = [\"tfs_client.py\"],\n    deps = [\n        \":client\",\n        \":utils\",\n        \"//idl:example_py_proto\",\n        \"//idl:line_id_py_proto\",\n        \"//idl:proto_parser_py_proto\",\n        \"//monolith/native_training/data:feature_list\",\n        \"//monolith/native_training/model_export:data_gen_utils\",\n    ],\n)\n\n\ncc_proto_library(\n    name = \"agent_service_cc_proto\",\n    srcs = [\n        \"agent_service.proto\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_grpc_library(\n    name = \"agent_service_cc_proto_grpc\",\n    srcs = [\n        \":agent_service_proto\",\n    ],\n    generate_mocks = True,\n    grpc_only = True,\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":agent_service_cc_proto\",\n    ],\n)\n\npy_library(\n    name = \"data_def\",\n    srcs = [\"data_def.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n        \"//monolith/native_training:net_utils\",\n    ],\n)\n\npy_test(\n    name = \"data_def_test\",\n    srcs = [\"data_def_test.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":data_def\",\n    ],\n)\n\npy_library(\n    name = \"tfs_monitor\",\n    srcs = [\"tfs_monitor.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":data_def\",\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"tfs_monitor_test\",\n    srcs = [\"tfs_monitor_test.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":constants\",\n        \":mocked_tfserving\",\n        \":tfs_monitor\",\n    ],\n)\n\npy_library(\n    name = \"zk_mirror\",\n    srcs = [\"zk_mirror.py\"],\n    deps = [\n        \":data_def\",\n        \":utils\",\n        requirement(\"kazoo\"),\n    ],\n)\n\npy_test(\n    name = \"zk_mirror_test\",\n    srcs = [\"zk_mirror_test.py\"],\n    deps = [\n        \":agent_service_py_pb2\",\n        \":constants\",\n        \":mocked_tfserving\",\n        \":mocked_zkclient\",\n        \":zk_mirror\",\n    ],\n)\n\npy_library(\n    name = \"resource_utils\",\n    srcs = [\"resource_utils.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":data_def\",\n        \":utils\",\n        \"//monolith/native_training/model_export:export_py_proto\",\n        \"//monolith/native_training/model_export:export_state_utils\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n        requirement(\"psutil\"),\n    ],\n)\n\npy_test(\n    name = \"resource_utils_test\",\n    srcs = [\"resource_utils_test.py\"],\n    deps = [\n        \":resource_utils\",\n    ],\n)\n\npy_library(\n    name = \"tfs_wrapper\",\n    srcs = [\"tfs_wrapper.py\"],\n    data = [\n        \"//conf:serving\",\n    ],\n    deps = [\n        \":utils\",\n        \"//monolith:utils\",\n    ],\n)\n\npy_library(\n    name = \"agent_v3\",\n    srcs = [\"agent_v3.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":agent_base\",\n        \":agent_service\",\n        \":backends\",\n        \":data_def\",\n        \":resource_utils\",\n        \":tfs_wrapper\",\n    ],\n)\n\npy_test(\n    name = \"agent_v3_test\",\n    srcs = [\"agent_v3_test.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":agent_v3\",\n        \":mocked_zkclient\",\n    ],\n)\n\npy_binary(\n    name = \"client\",\n    srcs = [\"client.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":data_def\",\n        \":utils\",\n        \":zk_mirror\",\n        requirement(\"kazoo\"),\n    ],\n)\n\nfilegroup(\n    name = \"agent_internal_data\",\n)\n\npy_binary(\n    name = \"agent\",\n    srcs = [\"agent.py\"],\n    data = [\n        \":agent_internal_data\",\n    ],\n    srcs_version = \"PY3\",\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":agent_v1\",\n        \":agent_v3\",\n        \":mocked_zkclient\",\n        \":model_manager\",\n    ],\n)\n\npy_binary(\n    name = \"run\",\n    srcs = [\"run.py\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n       \":tfs_client\",\n       \":agent_client\",\n       \":agent\",\n    ],\n)\n\npy_binary(\n    name = \"svr_client\",\n    srcs = [\"svr_client.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":agent_service_py_pb2\",\n        \":agent_service_py_pb2_grpc\",\n        \":utils\",\n    ],\n)\n\nfilegroup(\n    name = \"agent_exported\",\n)\n"
  },
  {
    "path": "monolith/agent_service/__init__.py",
    "content": ""
  },
  {
    "path": "monolith/agent_service/agent.conf",
    "content": "bzid predict_ctr\nbase_name predict_ctr\nbase_path hdfs:///test/data\nnum_ps 10\nserver_type entry\nzk_servers 127.0.0.1:12345\nmax_waiting_sec 600\nlayout_filters ps_0\nlayout_filters ps_1\n\n\nagent_version 1\nstand_alone_serving true\nupdate_model_status_interval 10\nenable_batching false\ntensorflow_session_parallelism 0\ntensorflow_intra_op_parallelism 0\ntensorflow_inter_op_parallelism 0\nper_process_gpu_memory_fraction 0\nnum_load_threads 0\nnum_unload_threads 0\nmax_num_load_retries 5\nload_retry_interval_micros 60 * 1000 * 1000\nfile_system_poll_wait_seconds 1\nfile_system_poll_wait_seconds_ps 0\nflush_filesystem_caches true\nsaved_model_tags none\ngrpc_channel_arguments none\ngrpc_max_threads 0\nenable_model_warmup true\nenable_signature_method_name_check false\nxla_cpu_compilation_enabled false\nenable_profiler true\nnum_shard 1\ndc_aware 1\n"
  },
  {
    "path": "monolith/agent_service/agent.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import app, flags, logging\nfrom concurrent.futures import ThreadPoolExecutor\nfrom enum import Enum\nfrom kazoo.client import KazooClient\nimport os\nimport copy\nimport subprocess\nimport signal\nfrom subprocess import CalledProcessError\nimport threading\nimport time\nfrom typing import List\nfrom multiprocessing import Process\n\nfrom monolith.agent_service.replica_manager import ReplicaManager\nfrom monolith.agent_service.agent_service import AgentService\nfrom monolith.agent_service.utils import AgentConfig, DeployType, check_port_open\nfrom monolith.native_training.zk_utils import MonolithKazooClient\nfrom monolith.native_training import env_utils\nfrom monolith.agent_service.agent_v1 import AgentV1\nfrom monolith.agent_service.agent_v3 import AgentV3\nfrom monolith.agent_service.model_manager import ModelManager\n\nFLAGS = flags.FLAGS\nflags.DEFINE_string('tfs_log', '/var/log/tfs.std.log',\n                    'The tfs log file path')\ndef run_agent(agent_config_path: str, tfs_log: str,\n              use_mps: bool, replica_id: int,\n              dense_service_index: int):\n  if use_mps:\n    os.environ[\"REPLICA_ID\"] = str(replica_id)\n    logging.info(f\"[INFO] the corresponding replica_id {replica_id}\")\n    os.environ[\"DENSE_SERVICE_IDX\"] = str(dense_service_index)\n    tfs_log = \"{}.mps{}\".format(tfs_log, dense_service_index)\n\n  config = AgentConfig.from_file(agent_config_path)\n  conf_path = os.path.dirname(agent_config_path)\n  if config.agent_version == 1:\n    agent = AgentV1(config, conf_path, tfs_log)\n  elif config.agent_version == 2:\n    raise Exception('agent_version v2 is not support')\n  elif config.agent_version == 3:\n    agent = AgentV3(config, conf_path, tfs_log)\n  else:\n    raise Exception(f\"agent_version error {config.agent_version}\")\n\n  # start model manager for rough sort model\n  model_manager = ModelManager(config.rough_sort_model_name,\n                               config.rough_sort_model_p2p_path,\n                               config.rough_sort_model_local_path, True)\n  ret = model_manager.start()\n  if not ret:\n    logging.error('model_manager start failed, kill self')\n    os.kill(os.getpid(), signal.SIGKILL)\n\n  agent.start()\n  agent.wait_for_termination()\n\n\ndef main(_):\n  try:\n    env_utils.setup_hdfs_env()\n  except Exception as e:\n    logging.error('setup_hdfs_env fail {}!'.format(e))\n  logging.info(f'environ is : {os.environ!r}')\n\n  if FLAGS.conf is None:\n    print(FLAGS.get_help())\n    return\n\n  config = AgentConfig.from_file(FLAGS.conf)\n\n  if config.deploy_type == DeployType.DENSE and config.dense_service_num > 1:\n    p_list = []\n    for i in range(config.dense_service_num):\n      cur_rid = config.replica_id * config.dense_service_num + i\n      p = Process(target=run_agent, args=(FLAGS.conf, FLAGS.tfs_log, True, cur_rid, i))\n      p.start()\n      p_list.append(p)\n    for p in p_list:\n      p.join()\n  else:\n    run_agent(FLAGS.conf, FLAGS.tfs_log, False, config.replica_id, 0)\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "monolith/agent_service/agent_base.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nfrom abc import ABCMeta, abstractmethod\nfrom monolith.agent_service.utils import AgentConfig, TFS_HOME, TFSServerType\n\nTFS_BINARY = f'{TFS_HOME}/bin/tensorflow_model_server'\nPROXY_BINARY = f'{TFS_HOME}/bin/server'\n\n\ndef get_cmd_path():\n  path = os.path.abspath(__file__)\n  return path\n\n\ndef get_cmd_and_port(config: AgentConfig,\n                     conf_path: str = None,\n                     server_type: str = None,\n                     config_file: str = None,\n                     tfs_binary: str = TFS_BINARY,\n                     proxy_binary: str = PROXY_BINARY):\n  if server_type == TFSServerType.PS:\n    return config.get_cmd_and_port(tfs_binary,\n                                   server_type=TFSServerType.PS,\n                                   config_file=config_file)\n  elif server_type == TFSServerType.ENTRY:\n    return config.get_cmd_and_port(tfs_binary,\n                                   server_type=TFSServerType.ENTRY,\n                                   config_file=config_file)\n  elif server_type == TFSServerType.DENSE:\n    return config.get_cmd_and_port(tfs_binary,\n                                   server_type=TFSServerType.DENSE,\n                                   config_file=config_file)\n  else:\n    proxy_conf = os.path.join(conf_path, 'proxy.conf')\n    if os.path.exists(proxy_conf):\n      cmd = f'{proxy_binary} --port={config.proxy_port} ' \\\n            f'--grpc_target=localhost:{config.tfs_entry_port} --conf_file={proxy_conf} &'\n    else:\n      cmd = f'{proxy_binary} --port={config.proxy_port} ' \\\n            f'--grpc_target=localhost:{config.tfs_entry_port} &'\n    return cmd, config.proxy_port\n\n\nclass ServingLog(object):\n\n  def __init__(self, log_prefix: str, tfs_log: str):\n    self._log_prefix = log_prefix\n    self._tfs_log = tfs_log\n    self._cwd = None\n    self._log = None\n\n  def __enter__(self):\n    dirname = os.path.dirname(self._tfs_log)\n    basename = os.path.basename(self._tfs_log)\n    log_filename = os.path.join(dirname, f\"{self._log_prefix}_{basename}\")\n    self._cwd = os.getcwd()\n    os.chdir(f'{TFS_HOME}/bin')\n    return open(log_filename, 'a')\n\n  def __exit__(self, exc_type, exc_val, exc_tb):\n    os.chdir(self._cwd)\n\n\nclass AgentBase(metaclass=ABCMeta):\n\n  def __init__(self, conf: AgentConfig):\n    self.config = conf\n\n  @abstractmethod\n  def start(self):\n    raise NotImplementedError(\"start is not implemented\")\n\n  @abstractmethod\n  def wait_for_termination(self):\n    raise NotImplementedError(\"wait_for_termination is not implemented\")\n"
  },
  {
    "path": "monolith/agent_service/agent_client.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import app, flags, logging\n\nimport grpc\nimport socket\nimport os, re\n\nfrom monolith.agent_service import utils\nfrom monolith.agent_service.agent_service_pb2_grpc import AgentServiceStub\nfrom monolith.agent_service.agent_service_pb2 import HeartBeatRequest, ServerType, \\\n  GetReplicasRequest\nfrom monolith.agent_service.data_def import ModelMeta, ReplicaMeta, ModelState\nfrom monolith.agent_service.resource_utils import cal_model_info_v2\nfrom monolith.agent_service.client import FLAGS\nfrom monolith.native_training import env_utils\nfrom monolith.native_training.zk_utils import MonolithKazooClient\nfrom kazoo.exceptions import NoNodeError\n\nflags.DEFINE_integer(\"port\", 0, \"agent_port\")\nflags.DEFINE_enum(\"args\", \"addr\",\n                  [\"addr\", \"portal\", \"pub\", \"res\", \"lock\", \"elect\", \"info\"],\n                  \"args: addr, portal, pub, res, lock, elect\")\nflags.DEFINE_enum(\"server_type\", \"ps\", [\"ps\", \"entry\", \"dense\"],\n                  \"server_type, ps or entry or dense\")\nflags.DEFINE_integer(\"task\", 0, \"task id of given server_type\")\nflags.DEFINE_string('model_dir', None, 'saved model dir')\nflags.DEFINE_string('ckpt', None, 'ckpt name')\nflags.DEFINE_integer('num_shard', -1,\n                     'number of shard will use of current model')\n\n\ndef main(_):\n  env_utils.setup_hdfs_env()\n  agent_conf = utils.AgentConfig.from_file(FLAGS.conf)\n  if FLAGS.port != 0:\n    agent_conf.agent_port = FLAGS.port\n\n  host = os.environ.get(\"MY_HOST_IP\",\n                        socket.gethostbyname(socket.gethostname()))\n  channel = grpc.insecure_channel(f\"{host}:{agent_conf.agent_port}\")\n  stub = AgentServiceStub(channel)\n  model_name = agent_conf.base_name or FLAGS.model_name\n\n  if FLAGS.server_type == \"ps\":\n    server_type = ServerType.PS\n  elif FLAGS.server_type == \"dense\":\n    server_type = ServerType.DENSE\n  else:\n    server_type = ServerType.ENTRY\n\n  if FLAGS.cmd_type == 'hb':\n    request = HeartBeatRequest(server_type=server_type)\n    addresses = stub.HeartBeat(request).addresses\n    for k, v in addresses.items():\n      addrs = f\"{v}\".strip().split(\"\\n\")\n      print(\"{k}  -> ({length}) \\n\\t{addrs}\".format(k=k,\n                                                    length=len(addrs),\n                                                    addrs=\"\\n\\t\".join(addrs)))\n  elif FLAGS.cmd_type == 'gr':\n    assert model_name is not None\n    request = GetReplicasRequest(server_type=server_type,\n                                 task=FLAGS.task,\n                                 model_name=model_name)\n    print(ServerType.Name(server_type), FLAGS.task, \" => \",\n          stub.GetReplicas(request).address_list.address)\n  elif (FLAGS.cmd_type == 'get' and\n        FLAGS.args == 'addr') or FLAGS.cmd_type == 'addr':\n    assert model_name is not None\n    zk = MonolithKazooClient(hosts=agent_conf.zk_servers)\n    zk.start()\n    # bzid/service/model_name/idc:cluster/server_type:task/replica_id\n    path_prefix = f'/{agent_conf.bzid}/service/{model_name}'\n    servers = []\n    TASK = re.compile(r'^(\\w+):(\\d+)$')\n    try:\n      ics_or_svrs = zk.get_children(path_prefix)\n      for ic_svr in ics_or_svrs:\n        matched = TASK.match(ic_svr)\n        if matched:\n          svr = ic_svr\n          servers.append(svr)\n        else:\n          ic = ic_svr\n          svrs = zk.get_children(f'{path_prefix}/{ic}')\n          if svrs:\n            servers.extend([f'{ic}/{svr}' for svr in svrs])\n    except NoNodeError as e:\n      print(f'{model_name} has not load !')\n      zk.stop()\n      return\n\n    entry_id = 0\n    for_print = []\n    if servers:\n      for server in servers:\n        replicas = zk.get_children(f\"{path_prefix}/{server}\")\n        if replicas:\n          for replica in replicas:\n            data, _ = zk.get(f\"{path_prefix}/{server}/{replica}\")\n            data = ReplicaMeta.deserialize(data)\n\n            replica_id = replica\n            for_print.append(\n                f\"{path_prefix}/{server}/{replica_id}\\tarchon_address: {data.archon_address}\\t\"\n                f\"address: {data.address}\\tstate: {ModelState.Name(data.stat)}\")\n\n    for_print.sort()\n    print(\"\\n\".join(for_print))\n    zk.stop()\n  elif FLAGS.cmd_type == 'get' and FLAGS.args == 'info':\n    print(cal_model_info_v2(FLAGS.model_dir, FLAGS.ckpt))\n  elif FLAGS.cmd_type == 'get':\n    zk = MonolithKazooClient(hosts=agent_conf.zk_servers)\n    zk.start()\n    # /{bzid}/resource/{shard_id}:{replica_id}  -> ResourceSpec\n    if FLAGS.args == 'res':\n      path_prefix = f'/{agent_conf.bzid}/resource'\n    elif FLAGS.args == 'pub':\n      path_prefix = f'/{agent_conf.bzid}/publish'\n    elif FLAGS.args == 'portal':\n      path_prefix = f'/{agent_conf.bzid}/portal'\n    elif FLAGS.args == 'lock':\n      path_prefix = f'/{agent_conf.bzid}/lock'\n    elif FLAGS.args == 'elect':\n      path_prefix = f'/{agent_conf.bzid}/election'\n    else:\n      return\n\n    try:\n      servers = zk.get_children(path_prefix)\n    except NoNodeError as e:\n      print(f'no {FLAGS.args} found !')\n      zk.stop()\n      return\n\n    resources = {}\n    if servers:\n      for server in servers:\n        data, _ = zk.get(f\"{path_prefix}/{server}\")\n        resources[server] = data\n\n    if resources:\n      keys = list(resources.keys())\n      keys.sort()\n      for key in keys:\n        print(key, resources[key])\n    else:\n      print(resources)\n    zk.stop()\n  elif FLAGS.cmd_type == 'load':\n    assert model_name is not None\n    zk = MonolithKazooClient(hosts=agent_conf.zk_servers)\n    zk.start()\n    mm = ModelMeta(model_name=model_name,\n                   model_dir=FLAGS.model_dir,\n                   ckpt=FLAGS.ckpt,\n                   num_shard=FLAGS.num_shard)\n    path = f'/{agent_conf.bzid}/portal/{model_name}'\n    try:\n      zk.create(path, value=mm.serialize(), include_data=True, makepath=True)\n    except Exception as e:\n      logging.info(e)\n      zk.set(path, value=mm.serialize())\n    zk.stop()\n  elif FLAGS.cmd_type == 'unload':\n    zk = MonolithKazooClient(hosts=agent_conf.zk_servers)\n    zk.start()\n\n    path = f'/{agent_conf.bzid}/portal/{model_name}'\n    try:\n      zk.delete(path)\n    except Exception as e:\n      logging.info(e)\n\n    zk.stop()\n  elif FLAGS.cmd_type == 'clean':\n    zk = MonolithKazooClient(hosts=agent_conf.zk_servers)\n    zk.start()\n\n    if FLAGS.args == 'portal':\n      path = f'/{agent_conf.bzid}/portal'\n      for node in zk.get_children(path):\n        zk.delete(os.path.join(path, node))\n    elif FLAGS.args == 'pub':\n      path = f'/{agent_conf.bzid}/publish'\n      for node in zk.get_children(path):\n        zk.delete(os.path.join(path, node))\n    elif FLAGS.args == 'addr':\n      path = f'/{agent_conf.bzid}/service'\n      for node in zk.get_children(path):\n        zk.delete(os.path.join(path, node), recursive=True)\n    elif FLAGS.args == 'res':\n      path = f'/{agent_conf.bzid}/resource'\n      for node in zk.get_children(path):\n        zk.delete(os.path.join(path, node), recursive=True)\n    else:\n      raise RuntimeError(f\"{FLAGS.args} is not support!\")\n\n    zk.stop()\n\n\nif __name__ == \"__main__\":\n  app.run(main)\n"
  },
  {
    "path": "monolith/agent_service/agent_controller.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nimport os\nimport fnmatch\nfrom absl import app, flags, logging\n\nimport tensorflow as tf\nfrom tensorflow.core.protobuf import saved_model_pb2\nfrom tensorflow.python.util import compat\n\nfrom monolith.native_training import env_utils\nfrom monolith.agent_service.backends import CtrlBackend, ZKBackend, SavedModel, SavedModelDeployConfig\n\nSUPPORTED_CMDS = \"decl|pub|unpub|bzid_info\"\nflags.DEFINE_string(\n    \"zk_servers\",\n    \"\",\n    \"zk connection string\")\nflags.DEFINE_string(\"bzid\", \"test\", \"namespace\")\nflags.DEFINE_string(\"export_base\", \"\", \"exported model base path\")\nflags.DEFINE_integer(\"overwrite\", 0, \"overwrite existing saved_model configs\")\nflags.DEFINE_string(\"model_name\", \"\", \"model_name\")\nflags.DEFINE_string(\"layout\", \"\", \"layout base\")\nflags.DEFINE_string(\"arch\", \"entry_ps\", \"serving architecture\")\nflags.DEFINE_string(\"cmd\", \"bzid_info\", SUPPORTED_CMDS)\n\nFLAGS = flags.FLAGS\n\n\ndef find_model_name(exported_models_path: str):\n  # find model name used in remote predict op\n  entry_path = os.path.join(exported_models_path, 'entry')\n  latest_timestamp = sorted(tf.io.gfile.listdir(entry_path))[0]\n  sm_file = os.path.join(entry_path, latest_timestamp, \"saved_model.pb\")\n  logging.info(f\"loading: {sm_file}\")\n  with tf.io.gfile.GFile(sm_file, 'rb') as f:\n    sm = saved_model_pb2.SavedModel()\n    sm.ParseFromString(compat.as_bytes(f.read()))\n    remote_predict_model_names = [\n        node.attr['model_name'].s.decode('utf-8')\n        for node in sm.meta_graphs[0].graph_def.node\n        if node.op == 'TfServingRemotePredict'\n    ]\n    if not remote_predict_model_names:\n      return None\n    else:\n      return remote_predict_model_names[0].split(\":\")[0]\n\n\ndef declare_saved_model(bd: CtrlBackend,\n                        export_base: str,\n                        model_name: str = None,\n                        overwrite=False,\n                        arch=\"entry_ps\"):\n  assert arch == \"entry_ps\", \"only entry + ps architecture supported\"\n  model_name_from_export = find_model_name(export_base)\n  if not model_name:\n    model_name = model_name_from_export\n  if model_name != model_name_from_export:\n    logging.error(\n        f\"user model_name: {model_name}, exported_model_name: {model_name_from_export}\"\n    )\n  assert model_name is not None, \"Model name is None\"\n  assert not bd.list_saved_models(\n      model_name) or overwrite, f\"{model_name} exists and not in overwrite mode\"\n\n  sub_graphs = tf.io.gfile.listdir(export_base)\n  for sub_graph in sub_graphs:\n    deploy_config = SavedModelDeployConfig(\n        model_base_path=os.path.join(export_base, sub_graph),\n        version_policy='latest' if sub_graph == 'entry' else 'latest_once')\n    bd.decl_saved_model(SavedModel(model_name, sub_graph), deploy_config)\n  logging.info(\n      f\"declare saved_model for {model_name} on path {export_base} success\")\n  return model_name\n\n\ndef map_model_to_layout(bd: CtrlBackend, model_pattern: str, layout_path: str,\n                        action: str):\n  model_name, sub_graph_pattern = model_pattern.split(\":\", 1)\n  sub_graphs = [\n      saved_model.sub_graph for saved_model in bd.list_saved_models(model_name)\n  ]\n  matched_sub_graphs = fnmatch.filter(sub_graphs, sub_graph_pattern)\n  for sub_graph in matched_sub_graphs:\n    saved_model = SavedModel(model_name, sub_graph)\n    if action == 'pub':\n      logging.info(f\"publishing {saved_model} to {layout_path}\")\n      bd.add_to_layout(layout_path, saved_model)\n    elif action == 'unpub':\n      logging.info(f\"deleting {saved_model} from {layout_path}\")\n      bd.remove_from_layout(layout_path, saved_model)\n\n\ndef bzid_info(bd: CtrlBackend):\n  print(json.dumps(bd.bzid_info(), indent=2))\n\n\ndef main(_):\n  if FLAGS.cmd not in SUPPORTED_CMDS.split(\"|\"):\n    raise ValueError(\n        f\"unsupported cmd {FLAGS.cmd}, options are {SUPPORTED_CMDS}\")\n  print()\n  bd = ZKBackend(FLAGS.bzid, FLAGS.zk_servers)\n  try:\n    bd.start()\n    if FLAGS.cmd == 'decl':\n      assert FLAGS.export_base is not None and len(FLAGS.export_base) > 0\n      declare_saved_model(bd,\n                          FLAGS.export_base,\n                          overwrite=FLAGS.overwrite,\n                          arch=FLAGS.arch)\n    elif FLAGS.cmd == 'pub' or FLAGS.cmd == 'unpub':\n      assert len(FLAGS.layout) > 0 and len(FLAGS.model_name) > 0\n      layout_path = f\"/{FLAGS.bzid}/layouts/{FLAGS.layout}\"\n      map_model_to_layout(bd, FLAGS.model_name, layout_path, action=FLAGS.cmd)\n    elif FLAGS.cmd == 'bzid_info':\n      bzid_info(bd)\n    else:\n      raise ValueError(\n          f\"unsupported cmd {FLAGS.cmd}, options are {SUPPORTED_CMDS}\")\n  finally:\n    bd.stop()\n\n\nif __name__ == \"__main__\":\n  try:\n    env_utils.setup_hdfs_env()\n  except Exception as e:\n    logging.error('setup_hdfs_env fail {}!'.format(e))\n  logging.set_verbosity(logging.INFO)\n  app.run(main)\n"
  },
  {
    "path": "monolith/agent_service/agent_controller_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nimport os\nimport unittest\n\nfrom monolith.agent_service import agent_controller\nfrom monolith.agent_service import backends\nfrom monolith.agent_service.mocked_zkclient import FakeKazooClient\n\n\ndef saved_model(sub_graph):\n  return backends.SavedModel('test_ffm_model', sub_graph)\n\n\nclass AgentControllerTest(unittest.TestCase):\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    cls.bzid = 'gip'\n    cls.bd = backends.ZKBackend(cls.bzid, zk_servers='127.0.0.1:9999')\n    cls.zk = FakeKazooClient()\n    cls.bd._zk = cls.zk\n    cls.bd.start()\n\n  @classmethod\n  def tearDownClass(cls) -> None:\n    cls.bd.stop()\n    print('tearDownClass finished!')\n\n  def test_decl_saved_models(self):\n    agent_controller.declare_saved_model(\n        self.bd,\n        os.path.join(\n            os.environ['TEST_SRCDIR'], os.environ[\"TEST_WORKSPACE\"],\n            \"monolith/native_training/model_export/testdata/saved_model\"),\n        'test_ffm_model',\n        overwrite=True)\n    saved_models = self.bd.list_saved_models('test_ffm_model')\n    self.assertEqual(\n        set(saved_models), {\n            saved_model(sub_graph)\n            for sub_graph in ['ps_0', 'ps_1', 'ps_2', 'ps_3', 'ps_4', 'entry']\n        })\n\n  def test_pub(self):\n    self.maxDiff = None\n    agent_controller.declare_saved_model(\n        self.bd,\n        os.path.join(\n            os.environ['TEST_SRCDIR'], os.environ[\"TEST_WORKSPACE\"],\n            \"monolith/native_training/model_export/testdata/saved_model\"),\n        'test_ffm_model',\n        overwrite=True)\n    agent_controller.map_model_to_layout(self.bd,\n                                         \"test_ffm_model:entry\",\n                                         \"/gip/layouts/test_layout1\",\n                                         action=\"pub\")\n    self.assertEqual(self.bd.bzid_info()['layout_info']['test_layout1'],\n                     ['test_ffm_model:entry'])\n    agent_controller.map_model_to_layout(self.bd,\n                                         \"test_ffm_model:ps_*\",\n                                         \"/gip/layouts/test_layout1\",\n                                         action=\"pub\")\n    self.assertEqual(self.bd.bzid_info()['layout_info']['test_layout1'], [\n        'test_ffm_model:entry', 'test_ffm_model:ps_0', 'test_ffm_model:ps_1',\n        'test_ffm_model:ps_2', 'test_ffm_model:ps_3', 'test_ffm_model:ps_4'\n    ])\n    agent_controller.map_model_to_layout(self.bd,\n                                         \"test_ffm_model:ps_*\",\n                                         \"/gip/layouts/test_layout1\",\n                                         action=\"unpub\")\n    self.assertEqual(self.bd.bzid_info()['layout_info']['test_layout1'],\n                     ['test_ffm_model:entry'])\n    agent_controller.map_model_to_layout(self.bd,\n                                         \"test_ffm_model:entry\",\n                                         \"/gip/layouts/test_layout1\",\n                                         action=\"unpub\")\n    self.assertEqual(self.bd.bzid_info()['layout_info']['test_layout1'], [])\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/agent_service/agent_service.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto3\";\n\noption cc_enable_arenas = true;\n\npackage monolith.serving.agent_service;\n\nenum ServerType {\n  PS = 0;\n  ENTRY = 1;\n  DENSE = 2;\n};\n\nmessage AddressList {\n  repeated string address = 1;\n}\n\nmessage GetReplicasRequest {\n    ServerType server_type = 1;\n    int32 task = 2;\n    string model_name = 3;\n}\n\nmessage GetReplicasResponse {\n  AddressList address_list = 1;\n}\n\nmessage GetResourceRequest {\n}\n\nmessage GetResourceResponse {\n  string address = 1;\n  int32 shard_id = 2;\n  int32 replica_id = 3;\n  int64 memory = 4;\n  float cpu = 5;\n  float network = 6;\n  float work_load = 7;\n}\n\n\nmessage HeartBeatRequest {\n  ServerType server_type = 1;\n}\n\n\nmessage HeartBeatResponse {\n  map<string, AddressList> addresses = 1;\n}\n\n\nservice AgentService {\n  rpc GetReplicas(GetReplicasRequest) returns (GetReplicasResponse) {}\n  rpc GetResource(GetResourceRequest) returns (GetResourceResponse) {}\n  rpc HeartBeat(HeartBeatRequest) returns (HeartBeatResponse) {}\n}\n"
  },
  {
    "path": "monolith/agent_service/agent_service.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom concurrent import futures\nimport logging\nimport grpc\nfrom typing import List, Dict, Callable\nfrom dataclasses import dataclass\nfrom functools import singledispatchmethod\n\nfrom monolith.agent_service.utils import AgentConfig, get_local_ip\nfrom monolith.agent_service.resource_utils import cal_available_memory_v2\nfrom monolith.agent_service.agent_service_pb2 import AddressList, GetReplicasRequest, GetReplicasResponse, \\\n  GetResourceRequest, GetResourceResponse, HeartBeatRequest, HeartBeatResponse\nfrom monolith.agent_service.agent_service_pb2_grpc import AgentServiceServicer\nfrom monolith.agent_service.agent_service_pb2_grpc import add_AgentServiceServicer_to_server\nfrom monolith.agent_service.replica_manager import ReplicaWatcher\nfrom monolith.agent_service.zk_mirror import ZKMirror\nfrom monolith.agent_service.data_def import ReplicaMeta\n\n\nclass AgentDataProvider:\n\n  def __init__(self, addrs_fn: Callable[[], Dict[str, List[str]]]):\n    self._addrs_fn = addrs_fn\n\n\nclass AgentServiceImpl(AgentServiceServicer):\n\n  @singledispatchmethod\n  def __init__(self, arg):\n    raise NotImplementedError('__init__ is not implemented!')\n\n  @__init__.register\n  def _(self, watcher: ReplicaWatcher, conf: AgentConfig = None):\n    self._watcher: ReplicaWatcher = watcher\n    self.conf = conf\n\n  @__init__.register\n  def _(self, zk: ZKMirror, conf: AgentConfig):\n    self._zk: ZKMirror = zk\n    self.conf = conf\n\n  @__init__.register\n  def _(self, data_provider: AgentDataProvider, conf: AgentConfig):\n    self._data_provider = data_provider\n    self.conf = conf\n\n  def GetReplicas(self, request: GetReplicasRequest,\n                  context) -> GetReplicasResponse:\n    response = GetReplicasResponse()\n    if self.conf is None or self.conf.agent_version == 1:\n      idc, cluster = self._watcher._conf.idc, self._watcher._conf.cluster\n      address = self._watcher.get_replicas(request.server_type, request.task,\n                                           idc, cluster)\n      response.address_list.address.extend(address)\n    elif self.conf.agent_version == 2:\n      rms: List[ReplicaMeta] = self._zk.get_task_replicas(\n          request.model_name, request.server_type, request.task)\n      response.address_list.address.extend([rm.address for rm in rms])\n    else:\n      raise NotImplementedError(\"not implement for agent v3\")\n    return response\n\n  def HeartBeat(self, request: HeartBeatRequest, context) -> HeartBeatResponse:\n    response = HeartBeatResponse()\n    addresses = response.addresses\n    if self.conf is None or self.conf.agent_version == 1:\n      dc_aware = self._watcher._conf.dc_aware\n      idc, cluster = self._watcher._conf.idc, self._watcher._conf.cluster\n      addrs = self._watcher.get_all_replicas(request.server_type, idc, cluster)\n      for key, values in addrs.items():\n        key = key.split('/')[-1] if dc_aware else key\n        addr_list = AddressList()\n        addr_list.address.extend(values)\n        addresses[key].CopyFrom(addr_list)\n    elif self.conf.agent_version == 2:\n      rm_dict: Dict[str, List[ReplicaMeta]] = self._zk.get_all_replicas(\n          request.server_type)\n      for key, rms in rm_dict.items():\n        addr_list = AddressList()\n        addr_list.address.extend([rm.address for rm in rms])\n        addresses[key].CopyFrom(addr_list)\n    else:\n      addrs_map = self._data_provider._addrs_fn()\n      if addrs_map:\n        for saved_model_name, addrs in addrs_map.items():\n          addr_list = AddressList()\n          addr_list.address.extend(addrs)\n          addresses[saved_model_name].CopyFrom(addr_list)\n    logging.info(f\"heartbeat response({request.server_type}): {response}\")\n    return response\n\n  def GetResource(self, request: GetResourceRequest,\n                  context) -> GetResourceResponse:\n    if self.conf is None or self.conf.agent_version == 1:\n      return GetResourceResponse()\n    else:\n      return GetResourceResponse(\n          address=f'{get_local_ip()}:{self.conf.agent_port}',\n          shard_id=int(self.conf.shard_id),\n          replica_id=int(self.conf.replica_id),\n          memory=cal_available_memory_v2(),\n          cpu=-1.0,\n          network=-1.0,\n          work_load=-1.0)\n\n\nclass AgentService:\n\n  @singledispatchmethod\n  def __init__(self, arg):\n    raise NotImplementedError('__init__ is not implemented!')\n\n  @__init__.register\n  def _(self, watcher: ReplicaWatcher, port: int = None, max_workers: int = 10):\n    self._server = grpc.server(\n        futures.ThreadPoolExecutor(max_workers=max_workers))\n    add_AgentServiceServicer_to_server(AgentServiceImpl(watcher), self._server)\n    self._server.add_insecure_port(f'[::]:{port or 0}')\n\n  @__init__.register\n  def _(self, zk: ZKMirror, conf: AgentConfig, max_workers: int = 10):\n    self._server = grpc.server(\n        futures.ThreadPoolExecutor(max_workers=max_workers))\n    add_AgentServiceServicer_to_server(AgentServiceImpl(zk, conf), self._server)\n    self._server.add_insecure_port(f'[::]:{conf.agent_port or 0}')\n\n  @__init__.register\n  def _(self, data_provider: AgentDataProvider, conf: AgentConfig, max_workers: int = 10):\n    self._server = grpc.server(\n        futures.ThreadPoolExecutor(max_workers=max_workers))\n    add_AgentServiceServicer_to_server(AgentServiceImpl(data_provider, conf),\n                                       self._server)\n    self._server.add_insecure_port(f'[::]:{conf.agent_port or 0}')\n\n  def start(self):\n    self._server.start()\n\n  def wait_for_termination(self):\n    self._server.wait_for_termination()\n\n  def stop(self, grace=None):\n    self._server.stop(grace=grace)\n"
  },
  {
    "path": "monolith/agent_service/agent_service_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport grpc\nfrom kazoo.exceptions import NoNodeError, NodeExistsError\nimport os\nimport socket\nimport unittest\n\nfrom monolith.agent_service import utils\nfrom monolith.agent_service.agent_service import AgentService\nfrom monolith.agent_service.agent_service_pb2 import HeartBeatRequest, ServerType, \\\n  GetReplicasRequest\nfrom monolith.agent_service.agent_service_pb2_grpc import AgentServiceStub\nfrom monolith.agent_service.mocked_zkclient import FakeKazooClient\nfrom monolith.agent_service.replica_manager import ReplicaWatcher, ReplicaMeta, ModelState\nfrom monolith.agent_service.svr_client import SvrClient\n\nMODEL_NAME = 'test_model_ctr'\nBASE_PATH = f'/test_model/{MODEL_NAME}/saved_models'\nNUM_PS_REPLICAS = 2\nNUM_ENTRY_REPLICAS = 2\n\n\nclass AgentServiceTest(unittest.TestCase):\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    os.environ['TCE_INTERNAL_IDC'] = 'lf'\n    os.environ['TCE_LOGICAL_CLUSTER'] = 'default'\n    cls.zk = FakeKazooClient()\n    cls.zk.start()\n    cls.agent_conf: utils.AgentConfig = utils.AgentConfig(bzid='test_model',\n                                                          base_name=MODEL_NAME,\n                                                          deploy_type='ps',\n                                                          base_path=BASE_PATH,\n                                                          num_ps=20,\n                                                          dc_aware=True)\n    cls.watcher = ReplicaWatcher(cls.zk, cls.agent_conf)\n    cls.register(cls.zk)\n    cls.watcher.watch_data()\n    cls.agent = AgentService(cls.watcher, port=cls.agent_conf.agent_port)\n    cls.agent.start()\n    cls.client = SvrClient(cls.agent_conf)\n\n  @classmethod\n  def tearDownClass(cls) -> None:\n    cls.agent.stop()\n    cls.watcher.stop()\n\n  @classmethod\n  def register(cls, zk):\n    path_prefix = cls.agent_conf.path_prefix\n    path_to_meta, idx = {}, 2\n    for task_id in range(cls.agent_conf.num_ps):\n      for replica_id in range(NUM_PS_REPLICAS):\n        meta = ReplicaMeta(address=f'192.168.1.{idx}:{utils.find_free_port()}',\n                           stat=ModelState.AVAILABLE)\n        replica_path = f'{path_prefix}/ps:{task_id}/{replica_id}'\n        print(replica_path, flush=True)\n        path_to_meta[replica_path] = meta\n        idx += 1\n\n    for replica_id in range(NUM_ENTRY_REPLICAS):\n      replica_path = f'{path_prefix}/entry:0/{replica_id}'\n      meta = ReplicaMeta(address=f'192.168.1.{idx}:{utils.find_free_port()}',\n                         stat=ModelState.AVAILABLE)\n      path_to_meta[replica_path] = meta\n      idx += 1\n\n    for replica_path, meta in path_to_meta.items():\n      replica_meta_bytes = bytes(meta.to_json(), encoding='utf-8')\n\n      try:\n        zk.retry(zk.create,\n                 path=replica_path,\n                 value=replica_meta_bytes,\n                 ephemeral=True,\n                 makepath=True)\n      except NodeExistsError:\n        logging.info(f'{replica_path} has already exists')\n        zk.retry(zk.set, path=replica_path, value=replica_meta_bytes)\n\n  def test_heart_beat(self):\n    resp = self.client.heart_beat(server_type=ServerType.PS)\n    self.assertTrue(len(resp.addresses) == 20)\n\n  def test_get_replicas(self):\n    resp = self.client.get_replicas(server_type=ServerType.PS,\n                                    task=NUM_PS_REPLICAS - 1)\n    self.assertTrue(len(resp.address_list.address) == NUM_PS_REPLICAS)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/agent_service/agent_v1.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import app, flags, logging\nfrom concurrent.futures import ThreadPoolExecutor\nfrom enum import Enum\nfrom kazoo.client import KazooClient\nimport os\nimport subprocess\nimport signal\nfrom subprocess import CalledProcessError\nimport threading\nimport time\nfrom typing import List\n\nfrom monolith.agent_service.replica_manager import ReplicaManager\nfrom monolith.agent_service.agent_service import AgentService\nfrom monolith.agent_service.utils import AgentConfig, TFSServerType, DeployType, check_port_open\nfrom monolith.agent_service.agent_base import AgentBase, ServingLog, TFS_HOME, get_cmd_and_port, TFS_BINARY\nfrom monolith.native_training.zk_utils import MonolithKazooClient\n\n\nclass ProcessType(Enum):\n  PS = 1\n  ENTRY = 2\n  PROXY = 3\n  UNKONWN = 4\n  DENSE = 5\n\n\nclass ProcessNode(object):\n\n  def __init__(self,\n               config: AgentConfig,\n               replica_mgr: ReplicaManager,\n               proc_type: ProcessType,\n               is_tce_main: bool = False,\n               conf_path: str = None,\n               tfs_log: str = None,\n               tfs_binary: str = TFS_BINARY):\n    assert proc_type != ProcessType.UNKONWN\n\n    self._config = config\n    self._replica_mgr = replica_mgr\n    self._shell = False\n    self._stderr = subprocess.STDOUT\n    self._env = os.environ\n    self.proc_type = proc_type\n    self.is_tce_main = is_tce_main\n    self._tfs_log = tfs_log\n    self._is_failover = False\n    self._port = 0\n\n    if proc_type == ProcessType.PS:\n      self._cmd, self._port = get_cmd_and_port(config,\n                                               conf_path,\n                                               TFSServerType.PS,\n                                               tfs_binary=tfs_binary)\n    elif proc_type == ProcessType.ENTRY:\n      self._env = os.environ.copy()\n      self._env[\"PORT2\"] = str(self._config.agent_port)\n      self._cmd, self._port = get_cmd_and_port(config,\n                                               conf_path,\n                                               TFSServerType.ENTRY,\n                                               tfs_binary=tfs_binary)\n    elif proc_type == ProcessType.DENSE:\n      self._cmd, self._port = get_cmd_and_port(config,\n                                               conf_path,\n                                               TFSServerType.DENSE,\n                                               tfs_binary=tfs_binary)\n    else:\n      self._cmd, self._port = get_cmd_and_port(config,\n                                               conf_path,\n                                               tfs_binary=tfs_binary)\n\n    self._popen = None\n    self._sub_procs = {}\n\n  @property\n  def sub_procs(self):\n    return self._sub_procs\n\n  def add_subproc(self, pn: 'ProcessNode'):\n    if pn.proc_type in self._sub_procs:\n      logging.warning(f'process {pn.proc_type} exists!')\n    else:\n      self._sub_procs[pn.proc_type] = pn\n\n  @property\n  def returncode(self):\n    if self._is_failover:\n      return None\n    else:\n      return None if self._popen is None else self._popen.returncode\n\n  def poll(self):\n    if self._is_failover:\n      return None\n    else:\n      return None if self._popen is None else self._popen.poll()\n\n  def kill(self):\n    # kill subprocess\n    for proc in self._sub_procs.values():\n      if proc is None:\n        continue\n      cnt, max_cnt = 0, 3\n      proc.poll()\n      while proc.returncode is None and cnt < max_cnt:\n        logging.info(f\"kill proc {proc}\")\n        proc.kill()\n        time.sleep(1)\n        proc.poll()\n        cnt += 1\n\n    # kill self\n    if self._popen is not None:\n      cnt, max_cnt = 0, 3\n      self._popen.poll()\n      while self._popen.returncode is None and cnt < max_cnt:\n        logging.info(f\"kill proc {self._popen}\")\n        self._popen.kill()\n        time.sleep(1)\n        self._popen.poll()\n        cnt += 1\n\n  def run(self):\n    waiting_sec, max_waiting_sec = 0, 3600\n    if self.proc_type == ProcessType.ENTRY:\n      # waiting for PS status change\n      time.sleep(self._config.update_model_status_interval * 2)\n      waiting_sec += self._config.update_model_status_interval * 2\n\n      # check at least one replica of all PSs are stared\n      while not self._replica_mgr.is_ps_set_started(\n      ) and waiting_sec < max_waiting_sec:\n        time.sleep(self._config.update_model_status_interval * 2)\n        waiting_sec += self._config.update_model_status_interval * 2\n      \n      if waiting_sec >= max_waiting_sec:\n        logging.error(\"found PS timeout\")\n        return False\n\n      # check at least one replica of Dense are stared\n      if self._config.dense_alone:\n        while not self._replica_mgr.is_dense_set_started(\n        ) and waiting_sec < max_waiting_sec:\n          time.sleep(self._config.update_model_status_interval * 2)\n          waiting_sec += self._config.update_model_status_interval * 2\n\n      if waiting_sec >= max_waiting_sec:\n        logging.error(\"found Dense timeout\")\n        return False\n\n    # strat self\n    with ServingLog(self.proc_type.name.lower(), self._tfs_log) as log_stdout:\n      # Popen will return\n      self._popen = subprocess.Popen(self._cmd.split(),\n                                     shell=self._shell,\n                                     stderr=self._stderr,\n                                     stdout=log_stdout if \"MLP_POD_NAME\" not in os.environ else None,\n                                     env=self._env)\n      logging.info(f'pid of <{self._cmd}> is {self._popen.pid}')\n\n    if not self.wait_for_started():\n      logging.error(f\"start {self.proc_type} failed\")\n      return False\n\n      # start subprocess\n    for proc in self._sub_procs.values():\n      if not proc.run():\n        logging.error(f\"start {proc} failed\")\n        return False\n\n    return True\n\n  def failover(self):\n    self._is_failover = True\n    if not self.is_tce_main and (self.proc_type == ProcessType.PS or\n                                 self.proc_type == ProcessType.DENSE):\n      logging.info(f\"failover {self.proc_type}, run\")\n      self.run()\n    else:\n      logging.info(f\"failover {self.proc_type}, kill\")\n      self.kill()\n    self._is_failover = False\n\n  def wait_for_started(self):\n    if self._port == 0:\n      return True\n    waiting_sec, max_waiting_sec = 0, 3600\n    while waiting_sec <= max_waiting_sec:\n      ret = check_port_open(self._port)\n      if ret:\n        logging.info(f\"proc {self.proc_type} opened!\")\n        return True\n      logging.info(f\"proc {self.proc_type} not open!\")\n      time.sleep(10)\n      waiting_sec += 10\n    logging.info(f\"proc {self.proc_type} start failed!\")\n    return False\n\n\ndef get_proc(node: ProcessNode, res: List[ProcessNode]):\n  res.append(node)\n  for proc in node.sub_procs.values():\n    if proc is not None:\n      get_proc(proc, res)\n\n\nclass ProcessMgr(object):\n  _is_killed = False\n  _lock = threading.RLock()\n\n  def __init__(self):\n    self.pid = os.getpid()\n    self._sub_procs: List[ProcessNode] = []\n\n    signal.signal(signal.SIGTERM, self.signal_handler)\n    signal.signal(signal.SIGINT, self.signal_handler)\n\n    self._thread_stopped = False\n    self._thread = threading.Thread(target=self._poll)\n    self._pool = ThreadPoolExecutor(max_workers=2)\n\n  def add_subproc(self, proc: ProcessNode):\n    self._sub_procs.append(proc)\n\n  def signal_handler(self, signum, frame):\n\n    def target():\n      with self._lock:\n        if not ProcessMgr._is_killed:\n          self._thread_stopped = True\n          ProcessMgr._is_killed = True\n          if signum in {signal.SIGINT, signal.SIGTERM}:\n            logging.info(f\"catch signal {signum}, kill all\")\n            self.kill_all()\n            return True\n          else:\n            raise RuntimeError(f\"unknown signal {signum} at {frame}\")\n        else:\n          return True\n\n    future = self._pool.submit(target)\n    future.result()\n\n  def _poll(self):\n    procs = []\n    for proc in self._sub_procs:\n      get_proc(proc, procs)\n\n    logging.info(f\"the number of procs is {len(procs)} \")\n    while not self._thread_stopped and not ProcessMgr._is_killed:\n      time.sleep(1)\n      for proc in procs:\n        proc.poll()\n        if ProcessMgr._is_killed or self._thread_stopped:\n          break\n        if proc.returncode is not None:\n          logging.info(\n              f\"{proc.proc_type} {proc.returncode} shutdown, kill all proc...\")\n          #proc.failover()\n          #先简化管理, 有进程挂掉的话就整体挂了\n          self.kill_all()\n\n  def start(self):\n    for proc in self._sub_procs:\n      if not proc.run():\n        logging.error(f\"start {proc} failed, kill all proc\")\n        self.kill_all()\n\n    logging.info('start poll thread')\n    self._thread.start()\n\n  def kill_all(self, include_self=True):\n    with self._lock:\n      ProcessMgr._is_killed = True\n      for proc in self._sub_procs:\n        logging.info(f\"kill proc {proc.proc_type}\")\n        # [todo] add graceful shutdown later\n        proc.kill()\n      if include_self:\n        logging.info(\"kill self\")\n        os.kill(os.getpid(), signal.SIGKILL)\n\n\nclass AgentV1(AgentBase):\n\n  def __init__(self,\n               config: AgentConfig,\n               conf_path: str,\n               tfs_log: str,\n               tfs_binary: str = TFS_BINARY):\n    super(AgentV1, self).__init__(config)\n    self.zk = MonolithKazooClient(hosts=config.zk_servers)\n    self.replica_mgr = ReplicaManager(self.zk, config)\n    self.agent_service = AgentService(self.replica_mgr.watcher,\n                                      port=config.agent_port)\n\n    pm = ProcessMgr()\n    if config.deploy_type == DeployType.MIXED:\n      ps_proc = ProcessNode(config,\n                            self.replica_mgr,\n                            proc_type=ProcessType.PS,\n                            conf_path=conf_path,\n                            tfs_log=tfs_log,\n                            tfs_binary=tfs_binary)\n      pm.add_subproc(ps_proc)\n\n      if config.dense_alone:\n        dense_proc = ProcessNode(config,\n                                 self.replica_mgr,\n                                 proc_type=ProcessType.DENSE,\n                                 conf_path=conf_path,\n                                 tfs_log=tfs_log,\n                                 tfs_binary=tfs_binary)\n        pm.add_subproc(dense_proc)\n\n      entry_proc = ProcessNode(config,\n                               self.replica_mgr,\n                               proc_type=ProcessType.ENTRY,\n                               is_tce_main=True,\n                               conf_path=conf_path,\n                               tfs_log=tfs_log,\n                               tfs_binary=tfs_binary)\n      pm.add_subproc(entry_proc)\n    elif config.deploy_type == DeployType.ENTRY:\n      proxy_proc = ProcessNode(config,\n                               self.replica_mgr,\n                               proc_type=ProcessType.ENTRY,\n                               is_tce_main=True,\n                               conf_path=conf_path,\n                               tfs_log=tfs_log,\n                               tfs_binary=tfs_binary)\n      pm.add_subproc(proxy_proc)\n    elif config.deploy_type == DeployType.PS:\n      ps_proc = ProcessNode(config,\n                            self.replica_mgr,\n                            proc_type=ProcessType.PS,\n                            is_tce_main=True,\n                            conf_path=conf_path,\n                            tfs_log=tfs_log,\n                            tfs_binary=tfs_binary)\n      pm.add_subproc(ps_proc)\n    else:\n      dense_proc = ProcessNode(config,\n                               self.replica_mgr,\n                               proc_type=ProcessType.DENSE,\n                               is_tce_main=True,\n                               conf_path=conf_path,\n                               tfs_log=tfs_log,\n                               tfs_binary=tfs_binary)\n      pm.add_subproc(dense_proc)\n\n    self.process_mgr = pm\n\n  def start(self):\n    self.zk.start()\n    logging.info(f'start kazoo finished!')\n    self.replica_mgr.start()\n    logging.info(f'start replica_mgr finished!')\n    self.agent_service.start()\n    logging.info(f'start agent service at localhost:{self.config.agent_port}')\n    self.process_mgr.start()\n    logging.info(f'start ProcessMgr finished!')\n\n  def wait_for_termination(self):\n    self.agent_service.wait_for_termination()\n\n  def stop(self):\n    self.process_mgr.kill_all(include_self=False)\n    logging.info(f'close ProcessMgr finished!')\n    self.agent_service.stop()\n    logging.info(f'close agent service at localhost:{self.config.agent_port}')\n    self.replica_mgr.stop()\n    logging.info(f'close replica_mgr finished!')\n    self.zk.stop()\n    logging.info(f'close kazoo finished!')\n"
  },
  {
    "path": "monolith/agent_service/agent_v3.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\nfrom collections import defaultdict\nfrom functools import partial\nfrom absl import app, flags, logging\nimport json\nimport os\nimport re\nimport time\nimport signal\nimport threading\nimport tempfile\nfrom threading import RLock\nfrom typing import Callable, Dict, List, Tuple\nfrom subprocess import Popen, STDOUT\n\nfrom google.protobuf import text_format, json_format\n\nfrom monolith.agent_service.utils import AgentConfig, DeployType, gen_model_config, get_local_ip, \\\n  normalize_regex\nfrom monolith.agent_service.tfs_wrapper import TFSWrapper, State\nfrom monolith.agent_service.agent_service import AgentService, AgentDataProvider\nfrom monolith.agent_service.agent_base import AgentBase\nfrom monolith.agent_service.backends import ZKBackend, Container, SavedModel, \\\n  ContainerServiceInfo, SavedModelDeployConfig\n\nfrom tensorflow_serving.config import model_server_config_pb2\n\n\ndef gen_empty_model_config_file():\n  tmp_file = tempfile.mktemp()\n  with open(tmp_file, \"w\") as f:\n    f.write(\"model_config_list {}\")\n  return tmp_file\n\n\nclass AgentV3(AgentBase):\n\n  _lock = RLock()\n\n  def __init__(self, config: AgentConfig, conf_path: str, tfs_log: str):\n    super(AgentV3, self).__init__(config)\n    assert config.deploy_type == DeployType.UNIFIED, \"agent v3 only supports unifed deploy_type\"\n    assert config.agent_version == 3, f\"agent version {config.agent_version} unexpected\"\n    self._conf_path = conf_path\n    self._tfs_log = tfs_log\n\n    self._exit_event = threading.Event()\n    signal.signal(signal.SIGTERM, self.signal_handler)\n    signal.signal(signal.SIGINT, self.signal_handler)\n\n    self._model_config_path = gen_empty_model_config_file()\n\n    self._tfs_wrapper = TFSWrapper(config.tfs_port_archon, config.tfs_port_grpc,\n                                   config.tfs_port_http,\n                                   self._model_config_path, config,\n                                   self._tfs_log)\n\n    self._layout_filters = []\n    if config.layout_filters:\n      shard_id = max(config.shard_id, 0)\n      shard_num = max(config.num_shard, 1)\n      for raw_filter in config.layout_filters:\n        for k, v in [('${shard_id}', shard_id), ('${shard_num}', shard_num)]:\n          raw_filter = raw_filter.replace(k, str(v))\n        match, cond = raw_filter.split(\";\", 1)\n        self._layout_filters.append((normalize_regex(match), cond))\n\n    self._container = Container(self.config.container_cluster,\n                                self.config.container_id)\n\n    local_ip = get_local_ip()\n    self._service_info = ContainerServiceInfo(\n        grpc=f\"{local_ip}:{self.config.tfs_port_grpc}\",\n        http=f\"{local_ip}:{self.config.tfs_port_http}\",\n        archon=f\"{local_ip}:{self.config.tfs_port_archon}\",\n        agent=f\"{local_ip}:{self.config.agent_port}\",\n        idc=self.config.idc,\n        debug_info=json.dumps({\n            'layout_path':\n                config.layout_path,\n            'layout_filters': [\n                f\"{match};{cond}\" for match, cond in self._layout_filters\n            ]\n        }))\n    self._backend = ZKBackend(bzid=config.bzid, zk_servers=config.zk_servers)\n\n    self._threads = []\n\n    self._agent_service = AgentService(\n        AgentDataProvider(addrs_fn=self._gen_addrs_map), conf=config)\n\n  def _gen_addrs_map(self):\n    service_map = self._backend.get_service_map()\n    addrs_map = {}\n    for model_name in service_map:\n      for sub_graph, service_infos in service_map[model_name].items():\n        addrs_map[f\"{model_name}:{sub_graph}\"] = [\n            service_info.grpc\n            if self._tfs_wrapper.is_grpc_remote_op else service_info.archon\n            for service_info in service_infos\n        ]\n    return addrs_map\n\n  def sync_available_saved_models(self):\n    saved_model_status = self._tfs_wrapper.list_saved_models_status()\n    available_saved_models = set()\n    for saved_model_name, status in saved_model_status.items():\n      if status.state == State.AVAILABLE:\n        model_name, sub_graph = saved_model_name.split(\":\")[:2]\n        available_saved_models.add(SavedModel(model_name, sub_graph))\n    self._backend.sync_available_saved_models(self._container,\n                                              available_saved_models)\n\n  def layout_update_callback(\n      self, saved_models: List[Tuple[SavedModel,\n                                     SavedModelDeployConfig]]) -> bool:\n    logging.info(f\"layout callback with saved_models: {saved_models}\")\n    model_server_config = model_server_config_pb2.ModelServerConfig()\n    model_server_config.model_config_list.SetInParent()\n    model_config_list = model_server_config.model_config_list.config\n\n    for saved_model, deploy_config in saved_models:\n      accepted = len(self._layout_filters) == 0\n      for match, cond in self._layout_filters:\n        m = re.match(match, saved_model.sub_graph)\n        if m:\n          if eval(cond, None, {k: int(v) for k, v in m.groupdict().items()}):\n            accepted = True\n            logging.info(f\"loading {str(saved_model)} with rule {match}:{cond}\")\n            break\n      if not accepted:\n        continue\n      tfs_model_config = gen_model_config(\n          name=str(saved_model),\n          base_path=deploy_config.model_base_path,\n          version_policy=deploy_config.version_policy)\n      model_config_list.add().CopyFrom(tfs_model_config)\n\n    logging.info(\n        f\"writing model server_config: {text_format.MessageToString(model_server_config)}\"\n    )\n    with open(self._model_config_path, 'w') as f:\n      f.write(text_format.MessageToString(model_server_config))\n    return True\n\n  def signal_handler(self, signum, frame):\n    logging.info(f\"catch signal {signum}, frame {frame}\")\n    self._exit_event.set()\n\n  def start_bg_thread(self, fn, interval=10):\n\n    def target():\n      while not self._exit_event.is_set():\n        try:\n          fn()\n        except Exception as e:\n          logging.error(f\"error in bg thread: {e}\")\n        time.sleep(interval)\n\n    bg_thread = threading.Thread(target=target)\n    bg_thread.start()\n    self._threads.append(bg_thread)\n\n  def start(self):\n    self._tfs_wrapper.start()\n    self._backend.start()\n    self._agent_service.start()\n\n    self.start_bg_thread(partial(self._backend.report_service_info,\n                                 self._container, self._service_info),\n                         interval=60)\n    self.start_bg_thread(self.sync_available_saved_models, interval=30)\n    self._backend.register_layout_callback(self.config.layout_path,\n                                           self.layout_update_callback)\n\n  def stop(self):\n    self._exit_event.set()\n    try:\n      for t in self._threads:\n        t.join()\n      self._agent_service.stop()\n      self._backend.stop()\n      self._tfs_wrapper.stop()\n    except Exception as e:\n      logging.warning(e)\n\n  def wait_for_termination(self):\n    while not self._exit_event.is_set():\n      time.sleep(1)\n      ret_code = self._tfs_wrapper.poll()\n      if ret_code is not None:\n        self._exit_event.set()\n\n    self.stop()\n    time.sleep(1)\n    os.kill(os.getpid(), signal.SIGKILL)\n"
  },
  {
    "path": "monolith/agent_service/agent_v3_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nimport time\nimport unittest\nimport os\n\nfrom absl import logging\n\nfrom monolith.agent_service import utils\nfrom monolith.agent_service import backends\nfrom monolith.agent_service.agent_v3 import AgentV3\nfrom monolith.agent_service.tfs_wrapper import FakeTFSWrapper\nfrom monolith.agent_service.mocked_zkclient import FakeKazooClient\n\n\nclass AgentV3Test(unittest.TestCase):\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    cls.bzid = 'gip'\n    os.environ['MY_HOST_IP'] = '127.0.0.1'\n    agent_conf = utils.AgentConfig(bzid='gip',\n                                   deploy_type='unified',\n                                   agent_version=3,\n                                   layout_pattern=\"/gip/layout\",\n                                   zk_servers=\"127.0.0.1:8888\")\n    base_path = os.environ[\"TEST_TMPDIR\"]\n\n    cls.agent = AgentV3(config=agent_conf,\n                        conf_path=os.path.join(base_path,\n                                               '/monolith_serving/conf'),\n                        tfs_log=os.path.join('monolith_serving/logs/log.log'))\n    model_config_path = cls.agent._model_config_path\n    # replace tfs wrapper and zk\n    cls.tfs_wrapper = FakeTFSWrapper(model_config_path)\n    cls.agent._tfs_wrapper = cls.tfs_wrapper\n    cls.zk = FakeKazooClient()\n    cls.backend = cls.agent._backend\n    cls.backend._zk = cls.zk\n\n    cls.agent.start()\n    logging.info('setUpClass finished!')\n\n    def base_path(sub_graph):\n      return os.path.join(os.environ[\"TEST_TMPDIR\"],\n                          \"test_ffm_model/exported_models\", sub_graph)\n\n    for sub_graph in ['entry', 'ps_0', 'ps_1', 'ps_2']:\n      config = {\n          'model_base_path': base_path(sub_graph),\n          'version_policy': 'latest'\n      }\n      path = f'/gip/saved_models/test_ffm_model/{sub_graph}'\n      value = json.dumps(config).encode('utf-8')\n      cls.zk.create(path, value=value, makepath=True)\n\n  @classmethod\n  def tearDownClass(cls) -> None:\n    cls.agent.stop()\n    logging.info('tearDownClass finished!')\n\n  def test_service_info(self):\n    self.assertEqual(self.agent._service_info,\n                     self.backend.get_service_info(self.agent._container))\n\n  def test_publish_models(self):\n    self.assertEqual(self.tfs_wrapper.list_saved_models(), [])\n    # publish\n    self.zk.ensure_path(\"/gip/layout/test_ffm_model:entry\")\n    self.zk.ensure_path(\"/gip/layout/test_ffm_model:ps_0\")\n    # check tfs serving\n    self.assertEqual(self.tfs_wrapper.list_saved_models(),\n                     ['test_ffm_model:entry', 'test_ffm_model:ps_0'])\n    # force binding info to propagate\n    self.agent.sync_available_saved_models()\n    self.assertEqual(\n        self.backend.get_service_map(), {\n            'test_ffm_model': {\n                'entry': [self.agent._service_info],\n                'ps_0': [self.agent._service_info]\n            }\n        })\n\n    # unload one model\n    self.zk.delete(\"/gip/layout/test_ffm_model:ps_0\")\n    # check tfs serving\n    self.assertEqual(self.tfs_wrapper.list_saved_models(),\n                     ['test_ffm_model:entry'])\n    # force binding info to propagate\n    self.agent.sync_available_saved_models()\n    self.assertEqual(self.backend.get_service_map(),\n                     {'test_ffm_model': {\n                         'entry': [self.agent._service_info]\n                     }})\n\n\nif __name__ == \"__main__\":\n  logging.use_absl_handler()\n  logging.get_absl_handler().setFormatter(fmt=logging.PythonFormatter())\n  logging.set_verbosity(logging.INFO)\n  unittest.main()"
  },
  {
    "path": "monolith/agent_service/backends.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport abc\nfrom collections import defaultdict\nfrom dataclasses import dataclass\nfrom functools import partial\nfrom threading import RLock, Event\n\nfrom absl import logging\nfrom dataclasses_json import dataclass_json\nfrom typing import Callable, Dict, List, Tuple, Any, Set, Union\n\nfrom kazoo.client import KazooState\nfrom kazoo.recipe.watchers import ChildrenWatch\nfrom kazoo.exceptions import NodeExistsError, ZookeeperError, \\\n    NoNodeError, NotEmptyError, ConnectionClosedError\nfrom monolith.native_training.zk_utils import MonolithKazooClient\n\n\n@dataclass(frozen=True)\nclass SavedModel:\n  model_name: str = None\n  sub_graph: str = None\n\n  def __repr__(self):\n    return str(self)\n\n  def __str__(self):\n    return f\"{self.model_name}:{self.sub_graph}\"\n\n\n@dataclass_json\n@dataclass(frozen=True)\nclass SavedModelDeployConfig:\n  model_base_path: str = None\n  version_policy: str = None\n\n  def serialize(self) -> bytes:\n    return bytes(self.to_json(), encoding='utf-8')\n\n  @classmethod\n  def deserialize(cls, serialized: bytes) -> 'SavedModelDeployConfig':\n    return cls.from_json(str(serialized, encoding='utf-8'))\n\n\n@dataclass(frozen=True)\nclass Container:\n  ctx_cluster: str = None\n  ctx_id: str = None\n\n  def __repr__(self):\n    return str(self)\n\n  def __str__(self):\n    return f\"{self.ctx_cluster}:{self.ctx_id}\"\n\n\n@dataclass_json\n@dataclass(frozen=True)\nclass ContainerServiceInfo:\n  grpc: str = None  # grpc ip:port\n  http: str = None  # http ip:port\n  archon: str = None  # archon ip:port\n  agent: str = None  # agent ip:port\n  idc: str = None  # dc name\n  debug_info: str = None\n\n  def serialize(self) -> bytes:\n    return bytes(self.to_json(), encoding='utf-8')\n\n  @classmethod\n  def deserialize(cls, serialized: bytes) -> 'ContainerServiceInfo':\n    return cls.from_json(str(serialized, encoding='utf-8'))\n\n\nclass AgentBackend(abc.ABC):\n\n  def __init__(self):\n    pass\n\n  @abc.abstractmethod\n  def register_layout_callback(\n      self, layout_path: str,\n      callback: Callable[[List[Tuple[SavedModel, SavedModelDeployConfig]]],\n                         None]\n  ) -> None:\n    \"\"\"\n    Invoke {callback} on layout updates(adding or removing saved_models)\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def sync_available_saved_models(self, saved_models: List[SavedModel]) -> None:\n    \"\"\"\n    Report available saved models serving in localhost\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def report_service_info(self, container: Container,\n                          service_info: ContainerServiceInfo) -> None:\n    pass\n\n  @abc.abstractmethod\n  def get_service_map(self) -> Dict[str, Dict[str, List[ContainerServiceInfo]]]:\n    \"\"\"\n    Get service info map\n    {\n      \"model_name\": {\n        \"sub_graph: [\n          {\n            \"idc\": \"LQ\",\n            \"archon\": \"10.xx.xx.1:9876\",\n            \"grpc\": \"10.xx.xx.1:8765\",\n            \"http\": \"10.xx.xx.1:6789\",\n            \"agent\": \"10.xx.xx.1:6787\"\n          }\n        ]\n      }\n    }\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def report_service_info(self, container: Container,\n                          service_info: ContainerServiceInfo) -> None:\n    pass\n\n  @abc.abstractmethod\n  def get_service_info(self, container) -> ContainerServiceInfo:\n    pass\n\n  @abc.abstractmethod\n  def start(self):\n    pass\n\n  @abc.abstractmethod\n  def stop(self):\n    pass\n\n\nclass CtrlBackend(abc.ABC):\n\n  def __init__(self):\n    pass\n\n  @abc.abstractmethod\n  def list_saved_models(self, model_name: str) -> List[SavedModel]:\n    pass\n\n  @abc.abstractmethod\n  def decl_saved_model(self, saved_model: SavedModel,\n                       deploy_config: SavedModelDeployConfig):\n    pass\n\n  @abc.abstractmethod\n  def add_to_layout(self, layout: str, saved_model: SavedModel):\n    pass\n\n  @abc.abstractmethod\n  def remove_from_layout(self, layout: str, saved_model: SavedModel):\n    pass\n\n  @abc.abstractmethod\n  def bzid_info(self):\n    pass\n\n  @abc.abstractmethod\n  def start(self):\n    pass\n\n  @abc.abstractmethod\n  def stop(self):\n    pass\n\n\nclass SyncBackend(abc.ABC):\n\n  def __init__(self):\n    pass\n\n  @abc.abstractmethod\n  def subscribe_model(self, model_name: str):\n    pass\n\n  @abc.abstractmethod\n  def get_sync_targets(\n      self, sub_graph: str) -> Tuple[str, Union[List[str], Dict]]:\n    pass\n\n  @abc.abstractmethod\n  def start(self):\n    pass\n\n  @abc.abstractmethod\n  def stop(self):\n    pass\n\n\nclass ZKBackend(AgentBackend, CtrlBackend, SyncBackend):\n\n  _lock = RLock()\n\n  def __init__(self, bzid: str, zk_servers: str):\n    super(ZKBackend, self).__init__()\n    self._bzid = bzid\n    self._zk = MonolithKazooClient(hosts=zk_servers)\n    self._available_saved_model = set()\n    self._service_info_map = {}\n    self._children_watcher_map: Dict[str, ChildrenWatch] = {}\n    self._sync_model_name = None\n    self._is_lost = Event()\n\n    def zk_listener(state):\n      if state == KazooState.LOST:\n        logging.error(\"zk state lost, set lost flag\")\n        self._is_lost.set()\n      else:\n        logging.warning(f\"zk state changed to {state}, unset lost flag\")\n        self._is_lost.clear()\n      return False\n\n    self._zk.add_listener(zk_listener)\n\n  def sync_available_saved_models(self, container: Container,\n                                  saved_models: Set[SavedModel]) -> None:\n    \"\"\"\n    Report available saved models serving in tensorflow serving\n    \"\"\"\n    with self._lock:\n      if self._is_lost.is_set():\n        self._available_saved_model.clear()\n        logging.warning(\"zk is lost, try restarting\")\n        self._zk.restart()\n        return\n      add_saved_models = saved_models - self._available_saved_model\n      remove_saved_models = self._available_saved_model - saved_models\n      logging.info(\n          f\"available saved models updating, add: {add_saved_models}, remove: {remove_saved_models}\"\n      )\n      for saved_model in add_saved_models:\n        bind_path = f\"/{self._bzid}/binding/{saved_model.model_name}/{saved_model.sub_graph}:{container}\"\n        self.create_znode(bind_path, b\"\", ephemeral=True, makepath=True)\n\n      for saved_model in remove_saved_models:\n        bind_path = f\"/{self._bzid}/binding/{saved_model.model_name}/{saved_model.sub_graph}:{container}\"\n        self.delete_znode(bind_path)\n      logging.info(f\"available saved models updated: {saved_models}\")\n      self._available_saved_model = saved_models\n\n  def register_layout_callback(\n      self, layout_path: str,\n      callback: Callable[[List[Tuple[SavedModel, SavedModelDeployConfig]]],\n                         bool]):\n    \"\"\"\n    Invoke {callback} on layout updates(adding or removing saved_models)\n    \"\"\"\n\n    def callback_wrap(children: List[str]):\n      with self._lock:\n        logging.info(f\"layout updated: {children}\")\n        model_names = set()\n        saved_models = []\n        for child in children:\n          model_name, sub_graph = child.split(\":\")[:2]\n          saved_model = SavedModel(model_name, sub_graph)\n          fetch_path = f\"/{self._bzid}/saved_models/{model_name}/{sub_graph}\"\n          data = self.get_znode(fetch_path)\n          if data is None:\n            logging.error(\"missing deploy config for saved model\")\n            continue\n          saved_models.append(\n              (saved_model, SavedModelDeployConfig.deserialize(data)))\n          model_names.add(model_name)\n\n        self._service_info_map = {\n            model_name: self._service_info_map.get(model_name, {})\n            for model_name in model_names\n        }\n        for model_name in model_names:\n          binding_watch_path = f\"/{self._bzid}/binding/{model_name}\"\n          self._children_watch(binding_watch_path,\n                               partial(self._bind_callback, model_name))\n        ret = callback(saved_models)\n        return ret\n\n    with self._lock:\n      self._zk.ensure_path(layout_path)\n      self._children_watch(layout_path, callback_wrap)\n\n  def get_service_map(self) -> Dict[str, Dict[str, List[ContainerServiceInfo]]]:\n    \"\"\"\n    Get service info map\n    {\n      \"model_name\": {\n        \"sub_graph: [\n          {\n            \"idc\": \"LQ\",\n            \"archon\": \"10.xx.xx.1:9876\",\n            \"grpc\": \"10.xx.xx.1:8765\",\n            \"http\": \"10.xx.xx.1:6789\",\n            \"agent\": \"10.xx.xx.1:6787\"\n          }\n        ]\n      }\n    }\n    \"\"\"\n    return self._service_info_map.copy()\n\n  def _bind_callback(self, model_name, children):\n    with self._lock:\n      if model_name not in self._service_info_map:\n        logging.info(f\"model {model_name} no longer subscribed.\")\n        return False\n      new_binding = defaultdict(list)\n      for child in children:\n        sub_graph, ctx_cluster, ctx_id = child.split(\":\")[:3]\n        saved_model = SavedModel(model_name, sub_graph)\n        container = Container(ctx_cluster, ctx_id)\n        service_info = self.get_service_info(container)\n        if service_info is None:\n          logging.error(f\"no serivice info of {child}\")\n          continue\n        new_binding[sub_graph].append(service_info)\n      self._service_info_map[model_name] = new_binding\n\n  def report_service_info(self, container: Container,\n                          service_info: ContainerServiceInfo) -> None:\n    service_info_path = f\"/{self._bzid}/container_service/{container}\"\n    self.create_znode(service_info_path,\n                      service_info.serialize(),\n                      ephemeral=True,\n                      makepath=True)\n\n  def get_service_info(self, container) -> ContainerServiceInfo:\n    service_info_path = f\"/{self._bzid}/container_service/{container}\"\n    data = self.get_znode(service_info_path)\n    if data is None:\n      return None\n    else:\n      return ContainerServiceInfo.deserialize(data)\n\n  def _children_watch(self, path, callback):\n    with self._lock:\n      if path in self._children_watcher_map and not self._children_watcher_map[\n          path]._stopped:\n        logging.info(f\"active watcher exists on path {path}\")\n      else:\n        self._zk.ensure_path(\n            path\n        )  # make sure the path exists otherwise the watcher may not be effective\n        self._children_watcher_map[path] = self._zk.ChildrenWatch(\n            path, callback)\n        logging.info(f\"registered new watcher on {path}\")\n\n  def list_saved_models(self, model_name: str) -> List[SavedModel]:\n    model_path = f\"/{self._bzid}/saved_models/{model_name}\"\n    try:\n      sub_graphs = self._zk.get_children(model_path)\n      return [SavedModel(model_name, sub_graph) for sub_graph in sub_graphs]\n    except NoNodeError:\n      return []\n\n  def decl_saved_model(self, saved_model: SavedModel,\n                       deploy_config: SavedModelDeployConfig):\n    saved_model_path = f\"/{self._bzid}/saved_models/{saved_model.model_name}/{saved_model.sub_graph}\"\n    logging.info(f\"publishing {saved_model} -> {deploy_config}\")\n    self.create_znode(saved_model_path,\n                      deploy_config.serialize(),\n                      makepath=True)\n\n  def add_to_layout(self, layout: str, saved_model: SavedModel):\n    path = f\"{layout}/{saved_model}\"\n    self._zk.ensure_path(path)\n\n  def remove_from_layout(self, layout: str, saved_model: SavedModel):\n    path = f\"{layout}/{saved_model}\"\n    try:\n      self._zk.delete(path)\n    except NoNodeError:\n      pass\n\n  def bzid_info(self):\n    # model deploy configs\n    model_info = defaultdict(lambda: defaultdict(dict))\n    if self._zk.exists(f\"/{self._bzid}/saved_models\"):\n      model_names = self._zk.get_children(f\"/{self._bzid}/saved_models\")\n      for model_name in model_names:\n        sub_graphs = self._zk.get_children(\n            f\"/{self._bzid}/saved_models/{model_name}\")\n        model_info[model_name]['sub_graphs_total'] = len(sub_graphs)\n        for sub_graph in sub_graphs:\n          model_info[model_name][sub_graph]['deploy_config'] = self.get_znode(\n              f\"/{self._bzid}/saved_models/{model_name}/{sub_graph}\").decode(\n                  'utf-8')\n\n    container_info = defaultdict(lambda: defaultdict(dict))\n\n    # container service info\n    if self._zk.exists(f\"/{self._bzid}/container_service\"):\n      containers = self._zk.get_children(f\"/{self._bzid}/container_service\")\n      for container in containers:\n        cluster, container_id = container.split(\":\")[:2]\n        container_info[cluster][container_id]['service_info'] = self.get_znode(\n            f\"/{self._bzid}/container_service/{container}\").decode('utf-8')\n\n    # layout info\n    layout_info = defaultdict(lambda: defaultdict(dict))\n    if self._zk.exists(f\"/{self._bzid}/layouts\"):\n      layouts = self._zk.get_children(f\"/{self._bzid}/layouts\")\n      for layout in layouts:\n        saved_models = self._zk.get_children(f\"/{self._bzid}/layouts/{layout}\")\n        if saved_models:\n          layout_info[layout] = sorted(saved_models)\n        else:\n          layout_info[layout] = []\n\n    # bindings\n    if self._zk.exists(f\"/{self._bzid}/binding\"):\n      model_names = self._zk.get_children(f\"/{self._bzid}/binding\")\n      for model_name in model_names:\n        bindings = self._zk.get_children(f\"/{self._bzid}/binding/{model_name}\")\n        for binding in bindings:\n          sub_graph, cluster, container_id = binding.split(\":\")[:3]\n          if 'bindings' not in model_info[model_name][sub_graph]:\n            model_info[model_name][sub_graph]['bindings'] = []\n            model_info[model_name][\n                'sub_graphs_available'] = model_info[model_name].get(\n                    'sub_graphs_available', 0) + 1\n          model_info[model_name][sub_graph]['bindings'].append(\n              f\"{cluster}:{container_id}\")\n          if 'saved_models' not in container_info[cluster][container_id]:\n            container_info[cluster][container_id]['saved_models'] = []\n          container_info[cluster][container_id]['saved_models'].append(\n              f\"{model_name}:{sub_graph}\")\n\n    def sorted_dict(d):\n      return dict(sorted(d.items()))\n\n    return {\n        'model_info': {\n            model_name: sorted_dict(model_info[model_name])\n            for model_name in model_info\n        },\n        'container_info': {\n            cluster: sorted_dict(container_info[cluster])\n            for cluster in container_info\n        },\n        'layout_info': {layout: layout_info[layout] for layout in layout_info}\n    }\n\n  # sync backend\n  def subscribe_model(self, model_name: str):\n    if model_name == self._sync_model_name:\n      return\n    assert self._sync_model_name is None\n    self._sync_model_name = model_name\n    self._service_info_map[model_name] = self._service_info_map.get(\n        model_name, {})\n    binding_watch_path = f\"/{self._bzid}/binding/{model_name}\"\n    self._children_watch(binding_watch_path,\n                         partial(self._bind_callback, model_name))\n\n  def get_sync_targets(self, sub_graph: str) -> Tuple[str, List[str]]:\n    with self._lock:\n      if self._is_lost.is_set():\n        self._available_saved_model.clear()\n        logging.warning(\"zk is lost, try restarting\")\n        self._zk.restart()\n\n      sub_graph_map = self._service_info_map.get(self._sync_model_name, {})\n      service_infos = sub_graph_map.get(sub_graph, [])\n      return f\"{self._sync_model_name}:{sub_graph}\", [\n          service_info.grpc for service_info in service_infos\n      ]\n\n  def create_znode(self, path, value, ephemeral=False, makepath=False) -> None:\n    with self._lock:\n      try:\n        self._zk.create(path,\n                        value=value,\n                        ephemeral=ephemeral,\n                        makepath=makepath)\n      except NodeExistsError as e:\n        self._zk.retry(self._zk.set, path=path, value=value)\n      except Exception as e:\n        logging.error(f\"exception in create_znode: {e}\")\n\n  def delete_znode(self, path) -> None:\n    with self._lock:\n      try:\n        self._zk.delete(path)\n      except Exception as e:\n        logging.error(f\"exception in delete_znode: {e}\")\n\n  def get_znode(self, path) -> bytes:\n    try:\n      return self._zk.get(path)[0]\n    except NoNodeError:\n      return None\n\n  def start(self):\n    self._zk.start()\n\n  def stop(self):\n    self._zk.stop()\n"
  },
  {
    "path": "monolith/agent_service/backends_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nimport os\nimport unittest\n\nfrom monolith.agent_service import utils\nfrom monolith.agent_service import backends\nfrom monolith.agent_service.mocked_zkclient import FakeKazooClient\n\n\nclass ZKBackendTest(unittest.TestCase):\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    cls.bzid = 'gip'\n    cls.container = backends.Container(\"default\", \"asdf\")\n    cls.service_info = backends.ContainerServiceInfo(grpc=\"localhost:8888\",\n                                                     http=\"localhost:8889\",\n                                                     archon=\"localhost:8890\",\n                                                     agent=\"localhost:8891\",\n                                                     idc=\"IDC\")\n    cls.backend = backends.ZKBackend(cls.bzid, zk_servers='127.0.0.1:9999')\n    cls.zk = FakeKazooClient()\n    cls.backend._zk = cls.zk\n    cls.layout_record = None\n\n    def layout_callback(saved_models):\n      cls.layout_record = saved_models\n\n    cls.backend.start()\n    cls.backend.report_service_info(cls.container, cls.service_info)\n    cls.layout_path = \"/gip/layouts/test_layout/mixed\"\n    cls.backend.register_layout_callback(cls.layout_path, layout_callback)\n    print(\"setUpClass finished!\")\n\n  @classmethod\n  def tearDownClass(cls) -> None:\n    cls.backend.stop()\n    print('tearDownClass finished!')\n\n  def test_register_service(self):\n    service_info = self.backend.get_service_info(self.container)\n    self.assertEqual(service_info, self.service_info)\n\n  def test_layout_callback(self):\n\n    def base_path(sub_graph):\n      return os.path.join(os.environ[\"TEST_TMPDIR\"],\n                          \"test_ffm_model/exported_models\", sub_graph)\n\n    for sub_graph in ['entry', 'ps_0', 'ps_1', 'ps_2']:\n      saved_model = backends.SavedModel('test_ffm_model', sub_graph)\n      self.backend.decl_saved_model(\n          saved_model,\n          backends.SavedModelDeployConfig(base_path(sub_graph), 'latest'))\n      self.backend.add_to_layout(self.layout_path, saved_model)\n    expected_saved_models = [\n        (backends.SavedModel(\"test_ffm_model\", \"entry\"),\n         backends.SavedModelDeployConfig(base_path('entry'), 'latest')),\n        (backends.SavedModel(\"test_ffm_model\", \"ps_0\"),\n         backends.SavedModelDeployConfig(base_path('ps_0'), 'latest')),\n        (backends.SavedModel(\"test_ffm_model\", \"ps_1\"),\n         backends.SavedModelDeployConfig(base_path('ps_1'), 'latest')),\n        (backends.SavedModel(\"test_ffm_model\", \"ps_2\"),\n         backends.SavedModelDeployConfig(base_path('ps_2'), 'latest')),\n    ]\n    self.assertEqual(self.layout_record, expected_saved_models)\n    self.backend.remove_from_layout(\n        self.layout_path, backends.SavedModel('test_ffm_model', 'entry'))\n    self.assertEqual(self.layout_record, [\n        (backends.SavedModel(\"test_ffm_model\", \"ps_0\"),\n         backends.SavedModelDeployConfig(base_path('ps_0'), 'latest')),\n        (backends.SavedModel(\"test_ffm_model\", \"ps_1\"),\n         backends.SavedModelDeployConfig(base_path('ps_1'), 'latest')),\n        (backends.SavedModel(\"test_ffm_model\", \"ps_2\"),\n         backends.SavedModelDeployConfig(base_path('ps_2'), 'latest')),\n    ])\n\n  def test_sync_available_models(self):\n    self.backend.sync_available_saved_models(\n        self.container, {\n            backends.SavedModel(\"test_ffm_model\", \"entry\"),\n            backends.SavedModel(\"test_ffm_model\", \"ps_0\"),\n            backends.SavedModel(\"test_ffm_model\", \"ps_1\"),\n        })\n    self.assertTrue(\n        self.zk.exists(f\"/gip/binding/test_ffm_model/entry:{self.container}\"))\n    self.assertTrue(\n        self.zk.exists(f\"/gip/binding/test_ffm_model/ps_0:{self.container}\"))\n    self.assertTrue(\n        self.zk.exists(f\"/gip/binding/test_ffm_model/ps_1:{self.container}\"))\n\n  def test_service_map(self):\n    self.backend.sync_available_saved_models(\n        self.container, {\n            backends.SavedModel(\"test_ffm_model\", \"entry\"),\n            backends.SavedModel(\"test_ffm_model\", \"ps_0\")\n        })\n    expected = {\n        'test_ffm_model': {\n            'ps_0': [self.service_info],\n            'entry': [self.service_info]\n        }\n    }\n    self.assertTrue(self.backend.get_service_map(), expected)\n\n  def test_sync_backend(self):\n    self.backend.subscribe_model(\"test_ffm_model\")\n    self.backend.sync_available_saved_models(\n        self.container, {\n            backends.SavedModel(\"test_ffm_model\", \"ps_0\"),\n            backends.SavedModel(\"test_ffm_model\", \"ps_1\"),\n            backends.SavedModel(\"test_ffm_model\", \"ps_2\"),\n        })\n    model_name, targets = self.backend.get_sync_targets(\"ps_1\")\n    self.assertEqual(model_name, \"test_ffm_model:ps_1\")\n    self.assertEqual(targets, [self.service_info.grpc])\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/agent_service/client.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import flags, logging, app\nfrom monolith.agent_service.zk_mirror import ZKMirror\nfrom monolith.native_training import env_utils\n\nfrom monolith.native_training.zk_utils import MonolithKazooClient\nfrom monolith.agent_service.data_def import ModelMeta, ReplicaMeta\nfrom typing import Dict\nfrom dataclasses import dataclass, field\n\nfrom tensorflow_serving.apis.get_model_status_pb2 import ModelVersionStatus\n\nModelState = ModelVersionStatus.State\n\nFLAGS = flags.FLAGS\nflags.DEFINE_enum(\"cmd_type\", \"hb\", [\n    \"hb\", \"gr\", \"addr\", \"get\", \"clean\", \"load\", \"unload\", \"meta\", \"status\",\n    \"profile\"\n], \"cmd_type: hb, gr, addr, res, status\")\nflags.DEFINE_string('zk_servers', None, 'zk servers')\nflags.DEFINE_string('bzid', None, 'business id')\nflags.DEFINE_string('model_name', None, 'model name')\nflags.DEFINE_string(\"target\", None, \"host:port\")\nflags.DEFINE_enum(\"input_type\", 'dump', [\n    \"json\", \"pbtext\", \"dump\", \"binary\", \"instance\", \"example_batch\",\n    \"example_batch_to_instance\"\n], \"inputs type for prediction\")\nflags.DEFINE_string(\"input_file\", None, \"The input file name\")\n\n\n@dataclass\nclass LoadSate:\n  portal: bool = None\n  publish: bool = None\n  service: dict = field(default_factory=dict)  # Dict[str, ModelState]\n\n\nclass ServingClient(object):\n\n  def __init__(self, zk_servers: str, bzid: str):\n    self.kazoo = MonolithKazooClient(hosts=zk_servers)\n    self.bzid = bzid\n    self._zk = ZKMirror(self.kazoo, bzid)\n    self._zk.start(is_client=True)\n\n  def load(self,\n           model_name: str,\n           model_dir: str,\n           ckpt: str = None,\n           num_shard: int = -1):\n    mm = ModelMeta(model_name=model_name,\n                   model_dir=model_dir,\n                   ckpt=ckpt,\n                   num_shard=num_shard)\n    path = mm.get_path(self._zk.portal_base_path)\n    if self._zk.exists(path):\n      raise RuntimeError(f'{model_name} has exists')\n    self._zk.create(path=path, value=mm.serialize(), include_data=True)\n\n  def unload(self, model_name: str):\n    mm = ModelMeta(model_name=model_name)\n    path = mm.get_path(self._zk.portal_base_path)\n    if self._zk.exists(path):\n      self._zk.delete(path)\n    else:\n      logging.warning(f'{model_name} not exists, nothing to do!')\n\n  def get_status(self, model_name: str) -> LoadSate:\n    state = LoadSate()\n    if self.kazoo.exists(f'/{self.bzid}/portal/{model_name}'):\n      state.portal = True\n\n    for node in self.kazoo.get_children(f'/{self.bzid}/publish'):\n      shard_id, replica_id, name = node.split(':')\n      if name == model_name:\n        state.publish = True\n        break\n\n    service = {}\n    for node in self.kazoo.get_children(f'/{self.bzid}/service/{model_name}'):\n      for replica in self.kazoo.get_children(\n          f'/{self.bzid}/service/{model_name}/{node}'):\n        path = f'/{self.bzid}/service/{model_name}/{node}/{replica}'\n        value, _ = self.kazoo.get(path)\n        rm = ReplicaMeta.deserialize(value)\n        service[f'{node}:{replica}'] = rm.stat\n    state.service = service\n\n    return state\n\n\ndef main(_):\n  env_utils.setup_host_ip()\n  if FLAGS.zk_servers is None:\n    raise ValueError(f'zk_servers is {FLAGS.zk_servers}')\n\n  if FLAGS.bzid is None:\n    raise ValueError(f'bzid is {FLAGS.bzid}')\n\n  client = ServingClient(FLAGS.zk_servers, FLAGS.bzid)\n\n  assert FLAGS.model_name is not None\n  if FLAGS.cmd_type == 'load':\n    assert FLAGS.model_dir is not None\n    client.load(FLAGS.model_name, FLAGS.model_dir, FLAGS.ckpt, FLAGS.num_shard)\n  elif FLAGS.cmd_type == 'unload':\n    client.unload(FLAGS.model_name)\n  else:\n    print(client.get_status(FLAGS.model_name))\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "monolith/agent_service/constants.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nHOST_SHARD_ENV = \"MONOLITH_HOST_SHARD_N\"\n"
  },
  {
    "path": "monolith/agent_service/data_def.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom dataclasses import dataclass, field\nfrom dataclasses_json import dataclass_json\nfrom enum import Enum\nimport os\nfrom typing import Dict, List, NewType, Optional\n\nfrom monolith.native_training.net_utils import AddressFamily\nfrom tensorflow_serving.util.status_pb2 import StatusProto\nfrom tensorflow_serving.apis.get_model_status_pb2 import ModelVersionStatus\n\nModelState = ModelVersionStatus.State\nModelName = NewType('ModelName', str)  # simple model_name\nSubModelName = NewType('SubModelName', str)  # entry, ps_{num}, dense\nSubModelSize = NewType('SubModelSize', int)  # size in byte\nTFSModelName = NewType('TFSModelName', str)  # f'{model_name}:{sub_model_name}'\nVersionPath = NewType('VersionPath',\n                      str)  # .../exported_models/{sub_model_name}/{version}\nEmptyStatus = StatusProto()\n\n\n@dataclass_json\n@dataclass\nclass ModelMeta(object):\n  model_name: str = None\n  model_dir: str = None\n  ckpt: str = None\n  num_shard: int = -1\n  action: str = 'NONE'\n  spec_replicas: List[int] = field(default_factory=list)\n\n  def get_path(self, base_path: str) -> str:\n    return os.path.join(base_path, self.model_name)\n\n  def serialize(self) -> bytes:\n    return bytes(self.to_json(), encoding='utf-8')\n\n  @classmethod\n  def deserialize(cls, serialized: bytes) -> 'ModelMeta':\n    return cls.from_json(str(serialized, encoding='utf-8'))\n\n\n@dataclass_json\n@dataclass\nclass ResourceSpec(object):\n  address: str = None  # host:port\n  shard_id: int = None\n  replica_id: int = None\n  memory: int = None\n  cpu: float = -1.0\n  network: float = -1.0\n  work_load: float = -1.0\n\n  def get_path(self, base_path: str) -> str:\n    return os.path.join(base_path, f\"{self.shard_id}:{self.replica_id}\")\n\n  def serialize(self) -> bytes:\n    return bytes(self.to_json(), encoding='utf-8')\n\n  @classmethod\n  def deserialize(cls, serialized: bytes) -> 'ResourceSpec':\n    return cls.from_json(str(serialized, encoding='utf-8'))\n\n\nclass PublishType(Enum):\n  LOAD = 1\n  UNLOAD = 2\n\n\n@dataclass_json\n@dataclass\nclass PublishMeta(object):\n  shard_id: int = None\n  replica_id: int = -1\n  model_name: str = None\n  num_ps: int = None\n  total_publish_num: int = 1\n  sub_models: Dict[SubModelName, VersionPath] = None\n  ptype: PublishType = PublishType.LOAD\n  is_spec: bool = False\n\n  def get_path(self, base_path: str) -> str:\n    return os.path.join(base_path,\n                        f'{self.shard_id}:{self.replica_id}:{self.model_name}')\n\n  def serialize(self) -> bytes:\n    return bytes(self.to_json(), encoding='utf-8')\n\n  @classmethod\n  def deserialize(cls, serialized: bytes) -> 'PublishMeta':\n    return cls.from_json(str(serialized, encoding='utf-8'))\n\n\n@dataclass_json\n@dataclass\nclass ReplicaMeta:\n  address: str = None  # host:port\n  address_ipv6: str = None  # [host]:port\n  stat: int = ModelState.UNKNOWN\n  model_name: Optional[str] = None\n  server_type: Optional[str] = None\n  task: int = -1\n  replica: int = -1\n  archon_address: str = None  # host:port\n  archon_address_ipv6: str = None  # [host]:port\n\n  def serialize(self) -> bytes:\n    return bytes(self.to_json(), encoding='utf-8')\n\n  @classmethod\n  def deserialize(cls, serialized: bytes) -> 'ReplicaMeta':\n    return cls.from_json(str(serialized, encoding='utf-8'))\n\n  def get_path(self, bzid: str, sep: str = '/') -> str:\n    paths = [\n        '', bzid, 'service', self.model_name, f'{self.server_type}:{self.task}',\n        str(self.replica)\n    ]\n    return sep.join(paths)\n\n  def get_address(self,\n                  use_archon: bool = False,\n                  address_family: str = AddressFamily.IPV4) -> str:\n    assert address_family in [AddressFamily.IPV4, AddressFamily.IPV6]\n    ipv4_address = self.archon_address if use_archon else self.address\n    if ipv4_address is not None and ipv4_address.startswith('0.0.0.0'):\n      ipv4_address = None\n    ipv6_address = self.archon_address_ipv6 if use_archon else self.address_ipv6\n    if ipv6_address is not None and ipv6_address.startswith('[::]'):\n      ipv6_address = None\n    if address_family == AddressFamily.IPV4:\n      address = ipv4_address or ipv6_address\n    else:\n      address = ipv6_address or ipv4_address\n    return address\n\n\nclass EventType(Enum):\n  PORTAL = 1  # Scheduler, ZK watch trigger\n  SERVICE = 2  # StatusReportHandler, time trigger\n  PUBLISH = 3  # ModelLoaderHandler, ZK watch trigger\n  RESOURCE = 4  # ResourceReportHandler, time trigger\n  UNKNOWN = 1\n\n\n@dataclass_json\n@dataclass\nclass Event(object):\n  path: str = None\n  data: bytes = b''\n  etype: EventType = EventType.UNKNOWN\n\n  def serialize(self) -> bytes:\n    return bytes(self.to_json(), encoding='utf-8')\n\n  @classmethod\n  def deserialize(cls, serialized: bytes) -> 'Event':\n    return cls.from_json(str(serialized, encoding='utf-8'))\n"
  },
  {
    "path": "monolith/agent_service/data_def_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport unittest\nfrom monolith.agent_service.data_def import ModelMeta, ResourceSpec, ReplicaMeta\n\n\nclass DataDefTest(unittest.TestCase):\n\n  def serde(self, item):\n    cls = item.__class__\n    serialized = item.serialize()\n    recom = cls.deserialize(serialized)\n    self.assertEqual(item, recom)\n\n  def test_model_info(self):\n    obj = ModelMeta(model_name='monolith',\n                    num_shard=3,\n                    model_dir=\"/tmp/opt\",\n                    ckpt='model.ckpt-1234')\n    self.serde(obj)\n\n  def test_resource(self):\n    obj = ResourceSpec(address=\"localhost:123\",\n                       shard_id=10,\n                       replica_id=2,\n                       memory=12345,\n                       cpu=3.5)\n    self.serde(obj)\n\n  def test_replica_meta(self):\n    obj = ReplicaMeta(address=\"localhost:123\",\n                      model_name='monolith',\n                      server_type='ps',\n                      task=0,\n                      replica=0)\n    self.serde(obj)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/agent_service/example_batch.pbtxt",
    "content": "named_feature_list {\n  name: \"f_goods_test30_bool\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9376215397644962785\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9380318556689891903\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_client_version\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_source_id\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_city\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test32_double\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9322172202116516833\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9322172202116516833\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_comment_cnt\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_pub_time_hour\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4235580633157097946\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4242591947990187885\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test09_array_int32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9987835853247866286\n      value: 9982825002555554975\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9987835853247866286\n      value: 9982825002555554975\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9987835853247866286\n      value: 9982825002555554975\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9987835853247866286\n      value: 9982825002555554975\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9987835853247866286\n      value: 9982825002555554975\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9987835853247866286\n      value: 9982825002555554975\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9987835853247866286\n      value: 9982825002555554975\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9987835853247866286\n      value: 9982825002555554975\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test15_array_float\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9736503367834602465\n      value: 9736503367834602465\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9736503367834602465\n      value: 9736503367834602465\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9736503367834602465\n      value: 9736503367834602465\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9736503367834602465\n      value: 9736503367834602465\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9736503367834602465\n      value: 9736503367834602465\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9736503367834602465\n      value: 9736503367834602465\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9736503367834602465\n      value: 9736503367834602465\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9736503367834602465\n      value: 9736503367834602465\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_spm_1\"\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5431062124068408289\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_share_cnt_1000\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_spm_3\"\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5467090921087372257\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_spm_2\"\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441722959692120433\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441722959692120433\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451370321420258447\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451370321420258447\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451333593801533170\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451370321420258447\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441722959692120433\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5450268195437205113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451370321420258447\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5447058606610205093\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451370321420258447\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5450268195437205113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451370321420258447\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441722959692120433\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451370321420258447\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451370321420258447\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5450268195437205113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5450268195437205113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5450268195437205113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5450268195437205113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5450268195437205113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5447058606610205093\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5450268195437205113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441722959692120433\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441980230172067264\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451370321420258447\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5447099530896665279\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5451370321420258447\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5447058606610205093\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5450268195437205113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5450268195437205113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5447099530896665279\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441722959692120433\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5441722959692120433\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_spm_4\"\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5485105319596854241\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_spu_id\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test45_map_string\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9254796155896927364\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9244648908249888622\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_price_reduction_10\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4367227356144754473\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4360224287068445047\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_id\"\n  feature {\n    fid_v1_list {\n      value: 34661039000183975\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26375391288018365\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26375391288018365\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26375391288018365\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26375391288018365\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26375391288018365\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26375391288018365\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26375391288018365\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26375391288018365\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26375391288018365\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26375391288018365\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 35592691808838616\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 32480246379207459\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26403633346786866\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26403633346786866\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26403633346786866\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 22739744141601140\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 22739744141601140\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 22739744141601140\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 31753659020026959\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 31753659020026959\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 33895127814546172\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 18976550735883040\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 18976550735883040\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 30810089642095820\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 30810089642095820\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 24555653156517823\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 28442428386816647\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 31371008067277372\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 31371008067277372\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 24213437492756285\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 34828384169520893\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 34828384169520893\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 34828384169520893\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 34828384169520893\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 34828384169520893\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 34828384169520893\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 34828384169520893\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 30621526964314858\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 26799910740734091\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 24017764260178887\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 24017764260178887\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 24017764260178887\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 32401856502699871\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 29165508905376910\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 25144527618414700\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 25144527618414700\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 25144527618414700\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 25144527618414700\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 30893925026832398\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 30893925026832398\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_network\"\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1102855795182531059\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107344313440999751\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 1107606481792732129\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test38_array_bool\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9466287390192372705\n      value: 9470390549237301823\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9470390549237301823\n      value: 9466287390192372705\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test05_string\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9724206661329807113\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9724206661329807113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9724206661329807113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9724206661329807113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9724206661329807113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9724206661329807113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9724206661329807113\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9724206661329807113\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_city\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 184466745546546060\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 188872157809150945\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 188872157809150945\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 188872157809150945\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 188872157809150945\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 188872157809150945\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 188872157809150945\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 188872157809150945\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test31_float\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9178057014040660961\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9178057014040660961\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_device_id\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test25_int32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9302601369885095920\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9306223536401580458\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_country\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test07_float\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9646431375287192545\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9646431375287192545\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9646431375287192545\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9646431375287192545\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9646431375287192545\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9646431375287192545\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9646431375287192545\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9646431375287192545\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_shipping_money\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test20_map_uint64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9875330678134588286\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9875330678134588286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9875330678134588286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9875330678134588286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9875330678134588286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9875330678134588286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9875330678134588286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9875330678134588286\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test42_map_int64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9583186420523358701\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9569085903473394558\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test02_int64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9687302302898843769\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9687302302898843769\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9687302302898843769\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9687302302898843769\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9687302302898843769\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9687302302898843769\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9687302302898843769\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9687302302898843769\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_district\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_current_price_1000\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3864695453538224966\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3872186882756116749\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test35_array_uint32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9390373322373335920\n      value: 9388843097214556352\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9394938221468407955\n      value: 9390373322373335920\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_age\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test24_map_double\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9963468822863469749\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9963468822863469749\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9963468822863469749\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9963468822863469749\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9963468822863469749\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9963468822863469749\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9963468822863469749\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9963468822863469749\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_id-f_goods_current_price\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9045399711331748120\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9050924295655723542\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_device_model\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_origin_price_1000\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3915976234101104791\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3916132237881122179\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test28_uint64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9475573618135007223\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9475573618135007224\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_id-f_goods_sale_number\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9065644441006867721\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9074052365851756434\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_share_cnt\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test08_double\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9952676149948386273\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9952676149948386273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9952676149948386273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9952676149948386273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9952676149948386273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9952676149948386273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9952676149948386273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9952676149948386273\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test06_bool\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9830678519426941503\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9830678519426941503\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9830678519426941503\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9830678519426941503\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9830678519426941503\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9830678519426941503\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9830678519426941503\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9830678519426941503\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test33_array_int32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9534996739393864528\n      value: 9542874036410030307\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9537475890510816686\n      value: 9532465039818505375\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_brand\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3743551367396753389\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3733245351290651264\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_current_price\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3826301672674962047\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3830953370607164169\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_tags_terms\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3811379433387304802\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3806659414003006399\n      value: 3817841950973150016\n      value: 3808895514000229327\n      value: 3808399441778900137\n      value: 3808399441778900137\n      value: 3808895514000229327\n      value: 3811495559608005733\n      value: 3817841611590067540\n      value: 3805280058801983575\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_cate_1\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3664100952242652919\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3671364035570364440\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_cate_2\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3687893206423298716\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3690602924044901962\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_cate_3\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3696969598358506601\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3698659331662272830\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_cate_4\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3716991466540562362\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3711405396388666663\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test44_map_uint64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9367013638409574893\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9352913121359610750\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_id-f_page\"\n  feature {\n    fid_v1_list {\n      value: 9092789705426625231\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9086397419364646740\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9086397419364646740\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9080929169466218336\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9092490710899668234\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9080929169466218336\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9080929169466218336\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9080929169466218336\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9080929169466218336\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9080929169466218336\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9080929169466218336\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9084723152110966336\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9090673728892834668\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9087002549578805200\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9086378705790910311\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9091939060511529031\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9094200603063820929\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9094200603063820929\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9096580265606473271\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9090211897252558350\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9097050149952374856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9086504574730266468\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9092015533406643567\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9086616598317095688\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9085846876813685546\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9085846876813685546\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9087911285192683050\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9082753167719844823\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9082815111106798175\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9082815111106798175\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9094183977061329440\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9082847486989399824\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9085289623114222694\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9085289623114222694\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9085289623114222694\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9085289623114222694\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9085289623114222694\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9095104172800023209\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9089914688358822412\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9088296710536755392\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9092609888464068017\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9092609888464068017\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9080303384419389583\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9089956094034039490\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9094391052982619763\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9085646402573353544\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9096908574899867209\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9096908574899867209\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9082413653420066872\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9082691189500607224\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9082691189500607224\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_user_id_type\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_origin_price_10\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3906092201329708288\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3905557326176545651\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test03_uint32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9755226191658047635\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9755226191658047635\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9755226191658047635\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9755226191658047635\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9755226191658047635\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9755226191658047635\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9755226191658047635\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9755226191658047635\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_shop_id\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_pub_time_day\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4220323736978779502\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4233021007139439725\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test14_array_bool\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9794649722407977535\n      value: 9790546563363048417\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9794649722407977535\n      value: 9790546563363048417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9794649722407977535\n      value: 9790546563363048417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9794649722407977535\n      value: 9790546563363048417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9794649722407977535\n      value: 9790546563363048417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9794649722407977535\n      value: 9790546563363048417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9794649722407977535\n      value: 9790546563363048417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9794649722407977535\n      value: 9790546563363048417\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test12_array_uint64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9939503882031591545\n      value: 9940527614559994373\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9939503882031591545\n      value: 9940527614559994373\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9939503882031591545\n      value: 9940527614559994373\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9939503882031591545\n      value: 9940527614559994373\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9939503882031591545\n      value: 9940527614559994373\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9939503882031591545\n      value: 9940527614559994373\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9939503882031591545\n      value: 9940527614559994373\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9939503882031591545\n      value: 9940527614559994373\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test10_array_int64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9903475085012627577\n      value: 9904498817541030405\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9903475085012627577\n      value: 9904498817541030405\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9903475085012627577\n      value: 9904498817541030405\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9903475085012627577\n      value: 9904498817541030405\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9903475085012627577\n      value: 9904498817541030405\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9903475085012627577\n      value: 9904498817541030405\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9903475085012627577\n      value: 9904498817541030405\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9903475085012627577\n      value: 9904498817541030405\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test48_map_double\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9504076465062773640\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9495094461616938165\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test36_array_uint64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9418110057785016837\n      value: 9404144550176698117\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9417086325256614009\n      value: 9418110057785016837\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test11_array_uint32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9593096605072709779\n      value: 9588531705977637744\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9593096605072709779\n      value: 9588531705977637744\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9593096605072709779\n      value: 9588531705977637744\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9593096605072709779\n      value: 9588531705977637744\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9593096605072709779\n      value: 9588531705977637744\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9593096605072709779\n      value: 9588531705977637744\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9593096605072709779\n      value: 9588531705977637744\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9593096605072709779\n      value: 9588531705977637744\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_os_version\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_comment_cnt_10\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_praise_cnt_10\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_free_shipping\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_os\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_client_version\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_sale_number\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4030042199373742655\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4019703662382893803\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test27_uint32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9228243735787998064\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9232808634883070099\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_scm\"\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5526585389177434390\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5513886323412268515\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_share_cnt_10\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_os_version\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"__LINE_ID__\"\n  feature {\n    bytes_list {\n      value: \"\\030\\305\\331\\221\\203\\006!\\004\\\\.\\347\\231=\\233H2\\001\\001Z\\001a\\240\\001\\305\\331\\221\\203\\006\\212\\003\\0171c86a18cbb1060f\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\200\\342\\220\\203\\006!\\346\\010T\\347\\364\\020\\365!2\\001\\001Z\\001a\\240\\001\\200\\342\\220\\203\\006\\212\\003\\01721bacb96ae78a72\\232\\n\\0231$##$1001$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\206\\342\\220\\203\\006!\\224\\331\\334\\036O\\026\\365!2\\001\\001Z\\001a\\240\\001\\206\\342\\220\\203\\006\\212\\003\\01721bacb96ae78a72\\232\\n\\0231$##$1001$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\315\\233\\221\\203\\006!\\213w\\364\\272.:\\260G2\\001\\001Z\\001a\\240\\001\\315\\233\\221\\203\\006\\212\\003\\01721bacb96ae78a72\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\203\\333\\221\\203\\006!\\200\\255\\205n\\330\\364\\017y2\\001\\001Z\\001a\\240\\001\\203\\333\\221\\203\\006\\212\\003\\01721bacb96ae78a72\\232\\n\\0231$##$1002$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\310\\333\\221\\203\\006!O4\\025&\\240\\242\\263x2\\001\\001Z\\001a\\240\\001\\310\\333\\221\\203\\006\\212\\003\\01721bacb96ae78a72\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\323\\334\\221\\203\\006!\\036j\\017k\\237\\271K\\0302\\001\\001Z\\001a\\240\\001\\323\\334\\221\\203\\006\\212\\003\\01721bacb96ae78a72\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\230\\337\\221\\203\\006!\\334DZN^\\267\\207`2\\001\\001Z\\001a\\240\\001\\230\\337\\221\\203\\006\\212\\003\\01721bacb96ae78a72\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\317\\340\\221\\203\\006!\\352P\\331\\203+\\362W~2\\001\\001Z\\001a\\240\\001\\317\\340\\221\\203\\006\\212\\003\\01721bacb96ae78a72\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\246\\342\\221\\203\\006!\\234\\311\\264G\\351`\\367X2\\001\\001Z\\001a\\240\\001\\246\\342\\221\\203\\006\\212\\003\\01721bacb96ae78a72\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\253\\345\\221\\203\\006!\\217\\312\\036\\022\\213\\3378\\0022\\001\\001Z\\001a\\240\\001\\253\\345\\221\\203\\006\\212\\003\\01721bacb96ae78a72\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\213\\211\\222\\203\\006!\\205\\246\\302\\320\\343\\366UG2\\001\\001Z\\001a\\240\\001\\213\\211\\222\\203\\006\\212\\003\\01725bce83ed9020ac\\232\\n\\0231$##$1002$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\311\\233\\220\\203\\006!\\337\\276\\373\\235*\\237K\\0302\\001\\001Z\\001a\\240\\001\\311\\233\\220\\203\\006\\212\\003\\01728a0ca96b8b56c3\\232\\n\\0231$##$1007$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\315\\202\\217\\203\\006!e\\353\\371\\t\\247\\206}\\0372\\001\\001Z\\001a\\240\\001\\315\\202\\217\\203\\006\\212\\003\\0172ed86ac692fdee1\\232\\n\\0231$##$1002$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\335\\203\\217\\203\\006!~\\374+\\270\\304?\\017W2\\001\\001Z\\001a\\240\\001\\335\\203\\217\\203\\006\\212\\003\\0172ed86ac692fdee1\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\222\\224\\217\\203\\006!\\r\\237<\\261\\001\\201?\\'2\\001\\001Z\\001a\\240\\001\\222\\224\\217\\203\\006\\212\\003\\0172ed86ac692fdee1\\232\\n\\0231$##$1001$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\267\\261\\222\\203\\006!e\\t\\276$\\025`a\\0272\\001\\001Z\\001b\\240\\001\\267\\261\\222\\203\\006\\212\\003\\01730985df86b21b67\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\325\\270\\222\\203\\006!J\\274\\017\\323A\\3509\\0022\\001\\001Z\\001b\\240\\001\\325\\270\\222\\203\\006\\212\\003\\01730985df86b21b67\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\360\\270\\222\\203\\006!\\036GfC\\366\\220\\'\\r2\\001\\001Z\\001b\\240\\001\\360\\270\\222\\203\\006\\212\\003\\01730985df86b21b67\\232\\n\\0231$##$1000$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\207\\372\\217\\203\\006!\\374=c+\\323\\331\\025\\0162\\001\\001Z\\001a\\240\\001\\207\\372\\217\\203\\006\\212\\003\\017369da2888203e1e\\232\\n\\0231$##$1002$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\226\\372\\217\\203\\006!\\313\\322\\24794\\341\\304\\0322\\001\\001Z\\001a\\240\\001\\226\\372\\217\\203\\006\\212\\003\\017369da2888203e1e\\232\\n\\0231$##$1004$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\213\\351\\221\\203\\006!b\\234r\\324\\210\\256\\025\\0022\\001\\001Z\\001a\\240\\001\\213\\351\\221\\203\\006\\212\\003\\0173aab3d8116557fa\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\305\\335\\217\\203\\006!\\252\\357\\274\\025\\205\\3353e2\\001\\001Z\\001b\\240\\001\\305\\335\\217\\203\\006\\212\\003\\01747f669fd3349cc6\\232\\n\\0231$##$1002$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\322\\342\\217\\203\\006!~\\346\\220l_\\361\\266.2\\001\\001Z\\001b\\240\\001\\322\\342\\217\\203\\006\\212\\003\\01747f669fd3349cc6\\232\\n\\0231$##$1000$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\366\\263\\222\\203\\006!\\312\\263*l\\273r\\302y2\\001\\001Z\\001a\\240\\001\\366\\263\\222\\203\\006\\212\\003\\0176223504cb9aa333\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\216\\264\\222\\203\\006!\\223\\321\\260\\345\\242`a\\0272\\001\\001Z\\001a\\240\\001\\216\\264\\222\\203\\006\\212\\003\\0176223504cb9aa333\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\376\\350\\216\\203\\006!\\320\\224?\\232\\357\\347\\22632\\001\\001Z\\001a\\240\\001\\376\\350\\216\\203\\006\\212\\003\\01762a75ce41714473\\232\\n\\0231$##$1002$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\362\\353\\220\\203\\006!\\353\\363\\355\\243\\001\\357\\003#2\\001\\001Z\\001a\\240\\001\\362\\353\\220\\203\\006\\212\\003\\017673702cccb12d46\\232\\n\\0231$##$1001$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\200\\314\\220\\203\\006!\\026\\345\\223[$\\023\\244\\\\2\\001\\002Z\\001a\\240\\001\\200\\314\\220\\203\\006\\212\\003\\0176a32e345a4503a8\\232\\n\\0231$##$1002$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\246\\313\\220\\203\\006!\\246\\310\\205\\354U\\341\\333P2\\001\\001Z\\001a\\240\\001\\246\\313\\220\\203\\006\\212\\003\\0176a32e345a4503a8\\232\\n\\0231$##$1002$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\313\\276\\215\\203\\006!\\030\\030\\233%\\255\\252\\261A2\\001\\001Z\\001a\\240\\001\\313\\276\\215\\203\\006\\212\\003\\01771bd60bc5418391\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\324\\241\\220\\203\\006!K\\357\\275\\346%\\232uC2\\001\\001Z\\001b\\240\\001\\324\\241\\220\\203\\006\\212\\003\\017872ad0c97f0b04d\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\336\\242\\220\\203\\006!\\303,CQ\\302\\214\\222)2\\001\\001Z\\001b\\240\\001\\336\\242\\220\\203\\006\\212\\003\\017872ad0c97f0b04d\\232\\n\\0231$##$1000$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\227\\243\\220\\203\\006!\\361\\272\\277\\246K\\254K\\0302\\001\\001Z\\001b\\240\\001\\227\\243\\220\\203\\006\\212\\003\\017872ad0c97f0b04d\\232\\n\\0231$##$1000$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\270\\243\\220\\203\\006!\\3767\\254\\023\\265o\\n,2\\001\\001Z\\001b\\240\\001\\270\\243\\220\\203\\006\\212\\003\\017872ad0c97f0b04d\\232\\n\\0231$##$1000$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\362\\255\\220\\203\\006!\\321\\372\\321\\245T\\r\\325R2\\001\\001Z\\001b\\240\\001\\362\\255\\220\\203\\006\\212\\003\\017872ad0c97f0b04d\\232\\n\\0231$##$1000$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\243\\256\\220\\203\\006!\\252\\207l&\\024\\321\\270^2\\001\\001Z\\001b\\240\\001\\243\\256\\220\\203\\006\\212\\003\\017872ad0c97f0b04d\\232\\n\\0231$##$1000$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\267\\265\\220\\203\\006!\\025\\241U,\\212H\\214 2\\001\\001Z\\001b\\240\\001\\267\\265\\220\\203\\006\\212\\003\\017872ad0c97f0b04d\\232\\n\\0231$##$1004$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\337\\250\\217\\203\\006!\\355SRF\\327\\315\\323U2\\001\\001Z\\001b\\240\\001\\337\\250\\217\\203\\006\\212\\003\\017b5b5e702d95bdfb\\232\\n\\0231$##$1000$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\353\\211\\217\\203\\006!\\337\\301q\\264\\313W\\34222\\001\\001Z\\001b\\240\\001\\353\\211\\217\\203\\006\\212\\003\\017b5edbf560d2caa9\\232\\n\\0231$##$1001$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\272\\270\\220\\203\\006!\\271k\\225\\372\\001\\003\\232\\0132\\001\\001Z\\001a\\240\\001\\272\\270\\220\\203\\006\\212\\003\\017bd8be8b84a911ad\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\366\\270\\220\\203\\006!;\\216\\350ba\\273\\034z2\\001\\001Z\\001a\\240\\001\\366\\270\\220\\203\\006\\212\\003\\017bd8be8b84a911ad\\232\\n\\0231$##$1005$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\206\\224\\222\\203\\006!\\322\\277\\374\\271$}\\213c2\\001\\001Z\\001a\\240\\001\\206\\224\\222\\203\\006\\212\\003\\017bd8be8b84a911ad\\232\\n\\0231$##$1002$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\317\\227\\215\\203\\006!,\\273Sc\\263*\\210Y2\\001\\001Z\\001a\\240\\001\\317\\227\\215\\203\\006\\212\\003\\017ca6b013bfc776c3\\232\\n\\0231$##$1006$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\210\\332\\220\\203\\006!\\034 \\252e\\314\\251K\\0302\\001\\001Z\\001b\\240\\001\\210\\332\\220\\203\\006\\212\\003\\017f4ae638e6d1400e\\232\\n\\0231$##$1002$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\371\\211\\221\\203\\006!\\343$L+\\374\\265\\262i2\\001\\001Z\\001a\\240\\001\\371\\211\\221\\203\\006\\212\\003\\017f821dbfa85c83ed\\232\\n\\0231$##$1004$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\365\\225\\221\\203\\006!\\202\\353\\224\\226\\253V0&2\\001\\001Z\\001a\\240\\001\\365\\225\\221\\203\\006\\212\\003\\017f821dbfa85c83ed\\232\\n\\0231$##$1000$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\214\\226\\221\\203\\006!\\215\\017\\r\\216\\322eO\\\"2\\001\\001Z\\001a\\240\\001\\214\\226\\221\\203\\006\\212\\003\\017f821dbfa85c83ed\\232\\n\\0231$##$1000$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\252\\235\\221\\203\\006!T\\350\\307\\222\\227\\271f\\1772\\001\\001Z\\001a\\240\\001\\252\\235\\221\\203\\006\\212\\003\\017f821dbfa85c83ed\\232\\n\\0231$##$1006$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\241\\230\\217\\203\\006!\\320\\237-\\325UD/,2\\001\\001Z\\001a\\240\\001\\241\\230\\217\\203\\006\\212\\003\\017fc6289b84feeb5f\\232\\n\\0231$##$1001$##$1$##$1\"\n    }\n  }\n  feature {\n    bytes_list {\n      value: \"\\030\\210\\260\\217\\203\\006!\\323\\3127i@\\033\\315^2\\001\\001Z\\001a\\240\\001\\210\\260\\217\\203\\006\\212\\003\\017fc6289b84feeb5f\\232\\n\\0231$##$1001$##$1$##$1\"\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test26_int64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9201937275671233029\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9200913543142830201\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"__LABEL__\"\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n  feature {\n    float_list {\n      value: 0.0\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test23_map_float\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9801309575956515417\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9801309575956515417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9801309575956515417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9801309575956515417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9801309575956515417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9801309575956515417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9801309575956515417\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9801309575956515417\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_price_reduction\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3934388063731853951\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3930250596545429415\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_membership_level\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_platform\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test21_map_string\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9622951276949010286\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9622951276949010286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9622951276949010286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9622951276949010286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9622951276949010286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9622951276949010286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9622951276949010286\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9622951276949010286\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_network\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_praise_cnt\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_origin_price\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3875588474307037931\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3884996566135610121\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_rating\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_detail_pic_num\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test43_map_uint32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9560876433953954425\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9564567803863763281\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_country\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test16_array_double\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9916647352929422305\n      value: 9916647352929422305\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9916647352929422305\n      value: 9916647352929422305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9916647352929422305\n      value: 9916647352929422305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9916647352929422305\n      value: 9916647352929422305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9916647352929422305\n      value: 9916647352929422305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9916647352929422305\n      value: 9916647352929422305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9916647352929422305\n      value: 9916647352929422305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9916647352929422305\n      value: 9916647352929422305\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_id\"\n  feature {\n    fid_v1_list {\n      value: 3610133626909631996\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3604145606281754473\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3616415016801295334\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3619863857700495324\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3617246315459696229\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3614776421570243930\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3617016026378354835\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3619262297046717444\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3619249165061197326\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3618989404930912336\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3613697671503572881\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3603154114569699616\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3611397315614213708\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3614763883536608253\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3606701039490254176\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3620064348517401255\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3617870400400875595\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3604189663600640666\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3615867999634237576\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3620527882979129921\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3612210403309400165\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3619712610574498604\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3616268254331474538\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3610856047563518050\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3606887699004289777\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3610970158357720999\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3607339321121329188\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3603297871905146955\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3605886361555974207\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3611846377146202666\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3603259629256285404\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3607259625265071215\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3616775414741526157\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3603039257404856223\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3620296482659719726\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3611735379914375886\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3619698866543905695\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3611138543871412195\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3608086200946945886\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3608534566476548317\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3607157927903496194\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3603776236828038739\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3607177734847050046\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3610806719725808236\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3606611260239343721\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3604130491108528090\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3605348026796130101\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3616118806628872259\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3615956039523072078\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3616815934704003769\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 3611149856407841749\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_title\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3750933102327502546\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3760993937582903701\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_os\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_sale_number_1000\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4066070996392706623\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4054836228468787784\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_area\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test41_map_int32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9519402512114369756\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9523202332830430791\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test39_array_float\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9286143405097552865\n      value: 9286143405097552865\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9286143405097552865\n      value: 9286143405097552865\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test01_int32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 10008785078271377834\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 10008785078271377834\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 10008785078271377834\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 10008785078271377834\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 10008785078271377834\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 10008785078271377834\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 10008785078271377834\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 10008785078271377834\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_pub_time_month\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4214234648173981377\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4211846706978290518\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_comment_cnt_1000\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test46_map_bool\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9432690607210547955\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9438915382847444385\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_spm\"\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5406374454400203474\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5406374454400203474\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5409103793660641491\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5409103793660641491\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5417348509665164251\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5409103793660641491\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5406374454400203474\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5414531140030155305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5409103793660641491\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5419024638130229596\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5409103793660641491\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5414531140030155305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5409103793660641491\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5406374454400203474\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5409103793660641491\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5409103793660641491\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5414531140030155305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5414531140030155305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5414531140030155305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5414531140030155305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5414531140030155305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5419024638130229596\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5414531140030155305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5406374454400203474\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5416942885167263790\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5409103793660641491\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5404484209003362014\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5409103793660641491\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5419024638130229596\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5414531140030155305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5414531140030155305\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5404484209003362014\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5406374454400203474\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5406374454400203474\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_user_district\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test40_array_double\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9160042615531178977\n      value: 9160042615531178977\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9160042615531178977\n      value: 9160042615531178977\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_status\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3647636671629691873\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3647636671629691873\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_register_time_year\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 74831614955585623\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 79882321954458273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 79882321954458273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 79882321954458273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 79882321954458273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 79882321954458273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 79882321954458273\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 79882321954458273\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_title_terms\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3772834016398264993\n      value: 3765913701858879735\n      value: 3773000691200033589\n      value: 3781801790151204962\n      value: 3766856870087500518\n      value: 3775195877281589288\n      value: 3774546363060474958\n      value: 3767514185271515909\n      value: 3768887054169500879\n      value: 3769995601855538213\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3782553589976358690\n      value: 3774011077586830980\n      value: 3776247000003769261\n      value: 3781324026518362914\n      value: 3775919041219263551\n      value: 3765703767519208555\n      value: 3769675736649258697\n      value: 3776247000003769261\n      value: 3781324026518362914\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test19_map_uint32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9780740585977547089\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9780740585977547089\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9780740585977547089\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9780740585977547089\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9780740585977547089\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9780740585977547089\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9780740585977547089\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9780740585977547089\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_tags_terms\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_device_model\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_page\"\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5495766155220566385\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5495766155220566385\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505413516948704399\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505413516948704399\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505376789329979122\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505413516948704399\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5495766155220566385\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5504311390965651065\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505413516948704399\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5501101802138651045\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505413516948704399\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5504311390965651065\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505413516948704399\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5495766155220566385\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505413516948704399\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505413516948704399\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5504311390965651065\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5504311390965651065\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5504311390965651065\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5504311390965651065\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5504311390965651065\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5501101802138651045\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5504311390965651065\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5495766155220566385\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5496023425700513216\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505413516948704399\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5501142726425111231\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5505413516948704399\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5501101802138651045\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5504311390965651065\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5504311390965651065\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5501142726425111231\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5495766155220566385\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5495766155220566385\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_is_dup\"\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 5598068063666195856\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test04_uint64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9601674407701381112\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9601674407701381112\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9601674407701381112\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9601674407701381112\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9601674407701381112\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9601674407701381112\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9601674407701381112\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9601674407701381112\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test29_string\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9454213769273926101\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9453990683687577353\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_sale_number_10\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4048056597883224639\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4050207389405564160\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_gender\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_current_price_10\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3843523687113293978\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3847744272348507369\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test13_array_string\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9670163465801361161\n      value: 9670386551387709909\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9670163465801361161\n      value: 9670386551387709909\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9670163465801361161\n      value: 9670386551387709909\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9670163465801361161\n      value: 9670386551387709909\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9670163465801361161\n      value: 9670386551387709909\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9670163465801361161\n      value: 9670386551387709909\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9670163465801361161\n      value: 9670386551387709909\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9670163465801361161\n      value: 9670386551387709909\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_id-f_goods_cate_1\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9117240382873998683\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9119401595761469634\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_id-f_goods_cate_2\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9145018947826707613\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9145794885964584340\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_tags\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3793365034877822818\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3793205953451737140\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test22_map_bool\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9853246548565530017\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9853246548565530017\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9853246548565530017\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9853246548565530017\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9853246548565530017\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9853246548565530017\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9853246548565530017\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9853246548565530017\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_register_time_month\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 98148548915816378\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 100705584251763957\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 100705584251763957\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 100705584251763957\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 100705584251763957\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 100705584251763957\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 100705584251763957\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 100705584251763957\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_province\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_id-f_goods_brand\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9101553090662715167\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9111910659614948266\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_area\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test34_array_int64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9219951674180715013\n      value: 9205986166572396293\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9218927941652312185\n      value: 9219951674180715013\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_current_price_ratio\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3961537472356691931\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 3954672822003818443\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_praise_cnt_1000\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_ctx_platform\"\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946231517668508309\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 938505698453898612\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946817063302724633\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 946817063302724633\n    }\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test47_map_float\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9261723814926832396\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9260877620672055897\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_tags\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_province\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_test37_array_string\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9343519995220875194\n      value: 9333547367854110383\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9345904292630685449\n      value: 9346127378217034197\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test18_map_int64\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9695186693039768446\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9695186693039768446\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9695186693039768446\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9695186693039768446\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9695186693039768446\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9695186693039768446\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9695186693039768446\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9695186693039768446\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_user_test17_map_int32\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9865475904510588487\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 9865475904510588487\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9865475904510588487\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9865475904510588487\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9865475904510588487\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9865475904510588487\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9865475904510588487\n    }\n  }\n  feature {\n    fid_v1_list {\n      value: 9865475904510588487\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nnamed_feature_list {\n  name: \"f_goods_pub_time_year\"\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4187165182116350625\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n    fid_v1_list {\n      value: 4187165182116350625\n    }\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n  feature {\n  }\n}\nbatch_size: 51\n"
  },
  {
    "path": "monolith/agent_service/mocked_tfserving.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nfrom concurrent import futures\nfrom dataclasses import dataclass\nimport grpc\nfrom google.protobuf.any_pb2 import Any\nfrom queue import Queue\nimport random\nimport threading\nimport time\nfrom typing import List, Tuple, Union, Optional\n\nfrom google.protobuf import text_format\n\nfrom tensorflow_serving.apis.get_model_status_pb2 import GetModelStatusRequest, \\\n  GetModelStatusResponse\nfrom tensorflow_serving.apis.get_model_metadata_pb2 import GetModelMetadataRequest, \\\n  GetModelMetadataResponse\nfrom tensorflow_serving.apis.model_management_pb2 import ReloadConfigRequest, \\\n  ReloadConfigResponse\nfrom tensorflow_serving.apis.model_service_pb2_grpc import ModelServiceServicer\nfrom tensorflow_serving.apis.model_service_pb2_grpc import add_ModelServiceServicer_to_server\nfrom tensorflow_serving.apis.prediction_service_pb2_grpc import PredictionServiceServicer\nfrom tensorflow_serving.apis.prediction_service_pb2_grpc import add_PredictionServiceServicer_to_server\nfrom tensorflow_serving.config import model_server_config_pb2\n\n\n\nfrom monolith.agent_service.utils import gen_model_version_status, gen_status_proto, gen_model_config, \\\n  ModelState, ErrorCode\n\n\n@dataclass\nclass ModelConf:\n  model_name: str = None\n  base_path: str = None\n  version_policy: str = 'latest'\n  version_data: Union[int, List[int]] = None\n  model_platform: str = 'tensorflow'\n  signature_name: Tuple = ('update', 'predict')\n\n\n@dataclass\nclass ModelVersion:\n  version: int = 0\n  version_label: str = None\n  state: int = ModelState.UNKNOWN\n\n\nclass ModelMeta:\n\n  def __init__(self, conf: ModelConf, versions: List[ModelVersion] = None):\n    self.conf = conf\n    self.versions = versions or [ModelVersion()]\n    self._unloading = False\n\n  def is_unloading(self):\n    return self._unloading\n\n  def set_unloading(self):\n    self._unloading = True\n\n\n@dataclass\nclass Event:\n  model_name: str = None\n  version: int = 0\n  state: int = ModelState.UNKNOWN\n\n\nclass ModelMgr:\n\n  def __init__(self, model_config_list=None):\n    self._models = {}\n    self._lock = threading.Lock()\n    self._queue = Queue()\n    self._has_stopped = False\n    self._thread: Optional[threading.Thread] = None\n\n    if model_config_list is not None:\n      self.load(model_config_list)\n\n  def load(self, model_config_list):\n    for config in model_config_list:\n      if config.model_version_policy.HasField('latest'):\n        version_policy = 'latest'\n        version_data = config.model_version_policy.latest.num_versions\n        versions = [ModelVersion(i + 1) for i in range(version_data)]\n      elif config.model_version_policy.HasField('all'):\n        version_policy = 'latest'\n        version_data = None\n        versions = [ModelVersion(1)]\n      else:\n        version_policy = 'specific'\n        version_data = config.model_version_policy.specific.versions\n        version_data.sort()\n        versions = [ModelVersion(i) for i in version_data]\n\n      model_conf = ModelConf(config.name,\n                             config.base_path,\n                             version_policy=version_policy,\n                             version_data=version_data)\n      self._models[config.name] = ModelMeta(model_conf, versions)\n\n      logging.info('start load a new model {}'.format(config.name))\n      for v in versions:\n        self._queue.put(\n            Event(model_name=config.name,\n                  version=v.version,\n                  state=ModelState.START))\n\n  def remove(self, model_name_list):\n    for model_name in model_name_list:\n      model: ModelMeta = self._models[model_name]\n      model.set_unloading()\n      logging.info('start remove the model {}'.format(model_name))\n      for version in model.versions:\n        self._queue.put(Event(model_name, version.version,\n                              ModelState.UNLOADING))\n\n  def get_status(self, model_spec):\n    model_version_status = []\n    with self._lock:\n      if model_spec.name in self._models:\n        model_meta: ModelMeta = self._models[model_spec.name]\n        if model_spec.WhichOneof('version_choice') is None:\n          for version in model_meta.versions:\n            mvs = gen_model_version_status(version.version, version.state)\n            model_version_status.append(mvs)\n        else:\n          if model_spec.HasField('version'):\n            value = model_spec.version.value\n            for version in model_meta.versions:\n              if version.version == value:\n                mvs = gen_model_version_status(version.version, version.state)\n                model_version_status.append(mvs)\n                break\n          else:\n            value = model_spec.version_label\n            for version in model_meta.versions:\n              if version.version_label == value:\n                mvs = gen_model_version_status(version.version, version.state)\n                model_version_status.append(mvs)\n                break\n\n      if len(model_version_status) == 0:\n        mvs = gen_model_version_status(\n            -1,\n            error_code=ErrorCode.NOT_FOUND,\n            error_message=f'{model_spec.name} is not found')\n        model_version_status.append(mvs)\n\n    return model_version_status\n\n  def get_metadata(self, model_spec, metadata_field):\n    metadata = {}\n    if metadata_field is not None and len(metadata_field) > 0:\n      with self._lock:\n        model_meta: ModelMeta = self._models[model_spec.name]\n        conf = model_meta.conf\n        for field in metadata_field:\n          if hasattr(conf, field):\n            metadata[field] = getattr(conf, field)\n\n        if model_spec.HasField('version'):\n          version = model_spec.version.value\n          for v in model_meta.versions:\n            if v.version == version:\n              for field in metadata_field:\n                if hasattr(v, field):\n                  metadata[field] = getattr(v, field)\n              break\n\n    return metadata\n\n  def get_alive_model_names(self):\n    with self._lock:\n      return {k for k, v in self._models.items() if not v.is_unloading()}\n\n  def start(self):\n    self._thread = threading.Thread(target=self._poll,)\n    self._thread.start()\n\n  def stop(self):\n    self._has_stopped = True\n    if self._thread is not None:\n      self._thread.join()\n      self._thread = None\n\n  def _poll(self):\n    start_time = time.time()\n    while not self._has_stopped:\n      if not self._queue.empty():\n        event = self._queue.get()\n        self._event_handler(event)\n\n      end_time = time.time()\n      if end_time - start_time > 30:\n        start_time = end_time\n        model_names = list(self._models.keys())\n        if len(model_names) == 0:\n          continue\n        model_name = random.choice(list(self._models.keys()))\n        model_conf: ModelConf = self._models[model_name].conf\n        if model_conf.version_policy != 'specific':\n          versions = self._models[model_name].versions\n          version = versions[-1].version + 1\n          versions.append(ModelVersion(version))\n          logging.info(\n              'start load a new version of model {}'.format(model_name))\n          self._queue.put(Event(model_name, version, ModelState.START))\n\n      # time.sleep(random.uniform(0, 0.1))\n\n  def _event_handler(self, event: Event):\n    with self._lock:\n      model: ModelMeta = self._models.get(event.model_name, None)\n      if model is None:\n        logging.error(f'{event.model_name} has removed!')\n        return\n\n      log_flag = False\n      if event.state == ModelState.START:\n        for version in model.versions:\n          if version.version == event.version:\n            if version.state == ModelState.UNKNOWN:\n              version.state = event.state\n              self._queue.put(\n                  Event(event.model_name, event.version, ModelState.LOADING))\n              log_flag = True\n            break\n      elif event.state == ModelState.LOADING:\n        for version in model.versions:\n          if version.version == event.version:\n            if version.state == ModelState.START:\n              version.state = event.state\n              self._queue.put(\n                  Event(event.model_name, event.version, ModelState.AVAILABLE))\n              log_flag = True\n            break\n      elif event.state == ModelState.AVAILABLE:\n        for version in model.versions:\n          if version.version == event.version:\n            if version.state == ModelState.LOADING:\n              version.state = event.state\n              log_flag = True\n\n              if model.conf.version_policy == 'latest':\n                if len(model.versions) > model.conf.version_data:\n                  self._queue.put(\n                      Event(event.model_name, model.versions[0].version,\n                            ModelState.UNLOADING))\n            break\n      elif event.state == ModelState.UNLOADING:\n        for version in model.versions:\n          if version.version == event.version:\n            # in case unloading in unloading\n            if version.state not in {ModelState.UNLOADING, ModelState.END}:\n              version.state = event.state\n              self._queue.put(\n                  Event(event.model_name, event.version, ModelState.END))\n              log_flag = True\n            break\n      elif event.state == ModelState.END:\n        index = -1\n        for i, version in enumerate(model.versions):\n          if version.version == event.version:\n            if version.state == ModelState.UNLOADING:\n              version.state = event.state\n              logging.info(\n                  f'{event.model_name}-{event.version}: state is {ModelState.Name(event.state)}'\n              )\n            index = i\n            break\n\n        if index >= 0:\n          logging.info(\n              f'{event.model_name}-{model.versions[index].version} is removed!')\n          del model.versions[index]\n\n        if len(model.versions) == 0:\n          logging.info(f'{event.model_name} is removed!')\n          del self._models[event.model_name]\n      else:\n        logging.error('unknown event')\n\n      if log_flag:\n        logging.info(\n            f'{event.model_name}-{event.version}: state is {event.state}')\n\n\nclass ModelServiceImpl(ModelServiceServicer):\n\n  def __init__(self, model_mgr: ModelMgr):\n    self._model_mgr = model_mgr\n\n  def GetModelStatus(self, request: GetModelStatusRequest, context):\n    response = GetModelStatusResponse()\n    model_version_status = self._model_mgr.get_status(request.model_spec)\n    response.model_version_status.extend(model_version_status)\n    return response\n\n  def HandleReloadConfigRequest(self, request: ReloadConfigRequest, context):\n    model_config_list = request.config.model_config_list.config\n\n    old_names = self._model_mgr.get_alive_model_names()\n    new_names = {config.name for config in model_config_list}\n\n    to_remove = old_names - new_names\n    self._model_mgr.remove(to_remove)\n\n    to_load = new_names - old_names\n    self._model_mgr.load(\n        [config for config in model_config_list if config.name in to_load])\n\n    response = ReloadConfigResponse()\n    response.status.CopyFrom(gen_status_proto())\n    return response\n\n\nclass PredictionServiceImpl(PredictionServiceServicer):\n\n  def __init__(self, model_mgr: ModelMgr):\n    self._model_mgr = model_mgr\n\n  def Predict(self, request, context):\n    pass\n\n  def GetModelMetadata(self, request: GetModelMetadataRequest, context):\n    model_spec = request.model_spec\n    metadata_field = set(request.metadata_field)\n\n    response = GetModelMetadataResponse()\n    response.model_spec.CopyFrom(model_spec)\n    metadata = self._model_mgr.get_metadata(model_spec, metadata_field)\n    for k, v in metadata.items():\n      value = bytes(repr(v), encoding='utf-8')\n      response.metadata[k].CopyFrom(Any(value=value))\n\n    return response\n\n\nclass FakeTFServing:\n\n  def __init__(self,\n               model_name: str = None,\n               base_path: str = None,\n               num_versions: int = 1,\n               port: int = 8500,\n               max_workers: int = 10,\n               model_config_file=None):\n    if model_config_file is None:\n      self._model_mgr = ModelMgr(\n          [gen_model_config(model_name, base_path, version_data=num_versions)])\n    elif isinstance(model_config_file, str):\n      msc = model_server_config_pb2.ModelServerConfig()\n      with open(model_config_file, 'r') as fp:\n        text = ''.join(fp.readlines())\n        text_format.Parse(text, msc)\n      self._model_mgr = ModelMgr(msc.model_config_list.config)\n    else:\n      assert isinstance(model_config_file,\n                        model_server_config_pb2.ModelServerConfig)\n      self._model_mgr = ModelMgr(model_config_file.model_config_list.config)\n\n    self._server = grpc.server(\n        futures.ThreadPoolExecutor(max_workers=max_workers))\n    add_ModelServiceServicer_to_server(ModelServiceImpl(self._model_mgr),\n                                       self._server)\n    add_PredictionServiceServicer_to_server(\n        PredictionServiceImpl(self._model_mgr), self._server)\n    self._server.add_insecure_port(f'[::]:{port}')\n\n  def start(self):\n    self._model_mgr.start()\n    self._server.start()\n    self._server.wait_for_termination()\n\n  def stop(self, grace=None):\n    self._server.stop(grace=grace)\n    self._model_mgr.stop()\n\n\nif __name__ == '__main__':\n  tfs = FakeTFServing('model_test', '/tmp/model/monolith', num_versions=1)\n  tfs.start()\n"
  },
  {
    "path": "monolith/agent_service/mocked_tfserving_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport grpc\nimport socket\nimport time\nimport threading\nimport unittest\n\nfrom tensorflow_serving.apis.get_model_metadata_pb2 import GetModelMetadataRequest, \\\n  GetModelMetadataResponse\nfrom tensorflow_serving.apis.get_model_status_pb2 import GetModelStatusRequest, \\\n  GetModelStatusResponse\nfrom tensorflow_serving.apis.model_management_pb2 import ReloadConfigRequest, \\\n  ReloadConfigResponse\nfrom tensorflow_serving.apis.model_service_pb2_grpc import ModelServiceStub\nfrom tensorflow_serving.apis.prediction_service_pb2_grpc import PredictionServiceStub\n\nfrom monolith.agent_service import utils\nfrom monolith.agent_service.mocked_tfserving import FakeTFServing\n\nMODEL_NAME = 'test_model_test'\nBASE_PATH = '/tmp/test_model/monolith'\nPORT = utils.find_free_port()\n\nAddress = f'{socket.gethostbyname(socket.gethostname())}:{PORT}'\n\n\nclass MockedTFSTest(unittest.TestCase):\n  tfs: FakeTFServing = None\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    cls.tfs = FakeTFServing(MODEL_NAME, BASE_PATH, num_versions=2, port=PORT)\n    # cls.tfs.start()\n    thread = threading.Thread(target=lambda: cls.tfs.start())\n    thread.start()\n    time.sleep(5)\n\n  @classmethod\n  def tearDownClass(cls) -> None:\n    cls.tfs.stop()\n\n  def test_get_model_metadata(self):\n    request = GetModelMetadataRequest()\n    request.model_spec.CopyFrom(\n        utils.gen_model_spec(MODEL_NAME, 2, signature_name='predict'))\n    request.metadata_field.extend(\n        ['base_path', 'num_versions', 'signature_name'])\n\n    stub = PredictionServiceStub(grpc.insecure_channel(Address))\n    self.assertTrue(\n        isinstance(stub.GetModelMetadata(request), GetModelMetadataResponse))\n\n  def test_get_model_status(self):\n    stub = ModelServiceStub(grpc.insecure_channel(Address))\n    request = GetModelStatusRequest()\n    request.model_spec.CopyFrom(\n        utils.gen_model_spec(MODEL_NAME, 1, signature_name='predict'))\n    self.assertTrue(\n        isinstance(stub.GetModelStatus(request), GetModelStatusResponse))\n\n  def test_handle_reload_config_request(self):\n    stub = ModelServiceStub(grpc.insecure_channel(Address))\n    request = ReloadConfigRequest()\n    model_config_list = request.config.model_config_list.config\n    model_config_list.extend([\n        utils.gen_model_config(name='test_model',\n                               base_path='/tmp/test_model/ctr/saved_model',\n                               version_data=2),\n        utils.gen_model_config(name='test_model',\n                               base_path='/tmp/test_model/cvr/saved_model',\n                               version_data=1),\n    ])\n    self.assertTrue(\n        isinstance(stub.HandleReloadConfigRequest(request),\n                   ReloadConfigResponse))\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/agent_service/mocked_zkclient.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nfrom functools import partial\nfrom kazoo.protocol.states import ZnodeStat, WatchedEvent, EventType, KeeperState\nfrom kazoo.exceptions import NoNodeError, NodeExistsError, NotEmptyError, CancelledError\nimport os\nimport time\nfrom threading import Lock\nfrom typing import List, Dict, Union, Callable, Optional\n\n\nclass ChildrenWatch:\n\n  def __init__(self,\n               client,\n               path: str,\n               func: Union[Callable[[List[str]], None],\n                           Callable[[List[str], WatchedEvent], None]],\n               send_event=False):\n    self.path = path\n    self.send_event = send_event\n    self._stopped = False\n    self._func = func\n    catalog = client._catalog\n    catalog.add_children_watch(self)\n\n  def __call__(self, children: List[str], event: WatchedEvent):\n    if self.send_event:\n      self._func(children, event)\n    else:\n      self._func(children)\n\n\nclass DataWatch:\n\n  def __init__(self, client, path: str,\n               func: Union[Callable[[bytes, ZnodeStat, WatchedEvent], None],\n                           Callable[[bytes, ZnodeStat], None]]):\n    self.path = path\n    self._func = func\n    catalog = client._catalog\n    catalog.add_data_watch(self)\n\n  def __call__(self, data: bytes, state: ZnodeStat, event: WatchedEvent):\n    try:\n      self._func(data, state, event)\n    except TypeError:\n      self._func(data, state)\n\n\nclass Election(object):\n\n  def __init__(self, client, path, identifier=None):\n    self.lock = Lock()\n\n  def run(self, func, *args, **kwargs):\n    if not callable(func):\n      raise ValueError(\"leader function is not callable\")\n\n    try:\n      with self.lock:\n        func(*args, **kwargs)\n    except CancelledError:\n      pass\n\n  def cancel(self):\n    self.lock.cancel()\n\n\nclass Node:\n\n  def __init__(self,\n               path: str,\n               value: bytes = b'',\n               ephemeral: bool = False,\n               data_watch: DataWatch = None,\n               children_watch: ChildrenWatch = None):\n    self.path: str = path\n    self.value: bytes = value\n    self.ephemeral: bool = ephemeral\n    self.children: Dict[str, Node] = {}\n\n    self._ctime = int(time.time())\n    self._mtime = int(time.time())\n    self._version = 0\n\n    self._data_watch = data_watch\n    self._children_watch = children_watch\n\n    event = WatchedEvent(type=EventType.CREATED,\n                         state=KeeperState.CONNECTED,\n                         path=self.path)\n    if self._data_watch is not None:\n      self._data_watch(self.value, self.state, event)\n    if self._children_watch is not None:\n      self._children_watch([], event)\n\n  @property\n  def state(self):\n    return ZnodeStat(czxid=0,\n                     mzxid=0,\n                     ctime=self._ctime,\n                     mtime=self._mtime,\n                     version=self._version,\n                     cversion=0,\n                     aversion=0,\n                     ephemeralOwner=0,\n                     dataLength=len(self.value),\n                     numChildren=len(self.children),\n                     pzxid=0)\n\n  @property\n  def basename(self):\n    return os.path.basename(self.path)\n\n  def set(self, value: bytes):\n    self._mtime = int(time.time())\n    self._version += 1\n    self.value = value\n\n    if self._data_watch is not None:\n      event = WatchedEvent(type=EventType.CHANGED,\n                           state=KeeperState.CONNECTED,\n                           path=self.path)\n      self._data_watch(self.value, self.state, event)\n\n  def get(self):\n    return self.value\n\n  def set_data_watch(self, watch: DataWatch):\n    self._data_watch = watch\n    self._data_watch(self.value, self.state, None)\n\n  def set_children_watch(self, watch: ChildrenWatch):\n    self._children_watch = watch\n    self._children_watch(list(self.children.keys()), None)\n\n  def create_child(self,\n                   path: str,\n                   value: bytes = b'',\n                   ephemeral: bool = False,\n                   data_watch=None,\n                   children_watch=None):\n    basename = os.path.basename(path)\n    self._mtime = int(time.time())\n    if self.path == os.path.sep:  # root\n      child_path = f'{os.path.sep}{basename}'\n    else:\n      child_path = f'{self.path}{os.path.sep}{basename}'\n    node = Node(child_path, value, ephemeral, data_watch, children_watch)\n    self.children[basename] = node\n\n    if self._children_watch is not None:\n      event = WatchedEvent(type=EventType.CHILD,\n                           state=KeeperState.CONNECTED,\n                           path=self.path)\n      self._children_watch(list(self.children.keys()), event)\n\n    return node\n\n  def get_or_create_child(self, path):\n    name = os.path.basename(path)\n    if name in self.children:\n      return self.children[name]\n    else:\n      return self.create_child(path)\n\n  def get_child(self, path):\n    return self.children.get(os.path.basename(path), None)\n\n  def has_child(self, path=None):\n    if path is None:\n      return len(self.children) > 0\n    else:\n      return os.path.basename(path) in self.children\n\n  def remove_child(self, path, recursive: bool = False):\n    if self.has_child(path):\n      self._mtime = int(time.time())\n      node = self.children[os.path.basename(path)]\n      if not recursive and node.has_child():\n        raise NotEmptyError(f'{path} is not empty!')\n      del self.children[os.path.basename(path)]\n\n      if self._children_watch is not None:\n        event = WatchedEvent(type=EventType.CHILD,\n                             state=KeeperState.CONNECTED,\n                             path=self.path)\n        self._children_watch(list(self.children.keys()), event=event)\n    else:\n      raise NoNodeError(f'{path} is not exists!')\n\n  def __del__(self):\n    event = WatchedEvent(type=EventType.DELETED,\n                         state=KeeperState.CONNECTED,\n                         path=self.path)\n    if self._data_watch is not None:\n      self._data_watch(self.value, self.state, event)\n      self._data_watch = None\n\n    for child in list(self.children.keys()):\n      del self.children[child]\n\n    if self._children_watch is not None:\n      self._children_watch(list(self.children.keys()), event=event)\n      self._children_watch = None\n\n    del self.path, self.value, self.ephemeral, self._ctime, self._mtime, self._version\n    del self._data_watch, self._children_watch, self.children\n\n\nclass Catalog:\n\n  def __init__(self):\n    self.root = Node(os.path.sep)\n    self._data_watches = {}\n    self._children_watches = {}\n    self._sequence_paths = {}\n\n  def add_data_watch(self, watch: DataWatch):\n    self._data_watches[watch.path] = watch\n    try:\n      node = self.get(watch.path)\n      node.set_data_watch(watch)\n    except Exception:\n      pass\n\n  def add_children_watch(self, watch: ChildrenWatch):\n    self._children_watches[watch.path] = watch\n    try:\n      node = self.get(watch.path)\n      node.set_children_watch(watch)\n    except Exception as e:\n      pass\n\n  def ensure_path(self, path: str) -> Node:\n    items = [item for item in path.split(os.path.sep) if len(item) > 0]\n    cpath, cnode = '', self.root\n    for item in items:\n      cpath = f'{cpath}{os.path.sep}{item}'\n      cnode = cnode.get_or_create_child(cpath)\n      if cnode.path in self._data_watches and cnode._data_watch is None:\n        cnode._data_watch = self._data_watches[cnode.path]\n      if cnode.path in self._children_watches and cnode._children_watch is None:\n        cnode._children_watch = self._children_watches[cnode.path]\n\n    return cnode\n\n  def create(self,\n             path: str,\n             value: bytes = b'',\n             ephemeral: bool = False,\n             makepath: bool = False,\n             sequence: bool = False):\n    if sequence:\n      if path in self._sequence_paths:\n        self._sequence_paths[path] += 1\n        path = f'{path}{self._sequence_paths[path]:010d}'\n      else:\n        self._sequence_paths[path] = 0\n        path = f'{path}{0:010d}'\n\n    dirname = os.path.dirname(path)\n    if makepath:\n      pnode = self.ensure_path(dirname)\n    else:\n      pnode = self.get(dirname)\n\n    if pnode.has_child(path):\n      raise NodeExistsError(f'{path} Exists!')\n    else:\n      data_watch = self._data_watches.get(path, None)\n      children_watch = self._children_watches.get(path, None)\n      return pnode.create_child(path, value, ephemeral, data_watch,\n                                children_watch)\n\n  def delete(self, path: str, recursive: bool = False):\n    dirname = os.path.dirname(path)\n    pnode = self.get(dirname)\n    pnode.remove_child(path, recursive)\n\n  def set(self, path: str, value: bytes):\n    self.get(path).set(value)\n\n  def get(self, path: str) -> Node:\n    items = [item for item in path.split(os.path.sep) if len(item) > 0]\n    cpath, cnode = '', self.root\n    for item in items:\n      cpath = f'{cpath}{os.path.sep}{item}'\n      cnode = cnode.get_child(cpath)\n      if cnode is None:\n        raise NoNodeError(f'{path} is not exists!')\n\n    return cnode\n\n\nclass FakeKazooClient:\n\n  def __init__(self, zk_server: str = None):\n    self._zk_server = zk_server\n    self._catalog: Optional[Catalog] = None\n\n    self.DataWatch = partial(DataWatch, self)\n    self.ChildrenWatch = partial(ChildrenWatch, self)\n    self.Election = partial(Election, self)\n\n  def ensure_path(self, path: str):\n    self._catalog.ensure_path(path)\n\n  def start(self):\n    self._catalog = Catalog()\n\n  def create(self,\n             path: str,\n             value: bytes = b'',\n             acl=None,\n             ephemeral: bool = False,\n             makepath: bool = False,\n             include_data: bool = False,\n             sequence: bool = False):\n    node = self._catalog.create(path, value, ephemeral, makepath, sequence)\n\n    if include_data:\n      return node.path, node.state\n    else:\n      return node.path\n\n  def delete(self, path: str, recursive: bool = True):\n    self._catalog.delete(path, recursive)\n\n  def set(self, path: str, value: bytes):\n    self._catalog.set(path, value)\n\n  def get(self, path: str):\n    node = self._catalog.get(path)\n    return node.value, node.state\n\n  def exists(self, path: str):\n    try:\n      node = self._catalog.get(path)\n      return True\n    except NoNodeError as e:\n      return False\n\n  def get_children(self, path: str, include_data=False):\n    node = self._catalog.get(path)\n    children = list(node.children.keys())\n    if include_data:\n      return children, node.state\n    else:\n      return children\n\n  def retry(self, func, *args, **kwargs):\n    return func(*args, **kwargs)\n\n  def stop(self):\n    self._catalog = None\n\n  def close(self):\n    if self._catalog is not None:\n      self.stop()\n\n  def add_listener(self, listener):\n    pass\n"
  },
  {
    "path": "monolith/agent_service/mocked_zkclient_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nfrom kazoo.exceptions import NoNodeError, NodeExistsError\n\nimport unittest\n\nfrom monolith.agent_service.mocked_zkclient import FakeKazooClient\n\n\nclass MockedZKClientTest(unittest.TestCase):\n  client = None\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    cls.client = FakeKazooClient()\n    cls.client.start()\n\n  @classmethod\n  def tearDownClass(cls) -> None:\n    cls.client.stop()\n\n  def test_create(self):\n    path = '/monolith/zk/data'\n    try:\n      real_path = self.client.create(path, makepath=True)\n      self.assertEqual(real_path, path)\n    except NoNodeError as e:\n      logging.info(f'{e}')\n    except NodeExistsError as e:\n      logging.info(f'{e}')\n\n  def test_set_get(self):\n    path = '/monolith/zk/data'\n    data = b'hi, I am Fitz!'\n    try:\n      real_path, state = self.client.create(path,\n                                            makepath=True,\n                                            include_data=True)\n      self.assertEqual(real_path, path)\n    except NoNodeError as e:\n      logging.info(f'{e}')\n    except NodeExistsError as e:\n      logging.info(f'{e}')\n\n    not_exists_path = f\"{path}/error\"\n    try:\n      self.client.set(not_exists_path, data)\n    except NoNodeError as e:\n      logging.error(f'{e}')\n\n    self.client.set(path, b'hi, I am Fitz!')\n\n    try:\n      gdata, _ = self.client.get(not_exists_path)\n      self.assertEqual(gdata, data)\n    except NoNodeError as e:\n      logging.error(f'{e}')\n\n    gdata, state = self.client.get(path)\n    self.assertEqual(gdata, data)\n\n  def test_delete(self):\n    path = '/monolith/zk/data'\n    try:\n      real_path = self.client.create(path, makepath=True)\n      self.assertEqual(real_path, path)\n    except NoNodeError as e:\n      logging.info(f'{e}')\n    except NodeExistsError as e:\n      logging.info(f'{e}')\n\n    self.client.delete(path)\n    self.client.delete('/monolith')\n\n  def test_data_watch(self):\n    path = '/monolith/zk/data'\n    try:\n      real_path = self.client.create(path, makepath=True)\n      self.assertEqual(real_path, path)\n    except NoNodeError as e:\n      logging.info(f'{e}')\n    except NodeExistsError as e:\n      logging.info(f'{e}')\n\n    def data_watch(data, state, event):\n      print('data_watch', data, state, event)\n\n    self.client.DataWatch(path=path, func=data_watch)\n\n  def test_children_watch(self):\n    path = '/monolith/zk/data'\n\n    def children_watch(children, event):\n      print('children_watch', children, event)\n\n    self.client.ChildrenWatch(path='/monolith/zk',\n                              func=children_watch,\n                              send_event=True)\n\n    try:\n      real_path = self.client.create(path, makepath=True)\n      self.assertEqual(real_path, path)\n    except NoNodeError as e:\n      logging.info(f'{e}')\n    except NodeExistsError as e:\n      logging.info(f'{e}')\n\n    def data_watch(data, state, event):\n      print('data_watch', data, state, event)\n\n    self.client.DataWatch(path=path, func=data_watch)\n\n    self.client.create('/monolith/zk/test', b'123')\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/agent_service/model_manager.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport sys\nimport os\nimport threading\nimport time\nimport shutil\nimport logging\nfrom monolith.native_training.metric import cli\n\n#from absl import logging\n\n\n# copy latest model from source path(p2p path) to receive path(model path)\nclass ModelManager(object):\n\n  WRITE_DONE = '.write.done'\n  READ_LOCK = '.read.lock'\n\n  def __init__(self, model_name, source_path, receive_path, use_metrics):\n    self._worker = None\n    self._source_path = source_path\n    self._receive_path = receive_path\n    self._model_name = model_name\n    self._models = {}  # model_name to version list\n    self._latest_models = {}\n    self._wait_timeout = 1200\n    self._loop_thread = None\n    self._loop_interval = 30\n    self._exist = False\n    self._remain_version_num = 5\n    self._lock_files = set()\n    self._use_metrics = use_metrics\n    self._metrics = None\n\n    if self._model_name and self._use_metrics:\n      self.init_metrics()\n\n  def init_metrics(self):\n    self._metrics = cli.get_cli('data.monolith_serving.online')\n\n  def stop(self):\n    self._exist = True\n    if self._loop_thread:\n      self._loop_thread.join()\n\n  def start(self):\n    ret = False\n    try:\n      ret = self._start()\n    except Exception as e:\n      logging.error('model manager start failed: %s', str(e))\n      ret = False\n    return ret\n\n  def _start(self):\n    if self._model_name is None:\n      logging.info('ModelManager is not needed')\n      return True\n\n    # delete receive path first\n    if not self.delete(self._receive_path):\n      return False\n\n    # wait for the source path\n    if not self.wait_for_download():\n      return False\n\n    # do loop once to copy model\n    while True:\n      try:\n        if self.loop_once():\n          break\n      except BaseException as err:\n        logging.error('model manager loop once failed: %s', str(err))\n\n      logging.info('loop once failed, wait for ready model')\n      time.sleep(10)\n\n    self.remove_read_lock()\n    self._loop_thread = threading.Thread(target=self.run,\n                                         name=\"thread-model_manager\")\n    self._loop_thread.start()\n\n    return True\n\n  def touch(self, file):\n    try:\n      f = open(file, 'w+')\n      f.close()\n      return True\n    except BaseException:\n      pass\n    return False\n\n  def run(self):\n    while not self._exist:\n      try:\n        ret = self.loop_once()\n        self.remove_read_lock()\n        if not ret:\n          logging.error('model manager loop once failed')\n      except BaseException as err:\n        logging.error('model manager loop once failed: %s', str(err))\n\n      if self._use_metrics:\n        self.check_model_update_time()\n      time.sleep(self._loop_interval)\n      self.remove_old_file()\n\n  def check_model_update_time(self):\n    if not self._metrics:\n      return\n\n    if self._model_name not in self._latest_models:\n      logging.error('model %s not in _latest_models: %s', self._model_name,\n                    str(self._latest_models))\n      self._metrics.emit_counter('loop_once_failed',\n                                 1,\n                                 tagkv={'model': self._model_name})\n      return\n    version, update_time = self._latest_models[self._model_name]\n    cur_time = int(time.time())\n    self._metrics.emit_store('version.delay',\n                             cur_time - int(version),\n                             tagkv={'model': self._model_name})\n    self._metrics.emit_store('update.delay',\n                             cur_time - update_time,\n                             tagkv={'model': self._model_name})\n\n  def remove_old_file(self):\n    for model_name in self._models:\n      model_files_list = self._models[model_name]\n      if len(model_files_list) > self._remain_version_num:\n        old_files = model_files_list.pop(0)\n        for old_file in old_files[1]:\n          self.delete(old_file)\n\n  def create_read_lock(self, name):\n    lock_name = name + self.READ_LOCK\n    if self.touch(lock_name):\n      return lock_name\n    else:\n      logging.error(\"create lock %s failed\", lock_name)\n      return lock_name\n\n  def remove_read_lock(self):\n    for lock_file in self._lock_files:\n      self.delete(lock_file)\n    self._lock_files.clear()\n\n    # remove other lock\n    ret = list(os.walk(self._source_path))\n    if len(ret) == 0:\n      return\n\n    root, dirs, files = ret[0]\n\n    for file in files:\n      if file.endswith(self.READ_LOCK):\n        completed_name = os.join(root, file)\n        logging.info('delete lock file: %s', completed_name)\n        self.delete(completed_name)\n\n  def loop_once(self):\n    source_data = {}\n    result = True\n    try:\n      source_data = self.get_source_data()\n    except BaseException as err:\n      logging.error('get download data failed: %s', str(err))\n      return False\n    for model_name in source_data:\n      new_version = source_data[model_name][0]\n      if model_name in self._models and len(self._models[model_name]) > 0:\n        old_version = self._models[model_name][-1][0]\n        if old_version >= new_version:\n          continue\n\n      ret, file_list = self.copy_model(model_name, new_version,\n                                       source_data[model_name][1])\n      if ret:\n        if model_name not in self._models:\n          self._models[model_name] = []\n\n        self._models[model_name].append((new_version, file_list))\n        cur_time = int(time.time())\n        self._latest_models[model_name] = (new_version, cur_time)\n        logging.info(f'{model_name} update to {new_version}')\n      else:\n        logging.error(f'copy {model_name} failed')\n        result = False\n\n    return result\n\n  def copy_model(self, model_name, version, model_data):\n    sub_model_num = len(model_data)\n    ready_data = []\n    result = []\n    ready_num = 0\n    for sub_model_name, sub_model_data in model_data:\n      # sub_model_name: ps_0/version\n      # sub_model_data: /xxx/model_name@version/ps_0/version\n      try:\n        src_file = sub_model_data\n        dst_file = os.path.join(self._receive_path, model_name, sub_model_name)\n        temp_dst_file = dst_file + '-temp'\n\n        result.append(dst_file)\n\n        if os.path.exists(dst_file):\n          logging.error(f'{dst_file} exist')\n          ready_num += 1\n          continue\n\n        if os.path.exists(temp_dst_file):\n          logging.error(f'{temp_dst_file} exist')\n          ready_num += 1\n          ready_data.append((temp_dst_file, dst_file))\n          continue\n\n        shutil.copytree(src_file, temp_dst_file)\n        ready_data.append((temp_dst_file, dst_file))\n        ready_num += 1\n      except BaseException as err:\n        logging.error('copy model %s -> %s faild: %s', src_file, temp_dst_file,\n                      str(err))\n        self.delete(temp_dst_file)\n        break\n\n    if ready_num != sub_model_num:\n      logging.error(\n          f'copy model faild, ready_num={ready_num}, expect_num={sub_model_num}'\n      )\n      for data in ready_data:\n        self.delete(data[0])\n      return False, []\n\n    for data in ready_data:\n      os.rename(data[0], data[1])\n\n    return True, result\n\n  def wait_for_download(self):\n    duartion = 0\n    download_path_ready = os.path.exists(self._source_path)\n\n    while not download_path_ready and duartion < self._wait_timeout:\n      logging.info(f'wait {self._source_path} created')\n      time.sleep(10)\n      duartion += 10\n      download_path_ready = os.path.exists(self._source_path)\n\n    if not download_path_ready:\n      logging.error(f'{self._source_path} is not ready')\n      return False\n\n    while duartion < self._wait_timeout:\n      ret = list(os.walk(self._source_path))\n\n      if len(ret) > 0:\n        root, dirs, files = ret[0]\n\n        for file in files:\n          if file.endswith(self.WRITE_DONE) and file.startswith(\n              self._model_name):\n            logging.info(f'{file} is ready')\n            return True\n\n      logging.info('no ready model found')\n      time.sleep(10)\n      duartion += 10\n    logging.error('no ready model found')\n    return False\n\n  def get_source_data(self):\n    source_data = {}\n    ret = list(os.walk(self._source_path))\n    if len(ret) == 0:\n      logging.error(f'{self._source_path} is empty')\n      return source_data\n\n    root, dirs, files = ret[0]\n\n    done_file_set = set()\n    for file in files:\n      if file.endswith(self.WRITE_DONE) and file.startswith(self._model_name):\n        done_file_set.add(file)\n\n    for model_data in dirs:\n      lock_file = self.create_read_lock(os.path.join(root, model_data))\n      self._lock_files.add(lock_file)\n\n      if self.get_done_file(model_data) in done_file_set:\n        data = model_data.split('@')\n        if len(data) != 2:\n          logging.error(f'{model_data} is not valid')\n          continue\n\n        model_name, version = data\n\n        # real_path: /xxx/model_name@version/model_name\n        real_path = os.path.join(root, model_data, model_name)\n        # version_data: [(ps_0/version,/xxx/model_name@version/ps_0/version), (..,..)]\n        version_data = self.get_version_data(real_path, version)\n\n        if len(version_data) == 0:\n          continue\n\n        if model_name not in source_data:\n          source_data[model_name] = (version, version_data, real_path)\n        else:\n          old_data = source_data[model_name]\n          if old_data[0] < version:\n            source_data[model_name] = (version, version_data, real_path)\n\n    return source_data\n\n  def get_version_data(self, path, version):\n    ret = list(os.walk(path))\n    if len(ret) == 0:\n      logging.error(f'get version data [{path}] failed')\n      return []\n\n    sub_root, sub_dirs, sub_files = ret[0]\n    if len(sub_dirs) == 0:\n      return []\n\n    res = []\n    for sub_dir in sub_dirs:\n      # sub_dir: ps_0\n      # version_dir: /xxx/model_name@version/ps_0/version\n      version_dir = os.path.join(sub_root, sub_dir, version)\n      if not os.path.exists(version_dir):\n        logging.error(f'{version_dir} not exist')\n        return []\n      else:\n        res.append((os.path.join(sub_dir, version), version_dir))\n\n    return res\n\n  def get_done_file(self, file):\n    return file + self.WRITE_DONE\n\n  def delete(self, file):\n    try:\n      if not os.path.exists(file):\n        return True\n\n      if os.path.isfile(file):\n        os.remove(file)\n      else:\n        shutil.rmtree(file)\n      return True\n    except BaseException as err:\n      logging.error('delete [%s] failed: %s', file, str(err))\n    return False\n\n\n"
  },
  {
    "path": "monolith/agent_service/model_manager_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging, app, flags\nimport json\nimport os\nimport shutil\nimport unittest\nimport time\n\nfrom monolith.agent_service.model_manager import ModelManager\n\nFLAGS = flags.FLAGS\n\n\nclass ModelManagerTest(unittest.TestCase):\n\n  def create_file(self, model_name, timestamp, p2p_data_path):\n    # model_data/test_model/ps_item_embedding_0/1234567\n\n    # p2p/test_model@1234567/test_model/ps_item_embedding_0/1234567\n    os.makedirs(\n        os.path.join(p2p_data_path, model_name + '@' + timestamp, model_name,\n                     'ps_item_embedding_0', timestamp))\n    os.makedirs(\n        os.path.join(p2p_data_path, model_name + '@' + timestamp, model_name,\n                     'ps_item_embedding_1', timestamp))\n\n    f = open(\n        os.path.join(p2p_data_path,\n                     model_name + '@' + timestamp + '.write.done'), 'w+')\n    f.close()\n\n  def test_start(self):\n    base_path = os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_model_manager\")\n    p2p_data_path = os.path.join(base_path, 'p2p')\n    model_data_path = os.path.join(base_path, 'model_data')\n\n    model_name = \"test_model\"\n    timestamp = \"1234567\"\n\n    self.create_file(model_name, timestamp, p2p_data_path)\n\n    model_manager = ModelManager(model_name, p2p_data_path, model_data_path,\n                                 False)\n    model_manager._wait_timeout = 5\n    model_manager._loop_interval = 5\n    ret = model_manager.start()\n    self.assertTrue(ret)\n\n    ready_path1 = os.path.join(model_data_path, model_name,\n                               'ps_item_embedding_0', timestamp)\n    ready_path2 = os.path.join(model_data_path, model_name,\n                               'ps_item_embedding_1', timestamp)\n\n    self.assertTrue(os.path.exists(ready_path1))\n    self.assertTrue(os.path.exists(ready_path2))\n\n    model_manager.stop()\n    shutil.rmtree(p2p_data_path)\n    shutil.rmtree(model_data_path)\n\n  def test_ignore_old(self):\n    base_path = os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_model_manager\")\n    p2p_data_path = os.path.join(base_path, 'p2p')\n    model_data_path = os.path.join(base_path, 'model_data')\n\n    model_name = \"test_model\"\n    timestamp = \"1234567\"\n    timestamp_old = \"1234566\"\n\n    self.create_file(model_name, timestamp, p2p_data_path)\n\n    model_manager = ModelManager(model_name, p2p_data_path, model_data_path,\n                                 False)\n    model_manager._wait_timeout = 5\n    model_manager._loop_interval = 5\n    ret = model_manager.start()\n    self.assertTrue(ret)\n\n    self.create_file(model_name, timestamp_old, p2p_data_path)\n    time.sleep(11)\n\n    ready_path1 = os.path.join(model_data_path, model_name,\n                               'ps_item_embedding_0', timestamp_old)\n    ready_path2 = os.path.join(model_data_path, model_name,\n                               'ps_item_embedding_1', timestamp_old)\n\n    self.assertFalse(os.path.exists(ready_path1))\n    self.assertFalse(os.path.exists(ready_path2))\n\n    model_manager.stop()\n    shutil.rmtree(p2p_data_path)\n    shutil.rmtree(model_data_path)\n\n\ndef main(_):\n  unittest.main()\n\n\nif __name__ == \"__main__\":\n  app.run(main)\n"
  },
  {
    "path": "monolith/agent_service/profile.sh",
    "content": "#! /bin/bash\n\n# set -x\n\n# grpc port: PORT3\ngpu_server_target=\"10.209.87.151:9469\"  # multi \"10.210.92.156:9361,10.198.98.198:9433\"\ncpu_server_target=\"10.211.69.228:9388\"\n\ntool_dir=`dirname $0`\nabs_tool_dir=`realpath $tool_dir`\nentry_agent_path=\"$abs_tool_dir/agent.conf\"\nprofile_data_dir=\"$abs_tool_dir/profile_data\"\n\nbin_name=\"tfs_client\"\nbin_path=\"/home/lilintong.22222/.cache/bazel/_bazel_lilintong.22222/5282ccf6d1eb9e524c65d4bb4a5b4207/execroot/__main__/bazel-out/k8-opt/bin/monolith/agent_service/${bin_name}\"\n\n\nfunction run_pro() {\n  target=\"$1\"\n  conf_path=\"$2\"\n  batch_size=\"$3\"\n  parallel_num=\"$4\"\n  profile_duration=\"$5\"\n  profile_data_dir=\"$6\"\n\n  $bin_path \\\n    --target=$target \\\n    --conf=$conf_path \\\n    --cmd_type=\"profile\" \\\n    --input_type=\"example_batch\" \\\n    --batch_size=$batch_size \\\n    --parallel_num=$parallel_num \\\n    --profile_duration=$profile_duration \\\n    --profile_data_dir=$profile_data_dir \\\n    --has_sort_id\n}\n\nfunction run_pro_async() {\n  target=\"$1\"\n  conf_path=\"$2\"\n  batch_size=\"$3\"\n  parallel_num=\"$4\"\n  profile_duration=\"$5\"\n  profile_data_dir=\"$6\"\n\n  $bin_path \\\n    --target=$target \\\n    --conf=$conf_path \\\n    --cmd_type=\"profile\" \\\n    --input_type=\"example_batch\" \\\n    --batch_size=$batch_size \\\n    --parallel_num=$parallel_num \\\n    --profile_duration=$profile_duration \\\n    --profile_data_dir=$profile_data_dir \\\n    --has_sort_id &\n}\n\nfunction run_alg() {\n  target=\"$1\"\n  conf_path=\"$2\"\n  batch_size=\"$3\"\n  input_path=\"$4\"\n  output_path=\"$5\"\n\n  $bin_path \\\n    --target=$target \\\n    --conf=$conf_path \\\n    --cmd_type=\"get\" \\\n    --input_type=\"example_batch\" \\\n    --batch_size=$batch_size \\\n    --input_file=$input_path \\\n    --has_sort_id > $output_path\n}\n\nfunction compare_alg() {\n  a_server_target=\"$1\"\n  b_server_target=\"$1\"\n\n  rm -f input_alg.pb\n  run_alg $a_server_target $entry_agent_path 1 input_alg.pb output_alg_gpu.txt\n  run_alg $b_server_target $entry_agent_path 1 input_alg.pb output_alg_cpu.txt\n\n  diff -urN output_alg_gpu.txt output_alg_cpu.txt > compare_alg.diff\n  cat compare_alg.diff\n}\n\nfunction warmup() {\n  server_target=\"$1\"\n\n  for ((i=0; i<3; i++)); do\n    run_alg $server_target $entry_agent_path 1 input_alg.pb output_alg_gpu.txt\n    cat output_alg_gpu.txt\n  done\n}\n\nbazel build :${bin_name}\n\nwarmup $gpu_server_target\n\n# compare_alg $gpu_server_target $cpu_server_target\n\n# sync profile\nrun_pro $gpu_server_target $entry_agent_path 128 1 300 $profile_data_dir\n# run_pro $cpu_server_target $entry_agent_path 128 1 600 $profile_data_dir\n\n# async profile\nfor ((i=1; i<=6; i++)); do\n  run_pro_async $gpu_server_target $entry_agent_path 128 11 300 $profile_data_dir\ndone\n"
  },
  {
    "path": "monolith/agent_service/replica_manager.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nfrom dataclasses import dataclass\nfrom dataclasses_json import dataclass_json\nfrom kazoo.client import KazooState\nfrom kazoo.protocol.states import WatchedEvent, EventType, ZnodeStat\nfrom kazoo.exceptions import NodeExistsError, NoNodeError\nimport os, re\nimport socket\nimport sys\nimport threading\nimport time\nimport traceback\nfrom typing import List, Dict, Union, Optional, Tuple\n\nfrom tensorflow.core.protobuf.error_codes_pb2 import Code as ErrorCode\n\nfrom monolith.agent_service.tfs_monitor import TFSMonitor\nfrom monolith.agent_service.utils import AgentConfig, ModelState, TFSServerType, DeployType, ZKPath\nfrom monolith.agent_service.agent_service_pb2 import ServerType\nfrom monolith.agent_service.data_def import ReplicaMeta\nfrom monolith.agent_service.backends import SyncBackend\nfrom monolith.native_training.model_export import export_state_utils\nfrom monolith.native_training.net_utils import AddressFamily\nfrom monolith.native_training.zk_utils import MonolithKazooClient, is_ipv6_only\nfrom monolith.native_training.metric import cli\nfrom monolith.native_training.runtime.parameter_sync.parameter_sync_pb2 import ClientConfig\nDEFAULT_USE_ARCHON = False\nclass ReplicaWatcher(object):\n\n  def __init__(self,\n               zk_client: MonolithKazooClient,\n               config: AgentConfig,\n               use_archon: bool = False,\n               zk_watch_address_family: str = AddressFamily.IPV4):\n    self._zk = zk_client\n    # the info of this replica\n    self._conf: AgentConfig = config\n\n    self._use_archon = use_archon\n    if zk_watch_address_family == AddressFamily.IPV4 and is_ipv6_only():\n      logging.warning(\"zk_watch_address_family change to IPV6\")\n      self._zk_watch_address_family = AddressFamily.IPV6\n    else:\n      self._zk_watch_address_family = zk_watch_address_family\n\n    self.path_prefix = os.path.join('/', config.bzid, 'service',\n                                    config.base_name)\n\n    # /bzid/service/model_name/server_type:task -> replica -> (addr, stat)\n    self._lock = threading.Lock()\n    self.replicas: Dict[str, Dict[str, ReplicaMeta]] = {}\n    self._has_stop = False\n    self._should_poll = True\n    self._thread = None\n\n  @property\n  def zk(self):\n    return self._zk\n\n  def watch_data(self):\n    if self._conf.dc_aware:\n      self.zk.ChildrenWatch(path=self.path_prefix,\n                            func=self._get_idc_cluster_children_watch(\n                                self.path_prefix))\n    else:\n      self.zk.ChildrenWatch(path=self.path_prefix,\n                            func=self._get_task_children_watch(\n                                self.path_prefix))\n    self._thread = threading.Thread(target=self._poll, daemon=True)\n    self._has_stop = False\n    self._thread.start()\n\n  def stop(self):\n    try:\n      self._has_stop = True\n      if self._thread is not None:\n        try:\n          self._thread.join()\n          self._thread = None\n        except:\n          self._thread = None\n    finally:\n      with self._lock:\n        self.replicas.clear()\n\n  def _get_idc_cluster_children_watch(self, path_prefix: str):\n    _idc_cluster = set()\n\n    def idc_cluster_children_watch(children: List[str]):\n      if children is not None:\n        for idc_cluster in children:\n          if idc_cluster not in _idc_cluster:\n            # idc_cluster -> idc:cluster\n            _idc_cluster.add(idc_cluster)\n            ic_path = os.path.join(path_prefix, idc_cluster)\n            self.zk.ChildrenWatch(path=ic_path,\n                                  func=self._get_task_children_watch(ic_path))\n\n    return idc_cluster_children_watch\n\n  def _get_task_children_watch(self, path_prefix: str):\n    _tasks = set()\n\n    def task_children_watch(children: List[str]):\n      if children is not None:\n        for task in children:\n          if task not in _tasks:\n            # task -> entry/ps/dense:idx\n            _tasks.add(task)\n            task_path = os.path.join(path_prefix, task)\n            self.zk.ChildrenWatch(\n                path=task_path,\n                func=self._get_replica_children_watch(task_path))\n\n    return task_children_watch\n\n  def _get_replica_children_watch(self, task_path: str):\n    _replicas = set()\n\n    def replica_children_watch(children: List[str]):\n      if children is not None:\n        for replica in children:\n          if replica not in _replicas:\n            _replicas.add(replica)\n            replica_path = os.path.join(task_path, replica)\n            self.zk.DataWatch(path=replica_path,\n                              func=self._get_data_watch(replica_path))\n\n    return replica_children_watch\n\n  def _get_data_watch(self, path):\n\n    def data_watch(data: bytes, state: ZnodeStat, event: WatchedEvent):\n      task_path = os.path.dirname(path)\n      rnode = str(int(os.path.basename(path)))\n      if data is None or len(data) == 0:\n        with self._lock:\n          if task_path in self.replicas:\n            if rnode in self.replicas[task_path]:\n              meta = self.replicas[task_path][rnode]\n              meta.stat = ModelState.UNKNOWN\n            else:\n              return\n          else:\n            return\n      else:\n        meta = ReplicaMeta.deserialize(data)\n\n      with self._lock:\n        if event is None or event.type == EventType.CREATED:\n          # in the first call, event is None\n          if task_path in self.replicas:\n            self.replicas[task_path][rnode] = meta\n          else:\n            self.replicas[task_path] = {rnode: meta}\n        elif event.type == EventType.DELETED:\n          if task_path in self.replicas.keys(\n          ) and rnode in self.replicas[task_path].keys():\n            del self.replicas[task_path][rnode]\n          if task_path in self.replicas.keys() and len(\n              self.replicas[task_path]) == 0:\n            del self.replicas[task_path]\n        elif event.type == EventType.CHANGED:\n          self.replicas[task_path][rnode] = meta\n        elif event.type == EventType.NONE:\n          meta.stat = ModelState.UNKNOWN\n          self.replicas[task_path][rnode] = meta\n        else:\n          assert event.type == EventType.CHILD\n\n    return data_watch\n\n  def _poll(self):\n    while not self._has_stop:\n      time.sleep(60)\n      if not self._should_poll:\n        continue\n      try:\n        tasks = []\n        if self._conf.dc_aware:\n          idc_clusters = self.zk.get_children(self.path_prefix)\n          if idc_clusters:\n            for ic in idc_clusters:\n              ic_path = os.path.join(self.path_prefix, ic)\n              ts = self.zk.get_children(ic_path)\n              if ts:\n                tasks.extend([f'{ic}/{t}' for t in ts])\n        else:\n          ts = self.zk.get_children(self.path_prefix)\n          if ts:\n            tasks.extend(ts)\n\n        replicas_tmp = {}\n        for task in tasks:\n          task_path = os.path.join(self.path_prefix, task)\n          replicas = self.zk.get_children(task_path)\n          replicas_tmp[task_path] = {}\n          if replicas:\n            for replica in replicas:\n              replica_path = os.path.join(task_path, replica)\n              value, _ = self.zk.get(replica_path)\n              if value is not None:\n                meta = ReplicaMeta.from_json(str(value, encoding='utf-8'))\n                replicas_tmp[task_path][str(int(replica))] = meta\n\n        with self._lock:\n          old_paths, new_paths = {}, {}\n          for task, replicas in self.replicas.items():\n            for replica in replicas:\n              key = os.path.join(task, replica)\n              old_paths[key] = self.replicas[task][replica]\n          for task, replicas in replicas_tmp.items():\n            for replica in replicas:\n              key = os.path.join(task, replica)\n              new_paths[key] = replicas_tmp[task][replica]\n          to_removed_replicas = set(old_paths) - set(new_paths)\n          if self._conf.deploy_type == DeployType.MIXED or self._conf.deploy_type == DeployType.PS:\n            server_type = TFSServerType.PS\n            port_grpc, port_archon = self._conf.tfs_port_grpc, self._conf.tfs_ps_archon_port\n          elif self._conf.deploy_type == DeployType.DENSE:\n            server_type = TFSServerType.DENSE\n            port_grpc, port_archon = self._conf.tfs_dense_port, self._conf.tfs_dense_archon_port\n          else:\n            server_type = None\n\n          need_register_replicas: Dict[str, ReplicaMeta] = {}\n          if server_type and to_removed_replicas:\n            for i in self._conf.get_server_schedule_iter(server_type):\n              for replica in to_removed_replicas:\n                if f'/{server_type}:{i}/{self._conf.replica_id}' in replica:\n                  meta: ReplicaMeta = old_paths[replica]\n                  meta.address = f\"{meta.address.split(':')[0]}:{port_grpc}\"\n                  meta.address_ipv6 = f\"{meta.address_ipv6.split(':')[0]}:{port_grpc}\"\n                  meta.archon_address = f\"{meta.archon_address.split(':')[0]}:{port_archon}\"\n                  meta.archon_address_ipv6 = f\"{meta.archon_address_ipv6.split(':')[0]}:{port_archon}\"\n                  need_register_replicas[replica] = meta\n\n          # update self.replicas\n          self.replicas = replicas_tmp\n\n          # register\n          while need_register_replicas:\n            zk_path, meta = need_register_replicas.popitem()\n            replica_meta_bytes = bytes(meta.to_json(), encoding='utf-8')\n            try:\n              self.zk.retry(self.zk.create,\n                            path=zk_path,\n                            value=replica_meta_bytes,\n                            ephemeral=True,\n                            makepath=True,\n                            sequence=False)\n            except NodeExistsError:\n              logging.info(f'{zk_path} has already exists')\n      except Exception as e:\n        exc_type, exc_value, exc_traceback_obj = sys.exc_info()\n        logging.log_every_n_seconds(logging.ERROR, f\"exc_type: {exc_type}\",\n                                    10 * 60)\n\n  def get_all_replicas(self,\n                       server_type: ServerType,\n                       idc: str = None,\n                       cluster: str = None) -> Dict[str, List[str]]:\n    st = ServerType.Name(server_type).lower()\n\n    result = {}\n    with self._lock:\n      for path, replicas in self.replicas.items():\n        zk_path = ZKPath(path)\n        dc_flag = zk_path.ship_in(idc, cluster) if self._conf.dc_aware else True\n        if zk_path.server_type == st and dc_flag:\n          key = os.path.join(\n              zk_path.location,\n              zk_path.task) if self._conf.dc_aware else zk_path.task\n          addrs = [\n              pm.get_address(use_archon=self._use_archon,\n                             address_family=self._zk_watch_address_family)\n              for pm in replicas.values()\n              if pm and pm.stat == ModelState.AVAILABLE\n          ]\n          if key in result:\n            result[key].extend(addrs)\n          else:\n            result[key] = addrs\n\n    if (server_type == ServerType.PS and\n        len(result) == 0) or (server_type == ServerType.DENSE and\n                              self._conf.dense_alone and len(result) == 0):\n      logging.error(f'empty replicas {self.path_prefix}-{st}')\n      logging.info('all replicas is ' + str(self.replicas))\n    return result\n\n  def get_replicas(self,\n                   server_type: ServerType,\n                   task: int,\n                   idc: str = None,\n                   cluster: str = None) -> List[str]:\n    st = ServerType.Name(server_type).lower()\n    with self._lock:\n      addrs = []\n      for path, replicas in self.replicas.items():\n        zk_path = ZKPath(path)\n        dc_flag = zk_path.ship_in(idc, cluster) if self._conf.dc_aware else True\n        if zk_path.server_type == st and int(zk_path.index) == task and dc_flag:\n          if replicas:\n            addrs.extend([\n                meta.get_address(use_archon=self._use_archon,\n                                 address_family=self._zk_watch_address_family)\n                for meta in replicas.values()\n                if meta.stat == ModelState.AVAILABLE\n            ])\n      return addrs\n\n  def get_replica(self,\n                  server_type: ServerType,\n                  task: int,\n                  replica: int,\n                  idc: str = None,\n                  cluster: str = None) -> Optional[Union[List[str], str]]:\n    st = ServerType.Name(server_type).lower()\n    result = []\n    with self._lock:\n      for path, replicas in self.replicas.items():\n        zk_path = ZKPath(path)\n        dc_flag = zk_path.ship_in(idc, cluster) if self._conf.dc_aware else True\n        if zk_path.server_type == st and int(zk_path.index) == task and dc_flag:\n          for replica_id, meta in replicas.items():\n            if int(replica_id) == replica:\n              if meta is not None and meta.stat == ModelState.AVAILABLE:\n                result.append(\n                    meta.get_address(\n                        use_archon=self._use_archon,\n                        address_family=self._zk_watch_address_family))\n\n    if result:\n      if len(result) == 1:\n        return result[0]\n      else:\n        return result\n    else:\n      return None\n\n  def get_replicas_with_extra_info(self,\n                                   server_type: ServerType,\n                                   task: int,\n                                   idc: str = None,\n                                   cluster: str = None) -> List[str]:\n    st = ServerType.Name(server_type).lower()\n    with self._lock:\n      addr_dict = {}\n      for path, replicas in self.replicas.items():\n        zk_path = ZKPath(path)\n        dc_flag = zk_path.ship_in(idc, cluster) if self._conf.dc_aware else True\n        if zk_path.server_type == st and int(zk_path.index) == task and dc_flag:\n          if replicas:\n            addr_dict.update({\n                meta.get_address(use_archon=self._use_archon,\n                                 address_family=self._zk_watch_address_family):\n                ClientConfig.TargetExtraInfo(idc=zk_path.idc,\n                                             cluster=zk_path.cluster,\n                                             replica_id=int(replica_id))\n                for replica_id, meta in replicas.items()\n                if meta.stat == ModelState.AVAILABLE\n            })\n      return addr_dict\n\n  def to_sync_wrapper(self) -> SyncBackend:\n    return SyncBackendWrapper(self)\n\n\nclass ReplicaUpdater(object):\n\n  def __init__(self, zk_client: MonolithKazooClient, config: AgentConfig):\n    self._zk = zk_client\n    self._conf: AgentConfig = config\n    self.path_prefix = config.path_prefix\n\n    self.model_monitor = TFSMonitor(config)\n\n    self.meta = {}\n    self._has_stop = False\n    self._should_reregister = False\n    self._should_update = True\n    self._thread = None\n    self._reregister_thread = None\n    self._watch_update_thread = None\n\n    self._entry_last_update_version = None\n    self._metrics_cli = None\n    self._metrics_cli_global = None\n    self._tagkv = {'status': 'OK'}\n    self._model_latest_version = {}\n    self._model_last_update_ts = {}\n    if self._conf.use_metrics:\n      try:\n        self.init_metrics()\n      except:\n        logging.error('init metrics error')\n        exc_type, exc_value, exc_traceback_obj = sys.exc_info()\n        logging.error(f\"exc_type: {exc_type}\")\n        logging.error(f\"exc_value: {exc_value}\")\n        traceback.print_tb(exc_traceback_obj, limit=10)\n    else:\n      logging.info(\"conf.use_metrics is false\")\n\n  def init_metrics(self):\n    if \"MONOLITH_METRIC_PREFIX\" in os.environ: # In MLP Env\n      prefix = os.environ.get(\"MONOLITH_METRIC_PREFIX\")\n    elif \"TCE_PSM\" in os.environ: # In  Byterec Env\n      prefix = os.environ.get(\"TCE_PSM\")\n    else:\n      prefix = \"data.monolith_serving.\" + self._conf.base_name\n    self._metrics_cli = cli.get_cli(prefix=prefix)\n    self._metrics_cli_global = cli.get_cli(prefix=\"data.monolith_serving.global\")\n    logging.info(f\"after init_metrics, prefix is {prefix}\")\n\n  @property\n  def zk(self):\n    return self._zk\n\n  @property\n  def model_names(self):\n    names = []\n    if self._conf.deploy_type == DeployType.MIXED or self._conf.deploy_type == DeployType.PS:\n      for task_id in range(self._conf.num_ps):\n        if task_id % self._conf.num_shard == self._conf.shard_id:\n          names.append(f'{TFSServerType.PS}_{task_id}')\n\n    if self._conf.deploy_type == DeployType.MIXED or self._conf.deploy_type == DeployType.ENTRY:\n      names.append(TFSServerType.ENTRY)\n\n    if self._conf.dense_alone and (self._conf.deploy_type == DeployType.MIXED or\n                                   self._conf.deploy_type == DeployType.DENSE):\n      names.append(f'{TFSServerType.DENSE}_0')\n\n    return names\n\n  @property\n  def entry_path(self):\n    return os.path.join(self.path_prefix, f'{TFSServerType.ENTRY}:0',\n                        str(self._conf.replica_id))\n\n  def ps_path(self, task_id: int):\n    return os.path.join(self.path_prefix, f'{TFSServerType.PS}:{task_id}',\n                        str(self._conf.replica_id))\n\n  def dense_path(self):\n    return os.path.join(self.path_prefix, f'{TFSServerType.DENSE}:0',\n                        str(self._conf.replica_id))\n\n  def _do_register(self, replica_path: str, grpc_port: int, archon_port: int):\n    try:\n      host = os.environ.get(\"MY_HOST_IP\",\n                            socket.gethostbyname(socket.gethostname()))\n    except:\n      host = '0.0.0.0'\n    try:\n      defalut_host_ipv6 = socket.getaddrinfo(socket.gethostname(), None,\n                                             socket.AF_INET6)[0][4][0]\n    except:\n      defalut_host_ipv6 = '::'\n    host_ipv6 = os.environ.get(\"MY_HOST_IPV6\")\n    if not host_ipv6:\n      host_ipv6 = defalut_host_ipv6\n    host_ipv6 = '[{}]'.format(host_ipv6)\n    replica_meta = ReplicaMeta(address=f'{host}:{grpc_port}',\n                               address_ipv6=f'{host_ipv6}:{grpc_port}',\n                               stat=ModelState.UNKNOWN,\n                               archon_address=f'{host}:{archon_port}',\n                               archon_address_ipv6=f'{host_ipv6}:{archon_port}')\n    self.meta[replica_path] = replica_meta\n    replica_meta_bytes = bytes(replica_meta.to_json(), encoding='utf-8')\n    node_stat = self.zk.exists(replica_path)\n    if not node_stat:\n      try:\n        sequence = True if TFSServerType.ENTRY in replica_path and self._conf.replica_id == -1 else False\n        real_path = self.zk.retry(self.zk.create,\n                                  path=replica_path,\n                                  value=replica_meta_bytes,\n                                  ephemeral=True,\n                                  makepath=True,\n                                  sequence=sequence)\n        if self._conf.replica_id == -1:\n          self._conf.replica_id = int(os.path.basename(real_path))\n          del self.meta[replica_path]\n          self.meta[real_path] = replica_meta\n      except NodeExistsError:\n        logging.info(f'{replica_path} has already exists')\n        self.zk.retry(self.zk.set, path=replica_path, value=replica_meta_bytes)\n    else:\n      value, _ = self.zk.get(replica_path)\n      if value != replica_meta_bytes:\n        self.zk.retry(self.zk.set, path=replica_path, value=replica_meta_bytes)\n\n  def register(self):\n    if self._conf.deploy_type == DeployType.MIXED or self._conf.deploy_type == DeployType.ENTRY:\n      if self._conf.replica_id == -1:\n        replica_path = f'{self.path_prefix}/{TFSServerType.ENTRY}:0/0'\n      else:\n        replica_path = f'{self.path_prefix}/{TFSServerType.ENTRY}:0/{self._conf.replica_id:011d}'\n      self._do_register(replica_path, self._conf.tfs_entry_port,\n                        self._conf.tfs_entry_archon_port)\n\n    if self._conf.deploy_type == DeployType.MIXED or self._conf.deploy_type == DeployType.PS:\n      for task_id in range(self._conf.num_ps):\n        if task_id % self._conf.num_shard == self._conf.shard_id:\n          self._do_register(self.ps_path(task_id), self._conf.tfs_ps_port,\n                            self._conf.tfs_ps_archon_port)\n\n    if self._conf.dense_alone and (self._conf.deploy_type == DeployType.MIXED or\n                                   self._conf.deploy_type == DeployType.DENSE):\n      self._do_register(self.dense_path(), self._conf.tfs_dense_port,\n                        self._conf.tfs_dense_archon_port)\n\n  def _do_update(self, name: str):\n    if name.startswith(TFSServerType.ENTRY):\n      replica_path = f'{self.path_prefix}/{TFSServerType.ENTRY}:0/{self._conf.replica_id:011d}'\n    elif name.startswith(TFSServerType.PS):\n      replica_path = self.ps_path(int(name.split(\"_\")[1]))\n    else:\n      replica_path = self.dense_path()\n\n    try:\n      model_status = self.model_monitor.get_model_status(name)\n    except Exception as e:\n      replica_meta = self.meta[replica_path]\n      if replica_meta.stat != ModelState.UNKNOWN:\n        replica_meta.stat = ModelState.UNKNOWN\n        replica_meta_bytes = bytes(replica_meta.to_json(), encoding='utf-8')\n        try:\n          self.zk.retry(self.zk.set,\n                        path=replica_path,\n                        value=replica_meta_bytes)\n        except NoNodeError:\n          self.zk.retry(self.zk.create,\n                        path=replica_path,\n                        value=replica_meta_bytes,\n                        ephemeral=True,\n                        makepath=True)\n      return\n\n    if model_status is not None and len(model_status) > 0:\n      model_version_status = None\n      if len(model_status) > 1:\n        model_status.sort(key=lambda mvs: mvs.version, reverse=True)\n        for m_status in model_status:\n          if m_status.state == ModelState.AVAILABLE:\n            model_version_status = m_status\n            break\n      if model_version_status is None:\n        # check model version status\n        model_version_status = model_status[0]\n\n      status = model_version_status.status\n      if status.error_code != ErrorCode.OK:\n        raise Exception(status.error_message)\n\n      # update state if changed\n      stat = model_version_status.state\n      replica_meta = self.meta[replica_path]\n      if replica_meta.stat != stat:\n        replica_meta.stat = stat\n        replica_meta_bytes = bytes(replica_meta.to_json(), encoding='utf-8')\n        try:\n          self.zk.retry(self.zk.set,\n                        path=replica_path,\n                        value=replica_meta_bytes)\n        except NoNodeError:\n          self.zk.retry(self.zk.create,\n                        path=replica_path,\n                        value=replica_meta_bytes,\n                        ephemeral=True,\n                        makepath=True)\n\n  def _updater(self):\n    while not self._has_stop:\n      curr_name = None\n      time.sleep(1)\n      if not self._should_update:\n        continue\n      try:\n        for name in self.model_names:\n          curr_name = name\n          self._do_update(name)\n      except Exception as e:\n        exc_type, exc_value, exc_traceback_obj = sys.exc_info()\n        logging.error(f\"exc_type: {exc_type}\")\n        logging.error(f\"exc_value: {exc_value}\")\n        traceback.print_tb(exc_traceback_obj, limit=10)\n        logging.error(f\"{e}, when model {curr_name} update\")\n      except (SystemExit, KeyboardInterrupt, GeneratorExit) as e:\n        self._has_stop = True\n        logging.error(f\"{e}, when model {curr_name} update\")\n\n  def _get_latest_version_in_fs(self, name):\n    exported_models_dir = os.path.join(self._conf.base_path, name)\n    state = export_state_utils.get_export_saver_listener_state(\n        exported_models_dir)\n    if state.entries:\n      return state.entries[-1].export_dir\n    else:\n      return None\n\n  def _check_version(self):\n    if not self._metrics_cli:\n      return\n\n    # metrics_v2\n    for name in self.model_names:\n      model_status = self.model_monitor.get_model_status(name)\n      req_ts = int(time.time())\n      if model_status is None or len(model_status) == 0:\n        continue\n      model_status.sort(key=lambda x: x.version, reverse=True)\n      latest_model_status = model_status[0]\n\n      latest_version = latest_model_status.version\n      if latest_model_status.status.error_code != ErrorCode.OK:\n        raise Exception(latest_model_status.status.error_message)\n\n      tags = {\n          \"model_name\": name,\n          # It's safe when clueter or idc is None\n          \"idc\": f\"{self._conf.idc}:{self._conf.cluster}\",\n          \"replica_id\": str(self._conf.replica_id),\n          \"shard_id\": str(self._conf.shard_id),\n          \"base_name\": self._conf.base_name\n      }\n      self._metrics_cli.emit_store(\"serving_model.latest_version\",\n                                   latest_version, tags)\n      if name in self._model_last_update_ts:\n        interval = req_ts - self._model_last_update_ts[name]\n        self._metrics_cli_global.emit_store(\"serving_model.since_last_update\",\n                                            interval, tags)\n      if name not in self._model_latest_version or self._model_latest_version[\n          name] < latest_version:\n        self._metrics_cli.emit_store(\"serving_model.update_ts\", req_ts, tags)\n        self._model_latest_version[name] = latest_version\n        self._model_last_update_ts[name] = req_ts\n      self._metrics_cli.flush()\n\n    return\n\n  def _watch_update(self):\n    if not self._metrics_cli:\n      return\n\n    while not self._has_stop:\n      time.sleep(60)\n      try:\n        self._check_version()\n      except:\n        exc_type, exc_value, exc_traceback_obj = sys.exc_info()\n        logging.error(f\"exc_type: {exc_type}\")\n        logging.log_every_n_seconds(logging.WARNING, traceback.format_exc(),\n                                    600)\n\n  def _reregister(self):\n    while not self._has_stop:\n      time.sleep(10)\n      if self._should_reregister:\n        self.register()\n        self._should_update = True\n        # self._should_reregister = False\n\n  def start(self):\n    self.model_monitor.start()\n    self._has_stop = False\n    if self._thread is None:\n      self._thread = threading.Thread(target=self._updater, daemon=True)\n      self._thread.start()\n    if self._reregister_thread is None:\n      self._reregister_thread = threading.Thread(target=self._reregister,\n                                                 daemon=True)\n      self._reregister_thread.start()\n    if self._watch_update_thread is None and self._metrics_cli is not None:\n      self._watch_update_thread = threading.Thread(target=self._watch_update,\n                                                   daemon=True)\n      self._watch_update_thread.start()\n\n  def stop(self):\n    try:\n      self._has_stop = True\n      if self._thread is not None:\n        try:\n          self._thread.join()\n        finally:\n          self._thread = None\n      if self._reregister_thread is not None:\n        try:\n          self._reregister_thread.join()\n        finally:\n          self._reregister_thread = None\n      if self._watch_update_thread is not None:\n        try:\n          self._watch_update_thread.join()\n        finally:\n          self._watch_update_thread = None\n    finally:\n      self.model_monitor.stop()\n      self.meta.clear()\n\n\nclass ZKListener(object):\n\n  def __init__(self, watcher: ReplicaWatcher, updater: ReplicaUpdater):\n    self._watcher: ReplicaWatcher = watcher\n    self._updater: ReplicaUpdater = updater\n    self._has_lost = False\n\n  def __call__(self, state: KazooState) -> bool:\n    if state == KazooState.LOST:\n      # The connection has been confirmed dead\n      logging.warning(\n          \"Any ephemeral nodes will need to be recreated upon re-establishing a connection.\"\n      )\n      self._has_lost = True\n      self._watcher._should_poll = False\n      self._updater._should_update = False\n    elif state == KazooState.SUSPENDED:\n      # Handle being disconnected from Zookeeper\n      return False\n    else:\n      # Handle being connected/reconnected to Zookeeper\n      if self._has_lost:\n        logging.info(\n            \"connected/reconnected after lost, restart updater and watcher\")\n        self._updater._should_reregister = True\n        time.sleep(5)  # wait for updater reregister\n        self._watcher._should_poll = True\n        self._has_lost = False\n\n    return False\n\n\nclass ReplicaManager:\n\n  def __init__(self, zk_client: MonolithKazooClient, config: AgentConfig):\n    self._watcher = ReplicaWatcher(zk_client, config, DEFAULT_USE_ARCHON)\n    self._updater = ReplicaUpdater(zk_client, config)\n    self._conf = config\n\n    listener = ZKListener(self._watcher, self._updater)\n    zk_client.add_listener(listener)\n\n  @property\n  def watcher(self):\n    return self._watcher\n\n  @property\n  def updater(self):\n    return self._updater\n\n  def start(self):\n    self._updater.register()\n    self._watcher.watch_data()\n    self._updater.start()\n\n  def stop(self):\n    self._updater.stop()\n    self._watcher.stop()\n\n  def get_all_replicas(self,\n                       server_type: ServerType,\n                       idc: str = None,\n                       cluster: str = None) -> Dict[str, List[str]]:\n    return self._watcher.get_all_replicas(server_type, idc, cluster)\n\n  def get_replicas(self,\n                   server_type: ServerType,\n                   task: int,\n                   idc: str = None,\n                   cluster: str = None) -> List[str]:\n    return self._watcher.get_replicas(server_type, task, idc, cluster)\n\n  def get_replica(self,\n                  server_type: ServerType,\n                  task: int,\n                  replica: int,\n                  idc: str = None,\n                  cluster: str = None) -> Optional[Union[List[str], str]]:\n    return self._watcher.get_replica(server_type, task, replica, idc, cluster)\n\n  def is_ps_set_started(self):\n    for i in range(self._conf.num_ps):\n      replicas = self._watcher.get_replicas(ServerType.PS, i, self._conf.idc,\n                                            self._conf.cluster)\n      if replicas is None or len(replicas) == 0:\n        return False\n    logging.info(\n        f\"get_all_replicas: {self._watcher.get_all_replicas(ServerType.PS)}\")\n    return True\n\n  def is_dense_set_started(self):\n    replicas = self._watcher.get_replicas(ServerType.DENSE, 0)\n    if replicas is None or len(replicas) == 0:\n      return False\n    logging.info(f\"get_replicas: {replicas}\")\n    return True\n\n\nclass SyncBackendWrapper(SyncBackend):\n\n  def __init__(self, watcher: ReplicaWatcher):\n    super(SyncBackendWrapper, self).__init__()\n    self._watcher = watcher\n    self._model_name = None\n\n  def subscribe_model(self, model_name: str):\n    self._model_name = model_name\n\n  def get_sync_targets(self, sub_graph: str) -> Tuple[str, Dict]:\n    ps, i = sub_graph.split(\"_\")[:2]\n    assert ps == \"ps\"\n    return sub_graph, self._watcher.get_replicas_with_extra_info(\n        ServerType.PS, int(i))\n\n  def start(self):\n    self._watcher.watch_data()\n\n  def stop(self):\n    self._watcher.stop()\n"
  },
  {
    "path": "monolith/agent_service/replica_manager_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nfrom kazoo.exceptions import NodeExistsError\nimport socket\nimport time\nimport os\nimport threading\n\nimport unittest\n\nfrom monolith.agent_service import constants\nfrom monolith.agent_service import utils\nfrom monolith.agent_service.agent_service_pb2 import ServerType\nfrom monolith.agent_service.mocked_tfserving import FakeTFServing\nfrom monolith.agent_service.mocked_zkclient import FakeKazooClient\nfrom monolith.agent_service.replica_manager import ReplicaUpdater, ReplicaWatcher, \\\n  ReplicaManager, ReplicaMeta, ModelState\n\nMODEL_NAME = 'test_model'\nBASE_PATH = f'/model/{MODEL_NAME}/saved_models'\nNUM_REPLICAS = 3\n\n\nclass ReplicaMgrTest(unittest.TestCase):\n  tfs: FakeTFServing = None\n  agent_conf: utils.AgentConfig = None\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    os.environ[constants.HOST_SHARD_ENV] = '5'\n    os.environ['SHARD_ID'] = '1'\n    os.environ['REPLICA_ID'] = '2'\n    os.environ['TCE_INTERNAL_IDC'] = 'lf'\n    os.environ['TCE_LOGICAL_CLUSTER'] = 'default'\n    cls.agent_conf = utils.AgentConfig(bzid='bzid',\n                                       base_name=MODEL_NAME,\n                                       deploy_type='mixed',\n                                       base_path=BASE_PATH,\n                                       num_ps=20,\n                                       num_shard=5,\n                                       dc_aware=True)\n\n    entry_cmd = cls.agent_conf.get_cmd('tensorflow_serving',\n                                       server_type=utils.TFSServerType.ENTRY)\n    start = entry_cmd.index('model_config_file') + len('model_config_file') + 1\n    end = entry_cmd.find(' ', start)\n    cls.tfs_entry = FakeTFServing(model_config_file=entry_cmd[start:end],\n                                  num_versions=1,\n                                  port=cls.agent_conf.tfs_entry_port)\n\n    ps_cmd = cls.agent_conf.get_cmd('tensorflow_serving',\n                                    server_type=utils.TFSServerType.PS)\n    start = ps_cmd.index('model_config_file') + len('model_config_file') + 1\n    end = ps_cmd.find(' ', start)\n    cls.tfs_ps = FakeTFServing(model_config_file=ps_cmd[start:end],\n                               num_versions=1,\n                               port=cls.agent_conf.tfs_ps_port)\n\n    cls.threads = [\n        threading.Thread(target=lambda: cls.tfs_entry.start()),\n        threading.Thread(target=lambda: cls.tfs_ps.start())\n    ]\n    for thread in cls.threads:\n      thread.start()\n    time.sleep(1)\n\n  @classmethod\n  def tearDownClass(cls) -> None:\n    cls.tfs_entry.stop()\n    cls.tfs_ps.stop()\n    for thread in cls.threads:\n      thread.join()\n\n  def register(self, zk):\n    path_prefix = self.agent_conf.path_prefix\n    path_to_meta, idx = {}, 2\n    for replica_id in range(NUM_REPLICAS):\n      for shard_id in range(self.agent_conf.num_shard):\n        if shard_id == self.agent_conf.shard_id and replica_id == self.agent_conf.replica_id:\n          continue\n\n        for task_id in range(self.agent_conf.num_ps):\n          if task_id % self.agent_conf.num_shard == shard_id:\n            meta = ReplicaMeta(\n                address=f'192.168.1.{idx}:{utils.find_free_port()}',\n                stat=ModelState.AVAILABLE)\n            replica_path = f'{path_prefix}/ps:{task_id}/{replica_id}'\n            path_to_meta[replica_path] = meta\n            idx += 1\n\n        replica_path = f'{path_prefix}/entry:0/{replica_id}'\n        meta = ReplicaMeta(address=f'192.168.1.{idx}:{utils.find_free_port()}',\n                           stat=ModelState.AVAILABLE)\n        path_to_meta[replica_path] = meta\n        idx += 1\n\n    for replica_path, meta in path_to_meta.items():\n      replica_meta_bytes = bytes(meta.to_json(), encoding='utf-8')\n\n      try:\n        zk.retry(zk.create,\n                 path=replica_path,\n                 value=replica_meta_bytes,\n                 ephemeral=True,\n                 makepath=True)\n      except NodeExistsError:\n        logging.info(f'{replica_path} has already exists')\n        zk.retry(zk.set, path=replica_path, value=replica_meta_bytes)\n\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/agent_service/resource_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport os\nimport time\nimport subprocess\nimport re\nimport psutil\nfrom google.protobuf import text_format\n\nimport tensorflow as tf\nfrom typing import Dict, Union, List\n\nimport monolith.agent_service.utils\nfrom monolith.agent_service.data_def import SubModelName, VersionPath, SubModelSize\n\nfrom monolith.native_training.model_export import export_pb2\nfrom monolith.native_training.model_export import export_state_utils\n\nROW = re.compile(r\"^.+_(\\d+)-\\d+-of-\\d+$\")\n\n\ndef _get_pod_cgroup_path():\n  cmd = [\"cat\", \"/proc/1/cgroup\"]\n  try:\n    out_bytes = subprocess.check_output(cmd)\n    out_list = out_bytes.decode('utf-8').strip().split('\\n')\n    for line in out_list:\n      if ':memory:' in line:\n        return line.strip().split(':')[-1].strip('/')\n  except Exception as e:\n    return None\n\n\n_POD_CGROUP_PATH = _get_pod_cgroup_path()\n\n\ndef exists(dirname: str) -> bool:\n  return tf.io.gfile.isdir(dirname) or tf.io.gfile.exists(dirname)\n\n\ndef open_hdfs(fname: Union[str, List[str]]):\n  cmd = [_HADOOP_BIN, 'fs', '-text']\n  if isinstance(fname, (list, tuple)):\n    cmd.extend(fname)\n  else:\n    cmd.append(fname)\n  out_list = None\n  cnt, max_try = 0, 3\n  while cnt < max_try:\n    try:\n      out_bytes = subprocess.check_output(cmd)\n      out_list = out_bytes.decode('utf-8').strip().split('\\n')\n      break\n    except Exception as e:\n      logging.info(e)\n      cnt += 1\n  assert out_list is not None\n  for line in out_list:\n    line = line.strip()\n    if len(line) > 0:\n      yield line\n\n\ndef cal_model_info_v2(\n    exported_models_path: str,\n    ckpt: str = None,\n    version: str = None) -> 'Dict[SubModelName, (SubModelSize, VersionPath)]':\n  # 1) get all names of saved_models\n  if os.path.isabs(exported_models_path):\n    exported_models_path = exported_models_path.rstrip('/')\n  else:\n    exported_models_path = os.path.abspath(exported_models_path.rstrip('/'))\n\n  if not tf.io.gfile.exists(exported_models_path):\n    raise Exception(f\"{exported_models_path} is not exists \")\n\n  model_info = {\n      sub_model_name: 0\n      for sub_model_name in tf.io.gfile.listdir(exported_models_path)\n      if not sub_model_name.startswith('.')\n  }\n\n  # 2) ensure checkpoint\n  ckpt_base_path = os.path.dirname(exported_models_path)\n  if ckpt is None:\n    checkpoint_state = tf.train.get_checkpoint_state(ckpt_base_path)\n    if checkpoint_state is not None:\n      ckpt = os.path.basename(checkpoint_state.model_checkpoint_path)\n  global_step = -1 if ckpt is None else int(ckpt.split('-')[-1])\n\n  # 3) ensure version\n  if version is None:\n    com_versions = None\n    for sub_model_name in tf.io.gfile.listdir(exported_models_path):\n      if sub_model_name.startswith('.'):\n        continue\n      tfs_base_path = os.path.join(exported_models_path, sub_model_name)\n      state = export_state_utils.get_export_saver_listener_state(tfs_base_path)\n      if global_step >= 0 and state is not None:\n        versions = set()\n        for se in state.entries:\n          _version = int(os.path.basename(se.export_dir))\n          versions.add(_version)\n          if se.global_step == global_step:\n            if version is None:\n              version = _version\n            else:\n              assert version == _version\n            break\n      else:\n        versions = set(\n            int(num)\n            for num in tf.io.gfile.listdir(tfs_base_path)\n            if num.isnumeric())\n\n      if com_versions is None:\n        com_versions = versions\n      else:\n        com_versions &= versions\n\n    assert com_versions is not None and len(com_versions) > 0\n    version = version or sorted(com_versions)[-1]\n  else:\n    version = int(version)\n\n  # 4) get dense part size of all saved_models\n  for sub_model_name in model_info:\n    version_path = os.path.join(exported_models_path, sub_model_name,\n                                str(version))\n    assert tf.io.gfile.exists(version_path)\n    for (dir_name, _, file_names) in tf.io.gfile.walk(version_path):\n      for fn in file_names:\n        stat = tf.io.gfile.stat(os.path.join(dir_name, fn))\n        model_info[sub_model_name] += stat.length\n\n  # 5) add assets length (sparse part size)\n  assets_path = os.path.join(ckpt_base_path, f'{ckpt}.assets')\n  if tf.io.gfile.exists(assets_path):\n    for fn in tf.io.gfile.listdir(assets_path):\n      matched = ROW.match(fn)\n      if matched:\n        key = f'ps_{matched.group(1)}'\n        stat = tf.io.gfile.stat(os.path.join(assets_path, fn))\n        model_info[key] += stat.length\n\n  return {\n      sub_model_name:\n      (size, os.path.join(exported_models_path, sub_model_name, str(version)))\n      for sub_model_name, size in model_info.items()\n  }\n\n\ndef total_memory() -> int:\n  memory_base = os.path.join(\"/sys/fs/cgroup/memory\", _POD_CGROUP_PATH)\n  limit_in_bytes = 0\n  with open(os.path.join(memory_base, 'memory.limit_in_bytes'), 'r') as stream:\n    for line in stream:\n      limit_in_bytes = int(line.strip())\n\n  if limit_in_bytes == 0:\n    return int(os.environ.get('MY_MEM_LIMIT'))\n  else:\n    return limit_in_bytes\n\n\ndef total_memory_v2() -> int:\n  mem = psutil.virtual_memory()\n  return mem.total\n\n\ndef cal_available_memory() -> int:\n  memory_base = os.path.join(\"/sys/fs/cgroup/memory\", _POD_CGROUP_PATH)\n  usage_in_bytes = 0\n  with open(os.path.join(memory_base, 'memory.usage_in_bytes'), 'r') as stream:\n    for line in stream:\n      usage_in_bytes = int(line.strip())\n\n  limit_in_bytes = 0\n  with open(os.path.join(memory_base, 'memory.limit_in_bytes'), 'r') as stream:\n    for line in stream:\n      limit_in_bytes = int(line.strip())\n  return limit_in_bytes - usage_in_bytes\n\n\ndef cal_available_memory_v2() -> int:\n  mem = psutil.virtual_memory()\n  return mem.available\n\n\nclass CPU(object):\n\n  def __init__(self, cpuacct_file):\n    self.cpuacct_file = cpuacct_file\n    self.last_wall_clock = self.wall_clock()\n    self.last_cpu_clock = self.cpu_clock()\n\n  def wall_clock(self):\n    try:\n      # time_ns() only supported by python 3.7\n      total_time = time.time_ns()\n    except Exception as e:\n      total_time = subprocess.check_output(['date', '+%s%N'])\n\n    return int(total_time)\n\n  def cpu_clock(self):\n    with open(self.cpuacct_file, 'r') as f:\n      use_time = int(f.read())\n    return use_time\n\n  def cpu_usage(self):\n    current_wall_clock = self.wall_clock()\n    current_cpu_clock = self.cpu_clock()\n\n    delta_cpu_time = current_cpu_clock - self.last_cpu_clock\n    delta_wall_time = current_wall_clock - self.last_wall_clock\n    usage = delta_cpu_time / delta_wall_time\n\n    self.last_wall_clock = current_wall_clock\n    self.last_cpu_clock = current_cpu_clock\n\n    return usage\n\n\ndef num_cpu():\n  cpu_base = os.path.join(\"/sys/fs/cgroup/cpu\", _POD_CGROUP_PATH)\n  cfs_quota_us = 0\n  with open(os.path.join(cpu_base, 'cpu.cfs_quota_us'), 'r') as stream:\n    for line in stream:\n      cfs_quota_us = int(line.strip())\n\n  cfs_period_us = 0\n  with open(os.path.join(cpu_base, 'cpu.cfs_period_us'), 'r') as stream:\n    for line in stream:\n      cfs_period_us = int(line.strip())\n\n  if cfs_period_us == 0:\n    return int(os.environ.get('MY_CPU_LIMIT'))\n  else:\n    return int(cfs_quota_us / cfs_period_us)\n\n\ndef cal_cpu_usage():\n  cpu_base = os.path.join(\"/sys/fs/cgroup/cpu\", _POD_CGROUP_PATH)\n  cpuacct_file = os.path.join(cpu_base, 'cpuacct.usage')\n  cpu = CPU(cpuacct_file)\n  cpu_usages, cnt, max_try = [], 0, 5\n  while cnt < max_try:\n    time.sleep(1)\n    cpu_usages.append(round(cpu.cpu_usage() * 100, 2))\n    cnt += 1\n  return sum(cpu_usages) / max_try\n\n\ndef cal_cpu_usage_v2() -> float:\n  return psutil.cpu_percent()\n"
  },
  {
    "path": "monolith/agent_service/resource_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport unittest\nfrom monolith.agent_service.resource_utils import cal_available_memory_v2, total_memory_v2, \\\n  cal_cpu_usage_v2, cal_model_info_v2, cal_available_memory\n\n\nclass UtilTest(unittest.TestCase):\n\n  def test_cal_avaiable_memory_v2(self):\n    total = total_memory_v2()\n    available = cal_available_memory_v2()\n    logging.info(f'the total memory is {total}, and {available} is available')\n    self.assertTrue(0 < available < total)\n\n  def test_cal_cpu_usage_v2(self):\n    usage = cal_cpu_usage_v2()\n    logging.info(f'the cpu usage is {usage}')\n    self.assertTrue(0 <= usage <= 100)\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "monolith/agent_service/run.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import app, flags, logging\n\nfrom monolith.agent_service.agent import main as agent_main\nfrom monolith.agent_service.agent_client import main as agent_client_main\nfrom monolith.agent_service.tfs_client import main as tfs_client_main\n\n\nFLAGS = flags.FLAGS\nflags.DEFINE_enum(\"bin_name\", \"agent\",\n                  [\"agent\", \"agent_client\", \"tfs_client\"],\n                  \"bin_name: agent, agent_client\")\n\ndef main(_):\n  if FLAGS.bin_name == 'agent':\n    agent_main(_)\n  elif FLAGS.bin_name == 'agent_client':\n    agent_client_main(_)\n  elif FLAGS.bin_name == 'tfs_client':\n    tfs_client_main(_)\n  else:\n    raise ValueError(\"Unknown bin: {}\".format(FLAGS.bin_name))\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "monolith/agent_service/svr_client.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging, flags\nimport grpc\nimport os\nimport socket\n\nfrom monolith.agent_service import utils\nfrom monolith.agent_service.agent_service_pb2 import HeartBeatRequest, ServerType, \\\n  GetReplicasRequest\nfrom monolith.agent_service.agent_service_pb2_grpc import AgentServiceStub\n\n\nclass SvrClient(object):\n\n  def __init__(self, config) -> None:\n    if isinstance(config, str):\n      self.agent_conf = utils.AgentConfig.from_file(config)\n    else:\n      self.agent_conf = config\n    self._stub = None\n\n  @property\n  def stub(self):\n    if self._stub is None:\n      local_host = socket.gethostbyname(socket.gethostname())\n      target = f'{os.environ.get(\"MY_HOST_IP\", local_host)}:{self.agent_conf.agent_port}'\n      channel = grpc.insecure_channel(target)\n      self._stub = AgentServiceStub(channel)\n\n    return self._stub\n\n  def get_server_type(self, st):\n    if isinstance(st, str):\n      if FLAGS.server_type == 'ps':\n        return ServerType.PS\n      elif FLAGS.server_type == 'entry':\n        return ServerType.ENTRY\n      elif FLAGS.server_type == 'dense':\n        return ServerType.DENSE\n      else:\n        raise Exception('server_type error')\n    else:\n      return st\n\n  def heart_beat(self, server_type):\n    server_type = self.get_server_type(server_type)\n    request = HeartBeatRequest(server_type=server_type)\n    resp = self.stub.HeartBeat(request)\n    print(resp.addresses, flush=True)\n    return resp\n\n  def get_replicas(self, server_type, task):\n    server_type = self.get_server_type(server_type)\n    request = GetReplicasRequest(server_type=server_type, task=task)\n    resp = self.stub.GetReplicas(request)\n    print(resp.address_list.address, flush=True)\n    return resp\n"
  },
  {
    "path": "monolith/agent_service/test_data/BUILD",
    "content": "package(default_visibility = [\n    \"//monolith/integration_test:__subpackages__\",\n    \"//monolith/agent_service:__subpackages__\",\n])\n\nfilegroup(\n    name = \"test_data\",\n    srcs = [\n        \"inst.pbtext\", \n        \"inst.json\", \n        \"inst.dump\",\n    ]\n)\n"
  },
  {
    "path": "monolith/agent_service/test_data/inst.dump",
    "content": "\"root\":\n\"feature\":\n0:\n\"fid\":\n0: \"1\"\n\"name\": \"fc_a\"\n1:\n\"fid\":\n0: \"2\"\n\"name\": \"fc_b\"\n2:\n\"fid\":\n0: \"3\"\n1: \"4\"\nlabel\":\n0: 0\n1: 1\n\"line_id\":\n\"actions\":\n0: 1\n"
  },
  {
    "path": "monolith/agent_service/test_data/inst.json",
    "content": "{\n    \"fid\": [\n        12345\n    ],\n    \"label\": [\n        0,\n        1\n    ],\n    \"line_id\": {\n        \"actions\": [\n            1\n        ]\n    }\n}"
  },
  {
    "path": "monolith/agent_service/test_data/inst.pbtext",
    "content": "fid: 1\nfid: 2\nlabel: 0.0\nlabel: 1.0\nline_id {\n  actions: 1\n}\nfeature {\n  name: \"a\"\n  fid: 3\n}"
  },
  {
    "path": "monolith/agent_service/tfs_client.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import app, flags, logging\nfrom datetime import datetime\nimport grpc\nimport json\nimport random\nimport socket\nimport os\nimport sys\nimport uuid\nimport time\nfrom struct import unpack\nfrom typing import List\nfrom google.protobuf import text_format\nfrom idl.matrix.proto.proto_parser_pb2 import Instance\nfrom multiprocessing import Pool\nimport threading\n\nfrom tensorflow_serving.apis.get_model_metadata_pb2 import GetModelMetadataRequest\nfrom tensorflow_serving.apis.get_model_status_pb2 import GetModelStatusRequest\nfrom tensorflow_serving.apis.model_service_pb2_grpc import ModelServiceStub\nfrom tensorflow_serving.apis.prediction_service_pb2_grpc import PredictionServiceStub\nfrom tensorflow_serving.apis.predict_pb2 import PredictRequest\nfrom tensorflow_serving.apis.model_management_pb2 import ReloadConfigRequest\nfrom tensorflow_serving.config.model_server_config_pb2 import ModelServerConfig\nfrom monolith.agent_service import utils\nfrom monolith.agent_service.client import FLAGS\nfrom monolith.native_training.data.utils import enable_tob_env\nfrom monolith.native_training.data.feature_list import FeatureList, get_feature_name_and_slot\nfrom idl.matrix.proto.example_pb2 import Example, ExampleBatch, FeatureListType, Feature\nfrom idl.matrix.proto.line_id_pb2 import LineId\nfrom monolith.native_training import env_utils\nfrom monolith.native_training.model_export import data_gen_utils\n\nVALID_SLOTS = []\n_NUM_SLOTS = 6\n_VOCAB_SIZES = [5, 5, 5, 5, 5, 5]\n\nflags.DEFINE_string(\"signature_name\", \"serving_default\", \"signature name\")\nflags.DEFINE_string(\"feature_list\", None, \"feature_list for prediction\")\nflags.DEFINE_enum(\"file_type\", 'pb', ['pb', 'pbtxt'], \"The input file type\")\nflags.DEFINE_integer(\"batch_size\", 8, \"batch_size for prediction\")\nflags.DEFINE_bool(\"lagrangex_header\", False, \"wheather has lagrangex_header\")\nflags.DEFINE_bool(\"has_sort_id\", False, \"wheather has sort_id\")\nflags.DEFINE_bool(\"kafka_dump\", False, \"wheather has kafka_dump\")\nflags.DEFINE_bool(\"kafka_dump_prefix\", False, \"wheather has kafka_dump_prefix\")\nflags.DEFINE_integer(\"parallel_num\", 1, \"parallel_num for profile\")\nflags.DEFINE_integer(\"profile_duration\", 600, \"second for profile\")\nflags.DEFINE_string(\"profile_data_dir\", None, \"profile input dir\")\n\nSKIP_LIST = {\n    '-', '_lt_', '_st_', '_lt', '_st', '_cp_', '_recent_', '_cp', '_recent'\n}\n\n\ndef read_header(stream):\n  int_size = 8\n  if FLAGS.lagrangex_header:\n    stream.read(int_size)\n  else:\n    aggregate_page_sortid_size = 0\n\n    if FLAGS.kafka_dump_prefix:\n      size = unpack(\"<Q\", stream.read(int_size))[0]\n      if size == 0:\n        size = unpack(\"<Q\", stream.read(int_size))[0]\n      else:\n        aggregate_page_sortid_size = size\n\n    if FLAGS.has_sort_id:\n      if aggregate_page_sortid_size == 0:\n        size = unpack(\"<Q\", stream.read(int_size))[0]\n      else:\n        aggregate_page_sortid_size = size\n      stream.read(size)\n    if FLAGS.kafka_dump:\n      stream.read(int_size)\n\n\ndef read_data(stream):\n  read_header(stream)\n  size = unpack(\"<Q\", stream.read(8))[0]\n  return stream.read(size)\n\n\ndef generate_random_instance(slots: List[int] = None,\n                             vocab_sizes: List[int] = _VOCAB_SIZES):\n  max_vocab = max(vocab_sizes)\n  if slots is None:\n    slots = list(range(1, len(_VOCAB_SIZES) + 1))\n\n  fids = [(slot << 54) | (i * max_vocab + random.randint(1, vocab_sizes[i]))\n          for i, slot in enumerate(slots)\n          for _ in range(vocab_sizes[i])]\n\n  instance = Instance()\n  instance.fid.extend(fids)\n  return instance.SerializeToString()\n\n\ndef generate_random_example_batch(feature_list: FeatureList,\n                                  batch_size: int = 256) -> str:\n  eb = ExampleBatch()\n  eb.batch_size = batch_size\n\n  for feature in feature_list:\n    flag = False\n    for s in SKIP_LIST:\n      if s in feature.name:\n        flag = True\n        break\n\n    if flag:\n      continue\n\n    if not (\"_id\" in feature.name or \"_name\" in feature.name):\n      continue\n\n    named_feature_list = eb.named_feature_list.add()\n    named_feature_list.name = feature.name\n    for _ in range(batch_size):\n      _feature = named_feature_list.feature.add()\n      if feature.method.lower().startswith(\n          'vectortop') and feature.args is not None:\n        if len(feature.args) > 0 and feature.args[0].isnumeric():\n          num = int(feature.args[0])\n          if num > 0:\n            num = random.randint(1, num)\n\n          fids = [(feature.slot << 48) | random.randint(1, sys.maxsize - 1)\n                  for _ in range(num)]\n          _feature.fid_v2_list.value.extend(fids)\n      else:\n        fid = (feature.slot << 48) | random.randint(1, (1 << 48) - 1)\n        _feature.fid_v2_list.value.append(fid)\n\n  named_feature_list = eb.named_feature_list.add()\n  named_feature_list.name = '__LINE_ID__'\n  for _ in range(batch_size):\n    _feature = named_feature_list.feature.add()\n    line_id = LineId()\n    line_id.sample_rate = 0.001\n    line_id.req_time = int(datetime.now().timestamp() - random.randint(1, 1000))\n    line_id.actions.extend([random.randint(1, 3), random.randint(3, 5)])\n    _feature.bytes_list.value.append(line_id.SerializeToString())\n\n  return eb.SerializeToString()\n\n\ndef get_instance_proto(input_file: str = None, batch_size: int = 256):\n  if input_file is None:\n    instances = [generate_random_instance() for _ in range(batch_size)]\n  else:\n    assert os.path.exists(input_file)\n    with open(input_file, 'rb') as stream:\n      instances = []\n      for _ in range(batch_size):\n        inst = Instance()\n        inst.ParseFromString(read_data(stream))\n        instances.append(inst.SerializeToString())\n  return utils.make_tensor_proto(instances)\n\n\ndef get_example_batch_proto(input_file: str = None,\n                            feature_list: FeatureList = None,\n                            batch_size: int = 256,\n                            file_type: str = 'pb'):\n  if input_file is None:\n    example_batch = generate_random_example_batch(feature_list, batch_size)\n  else:\n    assert os.path.exists(input_file)\n\n    eb = ExampleBatch()\n    if file_type == 'pb':\n      with open(input_file, 'rb') as stream:\n        eb.ParseFromString(read_data(stream))\n    else:\n      with open(input_file, 'r') as stream:\n        txt = stream.read()\n        text_format.Parse(txt, eb)\n\n    example_batch = eb.SerializeToString()\n  return utils.make_tensor_proto([example_batch])\n\n\ndef gen_random_file(input_file, variant_type=\"example_batch\"):\n  assert input_file is not None\n  assert len(VALID_SLOTS) > 0\n  parser_args = data_gen_utils.ParserArgs(\n      sparse_features=[\n          get_feature_name_and_slot(slot)[0] for slot in VALID_SLOTS\n      ],\n      extra_features=[\n          'uid', 'sample_rate', 'req_time', 'actions', 'stay_time', 'item_id',\n          'page', 'chnid'\n      ],\n      extra_feature_shapes=[1, 1, 1, 1, 1, 1, 1, 1],\n      batch_size=FLAGS.batch_size,\n      variant_type=variant_type)\n  actions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]\n\n  data_gen_utils.gen_random_data_file(input_file,\n                                      parser_args,\n                                      sort_id=FLAGS.has_sort_id,\n                                      kafka_dump=FLAGS.kafka_dump,\n                                      num_batch=1,\n                                      actions=actions)\n\n  return input_file\n\n\ndef get_example_batch_proto_v2(input_file: str):\n  if not os.path.exists(input_file):\n    gen_random_file(input_file)\n\n  eb = ExampleBatch()\n  with open(input_file, 'rb') as stream:\n    eb.ParseFromString(read_data(stream))\n\n  # use same user feature in one example_batch\n  user_fname_set = set(\n      [get_feature_name_and_slot(slot)[0] for slot in user_features])\n  for named_feature_list in eb.named_feature_list:\n    if named_feature_list.name in user_fname_set:\n      named_feature_list.type = FeatureListType.SHARED\n      new_feature = [named_feature_list.feature[0]] * eb.batch_size\n      del named_feature_list.feature[:]\n      named_feature_list.feature.extend(new_feature)\n\n  example_batch = eb.SerializeToString()\n  return utils.make_tensor_proto([example_batch])\n\n\ndef get_example_batch_to_instance(input_file: str, file_type: str):\n  assert os.path.exists(input_file)\n\n  eb = ExampleBatch()\n  if file_type == 'pb':\n    with open(input_file, 'rb') as stream:\n      eb.ParseFromString(read_data(stream))\n  else:\n    with open(input_file, 'r') as stream:\n      txt = stream.read()\n      text_format.Parse(txt, eb)\n\n  inst_list = []\n  mask = (1 << 48) - 1\n  for i in range(eb.batch_size):\n    inst = Instance()\n    for named_feature_list in eb.named_feature_list:\n      if named_feature_list.type == FeatureListType.SHARED:\n        efeat = named_feature_list.feature[0]\n      else:\n        efeat = named_feature_list.feature[i]\n\n      if named_feature_list.name == '__LABEL__':\n        inst.label.extend(efeat.float_list.value)\n      elif named_feature_list.name == '__LINE_ID__':\n        inst.line_id.ParseFromString(efeat.bytes_list.value[0])\n      elif len(efeat.fid_v1_list.value) > 0:\n        ifeat = inst.feature.add()\n        ifeat.name = named_feature_list.name\n        fid = efeat.fid_v1_list.value[0]\n        slot_id = fid >> 54\n        fid_v2 = [(slot_id << 48) | (mask & v) for v in efeat.fid_v1_list.value]\n        ifeat.fid.extend(fid_v2)\n      elif len(efeat.fid_v2_list.value) > 0:\n        ifeat = inst.feature.add()\n        ifeat.name = named_feature_list.name\n        ifeat.fid.extend(efeat.fid_v2_list.value)\n      elif len(efeat.float_list.value) > 0:\n        ifeat = inst.feature.add()\n        ifeat.name = named_feature_list.name\n        ifeat.float_value.extend(efeat.float_list.value)\n      elif len(efeat.double_list.value) > 0:\n        ifeat = inst.feature.add()\n        ifeat.name = named_feature_list.name\n        ifeat.float_value.extend(efeat.double_list.value)\n      elif len(efeat.int64_list.value) > 0:\n        ifeat = inst.feature.add()\n        ifeat.name = named_feature_list.name\n        ifeat.int64_value.extend(efeat.int64_list.value)\n      elif len(efeat.bytes_list.value) > 0:\n        ifeat = inst.feature.add()\n        ifeat.name = named_feature_list.name\n        ifeat.bytes_value.extend(efeat.bytes_list.value)\n      else:\n        pass\n\n    inst_list.append(inst.SerializeToString())\n\n  return utils.make_tensor_proto(inst_list)\n\n\nclass ProfileThread(threading.Thread):\n\n  def __init__(self, job_id, model_name, stub_list, repeat_time, data_cache):\n    super(ProfileThread, self).__init__()\n    self._job_id = job_id\n    self._model_name = model_name\n    self._stub_list = stub_list\n    self._repeat_time = repeat_time\n    self._data_cache = data_cache\n    self._data_size = len(data_cache)\n\n    self._req_count = 0\n    self._req_time_ms_list = []\n\n  def run(self):\n    run_st = int(time.time())\n    run_ed = run_st\n    show_count = 0\n    while run_ed - run_st < self._repeat_time:\n      try:\n        if self._job_id == 0 and (run_ed - run_st) >= 60 * show_count:\n          logging.info(\"Processing {}. Time: {}/{}(s)\".format(self._req_count,\n                                                              run_ed - run_st,\n                                                              self._repeat_time))\n          show_count += 1\n        request = PredictRequest()\n        request.model_spec.CopyFrom(\n            utils.gen_model_spec(self._model_name,\n                                 signature_name=FLAGS.signature_name))\n        select = random.randint(0, self._data_size - 1)\n        request.inputs[\"example_batch\"].CopyFrom(self._data_cache[select])\n        st = time.time() * 1000  # ms\n        select = random.randint(0, len(self._stub_list) - 1)\n        response = self._stub_list[select].Predict(request, 30)\n        ed = time.time() * 1000  # ms\n        req_time_ms = ed - st\n\n        self._req_time_ms_list.append(req_time_ms)\n        self._req_count += 1\n      except Exception as e:\n        logging.info(\"Warning! call request failed. {}\".format(repr(e)))\n        pass\n      run_ed = int(time.time())\n\n  def get_result(self):\n    self.join()\n    return self._req_time_ms_list\n\n\ndef main(_):\n  enable_tob_env()\n\n  env_utils.setup_host_ip()\n  agent_conf = utils.AgentConfig.from_file(FLAGS.conf)\n  host = os.environ.get(\"MY_HOST_IP\",\n                        socket.gethostbyname(socket.gethostname()))\n\n  model_name = FLAGS.model_name\n  if model_name is None:\n    if agent_conf.deploy_type == utils.DeployType.PS:\n      model_name = 'ps_{}'.format(agent_conf.shard_id)\n    elif agent_conf.deploy_type == utils.DeployType.DENSE:\n      model_name = utils.TFSServerType.DENSE\n    else:\n      model_name = utils.TFSServerType.ENTRY\n\n  if agent_conf.deploy_type == utils.DeployType.PS:\n    target = FLAGS.target or f\"{host}:{agent_conf.tfs_ps_port}\"\n  elif agent_conf.deploy_type == utils.DeployType.DENSE:\n    target = FLAGS.target or f\"{host}:{agent_conf.tfs_dense_port}\"\n  else:\n    target = FLAGS.target or f\"{host}:{agent_conf.tfs_entry_port}\"\n\n  target_list = target.split(',')\n  channel_list = [grpc.insecure_channel(tg) for tg in target_list]\n  channel = grpc.insecure_channel(target_list[0])\n\n  if FLAGS.cmd_type == 'status':\n    stub = ModelServiceStub(channel)\n    request = GetModelStatusRequest()\n    request.model_spec.CopyFrom(\n        utils.gen_model_spec(model_name, signature_name=FLAGS.signature_name))\n    print(stub.GetModelStatus(request))\n  elif FLAGS.cmd_type == 'meta':\n    stub = PredictionServiceStub(channel)\n    request = GetModelMetadataRequest()\n    request.model_spec.CopyFrom(\n        utils.gen_model_spec(model_name, signature_name=FLAGS.signature_name))\n    request.metadata_field.extend(\n        ['base_path', 'num_versions', 'signature_name'])\n\n    response = stub.GetModelMetadata(request)\n    print(response)\n  elif FLAGS.cmd_type == 'load':\n    request = ReloadConfigRequest()\n    model_configs = ModelServerConfig()\n    with open(FLAGS.input_file, 'r') as stream:\n      txt = stream.read()\n      text_format.Parse(txt, model_configs)\n\n    request.config.CopyFrom(model_configs)\n    stub = ModelServiceStub(channel)\n    response = stub.HandleReloadConfigRequest(request)\n    logging.info(f'{model_configs} load done!')\n\n    return response.status\n  elif FLAGS.cmd_type == 'profile':\n    assert len(VALID_SLOTS) > 0\n    # ./tfs_client --conf=/path/agent.conf --cmd_type=\"get\" --input_type=\"example_batch\" --batch_size=128 --has_sort_id\n    data_path_list = []\n    base_data_dir = FLAGS.profile_data_dir;\n    assert base_data_dir is not None\n    if not os.path.exists(base_data_dir):\n      os.makedirs(base_data_dir)\n    for file_name in os.listdir(base_data_dir):\n      data_path_list.append(os.path.join(base_data_dir, file_name))\n    data_num = 500\n    from tqdm import tqdm\n    if len(data_path_list) < data_num:\n      add_num = data_num - len(data_path_list)\n      for i in tqdm(range(add_num), desc=\"gen_random_file\"):\n        data_path = os.path.join(base_data_dir, \"{}.pb\".format(uuid.uuid1()))\n        gen_random_file(data_path)\n        data_path_list.append(data_path)\n    data_path_list = data_path_list[:data_num]\n\n    data_cache = []\n    for data_path in tqdm(data_path_list, desc=\"read_data\"):\n      data_cache.append(get_example_batch_proto_v2(data_path))\n\n    parallel_num = FLAGS.parallel_num\n    repeat_time = FLAGS.profile_duration\n\n    # stub = PredictionServiceStub(channel)\n    stub_list = [PredictionServiceStub(chnl) for chnl in channel_list]\n\n    thread_list = []\n    e2e_st = time.time() * 1000  # ms\n    for i in range(parallel_num):\n      thread = ProfileThread(i, model_name, stub_list, repeat_time, data_cache)\n      thread.start()\n      thread_list.append(thread)\n\n    total_req_time_ms_list = []\n    for thread in thread_list:\n      req_time_ms_list = thread.get_result()\n      total_req_time_ms_list.extend(req_time_ms_list)\n    e2e_ed = time.time() * 1000  # ms\n\n    if len(total_req_time_ms_list) > 0:\n      avg_req_time_ms = sum(total_req_time_ms_list) / len(\n          total_req_time_ms_list)\n      total_req_time_ms_list.sort()\n      p99_req_time_ms = total_req_time_ms_list[int(\n          round((len(total_req_time_ms_list) - 1) * 0.99))]\n      qps = len(total_req_time_ms_list) * 1000 / (e2e_ed - e2e_st)\n    else:\n      avg_req_time_ms = 0\n      p99_req_time_ms = 0\n      qps = 0\n    logging.info(\"[Profile] Count: {}, Avg Latency: {}, P99 Latency: {}, QPS: {}\".format(\n        len(total_req_time_ms_list), avg_req_time_ms, p99_req_time_ms, qps))\n  else:  # get\n    # url = f\"http://{target}/v1/models/{model_name}:{FLAGS.signature_name}\"\n    # cmd = ['curl', '-d', f\"'{FLAGS.inputs}'\", '-X', 'POST', url]\n    # output = subprocess.check_output(cmd, shell=True)\n    # print(output)\n\n    stub = PredictionServiceStub(channel)\n    request = PredictRequest()\n    request.model_spec.CopyFrom(\n        utils.gen_model_spec(model_name, signature_name=FLAGS.signature_name))\n\n    if FLAGS.input_type == 'instance':\n      if not os.path.exists(FLAGS.input_file):\n        gen_random_file(FLAGS.input_file, \"instance\")\n      tensor_proto = get_instance_proto(FLAGS.input_file, FLAGS.batch_size)\n      request.inputs[\"instances\"].CopyFrom(tensor_proto)\n    elif FLAGS.input_type == 'example_batch':\n      if FLAGS.input_file is None:\n        input_file = \"{}.pb\".format(uuid.uuid1())\n      else:\n        input_file = FLAGS.input_file\n      tensor_proto = get_example_batch_proto_v2(input_file)\n      request.inputs[\"example_batch\"].CopyFrom(tensor_proto)\n    else:\n      tensor_proto = get_example_batch_to_instance(FLAGS.input_file,\n                                                   FLAGS.file_type)\n      request.inputs[\"instances\"].CopyFrom(tensor_proto)\n\n    response = stub.Predict(request, 30)\n    print(response)\n\n\nif __name__ == \"__main__\":\n  app.run(main)\n"
  },
  {
    "path": "monolith/agent_service/tfs_client_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging, app, flags\nimport json\nimport os\nimport tempfile\nimport unittest\n\nfrom monolith.agent_service.tfs_client import get_instance_proto\nfrom monolith.agent_service.tfs_client import get_example_batch_to_instance\n\nFLAGS = flags.FLAGS\n\n\nclass TFSClientTest(unittest.TestCase):\n\n  def test_get_instance_proto(self):\n    tensor_proto = get_instance_proto()\n    self.assertEqual(tensor_proto.dtype, 7)\n    self.assertEqual(tensor_proto.tensor_shape.dim[0].size, 256)\n\n  def test_get_example_batch_to_instance_from_pb(self):\n    file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n    FLAGS.lagrangex_header = True\n    get_example_batch_to_instance(file_name, 'pb')\n\n  def test_get_example_batch_to_instance_from_pbtxt(self):\n    file_name = \"monolith/agent_service/example_batch.pbtxt\"\n    FLAGS.lagrangex_header = True\n    get_example_batch_to_instance(file_name, 'pbtxt')\n\n\ndef main(_):\n  unittest.main()\n\n\nif __name__ == \"__main__\":\n  app.run(main)\n"
  },
  {
    "path": "monolith/agent_service/tfs_monitor.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport os\nimport grpc\nfrom threading import RLock\nfrom functools import singledispatchmethod\nfrom typing import Dict, Iterable, Union, List\n\nfrom tensorflow.core.protobuf.error_codes_pb2 import Code\nfrom tensorflow_serving.util.status_pb2 import StatusProto\nfrom tensorflow_serving.apis.get_model_status_pb2 import ModelVersionStatus, GetModelStatusRequest\nfrom tensorflow_serving.apis.model_management_pb2 import ReloadConfigRequest\nfrom tensorflow_serving.apis.prediction_service_pb2_grpc import PredictionServiceStub\nfrom tensorflow_serving.apis.model_service_pb2_grpc import ModelServiceStub\nfrom tensorflow_serving.config.model_server_config_pb2 import ModelServerConfig\n\nfrom monolith.agent_service.data_def import PublishMeta, SubModelName, TFSModelName, \\\n  VersionPath, PublishType as PType\nfrom monolith.agent_service.utils import AgentConfig, gen_model_spec, gen_model_config, \\\n  DeployType, TFSServerType, get_local_ip, DEFAULT_MODEL_CONFIG\n\nState = ModelVersionStatus.State\n\n\nclass TFSMonitor(object):\n\n  def __init__(self, config: AgentConfig):\n    self._conf: AgentConfig = config\n    self._host = None\n    self._lock = RLock()\n    self.stubs = {\n        TFSServerType.ENTRY: {},\n        TFSServerType.PS: {},\n        TFSServerType.DENSE: {}\n    }\n\n  @property\n  def host(self):\n    if self._host is None or self._host in {'', 'localhost', '127.0.0.1'}:\n      self._host = get_local_ip()\n\n    return self._host\n\n  def get_addr(self, sub_model_name: SubModelName) -> str:\n    if self._conf.deploy_type == DeployType.MIXED:\n      if self.is_entry(sub_model_name):\n        return f\"{self.host}:{self._conf.tfs_entry_port}\"\n      elif self.is_ps(sub_model_name):\n        return f\"{self.host}:{self._conf.tfs_ps_port}\"\n      else:\n        return f\"{self.host}:{self._conf.tfs_dense_port}\"\n    elif self._conf.deploy_type == DeployType.ENTRY:\n      assert self.is_entry(sub_model_name)\n      return f\"{self.host}:{self._conf.tfs_entry_port}\"\n    elif self._conf.deploy_type == DeployType.PS:\n      assert self.is_ps(sub_model_name)\n      return f\"{self.host}:{self._conf.tfs_ps_port}\"\n    elif self._conf.deploy_type == DeployType.DENSE:\n      assert self.is_dense(sub_model_name)\n      return f\"{self.host}:{self._conf.tfs_dense_port}\"\n    else:\n      raise RuntimeError(f'deploy_type {self._conf.deploy_type} is error')\n\n  def get_service_type(self, sub_model_name: SubModelName):\n    if self._conf.deploy_type == DeployType.ENTRY:\n      return TFSServerType.ENTRY if sub_model_name.startswith(\n          TFSServerType.ENTRY) else None\n    elif self._conf.deploy_type == DeployType.PS:\n      return TFSServerType.PS if sub_model_name.startswith(\n          TFSServerType.PS) else None\n    elif self._conf.deploy_type == DeployType.DENSE:\n      return TFSServerType.DENSE if sub_model_name.startswith(\n          TFSServerType.DENSE) else None\n    else:\n      assert self._conf.deploy_type == DeployType.MIXED\n      if sub_model_name.startswith(TFSServerType.ENTRY):\n        return TFSServerType.ENTRY\n      elif sub_model_name.startswith(TFSServerType.PS):\n        return TFSServerType.PS\n      elif sub_model_name.startswith(TFSServerType.DENSE):\n        return TFSServerType.DENSE\n      else:\n        return None\n\n  def is_entry(self, sub_model_name: str):\n    return sub_model_name.startswith('entry')\n\n  def is_ps(self, sub_model_name: str):\n    return sub_model_name.startswith('ps')\n\n  def is_dense(self, sub_model_name: str):\n    return sub_model_name.startswith('dense')\n\n  def connect(self):\n    if self._conf.deploy_type in {DeployType.MIXED, DeployType.ENTRY}:\n      entry_channel = grpc.insecure_channel(\n          f'{self.host}:{self._conf.tfs_entry_port}')\n      self.stubs[TFSServerType.ENTRY]['channel'] = entry_channel\n      self.stubs[TFSServerType.ENTRY]['model_service'] = ModelServiceStub(\n          entry_channel)\n      self.stubs[TFSServerType.ENTRY][\n          'prediction_service'] = PredictionServiceStub(entry_channel)\n\n    if self._conf.deploy_type in {DeployType.MIXED, DeployType.PS}:\n      ps_channel = grpc.insecure_channel(\n          f'{self.host}:{self._conf.tfs_ps_port}')\n      self.stubs[TFSServerType.PS]['channel'] = ps_channel\n      self.stubs[TFSServerType.PS]['model_service'] = ModelServiceStub(\n          ps_channel)\n      self.stubs[TFSServerType.\n                 PS]['prediction_service'] = PredictionServiceStub(ps_channel)\n\n    if self._conf.dense_alone and self._conf.deploy_type in {\n        DeployType.MIXED, DeployType.DENSE\n    }:\n      dense_channel = grpc.insecure_channel(\n          f'{self.host}:{self._conf.tfs_dense_port}')\n      self.stubs[TFSServerType.DENSE]['channel'] = dense_channel\n      self.stubs[TFSServerType.DENSE]['model_service'] = ModelServiceStub(\n          dense_channel)\n      self.stubs[TFSServerType.DENSE][\n          'prediction_service'] = PredictionServiceStub(dense_channel)\n\n  def start(self):\n    self.stubs = {\n        TFSServerType.ENTRY: {},\n        TFSServerType.PS: {},\n        TFSServerType.DENSE: {}\n    }\n    self.connect()\n\n  def stop(self):\n    if len(self.stubs) > 0:\n      for stub in self.stubs.values():\n        if 'channel' in stub:\n          try:\n            stub['channel'].close()\n            stub['model_service'] = None\n            stub['prediction_service'] = None\n          except:\n            logging.error('stop channel fail!')\n      self.stubs.clear()\n\n  @singledispatchmethod\n  def get_model_status(self, arg):\n    raise NotImplementedError(\"get_model_status is not implemented!\")\n\n  @get_model_status.register\n  def _(self, pm: PublishMeta, fix_dense_version: bool = False):\n    # return 'Dict[TFSModelName, (VersionPath, ModelVersionStatus)]'\n    with self._lock:\n      model_status: Dict[str, State] = {}\n      for sub_model_name, smvpath in pm.sub_models.items():\n        service_type = self.get_service_type(sub_model_name)\n        if service_type is None:\n          continue\n\n        tfs_model_name = f'{pm.model_name}:{sub_model_name}'\n        request = GetModelStatusRequest()\n        # TODO(ltli): 这步修改不确定\n        is_dense_node = (\n            (not self._conf.dense_alone and self.is_entry(sub_model_name)) or\n            (self._conf.dense_alone and self.is_dense(sub_model_name)))\n        if not fix_dense_version and is_dense_node:\n          request.model_spec.CopyFrom(gen_model_spec(tfs_model_name))\n        else:\n          version = int(os.path.basename(smvpath))\n          request.model_spec.CopyFrom(gen_model_spec(tfs_model_name, version))\n\n        stub: ModelServiceStub = self.stubs[service_type]['model_service']\n\n        try:\n          model_version_status = stub.GetModelStatus(\n              request).model_version_status\n          if model_version_status is None or len(model_version_status) == 0:\n            status = ModelVersionStatus(\n                state=State.UNKNOWN,\n                status=StatusProto(error_code=Code.NOT_FOUND,\n                                   error_message=f'{tfs_model_name} not found'))\n          else:\n            # if there are more than one version, select the latest one\n            model_version_status = sorted(model_version_status,\n                                          key=lambda mvs: mvs.version)\n            status = model_version_status[-1]\n        except grpc._channel._InactiveRpcError as e:\n          logging.info(repr(e))\n          status = ModelVersionStatus(state=State.UNKNOWN,\n                                      status=StatusProto(\n                                          error_code=e.code().value[0],\n                                          error_message=e.details()))\n\n        model_status[tfs_model_name] = (smvpath, status)\n\n      return model_status\n\n  @get_model_status.register\n  def _(self,\n        name: str,\n        version: Union[int, str] = None,\n        signature_name: str = None) -> List[ModelVersionStatus]:\n    \"\"\"Get model version status\n    \n      :param name: The model name\n      :param version: The version of model. If  not specify version,\n                      information about all versions of the model will be returned.\n                      \n      :return a list of ModelVersionStatus, which has three attribute:\n        - version: int, Model version.\n        - state: State, Model state, A Enum of UNKNOWN, START, LOADING, AVAILABLE, UNLOADING, END.\n        - status: StatusProto, Model status.\n    \"\"\"\n\n    with self._lock:\n      service_type = self.get_service_type(SubModelName(name))\n      if service_type is None:\n        return []\n      else:\n        request = GetModelStatusRequest()\n        request.model_spec.CopyFrom(\n            gen_model_spec(name, version, signature_name))\n        stub = self.stubs[service_type]['model_service']\n        return stub.GetModelStatus(request).model_version_status\n\n  def gen_model_config(\n      self,\n      pms: Iterable[PublishMeta],\n      fix_dense_version: bool = False) -> Dict[str, ModelServerConfig]:\n    model_configs = {\n        TFSServerType.ENTRY: ModelServerConfig(),\n        TFSServerType.PS: ModelServerConfig(),\n        TFSServerType.DENSE: ModelServerConfig()\n    }\n\n    for pm in pms:\n      if pm.ptype == PType.UNLOAD:\n        continue\n\n      for sub_model_name, smv_path in pm.sub_models.items():\n        tfs_model_name = f'{pm.model_name}:{sub_model_name}'\n        service_type = self.get_service_type(sub_model_name)\n        if service_type is None:\n          continue\n\n        base_path = os.path.dirname(smv_path)\n        # TODO(ltli): 这步修改不确定\n        is_dense_node = (\n            (not self._conf.dense_alone and self.is_entry(sub_model_name)) or\n            (self._conf.dense_alone and self.is_dense(sub_model_name)))\n        if is_dense_node:\n          version_policy = 'specific' if fix_dense_version else 'latest'\n          version_data = int(\n              os.path.basename(smv_path)) if fix_dense_version else 1\n        else:\n          version_policy = 'specific'\n          version_data = int(os.path.basename(smv_path))\n\n        model_config = gen_model_config(tfs_model_name, base_path,\n                                        version_policy, version_data)\n        model_configs[service_type].model_config_list.config.append(\n            model_config)\n\n    return model_configs\n\n  def handle_reload_config_request(\n      self, service_type: str, model_configs: ModelServerConfig) -> StatusProto:\n    with self._lock:\n      request = ReloadConfigRequest()\n      # keep default model in memory, incase no model in tfs\n      model_config_list = model_configs.model_config_list.config\n      if not any(mc.name == 'default' for mc in model_config_list):\n        model_config = model_config_list.add()\n        model_config.CopyFrom(DEFAULT_MODEL_CONFIG)\n\n      request.config.CopyFrom(model_configs)\n      if service_type == TFSServerType.ENTRY:\n        port = self._conf.tfs_entry_port\n      elif service_type == TFSServerType.PS:\n        port = self._conf.tfs_ps_port\n      else:\n        port = self._conf.tfs_dense_port\n      logging.info(f'{service_type} load ({port}): \\n{request}')\n      try:\n        response = self.stubs[service_type][\n            'model_service'].HandleReloadConfigRequest(request)\n        logging.info(f'{service_type} load done!')\n      except Exception as e:\n        logging.info(repr(e))\n        raise e\n\n      return response.status\n"
  },
  {
    "path": "monolith/agent_service/tfs_monitor_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport socket\nimport time\nimport threading\nimport random\nimport unittest\n\nfrom monolith.agent_service import constants\nfrom monolith.agent_service import utils\nfrom monolith.agent_service.tfs_monitor import TFSMonitor\nfrom monolith.agent_service.mocked_tfserving import FakeTFServing\n\nfrom tensorflow_serving.config.model_server_config_pb2 import ModelServerConfig\nfrom monolith.agent_service.data_def import PublishMeta, SubModelName, TFSModelName, \\\n  VersionPath, PublishType as PType\nfrom monolith.agent_service.utils import AgentConfig, gen_model_spec, gen_model_config, \\\n  TFSServerType, get_local_ip\n\nMODEL_NAME = 'test_model'\nBASE_PATH = f'/tmp/{MODEL_NAME}/monolith'\nversion = '1634631496'\npath = '/tmp/monolith/agent_service/test_data/ckpt/exported_models/{}/{}'\n\n\nclass TFSMonitorTest(unittest.TestCase):\n  tfs: FakeTFServing = None\n  monitor: TFSMonitor = None\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    os.environ[constants.HOST_SHARD_ENV] = '10'\n    os.environ['SHARD_ID'] = '1'\n    os.environ['REPLICA_ID'] = '2'\n    cls.agent_conf = utils.AgentConfig(bzid='bzid', deploy_type='mixed')\n\n    cls.tfs_entry = FakeTFServing(num_versions=2,\n                                  port=cls.agent_conf.tfs_entry_port,\n                                  model_config_file=ModelServerConfig())\n    cls.tfs_ps = FakeTFServing(num_versions=2,\n                               port=cls.agent_conf.tfs_ps_port,\n                               model_config_file=ModelServerConfig())\n\n    entry = threading.Thread(target=lambda: cls.tfs_entry.start())\n    entry.start()\n    ps = threading.Thread(target=lambda: cls.tfs_ps.start())\n    ps.start()\n    time.sleep(2)\n\n    cls.monitor = TFSMonitor(cls.agent_conf)\n    cls.monitor.connect()\n\n    cls.data = {}\n\n  @classmethod\n  def tearDownClass(cls) -> None:\n    cls.monitor.stop()\n    cls.tfs_entry.stop()\n    cls.tfs_ps.stop()\n\n  def setUp(self):\n    sub_models: Dict[SubModelName, VersionPath] = {\n        'entry': path.format('entry', version),\n        'ps_0': path.format('ps_0', version),\n        'ps_3': path.format('ps_3', version),\n        'ps_5': path.format('ps_5', version)\n    }\n    pm = PublishMeta(shard_id=self.agent_conf.shard_id,\n                     replica_id=self.agent_conf.replica_id,\n                     model_name='test_1',\n                     num_ps=5,\n                     sub_models=sub_models)\n    self.data['setUp'] = self.monitor.get_model_status(pm)\n\n  def tearDown(self):\n    sub_models: Dict[SubModelName, VersionPath] = {\n        'entry': path.format('entry', version),\n        'ps_0': path.format('ps_0', version),\n        'ps_3': path.format('ps_3', version),\n        'ps_5': path.format('ps_5', version)\n    }\n    pm = PublishMeta(shard_id=self.agent_conf.shard_id,\n                     replica_id=self.agent_conf.replica_id,\n                     model_name='test_1',\n                     num_ps=5,\n                     sub_models=sub_models)\n\n    time.sleep(1)\n    before_status = self.data['setUp']\n    after_status = self.monitor.get_model_status(pm)\n    self.assertEqual(len(before_status), len(after_status))\n\n    if self.data['execute'] == 'reload_config':\n      for tfs_model_name, (bvp, bstate) in before_status.items():\n        (avp, astate) = after_status[tfs_model_name]\n        self.assertEqual(bvp, avp)\n        self.assertTrue(bstate.version == -1 and\n                        bstate.status.error_code == 5)  # NOT_FOUND\n        if astate.version == -1:\n          pass\n        elif astate.version == 1:\n          self.assertTrue(tfs_model_name.endswith('entry'))\n        else:\n          self.assertEqual(astate.version, int(os.path.basename(bvp)))\n    else:\n      for tfs_model_name, (bvp, bstate) in before_status.items():\n        (avp, astate) = after_status[tfs_model_name]\n        self.assertEqual(astate.version, -1)\n        self.assertEqual(bvp, avp)\n        if bstate.version == -1:\n          self.assertTrue(bstate.status.error_code == 5)  # NOT_FOUND\n        else:\n          self.assertTrue(bstate.version > 0)\n\n  def test_reload_config(self):\n    pms = []\n    for i in range(10):\n      num_ps = random.randint(5, 20)\n      sub_models = {\n          f'ps_{i}': path.format(f'ps_{i}', version)\n          for i in range(num_ps)\n          if i % 3 == 0\n      }\n      sub_models[TFSServerType.ENTRY] = path.format(f'entry', version)\n\n      pm = PublishMeta(shard_id=self.agent_conf.shard_id,\n                       replica_id=self.agent_conf.replica_id,\n                       model_name=f'test_{i}',\n                       num_ps=num_ps,\n                       sub_models=sub_models)\n      pms.append(pm)\n\n    model_configs: Dict[str,\n                        ModelServerConfig] = self.monitor.gen_model_config(pms)\n    for service_type, model_config in model_configs.items():\n      if len(model_config.model_config_list.config) > 0:\n        status = self.monitor.handle_reload_config_request(\n            service_type, model_config)\n    self.data['execute'] = 'reload_config'\n\n  def test_remove_config(self):\n    pms = []\n    for i in range(5, 10):\n      num_ps = random.randint(5, 20)\n      sub_models = {\n          f'ps_{i}': path.format(f'ps_{i}', version)\n          for i in range(num_ps)\n          if i % 3 == 0\n      }\n      sub_models[TFSServerType.ENTRY] = path.format(f'entry', version)\n\n      pm = PublishMeta(shard_id=self.agent_conf.shard_id,\n                       replica_id=self.agent_conf.replica_id,\n                       model_name=f'test_{i}',\n                       num_ps=num_ps,\n                       sub_models=sub_models)\n      pms.append(pm)\n\n    model_configs: Dict[str,\n                        ModelServerConfig] = self.monitor.gen_model_config(pms)\n    for service_type, model_config in model_configs.items():\n      if len(model_config.model_config_list.config) > 0:\n        status = self.monitor.handle_reload_config_request(\n            service_type, model_config)\n    self.data['execute'] = 'remove_config'\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/agent_service/tfs_wrapper.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport subprocess\nfrom typing import get_type_hints\n\nimport grpc\nfrom absl import app, flags, logging\nfrom google.protobuf import text_format\nfrom tensorflow_serving.util.status_pb2 import StatusProto\nfrom tensorflow_serving.config import model_server_config_pb2\nfrom tensorflow_serving.apis import model_pb2, get_model_status_pb2\nfrom tensorflow_serving.apis.get_model_status_pb2 import GetModelStatusRequest, ModelVersionStatus\nfrom tensorflow_serving.apis.model_service_pb2_grpc import ModelServiceStub\nfrom monolith.utils import find_main\nfrom monolith.agent_service.utils import TFS_HOME, TfServingConfig\n\nState = ModelVersionStatus.State\nTFS_BINARY = os.environ.get('MONOLITH_TFS_BINARY', None)\nclass TFSWrapper(object):\n\n  def __init__(self, archon_port: int, grpc_port: int, http_port: int,\n               model_config_file: str, binary_config: TfServingConfig,\n               log_file: str):\n    self._archon_port = archon_port\n    self._grpc_port = grpc_port\n    self._http_port = http_port\n    self._model_config_file = model_config_file\n    self._binary_config = binary_config\n    self._log_file = log_file\n    self._proc = None\n\n    # model service\n    self._channel = None\n    self._stub = None\n\n    cp = subprocess.run(f\"strings {TFS_BINARY} | grep PredictionServiceGrpc\",\n                        shell=True)\n    self._is_grpc_remote_op = cp.returncode == 0\n\n  def _prepare_cmd(self):\n    flags = []\n    flags.append(f'--model_config_file={self._model_config_file}')\n    flags.append(f\"--port={self._grpc_port}\")\n    flags.append(f\"--rest_api_port={self._http_port}\")\n    flags.append(\"--model_config_file_poll_wait_seconds=60\")\n\n    psm = os.environ.get(\"TCE_PSM\", \"\")\n    cluster = os.environ.get(\"TCE_CLUSTER\", \"\")\n    prefix = psm\n    flags.append(f\"--archon_port={self._archon_port}\")\n    flags.append(f\"--archon_rpc_psm={psm}\")\n    flags.append(f\"--archon_rpc_cluster={cluster}\")\n    flags.append(f\"--metrics_namespace_prefix={prefix}\")\n    if not self._is_grpc_remote_op:\n      flags.append(\n          f'--archon_entry_to_ps_rpc_timeout={self._binary_config.fetch_ps_timeout_ms}'\n      )\n    # set some dummy config for archon\n    flags.append(\"--conf_file=conf/service.conf\")\n    flags.append(\"--log_conf=conf/log4j.properties\")\n\n    for key, clz in get_type_hints(TfServingConfig).items():\n      default = getattr(TfServingConfig, key)\n      value = getattr(self._binary_config, key)\n      if key == 'platform_config_file':\n        platform_config_file = value or default\n        if platform_config_file is None:\n          flags.append('--platform_config_file=conf/platform_config_file.cfg')\n        else:\n          flags.append(f'--{key}={platform_config_file}')\n      elif value != default:\n        if clz == bool:\n          flags.append(f'--{key}={str(value).lower()}')\n        else:\n          flags.append(f'--{key}={value}')\n\n    return f'{TFS_BINARY} {\" \".join(flags)}'\n\n  @property\n  def is_grpc_remote_op(self):\n    return self._is_grpc_remote_op\n\n  def start(self):\n    os.chdir(find_main())\n    tfs_cmd = self._prepare_cmd()\n    logging.info(\n        f\"starting {'grpc' if self._is_grpc_remote_op else 'archon'} tfs in {os.getcwd()} using command {tfs_cmd}\"\n    )\n    with open(self._log_file, \"w\") as log_stdout:\n      self._proc = subprocess.Popen(tfs_cmd.split(),\n                                    shell=False,\n                                    stderr=subprocess.STDOUT,\n                                    stdout=log_stdout,\n                                    env=os.environ)\n\n    self._channel = grpc.insecure_channel(f'localhost:{self._grpc_port}')\n    self._stub = ModelServiceStub(self._channel)\n\n  def stop(self):\n    logging.info(\"stoping tfs\")\n    try:\n      self._channel.close()\n      if self._proc is not None and self._proc.stdout is not None:\n        self._proc.stdout.close()\n    except Exception as e:\n      logging.info(e)\n    finally:\n      self._proc.kill()\n\n  def poll(self):\n    self._proc.poll()\n    return self._proc.returncode\n\n  def model_config_text(self):\n    with open(self._model_config_file, \"r\") as output:\n      return output.read()\n\n  def list_saved_models(self):\n    model_server_config = text_format.Parse(\n        self.model_config_text(), model_server_config_pb2.ModelServerConfig())\n    model_config_list = model_server_config.model_config_list.config\n    return [config.name for config in model_config_list]\n\n  def list_saved_models_status(self):\n    saved_models = self.list_saved_models()\n    model_status = {}\n    for saved_model in saved_models:\n      model_spec = model_pb2.ModelSpec(name=saved_model)\n      request = GetModelStatusRequest()\n      request.model_spec.CopyFrom(model_spec)\n      try:\n        model_version_status = self._stub.GetModelStatus(\n            request).model_version_status\n        if model_version_status is None or len(model_version_status) == 0:\n          status = State.UNKNOWN\n        else:\n          # if there are more than one version, select the available one\n          model_version_status = sorted(model_version_status,\n                                        key=lambda mvs: mvs.version)\n          available_version_status = [\n              mvs for mvs in model_version_status\n              if mvs.state == State.AVAILABLE\n          ]\n          if available_version_status:\n            status = available_version_status[-1]\n          else:\n            status = model_version_status[-1]\n      except grpc.RpcError as e:\n        logging.error(repr(e))\n        status = ModelVersionStatus(state=State.UNKNOWN,\n                                    status=StatusProto(\n                                        error_code=e.code().value[0],\n                                        error_message=e.details()))\n\n      model_status[saved_model] = status\n    return model_status\n\n\nclass FakeTFSWrapper(object):\n\n  def __init__(self, model_config_file: str):\n    self._model_config_file = model_config_file\n\n  def start(self):\n    logging.info(\"starting tfs\")\n\n  def stop(self):\n    logging.info(\"stoping tfs\")\n\n  def poll(self):\n    return None\n\n  def model_config_text(self):\n    with open(self._model_config_file, \"r\") as output:\n      return output.read()\n\n  def list_saved_models(self):\n    model_server_config = text_format.Parse(\n        self.model_config_text(), model_server_config_pb2.ModelServerConfig())\n    model_config_list = model_server_config.model_config_list.config\n    return [config.name for config in model_config_list]\n\n  def list_saved_models_status(self):\n    saved_models = self.list_saved_models()\n    model_status = {}\n    for saved_model in saved_models:\n      status = ModelVersionStatus(state=State.AVAILABLE)\n      model_status[saved_model] = status\n    return model_status\n"
  },
  {
    "path": "monolith/agent_service/utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging, flags\nfrom contextlib import closing\nfrom dataclasses import dataclass\nimport google.protobuf.text_format as text_format\nimport google.protobuf.json_format as json_format\nimport json\nimport os\nimport re\nimport socket\nimport tempfile\nfrom typing import Dict, List, Union, Optional, get_type_hints\nfrom idl.matrix.proto.proto_parser_pb2 import Instance\n\nfrom tensorflow.core.framework.tensor_pb2 import TensorProto\nfrom tensorflow.core.framework.types_pb2 import DataType\nfrom tensorflow.core.protobuf.error_codes_pb2 import Code as ErrorCode\nfrom tensorflow_serving.apis import model_pb2\nfrom tensorflow_serving.apis.get_model_status_pb2 import ModelVersionStatus\nfrom tensorflow_serving.config import model_server_config_pb2\nfrom tensorflow_serving.sources.storage_path.file_system_storage_path_source_pb2 import \\\n  FileSystemStoragePathSourceConfig\nfrom tensorflow_serving.util.status_pb2 import StatusProto\nfrom tensorflow_serving.config import platform_config_pb2\nfrom tensorflow_serving.servables.tensorflow import session_bundle_config_pb2\nfrom tensorflow_serving.servables.tensorflow import saved_model_bundle_source_adapter_pb2\nfrom tensorflow.core.protobuf.config_pb2 import ConfigProto\n\nfrom monolith.agent_service import constants\nfrom monolith.native_training.zk_utils import default_zk_servers, _HOSTS, _PORT, is_ipv6_only\nimport hashlib\n\nModelState = ModelVersionStatus.State\nSEQ = re.compile(r\"[ =\\t]+\")\nServableVersionPolicy = FileSystemStoragePathSourceConfig.ServableVersionPolicy\nFeatureKeys = {\n    'name', 'fid', 'float_value', 'int64_value', 'bytes_value', 'fid_list',\n    'float_list', 'int64_list', 'bytes_list'\n}\nflags.DEFINE_string(\"conf\", \"\", \"agent conf file\")\n\nTFS_HOME = \"/opt/tiger/monolith_serving\"\nDEFAULT_MODEL_CONFIG = None\nDEFAULT_PLATFORM_CONFIG_FILE = \"{}/conf/platform_config_file.cfg\".format(TFS_HOME)\nold_isabs = os.path.isabs\n\n\ndef isabs(path: str):\n  if path.startswith('hdfs:/'):\n    return True\n  else:\n    return old_isabs(path)\n\n\nos.path.isabs = isabs\nDefaultRoughSortModelLocalPath = None\nDefaultRoughSortModelP2PPath = None\nclass TFSServerType:\n  PS = 'ps'\n  ENTRY = 'entry'\n  DENSE = 'dense'\n  UNIFIED = 'unified'\n\n\nclass DeployType(TFSServerType):\n  MIXED = 'mixed'  # bath ps anf entry are host in one tfs\n\n  def __init__(self, dtype: str):\n    assert dtype.lower() in {\n        self.ENTRY, self.PS, self.DENSE, self.MIXED, self.UNIFIED\n    }\n    self._dtype = dtype.lower()\n\n  def __str__(self):\n    return self._dtype\n\n  def __hash__(self):\n    return hash(self._dtype)\n\n  def __eq__(self, o):\n    if isinstance(o, str):\n      return self._dtype == o\n    elif isinstance(o, DeployType):\n      return self._dtype == o._dtype\n    else:\n      return False\n\n  def compat_server_type(self, server_type: str):\n    if server_type is None or server_type == DeployType.MIXED:\n      if self._dtype == DeployType.MIXED:\n        raise RuntimeError('DeployType and ServerType is not compatable!')\n      else:\n        return self._dtype\n    elif self._dtype == DeployType.MIXED:\n      return server_type\n    else:\n      assert self._dtype == server_type\n      return server_type\n\n\nclass RoughSortModelLoadedServer:\n  NONE = 'none'\n  ENTRY = 'entry'\n  PS = 'ps'\n  DENSE = 'dense'\n\n\nclass RoughSortModelPrefix:\n  PS = 'ps_item_embedding'\n  ENTRY = 'entry_item_embedding'\n  DENSE = 'dense_item_embedding'\n\n\ndef conf_parser(file_name: str, args: dict):\n  if not os.path.exists(file_name):\n    return\n\n  with open(file_name) as f:\n    for line in f:\n      line = line.strip()\n      if line.startswith('#') or len(line) == 0:\n        continue\n      else:\n        idx = line.find('#')\n        if idx > 0:\n          line = line[0:idx]\n\n        if line.startswith('include'):\n          conf_parser(line.split()[-1], args)\n        else:\n          try:\n            key, value = SEQ.split(line, maxsplit=1)\n            if key in args:\n              if type(args[key]) is not list:\n                args[key] = [args[key]] + [value]\n            elif value is not None and len(value) > 0:\n              args[key] = value\n          except Exception as e:\n            logging.error(f'{e}')\n\n\ndef find_free_port():\n  with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:\n    s.bind(('localhost', 0))\n    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n    host, port = s.getsockname()\n    return port\n\n\ndef check_port_open(port):\n  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n  try:\n    s.connect(('127.0.0.1', port))\n    s.close()\n  except Exception:\n    logging.info(f'port {port} not open!')\n    return False\n  logging.info(f'port {port} opened!')\n  return True\n\n\ndef write_to_tmp_file(content) -> str:\n  fd, path = tempfile.mkstemp()\n  with os.fdopen(fd, 'w') as fp:\n    fp.write(str(content))\n  return path\n\n\ndef replica_id_from_pod_name() -> int:\n  try:\n    if 'MY_POD_NAME' in os.environ:\n      md5 = hashlib.md5()\n      pod_name = os.environ.get('MY_POD_NAME', 'pod_name')\n      md5.update(pod_name.encode('utf-8'))\n      return int(md5.hexdigest()[10:20], base=16)\n    else:\n      return -1\n  except Exception as e:\n    return -1\n\n\n@dataclass\nclass TfServingConfig:\n  '''TfServingConfig\n\n  attributes:\n    :param enable_batching: enable batching\n    :param allow_version_labels_for_unavailable_models: If true, allows assigning unused version labels to models\n                                                        that are not available yet.\n    :param batching_parameters_file: If non-empty, read an ascii BatchingParameters protobuf from the supplied file name\n                                     and use the contained values instead of the defaults.\n    :param num_load_threads: The number of threads in the thread-pool used to load servables. If set as 0, we don't use\n                             a thread-pool, and servable loads are performed serially in the manager's main work loop,\n                             may casue the Serving request to be delayed. Default: 0\n    :param num_unload_threads: The number of threads in the thread-pool used to unload servables. If set as 0, we don't\n                               use a thread-pool, and servable loads are performed serially in the manager's main work\n                               loop, may casue the Serving request to be delayed. Default: 0\n    :param max_num_load_retries: maximum number of times it retries loading a model after the first failure, before\n                                 giving up. If set to 0, a load is attempted only once. Default: 5\n    :param load_retry_interval_micros: The interval, in microseconds, between each servable load retry. If set negative,\n                                       it doesn't wait. Default: 1 minute\n    :param file_system_poll_wait_seconds: Interval in seconds between each poll of the filesystem for new model version.\n                                          If set to zero poll will be exactly done once and not periodically. Setting\n                                          this to negative value will disable polling entirely causing ModelServer to\n                                          indefinitely wait for a new model at startup. Negative values are reserved for\n                                          testing purposes only.\n    :param flush_filesystem_caches: If true (the default), filesystem caches will be flushed after the initial load of\n                                    all servables, and after each subsequent individual servable reload\n                                    (if the number of load threads is 1). This reduces memory consumption of the model\n                                    server, at the potential cost of cache misses if model files are accessed after\n                                    servables are loaded.\n    :param tensorflow_session_parallelism: Number of threads to use for running a Tensorflow session.\n                                           Auto-configured by default. Note that this option is ignored if\n                                           --platform_config_file is non-empty.\n    :param tensorflow_intra_op_parallelism: Number of threads to use to parallelize the execution of an individual op.\n                                            Auto-configured by default. Note that this option is ignored\n                                            if --platform_config_file is non-empty.\n    :param tensorflow_inter_op_parallelism: Controls the number of operators that can be executed simultaneously.\n                                            Auto-configured by default. Note that this option is ignored\n                                            if --platform_config_file is non-empty.\n    :param ssl_config_file: If non-empty, read an ascii SSLConfig protobuf from the supplied file name and set up a\n                            secure gRPC channel\n    :param per_process_gpu_memory_fraction: Fraction that each process occupies of the GPU memory space the value is\n                                            between 0.0 and 1.0 (with 0.0 as the default) If 1.0, the server will\n                                            allocate all the memory when the server starts, If 0.0, Tensorflow will\n                                            automatically select a value.\n    :param allow_growth: allow gpu growth\n    :param saved_model_tags: Comma-separated set of tags corresponding to the meta graph def to load from SavedModel.\n    :param grpc_channel_arguments: A comma separated list of arguments to be passed to the grpc server.\n                                    (e.g. grpc.max_connection_age_ms=2000)\n    :param grpc_max_threads: Max grpc server threads to handle grpc messages.\n    :param enable_model_warmup: Enables model warmup, which triggers lazy initializations (such as TF optimizations)\n                                at load time, to reduce first request latency.\n    :param version: Display version\n    :param remove_unused_fields_from_bundle_metagraph: Removes unused fields from MetaGraphDef proto message to save\n                                                       memory.\n    :param enable_signature_method_name_check: Enable method_name check for SignatureDef. Disable this if agent_service native\n                                               TF2 regression/classification models.\n    :param xla_cpu_compilation_enabled: EXPERIMENTAL; CAN BE REMOVED ANYTIME! Enable XLA:CPU JIT (default is disabled).\n                                        With XLA:CPU JIT disabled, models utilizing this feature will return bad Status\n                                        on first compilation request.\n    :param enable_profiler: Enable profiler service.\n  '''\n\n  enable_batching: bool = False\n  allow_version_labels_for_unavailable_models: bool = False\n  batching_parameters_file: str = None\n  num_load_threads: int = 0\n  num_unload_threads: int = 0\n  max_num_load_retries: int = 5\n  load_retry_interval_micros: int = 60 * 1000 * 1000\n  file_system_poll_wait_seconds: int = 1\n  flush_filesystem_caches: bool = True\n  tensorflow_session_parallelism: int = 0\n  tensorflow_intra_op_parallelism: int = 0\n  tensorflow_inter_op_parallelism: int = 0\n  ssl_config_file: str = None\n  platform_config_file: str = None\n  per_process_gpu_memory_fraction: float = 0\n  allow_growth: bool = True\n  saved_model_tags: str = None\n  grpc_channel_arguments: str = None\n  grpc_max_threads: int = 0\n  enable_model_warmup: bool = True\n  version: str = None\n  remove_unused_fields_from_bundle_metagraph: bool = True\n  enable_signature_method_name_check: bool = False\n  xla_cpu_compilation_enabled: bool = False\n  enable_profiler: bool = True\n\n\n@dataclass\nclass AgentConfig(TfServingConfig):\n  '''AgentConfig\n\n  attributes:\n    :param bzid: business id of this agent_service, cannot be None.\n    :param base_name: base name of model\n    :param base_path: path to export\n    :param num_ps: The number of ps.\n    :param num_shard: The total number of shard.\n    :param deploy_type: Server type, can be ps/entry/dense/mixed.\n    :param stand_alone_serving: Whether is stand alone agent_service.\n    :param zk_servers: The zk servers.\n    :param proxy_port: TODO\n    :param tfs_entry_port: TODO\n    :param tfs_entry_http_port: TODO\n    :param tfs_ps_port: TODO\n    :param tfs_ps_http_port: TODO\n    :param dense_alone: whether dense alone\n    :param dense_service_num: dense service num for mps\n    :param tfs_dense_port: TODO\n    :param tfs_dense_http_port: TODO\n    :param agent_port: TODO\n    :param update_model_status_interval: Update model status interval.\n    :param max_waiting_sec: The waiting second for PS/DENSE to load, default 600\n    :param agent_version: Version of Agent, default 1\n    :param version_policy: Tensorflow version_policy, can be latest/specific/all\n    :param version_data: saved_model version\n    :param preload_jemalloc: preload jemalloc.so\n    :param rough_sort_model_name: model name for deep rough sort, which is generated by FeynmanTob\n    :param rough_sort_model_local_path: load deep rough sort model from this dir\n    :param rough_sort_model_loaded_server: load rough sort model on which server: ps or entry or dense\n    :param layout_pattern: layout path format\n    :param layout_filters: filter saved_models under layout_pattern to load\n    :param tfs_port_archon: service archon port\n    :param tfs_port_grpc: service grpc port\n    :param tfs_port_http: service http port\n    :param use_metrics: whether use metrics\n    :param file_system_poll_wait_seconds_ps: Interval in seconds between each poll of the filesystem for new model version.\n                                          If set to zero poll will be exactly done once and not periodically. Setting\n                                          this to negative value will disable polling entirely causing ModelServer to\n                                          indefinitely wait for a new model at startup. Negative values are reserved for\n                                          testing purposes only for ps.\n  '''\n\n  bzid: str = None\n  base_name: str = None\n  base_path: str = None\n  num_ps: int = 1\n  num_shard: int = None\n  deploy_type: str = None\n  replica_id: int = None\n  stand_alone_serving: bool = False\n  zk_servers: str = None\n  proxy_port: int = None\n  tfs_entry_port: int = None\n  tfs_entry_http_port: int = None\n  tfs_entry_archon_port: int = None\n  tfs_ps_port: int = None\n  tfs_ps_http_port: int = None\n  tfs_ps_archon_port: int = None\n  dense_alone: bool = False\n  dense_service_num: int = 3\n  tfs_dense_port: int = None\n  tfs_dense_http_port: int = None\n  tfs_dense_archon_port: int = None\n  agent_port: int = None\n  update_model_status_interval: int = 1\n  model_config_file = None\n  agent_version: int = 1\n  max_waiting_sec: int = 1200\n  preload_jemalloc: bool = True\n  version_policy: str = 'latest'\n  version_data: int = 1\n  fetch_ps_timeout_ms: int = 200\n  fetch_ps_long_conn_num: int = 100\n  fetch_ps_long_conn_enable: bool = True\n  fetch_ps_retry: int = 2\n  aio_thread_num: int = 30\n  file_system_poll_wait_seconds_ps: int = 0\n  # for deep rough sort\n  rough_sort_model_name: str = None\n  rough_sort_model_local_path: str = DefaultRoughSortModelLocalPath\n  rough_sort_model_loaded_server: str = RoughSortModelLoadedServer.ENTRY\n  rough_sort_model_p2p_path: str = DefaultRoughSortModelP2PPath\n  rough_sort_resource_constrained: bool = False\n  dc_aware: bool = False\n  # for unified container\n  layout_pattern: str = None\n  layout_filters: List = None\n  tfs_port_archon: int = None\n  tfs_port_grpc: int = None\n  tfs_port_http: int = None\n  use_metrics: bool = True\n\n  def __post_init__(self):\n    self.zk_servers = self._update_zk_servers(\n      self.zk_servers, is_ipv6_only())\n\n    if self.stand_alone_serving:\n      self.deploy_type = DeployType(DeployType.MIXED)\n    else:\n      assert self.deploy_type is not None\n      self.deploy_type = DeployType(self.deploy_type)\n\n    if self.num_shard is None:\n      self.num_shard = self.num_tce_shard\n    else:\n      assert self.num_shard == self.num_tce_shard\n\n    # PORT1 reserve for p2p\n    # PORT2 reserve for agent\n    if self.deploy_type == DeployType.MIXED:\n      self.proxy_port = find_free_port()\n      self.tfs_entry_archon_port = int(os.environ.get('PORT', find_free_port()))\n      self.tfs_entry_port = int(os.environ.get('PORT3', find_free_port()))\n      self.tfs_entry_http_port = int(os.environ.get('PORT4', find_free_port()))\n      self.tfs_ps_port = int(os.environ.get('PORT5', find_free_port()))\n      self.tfs_ps_http_port = int(os.environ.get('PORT6', find_free_port()))\n      self.tfs_ps_archon_port = int(os.environ.get('PORT7', find_free_port()))\n      if self.dense_alone:\n        dense_service_idx = int(os.environ.get('DENSE_SERVICE_IDX', '0'))\n        if dense_service_idx == 0:\n          self.tfs_dense_port = int(os.environ.get('PORT8', find_free_port()))\n          self.tfs_dense_http_port = int(os.environ.get('PORT9',\n                                                        find_free_port()))\n          self.tfs_dense_archon_port = int(os.environ.get('PORT10',\n                                                          find_free_port()))\n        else:\n          self.tfs_dense_archon_port = find_free_port()\n          self.tfs_dense_port = find_free_port()\n          self.tfs_dense_http_port = find_free_port()\n    elif self.deploy_type == DeployType.ENTRY:\n      self.proxy_port = find_free_port()\n      self.tfs_ps_archon_port = find_free_port()\n      self.tfs_ps_port = find_free_port()\n      self.tfs_ps_http_port = find_free_port()\n\n      if self.dense_alone:\n        self.tfs_dense_port = find_free_port()\n        self.tfs_dense_http_port = find_free_port()\n        self.tfs_dense_archon_port = find_free_port()\n\n      self.tfs_entry_archon_port = int(os.environ.get('PORT', find_free_port()))\n      self.tfs_entry_port = int(os.environ.get('PORT3', find_free_port()))\n      self.tfs_entry_http_port = int(os.environ.get('PORT4', find_free_port()))\n    elif self.deploy_type == DeployType.PS:\n      self.proxy_port = find_free_port()\n      self.tfs_entry_archon_port = find_free_port()\n      self.tfs_entry_port = find_free_port()\n      self.tfs_entry_http_port = find_free_port()\n\n      if self.dense_alone:\n        self.tfs_dense_port = find_free_port()\n        self.tfs_dense_http_port = find_free_port()\n        self.tfs_dense_archon_port = find_free_port()\n\n      self.tfs_ps_archon_port = int(os.environ.get('PORT', find_free_port()))\n      self.tfs_ps_port = int(os.environ.get('PORT3', find_free_port()))\n      self.tfs_ps_http_port = int(os.environ.get('PORT4', find_free_port()))\n    elif self.deploy_type == DeployType.DENSE:\n      assert self.dense_alone == True\n      self.proxy_port = find_free_port()\n      self.tfs_entry_archon_port = find_free_port()\n      self.tfs_entry_port = find_free_port()\n      self.tfs_entry_http_port = find_free_port()\n\n      self.tfs_ps_archon_port = find_free_port()\n      self.tfs_ps_port = find_free_port()\n      self.tfs_ps_http_port = find_free_port()\n\n      dense_service_idx = int(os.environ.get('DENSE_SERVICE_IDX', '0'))\n      if dense_service_idx == 0:\n        self.tfs_dense_archon_port = int(os.environ.get('PORT', find_free_port()))\n        self.tfs_dense_port = int(os.environ.get('PORT3', find_free_port()))\n        self.tfs_dense_http_port = int(os.environ.get('PORT4', find_free_port()))\n      else:\n        self.tfs_dense_archon_port = find_free_port()\n        self.tfs_dense_port = find_free_port()\n        self.tfs_dense_http_port = find_free_port()\n    else:\n      assert self.deploy_type == DeployType.UNIFIED\n      self.tfs_port_archon = int(os.environ.get('PORT', find_free_port()))\n      self.tfs_port_grpc = int(os.environ.get('PORT3', find_free_port()))\n      self.tfs_port_http = int(os.environ.get('PORT4', find_free_port()))\n\n    if self.agent_port is None:\n      self.agent_port = int(os.environ.get('PORT2', find_free_port()))\n\n    if self.agent_version == 1:\n      self.replica_id = replica_id_from_pod_name()\n    else:\n      replica_id = int(os.environ.get('REPLICA_ID', -1))\n      if replica_id == -1:\n        replica_id = replica_id_from_pod_name()\n      self.replica_id = replica_id\n\n    if not self.platform_config_file:\n      self.platform_config_file = DEFAULT_PLATFORM_CONFIG_FILE\n\n    self.generate_platform_config_file()\n\n  def generate_platform_config_file(self):\n    try:\n      session_config = ConfigProto()\n      session_config.intra_op_parallelism_threads = (\n          self.tensorflow_intra_op_parallelism or\n          int(os.getenv(\"MY_CPU_LIMIT\", \"0\"))) or 16\n      session_config.inter_op_parallelism_threads = (\n          self.tensorflow_inter_op_parallelism or\n          int(os.getenv(\"MY_CPU_LIMIT\", \"0\"))) or 16\n      session_config.allow_soft_placement = True\n      session_config.gpu_options.allow_growth = self.allow_growth\n      if self.dense_alone and self.enable_batching:\n        batching_parameters = session_bundle_config_pb2.BatchingParameters()\n        batching_parameters.max_batch_size.value = 1024\n        batching_parameters.batch_timeout_micros.value = 800\n        batching_parameters.max_enqueued_batches.value = 100000\n        batching_parameters.num_batch_threads.value = 8\n        batching_parameters.support_diff_dim_size_inputs = True\n        legacy_config = session_bundle_config_pb2.SessionBundleConfig(\n            session_config=session_config,\n            batching_parameters=batching_parameters)\n      else:\n        legacy_config = session_bundle_config_pb2.SessionBundleConfig(\n            session_config=session_config)\n      legacy_config.enable_model_warmup = self.enable_model_warmup\n      adapter = saved_model_bundle_source_adapter_pb2.SavedModelBundleSourceAdapterConfig(\n          legacy_config=legacy_config)\n      config_map = platform_config_pb2.PlatformConfigMap()\n      config_map.platform_configs['tensorflow'].source_adapter_config.Pack(\n          adapter)\n      text_config_map = text_format.MessageToString(config_map)\n      with open(self.platform_config_file, 'w') as f:\n        f.write(text_config_map)\n    except Exception as e:\n      logging.info(e)\n      try:\n        if os.path.isfile(self.platform_config_file):\n          os.remove(self.platform_config_file)\n      except Exception as e2:\n        logging.info(e2)\n\n  @property\n  def num_tce_shard(self) -> int:\n    return int(os.environ.get(constants.HOST_SHARD_ENV, 1))\n\n  @property\n  def shard_id(self) -> int:\n    return int(os.environ.get('SHARD_ID', -1))\n\n  @property\n  def idc(self) -> Optional[str]:\n    idc = os.environ.get('TCE_INTERNAL_IDC')\n    if idc is None:\n      return None\n    else:\n      return idc.lower()\n\n  @property\n  def cluster(self) -> Optional[str]:\n    cluster = (os.environ.get('TCE_LOGICAL_CLUSTER') or\n               os.environ.get('TCE_CLUSTER') or\n               os.environ.get('TCE_PHYSICAL_CLUSTER'))\n    if cluster is None:\n      return None\n    else:\n      return cluster.lower()\n\n  @property\n  def location(self) -> Optional[str]:\n    idc, cluster = self.idc, self.cluster\n    if idc is None or cluster is None:\n      return None\n    else:\n      return f'{idc}:{cluster}'\n\n  @property\n  def path_prefix(self) -> str:\n    if self.dc_aware:\n      return os.path.join('/', self.bzid, 'service', self.base_name,\n                          self.location)\n    else:\n      return os.path.join('/', self.bzid, 'service', self.base_name)\n\n  @property\n  def layout_path(self) -> str:\n    if self.layout_pattern.startswith(\"/\"):\n      return self.layout_pattern\n    else:\n      return f\"/{self.bzid}/layouts/{self.layout_pattern}\"\n\n  @property\n  def container_cluster(self) -> str:\n    psm = os.environ.get(\"TCE_PSM\", \"unknown\")\n    return f\"{psm};{self.idc};{self.cluster}\"\n\n  @property\n  def container_id(self) -> str:\n    return os.environ.get(\"MY_POD_NAME\", get_local_ip())\n\n  def get_cmd_and_port(self,\n                       binary,\n                       server_type: str = None,\n                       config_file: str = None):\n    server_type = self.deploy_type.compat_server_type(server_type)\n\n    if config_file is None:\n      model_server_config = self._gen_model_server_config(server_type)\n      config_file = write_to_tmp_file(model_server_config)\n\n    flags = []\n    flags.append(f'--model_config_file={config_file}')\n    psm = os.environ.get(\"TCE_PSM\", \"\")\n    cluster = os.environ.get(\"TCE_CLUSTER\", \"\")\n    prefix = psm\n    log_conf = '../conf/log4j.properties'\n\n    if self.deploy_type == DeployType.MIXED and server_type != TFSServerType.ENTRY:\n      psm = psm + '_' + server_type.lower()\n      prefix = psm\n      log_conf = '../conf/log4j_{}.properties'.format(server_type.lower())\n\n    flags.append(f\"--archon_rpc_psm={psm}\")\n    flags.append(f\"--archon_rpc_cluster={cluster}\")\n    flags.append(f\"--metrics_namespace_prefix={prefix}\")\n    flags.append(f\"--log_conf={log_conf}\")\n\n    if server_type == TFSServerType.PS:\n      flags.append(f\"--port={self.tfs_ps_port}\")\n      flags.append(f\"--rest_api_port={self.tfs_ps_http_port}\")\n      flags.append(f'--archon_port={self.tfs_ps_archon_port}')\n      port = self.tfs_ps_port\n    elif server_type == TFSServerType.DENSE:\n      flags.append(f\"--port={self.tfs_dense_port}\")\n      flags.append(f\"--rest_api_port={self.tfs_dense_http_port}\")\n      flags.append(f'--archon_port={self.tfs_dense_archon_port}')\n      if self.enable_batching:\n        flags.append(f'--enable_batching=true')\n      flags.append(\n          f'--archon_entry_to_ps_rpc_timeout={self.fetch_ps_timeout_ms}')\n      flags.append(\n          f'--archon_entry_to_ps_long_conn_num={self.fetch_ps_long_conn_num}')\n      flags.append(f'--archon_entry_to_ps_rpc_retry={self.fetch_ps_retry}')\n      flags.append(f'--archon_async_dispatcher_threads={self.aio_thread_num}')\n      if not self.fetch_ps_long_conn_enable:\n        flags.append(f'--archon_entry_to_ps_long_conn_enable=false')\n      port = self.tfs_dense_port\n    else:\n      flags.append(f\"--port={self.tfs_entry_port}\")\n      flags.append(f\"--rest_api_port={self.tfs_entry_http_port}\")\n      flags.append(f'--archon_port={self.tfs_entry_archon_port}')\n      flags.append(\n          f'--archon_entry_to_ps_rpc_timeout={self.fetch_ps_timeout_ms}')\n      flags.append(\n          f'--archon_entry_to_ps_long_conn_num={self.fetch_ps_long_conn_num}')\n      flags.append(f'--archon_entry_to_ps_rpc_retry={self.fetch_ps_retry}')\n      flags.append(f'--archon_async_dispatcher_threads={self.aio_thread_num}')\n      if not self.fetch_ps_long_conn_enable:\n        flags.append(f'--archon_entry_to_ps_long_conn_enable=false')\n      port = self.tfs_entry_port\n\n    if self.agent_version != 1:\n      flags.append(\"--model_config_file_poll_wait_seconds=0\")\n\n    for key, clz in get_type_hints(TfServingConfig).items():\n      default = getattr(TfServingConfig, key)\n      value = getattr(self, key)\n\n      if key == 'file_system_poll_wait_seconds':\n        if self.agent_version == 1:\n          if server_type == TFSServerType.PS:\n            flags.append(f'--file_system_poll_wait_seconds={self.file_system_poll_wait_seconds_ps}')\n          elif value != default:  # entry,dense\n            flags.append(f'--file_system_poll_wait_seconds={value}')\n      elif value != default:\n        if clz == bool:\n          flags.append(f'--{key}={str(value).lower()}')\n        else:\n          flags.append(f'--{key}={value}')\n\n    return f'{binary} {\" \".join(flags)}', port\n\n  def get_cmd(self, binary, server_type: str = None) -> str:\n    cmd, port = self.get_cmd_and_port(binary, server_type)\n    return cmd\n\n  def get_server_schedule_iter(self, server_type):\n    if self.deploy_type == DeployType.MIXED or self.deploy_type == DeployType.PS:\n      if server_type == TFSServerType.PS:\n        for i in range(self.num_ps):\n          if i % self.num_shard == self.shard_id:\n            yield i\n      else:\n        yield None\n    elif self.deploy_type == DeployType.DENSE and server_type == TFSServerType.DENSE:\n      # [TODO] (fitz) maybe there is a bug, fix it later\n      yield self.replica_id\n    else:\n        yield None\n\n  def _gen_model_server_config(\n      self,\n      server_type: str = None,\n  ) -> model_server_config_pb2.ModelServerConfig:\n    version_policy: str = self.version_policy\n    version_data: int = self.version_data\n    server_type = self.deploy_type.compat_server_type(server_type)\n    assert server_type is not None\n\n    model_server_config = model_server_config_pb2.ModelServerConfig()\n    model_config_list = model_server_config.model_config_list.config\n\n    if server_type == TFSServerType.PS:\n      for i in self.get_server_schedule_iter(server_type):\n        name = f'{server_type}_{i}'\n        model_config = model_config_list.add()\n        model_config.CopyFrom(\n            gen_model_config(name=name,\n                              base_path=os.path.join(self.base_path, name),\n                              version_policy=version_policy,\n                              version_data=version_data))\n        if self.rough_sort_model_name and self.rough_sort_model_loaded_server == RoughSortModelLoadedServer.PS:\n          name = f'{RoughSortModelPrefix.PS}_{i}'\n          model_config = model_config_list.add()\n          rough_model_path = os.path.join(self.rough_sort_model_local_path,\n                                          self.rough_sort_model_name, name)\n          model_config.CopyFrom(\n              gen_model_config(name=name,\n                                base_path=rough_model_path,\n                                version_policy=version_policy,\n                                version_data=version_data))\n    else:\n      if server_type == TFSServerType.DENSE:\n        name = f'{server_type}_0'\n      else:\n        name = server_type\n      model_config = model_config_list.add()\n      model_config.CopyFrom(\n          gen_model_config(name=name,\n                           base_path=os.path.join(self.base_path, name),\n                           version_policy=version_policy,\n                           version_data=version_data))\n\n      if self.rough_sort_resource_constrained and self.rough_sort_model_loaded_server == RoughSortModelLoadedServer.ENTRY:\n        name = f'{RoughSortModelPrefix.ENTRY}_0'\n        model_config = model_config_list.add()\n        rough_model_path = os.path.join(self.base_path, name)\n        model_config.CopyFrom(\n            gen_model_config(name=name,\n                             base_path=rough_model_path,\n                             version_policy=version_policy,\n                             version_data=version_data))\n      elif (self.rough_sort_model_name and (self.rough_sort_model_loaded_server\n                                          == RoughSortModelLoadedServer.ENTRY or\n                                          self.rough_sort_model_loaded_server\n                                          == RoughSortModelLoadedServer.DENSE)):\n        if self.rough_sort_model_loaded_server == RoughSortModelLoadedServer.ENTRY:\n          name = f'{RoughSortModelPrefix.ENTRY}_0'\n        elif self.rough_sort_model_loaded_server == RoughSortModelLoadedServer.DENSE:\n          name = f'{RoughSortModelPrefix.DENSE}_0'\n        model_config = model_config_list.add()\n        rough_model_path = os.path.join(self.rough_sort_model_local_path,\n                                        self.rough_sort_model_name, name)\n        model_config.CopyFrom(\n            gen_model_config(name=name,\n                             base_path=rough_model_path,\n                             version_policy=version_policy,\n                             version_data=version_data))\n\n    return model_server_config\n\n  @classmethod\n  def from_file(cls, fname):\n    kwarg = {}\n    conf_parser(fname, kwarg)\n\n    args = {}\n    for key, clz in get_type_hints(AgentConfig).items():\n      try:\n        if key in kwarg:\n          if clz == bool and kwarg[key].lower() in {\n              'true', 'y', 't', 'yes', '1'\n          }:\n            args[key] = True\n          elif clz == bool and kwarg[key].lower() in {\n              'false', 'n', 'f', 'no', '0'\n          }:\n            args[key] = False\n          elif clz in {int, float}:\n            args[key] = clz(eval(kwarg[key]))\n          elif clz == str:\n            if kwarg[key].lower() == 'none':\n              args[key] = None\n            else:\n              args[key] = kwarg[key]\n          elif clz == List:\n            if type(kwarg[key]) is not list:\n              args[key] = [kwarg[key]]\n            else:\n              args[key] = kwarg[key]\n          else:\n            args[key] = clz(kwarg[key])\n      except:\n        logging.error(f'type convert {key} error, the type is {clz}')\n\n    # for compat\n    if 'deploy_type' not in args:\n      args['deploy_type'] = kwarg.pop('server_type', None)\n\n    return cls(**args)\n\n  @classmethod\n  def _update_zk_servers(cls, zk_servers, use_ipv6: bool = False):\n    if zk_servers and use_ipv6:\n      ipv4s = [] \n      for addr in zk_servers.split(','):\n        ip_port = addr.rsplit(':', 1)\n        if len(ip_port) == 2:\n          items = ip_port[0].split('.')\n          if len(items) == 4 and all([item.isnumeric() for item in items]):\n            ipv4s.append(ip_port[0])\n      if len(ipv4s) > 0:\n        default_zk_servers_ipv4 = ','.join(['{ip}:{port}'.format(ip=ip, port=_PORT) for ip in _HOSTS])\n        if zk_servers == default_zk_servers_ipv4:\n          logging.warning('the host is is ipv6 only, but zk_servers specified is ipv4')\n          return default_zk_servers(True)\n        else:\n          raise Exception('the host is is ipv6 only, but zk_servers specified is ipv4')\n    return zk_servers\n\n\nclass ZKPath(object):\n  PAT = re.compile(\n      r'^/(?P<bzid>[-_0-9A-Za-z]+)/service/(?P<base_name>[-_0-9A-Za-z]+)(/(?P<idc>[-_0-9A-Za-z]+):(?P<cluster>[-_0-9A-Za-z]+))?/(?P<server_type>\\w+):(?P<index>\\d+)(/(?P<replica_id>\\d+))?$'\n  )\n\n  def __init__(self, path: str):\n    self.path = path\n\n    if path is None or len(path) != 0:\n      matched = self.PAT.match(self.path)\n      if matched:\n        self._group_dict = matched.groupdict()\n      else:\n        logging.info(f\"[INFO] path not matched: {path}\")\n        self._group_dict = None\n    else:\n      self._group_dict = None\n\n  def __getattr__(self, name: str):\n    assert name in {\n        'bzid', 'base_name', 'idc', 'cluster', 'server_type', 'index',\n        'replica_id'\n    }\n    if self._group_dict:\n      return self._group_dict.get(name)\n    else:\n      return None\n\n  @property\n  def task(self) -> str:\n    server_type, index = self.server_type, self.index\n\n    if server_type is not None and index is not None:\n      return f'{server_type}:{index}'\n    else:\n      return None\n\n  @property\n  def location(self) -> Optional[str]:\n    idc, cluster = self.idc, self.cluster\n    if idc is None or cluster is None:\n      return None\n    else:\n      return f'{idc}:{cluster}'\n\n  def ship_in(self, idc: str, cluster: str) -> bool:\n    if idc is None or cluster is None:\n      return True\n    else:\n      return idc == self.idc and cluster == self.cluster\n\n\ndef parse_pattern(pattern_str, init_val, comb_fn, lp='{', rp='}'):\n  ret_val = init_val\n  while len(pattern_str):\n    begin = pattern_str.find(lp)\n    end = pattern_str.find(rp, begin)\n    if begin == -1 or end == -1:\n      ret_val = comb_fn(ret_val, pattern_str, None)\n      break\n    ret_val = comb_fn(ret_val, pattern_str[:begin], pattern_str[begin + 1:end])\n    pattern_str = pattern_str[end + 1:]\n  return ret_val\n\n\ndef normalize_regex(pattern_str):\n\n  def comb(val: str, p1: str, p2: str):\n    if p2 is None:\n      return val + p1\n    return val + p1 + f'(?P<{p2}>\\d+)'\n\n  return parse_pattern(pattern_str, '', comb_fn=comb)\n\n\ndef expand_pattern(pattern_str):\n\n  def comb(vals: List, p1: str, p2: str):\n    if p2 is None:\n      return [val + p1 for val in vals]\n    l = []\n\n    for t in p2.split(','):\n      if '-' in t:\n        s, e = t.split('-')\n        l.extend(range(int(s), int(e)))\n      else:\n        l.extend(int(t))\n    return [val + p1 + str(i) for val in vals for i in l]\n\n  return parse_pattern(pattern_str, [''], comb, '[', ']')\n\n\ndef gen_model_spec(name: str,\n                   version: Union[int, str] = None,\n                   signature_name: str = None):\n  mode_spec = model_pb2.ModelSpec(name=name)\n\n  if version is not None:\n    if isinstance(version, int):\n      mode_spec.version.value = version\n    else:\n      mode_spec.version_label = version\n\n  if signature_name is not None:\n    mode_spec.signature_name = signature_name\n\n  return mode_spec\n\n\ndef gen_model_config(\n    name: str,\n    base_path: str,\n    version_policy: str = 'latest',\n    version_data: Union[int, List[int]] = 1,\n    model_platform: str = 'tensorflow',\n    version_labels: Dict[str,\n                         int] = None) -> model_server_config_pb2.ModelConfig:\n  model_config = model_server_config_pb2.ModelConfig(\n      name=name, base_path=base_path, model_platform=model_platform)\n\n  if version_policy.lower() == 'latest':\n    assert isinstance(version_data, int)\n    model_config.model_version_policy.latest.num_versions = version_data\n  elif version_policy.lower() == 'latest_once':\n    assert isinstance(version_data, int)\n    model_config.model_version_policy.latest_once.num_versions = version_data\n  elif version_policy.lower() == 'all':\n    model_config.model_version_policy.all.CopyFrom(ServableVersionPolicy.All())\n  elif version_policy.lower() == 'specific':\n    if isinstance(version_data, int):\n      version_data = [version_data]\n    assert isinstance(version_data, list)\n    model_config.model_version_policy.specific.versions.extend(version_data)\n  else:\n    raise ValueError(version_policy + \" is not allowed!\")\n\n  if version_labels is not None:\n    model_config.version_labels.update(version_labels)\n\n  return model_config\n\n\nDEFAULT_MODEL_CONFIG = gen_model_config(name='default',\n                                        base_path=os.path.join(\n                                            TFS_HOME, 'dat', 'saved_models',\n                                            'entry'))\n\n\ndef gen_status_proto(error_code: ErrorCode = ErrorCode.OK,\n                     error_message: str = None):\n  return StatusProto(error_code=error_code, error_message=error_message)\n\n\ndef gen_model_version_status(version: int,\n                             state: ModelState = ModelState.UNKNOWN,\n                             error_code: ErrorCode = ErrorCode.OK,\n                             error_message: str = None):\n  mvs = ModelVersionStatus(version=version, state=state)\n  mvs.status.CopyFrom(gen_status_proto(error_code, error_message))\n\n  return mvs\n\n\ndef make_tensor_proto(instances):\n  tp = TensorProto(dtype=DataType.DT_STRING)\n  dim = tp.tensor_shape.dim.add()\n  dim.size = len(instances)\n  tp.string_val.extend(instances)\n\n  return tp\n\n\nclass InstanceFormater:\n\n  def __init__(self, inst: Instance):\n    self._inst = inst\n\n  def __str__(self):\n    return f\"{self._inst}\"\n\n  def to_tensor_proto(self, batch_size: int):\n    serialized = self._inst.SerializeToString()\n    instances = [serialized for _ in range(batch_size)]\n\n    return make_tensor_proto(instances)\n\n  def to_pb(self, fname: str = None) -> str:\n    content = self._inst.SerializeToString()\n    if fname is None:\n      fd, path = tempfile.mkstemp()\n      with os.fdopen(fd, 'wb') as fp:\n        fp.write(content)\n      return path\n    else:\n      with open(fname, 'wb') as fid:\n        fid.write(content)\n      return fname\n\n  def to_json(self, fname: str = None) -> str:\n    content = json_format.MessageToJson(self._inst)\n    if fname is None:\n      return write_to_tmp_file(content)\n    else:\n      with open(fname) as fid:\n        fid.write(content)\n      return fname\n\n  def to_pb_text(self, fname: str = None) -> str:\n    if fname is None:\n      return write_to_tmp_file(self._inst)\n    else:\n      with open(fname) as fid:\n        fid.write(str(self._inst))\n      return fname\n\n  @classmethod\n  def from_json(cls, fname: str):\n    message = Instance()\n    with open(fname) as fid:\n      kwargs = json.load(fid)\n      return cls(json_format.ParseDict(kwargs, message))\n\n  @classmethod\n  def from_pb_text(cls, fname: str):\n    message = Instance()\n    text = []\n    with open(fname) as fid:\n      for line in fid:\n        text.append(line.strip())\n    return cls(text_format.Parse('\\n'.join(text), message))\n\n  @classmethod\n  def from_dump(cls, fname: str):\n    stack, kwargs = [], {}\n\n    def get_item():\n      if stack and kwargs:\n        arg = kwargs\n        for item in stack:\n          if item in arg:\n            arg = arg[item]\n          else:\n            return None\n        return arg\n      else:\n        return None\n\n    def set_item(item):\n      last_arg, arg = None, kwargs\n      for key in stack:\n        last_arg = arg\n        arg = arg[key]\n\n      if isinstance(item, dict):\n        if isinstance(arg, list):\n          stack.pop()\n          arg = last_arg\n\n        (key, value), = item.items()\n        if value is None:\n          if key.isnumeric() and int(key) == 0:\n            last_arg[stack[-1]] = [value]\n            stack.append(0)\n          elif key.isnumeric():\n            stack[-1] = int(key)\n            last_arg.append(value)\n          else:\n            if arg is None:\n              last_arg[stack[-1]] = item\n            elif len(stack) >= 2 and isinstance(stack[-1],\n                                                int) and stack[-2] == 'feature':\n              if key in FeatureKeys and key not in arg:\n                arg.update(item)\n              else:\n                stack.pop()\n                stack.pop()\n                kwargs.update(item)\n            else:\n              arg.update(item)\n            stack.append(key)\n        else:\n          if arg is None:\n            last_arg[stack[-1]] = item\n          else:\n            assert isinstance(arg, dict)\n            arg.update(item)\n      else:\n        if arg is None:\n          last_arg[stack[-1]] = [item]\n        else:\n          arg.append(item)\n\n    with open(fname) as fid:\n      for line in fid:\n        if line.startswith('\"root\":'):\n          continue\n        (key, value) = [\n            item.strip().strip('\"').strip(\"'\")\n            for item in line.strip().split(':')\n        ]\n        if len(value) == 0:\n          set_item(item={key: None})\n        else:\n          if value.isnumeric():\n            value = int(value)\n\n          if key.isnumeric():  # list\n            set_item(item=value)\n          else:  # dict\n            set_item(item={key: value})\n\n    message = Instance()\n    return cls(json_format.ParseDict(kwargs, message))\n\n\ndef pasre_sub_model_name(sub_model_name: str):\n  if sub_model_name is None or len(sub_model_name) == 0:\n    raise RuntimeError('sub_model_name is None or empty')\n\n  pasred = sub_model_name.strip().split('_')\n  if len(pasred) == 1:\n    return pasred[0].lower(), 0\n  else:\n    assert len(pasred) == 2\n    return pasred[0].lower(), int(pasred[1])\n\n\ndef get_local_ip() -> str:\n  try:\n    local_ip = os.environ.get(\"MY_HOST_IP\",\n                              socket.gethostbyname(socket.gethostname()))\n    if local_ip is not None and local_ip not in {'', 'localhost', '127.0.0.1'}:\n      return local_ip\n  except Exception as e:\n    logging.warning(e)\n\n  skt = None\n  try:\n    skt = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    skt.connect(('8.8.8.8', 80))\n    local_ip = skt.getsockname()[0]\n    if local_ip is not None and local_ip not in {'', 'localhost', '127.0.0.1'}:\n      return local_ip\n  except Exception as e:\n    logging.warning(e)\n  finally:\n    if skt is not None:\n      skt.close()\n\n  return 'localhost'\n"
  },
  {
    "path": "monolith/agent_service/utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport unittest\n\nfrom monolith.agent_service import utils\n\n\nclass ServingUtilsTest(unittest.TestCase):\n  @classmethod\n  def setUpClass(cls) -> None:\n    os.environ['MY_HOST_IP'] = '127.0.0.1'\n\n  def test_gen_model_spec(self):\n    name, version, signature_name = 'model', 1, 'predict'\n    model_spec = utils.gen_model_spec(name, version, signature_name)\n    self.assertEqual(model_spec.name, name)\n    self.assertEqual(model_spec.version.value, version)\n    self.assertEqual(model_spec.signature_name, signature_name)\n\n  def test_gen_model_config(self):\n    name, base_path, num_versions = 'model', '/tmp/model/saved_model', 2\n    version_labels = {'v0': 0, 'v1': 1}\n    model_config = utils.gen_model_config(name,\n                                          base_path,\n                                          version_data=num_versions,\n                                          version_labels=version_labels)\n    self.assertEqual(model_config.name, name)\n    self.assertEqual(model_config.base_path, base_path)\n    self.assertEqual(model_config.model_version_policy.latest.num_versions,\n                     num_versions)\n\n  def test_gen_status_proto(self):\n    status_proto = utils.gen_status_proto(utils.ErrorCode.CANCELLED,\n                                          error_message='CANCELLED')\n    self.assertEqual(status_proto.error_code, utils.ErrorCode.CANCELLED)\n    self.assertEqual(status_proto.error_message, 'CANCELLED')\n\n  def test_gen_model_version_status(self):\n    version, state = 1, utils.ModelState.START\n    error_code, error_message = utils.ErrorCode.NOT_FOUND, \"NOT_FOUND\"\n    model_version_status = utils.gen_model_version_status(\n        version, state, error_code, error_message)\n    self.assertEqual(model_version_status.version, version)\n    self.assertEqual(model_version_status.state, state)\n\n  def test_gen_from_file(self):\n    conf = utils.AgentConfig.from_file(\n        fname='monolith/agent_service/agent.conf')\n    self.assertTrue(conf.stand_alone_serving)\n\n  def test_list_field(self):\n    conf = utils.AgentConfig.from_file(\n        fname='monolith/agent_service/agent.conf')\n    self.assertEqual(conf.layout_filters, ['ps_0', 'ps_1'])\n\n  def test_instance_wrapper_from_json(self):\n    iw = utils.InstanceFormater.from_json(\n        'monolith/agent_service/test_data/inst.json')\n    tensor_proto = iw.to_tensor_proto(5)\n    self.assertEqual(tensor_proto.dtype, 7)\n    self.assertEqual(tensor_proto.tensor_shape.dim[0].size, 5)\n\n  def test_instance_wrapper_from_pbtext(self):\n    iw = utils.InstanceFormater.from_pb_text(\n        'monolith/agent_service/test_data/inst.pbtext')\n    tensor_proto = iw.to_tensor_proto(5)\n    self.assertEqual(tensor_proto.dtype, 7)\n    self.assertEqual(tensor_proto.tensor_shape.dim[0].size, 5)\n\n  def test_instance_wrapper_from_dump(self):\n    iw = utils.InstanceFormater.from_dump(\n        'monolith/agent_service/test_data/inst.dump')\n    tensor_proto = iw.to_tensor_proto(5)\n    self.assertEqual(tensor_proto.dtype, 7)\n    self.assertEqual(tensor_proto.tensor_shape.dim[0].size, 5)\n\n  def test_get_cmd_and_port(self):\n    conf = utils.AgentConfig.from_file(\n        fname='monolith/agent_service/agent.conf')\n    conf.agent_version = 2\n    cmd, port = conf.get_cmd_and_port(binary='tensorflow_model_server',\n                                      server_type='ps')\n    self.assertTrue('model_config_file_poll_wait_seconds' in cmd)\n\n  def test_zk_path_full(self):\n    zk_pzth = utils.ZKPath(\n        '/bzid/service/base_name/idc:cluster/server_type:0/1')\n    self.assertEqual(zk_pzth.bzid, 'bzid')\n    self.assertEqual(zk_pzth.base_name, 'base_name')\n    self.assertEqual(zk_pzth.idc, 'idc')\n    self.assertEqual(zk_pzth.cluster, 'cluster')\n    self.assertEqual(zk_pzth.server_type, 'server_type')\n    self.assertEqual(zk_pzth.index, '0')\n    self.assertEqual(zk_pzth.replica_id, '1')\n    self.assertEqual(zk_pzth.location, 'idc:cluster')\n    self.assertEqual(zk_pzth.task, 'server_type:0')\n    self.assertTrue(zk_pzth.ship_in(None, None))\n\n  def test_zk_path_partial(self):\n    zk_pzth = utils.ZKPath('/bzid/service/base_name/idc:cluster/server_type:0')\n    self.assertEqual(zk_pzth.bzid, 'bzid')\n    self.assertEqual(zk_pzth.base_name, 'base_name')\n    self.assertEqual(zk_pzth.idc, 'idc')\n    self.assertEqual(zk_pzth.cluster, 'cluster')\n    self.assertEqual(zk_pzth.server_type, 'server_type')\n    self.assertEqual(zk_pzth.index, '0')\n    self.assertEqual(zk_pzth.replica_id, None)\n    self.assertEqual(zk_pzth.location, 'idc:cluster')\n    self.assertEqual(zk_pzth.task, 'server_type:0')\n    self.assertTrue(zk_pzth.ship_in('idc', 'cluster'))\n\n  def test_zk_path_old_full(self):\n    zk_pzth = utils.ZKPath('/bzid/service/base_name/server_type:0/1')\n    self.assertEqual(zk_pzth.bzid, 'bzid')\n    self.assertEqual(zk_pzth.base_name, 'base_name')\n    self.assertEqual(zk_pzth.idc, None)\n    self.assertEqual(zk_pzth.cluster, None)\n    self.assertEqual(zk_pzth.server_type, 'server_type')\n    self.assertEqual(zk_pzth.index, '0')\n    self.assertEqual(zk_pzth.replica_id, '1')\n    self.assertEqual(zk_pzth.location, None)\n    self.assertEqual(zk_pzth.task, 'server_type:0')\n    self.assertTrue(zk_pzth.ship_in(None, None))\n\n  def test_zk_path_old_partial(self):\n    zk_pzth = utils.ZKPath('/bzid/service/base_name/server_type:0')\n    self.assertEqual(zk_pzth.bzid, 'bzid')\n    self.assertEqual(zk_pzth.base_name, 'base_name')\n    self.assertEqual(zk_pzth.idc, None)\n    self.assertEqual(zk_pzth.cluster, None)\n    self.assertEqual(zk_pzth.server_type, 'server_type')\n    self.assertEqual(zk_pzth.index, '0')\n    self.assertEqual(zk_pzth.replica_id, None)\n    self.assertEqual(zk_pzth.location, None)\n    self.assertEqual(zk_pzth.task, 'server_type:0')\n    self.assertTrue(zk_pzth.ship_in(None, None))\n\n  def test_zk_path_old_partial2(self):\n    zk_pzth = utils.ZKPath(\n        '/1_20001223_44ce735e-d05c-11ec-ba29-00163e356637/service/20001223_zm_test_realtime_training_1328_v4_r982567_0/ps:1'\n    )\n    self.assertEqual(zk_pzth.bzid,\n                     '1_20001223_44ce735e-d05c-11ec-ba29-00163e356637')\n    self.assertEqual(zk_pzth.base_name,\n                     '20001223_zm_test_realtime_training_1328_v4_r982567_0')\n    self.assertEqual(zk_pzth.idc, None)\n    self.assertEqual(zk_pzth.cluster, None)\n    self.assertEqual(zk_pzth.server_type, 'ps')\n    self.assertEqual(zk_pzth.index, '1')\n    self.assertEqual(zk_pzth.replica_id, None)\n    self.assertEqual(zk_pzth.location, None)\n    self.assertEqual(zk_pzth.task, 'ps:1')\n    self.assertTrue(zk_pzth.ship_in(None, None))\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/agent_service/zk_mirror.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport socket\nimport time\nimport traceback\nfrom queue import Queue\nfrom absl import logging\nfrom collections import defaultdict\nfrom threading import Thread, RLock\nfrom typing import List, Optional, Dict, Union, Set\nfrom monolith.agent_service.data_def import ResourceSpec, ReplicaMeta\nfrom monolith.agent_service.data_def import PublishMeta, PublishType as PType\nfrom monolith.agent_service.data_def import ModelState, ModelName, ModelMeta\nfrom monolith.agent_service.data_def import Event, EventType as EType\nfrom kazoo.client import Election\nfrom kazoo.protocol.states import WatchedEvent, EventType, ZnodeStat, KazooState\nfrom kazoo.exceptions import NodeExistsError, ZookeeperError, \\\n    NoNodeError, NotEmptyError, ConnectionClosedError\nfrom monolith.native_training.zk_utils import MonolithKazooClient\n\nfrom monolith.agent_service.utils import get_local_ip, DeployType, replica_id_from_pod_name\n\n\nclass ZKMirror(object):\n  _data_lock = RLock()\n  _zk_lock = RLock()\n  _sep = '/'\n\n  def __init__(\n      self,\n      zk: MonolithKazooClient,\n      bzid: str,\n      queue: Queue = None,\n      tce_shard_id: int = -1,  # for entry deploy mode, tce_shard_id is -1\n      num_tce_shard: int = 1,\n      deploy_type: str = DeployType.MIXED):\n    self._data: Dict[str, bytes] = {}\n    self._zk: MonolithKazooClient = zk\n    self.queue: Queue = queue\n    self._bzid: str = bzid\n    self._is_leader = False\n    self.tce_shard_id: int = tce_shard_id\n    self.num_tce_shard: int = num_tce_shard\n    self._local_host = get_local_ip()\n    self._deploy_type = deploy_type\n    self._leader = None\n\n    self._zk_lock_path = f\"/{bzid}/locks/\"\n    self._zk_election_path = f\"/{bzid}/election/\"\n\n    # /{bzid}/resource/{shard_id}:{replica_id}  -> ResourceSpec\n    self.resource_path: str = f'/{bzid}/resource'\n\n    # /{bzid}/portal/{model_name} -> ModelMeta\n    self.portal_base_path: str = f'/{bzid}/portal'\n\n    # /{bzid}/publish/{shard_id}:{model_name} -> PublishMeta\n    self.publish_base_path: str = f'/{bzid}/publish'\n\n    # /{bzid}/service/{model_name}/deploy_type:task_id/replica -> ReplicaMeta\n    self.service_base_path: str = f'/{bzid}/service'\n\n  @property\n  def is_leader(self) -> bool:\n    return self._is_leader\n\n  def set_leader(self):\n    self._is_leader = True\n\n  def create(self,\n             path,\n             value=b\"\",\n             acl=None,\n             ephemeral=False,\n             sequence=False,\n             makepath=True,\n             include_data=False):\n    with self._zk_lock:\n      try:\n        self._zk.create(path,\n                        value,\n                        acl=acl,\n                        ephemeral=ephemeral,\n                        sequence=sequence,\n                        makepath=makepath,\n                        include_data=include_data)\n      except NodeExistsError as e:\n        self._zk.retry(self._zk.set, path=path, value=value)\n      except Exception as e:\n        raise e\n\n  def ensure_path(self, path):\n    with self._zk_lock:\n      self._zk.retry(self._zk.ensure_path, path=path)\n\n  def set(self, path: str, value: bytes = b''):\n    with self._zk_lock:\n      try:\n        self._zk.retry(self._zk.set, path=path, value=value)\n      except NoNodeError as e:\n        self._zk.create(path=path, value=value, makepath=True)\n      except Exception as e:\n        raise e\n\n  def exists(self, path: str) -> bool:\n    with self._zk_lock:\n      try:\n        status = self._zk.exists(path=path)\n        if isinstance(status, bool):\n          return status\n        else:\n          return status is not None\n      except ZookeeperError as e:\n        return path in self._data\n\n  def delete(self, path: str, recursive: bool = True):\n    with self._zk_lock:\n      try:\n        self._zk.retry(self._zk.delete, path=path, recursive=recursive)\n      except NoNodeError as e:\n        logging.info(e)\n      except NotEmptyError as e:\n        self._zk.retry(self._zk.delete, path=path, recursive=True)\n      except Exception as e:\n        raise e\n\n  def get(self, path) -> Optional[bytes]:\n    with self._data_lock:\n      return self._data.get(path)\n\n  def get_children(self, path: str) -> List[str]:\n    with self._data_lock:\n      length = len(path.split(self._sep))\n      children = []\n      for p in self._data:\n        if p.startswith(path):\n          tl = p.split(self._sep)\n          if len(tl) > length:\n            children.append(tl[length])\n      return children\n\n  def report_resource(self, recource: ResourceSpec):\n    path = recource.get_path(self.resource_path)\n    value = b'' if recource is None else recource.serialize()\n    self.create(path=path, value=value, makepath=True, ephemeral=True)\n\n  @property\n  def resources(self) -> List[ResourceSpec]:\n    # /{bzid}/resource/{shard_id}:{replica_id}  -> ResourceSpec\n    with self._data_lock:\n      resource_paths = [\n          os.path.join(self.resource_path, child)\n          for child in self.get_children(self.resource_path)\n      ]\n      return [ResourceSpec.deserialize(self.get(p)) for p in resource_paths]\n\n  @property\n  def num_tce_replica(self) -> Optional[int]:\n\n    def get_replica_cnt():\n      replica_cnt = {}\n      with self._data_lock:\n        for path in self._data:\n          if path.startswith(self.resource_path):\n            replica_id = int(os.path.basename(path).split(':')[-1])\n            if replica_id == -1:\n              continue  # skip entry\n            elif replica_id in replica_cnt:\n              replica_cnt[replica_id] += 1\n            else:\n              replica_cnt[replica_id] = 1\n\n    replicas = get_replica_cnt()\n    while not all(cnt == self.tce_shard_id for cnt in replicas.values()):\n      time.sleep(5)\n      # log every minute\n      logging.log_every_n(level=logging.INFO,\n                          msg='cluster autoscaler or broken node, keep waiting',\n                          n=12)\n      replicas = get_replica_cnt()\n\n    return len(replica_cnt)\n\n  @property\n  def tce_replica_id(self) -> int:\n    replica_id = int(os.environ.get('REPLICA_ID', -1))\n    if replica_id == -1:\n      replica_id = replica_id_from_pod_name()\n    return replica_id\n\n  def publish_loadding(self, info: Union[PublishMeta, List[PublishMeta]]):\n    if isinstance(info, (list, tuple)):\n      for pm in info:\n        path = pm.get_path(self.publish_base_path)\n        value = pm.serialize()\n        loc = self.get(path)\n        if loc is None or loc != value:\n          self.create(path=path, value=value, makepath=True)\n    else:\n      path = info.get_path(self.publish_base_path)\n      value = info.serialize()\n      loc = self.get(path)\n      if loc is None or loc != value:\n        self.create(path=path, value=value, makepath=True)\n\n  def expected_loading(self) -> Dict[ModelName, PublishMeta]:\n    # /{bzid}/publish/{shard_id}:{model_name} -> PublishMeta\n    with self._data_lock:\n      nodes = self.get_children(self.publish_base_path)\n      models, select, shortest_sub_model_pm = {}, [], {}\n      for node in nodes:\n        path = os.path.join(self.publish_base_path, node)\n        pm = PublishMeta.deserialize(self.get(path))\n        shard_id, replica_id, model_name = node.split(':')\n        if model_name in models:\n          models[model_name] += 1\n        else:\n          models[model_name] = 1\n\n        # record the most sub_model pm\n        if model_name not in shortest_sub_model_pm:\n          shortest_sub_model_pm[model_name] = pm\n        elif len(shortest_sub_model_pm[model_name].sub_models) > len(\n            pm.sub_models):\n          shortest_sub_model_pm[model_name] = pm\n\n        # the last one\n        if models[model_name] == pm.total_publish_num:\n          select.append(shortest_sub_model_pm[model_name])\n\n      expected: Dict[str, PublishMeta] = {}\n      for pm in select:\n        path = os.path.join(\n            self.publish_base_path,\n            f'{self.tce_shard_id}:{self.tce_replica_id}:{pm.model_name}')\n        data = self.get(path)\n        # for new replica or entry, data is None\n        pm = pm if data is None else PublishMeta.deserialize(data)\n\n        model_name = pm.model_name\n        if pm.ptype != PType.LOAD:\n          logging.info(\"ptype is not load, skip!\")\n          continue\n\n        # note: the sceduler will not scedule entry submodel alone,\n        # all the submodels are sceduled with ps submodel.\n        # so there is an entry submodel in every PublishMeta.\n        # the shard_id of every service is -1.\n\n        if pm.shard_id == self.tce_shard_id and pm.replica_id == self.tce_replica_id:\n          # if service type is 'entry', then shard_id is -1,\n          # and no PublishMeta will fall in this branch\n          # only ps/dense/mixed service type will hit this branch\n          expected[model_name] = pm\n        elif pm.shard_id == self.tce_shard_id and not pm.is_spec:  # for autoscalar,  new replica\n          if model_name not in expected:\n            pm.replica_id = self.tce_replica_id\n            expected[model_name] = pm\n        else:\n          # all entry/ps/dense/mixed service type can hit this branch\n          # and ps/dense submodels were filtered\n          if model_name not in expected:\n            pm.shard_id = self.tce_shard_id\n            pm.replica_id = self.tce_replica_id\n            pm.sub_models = {\n                sub_model_name: vp\n                for sub_model_name, vp in pm.sub_models.items()\n                if sub_model_name.startswith('entry')\n            }\n            expected[model_name] = pm\n\n      return expected\n\n  def get_published_path(self, model_name: str) -> List[str]:\n    with self._data_lock:\n      paths = []\n      for path in self._data:\n        if path.startswith(\n            self.publish_base_path) and path.endswith(model_name):\n          paths.append(path)\n      return paths\n\n  def update_service(self, replicas: List[ReplicaMeta]):\n    # /{bzid}/service/{model_name}/deploy_type:task_id/replica -> ReplicaMeta\n    need_create_or_update, local_load_paths = {}, set()\n    for rm in replicas:\n      path = rm.get_path(self._bzid, self._sep)\n      value = rm.serialize()\n      local_load_paths.add(path)\n\n      loc = self.get(path)\n      if loc is None or loc != value:  # not exists or changed\n        need_create_or_update[path] = value\n\n    # only care about local replicas, remove first\n    need_remove_paths = self.local_replica_paths - local_load_paths\n    for path in need_remove_paths:\n      self.delete(path)\n\n    # create or update replicas\n    if need_create_or_update:\n      logging.info(f'need_create_or_update: {need_create_or_update}')\n    for path, value in need_create_or_update.items():\n      try:\n        self.create(path=path, value=value, ephemeral=True, makepath=True)\n      except Exception as e:\n        logging.info(repr(e))\n\n  @property\n  def local_replica_paths(self) -> Set[str]:\n    with self._data_lock:\n      local_replicas = set()\n      for path in self._data:\n        if path.startswith(self.service_base_path):\n          rm = ReplicaMeta.deserialize(self._data[path])\n          host = rm.address.split(':')[0]\n          if host == self._local_host and rm.replica == self.tce_replica_id:\n            local_replicas.add(path)\n      return local_replicas\n\n  def get_all_replicas(self, server_type: str) -> Dict[str, List[ReplicaMeta]]:\n    # f'{model_name}:{server_type}:{task_id}' -> ReplicaMeta\n    with self._data_lock:\n      result: Dict[str, List[ReplicaMeta]] = defaultdict(list)\n      for path, value in self._data.items():\n        if path.startswith(self.service_base_path):\n          raw_key = path[len(self.service_base_path):].strip(self._sep)\n          model_name, st, task, _ = raw_key.replace(self._sep, ':').split(':')\n          if st == server_type:\n            key = ':'.join([model_name, server_type, task])\n            rm = ReplicaMeta.deserialize(value)\n            if rm.stat == ModelState.AVAILABLE:\n              result[key].append(rm)\n\n      return result\n\n  def get_model_replicas(self, model_name: str,\n                         server_type: str) -> Dict[str, List[ReplicaMeta]]:\n    # f'{model_name}:{server_type}:{task_id}' -> ReplicaMeta\n    with self._data_lock:\n      result: Dict[str, List[ReplicaMeta]] = defaultdict(list)\n      base_path = os.path.join(self.service_base_path, model_name)\n      for task in self.get_children(base_path):\n        if task.startswith(server_type.lower()):\n          task_path = os.path.join(base_path, task)\n          for replica in self.get_children(task_path):\n            path = os.path.join(task_path, replica)\n            content = self._data.get(path)\n            if content is not None:\n              rm = ReplicaMeta.deserialize(content)\n              if rm.stat == ModelState.AVAILABLE:\n                result[f'{model_name}:{task}'].append(rm)\n      return result\n\n  def get_task_replicas(self, model_name: str, server_type: str,\n                        task: int) -> List[ReplicaMeta]:\n    with self._data_lock:\n      path = os.path.join(self.service_base_path, model_name,\n                          f'{server_type.lower()}:{task}')\n      result: List[ReplicaMeta] = []\n      for child in self.get_children(path):\n        content = self._data.get(os.path.join(path, child))\n        if content is not None:\n          rm = ReplicaMeta.deserialize(content)\n          if rm.stat == ModelState.AVAILABLE:\n            result.append(rm)\n      return result\n\n  def get_replica(self, model_name: str, server_type: str, task: int,\n                  replica: int) -> Optional[ReplicaMeta]:\n    with self._data_lock:\n      path = os.path.join(self.service_base_path, model_name,\n                          f'{server_type.lower()}:{task}', str(replica))\n      content = self._data.get(path)\n      if content is None:\n        return None\n      else:\n        rm = ReplicaMeta.deserialize(content)\n        if rm.stat == ModelState.AVAILABLE:\n          return rm\n        else:\n          return None\n\n  def watch_portal(self):\n    # 1) check portal/publish conscience\n    self._zk.ensure_path(path=self.portal_base_path)\n    self._zk.ensure_path(path=self.publish_base_path)\n    models_in_portal = set(\n        self._zk.get_children(path=self.portal_base_path) or [])\n    models_in_publish = {\n        item.split(':')[-1]  # {shard_id}:{model_name} -> model_name\n        for item in (self._zk.get_children(path=self.publish_base_path) or [])\n    }\n    if len(models_in_publish) > 0:\n      if len(models_in_portal) == 0:\n        # just remove all\n        remove = models_in_publish\n      else:\n        remove = models_in_publish - models_in_portal\n\n      for model in remove:\n        for key in self._zk.get_children(path=self.publish_base_path):\n          if key.endswith(model):\n            self.delete(path=os.path.join(self.publish_base_path, key),\n                        recursive=True)\n\n    # 2) watch portal\n    models = set()\n\n    def create_data_watch(data_path: str):\n      logging.info(\n          f\"add data watch for model {os.path.basename(data_path)} in portal\")\n\n      def data_watch(data: bytes, state: ZnodeStat, event: WatchedEvent):\n        # info = ModelMeta.deserialize(data)\n        with self._data_lock:\n          if event is None or event.type in {\n              EventType.CREATED, EventType.DELETED\n          }:\n            # in the first call, event is None\n            if event is None:\n              logging.info(f'call watch_portal when restart {data}')\n            if event is None and data is None:\n              action = EventType.DELETED\n            else:\n              action = EventType.NONE if event is None else event.type\n            if data is not None and len(data) > 0:\n              mm = ModelMeta.deserialize(data)\n              mm.action = action\n            else:\n              mm = ModelMeta(model_name=os.path.basename(data_path),\n                             action=action)\n            self.queue.put(Event(data_path, mm.serialize(), EType.PORTAL))\n          else:\n            assert event.type in {\n                EventType.CHILD, EventType.CHANGED, EventType.NONE\n            }\n\n      return data_watch\n\n    def children_watch(children: List[str]):\n      if children is None or len(children) == 0:\n        return\n      else:\n        for model in children:\n          if model not in models:\n            models.add(model)\n            path = os.path.join(self.portal_base_path, model)\n            self._zk.DataWatch(path=path, func=create_data_watch(path))\n\n    logging.info(f\"add children watch in portal\")\n    self._zk.ChildrenWatch(path=self.portal_base_path, func=children_watch)\n\n  def watch_publish(self):\n    publishs = set()\n    self._zk.ensure_path(path=self.publish_base_path)\n\n    def get_publish_cnt(model_name: str):\n      cnt = 0\n      for path in self._data:\n        if path.startswith(\n            self.publish_base_path) and path.endswith(model_name):\n          cnt += 1\n\n      return cnt\n\n    def create_data_watch(data_path: str):\n\n      def data_watch(data: bytes, state: ZnodeStat, event: WatchedEvent):\n        data = data or self._data.get(data_path, None)\n        if data is not None and len(data) > 0:\n          pm = PublishMeta.deserialize(data)\n        else:\n          logging.info(f'watch_publish: data is None, {event}')\n          return\n\n        with self._data_lock:\n          if event is None or event.type == EventType.CREATED:\n            # in the first call, event is None\n            if pm.ptype == PType.LOAD:\n              self._data[data_path] = data\n            else:\n              del self._data[data_path]\n          elif event.type == EventType.DELETED:\n            if data_path in self._data:\n              del self._data[data_path]\n          else:\n            assert event.type in {\n                EventType.CHILD, EventType.CHANGED, EventType.NONE\n            }\n\n          cnt = get_publish_cnt(pm.model_name)\n          if cnt == 0 or cnt == pm.total_publish_num:\n            logging.info(f\"all the publish of model {pm.model_name} arrived, \"\n                         f\"send event to {'unload' if cnt == 0 else 'load'}\")\n            load_path = pm.get_path(self.publish_base_path)\n            data = self._data.get(load_path, data)\n            self.queue.put(Event(data_path, data, EType.PUBLISH))\n\n      return data_watch\n\n    def children_watch(children: List[str]):\n      if children is None or len(children) == 0:\n        return\n      else:\n        for pub in children:\n          if pub not in publishs:\n            publishs.add(pub)\n            path = os.path.join(self.publish_base_path, pub)\n            self._zk.DataWatch(path=path, func=create_data_watch(path))\n\n    self._zk.ChildrenWatch(path=self.publish_base_path, func=children_watch)\n\n  def watch_resource(self):\n    instances = set()\n\n    def create_data_watch(data_path: str):\n\n      def data_watch(data: bytes, state: ZnodeStat, event: WatchedEvent):\n        data = data or self._data.get(data_path, None)\n        with self._data_lock:\n          if event is None or event.type == EventType.CREATED:\n            # in the first call, event is None\n            self._data[data_path] = data\n          elif event.type == EventType.DELETED:\n            del self._data[data_path]\n          elif event.type == EventType.CHANGED:\n            self._data[data_path] = data\n          else:\n            assert event.type in {EventType.CHILD, EventType.NONE}\n\n      return data_watch\n\n    def children_watch(children: List[str]):\n      if children is None or len(children) == 0:\n        return\n      else:\n        for inst in children:\n          if inst not in instances:\n            instances.add(inst)\n            path = os.path.join(self.resource_path, inst)\n            self._zk.DataWatch(path=path, func=create_data_watch(path))\n\n    self._zk.ChildrenWatch(path=self.resource_path, func=children_watch)\n\n  def watch_service(self):\n    # /{bzid}/service/{model_name}/deploy_type:task_id/replica -> ReplicaMeta\n\n    children_set = set()\n    self._zk.ensure_path(path=self.service_base_path)\n\n    def create_data_watch(data_path: str):\n      logging.info(f'data_path: {data_path}')\n\n      def data_watch(data: bytes, state: ZnodeStat, event: WatchedEvent):\n        logging.info(f'service data_watch: {data_path}: {data}, {event}')\n        data = data or self._data.get(data_path, None)\n        with self._data_lock:\n          if event is None or event.type == EventType.CREATED:\n            # in the first call, event is None\n            self._data[data_path] = data\n          elif event.type == EventType.DELETED:\n            del self._data[data_path]\n          elif event.type == EventType.CHANGED:\n            self._data[data_path] = data\n          else:\n            assert event.type in {EventType.CHILD, EventType.NONE}\n\n      return data_watch\n\n    def create_replica_watch(task_path: str):\n      logging.info(f'task_path: {task_path}')\n      model = os.path.basename(os.path.dirname(task_path))\n      task = os.path.basename(task_path)\n\n      def replica_watch(children: List[str]):\n        if children is None or len(children) == 0:\n          return\n        else:\n          for replica in children:\n            key = f\"{model}:{task}:{replica}\"\n            if key not in children_set:\n              children_set.add(key)\n              path = os.path.join(task_path, replica)\n              self._zk.DataWatch(path=path, func=create_data_watch(path))\n\n      return replica_watch\n\n    def create_task_watch(model_path: str):\n      logging.info(f'model_path: {model_path}')\n      model = os.path.basename(model_path)\n\n      def task_watch(children: List[str]):\n        if children is None or len(children) == 0:\n          return\n        else:\n          for task in children:\n            key = f\"{model}:{task}\"\n            if key not in children_set:\n              children_set.add(key)\n              path = os.path.join(model_path, task)\n              self._zk.ChildrenWatch(path=path, func=create_replica_watch(path))\n\n      return task_watch\n\n    def model_watch(children: List[str]):\n      if children is None or len(children) == 0:\n        return\n      else:\n        for model in children:\n          key = model\n          if key not in children_set:\n            children_set.add(key)\n            path = os.path.join(self.service_base_path, model)\n            self._zk.ChildrenWatch(path=path, func=create_task_watch(path))\n\n    self._zk.ChildrenWatch(path=self.service_base_path, func=model_watch)\n\n  def election(self, leader, sched, identifier: str = None):\n    self._leader = leader\n    identifier = identifier or os.environ.get('MY_POD_NAME')\n\n    if self._deploy_type == DeployType.ENTRY:\n      logging.info('entry cannot be leader!')\n      return\n\n    def target():\n      try:\n        election: Election = self._zk.Election(self._zk_election_path,\n                                               identifier)\n        election.run(leader, zk=self, sched=sched)\n      except ConnectionClosedError as e:\n        if self._zk.state in {KazooState.CONNECTED, KazooState.SUSPENDED}:\n          logging.info(f\"ConnectionClosedError, state is {self._zk.state}\")\n          pass\n        else:\n          logging.info(f\"kazo {self._zk.state}, restart!\")\n          with self._data_lock:\n            self._data = {}\n            while not self.queue.empty():\n              self.queue.get_nowait()\n          self.start()\n      except Exception as e:\n        logging.info(e)\n\n    thread = Thread(target=target)\n    thread.start()\n\n  def start(self, is_client: bool = False):\n    self._zk.start()\n    self.watch_service()\n    if not is_client:\n      self.watch_publish()\n\n  def stop(self):\n    if self._leader is not None:\n      self._leader.cancel()\n\n    self._zk.stop()\n"
  },
  {
    "path": "monolith/agent_service/zk_mirror_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport os\nfrom random import shuffle\nfrom queue import Queue\nfrom kazoo.exceptions import NodeExistsError\nimport socket\nimport time\nimport threading\n\nimport unittest\n\nfrom monolith.agent_service import constants\nfrom monolith.agent_service import utils\nfrom monolith.agent_service.agent_service_pb2 import ServerType\nfrom monolith.agent_service.mocked_tfserving import FakeTFServing\nfrom monolith.agent_service.mocked_zkclient import FakeKazooClient\nfrom monolith.agent_service.zk_mirror import ZKMirror\nfrom monolith.agent_service.data_def import PublishMeta, PublishType, ReplicaMeta, ResourceSpec, \\\n  SubModelName, VersionPath, ModelMeta, EventType\n\nMODEL_NAME = 'model'\nBASE_PATH = f'/tmp/{MODEL_NAME}/saved_models'\nNUM_REPLICAS = 3\n\n\nclass ZKMirrorTest(unittest.TestCase):\n  tfs: FakeTFServing = None\n  agent_conf: utils.AgentConfig = None\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    os.environ[constants.HOST_SHARD_ENV] = '10'\n    os.environ['SHARD_ID'] = '2'\n    os.environ['REPLICA_ID'] = '2'\n    cls.bzid = 'bzid'\n    cls.shard_id = 2\n    cls.num_tce_shard = 10\n    cls.replica_id = 2\n\n    cls.zk = ZKMirror(zk=FakeKazooClient(),\n                      bzid=cls.bzid,\n                      queue=Queue(),\n                      tce_shard_id=cls.shard_id,\n                      num_tce_shard=cls.num_tce_shard)\n    cls.zk.start()\n\n    cls.resource = ResourceSpec(\n        address=f'{utils.get_local_ip()}:1234',  # host:port\n        shard_id=cls.shard_id,\n        replica_id=cls.replica_id,\n        memory=12345,\n        cpu=5.6,\n        network=3.2,\n        work_load=0.7)\n\n  @classmethod\n  def tearDownClass(cls) -> None:\n    cls.zk.stop()\n\n  def test_crud(self):\n    # ensure_path\n    self.zk.ensure_path(path='/model/crud')\n    # exists\n    self.assertTrue(self.zk.exists(path='/model/crud'))\n    # create\n    self.zk.create(path='/model/crud/data', value=b'test', makepath=True)\n    # get/set\n    value, _ = self.zk._zk.get(path='/model/crud/data')\n    self.assertEqual(value, b'test')\n    self.zk.set(path='/model/crud/data', value=b'new_test')\n    value, _ = self.zk._zk.get(path='/model/crud/data')\n    self.assertEqual(value, b'new_test')\n    # delete\n    self.zk.delete(path='/model/crud', recursive=False)\n    self.assertFalse(self.zk.exists(path='/model/crud'))\n\n    # porperties\n    self.assertEqual(self.zk.num_tce_shard, 10)\n    self.assertEqual(self.zk.tce_replica_id, 2)\n    self.assertEqual(self.zk.tce_shard_id, 2)\n\n  def test_zk_mirror(self):\n    # 0) test_step0_request_loading\n    self.zk.watch_portal()\n    self.zk.watch_resource()\n\n    path = os.path.join(self.zk.portal_base_path, MODEL_NAME)\n    mm = ModelMeta(model_name=MODEL_NAME, model_dir=BASE_PATH, num_shard=5)\n    self.zk.create(path, mm.serialize())\n\n    # 1) test_step1_scheduler\n    path = os.path.join(self.zk.portal_base_path, MODEL_NAME)\n    event = self.zk.queue.get()\n    self.assertEqual(event.etype, EventType.PORTAL)\n    self.assertEqual(event.path, path)\n    mm = ModelMeta.deserialize(event.data)\n\n    version, num_ps, num_tce_shard = 123456, 10, self.zk.num_tce_shard\n    pms = []\n    tce_shards = list(range(self.zk.num_tce_shard))\n    shuffle(tce_shards)\n\n    # scheduler\n    for i in range(mm.num_shard):\n      sub_models: Dict[SubModelName, VersionPath] = {\n          f'ps_{k}': f'{mm.model_dir}/ps_{k}/{version}'\n          for k in range(num_ps)\n          if k % mm.num_shard == i\n      }\n      sub_models['entry'] = f'{mm.model_dir}/entry/{version}'\n\n      # random schedule, and ensure current shard included\n      if i == 0:\n        shard_id = self.shard_id\n      else:\n        shard_id = tce_shards.pop()\n        if shard_id == self.shard_id:\n          shard_id = tce_shards.pop()\n\n      for replica_id in range(NUM_REPLICAS):\n        pm = PublishMeta(shard_id=shard_id,\n                         replica_id=replica_id,\n                         model_name=mm.model_name,\n                         num_ps=10,\n                         sub_models=sub_models)\n        pms.append(pm)\n\n    for pm in pms:\n      pm.total_publish_num = len(pms)\n    self.zk.publish_loadding(pms)\n\n    # 2) test_step2_loading\n    expected_loading = self.zk.expected_loading()\n    for model_name, pm in expected_loading.items():\n      self.assertEqual(model_name, MODEL_NAME)\n      self.assertEqual(self.shard_id, pm.shard_id)\n      self.assertTrue('entry' in pm.sub_models)\n\n    # 3) test_step3_update_service\n    expected_loading = self.zk.expected_loading()\n    for model_name, pm in expected_loading.items():\n      replicas = []\n      for sub_model_name, vp in pm.sub_models.items():\n        if sub_model_name == 'entry':\n          server_type, task = 'entry', 0\n        else:\n          server_type, task = sub_model_name.split('_')\n          task = int(task)\n\n        rm = ReplicaMeta(\n            address=f'{utils.get_local_ip()}:8080',  # host:port\n            model_name=model_name,\n            server_type=server_type,\n            task=task,\n            replica=self.replica_id,\n            stat=utils.ModelState.AVAILABLE)\n        replicas.append(rm)\n      self.zk.update_service(replicas)\n\n    # 4) test_step4_replicas_ops\n    local_ip = utils.get_local_ip()\n    entry_replica = ReplicaMeta(address=f'{local_ip}:8080',\n                                model_name='model',\n                                server_type='entry',\n                                task=0,\n                                replica=2,\n                                stat=30)\n    ps0_replica = ReplicaMeta(address=f'{local_ip}:8080',\n                              model_name='model',\n                              server_type='ps',\n                              task=0,\n                              replica=2,\n                              stat=30)\n    ps5_replica = ReplicaMeta(address=f'{local_ip}:8080',\n                              model_name='model',\n                              server_type='ps',\n                              task=5,\n                              replica=2,\n                              stat=30)\n\n    all_replicas = self.zk.get_all_replicas(server_type='ps')\n    self.assertEqual(all_replicas['model:ps:0'][0], ps0_replica)\n    self.assertEqual(all_replicas['model:ps:5'][0], ps5_replica)\n\n    model_replicas = self.zk.get_model_replicas(model_name=MODEL_NAME,\n                                                server_type='entry')\n    self.assertEqual(model_replicas['model:entry:0'][0], entry_replica)\n\n    task_replicas = self.zk.get_task_replicas(model_name=MODEL_NAME,\n                                              server_type='ps',\n                                              task=0)\n    self.assertEqual(task_replicas[0], ps0_replica)\n\n    self.assertEqual(\n        ps5_replica,\n        self.zk.get_replica(model_name=MODEL_NAME,\n                            server_type='ps',\n                            task=5,\n                            replica=2))\n\n    local_replica_paths = {\n        '/bzid/service/model/ps:0/2', '/bzid/service/model/entry:0/2',\n        '/bzid/service/model/ps:5/2'\n    }\n    self.assertSetEqual(local_replica_paths, self.zk.local_replica_paths)\n\n    # 5) test_step5_report_resources\n    self.zk.report_resource(self.resource)\n\n    # 6) test_step6_get_resources\n    self.assertEqual(self.zk.resources[0], self.resource)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/base_runner.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Base class for all jobs.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\n\n\nclass BaseRunner(object):\n  \"\"\"Base class for all jobs.\"\"\"\n\n  def __init__(self, *args, **kwargs):\n    \"\"\"Construct a new BaseRunner.\n    Args:\n      params:  Params object containing model configuration.\n      model_dir:  String path to the log directory to output to.\n    \"\"\"\n    pass\n\n  def run(self):\n    raise NotImplementedError\n\n  def write_summary(self, logs, summary_writer, current_step):\n    \"\"\"Write out summaries of current training step for the checkpoint.\"\"\"\n    with tf.compat.v1.Graph().as_default():\n      summaries = [\n          tf.compat.v1.Summary.Value(tag=tag, simple_value=value)\n          for tag, value in logs.items()\n      ]\n      tf_summary = tf.compat.v1.Summary(value=summaries)\n      summary_writer.add_summary(tf_summary, current_step)\n"
  },
  {
    "path": "monolith/common/python/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_library\")\n\nfilegroup(\n    name = \"libtcmalloc\",\n    srcs = [\"@gperftools//:libtcmalloc\"],\n)\n\nfilegroup(\n    name = \"mem_profiling_internal_deps\",\n)\n\npy_library(\n    name = \"mem_profiling\",\n    srcs = [\"mem_profiling.py\"],\n    data = [\n        \":libtcmalloc\",\n        \":mem_profiling_internal_deps\",\n    ],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//monolith:utils\",\n        \"//monolith/native_training:mlp_utils\",\n    ],\n)\n"
  },
  {
    "path": "monolith/common/python/mem_profiling.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nfrom monolith import utils\nfrom monolith.native_training.mlp_utils import MLPEnv\n\n\ndef enable_tcmalloc():\n  libs = os.environ.get(\"LD_PRELOAD\", \"\").split(\":\")\n  libs.append(\n      utils.get_libops_path(\"../gperftools/libtcmalloc/lib/libtcmalloc.so\"))\n  os.environ[\"LD_PRELOAD\"] = \":\".join(libs)\n\n\ndef setup_heap_profile(heap_profile_inuse_interval=104857600,\n                       heap_profile_allocation_interval=1073741824,\n                       heap_profile_time_interval=0,\n                       sample_ratio=1.0,\n                       heap_profile_mmap=False,\n                       heap_pro_file=None):\n  \"\"\"See https://gperftools.github.io/gperftools/heapprofile.html for the meaning of each\n  parameters meaning.\n\n  Args:\n    sample_ratio: ratio of new we tracked in the heap profiler. Since the full profiler is\n    very slow, usually can be set something like 1/64.\n  \"\"\"\n  enable_tcmalloc()\n  mlp_env = MLPEnv()\n  os.environ[\"HEAPPROFILE\"] = os.path.join(heap_pro_file or utils.find_main(),\n                                           f\"hprof_{mlp_env.index}\")\n  os.environ[\"HEAP_PROFILE_INUSE_INTERVAL\"] = str(\n      int(heap_profile_inuse_interval / sample_ratio))\n  os.environ[\"HEAP_PROFILE_ALLOCATION_INTERVAL\"] = str(\n      int(heap_profile_allocation_interval / sample_ratio))\n  os.environ[\"HEAP_PROFILE_SAMPLE_RATIO\"] = str(sample_ratio)\n  os.environ[\"HEAP_PROFILE_TIME_INTERVAL\"] = str(heap_profile_time_interval)\n  os.environ[\"HEAP_PROFILE_MMAP\"] = str(heap_profile_mmap).lower()\n"
  },
  {
    "path": "monolith/core/BUILD",
    "content": "load(\"@pip_deps//:requirements.bzl\", \"requirement\")\nload(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\n\npackage(\n    default_visibility = [\"//visibility:public\"],\n)\n\npy_library(\n    name = \"base_embedding_task\",\n    srcs = [\"base_embedding_task.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":auto_checkpoint_feed_hook\",\n        \":base_embedding_host_call\",\n        \":base_task\",\n        \":feature\",\n        \":util\",\n    ],\n)\n\npy_library(\n    name = \"base_layer\",\n    srcs = [\"base_layer.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":hyperparams\",\n        \":py_utils\",\n    ],\n)\n\npy_library(\n    name = \"base_host_call\",\n    srcs = [\"base_host_call.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n    ],\n)\n\npy_library(\n    name = \"base_embedding_host_call\",\n    srcs = [\"base_embedding_host_call.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":base_host_call\",\n        \":tpu_variable\",\n    ],\n)\n\npy_test(\n    name = \"base_embedding_host_call_test\",\n    srcs = [\"base_embedding_host_call_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":base_embedding_host_call\",\n    ],\n)\n\npy_library(\n    name = \"host_call\",\n    srcs = [\"host_call.py\"],\n    srcs_version = \"PY3\",\n)\n\npy_library(\n    name = \"mixed_emb_op_comb_nws\",\n    srcs = [\"mixed_emb_op_comb_nws.py\"],\n    srcs_version = \"PY3\",\n)\n\npy_test(\n    name = \"base_layer_test\",\n    srcs = [\"base_layer_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":base_layer\",\n    ],\n)\n\npy_library(\n    name = \"base_model_params\",\n    srcs = [\"base_model_params.py\"],\n    srcs_version = \"PY3\",\n)\n\npy_library(\n    name = \"base_task\",\n    srcs = [\"base_task.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":base_layer\",\n        \":hyperparams\",\n    ],\n)\n\npy_test(\n    name = \"core_test_suite\",\n    srcs = [\"core_test_suite.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":base_embedding_host_call_test\",\n        \":base_layer_test\",\n        \":hyperparams_test\",\n        \":util_test\",\n    ],\n)\n\npy_library(\n    name = \"dense\",\n    srcs = [\"dense.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":base_layer\",\n        \":variance_scaling\",\n    ],\n)\n\npy_test(\n    name = \"dense_test\",\n    srcs = [\"dense_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":dense\",\n        \":testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"feature\",\n    srcs = [\"feature.py\"],\n    srcs_version = \"PY3\",\n)\n\npy_test(\n    name = \"hyperparams_test\",\n    srcs = [\"hyperparams_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":hyperparams\",\n    ],\n)\n\npy_library(\n    name = \"model\",\n    srcs = [\"model.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":feature\",\n    ],\n)\n\npy_library(\n    name = \"hyperparams\",\n    srcs = [\"hyperparams.py\"],\n    srcs_version = \"PY3\",\n)\n\npy_library(\n    name = \"model_imports_no_params\",\n    srcs = [\"model_imports.py\"],\n    srcs_version = \"PY3\",\n)\n\npy_library(\n    name = \"model_imports\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":model_imports_no_params\",\n    ],\n)\n\npy_library(\n    name = \"model_registry\",\n    srcs = [\"model_registry.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":base_model_params\",\n        \":model_imports_no_params\",\n    ],\n)\n\npy_library(\n    name = \"optimizers\",\n    srcs = [\"optimizers.py\"],\n    srcs_version = \"PY3\",\n)\n\npy_library(\n    name = \"py_utils\",\n    srcs = [\"py_utils.py\"],\n    srcs_version = \"PY3\",\n    deps = [],\n)\n\npy_library(\n    name = \"tpu_variable\",\n    srcs = [\"tpu_variable.py\"],\n    srcs_version = \"PY3\",\n    deps = [],\n)\n\npy_library(\n    name = \"testing_utils\",\n    srcs = [\"testing_utils.py\"],\n    srcs_version = \"PY3\",\n)\n\npy_library(\n    name = \"util\",\n    srcs = [\"util.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        requirement(\"google-cloud-storage\"),\n    ],\n)\n\npy_library(\n    name = \"variance_scaling\",\n    srcs = [\"variance_scaling.py\"],\n    srcs_version = \"PY3\",\n)\n\n\npy_library(\n    name = \"auto_checkpoint_feed_hook\",\n    srcs = [\"auto_checkpoint_feed_hook.py\"],\n    srcs_version = \"PY3\",\n)\n\npy_test(\n    name = \"feature_test\",\n    srcs = [\"feature_test.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \"feature\",\n        \"hyperparams\",\n    ],\n)\n\npy_library(\n    name = \"util_test\",\n    srcs = [\"util_test.py\"],\n    srcs_version = \"PY3\",\n    deps = [\":util\"],\n)\n"
  },
  {
    "path": "monolith/core/__init__.py",
    "content": ""
  },
  {
    "path": "monolith/core/auto_checkpoint_feed_hook.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport threading\nimport time\nimport os\nfrom six.moves import queue as Queue  # pylint: disable=redefined-builtin\nfrom six.moves import xrange  # pylint: disable=redefined-builtin\n\nimport tensorflow.compat.v1 as tf\nfrom tensorflow.core.protobuf import config_pb2\nfrom tensorflow.core.protobuf.tpu import compilation_result_pb2 as tpu_compilation_result\nfrom tensorflow.python.ops import summary_ops_v2 as contrib_summary\n\n_USER_PROVIDED_SIGNAL_NAME = \"_user_provided_signal_name\"\n_TPU_ESTIMATOR = 'tpu_estimator'\n_ITERATIONS_PER_LOOP_VAR = 'iterations_per_loop'\n\n\nclass PeriodicLogger(object):\n\n  def __init__(self, seconds):\n    self._log_every_n_seconds = seconds\n    self._last_log_time = 0\n\n  def log(self, msg, *args, **kw):\n    if time.time() - self._last_log_time > self._log_every_n_seconds:\n      self._last_log_time = time.time()\n      tf.compat.v1.logging.info(msg, *args, **kw)\n\n\nclass _SIGNAL(object):\n  \"\"\"Signal used to control the thread of infeed/outfeed.\n\n  All preserved signals must be negative numbers. Positive numbers are used to\n  indicate the number of iterations for next training/evaluation loop.\n  \"\"\"\n  NEXT_BATCH = -1\n  STOP = -2\n\n\nclass _OpQueueContext(object):\n  \"\"\"Manages work queue and thread for a infeed/outfeed thread.\"\"\"\n\n  def __init__(self, name, target, args):\n    self._name = name\n    self._queue = Queue.Queue()\n    args = (self,) + args\n    self._thread = threading.Thread(name=name, target=target, args=args)\n    self._thread.daemon = True\n    self._thread.start()\n\n  def stop(self):\n    self._queue.put(_SIGNAL.STOP)\n\n  def send_next_batch_signal(self, iterations):\n    self._queue.put(iterations)\n\n  def read_iteration_counts(self):\n    while True:\n      iterations = self._queue.get(block=True)\n      tf.compat.v1.logging.debug('%s read iterations %s', self._name,\n                                 iterations)\n      if iterations == _SIGNAL.STOP:\n        tf.compat.v1.logging.info('%s received shutdown signal, stopping.',\n                                  self._name)\n        return\n      yield iterations\n\n  def join(self):\n    tf.compat.v1.logging.info('Shutting down %s thread.', self._name)\n    self.stop()\n    self._thread.join()\n\n\nclass _OpSignalOnceQueueContext(_OpQueueContext):\n  \"\"\"Manages work queue and thread for a infeed/outfeed thread.\n\n  This subclass only signals once.\n  \"\"\"\n\n  def __init__(self, name, target, args):\n    super(_OpSignalOnceQueueContext, self).__init__(name, target, args)\n    self._has_signaled = False\n\n  def send_next_batch_signal(self, iterations):\n    if not self._has_signaled:\n      self._queue.put(iterations)\n      self._has_signaled = True\n\n\nclass TPUInfeedOutfeedSessionWithEndOfStreamHandlingHook(\n    tf.estimator.SessionRunHook):\n  \"\"\"A Session hook setting up the TPU initialization, infeed, and outfeed.\n\n  This hook does two major things:\n  1. initialize and shutdown TPU system.\n  2. launch and join the threads for infeed enqueue and (optional) outfeed\n     dequeue.\n  \"\"\"\n\n  def __init__(self,\n               ctx,\n               enqueue_ops,\n               dequeue_ops,\n               tpu_compile_op,\n               run_infeed_loop_on_coordinator=True,\n               rendezvous=None,\n               master=None,\n               session_config=None,\n               tpu_init_ops=None,\n               outfeed_every_n_steps=1):\n    self._master_job = ctx.master_job\n    self._enqueue_ops = enqueue_ops\n    self._dequeue_ops = dequeue_ops\n    self._rendezvous = rendezvous\n    self._master = master\n    self._session_config = session_config\n    self._init_ops = list(tpu_init_ops or [])\n    if ctx.embedding_config is None:\n      self._embedding_layer_config = None\n    else:\n      self._embedding_layer_config = (\n          ctx.embedding_config.tpu_embedding.config_proto)\n    self._run_infeed_loop_on_coordinator = run_infeed_loop_on_coordinator\n    self._initial_infeed_sleep_secs = (\n        ctx.config.tpu_config.initial_infeed_sleep_secs)\n    self._tpu_compile_op = tpu_compile_op\n\n    # When using model parallelism, the TPU is pre-initialized at startup to\n    # fetch mesh information. We skip re-initializing it here for\n    # MeshTensorFlow since it places variables on TPU directly. Reinitialize tpu\n    # is causing the variable corruption since the previous allocated memory\n    # might be overwritten for other purpose.\n    if (ctx.model_parallelism_enabled and\n        (ctx.config.tpu_config.per_host_input_for_training is\n         tpu_config.InputPipelineConfig.BROADCAST)):\n      self._should_initialize_tpu = False\n    else:\n      self._should_initialize_tpu = True\n    self._outfeed_every_n_steps = outfeed_every_n_steps\n\n    self.stopping_signal = False\n\n  def _create_or_get_iterations_per_loop(self):\n    \"\"\"Creates or gets the iterations_per_loop variable.\n\n    In TPUEstimator, the user provided computation, the model_fn, is wrapped\n    inside a tf.while_loop for peak performance. The iterations of the loop are\n    specified by this variable, which adjusts its value on the CPU after each TPU\n    program execution and before the next TPU execution.\n\n    The purpose of using a variable, rather then a constant, is to allow\n    TPUEstimator adapt the TPU training iterations according to the final steps\n    specified by users. For example, if the user sets the iterations_per_loop as 4\n    in TPUConfig and steps as 10 in TPUEstimator.train(), the iterations_per_loop\n    variable will have the following value before each TPU training.\n\n        - 1-th TPU execution: iterations_per_loop = 4\n        - 2-th TPU execution: iterations_per_loop = 4\n        - 3-th TPU execution: iterations_per_loop = 2\n\n    As model_fn increases the global step once per train_op invocation, the global\n    step is 10 after all TPU executions, matching the steps=10 inputs passed in by\n    users.\n\n    Returns:\n      A TF non-trainable resource variable.\n\n    Raises:\n      RuntimeError: If multi iterations_per_loop variables were found.\n    \"\"\"\n    graph = tf.compat.v1.get_default_graph()\n    collection_name = '{}_{}'.format(_TPU_ESTIMATOR, _ITERATIONS_PER_LOOP_VAR)\n    iter_vars = graph.get_collection(collection_name)\n    if len(iter_vars) == 1:\n      return iter_vars[0]\n    elif len(iter_vars) > 1:\n      raise RuntimeError('Multiple iterations_per_loop_var in collection.')\n\n    with ops.colocate_with(tf.compat.v1.train.get_global_step()):\n      with tf.compat.v1.variable_scope(_TPU_ESTIMATOR,\n                                       reuse=tf.compat.v1.AUTO_REUSE):\n        return tf.compat.v1.get_variable(\n            _ITERATIONS_PER_LOOP_VAR,\n            initializer=tf.compat.v1.initializers.zeros(),\n            shape=[],\n            dtype=tf.dtypes.int32,\n            trainable=False,\n            collections=[\n                collection_name, tf.compat.v1.GraphKeys.LOCAL_VARIABLES\n            ],\n            use_resource=True)\n\n  def begin(self):\n    tf.compat.v1.logging.info('TPU job name %s', self._master_job)\n    self._iterations_per_loop_var = self._create_or_get_iterations_per_loop()\n    if self._should_initialize_tpu:\n      self._finalize_ops = [\n          tf.compat.v1.tpu.shutdown_system(job=self._master_job)\n      ]\n    else:\n      self._finalize_ops = []\n\n    summary_writer_init_ops = contrib_summary.summary_writer_initializer_op()\n    self._init_ops.extend(summary_writer_init_ops)\n    # Get all the writer resources from the initializer, so we know what to\n    # flush.\n    for op in summary_writer_init_ops:\n      self._finalize_ops.append(contrib_summary.flush(writer=op.inputs[0]))\n\n  def _run_infeed(self, queue_ctx, session):\n    tf.compat.v1.logging.info('Starting infeed thread controller.')\n    if self._initial_infeed_sleep_secs:\n      tf.compat.v1.logging.info('Infeed thread sleeping for %d seconds.',\n                                self._initial_infeed_sleep_secs)\n      time.sleep(self._initial_infeed_sleep_secs)\n      tf.compat.v1.logging.info('Infeed thread starting after sleep')\n\n    with self._rendezvous.catch_errors(source='infeed', session=session):\n      if self._run_infeed_loop_on_coordinator:\n        for count, steps in enumerate(queue_ctx.read_iteration_counts()):\n          for i in xrange(steps):\n            tf.compat.v1.logging.debug('Infeed enqueue for iteration (%d, %d)',\n                                       count, i)\n            session.run(self._enqueue_ops)\n      else:\n        for _ in queue_ctx.read_iteration_counts():\n          session.run(self._enqueue_ops)\n      tf.compat.v1.logging.info('Infeed thread finished, shutting down.')\n\n  def _run_outfeed(self, queue_ctx, session):\n    tf.compat.v1.logging.info('Starting outfeed thread controller.')\n    status_logger = PeriodicLogger(seconds=60)\n    with self._rendezvous.catch_errors(source='outfeed', session=session):\n      stopping_signals = False\n      for count, steps in enumerate(queue_ctx.read_iteration_counts()):\n        step_counter = 0\n        for i in xrange(steps):\n          tf.compat.v1.logging.debug('Outfeed dequeue for iteration (%d, %d)',\n                                     count, i)\n          if step_counter % self._outfeed_every_n_steps == 0:\n            ret = session.run(self._dequeue_ops)\n            if _USER_PROVIDED_SIGNAL_NAME in ret:\n              if 'stopping' not in ret[_USER_PROVIDED_SIGNAL_NAME]:\n                raise RuntimeError('ret[{}] must contain key \\'stopping\\'.'\n                                  ).format(_USER_PROVIDED_SIGNAL_NAME)\n              if ret[_USER_PROVIDED_SIGNAL_NAME]['stopping'][0] == True \\\n                and stopping_signals == False:\n                stopping_signals = True\n                tf.compat.v1.logging.info(\n                    'Encountered stop signal at iteration (%d, %d).', count, i)\n          step_counter += 1\n          status_logger.log('Outfeed finished for iteration (%d, %d)', count, i)\n        if stopping_signals == True:\n          tf.compat.v1.logging.info(\n              'Set shared stop signal at iteration (%d, %d).', count, i)\n          self.stopping_signal = True\n      tf.compat.v1.logging.info('Outfeed thread finished, shutting down.')\n\n  def _create_infeed_controller(self, name, target, args):\n    return _OpQueueContext(name=name, target=target, args=args)\n\n  def _assertCompilationSucceeded(self, result, coord):\n    proto = tpu_compilation_result.CompilationResultProto()\n    proto.ParseFromString(result)\n    if proto.status_error_message:\n      tf.compat.v1.logging.error('Compilation failed: {}'.format(\n          proto.status_error_message))\n      coord.request_stop()\n    else:\n      tf.compat.v1.logging.info('Compilation succeeded')\n\n  def after_create_session(self, session, coord):\n    if self._should_initialize_tpu:\n      tf.compat.v1.logging.info('Init TPU system')\n      start = time.time()\n      with tf.Graph().as_default():\n        with tf.compat.v1.Session(self._master,\n                                  config=self._session_config) as sess:\n          sess.run(\n              tf.compat.v1.tpu.initialize_system(\n                  job=self._master_job,\n                  embedding_config=self._embedding_layer_config))\n      tf.compat.v1.logging.info('Initialized TPU in %d seconds',\n                                time.time() - start)\n\n    session.run(self._init_ops,\n                options=config_pb2.RunOptions(timeout_in_ms=30 * 60 * 1000))\n\n    if os.environ.get('TPU_SPLIT_COMPILE_AND_EXECUTE', '') == '1':\n      tf.compat.v1.logging.info(\n          'Compiling user program: this may take a while...')\n      self._assertCompilationSucceeded(session.run(self._tpu_compile_op), coord)\n\n    self._infeed_controller = self._create_infeed_controller(\n        name='InfeedController', target=self._run_infeed, args=(session,))\n\n    self._outfeed_controller = _OpQueueContext(name='OutfeedController',\n                                               target=self._run_outfeed,\n                                               args=(session,))\n\n    # Enable the worker watchdog to terminate workers on coordinator exit.\n    watchdog_timeout = int(os.environ.get('TF_TPU_WATCHDOG_TIMEOUT', '0'))\n    if watchdog_timeout > 0:\n      session_support.start_worker_watchdog(session,\n                                            shutdown_timeout=watchdog_timeout)\n\n  def before_run(self, run_context):\n    if self.stopping_signal == True:\n      tf.compat.v1.logging.info(\n          'Throw OutOfRangeError error due to encountering stopping signal in before_run.'\n      )\n      raise tf.errors.OutOfRangeError(None, None, 'Stopped by stopping signal.')\n\n    iterations = run_context.session.run(self._iterations_per_loop_var)\n\n    tf.compat.v1.logging.info('Enqueue next (%d) batch(es) of data to infeed.',\n                              iterations)\n    self._infeed_controller.send_next_batch_signal(iterations)\n\n    tf.compat.v1.logging.info(\n        'Dequeue next (%d) batch(es) of data from outfeed.', iterations)\n    self._outfeed_controller.send_next_batch_signal(iterations)\n\n  def end(self, session):\n    tf.compat.v1.logging.info('Stop infeed thread controller')\n    self._infeed_controller.join()\n    self._rendezvous.record_done('infeed')\n\n    tf.compat.v1.logging.info('Stop output thread controller')\n    self._outfeed_controller.join()\n    self._rendezvous.record_done('outfeed')\n\n    tf.compat.v1.logging.info('Shutdown TPU system.')\n    session.run(self._finalize_ops)\n\n  @staticmethod\n  def get_stopping_signals_and_name(features):\n    stopping_signals = None\n    if _USER_PROVIDED_SIGNAL_NAME in features:\n      tf.compat.v1.logging.info(\"Get stopping signals and name.\")\n      sum_stopping_signals = tf.compat.v1.tpu.cross_replica_sum(\n          tf.cast(features[_USER_PROVIDED_SIGNAL_NAME], tf.int32))\n      stopping_signals = {'stopping': sum_stopping_signals > 0}\n\n    return stopping_signals, _USER_PROVIDED_SIGNAL_NAME\n"
  },
  {
    "path": "monolith/core/base_embedding_host_call.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport sys\n\nfrom absl import logging\nimport tensorflow.compat.v1 as tf\nimport tensorflow as tf2\n\nfrom monolith.core.base_host_call import BaseHostCall\nfrom monolith.core.tpu_variable import ReplicatedVariable\n\n_LABLES_FOR_AUC_CALCULATION = \"labels_for_auc_calculation\"\n_Y_PRED_FOR_AUC_CALCULATION = \"y_pred_for_auc_calculation\"\n_REQ_TIME = \"req_time\"\n_SAMPLE_RATE = \"sample_rate\"\n_UID = \"uid\"\n_UID_BUCKET = 'uid_bucket'\n\n_DEEPINSIGHT_SAMPLE_RATES = \"di_example_sample_rates\"\n_DEEPINSIGHT_LABELS = \"di_labels\"\n_DEEPINSIGHT_PREDS = \"di_preds\"\n_DEEPINSIGHT_REQ_TIMES = \"di_req_times\"\n\n_RATIO_N = 1000\n_UID_SAMPLE_RATE = 0.01\n\n_HOST_CALL_AUC_METRICS = set([\n    _LABLES_FOR_AUC_CALCULATION, _Y_PRED_FOR_AUC_CALCULATION, _SAMPLE_RATE,\n    _REQ_TIME, _UID_BUCKET\n])\n\n# TPU variables names\n_LABELS_TPU_VARIABLE = \"labels_tpu_variable\"\n_PREDS_TPU_VARIABLE = \"preds_tpu_variable\"\n_UID_BUCKETS_TPU_VARIABLE = \"uid_buckets_tpu_variable\"\n_REQ_TIMES_TPU_VARIABLE = \"req_times_tpu_variable\"\n_SAMPLE_RATES_TPU_VARIABLE = \"sample_rates_tpu_variable\"\n_ACCUMULATED_COUNTER_TPU_VARIABLE = \"tpu_variables_accumulated_times\"\n\n_DEPRECATED_METRIC_NAMES = [\n    _LABLES_FOR_AUC_CALCULATION, _Y_PRED_FOR_AUC_CALCULATION, _UID_BUCKET,\n    _REQ_TIME, _SAMPLE_RATE\n]\n\n\nclass TPUVariableRestoreHook(tf.estimator.SessionRunHook):\n  \"\"\"Initialize variables on TPU devices.\"\"\"\n\n  def __init__(self, op):\n    self._op = op\n\n  def after_create_session(self, session, coord):\n    logging.info(\"Initialize variables on TPU devices.\")\n    session.run(self._op)\n\n\nclass BaseEmbeddingHostCall(BaseHostCall):\n\n  def __init__(self, output_dir, enable_host_call, enable_deepinsight,\n               enable_host_call_scalar_metrics,\n               enable_caching_with_tpu_var_mode, top_k_sampling_num_per_core,\n               params):\n    super(BaseEmbeddingHostCall, self).__init__(output_dir, enable_host_call)\n    self._enable_host_call = params[\"enable_host_call\"]\n    self._enable_deepinsight = enable_deepinsight\n    self._enable_host_call_scalar_metrics = enable_host_call_scalar_metrics\n    self._enable_caching_with_tpu_var_mode = enable_caching_with_tpu_var_mode\n    self._top_k_sampling_num_per_core = top_k_sampling_num_per_core\n    if params[\"cpu_test\"] is True:\n      self._context = None\n    else:\n      self._context = params[\"context\"]\n    self._host_call_every_n_steps = params[\"host_call_every_n_steps\"]\n\n    # Each TPU core uses these tpu variables to record metrics.\n    #   labels tpu variable, shape is (topk_num * host_call_steps, )\n    #   preds tpu variable, shape is (topk_num * host_call_steps, )\n    #   uid_buckets tpu variable, shape is (topk_num * host_call_steps, )\n    #   req_times tpu variable, shape is (host_call_steps, )\n    #   sample_rates tpu variable, shape is (host_call_steps, )\n    self._labels_tpu_variable = None\n    self._preds_tpu_variable = None\n    self._uid_buckets_tpu_variable = None\n    self._req_times_tpu_variable = None\n    self._sample_rates_tpu_variable = None\n    # Counter of accumulating times for next host call.\n    self._accumulated_counter_tpu_variable = None\n\n    self.tpu_var_restore_hooks = []\n\n    # Create TPU variables.\n    self._create_all_tpu_variables()\n\n  # Use TPU variables to reach each step's metrics and process all metrics\n  # accumulated in each host call for deepinsight usage.\n  def _create_all_tpu_variables(self):\n    if self._enable_host_call is False:\n      logging.info(\"enable_host_call is False, do not create tpu variables.\")\n      return\n\n    if self._enable_caching_with_tpu_var_mode is False:\n      logging.info(\n          \"enable_caching_with_tpu_var_mode is False, do not create tpu variables.\"\n      )\n      return\n\n    assert self._host_call_every_n_steps > 1, \"If tpu variables caching is enabled, we need host_call_every_n_steps bigger than 1.\"\n\n    logging.info(\"Create all tpu variables.\")\n\n    # Create TPU variables for metrics.\n    max_accumulated_samples_per_host_call = self._top_k_sampling_num_per_core * self._host_call_every_n_steps\n    self._labels_tpu_variable = self._create_tpu_var(\n        _LABELS_TPU_VARIABLE, [max_accumulated_samples_per_host_call],\n        tf.float32)\n    self._preds_tpu_variable = self._create_tpu_var(\n        _PREDS_TPU_VARIABLE, [max_accumulated_samples_per_host_call],\n        tf.float32)\n    self._uid_buckets_tpu_variable = self._create_tpu_var(\n        _UID_BUCKETS_TPU_VARIABLE, [max_accumulated_samples_per_host_call],\n        tf.int32)\n\n    self._req_times_tpu_variable = self._create_tpu_var(\n        _REQ_TIMES_TPU_VARIABLE, [self._host_call_every_n_steps], tf.int64)\n    self._sample_rates_tpu_variable = self._create_tpu_var(\n        _SAMPLE_RATES_TPU_VARIABLE, [self._host_call_every_n_steps], tf.float32)\n\n    # Create meta to maintain TPU varaibles.\n    self._accumulated_counter_tpu_variable = self._create_tpu_var(\n        _ACCUMULATED_COUNTER_TPU_VARIABLE, [], tf.int32)\n\n  def _create_tpu_var(self, var_name, var_shape, var_type):\n    ctx = self._context\n    master = ctx._internal_ctx.master_job\n    job_device = '' if master is None else ('/job:%s' % master)\n    slices = []\n    tpu_host_placement_fn = ctx.tpu_host_placement_function\n    with tf.control_dependencies(None):\n      assign_ops = []\n      for h in range(ctx.num_hosts):\n        with tf.device(tpu_host_placement_fn(h)):\n          zero_tensor = tf.zeros(shape=var_shape, dtype=var_type)\n        for d in range(ctx.num_of_replicas_per_host):\n          with tf.device('%s/task:%d/device:TPU:%d' % (job_device, h, d)):\n            slice_var = tf.Variable(initial_value=zero_tensor,\n                                    trainable=False,\n                                    name=\"slice_{}_{}_{}\".format(\n                                        var_name, h, d),\n                                    dtype=var_type,\n                                    expected_shape=var_shape,\n                                    collections=[\"TPU_VAR\"])\n            slices.append(slice_var)\n          assign_ops.append(tf.assign(slice_var, zero_tensor))\n\n      tpu_var = ReplicatedVariable(var_name, slices)\n      group_assign_op = tf.group(assign_ops)\n      tpu_var_restore_hook = TPUVariableRestoreHook(group_assign_op)\n      self.tpu_var_restore_hooks.append(tpu_var_restore_hook)\n      logging.info(\"Created TPU variable, name: {}, shape: {}, type: {}\".format(\n          var_name, var_shape, var_type))\n      return tpu_var\n\n  def _compute_new_value(self, base_tpu_var, delta_value, update_offset):\n    # Need assert shape.rank is 1 for both base_tpu_var and delta_value.\n    assert base_tpu_var.get_shape().rank == 1, \\\n      \"base_tpu_var's rank must be 1, base_tpu_var shape: {}\".format(base_tpu_var.get_shape())\n    assert delta_value.get_shape().rank == 1, \\\n      \"delta_value's rank must be 1, delta_value shape: {}\".format(delta_value.get_shape())\n    assert base_tpu_var.dtype == delta_value.dtype, \"base_tpu_var dtype: {} must be same as delta_value dtype: {}\" \\\n      .format(base_tpu_var.dtype, delta_value.dtype)\n\n    # Padding in the end of delta_value so that it has same shape with base_tpu_var.\n    # And then right shift delta_value so that its valid data starts from update_offset.\n    # Note: Here we can't pad directly to delta_value in the begining and end. Because the pad position\n    # depends on update_offset which is a tensor. And that will make the pad op encounter a compilation error\n    # as following:\n    # Compilation failure: Input 1 to node `Pad` with op Pad must be a compile-time constant.\n    # XLA compilation requires that operator arguments that represent shapes or dimensions be evaluated to\n    # concrete values at compile time. This error means that a shape or dimension argument could not be\n    # evaluated at compile time, usually because the value of the argument depends on a parameter to the\n    #  computation, on a variable, or on a stateful operation such as a random number generator.\n    base_len = base_tpu_var.shape.as_list()[0]\n    delta_len = delta_value.shape.as_list()[0]\n    paddings = [[0, base_len - delta_len]]\n    delta_value = tf.pad(delta_value, paddings, 'CONSTANT', constant_values=0)\n    # Right shift delta_tpu_var according to update_offset in base_tpu_var.\n    delta_value = tf.roll(delta_value, shift=update_offset, axis=0)\n\n    return base_tpu_var + delta_value\n\n  def _clear_value_at_index_1(self, tpu_var, var_type, index):\n    return tf.where(tf.math.equal(index, 1),\n                    tf.zeros_like(tpu_var, dtype=var_type), tpu_var)\n\n  def update_tpu_variables_ops(self, global_step, labels, preds, uid_buckets,\n                               req_times, sample_rates):\n    if self._enable_host_call is False:\n      logging.info(\"enable_host_call is False, do not update tpu variables.\")\n      return []\n\n    if self._enable_caching_with_tpu_var_mode is False:\n      logging.info(\n          \"enable_caching_with_tpu_var_mode is False, do not update tpu variables.\"\n      )\n      return []\n\n    logging.info(\"Update tpu variables.\")\n\n    assert labels is not None\n    assert preds is not None\n    assert uid_buckets is not None\n\n    expected_shape = [self._top_k_sampling_num_per_core]\n    assert labels.get_shape() == expected_shape, \\\n       \"Expect shape: {}, but shape is {}\".format(expected_shape, labels.get_shape())\n    assert preds.get_shape() == expected_shape, \\\n       \"Expect shape: {}, but shape is {}\".format(expected_shape, preds.get_shape())\n    assert uid_buckets.get_shape() == expected_shape, \\\n       \"Expect shape: {}, but shape is {}\".format(expected_shape, uid_bkcets.get_shape())\n\n    # We do host call host_call_every_n_steps steps. At step 0, host_call_every_n_steps,\n    # 2 * host_call_every_n_steps, ..., we need a completed data until this step, and we do a host call\n    # to dump those data. At step 1, host_call_every_n_steps + 1, 2 * host_call_every_n_steps + 1,\n    # We will need clear any accumulated data firstly and then start accumulating new data again.\n    # We use index = tf.math.floormod(global_step, host_call_every_n_steps) to represent the step index where we are.\n    # If index is 1, we will clear everything with TPU variables before accumulating new data.\n    index = tf.math.floormod(global_step, self._host_call_every_n_steps)\n    old_accumulated_counter_value = self._clear_value_at_index_1(\n        self._accumulated_counter_tpu_variable, tf.int32, index)\n    old_labels_value = self._clear_value_at_index_1(self._labels_tpu_variable,\n                                                    tf.float32, index)\n    old_preds_value = self._clear_value_at_index_1(self._preds_tpu_variable,\n                                                   tf.float32, index)\n    old_uid_buckets_value = self._clear_value_at_index_1(\n        self._uid_buckets_tpu_variable, tf.int32, index)\n    old_req_times_tpu_variable = self._clear_value_at_index_1(\n        self._req_times_tpu_variable, tf.int64, index)\n    old_sample_rates_value = self._clear_value_at_index_1(\n        self._sample_rates_tpu_variable, tf.float32, index)\n\n    # Update labels, preds, and uid_buckets which have self._top_k_sampling_num_per_core elements in the last dimension.\n    tpu_var_offset = old_accumulated_counter_value * self._top_k_sampling_num_per_core\n\n    new_labels_value = self._compute_new_value(old_labels_value, labels,\n                                               tpu_var_offset)\n    new_preds_value = self._compute_new_value(old_preds_value, preds,\n                                              tpu_var_offset)\n    new_uid_buckets_tpu_value = self._compute_new_value(old_uid_buckets_value,\n                                                        uid_buckets,\n                                                        tpu_var_offset)\n    new_req_times_value = self._compute_new_value(\n        old_req_times_tpu_variable, req_times, old_accumulated_counter_value)\n    new_sample_rates_value = self._compute_new_value(\n        old_sample_rates_value, sample_rates, old_accumulated_counter_value)\n\n    # Increment tpu variable counter.\n    new_accumulated_counter_value = tf.math.add(old_accumulated_counter_value,\n                                                1)\n\n    # Update tpu variables.\n    update_tpu_var_ops = [\n        tf.assign(self._labels_tpu_variable, new_labels_value),\n        tf.assign(self._preds_tpu_variable, new_preds_value),\n        tf.assign(self._uid_buckets_tpu_variable, new_uid_buckets_tpu_value),\n        tf.assign(self._req_times_tpu_variable, new_req_times_value),\n        tf.assign(self._sample_rates_tpu_variable, new_sample_rates_value),\n    ]\n\n    # Update tpu variable counter should only happen after updating tpu variables.\n    with tf.control_dependencies(update_tpu_var_ops):\n      update_tpu_var_counter_op = tf.assign(\n          self._accumulated_counter_tpu_variable, new_accumulated_counter_value)\n\n    return [update_tpu_var_counter_op]\n\n  def record_summary_tpu_variables(self):\n    if self._enable_host_call is False:\n      logging.info(\n          \"enable_host_call is False, do not record summary tpu variables.\")\n      return\n\n    if self._enable_caching_with_tpu_var_mode is False:\n      logging.info(\n          \"enable_caching_with_tpu_var_mode is False, record summary tpu variables.\"\n      )\n      return\n\n    logging.info(\"Record tpu variables.\")\n    self.record_summary_tensor(_LABELS_TPU_VARIABLE,\n                               self._labels_tpu_variable.read_value())\n    self.record_summary_tensor(_PREDS_TPU_VARIABLE,\n                               self._preds_tpu_variable.read_value())\n    self.record_summary_tensor(_UID_BUCKETS_TPU_VARIABLE,\n                               self._uid_buckets_tpu_variable.read_value())\n    self.record_summary_tensor(_REQ_TIMES_TPU_VARIABLE,\n                               self._req_times_tpu_variable.read_value())\n    self.record_summary_tensor(_SAMPLE_RATES_TPU_VARIABLE,\n                               self._sample_rates_tpu_variable.read_value())\n    self.record_summary_tensor(\n        _ACCUMULATED_COUNTER_TPU_VARIABLE,\n        self._accumulated_counter_tpu_variable.read_value())\n\n  def record_summary_tensor(self, name, tensor):\n    if self._enable_host_call_scalar_metrics is False and name not in _HOST_CALL_AUC_METRICS:\n      return\n\n    if self._enable_caching_with_tpu_var_mode is True and name in _DEPRECATED_METRIC_NAMES:\n      return\n\n    super(BaseEmbeddingHostCall, self).record_summary_tensor(name, tensor)\n\n  def _verify_shape_and_dtype(self, tensor, shape_list, dtype):\n    assert tensor is not None\n    assert tensor.shape.as_list(\n    ) == shape_list, \"Expect shape: {}, but actual shape: {}\".format(\n        shape_list, tensor.shape.as_list())\n    assert tensor.dtype == dtype, \"Expect dtype {}, but actual dtype: {}\".format(\n        dtype, tensor.dtype)\n\n  def _slice_tensor(self, tensor, indices, expect_shape, expect_dtype):\n    \"\"\"Select elements from a given tensor using given indices.\n\n    Args:\n      tensor: The Tensor whose elements are selected using the indices.\n      indices: The Tensor storing the indices to be sliced.\n      expect_shape: The expected shape of tensor.\n      expect_dtype: The expected dtype of tensor.\n\n    Return:\n      The sliced tensor.\n    \"\"\"\n    self._verify_shape_and_dtype(tensor, expect_shape, expect_dtype)\n    # Flatten the tensor here and simplify its data format using reshape,\n    # which is low cost without real data copy.\n    # Each tensor has shape (n, ), n equals to core_number * batch_size_per_core\n    tensor = tf.reshape(tensor, [-1])\n    sliced_tensor = tf.gather(tensor, indices)\n\n    return sliced_tensor\n\n  def _serialize_tensor(self, sampled_tensor, gs, message_name):\n    tf2.summary.text(message_name,\n                     data=tf.io.serialize_tensor(sampled_tensor),\n                     step=gs)\n\n  # This function is deprecated for host_call.py.\n  def _serialize_messages(self, labels, y_preds, sample_rates, req_times,\n                          uid_buckets, gs):\n    assert labels is not None\n    assert y_preds is not None\n    assert sample_rates is not None\n    assert req_times is not None\n    assert uid_buckets is not None\n\n    # For sample_rates and req_times, we only need to keep the first one.\n    expect_shape = sample_rates.shape.as_list()\n    assert len(\n        expect_shape\n    ) == 1, \"Expect sample_rates shape rank to be 1, but its shape is {}\".format(\n        expect_shape)\n\n    self._serialize_tensor(sample_rates, [0], expect_shape, tf.float32, gs,\n                           _DEEPINSIGHT_SAMPLE_RATES)\n    self._serialize_tensor(req_times, [0], expect_shape, tf.int64, gs,\n                           _DEEPINSIGHT_REQ_TIMES)\n\n  def _write_summary_ops(self,\n                         gs,\n                         labels,\n                         y_preds,\n                         uid_buckets,\n                         req_times,\n                         sample_rates,\n                         stopping_signals_sum=None):\n    if labels is not None and y_preds is not None:\n      # Filter labels and y_preds by uids to ensure that only a fraction of\n      # uids are selected for AUC calculation\n      if uid_buckets is not None:\n        # Filter out data with uid_bucket < _UID_SAMPLE_RATE * _RATIO_N to write to summary file\n        reshaped_uid_buckets = tf.reshape(uid_buckets, [-1])\n\n        if stopping_signals_sum is None:\n          indices = tf.squeeze(\n              tf.where(reshaped_uid_buckets < int(_UID_SAMPLE_RATE * _RATIO_N)))\n        else:\n          indices = tf.squeeze(\n              tf.where(\n                  tf.math.logical_and(\n                      reshaped_uid_buckets < int(_UID_SAMPLE_RATE * _RATIO_N),\n                      tf.math.equal(stopping_signals_sum, 0))))\n        expect_shape = labels.shape.as_list()\n        labels = self._slice_tensor(labels, indices, expect_shape, tf.float32)\n        y_preds = self._slice_tensor(y_preds, indices, expect_shape, tf.float32)\n        self._serialize_tensor(labels, gs, _DEEPINSIGHT_LABELS)\n        self._serialize_tensor(y_preds, gs, _DEEPINSIGHT_PREDS)\n\n        if self._enable_deepinsight is True and req_times is not None:\n          assert req_times.get_shape().rank == 2, \"req_times shape: {}\".format(\n              req_times.get_shape())\n\n          # Repeat each element self._top_k_sampling_num_per_core times in dim(1).\n          req_times = tf.repeat(req_times, [self._top_k_sampling_num_per_core],\n                                axis=1)\n          req_times = self._slice_tensor(req_times, indices, expect_shape,\n                                         tf.int64)\n          self._serialize_tensor(req_times, gs, _DEEPINSIGHT_REQ_TIMES)\n\n      # Calculate AUC based on filtered labels and y_preds\n      auc, auc_op = tf.metrics.auc(labels=labels, predictions=y_preds)\n      tf2.summary.scalar(\"auc\", data=auc, step=gs)\n\n      # Serialize message\n      if self._enable_deepinsight is True:\n        if sample_rates is not None:\n          expect_shape = sample_rates.shape.as_list()\n          assert len(\n              expect_shape\n          ) == 1, \"Expect sample_rates shape rank to be 1, but its shape is {}\".format(\n              expect_shape)\n\n          # For sample_rates and req_times, we only need to keep the first element.\n          sampled_sample_rates = self._slice_tensor(sample_rates, [0],\n                                                    expect_shape, tf.float32)\n\n          self._serialize_tensor(sampled_sample_rates, gs,\n                                 _DEEPINSIGHT_SAMPLE_RATES)\n    else:\n      auc_op = None\n\n    tf2.summary.scalar(\"sampled_labels_variable_avg\",\n                       data=tf.reduce_mean(labels),\n                       step=gs)\n    tf2.summary.scalar(\"sampled_preds_variable_avg\",\n                       data=tf.reduce_mean(y_preds),\n                       step=gs)\n    tf2.summary.scalar(\"req_times_variable_avg\",\n                       data=tf.reduce_mean(req_times),\n                       step=gs)\n    tf2.summary.scalar(\"sample_rates_variable_avg\",\n                       data=tf.reduce_mean(sample_rates),\n                       step=gs)\n\n    return auc_op\n\n  def generate_host_call_hook(self):\n\n    def _host_call(*args):\n      gs, tensors = self.decompress_tensors(args)\n      summary_writer = tf2.summary.create_file_writer(self._output_dir +\n                                                      \"/host_call\",\n                                                      flush_millis=10000,\n                                                      max_queue=5000)\n      with summary_writer.as_default():\n        labels = None\n        y_preds = None\n        req_times = None\n        sample_rates = None\n        uid_buckets = None\n\n        for i, t in enumerate(tensors):\n          if i == 0:\n            continue\n\n          name = self._tensor_names[i]\n          data = None\n          if \"_avg\" in name:\n            data = tf.reduce_mean(t)\n          elif \"_max\" in name:\n            data = tf.reduce_max(t)\n          elif \"_sum\" in name:\n            data = tf.reduce_sum(t)\n          elif _LABLES_FOR_AUC_CALCULATION in name:\n            labels = t\n          elif _Y_PRED_FOR_AUC_CALCULATION in name:\n            y_preds = t\n          elif _REQ_TIME in name:\n            req_times = tf.expand_dims(t, -1)\n          elif _SAMPLE_RATE in name:\n            sample_rates = t\n          elif _UID_BUCKET in name:\n            uid_buckets = t\n          else:\n            data = t[0]\n\n          if data is not None:\n            tf2.summary.scalar(name, data=data, step=gs)\n\n        auc_op = self._write_summary_ops(gs, labels, y_preds, uid_buckets,\n                                         req_times, sample_rates)\n\n      if auc_op is not None:\n        return tf.group(tf.compat.v1.summary.all_v2_summary_ops(), auc_op)\n      else:\n        return tf.compat.v1.summary.all_v2_summary_ops()\n\n    def get_used_slice(tpu_variable, used_elements_count):\n      return tf.slice(tpu_variable, [0, 0], [-1, used_elements_count])\n\n    def _host_call_with_tpu(*args):\n      gs, tensors = self.decompress_tensors(args)\n      summary_writer = tf2.summary.create_file_writer(self._output_dir +\n                                                      \"/host_call\",\n                                                      flush_millis=10000,\n                                                      max_queue=5000)\n\n      stopping_signals_sum = None\n      auc_op = None\n      with summary_writer.as_default():\n        labels_value = None\n        preds_value = None\n        uid_buckets_value = None\n        req_times_value = None\n        sample_rates_value = None\n        accumulated_counter_value = None\n\n        for i, t in enumerate(tensors):\n          if i == 0:\n            continue\n\n          name = self._tensor_names[i]\n          data = None\n          if \"_avg\" in name:\n            data = tf.reduce_mean(t)\n          elif \"_max\" in name:\n            data = tf.reduce_max(t)\n          elif \"_sum\" in name:\n            data = tf.reduce_sum(t)\n          elif _LABELS_TPU_VARIABLE in name:\n            labels_value = t\n          elif _PREDS_TPU_VARIABLE in name:\n            preds_value = t\n          elif _UID_BUCKETS_TPU_VARIABLE in name:\n            uid_buckets_value = t\n          elif _REQ_TIMES_TPU_VARIABLE in name:\n            req_times_value = t\n          elif _SAMPLE_RATES_TPU_VARIABLE in name:\n            sample_rates_value = t\n          elif _ACCUMULATED_COUNTER_TPU_VARIABLE in name:\n            accumulated_counter_value = t\n          elif \"stopping_signals\" in name:\n            stopping_signals_sum = tf.reduce_sum(tf.cast(t, tf.int32))\n          elif name not in _DEPRECATED_METRIC_NAMES:\n            data = t[0]\n\n          if data is not None:\n            tf2.summary.scalar(name, data=data, step=gs)\n\n        # Check labels, preds, uid_buckets shape is as expected.\n        expected_multiple_values_per_step_shape = [\n            self._context.num_replicas,\n            self._host_call_every_n_steps * self._top_k_sampling_num_per_core\n        ]\n        assert labels_value.get_shape() == expected_multiple_values_per_step_shape, \\\n          \"labels_tpu_variable shape: {}, expectd_shape: {}.\" \\\n          .format(labels_value.get_shape(), expected_multiple_values_per_step_shape)\n        assert preds_value.get_shape() == expected_multiple_values_per_step_shape, \\\n          \"preds_tpu_variable shape: {}, expectd_shape: {}.\" \\\n          .format(preds_value.get_shape(), expected_multiple_values_per_step_shape)\n        assert uid_buckets_value.get_shape() == expected_multiple_values_per_step_shape, \\\n          \"uid_buckets_tpu_variable shape: {}, expectd_shape: {}.\" \\\n          .format(uid_buckets_value.get_shape(), expected_multiple_values_per_step_shape)\n\n        # Check tpu_variable_accumulated_times_scalar is as expected.\n        expected_scalar_shape = [self._context.num_replicas]\n        assert accumulated_counter_value.get_shape()== expected_scalar_shape, \\\n          \"tpu_variable_accumulated_times_scalar shape: {}, expectd_shape: {}.\" \\\n          .format(accumulated_counter_value.get_shape(), expected_scalar_shape)\n\n        used_slice_len = accumulated_counter_value[\n            0] * self._top_k_sampling_num_per_core\n\n        # Get the used parts of TPU variable. Then reshape all tpu variables to be similar shape with same rank and same batch dimension\n        # as other non-tpu variables.\n        labels = get_used_slice(labels_value, used_slice_len)\n        y_preds = get_used_slice(preds_value, used_slice_len)\n        uid_buckets = get_used_slice(uid_buckets_value, used_slice_len)\n\n        # Check req_times, sample_rates shape is as expected.\n        expected_single_value_per_step_shape = [\n            self._context.num_replicas, self._host_call_every_n_steps\n        ]\n        assert req_times_value.get_shape()== expected_single_value_per_step_shape, \\\n          \"req_times_tpu_variable shape: {}, expectd_shape: {}.\" \\\n          .format(req_times_value.get_shape(), expected_single_value_per_step_shape)\n\n        # Get the used part of req_times TPu variable.\n        req_times = get_used_slice(req_times_value,\n                                   accumulated_counter_value[0])\n\n        assert sample_rates_value.get_shape()== expected_single_value_per_step_shape, \\\n          \"sample_rates_tpu_variable shape: {}, expectd_shape: {}.\" \\\n          .format(sample_rates_value.get_shape(), expected_single_value_per_step_shape)\n\n        # Attention, here for performance purpose we use one one sample_rate\n        # to represent all examples in this host_call. We will evaluate from the deepinsight showing side\n        # to see if this has big impact when user use it.\n        sample_rates = tf.squeeze(tf.slice(sample_rates_value, [0, 0], [-1, 1]))\n\n        tf2.summary.scalar(\"uid_buckets_tpu_variable_avg\",\n                           data=tf.reduce_mean(uid_buckets),\n                           step=gs)\n        tf2.summary.scalar(\"accumulated_times_tpu_variable_avg\",\n                           data=tf.reduce_mean(accumulated_counter_value),\n                           step=gs)\n        tf2.summary.scalar(\"used_slice_len_avg\",\n                           data=tf.reduce_min(used_slice_len),\n                           step=gs)\n\n        if stopping_signals_sum is not None:\n          tf2.summary.scalar(\"stopping_signals_sum\",\n                             data=stopping_signals_sum,\n                             step=gs)\n\n        auc_op = self._write_summary_ops(gs, labels, y_preds, uid_buckets,\n                                         req_times, sample_rates,\n                                         stopping_signals_sum)\n\n      if auc_op is not None:\n        return tf.group(tf.compat.v1.summary.all_v2_summary_ops(), auc_op)\n      else:\n        return tf.compat.v1.summary.all_v2_summary_ops()\n\n    if self._enable_host_call == True:\n      self.compress_tensors()\n      if self._enable_caching_with_tpu_var_mode is False:\n        return (_host_call, self._tensors)\n      else:\n        return (_host_call_with_tpu, self._tensors)\n    else:\n      logging.info(\"host_call has been disabled\")\n      return None\n"
  },
  {
    "path": "monolith/core/base_embedding_host_call_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport collections\nimport enum\nimport functools\nimport re\nimport sys\n\nfrom absl import app as absl_app\nfrom absl import flags\nfrom absl import logging\nimport tensorflow.compat.v1 as tf\nimport unittest\n\nimport monolith.core.base_embedding_host_call as base_embedding_host_call\n\ntf.disable_eager_execution()\n\n\nclass BaseEmbeddingHostCallTest(unittest.TestCase):\n\n  def test_compute_new_value(self):\n    global_step = tf.train.get_or_create_global_step()\n    params = {\n        \"enable_host_call\": False,\n        \"context\": None,\n        \"cpu_test\": False,\n        \"host_call_every_n_steps\": 100\n    }\n    host_call = base_embedding_host_call.BaseEmbeddingHostCall(\n        \"\", False, False, False, False, 10, params)\n\n    base_value = tf.zeros([10], dtype=tf.int32)\n    delta_value = tf.ones([2], dtype=tf.int32)\n    offset = tf.constant(1, dtype=tf.int32)\n    base_value = host_call._compute_new_value(base_value, delta_value, offset)\n    expected_value = tf.constant([0, 1, 1, 0, 0, 0, 0, 0, 0, 0], dtype=tf.int32)\n    ret = tf.reduce_all(tf.math.equal(base_value, expected_value))\n    with tf.Session() as sess:\n      ret = sess.run(ret)\n      self.assertTrue(ret)\n\n    offset = tf.constant(5)\n    base_value = host_call._compute_new_value(base_value, delta_value, offset)\n    expected_value = tf.constant([0, 1, 1, 0, 0, 1, 1, 0, 0, 0], dtype=tf.int32)\n    ret = tf.reduce_all(tf.math.equal(base_value, expected_value))\n    with tf.Session() as sess:\n      ret = sess.run(ret)\n      self.assertTrue(ret)\n\n    offset = tf.constant(6)\n    base_value = host_call._compute_new_value(base_value, delta_value, offset)\n    expected_value = tf.constant([0, 1, 1, 0, 0, 1, 2, 1, 0, 0], dtype=tf.int32)\n    ret = tf.reduce_all(tf.math.equal(base_value, expected_value))\n    with tf.Session() as sess:\n      ret = sess.run(ret)\n      self.assertTrue(ret)\n\n\nif __name__ == \"__main__\":\n  unittest.main(verbosity=2)\n"
  },
  {
    "path": "monolith/core/base_embedding_task.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Base class for TPU embedding task\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport functools\nimport math\nimport numpy as np\nimport os\nimport shutil\nimport subprocess\n\nfrom absl import logging\n\nimport tensorflow.compat.v1 as tf\nfrom tensorflow.python.tpu import tpu_embedding\n\nfrom monolith.core import base_task\nimport monolith.core.auto_checkpoint_feed_hook as fh\nimport monolith.core.base_embedding_host_call as hs\nfrom monolith.core.base_embedding_host_call import BaseEmbeddingHostCall\nfrom monolith.core.feature import FeatureSlot, FeatureColumnV1, FeatureColumn3D, Env\nfrom monolith.core import base_task\nimport monolith.core.util as util\n\n\nclass BaseEmbeddingTask(base_task.BaseTask):\n  \"\"\"A embedding task which trains Sail-like model on TPU.\"\"\"\n\n  @classmethod\n  def params(cls):\n    p = super(BaseEmbeddingTask, cls).params()\n    p.define(\n        'vocab_size_per_slot', None,\n        'Fixed vocab_size for all the slots, this mainly '\n        'for quick testing purpose.')\n    p.define(\n        'custom_vocab_size_mapping',\n        None,\n        'Fixed vocab size for some slots',\n    )\n    p.define(\n        'vocab_size_offset', None,\n        'Manually increase the vocab_size of each slot by a constant. '\n        'This is used to provide a quick fix for issues/problems in '\n        'generating GCP data, e.g., incorrect vocab_id assignment or '\n        'incorrect vocab_size calculation.')\n    p.define(\n        'qr_multi_hashing', False,\n        'If True, enable QR multi hashing trick for the slots with vocab_id larger than the threshold.'\n    )\n    p.define('qr_hashing_threshold', 100000000,\n             'Threshold for the the QR slot.')\n    p.define('qr_collision_rate', 4,\n             'The hashing collision rate for the QR slot.')\n    p.define('vocab_file_path', None, 'Path for the vocab file.')\n    p.define('enable_deepinsight', False,\n             'Whether connect to deepinsight to show the results.')\n    p.define(\n        'enable_host_call_scalar_metrics', False,\n        'If True, enable host call computation of scalar metrics, including \\\n          average AUC per core, average loss, average label, learning rate, \\\n          (potentially) weight and gradient norms, etc. These metrics are \\\n          useful for model development and debugging. If False, only compute \\\n          basic metrics such as AUC, sample rate, etc.')\n    p.define(\n        'enable_host_call_norm_metrics', False,\n        'Whether to enable host call computation of weight and gradient \\\n          norms. If enable_host_call_scalar_metrics is False, this param is \\\n          NOT used.')\n    p.define('files_interleave_cycle_length', 4,\n             'The number of input files that will be processed concurrently.')\n    p.define(\n        'deterministic', False,\n        'Whether enable deterministic mini-batch training for comparable experiments.'\n    )\n    p.define(\"gradient_multiplier\", 1.0, \"Gradient multiplier for embeddings.\")\n    p.define('enable_caching_with_tpu_var_mode', False,\n             'Whether enable host call with tpu variables mode.')\n    # TODO(youlong): Revisit top_k_sampling_num_per_core\n    p.define(\n        'top_k_sampling_num_per_core', 6,\n        'The number of samples to use per core for DeepInsight AUC \\\n              calculation. A lower number means fewer samples used and faster \\\n              training, and a bigger number means more samples used and slower \\\n              training.')\n    p.define('use_random_init_embedding_for_oov', False,\n             'Whether use random initialized embedding for oov ids')\n    p.define('merge_vector', False,\n             'If True, enable merging vector tables of the same slot.')\n    # If there is file_pattern specified, file_pattern will override file_folder.\n    p.train.define('file_folder', None,\n                   'Training input data folder before date string.')\n    p.train.define('date_and_file_name_format', \"*/*/part*\",\n                   \"Training file's date and name pattern.\")\n    p.train.define(\n        'start_date', None,\n        'Training input data start date inclusively, for example: 20201201.')\n    p.train.define(\n        'end_date', None,\n        'Training input data end date inclusively, for example: 20201210.')\n    p.train.define('vocab_file_folder_prefix', None,\n                   'Prefix of hdfs folder to keep per day vocab size file.')\n    return p\n\n  def __init__(self, params):\n    \"\"\"Constructs a BaseAttentionLayer object.\"\"\"\n    super(BaseEmbeddingTask, self).__init__(params)\n    self.p = params\n    self._enable_deepinsight = self.p.enable_deepinsight\n    self._enable_host_call_scalar_metrics = self.p.enable_host_call_scalar_metrics\n    self._enable_caching_with_tpu_var_mode = self.p.enable_caching_with_tpu_var_mode\n    self._top_k_sampling_num_per_core = self.p.top_k_sampling_num_per_core\n\n    if params.vocab_size_per_slot is not None:\n      logging.info(\"Set fixed vocab_size: {} for all the slots.\".format(\n          params.vocab_size_per_slot))\n    if params.custom_vocab_size_mapping is not None:\n      logging.info(\"Set fixed vocab size for some slots: {}\".format(\n          params.custom_vocab_size_mapping))\n    vocab_size_dict = self._create_vocab_dict()\n\n    self._env = Env(vocab_size_dict=vocab_size_dict, params=self.p)\n\n    self._feature_to_config_dict = {}\n    self._table_to_config_dict = {}\n\n  def download_vocab_size_file_from_hdfs(self):\n    tmp_folder = \"temp\"\n    if os.path.exists(tmp_folder):\n      shutil.rmtree(tmp_folder)\n    os.mkdir(tmp_folder)\n    hdfs_vocab_size_file_path = \"{}{}/part*.csv\".format(\n        self.p.train.vocab_file_folder_prefix, self.p.train.end_date)\n    cmd = \"hadoop fs -copyToLocal {} {}\".format(hdfs_vocab_size_file_path,\n                                                tmp_folder)\n    logging.info(\n        \"Hdfs path prefix: {}, end_date: {}, download vocab size file from hdfs cmd: {}\"\n        .format(self.p.train.vocab_file_folder_prefix, self.p.train.end_date,\n                cmd))\n    ret = subprocess.run(cmd, shell=True)\n    downloaded_files = os.listdir(tmp_folder)\n    if ret.returncode == 0 and len(downloaded_files) == 1:\n      self.p.vocab_file_path = os.path.join(tmp_folder, downloaded_files[0])\n      logging.info(\n          \"Download vocab size file successfully from hdfs: {}, use downloaded vocab size file: {}.\"\n          .format(hdfs_vocab_size_file_path, self.p.vocab_file_path))\n    else:\n      logging.info(\"Downloaded files: {}\".format(downloaded_files))\n      logging.info(\"Use default vocab size file: {}\".format(\n          self.p.vocab_file_path))\n\n  def _create_vocab_dict(self):\n    \"\"\"Create vocab dict from a tsv file.\"\"\"\n    vocab_size_per_slot = self.p.vocab_size_per_slot\n    custom_vocab_size_mapping = self.p.custom_vocab_size_mapping\n\n    vocab_size_dict = {}\n    if self.p.train.end_date is not None and self.p.train.vocab_file_folder_prefix is not None:\n      self.download_vocab_size_file_from_hdfs()\n\n    assert self.p.vocab_file_path is not None, \\\n      \"Either provide vocab_file_path or vocab_file_folder_prefix and end date.\"\n\n    with open(self.p.vocab_file_path) as f:\n      for line in f:\n        fields = line.strip().split(\"\\t\")\n        assert len(fields) == 2, \"each line in {} must have 2 fields\".format(\n            fields)\n        if fields[0].isdigit() == False:\n          continue\n\n        slot_id = int(fields[0])\n        if vocab_size_per_slot is not None:\n          distinct_count = vocab_size_per_slot\n        else:\n          distinct_count = int(fields[1])\n          if custom_vocab_size_mapping is not None and slot_id in custom_vocab_size_mapping:\n            distinct_count = custom_vocab_size_mapping[slot_id]\n\n        if self.p.vocab_size_offset is not None:\n          distinct_count += self.p.vocab_size_offset\n\n        vocab_size_dict[slot_id] = distinct_count\n\n    logging.info(\"Slot and vocab size: {}\".format(vocab_size_dict))\n    return vocab_size_dict\n\n  def _parse_inputs(return_values):\n    if isinstance(return_values, tuple):\n      features, labels = return_values\n    else:\n      features, labels = return_values, None\n    return features, labels\n\n  def create_input_fn(self, mode=tf.estimator.ModeKeys.TRAIN):\n    \"\"\"Create input_fn given the mode.\n\n        Args:\n            mode: tf.estimator.ModeKeys.TRAIN/EVAL/PREDICT.\n        Returns:\n            An input fn for Estimator.\n        \"\"\"\n    # TODO(youlong.cheng): support eval and predict.\n    assert mode == tf.estimator.ModeKeys.TRAIN\n    file_pattern = self.p.train.file_pattern\n\n    def tf_example_parser(examples):\n      \"\"\"Parse multiple examples.\"\"\"\n      feature_map = self._get_feature_map()\n      example = tf.io.parse_example(serialized=examples, features=feature_map)\n      return self._post_process_example(example)\n\n    def insert_stopping_signal(stop, batch_size, stopping_signals_name):\n\n      def _map_fn(features):\n        empty_sparse_tensor = tf.sparse.SparseTensor(\n            indices=tf.zeros([0, 2], dtype=tf.int64),\n            values=tf.zeros([0], dtype=tf.int64),\n            dense_shape=(batch_size, 1))\n\n        shape = [batch_size]\n        if stop is True:\n          for name, tensor in features.items():\n            # For sparse tensors, set them to empty.\n            if isinstance(tensor, tf.sparse.SparseTensor) is True:\n              features[name] = empty_sparse_tensor\n          features[stopping_signals_name] = tf.ones(shape=shape,\n                                                    dtype=tf.dtypes.bool)\n        else:\n          features[stopping_signals_name] = tf.zeros(shape=shape,\n                                                     dtype=tf.dtypes.bool)\n\n        return features\n\n      return _map_fn\n\n    def input_fn(params):\n      \"\"\"Returns training or eval examples, batched as specified in params.\"\"\"\n      logging.info(\"Model input_fn\")\n\n      if params[\"cpu_test\"] is True:\n        dataset = tf.data.TFRecordDataset(file_pattern,\n                                          compression_type=None,\n                                          buffer_size=None)\n        dataset = dataset.batch(params[\"batch_size\"], drop_remainder=True).map(\n            tf_example_parser,\n            num_parallel_calls=tf.data.experimental.AUTOTUNE,\n            deterministic=self.p.deterministic)\n        return dataset.repeat()\n\n      # By shuffle=False, list_files will get all files already in time sorted order.\n      if file_pattern is not None:\n        files = tf.data.Dataset.list_files(file_pattern, shuffle=False)\n      else:\n        assert self.p.train.file_folder is not None, \\\n          \"p.train.file_folder must be defined if file_pattern is None.\"\n        assert self.p.train.date_and_file_name_format is not None, \\\n          \"p.train.date_and_file_name_format must be defined if file_pattern is None.\"\n        file_pattern_ = os.path.join(self.p.train.file_folder,\n                                     self.p.train.date_and_file_name_format)\n        logging.info(\"file_pattern: {}, file_folder: {}\".format(\n            file_pattern_, self.p.train.file_folder))\n        files = tf.data.Dataset.list_files(file_pattern_, shuffle=False)\n        assert self.p.train.end_date is not None, \\\n          \"end_date in config or flag must be defined if file_pattern is None\"\n        assert params[\"enable_stopping_signals\"] is not None, \\\n          \"When using end_date of input data, enable_stopping_signals need to be provided.\"\n        files = util.range_dateset(files,\n                                   self.p.train.file_folder,\n                                   start_date=self.p.train.start_date,\n                                   end_date=self.p.train.end_date)\n\n      # This function will get called once per TPU task. Each task will process the files\n      # with indexs which modulo num_calls equals to call_index.\n      _, call_index, num_calls, _ = (\n          params[\"context\"].current_input_fn_deployment())\n      files = files.shard(num_calls, call_index)\n\n      def fetch_dataset(filename):\n        dataset = tf.data.TFRecordDataset(filename,\n                                          compression_type=None,\n                                          buffer_size=None)\n        return dataset\n\n      # Read the data from disk in parallel.\n      # Files will be process from the beginning to the end. With a local parallel of interleaving\n      # multiple files currently. Number of interleaving files are defined by the cycle.\n      dataset = files.interleave(\n          fetch_dataset,\n          cycle_length=self.p.files_interleave_cycle_length,\n          num_parallel_calls=tf.data.experimental.AUTOTUNE,\n          deterministic=self.p.deterministic)\n      dataset = dataset.batch(params[\"batch_size\"], drop_remainder=True).map(\n          tf_example_parser,\n          num_parallel_calls=tf.data.experimental.AUTOTUNE,\n          deterministic=self.p.deterministic)\n\n      if self.p.train.repeat:\n        assert params[\"enable_stopping_signals\"] is False\n        dataset = dataset.repeat()\n\n      enable_stopping_signals = params[\"enable_stopping_signals\"]\n      if enable_stopping_signals:\n        logging.info(\"Adding stop signals to original data set.\")\n\n        # Add stop signal to help handling end of stream.\n        user_provided_dataset = dataset.map(\n          insert_stopping_signal(\n              stop=False, batch_size=params[\"batch_size\"], \\\n              stopping_signals_name=fh._USER_PROVIDED_SIGNAL_NAME),\n          num_parallel_calls=tf.data.experimental.AUTOTUNE,\n          deterministic=False)\n\n        final_batch_dataset = dataset.repeat().map(\n          insert_stopping_signal(\n              stop=True, batch_size=params[\"batch_size\"], \\\n              stopping_signals_name=fh._USER_PROVIDED_SIGNAL_NAME),\n          num_parallel_calls=tf.data.experimental.AUTOTUNE,\n          deterministic=False)\n\n        dataset = user_provided_dataset.concatenate(final_batch_dataset)\n\n      dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)\n\n      # Test only\n      # dataset = dataset.take(128).cache().repeat()\n\n      return dataset\n\n    return input_fn\n\n  def logits_fn(self):\n    \"\"\"Calculate logits.\"\"\"\n    # Inherated class must implement this function.\n    raise NotImplementedError\n\n  def init_slot_to_env(self):\n    \"\"\"Run this in the beginning to initialize the slot and its embedding dims information.\"\"\"\n    logging.info(\"Model init_slot_to_env\")\n    logging.info(\"init_slot_to_env, self: {}\".format(self))\n    self.logits_fn()\n    self._env.finalize()\n\n  def create_model_fn(self,):\n    \"\"\"Create model fn.\"\"\"\n    raise NotImplementedError('Abstract method.')\n\n  def _get_feature_map(self):\n    \"\"\"Returns data format of the serialized tf record file.\"\"\"\n    # Inherated class must implement this function.\n    raise NotImplementedError\n\n  def _post_process_example(self, example):\n    \"\"\"Postprocess example.\"\"\"\n    # build tensors for each embeddings in each slot\n    for slot_id, feature_slot in list(\n        self._env.slot_id_to_feature_slot.items()):\n      # Check if feature_slot has at least one FeatureColumn associated with it\n      # If not, it means that the slot only has ZeroFeatureColumn, so we\n      # ignore it.\n      if len(feature_slot.feature_columns) > 0:\n        for feature_column in feature_slot.feature_columns:\n          # If the vocab size per slot is set, we need to adjust the\n          # vocab_id so that no vocab_id exceed this vocab size per slot\n          embedding_tensor = example[\"{}_0\".format(feature_column.fc_name)]\n\n          if isinstance(feature_column, FeatureColumn3D):\n            new_embedding_tensor = embedding_tensor.to_sparse()\n            new_embedding_tensor = tf.SparseTensor(\n                indices=new_embedding_tensor.indices,\n                values=tf.maximum(new_embedding_tensor.values, 0),\n                dense_shape=new_embedding_tensor.dense_shape)\n          else:\n            new_embedding_tensor = tf.SparseTensor(\n                indices=embedding_tensor.indices,\n                values=tf.maximum(embedding_tensor.values, 0),\n                dense_shape=embedding_tensor.dense_shape)\n\n          if self.p.vocab_size_per_slot is not None:\n            new_embedding_tensor = tf.SparseTensor(\n                indices=new_embedding_tensor.indices,\n                values=tf.math.mod(new_embedding_tensor.values,\n                                   self.p.vocab_size_per_slot),\n                dense_shape=new_embedding_tensor.dense_shape)\n            vocab_size = self.p.vocab_size_per_slot\n          else:\n            vocab_size = self._env._vocab_size_dict.get(slot_id, 10)\n            if self.p.custom_vocab_size_mapping is not None and slot_id in self.p.custom_vocab_size_mapping:\n              new_embedding_tensor = tf.SparseTensor(\n                  indices=new_embedding_tensor.indices,\n                  values=tf.math.mod(new_embedding_tensor.values,\n                                     self.p.custom_vocab_size_mapping[slot_id]),\n                  dense_shape=new_embedding_tensor.dense_shape)\n              vocab_size = self.p.custom_vocab_size_mapping[slot_id]\n\n          if self.p.qr_multi_hashing and vocab_size > self.p.qr_hashing_threshold:\n            # setting quotient/remainder vocab size\n            R_vocab_size = vocab_size // self.p.qr_collision_rate + 1\n            Q_vocab_size = self.p.qr_collision_rate + 1\n\n            embedding_tensor = example[\"{}_0\".format(feature_column.fc_name)]\n            del example[\"{}_0\".format(feature_column.fc_name)]\n\n            # creating two features for remainder/quotient\n            for feature_slice in feature_column.feature_slice_to_tf_placeholder:\n              example[\"{}_{}_0\".format(\n                  feature_column.fc_name,\n                  feature_slice.slice_index)] = tf.SparseTensor(\n                      indices=embedding_tensor.indices,\n                      values=tf.math.floormod(embedding_tensor.values,\n                                              R_vocab_size),\n                      dense_shape=embedding_tensor.dense_shape)\n              example[\"{}_{}_1\".format(\n                  feature_column.fc_name,\n                  feature_slice.slice_index)] = tf.SparseTensor(\n                      indices=embedding_tensor.indices,\n                      values=tf.math.floordiv(embedding_tensor.values,\n                                              R_vocab_size),\n                      dense_shape=embedding_tensor.dense_shape)\n\n          else:\n            if isinstance(feature_column, FeatureColumn3D):\n              # Get row_lengths from embedding_tensor, which is RaggedTensor\n              # for FeatureColumn3D\n              row_lengths = tf.cast(\n                  embedding_tensor.row_lengths(),\n                  tf.int32,\n              )  # [B] Tensor\n              example[\"{}_0_row_lengths\".format(\n                  feature_column.fc_name)] = row_lengths\n              # seq feature, dims[0][0] is max seq length\n              new_embedding_tensor = tf.sparse.slice(\n                  new_embedding_tensor, [0, 0], [\n                      new_embedding_tensor.dense_shape[0],\n                      feature_column.max_seq_length\n                  ])\n\n            example[\"{}_0\".format(feature_column.fc_name)] = tf.sparse.reorder(\n                new_embedding_tensor)\n\n            for feature_slice in feature_column.feature_slice_to_tf_placeholder:\n              if feature_slice.slice_index != 0:\n                example[\"{}_{}\".format(\n                    feature_column.fc_name,\n                    feature_slice.slice_index)] = example[\"{}_0\".format(\n                        feature_column.fc_name)]\n    # This logic is to calculate AUC which follows current DeepInsight sampling logic.\n    # Basically we distribute samples by their UIDs in _RATIO_N buckets.\n    # Like we get their UID_BUCKET = UID % _RATIO_N.\n    # Later we choose only the examples if their UID_BUCKET < _RATIO_N * _UID_SAMPLE_RATE\n    if hs._UID in example:\n      example[hs._UID_BUCKET] = tf.cast(\n          tf.math.mod(example[hs._UID], hs._RATIO_N), tf.int32)\n    return example\n\n  def create_feature_and_table_config_dict(self):\n    \"\"\"Prepares the table and feature config given the parameters.\"\"\"\n    env = self._env\n    assert env.is_finalized()\n\n    for slot_id, feature_slot in list(\n        self._env.slot_id_to_feature_slot.items()):\n      vocab_size = env.vocab_size_dict.get(slot_id, 1)\n      # Check if feature_slot has at least one FeatureColumn associated with it\n      # If not, it means that the slot only has ZeroFeatureColumn, so we\n      # ignore it.\n      if len(feature_slot.feature_columns) > 0:\n        # Iterate through feature columns to create TableConfig and FeatureConfig\n        for feature_column in feature_slot.feature_columns:\n          for feature_slice in feature_column.feature_slice_to_tf_placeholder:\n            if self.p.qr_multi_hashing and vocab_size > self.p.qr_hashing_threshold:\n              # creating quotient/remainder embedding table\n              logging.info('Setting QR table for slot {}'.format(slot_id))\n\n              R_vocab_size = vocab_size // self.p.qr_collision_rate + 1\n              Q_vocab_size = self.p.qr_collision_rate + 1\n\n              # remainder embedding table\n              table_name = \"table_{}_{}_0\".format(slot_id,\n                                                  feature_slice.slice_index)\n              if table_name not in self._table_to_config_dict:\n                Rtable = tpu_embedding.TableConfig(\n                    vocabulary_size=R_vocab_size,\n                    dimension=feature_slice.dim,\n                    initializer=feature_slice.initializer,\n                    combiner=\"sum\",\n                    learning_rate_fn=feature_slice.learning_rate_fn,\n                    optimization_parameters=feature_slice.optimizer)\n\n                self._table_to_config_dict[table_name] = Rtable\n              # remainder feature config\n              feature_name = \"{}_{}_0\".format(feature_column.fc_name,\n                                              feature_slice.slice_index)\n              self._feature_to_config_dict[\n                  feature_name] = tpu_embedding.FeatureConfig(table_name)\n\n              # quotient embedding table\n              table_name = \"table_{}_{}_1\".format(slot_id,\n                                                  feature_slice.slice_index)\n              if table_name not in self._table_to_config_dict:\n                Qtable = tpu_embedding.TableConfig(\n                    vocabulary_size=Q_vocab_size,\n                    dimension=feature_slice.dim,\n                    initializer=feature_slice.initializer,\n                    combiner=\"sum\",\n                    learning_rate_fn=feature_slice.learning_rate_fn,\n                    optimization_parameters=feature_slice.optimizer)\n                self._table_to_config_dict[table_name] = Qtable\n\n              # quotient feature config\n              feature_name = \"{}_{}_1\".format(feature_column.fc_name,\n                                              feature_slice.slice_index)\n              self._feature_to_config_dict[\n                  feature_name] = tpu_embedding.FeatureConfig(table_name)\n\n            table_name = \"table_{}_{}\".format(slot_id,\n                                              feature_slice.slice_index)\n            if table_name not in self._table_to_config_dict:\n              table = tpu_embedding.TableConfig(\n                  vocabulary_size=vocab_size,\n                  dimension=feature_slice.dim,\n                  initializer=feature_slice.initializer,\n                  combiner=\"sum\",\n                  learning_rate_fn=feature_slice.learning_rate_fn,\n                  optimization_parameters=feature_slice.optimizer)\n              self._table_to_config_dict[table_name] = table\n            feature_name = \"{}_{}\".format(feature_column.fc_name,\n                                          feature_slice.slice_index)\n            # Multiple feature configs can share the same table config\n            if isinstance(feature_column, FeatureColumn3D):\n              self._feature_to_config_dict[\n                  feature_name] = tpu_embedding.FeatureConfig(\n                      table_name,\n                      max_sequence_length=feature_column.max_seq_length)\n            else:\n              self._feature_to_config_dict[\n                  feature_name] = tpu_embedding.FeatureConfig(table_name)\n    return self._feature_to_config_dict, self._table_to_config_dict\n\n  def cross_shard_optimizer(self, optimizer, params):\n    if params[\"cpu_test\"]:\n      return optimizer\n    else:\n      return tf.compat.v1.tpu.CrossShardOptimizer(optimizer)\n\n  def process_features_for_cpu_test(self, features):\n    processed_features = {}\n    for feature_name, feature_value in features.items():\n      if isinstance(feature_value, tf.sparse.SparseTensor):\n        feature_config = self._feature_to_config_dict[feature_name]\n        table_config = self._table_to_config_dict[feature_config.table_id]\n\n        dim = table_config.dimension\n        max_sequence_length = feature_config.max_sequence_length\n        vocab_size = table_config.vocabulary_size\n\n        if feature_config.max_sequence_length == 0:\n          initvalue = (np.random.rand(vocab_size, dim) - 0.5) / (vocab_size *\n                                                                 dim)\n        else:\n          initvalue = (np.random.rand(vocab_size, max_sequence_length * dim) -\n                       0.5) / (vocab_size * max_sequence_length * dim)\n\n        initvalue = tf.cast(initvalue, tf.float32)\n        embedding_variable = tf.compat.v1.get_variable(name=feature_name,\n                                                       initializer=initvalue,\n                                                       dtype=tf.float32)\n\n        # Get new feature ids based on vocab_size. We mod\n        # by vocab_size to make sure new feature ids will be\n        # within vocab_size. This is only for test purpose.\n        new_feature_ids = tf.SparseTensor(indices=feature_value.indices,\n                                          values=tf.math.mod(\n                                              feature_value.values, vocab_size),\n                                          dense_shape=feature_value.dense_shape)\n\n        # Get embeddings.\n        embeddings = tf.nn.safe_embedding_lookup_sparse(embedding_variable,\n                                                        new_feature_ids,\n                                                        sparse_weights=None,\n                                                        combiner=\"sum\")\n\n        if max_sequence_length != 0:\n          embeddings = tf.reshape(embeddings, [-1, max_sequence_length, dim])\n\n        processed_features[feature_name] = embeddings\n      else:\n        processed_features[feature_name] = feature_value\n\n    # For CPU test, we will clear this state from now on. so later some host_call\n    # will not use do use them to do tpu specific operation.\n    self._feature_to_config_dict.clear()\n    self._table_to_config_dict.clear()\n\n    return processed_features\n"
  },
  {
    "path": "monolith/core/base_host_call.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport sys\n\nfrom absl import logging\nimport tensorflow as tf\n\n\nclass BaseHostCall(object):\n\n  def __init__(self, output_dir, enable_host_call):\n    self._output_dir = output_dir\n    self._enable_host_call = enable_host_call\n    self._tensor_names = [\"global_step\"]\n    gs = tf.compat.v1.train.get_global_step()\n    # Creating batch dimension since host call needs to concat all the cores'\n    # results.\n    self._tensors = [tf.reshape(gs, [-1])]\n    # compressed_tensor_list is a list of compressed tensors, in\n    # which each compressed tensor is the concatenation of multiple\n    # uncompressed tensors. To decompress, we need to store the original\n    # sizes of all uncompressed tensors, organized in list of lists.\n    # Each list corresponds to a compressed tensors.\n    self._lists_tensor_sizes = []  # A list of lists\n\n  def record_summary_tensor(self, name, tensor):\n    if not self._enable_host_call:\n      return\n\n    if name in self._tensor_names:\n      logging.info('{} | {}'.format(name, self._tensor_names))\n    assert name not in self._tensor_names\n    self._tensor_names.append(name)\n\n    assert len(tensor.get_shape()) <= 1, \"Now we only support tensor with shape (k, ) or ()\"\\\n        \"but we met tensor with shape: {}\".format(tensor.get_shape())\n\n    # Creating batch dimension since host call needs to concat all the cores'\n    # results.\n    reshaped_tensor = tf.reshape(tensor, [-1])\n    self._tensors.append(reshaped_tensor)\n\n  def compress_tensors(self):\n    \"\"\"For n tensors with shape (k_i, ) and same data type, concat them on axis=1.\n\n        After concatenation the compressed tensors is stored as in shape\n        (1, k_0 + k_1 + ... + k_{n-1}).\n        \"\"\"\n    assert len(self._tensor_names) == len(self._tensors), \"tensor_names and tensors must have same length,\" \\\n            \"tensor_names length: {}, tensors length: {}\".format(len(self._tensor_names), len(self._tensors))\n    # key is tensor data type, value is a list of tensor names with this data type.\n    data_type_to_tensor_names = {}\n    # key is tensor data type, value is tensor a list of tensors with this data type.\n    date_type_to_tensors = {}\n\n    # Group tensor names and tensors by same data type.\n    for tensor_name, tensor in zip(self._tensor_names, self._tensors):\n      data_type_to_tensor_names.setdefault(tensor.dtype, []).append(tensor_name)\n      date_type_to_tensors.setdefault(tensor.dtype, []).append(tensor)\n\n    # Compress n tensors tensor_0, tensor_1, ... , tensor_{n-1} with shape\n    # (k_0, ), (k_1, )... (k_{n-1}, ) of same data\n    # type to one tensor with shape (1, k_0 + ... + k_{n-1})\n    compressed_tensor_name_list = []\n    compressed_tensor_list = []\n    for data_type, tensor_list in date_type_to_tensors.items():\n      compressed_tensor_name_list.extend(data_type_to_tensor_names[data_type])\n\n      # concat a list of tensors with shapes\n      # (k_0, ), (k_1, )... (k_{n-1}, ) to a tensor with shape\n      # (k_0 + k_1 + ... + k_{n-1}, )\n      tensor_sizes = []\n      for tensor in tensor_list:\n        tensor_sizes.append(tensor.shape[0].value)\n      self._lists_tensor_sizes.append(tensor_sizes)\n      compressed_tensor = tf.concat(tensor_list, axis=0)\n\n      # expand dimension at 0 to make it have the batch dimension\n      # tensor with shape (k_0 + k_1 + ... + k_{n-1}, )\n      # => tensor with shape (1, k_0 + k_1 + ... + k_{n-1})\n      compressed_tensor = tf.expand_dims(compressed_tensor, axis=0)\n      compressed_tensor_list.append(compressed_tensor)\n\n      logging.info(\n          \"Host call compressed tensors, data type: {}, compressed tensor shape: {}\"\n          .format(data_type, compressed_tensor.shape))\n\n    self._tensor_names = compressed_tensor_name_list\n    self._tensors = compressed_tensor_list\n\n  def decompress_tensors(self, tensors):\n    \"\"\"\n        Decompress the compressed tensors into list of decompressed tensors.\n\n        Given a list of compressed tensors from *args. Each tensor has shape\n        (num_cores, k_0 + k_1 + ... + k_{n-1}), in which the second dimension\n        is the sum of lengths of uncompressed tensors (k_i, ) from the same\n        core, with same shape and data type. Parse and convert them to a list of\n        decompressed tensors.\n\n        Each decompressed tensor has shape (num_cores, k_i). Decompressed tensor\n        number must match with number of tensor names as well.\n        \"\"\"\n    # Need decompress tensors\n    decompressed_tensor_list = []\n    for index, compressed_tensor in enumerate(tensors):\n      # For each tensor, its shape is (num_cores, k_0 + ... + k_{n-1})\n      assert len(compressed_tensor.get_shape(\n      )) == 2, \"Compressed tensors shape must be (n, m), met shape: {}\".format(\n          compressed_tensor.shape)\n      logging.info(\"Decompressed tensors shape: {}, dtype: {}.\".format(\n          compressed_tensor.shape, compressed_tensor.dtype))\n\n      # tensor with shape (num_cores, k_0 + ... + k_{n-1})\n      # => list of tensors with shape (num_cores, k_i)\n      split_tensors = tf.split(compressed_tensor,\n                               self._lists_tensor_sizes[index],\n                               axis=1)\n\n      for tensor in split_tensors:\n        # Each decompressed tensor with shape (num_cores, k_i)\n        # => (num_cores * k_i, ).\n        tensor = tf.squeeze(tensor)\n        decompressed_tensor_list.append(tensor)\n\n    assert self._tensor_names[\n        0] == \"global_step\", \"The first tensor name must be global_step, met value: {}\".format(\n            self._tensor_names[0][0])\n    return decompressed_tensor_list[0][0], decompressed_tensor_list\n\n  def generate_host_call_hook(self):\n    # Children should implement this API and implement it with model specific host call logic.\n    return None\n"
  },
  {
    "path": "monolith/core/base_layer.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\"\"\"Base class for all layers.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport tensorflow as tf\nfrom monolith.core.hyperparams import InstantiableParams\nfrom monolith.core.py_utils import NestedMap\nfrom collections import defaultdict\n\n_layer_loss = defaultdict(dict)  # _layer_loss[graph][name]\n_name_inuse = defaultdict(int)\n\n\nclass BaseLayer(object):\n\n  @classmethod\n  def params(cls):\n    \"\"\"Returns the layer params.\"\"\"\n    p = InstantiableParams(cls)\n    p.define('name', get_uname(cls.__name__), 'Name of this layer object.')\n    return p\n\n  def __init__(self, params):\n    \"\"\"Layer constructor.\n        Args:\n            params: A params used to construct this layer.\n        \"\"\"\n    assert params.name, ('Layer params for %s must have a \"name\"' %\n                         self.__class__.__name__)\n\n    # Child layers created by this layer through CreateChild/CreateChildren.\n    self._private_children = NestedMap()\n\n  @property\n  def children(self):\n    \"\"\"Returns children layers of this layer in a `.NestedMap`.\"\"\"\n    return self._private_children\n\n  def __getattr__(self, name):\n    \"\"\"Returns the child layer of the given name.\"\"\"\n    if name == '_private_children':\n      raise AttributeError(\n          'pre-mature access to __getattr__ before _private_children '\n          'is created.')\n    if name in self._private_children:\n      return self._private_children[name]\n    elif (hasattr(type(self), name) and\n          isinstance(getattr(type(self), name), property)):\n      # There was an AttributeError raised by a property getter.\n      # Call property getter again directly to raise the same error.\n      return getattr(type(self), name).fget(self)\n    else:\n      raise AttributeError('%s is not a sub-layer of %s.' % (name, self))\n\n  def __call__(self, *args, **kwargs):\n    \"\"\"Forwards call to FProp.\"\"\"\n    return self.fprop(*args, **kwargs)\n\n  def fprop(self, *args, **kwargs):\n    \"\"\"Forward propagation.\n        The central interface that subclasses should implement. The caller\n        calls `FProp`.\n        Args:\n            *args: List args.\n            **kwargs: Keyward args.\n        \"\"\"\n    del args\n    del kwargs\n    raise NotImplementedError('Abstract method of %s' % self)\n\n  def create_child(self, name, params):\n    \"\"\"Create a sub layer.\n            The created sub layer can be accessed by `name`. E.g.::\n                self.create_child('foo', ...)\n                self.foo.fprop...\n        or::\n                self.children['foo'].fprop...\n                self.children.foo.fprop...\n        Args:\n            name: Sub layer name used as key to access it as attribute\n            params: `Hyperparams` object to instantiate a layer.\n        \"\"\"\n    # self._check_name(name)\n    if not params.name:\n      params.name = self.p.name\n    # p = copy_params_to(self.p, params.copy())\n    # params = copy_params_to(self.p, params.copy())\n    child = params.instantiate()\n    self._private_children[name] = child\n\n  def create_children(self, name, params):\n    \"\"\"Create a list or dict of sub layers.\n\n        The created sub layer list can be accessed by `name`. E.g.:\n            self.create_children('foo', ...)\n            self.foo[10].FProp...\n        or:\n            self.children['foo'][10].Fprop...\n            self.children.foo[10].Fprop...\n\n        Args:\n            name: The name for the sub layers, which is used as the key into\n                vars/theta.\n            params: a list of `Hyperparams` objects to create.\n        \"\"\"\n    self._private_children[name] = []\n    for index, param in enumerate(params):\n      if not param.name:\n        param.name = '%s_%d' % (name, index)\n      child = param.instantiate()\n      self._private_children[name].append(child)\n\n\ndef get_uname(name):\n  if name in _name_inuse:\n    _name_inuse[name] += 1\n    return \"{name}_{idx}\".format(name=name, idx=_name_inuse[name])\n  else:\n    return name\n\n\ndef add_layer_loss(name, loss):\n  graph_layer_loss = _layer_loss[tf.compat.v1.get_default_graph()]\n  if name in graph_layer_loss:\n    graph_layer_loss[name] += loss\n  else:\n    graph_layer_loss[name] = loss\n\n\ndef get_layer_loss():\n  return _layer_loss[tf.compat.v1.get_default_graph()]\n"
  },
  {
    "path": "monolith/core/base_layer_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom monolith.core import base_layer\nfrom monolith.core import hyperparams\nimport unittest\n\n\nclass BaseLayerTest(unittest.TestCase):\n\n  def test_create_child(self):\n    layer_p = base_layer.BaseLayer.params()\n    layer_p.name = 'test'\n    layer = layer_p.instantiate()\n    layer._disable_create_child = False  # pylint: disable=protected-access\n    layer.create_child(name='a', params=layer_p)\n    self.assertTrue('a' in layer.children)\n\n  def test_create_children(self):\n    layer_p = base_layer.BaseLayer.params()\n    layer_p.name = 'test'\n    layer = layer_p.instantiate()\n    layer._disable_create_child = False  # pylint: disable=protected-access\n    layer.create_children(name='a', params=[layer_p, layer_p])\n    self.assertTrue('a' in layer.children)\n    self.assertEqual(len(layer.a), 2)\n\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "monolith/core/base_model_params.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\n\nclass SingleTaskModelParams(object):\n  \"\"\"Model Params for a `.SingleTaskModel`.\"\"\"\n\n  def task(self):\n    \"\"\"Returns task params.\"\"\"\n    raise NotImplementedError('Abstract method')\n"
  },
  {
    "path": "monolith/core/base_task.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Base task.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom monolith.core import base_layer\nfrom monolith.core import hyperparams\n\n\nclass BaseTask(base_layer.BaseLayer):\n  \"\"\"A single training task.\"\"\"\n\n  @classmethod\n  def params(cls):\n    p = super(BaseTask, cls).params()\n\n    p.define('accelerator', None,\n             'Accelerator to use. One of [None, \"tpu\", \"horovod\"].')\n\n    p.define('input', hyperparams.Params(), 'Input Params.')\n    p.input.define('eval_examples', None,\n                   'Number of total examples for evaluation.')\n    p.input.define('train_examples', None,\n                   'Number of total examples for training.')\n\n    p.define('eval', hyperparams.Params(),\n             'Params to control how this task should be evaled.')\n    p.eval.define('per_replica_batch_size', None,\n                  'Number of per replica batch size')\n    p.eval.define('steps_per_eval', 10000,\n                  'Number of training steps between two evluations.')\n    p.eval.define('steps', None, 'Number of steps for which to eval model.')\n\n    p.define('train', hyperparams.Params(),\n             'Params to control how this task should be trained.')\n\n    p.train.define('steps', None, 'Number of steps for which to train model.')\n    p.train.define('max_steps', None,\n                   'Number of total steps for which to train model.')\n    p.train.define('per_replica_batch_size', None,\n                   'Number of per replica batch size')\n    p.train.define(\n        'file_pattern', None,\n        'Training input data. If file_pattern and file_folder are both' \\\n        ' provided, use file pattern firstly.')\n    p.train.define('repeat', False,\n                   'Whether repeat in the training job or not.')\n    p.train.define('label_key', 'label',\n                   'The key of the label field in the data.')\n    p.train.define(\n        'save_checkpoints_steps', None,\n        'Save checkpoint every save_checkpoints_steps. If None, overwrite by runner.'\n    )\n    p.train.define(\n        'save_checkpoints_secs', None,\n        'Save checkpoint every save_checkpoints_secs. If None, overwrite by runner.'\n    )\n    p.train.define('dense_only_save_checkpoints_secs', None,\n                   'Save dense checkpoint every save_checkpoints_secs')\n    p.train.define('dense_only_save_checkpoints_steps', None,\n                   'Save dense checkpoint every save_checkpoints_steps')\n    return p\n\n  def __init__(self, params):\n    \"\"\"Constructs a BaseTask object.\"\"\"\n    super(BaseTask, self).__init__(params)\n\n  def create_input_fn(self, mode):\n    \"\"\"Create input_fn given the mode.\n\n        Args:\n            mode: tf.estimator.ModeKeys.TRAIN/EVAL/PREDICT.\n        Returns:\n            An input fn for Estimator.\n        \"\"\"\n    raise NotImplementedError('Abstract method.')\n\n  def create_model_fn(self,):\n    \"\"\"Create model fn.\"\"\"\n    raise NotImplementedError('Abstract method.')\n"
  },
  {
    "path": "monolith/core/base_tpu_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.core import model_registry\nfrom monolith.tpu_runner import TPURunner\n\n\nclass BaseTPUTest(tf.test.TestCase):\n  \"\"\"Base class for tpu test.\"\"\"\n\n  def runWithCPU(self, task_name):\n    task_param = model_registry.GetParams(task_name)\n    runner = TPURunner(task_param)\n    runner._cpu_test = True\n    runner._host_call_every_n_steps = 0\n    runner.run()\n\n  def runMergeVectorTestOnCPU(self, task_name):\n    task_param = model_registry.GetParams(task_name)\n    task_param.merge_vector = True\n    runner = TPURunner(task_param)\n    runner._cpu_test = True\n    runner._host_call_every_n_steps = 0\n    runner.run()\n\n    env = runner._task._env\n    # Verify slot number should be same before and after merged vectors.\n    self.assertEqual(len(env._slot_to_dims.keys()),\n                     len(env._slot_to_merged_dims.keys()))\n\n    # Verify all slots are same, merged dims are expected.\n    for slot_id, original_dims in env._slot_to_dims.items():\n      #Verify slot_id are all the same before and after merged vecotrs.\n      self.assertIn(slot_id, env._slot_to_merged_dims)\n      merged_dims = env._slot_to_merged_dims[slot_id]\n\n      if original_dims[0] == 1:\n        # Veirfy bias dim is same.\n        self.assertEqual(merged_dims[0], 1)\n        # Verify merged vector dim is same.\n        if len(original_dims) > 1:\n          expect_merged_dim = sum(original_dims[1:])\n          self.assertEqual(len(merged_dims), 2)\n          self.assertEqual(expect_merged_dim, merged_dims[1])\n        else:\n          self.assertEqual(len(merged_dims), 1)\n      else:\n        # Verify merged vector dim is same.\n        expect_merged_dim = sum(original_dims)\n        self.assertEqual(len(merged_dims), 1)\n        self.assertEqual(expect_merged_dim, merged_dims[0])\n\n    # Verify all split features are as expected.\n    for name, embedding in env._tpu_features.items():\n      if \"slot_\" in name:\n        slot_id = int(name.split(\"_\")[1])\n        index = int(name.split(\"_\")[2])\n        expect_dim = env._slot_to_dims[slot_id][index]\n        actual_dim = embedding.get_shape().as_list()[1]\n        self.assertEqual(actual_dim, expect_dim)\n"
  },
  {
    "path": "monolith/core/core_test_suite.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport sys\nimport unittest\n\nfrom monolith.core.hyperparams_test import ParamsTest\nfrom monolith.core.base_layer_test import BaseLayerTest\nfrom monolith.core.base_embedding_host_call_test import BaseEmbeddingHostCallTest\nfrom monolith.core.util_test import UtilTest\n\n\ndef suite():\n  suite = unittest.TestSuite()\n  suite.addTest(unittest.makeSuite(ParamsTest))\n  suite.addTest(unittest.makeSuite(BaseLayerTest))\n  suite.addTest(unittest.makeSuite(BaseEmbeddingHostCallTest))\n  suite.addTest(unittest.makeSuite(UtilTest))\n  return suite\n\n\nif __name__ == '__main__':\n  runner = unittest.TextTestRunner(verbosity=2)\n  runner.run(suite())\n"
  },
  {
    "path": "monolith/core/dense.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"Code to implement custom Sail-like layer using TensorFlow Keras API.\"\n\nfrom __future__ import absolute_import, division, print_function\n\nimport functools\nimport sys\n\nimport numpy as np\nimport scipy.stats as stats\nimport tensorflow as tf\nfrom absl import logging\nfrom tensorflow.python.framework import dtypes, tensor_shape\nfrom tensorflow.python.keras import activations\nfrom tensorflow.python.keras import backend as K\nfrom tensorflow.python.keras import constraints, initializers, regularizers\nfrom tensorflow.python.keras.engine.input_spec import InputSpec\nfrom tensorflow.python.ops import gen_math_ops, math_ops, nn\n\nfrom monolith.core.base_layer import BaseLayer\nfrom monolith.core.variance_scaling import VarianceScaling\n\n\nclass Dense(tf.keras.layers.Dense, BaseLayer):\n\n  @classmethod\n  def params(cls):\n    p = super(Dense, cls).params()\n    p.define('units', 512, 'Positive integer, dimensionality of the ' \\\n        'output space.')\n    p.define('activation', None, 'Activation function to use.')\n    p.define('use_bias', True, 'Boolean, whether the layer uses a bias ' \\\n        'vector.')\n    p.define('kernel_initializer',\n        VarianceScaling(mode='fan_avg', distribution='uniform'),\n        'Initializer for the `kernel` weights matrix. Currently only '\\\n        'supporting variance scaling initializer.')\n    p.define('bias_initializer', 'zeros', 'Initializer for the bias vector.')\n    p.define('allow_kernel_norm', True,\n             'Boolean, kernel normalization is only applicable when TRAINING.')\n    p.define('kernel_norm_trainable', True,\n             'Boolean, whether a trainable weight norm variable is allocated')\n    p.define('partitioner', None,\n             'VariablePartitioner, if we will use partitioned variable')\n\n    return p\n\n  def __init__(self, params, **kwargs):\n    if 'input_shape' not in kwargs and 'input_dim' in kwargs:\n      kwargs['input_shape'] = (kwargs.pop('input_dim'),)\n    # Call the __init__() function for BaseLayer\n    BaseLayer.__init__(self, params)\n    # Call the _init__() function for tf.keras.layers.Dense\n    super(Dense, self).__init__(\n        units=params.units,\n        activation=params.activation,\n        use_bias=params.use_bias,\n        kernel_initializer=params.kernel_initializer,\n        bias_initializer=params.bias_initializer,\n        activity_regularizer=None,\n        kernel_constraint=None,\n        bias_constraint=None,\n        **kwargs,\n    )\n    # Change/Add some class properties to the tf.keras.layers.Dense\n    # properties. Note that this Dense layer does not support regularizers\n    # and constraints.\n    self.p = params\n    self.units = int(\n        params.units) if not isinstance(params.units, int) else params.units\n    self.activation = activations.get(params.activation)\n    self.use_bias = params.use_bias\n    self.kernel_initializer = params.kernel_initializer\n    self.bias_initializer = initializers.get(params.bias_initializer)\n\n    self.supports_masking = True\n    self.input_spec = InputSpec(min_ndim=2)\n\n    self.allow_kernel_norm = params.allow_kernel_norm\n    self.kernel_norm_trainable = params.kernel_norm_trainable\n    self.var_name_prefix = params.name\n    self.partitioner = params.partitioner\n\n  def build(self, input_shape):\n    dtype = tf.dtypes.as_dtype(self.dtype or K.floatx())\n    if not (dtype.is_floating or dtype.is_complex):\n      raise TypeError('Unable to build `Dense` layer with non-floating point '\n                      'dtype %s' % (dtype,))\n    input_shape = tensor_shape.TensorShape(input_shape)\n    if tensor_shape.dimension_value(input_shape[-1]) is None:\n      raise ValueError('The last dimension of the inputs to `Dense` '\n                       'should be defined. Found `None`.')\n    last_dim = tensor_shape.dimension_value(input_shape[-1])\n    self.input_spec = InputSpec(min_ndim=2, axes={-1: last_dim})\n\n    kernel_shape = [last_dim, self.units]\n    init_kernel = self.kernel_initializer(shape=kernel_shape, dtype=self.dtype)\n    if self.partitioner is None:\n      kernel_initializer = lambda shape, dtype: init_kernel\n    else:\n      kernel_initializer = init_kernel\n\n    self.kernel = tf.compat.v1.get_variable(initializer=kernel_initializer,\n                                            trainable=True,\n                                            name=\"{}/kernel\".format(\n                                                self.var_name_prefix),\n                                            shape=kernel_shape,\n                                            dtype=dtype,\n                                            partitioner=self.partitioner)\n\n    # Add the option for allow_kernel_norm\n    if self.allow_kernel_norm:\n      self.kernel = tf.nn.l2_normalize(self.kernel,\n                                       axis=0,\n                                       epsilon=1e-6,\n                                       name='normalized_kernel')\n      if self.kernel_norm_trainable:\n        # Use np to mitigate the error thrown by tensorflow due to the variable\n        # initializer inside a conditional.\n        init_trainable_kernel_norm = np.linalg.norm(\n            init_kernel,\n            axis=0,\n        )\n        if self.partitioner is None:\n          norm_initializer = lambda shape, dtype: init_trainable_kernel_norm\n        else:\n          norm_initializer = init_trainable_kernel_norm\n        self.trainable_kernel_norm = tf.compat.v1.get_variable(\n            initializer=norm_initializer,\n            shape=init_trainable_kernel_norm.shape,\n            trainable=True,\n            name='{}/trainable_kernel_norm'.format(self.var_name_prefix),\n            dtype=dtype,\n            partitioner=self.partitioner)\n        self.kernel = tf.multiply(self.kernel,\n                                  self.trainable_kernel_norm,\n                                  name='mul_of_kernel_and_trainable_norm')\n\n    if self.use_bias:\n      self.bias = self.add_weight(name='{}/bias'.format(self.var_name_prefix),\n                                  shape=[\n                                      self.units,\n                                  ],\n                                  initializer=self.bias_initializer,\n                                  dtype=dtype,\n                                  trainable=True)\n    else:\n      self.bias = None\n    self.built = True\n\n  def get_config(self):\n    config = {\n        'units': self.units,\n        'activation': activations.serialize(self.activation),\n        'use_bias': self.use_bias,\n        'kernel_initializer': initializers.serialize(self.kernel_initializer),\n        'bias_initializer': initializers.serialize(self.bias_initializer),\n        'allow_kernel_norm': self.allow_kernel_norm,\n        'kernel_norm_trainable': self.kernel_norm_trainable,\n        'partitioner': self.partitioner,\n    }\n    base_config = super(Dense, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n  def fprop(self, inputs, **kwargs):\n    return self.call(inputs)\n"
  },
  {
    "path": "monolith/core/dense_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Tests for the Dense layer.\"\"\"\n\nfrom __future__ import absolute_import, division, print_function\n\nimport textwrap\n\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.python import keras\nfrom tensorflow.python.eager import context\nfrom tensorflow.python.framework import dtypes, ops\nfrom tensorflow.python.keras import backend\n\nfrom monolith.core import testing_utils\nfrom monolith.core.dense import Dense\n\n\nclass DenseTest(tf.test.TestCase):\n\n  def test_dense_instantiate(self):\n    dense_layer_template = Dense.params()\n\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.units = 3\n    testing_utils.layer_test(Dense,\n                             kwargs={'params': test_params0},\n                             input_shape=(3, 2))\n\n    test_params1 = dense_layer_template.copy()\n    test_params1.name = 'test_dense1'\n    test_params1.units = 3\n    testing_utils.layer_test(Dense,\n                             kwargs={'params': test_params1},\n                             input_shape=(3, 4, 2))\n\n    test_params2 = dense_layer_template.copy()\n    test_params2.name = 'test_dense2'\n    test_params2.units = 3\n    testing_utils.layer_test(Dense,\n                             kwargs={'params': test_params2},\n                             input_shape=(None, None, 2))\n\n    test_params3 = dense_layer_template.copy()\n    test_params3.name = 'test_dense3'\n    test_params3.units = 3\n    testing_utils.layer_test(Dense,\n                             kwargs={'params': test_params3},\n                             input_shape=(3, 4, 5, 2))\n\n  def test_dense_dtype(self):\n    dense_layer_template = Dense.params()\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.units = 3\n\n    inputs = ops.convert_to_tensor_v2(\n        np.random.randint(low=0, high=7, size=(2, 2)))\n    layer = Dense(test_params0, dtype='float32')\n    outputs = layer(inputs)\n    self.assertEqual(outputs.dtype, 'float32')\n\n  def test_dense(self):\n    dense_layer_template = Dense.params()\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.units = 3\n\n    layer = Dense(test_params0)\n\n    output = layer(keras.backend.variable(np.ones((2, 4))))\n    self.assertAllEqual((2, 3), output.shape)\n\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(output)\n\n  def test_dense_with_partitioner(self):\n    param = Dense.params()\n    param.name = \"test_dense_with_partitioner\"\n    param.units = 5\n    param.partitioner = tf.compat.v1.variable_axis_size_partitioner(1024)\n    layer = Dense(param)\n    output = layer(keras.backend.variable(np.ones((2, 4096))))\n    self.assertAllEqual((2, 5), output.shape)\n\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(output)\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/core/feature.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Sail like API.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import logging\nimport tensorflow as tf\nfrom collections import namedtuple\n\n\nclass FeatureSlice(object):\n  \"\"\"Sail-like FeatureSlice implementation.\"\"\"\n\n  def __init__(\n      self,\n      feature_slot,\n      dim,\n      slice_index,\n      optimizer=None,\n      initializer=None,\n      learning_rate_fn=None,\n  ):\n    \"\"\"Initialize a FeatureSlice object.\n\n    Args:\n      feature_slot: A FeatureSlot object that this FeatureSlice object belongs\n        to.\n      dim: The dim of the FeatureSlice.\n      slice_index: The index of this FeatureSlice in the FeatureSlot list of\n        FeatureSlice.\n      optimizer: TensorFlow optimization parameters (e.g.,\n        tf.compat.v1.tpu.experimental.FtrlParameters).\n      initializer: TensorFlow initializer (e.g., tf.random_uniform_initializer).\n      learning_rate_fn: A function that changes the learning rate over the\n        training period (e.g., tf.compat.v1.train.polynomial_decay).\n    \"\"\"\n    self._feature_slot = feature_slot\n    self._dim = dim\n    self._slice_index = slice_index\n    self._optimizer = optimizer\n    self._initializer = initializer\n    self._learning_rate_fn = learning_rate_fn\n\n  def __repr__(self):\n    \"\"\"The default __repr__() method is overwritten to enable FeatureSlice as dict key.\"\"\"\n    return '[FeatureSlice][slot_{}][{}]'.format(self._feature_slot.slot_id(),\n                                                self._slice_index)\n\n  def __hash__(self):\n    \"\"\"The default __hash__() method is overwritten to enable FeatureSlice as dict key.\"\"\"\n    return hash((self._feature_slot.slot_id(), self._slice_index))\n\n  @property\n  def dim(self):\n    return self._dim\n\n  @property\n  def slice_index(self):\n    return self._slice_index\n\n  @property\n  def optimizer(self):\n    return self._optimizer\n\n  @property\n  def initializer(self):\n    return self._initializer\n\n  @property\n  def learning_rate_fn(self):\n    return self._learning_rate_fn\n\n\nclass FeatureSlot(object):\n  \"\"\"Sail like FeatureSlot implementation.\"\"\"\n\n  def __init__(\n      self,\n      env,\n      slot_id,\n      has_bias=False,\n      bias_optimizer=tf.compat.v1.tpu.experimental.FtrlParameters(\n          learning_rate=0.01),\n      bias_initializer=tf.zeros_initializer(),\n      bias_learning_rate_fn=None,\n      default_vec_optimizer=tf.compat.v1.tpu.experimental.AdagradParameters(\n          learning_rate=0.01),\n      default_vec_initializer=tf.random_uniform_initializer(minval=-0.001,\n                                                            maxval=0.001),\n      default_vec_learning_rate_fn=None,\n      occurrence_threshold=None,\n      expire_time=None,\n  ):\n    \"\"\"Initialize a FeatureSlot object.\n\n    Args:\n      env: An Env object that this FeatureSlot belongs to.\n      slot_id: The slot_id of the FeatureSlot.\n      has_bias: A Boolean on whether this FeatureSlot has a bias FeatureSlice.\n      bias_optimizer: TensorFlow optimization parameters for bias slice (e.g.,\n        tf.compat.v1.tpu.experimental.FtrlParameters).\n      bias_initializer: TensorFlow initializer for bias slice (e.g.,\n        tf.random_uniform_initializer).\n      bias_learning_rate_fn: A function that changes the learning rate of the\n        bias over the training period (e.g.,\n        tf.compat.v1.train.polynomial_decay).\n      default_vec_optimizer: The default TensorFlow optimization parameters for\n        vector slices.\n      default_vec_initializer: The default TensorFlow initializer for vector\n        slices.\n      default_vec_learning_rate_fn: The default TensorFlow learning rate\n        function for vector slices.\n      occurrence_threshold: The number of occurrences that an FID of the slot_id\n        must occur in order to be recorded into the training data.\n      expire_time: FID may be evicted from the model if not been updated\n        for expire_time days.\n    \"\"\"\n    self._env = env\n    self._slot_id = slot_id\n    self._has_bias = has_bias\n    self._bias_optimizer = bias_optimizer\n    self._bias_initializer = bias_initializer\n    self._bias_learning_rate_fn = bias_learning_rate_fn\n    self._default_vec_optimizer = default_vec_optimizer\n    self._default_vec_initializer = default_vec_initializer\n    self._default_vec_learning_rate_fn = default_vec_learning_rate_fn\n    self._occurrence_threshold = occurrence_threshold\n    self._expire_time = expire_time\n    self._feature_slices = []\n    self._merged_feature_slices = []\n    self._feature_columns = []\n    self._env.set_feature_slot(slot_id, self)\n\n    if self._has_bias:\n      feature_slice = FeatureSlice(\n          feature_slot=self,\n          dim=1,\n          slice_index=0,\n          optimizer=self._bias_optimizer,\n          initializer=self._bias_initializer,\n          learning_rate_fn=self._bias_learning_rate_fn,\n      )\n      self._feature_slices.append(feature_slice)\n\n  def get_env(self):\n    return self._env\n\n  def slot_id(self):\n    return self._slot_id\n\n  def has_bias(self):\n    return self._has_bias\n\n  def add_feature_slice(\n      self,\n      dim,\n      optimizer=None,\n      initializer=None,\n      learning_rate_fn=None,\n  ):\n    \"\"\"Create a FeatureSlice and add to _feature_slices.\"\"\"\n    optimizer = optimizer if optimizer is not None else self._default_vec_optimizer\n    initializer = initializer if initializer is not None else self._default_vec_initializer\n    learning_rate_fn = learning_rate_fn if learning_rate_fn is not None else self._default_vec_learning_rate_fn\n    feature_slice = FeatureSlice(\n        feature_slot=self,\n        dim=dim,\n        slice_index=len(self._feature_slices),\n        optimizer=optimizer,\n        initializer=initializer,\n        learning_rate_fn=learning_rate_fn,\n    )\n    self._feature_slices.append(feature_slice)\n\n    return feature_slice\n\n  def add_merged_feature_slice(\n      self,\n      dim,\n      optimizer=None,\n      initializer=None,\n      learning_rate_fn=None,\n  ):\n    \"\"\"Create a FeatureSlice for merged embedding and add to _merged_feature_slices.\"\"\"\n    optimizer = optimizer if optimizer is not None else self._default_vec_optimizer\n    initializer = initializer if initializer is not None else self._default_vec_initializer\n    learning_rate_fn = learning_rate_fn if learning_rate_fn is not None else self._default_vec_learning_rate_fn\n    feature_slice = FeatureSlice(\n        feature_slot=self,\n        dim=dim,\n        slice_index=len(self._merged_feature_slices),\n        optimizer=optimizer,\n        initializer=initializer,\n        learning_rate_fn=learning_rate_fn,\n    )\n    self._merged_feature_slices.append(feature_slice)\n\n    return feature_slice\n\n  def _add_feature_column(self, feature_column):\n    \"\"\"Add a FeatureColumn object to the FeatureSlot.\"\"\"\n    self._feature_columns.append(feature_column)\n    # If the FeatureSlot has bias, add the bias FeatureSlice to all\n    # FeatureColumn objects\n    if self._has_bias:\n      for feature_column in self._feature_columns:\n        feature_column._bias = feature_column.embedding_lookup(\n            self._feature_slices[0])\n\n  @property\n  def bias_optimizer(self):\n    return self._bias_optimizer\n\n  @property\n  def bias_initializer(self):\n    return self._bias_initializer\n\n  @property\n  def bias_learning_rate_fn(self):\n    return self._bias_learning_rate_fn\n\n  @property\n  def default_vec_optimizer(self):\n    return self._default_vec_optimizer\n\n  @property\n  def default_vec_initializer(self):\n    return self._default_vec_initializer\n\n  @property\n  def default_vec_learning_rate_fn(self):\n    return self._default_vec_learning_rate_fn\n\n  @property\n  def feature_slices(self):\n    return self._feature_slices\n\n  @property\n  def merged_feature_slices(self):\n    return self._merged_feature_slices\n\n  @property\n  def feature_columns(self):\n    return self._feature_columns\n\n\nclass FeatureColumnV1(object):\n  \"\"\"Sail like class FeatureColumnV1 implementation.\"\"\"\n\n  def __init__(self, feature_slot, fc_name):\n    \"\"\"Initialize a FeatureSlot object.\n\n    Args:\n      feature_slot: A FeatureSlot object that this FeatureColumnV1 belongs to.\n      fc_name: The name of the feature column (e.g, \"slot_1_0\").\n    \"\"\"\n    self._feature_slot = feature_slot\n    self._fc_name = fc_name\n    self._feature_slice_to_tf_placeholder = {}\n    self._merged_feature_slice_to_tf_placeholder = {}\n    self._bias = None\n    self._feature_slot._add_feature_column(self)\n\n  def get_env(self):\n    return self._feature_slot.get_env()\n\n  def embedding_lookup(\n      self,\n      feature_slice,\n      init_minval_for_oov=None,\n      init_maxval_for_oov=None,\n  ):\n    return self.get_env()._embedding_lookup(self, feature_slice,\n                                            init_minval_for_oov,\n                                            init_maxval_for_oov)\n\n  def get_bias(self):\n    assert self._bias is not None\n    return self._bias\n\n  @property\n  def feature_slot(self):\n    return self._feature_slot\n\n  @property\n  def fc_name(self):\n    return self._fc_name\n\n  @property\n  def feature_slice_to_tf_placeholder(self):\n    env = self.get_env()\n    assert env.is_finalized, \"is_finalized must be true which means \\\n      _feature_slice_to_tf_placeholder must be initialized before using this \\\n      _feature_slice_to_tf_placeholder\"\n\n    if env._merge_vector:\n      return self._merged_feature_slice_to_tf_placeholder\n    else:\n      return self._feature_slice_to_tf_placeholder\n\n\nclass FeatureColumn3D(object):\n  \"\"\"Sail like class FeatureColumn3D implementation.\"\"\"\n\n  def __init__(self, feature_slot, max_seq_length, fc_name):\n    self._feature_slot = feature_slot\n    self._fc_name = fc_name\n    self._feature_slice_to_tf_placeholder = {}\n    self._bias = None\n    logging.info(\"max_seq_length {}\".format(max_seq_length))\n    self._max_seq_length = max_seq_length\n    self._feature_slot._add_feature_column(self)\n\n  def get_env(self):\n    return self._feature_slot.get_env()\n\n  def embedding_lookup(\n      self,\n      feature_slice,\n      max_seq_length,\n      init_minval_for_oov=None,\n      init_maxval_for_oov=None,\n  ):\n    return self.get_env()._seq_embedding_lookup(self, feature_slice,\n                                                self._max_seq_length,\n                                                init_minval_for_oov,\n                                                init_maxval_for_oov)\n\n  def get_bias(self):\n    assert self._bias is not None\n    return self._bias\n\n  @property\n  def feature_slot(self):\n    return self._feature_slot\n\n  @property\n  def fc_name(self):\n    return self._fc_name\n\n  @property\n  def feature_slice_to_tf_placeholder(self):\n    return self._feature_slice_to_tf_placeholder\n\n  @property\n  def max_seq_length(self):\n    return self._max_seq_length\n\n  def size_tensor_lookup(self):\n    \"\"\"Name with '_size' as the suffix of ${fc_name}\"\"\"\n    return self.get_env()._size_tensor_lookup(self)\n\n\nclass Env(object):\n  \"\"\"Environment which holds important information and track the embedding tables.\"\"\"\n\n  def __init__(self, vocab_size_dict, params):\n    self._vocab_size_dict = vocab_size_dict\n    self._slot_id_to_feature_slot = {}  # {1: FeatureSlot}\n    self._tpu_features = None\n    self._is_finalized = False\n\n    self.set_params(params)\n\n  def set_tpu_features(self, tpu_features):\n    self._tpu_features = tpu_features\n\n    if self._merge_vector:\n      for slot_id, feature_slot in self._slot_id_to_feature_slot.items():\n        # Split the merged embeddings\n        self._split_merged_embedding(feature_slot)\n\n  def set_feature_slot(self, slot_id, feature_slot):\n    # Set feature slot only be called during the first round of calling logits in which env is not finalized yet.\n    if self._is_finalized == True:\n      return\n    assert slot_id not in self._slot_id_to_feature_slot, \"Feature slot with id: {} can not be created more than once.\".format(\n        slot_id)\n    self._slot_id_to_feature_slot[slot_id] = feature_slot\n\n  def set_params(self, params):\n    self._QR_multi_hashing = params.qr_multi_hashing\n    self._QR_hashing_threshold = params.qr_hashing_threshold\n    self._QR_collision_rate = params.qr_collision_rate\n    self._use_random_init_embedding_for_oov = params.use_random_init_embedding_for_oov\n    self._merge_vector = params.merge_vector\n\n  def is_finalized(self):\n    return self.is_finalized\n\n  def _embedding_lookup(self,\n                        feature_column,\n                        feature_slice,\n                        init_minval_for_oov=None,\n                        init_maxval_for_oov=None):\n    assert feature_column._feature_slot.slot_id(\n    ) == feature_slice._feature_slot.slot_id()\n\n    if self._tpu_features:\n      logging.vlog(2, \"__embedding_loopup with features exist.\")\n\n      if self._QR_multi_hashing and self._vocab_size_dict[\n          slot_id] > self._QR_hashing_threshold:\n        logging.vlog(\n            2, \"__embedding_lookup of QR hashing for slot {}.\".format(slot_id))\n        # taking quotient feature and remainder feature\n        keyR = \"{}_{}_0\".format(feature_column.fc_name,\n                                feature_slice.slice_index)\n        keyQ = \"{}_{}_1\".format(feature_column.fc_name,\n                                feature_slice.slice_index)\n        assert keyR in self._tpu_features and keyQ in self._tpu_features, \\\n            \"keyR: {} or keyQ: {} not in tpu features, probably need to check core.base_embedding_task._post_process_example()\".format(keyR, keyQ)\n\n        # combining quotient feature and remainder feature\n        # element-wise addition performs better than element-wise multiplication\n        return self._tpu_features[keyR] + self._tpu_features[keyQ]\n      else:\n        key = \"{}_{}\".format(feature_column.fc_name, feature_slice.slice_index)\n        if not self._use_random_init_embedding_for_oov or init_minval_for_oov is None:\n          return self._tpu_features[key]\n        norm = tf.norm(self._tpu_features[key], axis=1)\n        random = tf.random.uniform(\n            tf.shape(self._tpu_features[key]),\n            minval=init_minval_for_oov,\n            maxval=init_maxval_for_oov,\n        )\n        cond = tf.expand_dims(tf.less(norm, 1e-10), -1)\n        return tf.where(cond, random, self._tpu_features[key])\n    else:\n      logging.vlog(2, \"__embedding_lookup with no features exist.\")\n      if feature_slice not in feature_column._feature_slice_to_tf_placeholder:\n        feature_column._feature_slice_to_tf_placeholder[\n            feature_slice] = tf.compat.v1.placeholder(tf.float32,\n                                                      [None, feature_slice.dim])\n\n      return feature_column._feature_slice_to_tf_placeholder[feature_slice]\n\n  def _seq_embedding_lookup(self,\n                            feature_column,\n                            feature_slice,\n                            max_seq_length,\n                            init_minval_for_oov=None,\n                            init_maxval_for_oov=None):\n    assert feature_column._feature_slot.slot_id(\n    ) == feature_slice._feature_slot.slot_id()\n\n    if self._tpu_features:\n      logging.vlog(2, \"__embedding_loopup with features exist.\")\n      key = \"{}_{}\".format(feature_column.fc_name, feature_slice.slice_index)\n      if not self._use_random_init_embedding_for_oov or init_minval_for_oov is None:\n        return self._tpu_features[key]\n      norm = tf.norm(self._tpu_features[key], axis=1)\n      random = tf.random.uniform(\n          tf.shape(self._tpu_features[key]),\n          minval=feature_slice.init_minval_for_oov,\n          maxval=feature_slice.init_maxval_for_oov,\n      )\n      cond = tf.expand_dims(tf.less(norm, 1e-10), -1)\n      return tf.where(cond, random, self._tpu_features[key])\n    else:\n      logging.vlog(2, \"__embedding_lookup with no features exist.\")\n      if feature_slice not in feature_column._feature_slice_to_tf_placeholder:\n        feature_column._feature_slice_to_tf_placeholder[\n            feature_slice] = tf.compat.v1.placeholder(\n                tf.float32, [None, max_seq_length, feature_slice.dim])\n\n      return feature_column._feature_slice_to_tf_placeholder[feature_slice]\n\n  def _size_tensor_lookup(self, feature_column):\n    if self._tpu_features:\n      key = \"{}_0_row_lengths\".format(feature_column.fc_name)\n      row_lengths = self._tpu_features[key]\n      # Convert row_lengths to [B, max_seq_length] Tensor, in which\n      # the first row_length elements of each row are 1, and the rest are\n      # 0. This is used as the size_tensor\n      batch_size = tf.size(row_lengths)  # 0-D Tensor\n      boolean_mask = tf.less(\n          tf.reshape(\n              tf.tile(tf.range(0, feature_column.max_seq_length), [batch_size]),\n              [batch_size, -1],\n          ), tf.expand_dims(row_lengths, 1))  # [B, max_seq_length] Tensor\n      return tf.cast(boolean_mask, tf.int32)\n    else:\n      return tf.compat.v1.placeholder(\n          tf.float32,\n          [None, feature_column.max_seq_length],\n          '{}_size'.format(feature_column.fc_name),\n      )\n\n  def finalize(self):\n    \"\"\"Finalize the env after slot to dims dict has been initialized.\"\"\"\n    assert self._is_finalized == False, \"Env can't be finalized more than once\"\n    self._is_finalized = True\n\n    if self._merge_vector:\n      self._merge_vector_in_same_slot()\n\n  def _split_merged_embedding(self, feature_slot):\n    \"\"\"Split merged embedding into embedding splits of corresponding dim.\n\n    Currently, this assumes all vector FeatureSlice embeddings are shared among\n    all the FeatureColumns, so all vector FeatureSlice embeddings can be merged\n    into one embedding.\n    \"\"\"\n    # Iterate through feature columns\n    for feature_column in feature_slot.feature_columns:\n      merged_embedding = None\n      for merged_feature_slice in feature_column._merged_feature_slice_to_tf_placeholder:\n        if merged_feature_slice.slice_index == 0 and feature_slot.has_bias():\n          # For bias, keep it as bias will not be merged. Nothing to do.\n          assert merged_feature_slice.dim == 1, \"Bias in {} must have dim equal to 1, but actual dim is {}.\".format(\n              feature_column.fc_name, merged_feature_slice.dim)\n        else:\n          merged_feature_name = \"{}_{}\".format(feature_column.fc_name,\n                                               merged_feature_slice.slice_index)\n          merged_embedding = self._tpu_features[merged_feature_name]\n          # del self._tpu_features[merged_feature_name]\n\n      if merged_embedding is not None:\n        # Split embeddings will be written to the position starting from the previous merged embedding position.\n        dim_splits = [\n            feature_slice.dim for feature_slice in feature_slot.feature_slices\n        ]\n        if feature_slot.has_bias():\n          dim_splits = [\n              feature_slice.dim for feature_slice in feature_slot.feature_slices\n          ][1:]\n        embedding_splits = tf.split(merged_embedding, dim_splits, axis=1)\n\n      for feature_slice in feature_column._feature_slice_to_tf_placeholder:\n        if feature_slice.slice_index == 0 and feature_slot.has_bias():\n          # For bias, keep it as bias will not be merged. Nothing to do.\n          assert feature_slice.dim == 1, \"Bias in {} must have dim equal to 1, but actual dim is {}.\".format(\n              feature_column.fc_name, feature_slice.dim)\n        else:\n          if merged_embedding is not None:\n            if feature_slot.has_bias():\n              split_index = feature_slice.slice_index - 1\n            else:\n              split_index = feature_slice.slice_index\n            split = embedding_splits[split_index]\n            self._tpu_features[\"{}_{}\".format(\n                feature_column.fc_name, feature_slice.slice_index)] = split\n\n  def _merge_vector_in_same_slot(self):\n    \"\"\"Merge vectors in the same slot.\n\n    Currently, this assumes all vector FeatureSlice embeddings are shared among\n    all the FeatureColumns, so all vector FeatureSlice embeddings can be merged\n    into one embedding.\n    \"\"\"\n    # TODO (long): Support the case where only a subset of FeatureSlices are\n    # shared, so some vector FeatureSlices cannot be merged\n    for slot_id, feature_slot in self._slot_id_to_feature_slot.items():\n      merged_vector_dim = 0\n      # Iterate through all FeatureSlices in FeatureSlot to calculate the merged\n      # embedding dimension\n      for feature_slice in feature_slot.feature_slices:\n        # Bias will not be merged\n        if feature_slot.has_bias() and feature_slice.slice_index == 0:\n          assert feature_slice.dim == 1, \"Bias in {} must have dim equal to 1, but actual dim is {}.\".format(\n              slot_id, feature_slice.dim)\n          feature_slot._merged_feature_slices.append(feature_slice)\n          for feature_column in feature_slot.feature_columns:\n            feature_column._merged_feature_slice_to_tf_placeholder[\n                feature_slice] = feature_column._feature_slice_to_tf_placeholder[\n                    feature_slice]\n        else:\n          merged_vector_dim += feature_slice.dim\n      # Created a merged slice whose dim is the sum of the dims of all vector\n      # FeatureSlices\n      if merged_vector_dim > 0:\n        # Create the merged FeatureSlice with the merged embedding dimension\n        merged_feature_slice = feature_slot.add_merged_feature_slice(\n            merged_vector_dim)\n        # Add the merged FeatureSlice and its corresponding tf.placeholder to\n        # each FeatureColumn in the FeatureSlot\n        for feature_column in feature_slot.feature_columns:\n          feature_column._merged_feature_slice_to_tf_placeholder[\n              merged_feature_slice] = tf.compat.v1.placeholder(\n                  tf.float32, [None, merged_feature_slice.dim])\n\n  @property\n  def vocab_size_dict(self):\n    return self._vocab_size_dict\n\n  @property\n  def slot_id_to_feature_slot(self):\n    assert self.is_finalized, \"is_finalized must be true which means _slot_id_to_feature_slot \\\n      must be initialized before using this _slot_id_to_feature_slot\"\n\n    return self._slot_id_to_feature_slot\n\n  @property\n  def features(self):\n    return self._tpu_features\n"
  },
  {
    "path": "monolith/core/feature_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nimport monolith.core.hyperparams as _params\nfrom monolith.core.feature import FeatureSlot, FeatureColumnV1, Env\n\n\nclass FeatureSlotTest(tf.test.TestCase):\n\n  def test_has_bias(self):\n    params = _params.Params()\n    params.define('qr_multi_hashing', False, '')\n    params.define('qr_hashing_threshold', 100000000, '')\n    params.define('qr_collision_rate', 4, '')\n    params.define('use_random_init_embedding_for_oov', False, '')\n    params.define('merge_vector', False, '')\n    env = Env({}, params)\n    fs_1 = FeatureSlot(env=env, slot_id=1, has_bias=True)\n\n    self.assertEqual(len(fs_1.feature_slices), 1)\n    self.assertEqual(fs_1.feature_slices[0].dim, 1)\n    self.assertEqual(fs_1.feature_slices[0].slice_index, 0)\n\n  def test_add_feature_slice(self):\n    params = _params.Params()\n    params.define('qr_multi_hashing', False, '')\n    params.define('qr_hashing_threshold', 100000000, '')\n    params.define('qr_collision_rate', 4, '')\n    params.define('use_random_init_embedding_for_oov', False, '')\n    params.define('merge_vector', False, '')\n    env = Env({}, params)\n    fs_1 = FeatureSlot(env=env, slot_id=1, has_bias=True)\n\n    fs_1.add_feature_slice(dim=10)\n\n    self.assertEqual(len(fs_1.feature_slices), 2)\n    self.assertEqual(fs_1.feature_slices[0].dim, 1)\n    self.assertEqual(fs_1.feature_slices[0].slice_index, 0)\n    self.assertEqual(fs_1.feature_slices[1].dim, 10)\n    self.assertEqual(fs_1.feature_slices[1].slice_index, 1)\n\n\nclass FeatureColumnV1Test(tf.test.TestCase):\n\n  def test_add_feature_column(self):\n    params = _params.Params()\n    params.define('qr_multi_hashing', False, '')\n    params.define('qr_hashing_threshold', 100000000, '')\n    params.define('qr_collision_rate', 4, '')\n    params.define('use_random_init_embedding_for_oov', False, '')\n    params.define('merge_vector', False, '')\n\n    env = Env({}, params)\n    fs_1 = FeatureSlot(env=env, slot_id=1, has_bias=True)\n    fs_1.add_feature_slice(dim=10)\n    fc_1 = FeatureColumnV1(fs_1, 'fc_name_1')\n\n    self.assertEqual(len(fs_1._feature_columns), 1)\n\n  def test_merge_split_vector_in_same_slot(self):\n    params = _params.Params()\n    params.define('qr_multi_hashing', False, '')\n    params.define('qr_hashing_threshold', 100000000, '')\n    params.define('qr_collision_rate', 4, '')\n    params.define('use_random_init_embedding_for_oov', False, '')\n    params.define('merge_vector', True, '')\n\n    env = Env({}, params)\n\n    # Test merge logic.\n    fs_1 = FeatureSlot(env=env, slot_id=1, has_bias=True)\n    slice_1_1 = fs_1.add_feature_slice(dim=2)\n    fs_2 = FeatureSlot(env=env, slot_id=2, has_bias=True)\n    fs_3 = FeatureSlot(env=env, slot_id=3, has_bias=False)\n    slice_3_0 = fs_3.add_feature_slice(dim=2)\n    slice_3_1 = fs_3.add_feature_slice(dim=3)\n    fs_4 = FeatureSlot(env=env, slot_id=4, has_bias=True)\n    slice_4_1 = fs_4.add_feature_slice(dim=2)\n    slice_4_2 = fs_4.add_feature_slice(dim=3)\n    slice_4_3 = fs_4.add_feature_slice(dim=4)\n\n    fc_1 = FeatureColumnV1(fs_1, 'fc_name_1')\n    fc_1.embedding_lookup(slice_1_1)\n    fc_2 = FeatureColumnV1(fs_2, 'fc_name_2')\n    fc_3 = FeatureColumnV1(fs_3, 'fc_name_3')\n    fc_3.embedding_lookup(slice_3_0)\n    fc_3.embedding_lookup(slice_3_1)\n    fc_4 = FeatureColumnV1(fs_4, 'fc_name_4')\n    fc_4.embedding_lookup(slice_4_1)\n    fc_4.embedding_lookup(slice_4_2)\n    fc_4.embedding_lookup(slice_4_3)\n    fc_5 = FeatureColumnV1(fs_4, 'fc_name_5')\n    fc_5.embedding_lookup(slice_4_1)\n    fc_5.embedding_lookup(slice_4_2)\n    fc_5.embedding_lookup(slice_4_3)\n\n    env._merge_vector_in_same_slot()\n    # Check the length of merged feature slices in FeatureSlot\n    self.assertEqual(len(fs_1._merged_feature_slices), 2)\n    self.assertEqual(len(fs_2._merged_feature_slices), 1)\n    self.assertEqual(len(fs_3._merged_feature_slices), 1)\n    self.assertEqual(len(fs_4._merged_feature_slices), 2)\n    # Check the dim of each merged feature slice in FeatureSlot\n    self.assertEqual(fs_1._merged_feature_slices[0].dim, 1)\n    self.assertEqual(fs_1._merged_feature_slices[1].dim, 2)\n    self.assertEqual(fs_2._merged_feature_slices[0].dim, 1)\n    self.assertEqual(fs_3._merged_feature_slices[0].dim, 5)\n    self.assertEqual(fs_4._merged_feature_slices[0].dim, 1)\n    self.assertEqual(fs_4._merged_feature_slices[1].dim, 9)\n    # Check the dim of each merged feature slice in FeatureColumn\n    self.assertTrue(fs_1._merged_feature_slices[0] in\n                    fc_1._merged_feature_slice_to_tf_placeholder)\n    self.assertTrue(fs_1._merged_feature_slices[1] in\n                    fc_1._merged_feature_slice_to_tf_placeholder)\n    self.assertTrue(fs_2._merged_feature_slices[0] in\n                    fc_2._merged_feature_slice_to_tf_placeholder)\n    self.assertTrue(fs_3._merged_feature_slices[0] in\n                    fc_3._merged_feature_slice_to_tf_placeholder)\n    self.assertTrue(fs_4._merged_feature_slices[0] in\n                    fc_4._merged_feature_slice_to_tf_placeholder)\n    self.assertTrue(fs_4._merged_feature_slices[1] in\n                    fc_4._merged_feature_slice_to_tf_placeholder)\n    self.assertTrue(fs_4._merged_feature_slices[0] in\n                    fc_5._merged_feature_slice_to_tf_placeholder)\n    self.assertTrue(fs_4._merged_feature_slices[1] in\n                    fc_5._merged_feature_slice_to_tf_placeholder)\n\n    # Test split logic\n    env._tpu_features = {}\n    env._tpu_features[\"fc_name_1_0\"] = tf.constant([[1]])\n    env._tpu_features[\"fc_name_1_1\"] = tf.constant([[2, 3]])\n    env._tpu_features[\"fc_name_2_0\"] = tf.constant([[4]])\n    env._tpu_features[\"fc_name_3_0\"] = tf.constant([[7, 8, 9, 10, 11]])\n    env._tpu_features[\"fc_name_4_0\"] = tf.constant([[12]])\n    env._tpu_features[\"fc_name_4_1\"] = tf.constant(\n        [[13, 14, 15, 16, 17, 18, 19, 20, 21]])\n    env._tpu_features[\"fc_name_5_0\"] = tf.constant([[12]])\n    env._tpu_features[\"fc_name_5_1\"] = tf.constant(\n        [[13, 14, 15, 16, 17, 18, 19, 20, 21]])\n\n    with tf.compat.v1.Session() as sess:\n      env._split_merged_embedding(fs_1)\n      env._split_merged_embedding(fs_2)\n      env._split_merged_embedding(fs_3)\n      env._split_merged_embedding(fs_4)\n\n      features = sess.run(env._tpu_features)\n      self.assertAllEqual(features[\"fc_name_1_0\"], [[1]])\n      self.assertAllEqual(features[\"fc_name_1_1\"], [[2, 3]])\n      self.assertAllEqual(features[\"fc_name_2_0\"], [[4]])\n      self.assertAllEqual(features[\"fc_name_3_0\"], [[7, 8]])\n      self.assertAllEqual(features[\"fc_name_3_1\"], [[9, 10, 11]])\n      self.assertAllEqual(features[\"fc_name_4_0\"], [[12]])\n      self.assertAllEqual(features[\"fc_name_4_1\"], [[13, 14]])\n      self.assertAllEqual(features[\"fc_name_4_2\"], [[15, 16, 17]])\n      self.assertAllEqual(features[\"fc_name_4_3\"], [[18, 19, 20, 21]])\n      self.assertAllEqual(features[\"fc_name_5_0\"], [[12]])\n      self.assertAllEqual(features[\"fc_name_5_1\"], [[13, 14]])\n      self.assertAllEqual(features[\"fc_name_5_2\"], [[15, 16, 17]])\n      self.assertAllEqual(features[\"fc_name_5_3\"], [[18, 19, 20, 21]])\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/core/host_call.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport sys\n\nfrom absl import logging\nimport json\nimport numpy as np\nimport tensorflow as tf\n\n_LABLES_FOR_AUC_CALCULATION = \"labels_for_auc_calculation\"\n_Y_PRED_FOR_AUC_CALCULATION = \"y_pred_for_auc_calculation\"\n_REQ_TIME = \"req_time\"\n_SAMPLE_RATE = \"sample_rate\"\n\n_DEEPINSIGHT_SAMPLE_RATES = \"di_example_sample_rates\"\n_DEEPINSIGHT_LABELS = \"di_labels\"\n_DEEPINSIGHT_PREDS = \"di_preds\"\n_DEEPINSIGHT_REQ_TIMES = \"di_req_times\"\n\n\nclass HostCall():\n\n  def __init__(self, output_dir, enable_host_call, enable_deepinsight):\n    self._output_dir = output_dir\n    self._enable_host_call = enable_host_call\n    self._enable_deepinsight = enable_deepinsight\n    self._tensor_names = [\"global_step\"]\n    gs = tf.compat.v1.train.get_global_step()\n    # Creating batch dimension since host call needs to concat all the cores'\n    # results.\n    self._tensors = [tf.reshape(gs, [-1])]\n    # compressed_tensor_list is a list of compressed tensors, in\n    # which each compressed tensor is the concatenation of multiple\n    # uncompressed tensors. To decompress, we need to store the original\n    # sizes of all uncompressed tensors, organized in list of lists.\n    # Each list corresponds to a compressed tensors.\n    self._lists_tensor_sizes = []  # A list of lists\n\n  def record_summary_tensor(self, name, tensor):\n    assert name not in self._tensor_names\n    self._tensor_names.append(name)\n\n    assert len(tensor.get_shape()) <= 1, \"Now we only support tensor with shape (k, ) or ()\"\\\n        \"but we met tensor with shape: {}\".format(tensor.get_shape())\n\n    # Creating batch dimension since host call needs to concat all the cores'\n    # results.\n    reshaped_tensor = tf.reshape(tensor, [-1])\n    self._tensors.append(reshaped_tensor)\n\n  def compress_tensors(self):\n    \"\"\"For n tensors with shape (k_i, ) and same data type, concat them on axis=1.\n\n        After concatenation the compressed tensors is stored as in shape\n        (1, k_0 + k_1 + ... + k_{n-1}).\n        \"\"\"\n    assert len(self._tensor_names) == len(self._tensors), \"tensor_names and tensors must have same length,\" \\\n            \"tensor_names length: {}, tensors length: {}\".format(len(self._tensor_names), len(self._tensors))\n    # key is tensor data type, value is a list of tensor names with this data type.\n    data_type_to_tensor_names = {}\n    # key is tensor data type, value is tensor a list of tensors with this data type.\n    date_type_to_tensors = {}\n\n    # Group tensor names and tensors by same data type.\n    for tensor_name, tensor in zip(self._tensor_names, self._tensors):\n      data_type_to_tensor_names.setdefault(tensor.dtype, []).append(tensor_name)\n      date_type_to_tensors.setdefault(tensor.dtype, []).append(tensor)\n\n    # Compress n tensors tensor_0, tensor_1, ... , tensor_{n-1} with shape\n    # (k_0, ), (k_1, )... (k_{n-1}, ) of same data\n    # type to one tensor with shape (1, k_0 + ... + k_{n-1})\n    compressed_tensor_name_list = []\n    compressed_tensor_list = []\n    for data_type, tensor_list in date_type_to_tensors.items():\n      compressed_tensor_name_list.extend(data_type_to_tensor_names[data_type])\n\n      # concat a list of tensors with shapes\n      # (k_0, ), (k_1, )... (k_{n-1}, ) to a tensor with shape\n      # (k_0 + k_1 + ... + k_{n-1}, )\n      tensor_sizes = []\n      for tensor in tensor_list:\n        tensor_sizes.append(tensor.shape[0].value)\n      self._lists_tensor_sizes.append(tensor_sizes)\n      compressed_tensor = tf.concat(tensor_list, axis=0)\n\n      # expand dimension at 0 to make it have the batch dimension\n      # tensor with shape (k_0 + k_1 + ... + k_{n-1}, )\n      # => tensor with shape (1, k_0 + k_1 + ... + k_{n-1})\n      compressed_tensor = tf.expand_dims(compressed_tensor, axis=0)\n      compressed_tensor_list.append(compressed_tensor)\n\n      logging.info(\n          \"Host call compressed tensors, data type: {}, compressed tensor shape: {}\"\n          .format(data_type, compressed_tensor.shape))\n\n    self._tensor_names = compressed_tensor_name_list\n    self._tensors = compressed_tensor_list\n\n  def decompress_tensors(self, tensors):\n    \"\"\"\n        Decompress the compressed tensors into list of decompressed tensors.\n\n        Given a list of compressed tensors from *args. Each tensor has shape\n        (num_cores, k_0 + k_1 + ... + k_{n-1}), in which the second dimension\n        is the sum of lengths of uncompressed tensors (k_i, ) from the same\n        core, with same shape and data type. Parse and convert them to a list of\n        decompressed tensors.\n\n        Each decompressed tensor has shape (num_cores, k_i). Decompressed tensor\n        number must match with number of tensor names as well.\n        \"\"\"\n    # Need decompress tensors\n    decompressed_tensor_list = []\n    for index, compressed_tensor in enumerate(tensors):\n      # For each tensor, its shape is (num_cores, k_0 + ... + k_{n-1})\n      assert len(compressed_tensor.get_shape(\n      )) == 2, \"Compressed tensors shape must be (n, m), met shape: {}\".format(\n          compressed_tensor.shape)\n      logging.info(\"Decompressed tensors shape: {}, dtype: {}.\".format(\n          compressed_tensor.shape, compressed_tensor.dtype))\n\n      # tensor with shape (num_cores, k_0 + ... + k_{n-1})\n      # => list of tensors with shape (num_cores, k_i)\n      split_tensors = tf.split(compressed_tensor,\n                               self._lists_tensor_sizes[index],\n                               axis=1)\n\n      for tensor in split_tensors:\n        # Each decompressed tensor with shape (num_cores, k_i)\n        # => (num_cores * k_i, ).\n        tensor = tf.squeeze(tensor)\n        decompressed_tensor_list.append(tensor)\n\n    assert self._tensor_names[\n        0] == \"global_step\", \"The first tensor name must be global_step, met value: {}\".format(\n            self._tensor_names[0][0])\n    return decompressed_tensor_list[0][0], decompressed_tensor_list\n\n  def _verify_shape_and_dtype(self, tensor, shape_list, dtype):\n    assert tensor is not None\n    assert tensor.shape.as_list(\n    ) == shape_list, \"Expect shape: {}, but actual shape: {}\".format(\n        shape_list, tensor.shape.as_list())\n    assert tensor.dtype == dtype, \"Expect dtype {}, but actual dtype: {}\".format(\n        dtype, tensor.dtype)\n\n  def _serialize_messages(self, labels, y_preds, sample_rates, req_times, gs):\n    assert labels is not None\n    labels_shape = labels.shape.as_list()\n    assert len(labels_shape\n              ) == 2, \"Expect labels_shape to be 1, but its shape is {}\".format(\n                  labels_shape)\n\n    self._verify_shape_and_dtype(y_preds, labels_shape, tf.float32)\n    self._verify_shape_and_dtype(sample_rates, labels_shape, tf.float32)\n    self._verify_shape_and_dtype(req_times, labels_shape, tf.int64)\n\n    # reshape is low cost without real data copy.\n    # flatten the tensor here and simplify the data format before serializing to string.\n    # Each tensor has shape (n, ), n equals to core_number * batch_size_per_core\n    labels = tf.reshape(labels, [-1])\n    y_preds = tf.reshape(y_preds, [-1])\n    sample_rates = tf.reshape(sample_rates, [-1])\n    req_times = tf.reshape(req_times, [-1])\n\n    # The first two model names and di sample rates can be get from host_call folder suffix\n    tf.compat.v1.summary.text(_DEEPINSIGHT_SAMPLE_RATES,\n                              data=tf.io.serialize_tensor(sample_rates),\n                              step=gs)\n    tf.compat.v1.summary.text(_DEEPINSIGHT_LABELS,\n                              data=tf.io.serialize_tensor(labels),\n                              step=gs)\n    tf.compat.v1.summary.text(_DEEPINSIGHT_PREDS,\n                              data=tf.io.serialize_tensor(y_preds),\n                              step=gs)\n    tf.compat.v1.summary.text(_DEEPINSIGHT_REQ_TIMES,\n                              data=tf.io.serialize_tensor(req_times),\n                              step=gs)\n\n  def generate_host_call_hook(self):\n\n    def _host_call(*args):\n      gs, tensors = self.decompress_tensors(args)\n      summary_writer = tf.compat.v1.summary.create_file_writer(\n          self._output_dir + \"/host_call\", flush_millis=10000, max_queue=5000)\n      with summary_writer.as_default():\n        labels = None\n        y_preds = None\n        req_times = None\n        sample_rates = None\n\n        for i, t in enumerate(tensors):\n          if i == 0:\n            continue\n\n          name = self._tensor_names[i]\n          data = None\n          if \"_avg\" in name:\n            data = tf.reduce_mean(t)\n          elif \"_max\" in name:\n            data = tf.reduce_max(t)\n          elif _LABLES_FOR_AUC_CALCULATION in name:\n            labels = t\n          elif _Y_PRED_FOR_AUC_CALCULATION in name:\n            y_preds = t\n          elif _REQ_TIME in name:\n            req_times = t\n          elif _SAMPLE_RATE in name:\n            sample_rates = t\n          else:\n            data = t[0]\n\n          if data is not None:\n            tf.compat.v1.summary.scalar(name, data=data, step=gs)\n        if labels is not None and y_preds is not None:\n          auc, auc_op = tf.compat.v1.metrics.auc(labels=labels,\n                                                 predictions=y_preds)\n          tf.compat.v1.summary.scalar(\"auc\", data=auc, step=gs)\n        else:\n          auc_op = None\n\n        if self._enable_deepinsight is True and labels is not None:\n          messages = self._serialize_messages(labels, y_preds, sample_rates,\n                                              req_times, gs)\n\n      if auc_op is not None:\n        return tf.group(tf.compat.v1.summary.all_v2_summary_ops(), auc_op)\n      else:\n        return tf.compat.v1.summary.all_v2_summary_ops()\n\n    if self._enable_host_call == True:\n      self.compress_tensors()\n      return (_host_call, self._tensors)\n    else:\n      logging.info(\"host_call has been disabled\")\n      return None\n"
  },
  {
    "path": "monolith/core/hyperparams.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\"\"\"Defines Params base class, used for defining class/function parameters.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport copy\nimport re\nimport six\nfrom inspect import Parameter, signature\n\nimport tensorflow as tf\n\n\ndef _is_named_tuple(x):\n  \"\"\"Returns whether an object is an instance of a collections.namedtuple.\n    Examples::\n      _is_named_tuple((42, 'hi')) ==> False\n      Foo = collections.namedtuple('Foo', ['a', 'b'])\n      _is_named_tuple(Foo(a=42, b='hi')) ==> True\n    Args:\n    x: The object to check.\n    \"\"\"\n  return isinstance(x, tuple) and hasattr(x, '_fields')\n\n\nclass _SortedDict(dict):\n  \"\"\"A dict with a __repr__ that is always sorted by key.\"\"\"\n\n  def __repr__(self):\n    return '{' + ', '.join(\n        '%r: %r' % item for item in sorted(self.items())) + '}'\n\n\nclass _Param(object):\n  \"\"\"Stores data for a single parameter.\"\"\"\n\n  def __init__(self, name, default_value, description):\n    self._name = name\n    self._value = default_value\n    self._description = description\n\n  def __eq__(self, other):\n    # pylint: disable=protected-access\n    return self._name == other._name and self._value == other._value\n\n  # Deep copy the value only if it is supported.\n  def __deepcopy__(self, memo):\n    if isinstance(self._value, (tf.Tensor)):\n      # In case self._value is a tensor, let's just make a reference.\n      value = self._value\n    else:\n      value = copy.deepcopy(self._value, memo)\n    p = _Param(self._name, value, self._description)\n    # Q(yonghui): Is this the right use of memo.\n    memo[id(self)] = p\n    return p\n\n  def to_string(self, nested_depth):\n    \"\"\"Prints the parameter as a string.\"\"\"\n\n    def GetRepr(val):\n      \"\"\"Get the representation of `val`.\"\"\"\n      if isinstance(val, Params):\n        return _SortedDict({k: GetRepr(v) for k, v in val.iter_params()})\n      if isinstance(val, dict):\n        return _SortedDict({k: GetRepr(v) for k, v in six.iteritems(val)})\n      if isinstance(val, (list, tuple)) and not _is_named_tuple(val):\n        # NB: this constructor signature works for tuples, but not namedtuples.\n        return type(val)([GetRepr(v) for v in val])\n      # NOTE(markmurphy): I introduced Repr() because it's impossible (afaik) to\n      # overwrite the __str__ or __repr__ method of a types.FunctionType object.\n      if hasattr(val, 'Repr'):\n        return val.Repr()\n      return val\n\n    nested_indent = '  ' * nested_depth\n    if isinstance(self._value, Params):\n      # pylint: disable=protected-access\n      value_str = self._value._to_string(nested_depth)\n    elif isinstance(self._value, six.string_types):\n      return '%s%s: \"%s\"' % (nested_indent, self._name, self._value)\n    else:\n      value_str = str(GetRepr(self._value))\n    return '%s%s: %s' % (nested_indent, self._name, value_str)\n\n  def set(self, value):\n    # Note that we don't make a copy of Params objects.\n    # TODO(sadovsky): Maybe add safeguard to ensure that Params object is not\n    # owned by other Params objects.\n    self._value = value\n\n  def get(self):\n    return self._value\n\n\ndef copy_params_to(from_p, to_p, skip=None):\n  \"\"\"Copy from one Params to another, with optional skipped params.\n\n  Args:\n    from_p: Source params to copy from.\n    to_p: Destination params to copy to.\n    skip: If not None, a list of strings of param names to skip.\n\n  Returns:\n    None\n  \"\"\"\n  for n, p in from_p.iter_params():\n    if skip and n in skip:\n      continue\n    if isinstance(p, Params):\n      to_p.set(**{n: p.copy()})\n    else:\n      to_p.set(**{n: p})\n  return to_p\n\n\nclass Params(object):\n  \"\"\"Stores data for a set of parameters.\n    Provides attribute-based API, e.g. \"params.foo = 5\".\n    Uses internal {'name': Params} dict for storing parameter data.\n    \"\"\"\n\n  def __init__(self):\n    self.__dict__['_immutable'] = False\n    self._params = {}  # name => _Param\n\n  def __setattr__(self, name, value):\n    if self._immutable:\n      raise TypeError('This Params instance is immutable.')\n    if name == '_params' or name == '_immutable':\n      self.__dict__[name] = value\n    else:\n      try:\n        self._params[name].set(value)\n      except KeyError:\n        raise AttributeError(self._key_error_string(name))\n\n  def __getattr__(self, name):\n    if name == '_params' or name == '_immutable':\n      return self.__dict__[name]\n    try:\n      return self._params[name].get()\n    except KeyError:\n      # cPickle expects __getattr__ to raise AttributeError, not KeyError.\n      raise AttributeError(self._key_error_string(name))\n\n  def __setitem__(self, name, value):\n    self.__setattr__(name, value)\n\n  def __getitem__(self, key):\n    return self.__getattr__(key)\n\n  def __dir__(self):\n    return sorted(self._params.keys())\n\n  def __contains__(self, name):\n    return name in self._params\n\n  def __len__(self):\n    return len(self._params)\n\n  # Note: This gets called by Params.__eq__() on nested Params objects.\n  def __eq__(self, other):\n    return isinstance(other, Params) and self._params == other._params  # pylint: disable=protected-access\n\n  def __ne__(self, other):\n    return not self == other\n\n  def __str__(self):\n    return self._to_string(0)\n\n  def _to_string(self, nested_depth):\n    # Note: We use iteritems() below so as to sort by name.\n    sorted_param_strs = [\n        v.to_string(nested_depth + 1)\n        for (_, v) in sorted(six.iteritems(self._params))\n    ]\n    nested_indent = '  ' * nested_depth\n    return '{\\n%s\\n%s}' % ('\\n'.join(sorted_param_strs), nested_indent)\n\n  # Override __deepcopy__ so that copy.deepcopy(self._params) properly\n  # deep-copies nested Params objects.\n  # TODO(sadovsky): Is it okay not to touch memo?\n  def __deepcopy__(self, unused_memo):\n    return self.copy()\n\n  def _similar_keys(self, name):\n    \"\"\"Return a list of params keys that are similar to name.\"\"\"\n\n    def _overlaps(name, key):\n      \"\"\"The fraction of 3-char substrings in <name> that appear in key.\"\"\"\n      matches = 0\n      trials = 0\n      for i in range(len(name) - 3):\n        trials += 1\n        if name[i:i + 3] in key:\n          matches += 1\n      if trials:\n        return float(matches) / trials\n      return 0\n\n    if '_params' in self.__dict__:\n      return [key for key in self._params if _overlaps(name, key) > 0.5]\n    return []\n\n  def _key_error_string(self, name):\n    similar = self._similar_keys(name)\n    if similar:\n      return name + ' (did you mean: [%s])' % (','.join(sorted(similar)))\n    return name\n\n  def copy(self):\n    return self._copy_to(type(self)())\n\n  def _copy_to(self, res):\n    # pylint: disable=protected-access\n    res._params = copy.deepcopy(self._params)\n    res._immutable = self._immutable\n    # pylint: enable=protected-access\n    return res\n\n  # TODO(sadovsky):\n  # - Maybe let users specify whether this parameter is allowed to have\n  #   value=None, and if not, assert on get(), like required proto field.\n  # - Maybe enforce that value is one of\n  #     {number, string, bool, list, dict, Params}.\n  def define(self, name, default_value, description):\n    \"\"\"Defines a parameter.\n        Args:\n          name: The parameter name. Must only contain lowercase letters, numbers,\n              and underscores. Must start with lowercase letter.\n          default_value: Default value for this parameter. May be None.\n          description: String description of this parameter.\n        Raises:\n          AttributeError: If parameter 'name' is already defined.\n        \"\"\"\n    if self._immutable:\n      raise TypeError('This Params instance is immutable.')\n    assert name is not None and isinstance(\n        name, six.string_types) and (re.match('^[a-z][a-z0-9_]*$', name)\n                                     is not None)\n    if name in self._params:\n      raise AttributeError('Parameter %s is already defined' % name)\n    self._params[name] = _Param(name, default_value, description)\n\n  def contain(self, name):\n    return name in self._params\n\n  def freeze(self):\n    \"\"\"Marks this Params as immutable.\"\"\"\n    self._immutable = True\n\n  def is_immutable(self):\n    \"\"\"Return whether this Params is immutable.\"\"\"\n    return self._immutable\n\n  def _get_nested(self, name):\n    \"\"\"Returns nested param by its name.\"\"\"\n    parts = name.split('.')\n    curr = self\n    for i, part in enumerate(parts[:-1]):\n      # get the value (nested Params object) associated with name 'part'.\n      try:\n        is_list = re.match(r'^(.+)\\[(.+)\\]$', part)\n        if is_list:\n          part = is_list.group(1)\n          list_index = int(is_list.group(2))\n        # pylint: disable=protected-access\n        curr = curr._params[part].get()\n        if is_list:\n          curr = curr[list_index]\n      except KeyError:\n        raise AttributeError('.'.join(parts[:i + 1]))\n      assert isinstance(curr, Params), ('Cannot introspect %s for %s' %\n                                        (type(curr), '.'.join(parts[:i + 1])))\n    return curr, parts[-1]\n\n  def set(self, **kwargs):\n    \"\"\"Sets multiple parameters.\n        Dots in names indicate navigation into nested Params objects. We do not\n        allow navigation into lists or dicts, and may ban these types altogether in\n        favor of string representations.\n        Args:\n          **kwargs: Name-value pairs to set.\n        Returns:\n          self\n        \"\"\"\n    if self._immutable:\n      raise TypeError('This Params instance is immutable: %s' % self)\n    for name, value in six.iteritems(kwargs):\n      # get nested param.\n      param, key = self._get_nested(name)\n      # Update the value associated with key.\n      try:\n        # pylint: disable=protected-access\n        param._params[key].set(value)\n      except KeyError:\n        raise AttributeError(self._key_error_string(name))\n    return self\n\n  def get(self, name):\n    \"\"\"get parameter.\n        Dots in names indicate navigation into nested Params objects. We do not\n        allow navigation into lists or dicts, and may ban these types altogether in\n        favor of string representations.\n        Args:\n          name: (str) Name.\n        Returns:\n          value.\n        Raises:\n          AttributeError: if parameter is not found\n        \"\"\"\n    param, key = self._get_nested(name)\n    # get the value associated with key.\n    try:\n      # pylint: disable=protected-access\n      return param._params[key].get()\n    except KeyError:\n      raise AttributeError(self._key_error_string(name))\n\n  def delete(self, *args):\n    \"\"\"Deletes multiple parameters.\n        Dots in names indicate navigation into nested Params objects. We do not\n        allow navigation into lists or dicts, and may ban these types altogether in\n        favor of string representations.\n        Args:\n          *args: List of names.\n        Returns:\n          self\n        \"\"\"\n    if self._immutable:\n      raise TypeError('This Params instance is immutable.')\n    for name in args:\n      # get nested param.\n      param, key = self._get_nested(name)\n      # delete the key.\n      try:\n        # pylint: disable=protected-access\n        del param._params[key]\n      except KeyError:\n        raise AttributeError(self._key_error_string(name))\n    return self\n\n  def iter_params(self):\n    \"\"\"Pythonic dict-like iteration.\"\"\"\n    for name, param in six.iteritems(self._params):\n      yield (name, param.get())\n\n\nallowed_kwargs = {\n    'input_dim', 'input_shape', 'batch_input_shape', 'weights',\n    'activity_regularizer', 'autocast', 'implementation', 'name'\n}\n\n\ndef _inverted_index(ips: 'InstantiableParams', idx):\n  for name, item in ips.iter_params():\n    if isinstance(item, Params):\n      _inverted_index(item, idx)\n    else:\n      idx[name] = item\n\n\nclass InstantiableParams(Params):\n  \"\"\"Params which can be instantiated.\n    When using InstantiableParams, callers must provide a class which supports\n    initialization using a Params instance.\n    This covers a common use case of Params to hold a configuration for a given\n    class.\n    \"\"\"\n\n  def __init__(self, cls=None):\n    super(InstantiableParams, self).__init__()\n    self.define('cls', cls, 'Cls that this param object is associated with.')\n\n  def instantiate(self):\n    \"\"\"instantiate an instance that this Params is configured for.\"\"\"\n    assert self.cls is not None\n    # The class initializer is expected to support initialization using Params.\n\n    parameters = signature(self.cls.__init__).parameters\n    if len(parameters) == 2 and hasattr(self.cls,\n                                        'params') and 'params' in parameters:\n      return self.cls(self)\n    else:\n      index, args = {}, {}\n      _inverted_index(self, index)\n      for name, p in parameters.items():\n        if p.kind in {Parameter.VAR_KEYWORD, Parameter.VAR_POSITIONAL}:\n          continue\n\n        if name not in {'self', 'cls'} and name in index:\n          args[name] = index[name]\n\n      for key in allowed_kwargs:\n        if key in index and index[key] is not None:\n          args[key] = index[key]\n\n      return self.cls(**args)\n\n  def copy(self):\n    return self._copy_to(type(self)(self.cls))\n\n\ndef update_params(ips: Params, args):\n  for key, value in ips.iter_params():\n    if isinstance(value, Params):\n      update_params(value, args)\n    else:\n      if key in args:\n        ips[key] = args.pop(key)\n"
  },
  {
    "path": "monolith/core/hyperparams_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport collections\nimport enum\nimport functools\nimport re\nimport sys\n\nfrom absl import app as absl_app\nfrom absl import flags\nfrom absl import logging\nimport tensorflow as tf\nimport unittest\n\nimport monolith.core.hyperparams as _params\n\n\nclass TestEnum(enum.Enum):\n  \"\"\"Test enum class.\"\"\"\n  A = 1\n  B = 2\n\n\nclass ParamsTest(unittest.TestCase):\n\n  def test_equals(self):\n    params1 = _params.Params()\n    params2 = _params.Params()\n    self.assertTrue(params1 == params2)\n    params1.define('first', 'firstvalue', '')\n    self.assertFalse(params1 == params2)\n    params2.define('first', 'firstvalue', '')\n    self.assertTrue(params1 == params2)\n    some_object = object()\n    other_object = object()\n    params1.define('second', some_object, '')\n    params2.define('second', other_object, '')\n    self.assertFalse(params1 == params2)\n    params2.second = some_object\n    self.assertTrue(params1 == params2)\n    params1.define('third', _params.Params(), '')\n    params2.define('third', _params.Params(), '')\n    self.assertTrue(params1 == params2)\n    params1.third.define('fourth', 'x', '')\n    params2.third.define('fourth', 'y', '')\n    self.assertFalse(params1 == params2)\n    params2.third.fourth = 'x'\n    self.assertTrue(params1 == params2)\n    # Comparing params to non-param instances.\n    self.assertFalse(params1 == 3)\n    self.assertFalse(3 == params1)\n\n  def test_deep_copy(self):\n    inner = _params.Params()\n    inner.define('alpha', 2, '')\n    inner.define('tensor', tf.constant(0), '')\n    outer = _params.Params()\n    outer.define('beta', 1, '')\n    outer.define('inner', inner, '')\n    outer_copy = outer.copy()\n    self.assertIsNot(outer, outer_copy)\n    self.assertEqual(outer, outer_copy)\n    self.assertIsNot(outer.inner, outer_copy.inner)\n    self.assertEqual(outer.inner, outer_copy.inner)\n    self.assertEqual(outer.inner.alpha, outer_copy.inner.alpha)\n    self.assertIs(outer.inner.tensor, outer_copy.inner.tensor)\n\n  def test_copy_params_to(self):\n    source = _params.Params()\n    dest = _params.Params()\n    source.define('a', 'a', '')\n    source.define('b', 'b', '')\n    source.define('c', 'c', '')\n    dest.define('a', '', '')\n    _params.copy_params_to(source, dest, skip=['b', 'c'])\n    self.assertEqual(source.a, dest.a)\n    self.assertNotIn('b', dest)\n    self.assertNotIn('c', dest)\n\n  def test_define_existing(self):\n    p = _params.Params()\n    p.define('foo', 1, '')\n    self.assertRaisesRegex(AttributeError, 'already defined',\n                           lambda: p.define('foo', 1, ''))\n\n  def test_legal_param_names(self):\n    p = _params.Params()\n    self.assertRaises(AssertionError, lambda: p.define(None, 1, ''))\n    self.assertRaises(AssertionError, lambda: p.define('', 1, ''))\n    self.assertRaises(AssertionError, lambda: p.define('_foo', 1, ''))\n    self.assertRaises(AssertionError, lambda: p.define('Foo', 1, ''))\n    self.assertRaises(AssertionError, lambda: p.define('1foo', 1, ''))\n    self.assertRaises(AssertionError, lambda: p.define('foo$', 1, ''))\n    p.define('foo_bar', 1, '')\n    p.define('foo9', 1, '')\n\n  def test_set_and_get(self):\n    p = _params.Params()\n    self.assertRaisesRegex(AttributeError, 'foo', lambda: p.set(foo=4))\n    # We use setattr() because lambda cannot contain explicit assignment.\n    self.assertRaisesRegex(AttributeError, 'foo', lambda: setattr(p, 'foo', 4))\n    p.define('foo', 1, '')\n    self.assertEqual(p.foo, 1)\n    self.assertEqual(p.get('foo'), 1)\n    self.assertIn('foo', p)\n    self.assertNotIn('bar', p)\n    p.set(foo=2)\n    self.assertEqual(p.foo, 2)\n    self.assertEqual(p.get('foo'), 2)\n    p.foo = 3\n    self.assertEqual(p.foo, 3)\n    self.assertEqual(p.get('foo'), 3)\n    p.delete('foo')\n    self.assertNotIn('foo', p)\n    self.assertNotIn('bar', p)\n    self.assertRaisesRegex(AttributeError, 'foo', lambda: p.foo)\n    self.assertRaisesRegex(AttributeError, 'foo', p.get, 'foo')\n\n  def test_set_and_get_nested_param(self):\n    innermost = _params.Params()\n    innermost.define('delta', 22, '')\n    innermost.define('zeta', 5, '')\n\n    inner = _params.Params()\n    inner.define('alpha', 2, '')\n    inner.define('innermost', innermost, '')\n\n    outer = _params.Params()\n    outer.define('beta', 1, '')\n    outer.define('inner', inner, '')\n    outer.define('d', dict(foo='bar'), '')\n\n    self.assertEqual(inner.alpha, 2)\n    self.assertEqual(outer.beta, 1)\n    self.assertEqual(outer.d['foo'], 'bar')\n    self.assertEqual(outer.inner.alpha, 2)\n    self.assertEqual(outer.inner.innermost.delta, 22)\n    self.assertEqual(outer.inner.innermost.zeta, 5)\n\n    self.assertEqual(inner.get('alpha'), 2)\n    self.assertEqual(outer.get('beta'), 1)\n    self.assertEqual(outer.get('d')['foo'], 'bar')\n    self.assertEqual(outer.get('inner.alpha'), 2)\n    self.assertEqual(outer.get('inner.innermost.delta'), 22)\n    self.assertEqual(outer.get('inner.innermost.zeta'), 5)\n\n    outer.set(**{'inner.alpha': 3})\n    outer.set(d=dict(foo='baq'))\n    outer.delete('beta')\n    outer.delete('inner.innermost.zeta')\n\n    self.assertEqual(inner.alpha, 3)\n    self.assertRaisesRegex(AttributeError, 'beta', lambda: outer.beta)\n    self.assertEqual(outer.d['foo'], 'baq')\n    self.assertEqual(outer.inner.alpha, 3)\n    self.assertEqual(outer.inner.innermost.delta, 22)\n    self.assertRaisesRegex(AttributeError, 'zeta',\n                           lambda: outer.inner.innermost.zeta)\n\n    self.assertEqual(inner.get('alpha'), 3)\n    self.assertRaisesRegex(AttributeError, 'beta', outer.get, 'beta')\n    self.assertEqual(outer.get('d')['foo'], 'baq')\n    self.assertEqual(outer.get('inner.alpha'), 3)\n    self.assertEqual(outer.get('inner.innermost.delta'), 22)\n    self.assertRaisesRegex(AttributeError, 'inner.innermost.zeta', outer.get,\n                           'inner.innermost.zeta')\n\n    # NOTE(igushev): Finding nested Param object is shared between Get, Set and\n    # Delete, so we test only Set.\n    self.assertRaisesRegex(AttributeError, r'inner\\.gamma',\n                           lambda: outer.set(**{'inner.gamma': 5}))\n    self.assertRaisesRegex(AttributeError, r'inner\\.innermost\\.bad',\n                           lambda: outer.set(**{'inner.innermost.bad': 5}))\n    self.assertRaisesRegex(AssertionError, '^Cannot introspect',\n                           lambda: outer.set(**{'d.foo': 'baz'}))\n\n  def test_freeze(self):\n    p = _params.Params()\n    self.assertRaises(AssertionError, lambda: p.define('_immutable', 1, ''))\n    self.assertRaisesRegex(AttributeError, 'foo', lambda: p.set(foo=4))\n    # We use setattr() because lambda cannot contain explicit assignment.\n    self.assertRaisesRegex(AttributeError, 'foo', lambda: setattr(p, 'foo', 4))\n    p.define('foo', 1, '')\n    p.define('nested', p.copy(), '')\n    self.assertEqual(p.foo, 1)\n    self.assertEqual(p.get('foo'), 1)\n    self.assertEqual(p.nested.foo, 1)\n    p.freeze()\n\n    self.assertRaises(TypeError, lambda: p.set(foo=2))\n    self.assertEqual(p.get('foo'), 1)\n    self.assertRaises(TypeError, lambda: setattr(p, 'foo', 3))\n    self.assertEqual(p.foo, 1)\n    self.assertRaises(TypeError, lambda: p.delete('foo'))\n    self.assertEqual(p.foo, 1)\n    self.assertRaises(TypeError, lambda: p.define('bar', 1, ''))\n    self.assertRaisesRegex(AttributeError, 'bar', p.get, 'bar')\n\n    p.nested.foo = 2\n    self.assertEqual(p.foo, 1)\n    self.assertEqual(p.nested.foo, 2)\n\n    self.assertRaises(TypeError, lambda: setattr(p, '_immutable', False))\n\n    # Copies are still immutable.\n    q = p.copy()\n    self.assertRaises(TypeError, lambda: q.set(foo=2))\n\n  def test_to_string(self):\n    outer = _params.Params()\n    outer.define('foo', 1, '')\n    inner = _params.Params()\n    inner.define('bar', 2, '')\n    outer.define('inner', inner, '')\n    outer.define('list', [1, inner, 2], '')\n    outer.define('dict', {'a': 1, 'b': inner}, '')\n    outer.define('enum', TestEnum.B, '')\n    self.assertEqual(\n        '\\n' + str(outer), \"\"\"\n{\n  dict: {'a': 1, 'b': {'bar': 2}}\n  enum: TestEnum.B\n  foo: 1\n  inner: {\n    bar: 2\n  }\n  list: [1, {'bar': 2}, 2]\n}\"\"\")\n\n  def test_iter_params(self):\n    keys, values = ['a', 'b', 'c', 'd', 'e'], [True, None, 'zippidy', 78.5, 5]\n    p = _params.Params()\n    for k, v in zip(keys, values):\n      p.define(k, v, 'description of %s' % k)\n\n    k_set, v_set = set(keys), set(values)\n    number_of_params = 0\n    for k, v in p.iter_params():\n      self.assertIn(k, k_set)\n      self.assertIn(v, v_set)\n      number_of_params += 1\n    self.assertEqual(number_of_params, len(keys))\n\n  def test_similar_keys(self):\n    p = _params.Params()\n    p.define('activation', 'RELU', 'Can be a string or a list of strings.')\n    p.define('activations', 'RELU', 'Many activations.')\n    p.define('cheesecake', None, 'dessert')\n    p.define('tofu', None, 'not dessert')\n\n    def set_param():\n      p.actuvation = 1\n\n    self.assertRaisesRegexp(\n        AttributeError,\n        re.escape('actuvation (did you mean: [activation,activations])'),\n        set_param)\n\n\nif __name__ == \"__main__\":\n  unittest.main(verbosity=2)\n"
  },
  {
    "path": "monolith/core/mixed_emb_op_comb_nws.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# -*- encoding=utf-8 -*-\n\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.keras.layers import Layer, InputSpec\n\n\nclass TeacherEmbeddingTransform(Layer):\n  \"\"\"Combined.\n\n    Example::\n\n        # as first layer in a sequential model:\n        #  x is a compatible tensor\n        x = layers.Dense(32, input_shape=(16,))(x)\n        # now the model will take as input arrays of shape (*, 16)\n        # and output arrays of shape (*, 32)\n\n    Args:\n        units: Positive integer, dimensionality of the output space.\n        activation: Activation function to use\n            If you don't specify anything, no activation is applied\n            (ie. \"linear\" activation: `a(x) = x`).\n        use_bias: Boolean, whether the layer uses a bias vector.\n        kernel_initializer: Initializer for the `kernel` weights matrix\n        bias_initializer: Initializer for the bias vector\n        allow_kernel_norm: T/F\n            kernel normalization is only applicable when TRAINING\n        kernel_normalization_trainable:\n            If True, a trainable weight norm variable is allocated\n\n    Input shapes:\n        nD tensor with shape: `(batch_size, ..., input_dim)`.\n        The most common situation would be\n        a 2D input with shape `(batch_size, input_dim)`.\n\n    Output shapes:\n        nD tensor with shape: `(batch_size, ..., units)`.\n        For instance, for a 2D input with shape `(batch_size, input_dim)`,\n        the output would have shape `(batch_size, units)`.\n    \"\"\"\n\n  def __init__(self, max_choice_per_embedding, teacher_embedding_sizes_list,\n               **kwargs):\n    super(TeacherEmbeddingTransform, self).__init__(**kwargs)\n    assert len(max_choice_per_embedding) == len(teacher_embedding_sizes_list)\n    self._max_choice_per_embedding = np.array(max_choice_per_embedding)\n    self._teacher_embedding_sizes_list = np.array(teacher_embedding_sizes_list)\n\n    self.input_spec = InputSpec(ndim=2)\n\n  def build(self, input_shape):\n    assert len(input_shape) == 2\n    input_dim = input_shape[-1]\n    assert input_dim == np.sum(self._teacher_embedding_sizes_list)\n\n    total_teacher_embedding_transform_weight_size = np.sum(\n        self._max_choice_per_embedding * self._teacher_embedding_sizes_list)\n    self._teacher_embedding_transform_weight = self.add_weight(\n        initial_value=tf.keras.initializers.TruncatedNormal(stddev=0.15)(\n            [total_teacher_embedding_transform_weight_size, 1], self.dtype),\n        name='teacher_embedding_transform_weight')\n    self._snapshot_for_serving(self._teacher_embedding_transform_weight,\n                               'teacher_embedding_transform_weight')\n    self._teacher_embedding_transform_bias = self.add_weight(\n        initial_value=tf.keras.initializers.Zeros()(\n            [np.sum(self._max_choice_per_embedding)], self.dtype),\n        name='teacher_embedding_transform_bias')\n    self._snapshot_for_serving(self._teacher_embedding_transform_bias,\n                               'teacher_embedding_transform_bias')\n    self.built = True\n\n  def call(self, inputs):\n    teacher_embedding = inputs\n    current_weight_idx = 0\n    current_teacher_idx = 0\n    teacher_transformed = []\n    for i in range(self._max_choice_per_embedding.shape[0]):\n      teacher_embedding_slice = teacher_embedding[:, current_teacher_idx:\n                                                  current_teacher_idx + self.\n                                                  _teacher_embedding_sizes_list[\n                                                      i]]\n      transform_weight_slice = self._teacher_embedding_transform_weight[\n          current_weight_idx:current_weight_idx +\n          self._teacher_embedding_sizes_list[i] *\n          self._max_choice_per_embedding[i]]\n      teacher_transformed.append(\n          tf.matmul(\n              teacher_embedding_slice,\n              tf.reshape(transform_weight_slice, [\n                  self._teacher_embedding_sizes_list[i],\n                  self._max_choice_per_embedding[i]\n              ])))\n      current_weight_idx += self._teacher_embedding_sizes_list[\n          i] * self._max_choice_per_embedding[i]\n      current_teacher_idx += self._teacher_embedding_sizes_list[i]\n\n    return tf.concat(teacher_transformed,\n                     axis=1) + self._teacher_embedding_transform_bias\n\n  def compute_output_shape(self, input_shape):\n    raise NotImplementedError(\"I don't think I need to implement this one.\")\n\n  def get_config(self):\n    config = {\n        'max_choice_per_embedding': self._max_choice_per_embedding,\n        'teacher_embedding_sizes_list': self._teacher_embedding_sizes_list\n    }\n    base_config = super(TeacherEmbeddingTransform, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\nclass MixedEmbedOpComb(Layer):\n  \"\"\"Combined.\n\n    Example::\n\n        # as first layer in a sequential model:\n        #  x is a compatible tensor\n        x = layers.Dense(32, input_shape=(16,))(x)\n        # now the model will take as input arrays of shape (*, 16)\n        # and output arrays of shape (*, 32)\n\n    Args:\n        units: Positive integer, dimensionality of the output space.\n        activation: Activation function to use\n            If you don't specify anything, no activation is applied\n            (ie. \"linear\" activation: `a(x) = x`).\n        use_bias: Boolean, whether the layer uses a bias vector.\n        kernel_initializer: Initializer for the `kernel` weights matrix\n        bias_initializer: Initializer for the bias vector\n        allow_kernel_norm: T/F\n            kernel normalization is only applicable when TRAINING\n        kernel_normalization_trainable:\n            If True, a trainable weight norm variable is allocated\n\n    Input shapes:\n        nD tensor with shape: `(batch_size, ..., input_dim)`.\n        The most common situation would be\n        a 2D input with shape `(batch_size, input_dim)`.\n\n    Output shapes:\n        nD tensor with shape: `(batch_size, ..., units)`.\n        For instance, for a 2D input with shape `(batch_size, input_dim)`,\n        the output would have shape `(batch_size, units)`.\n    \"\"\"\n\n  def __init__(self,\n               slot_names,\n               embedding_size_choices_list,\n               warmup_steps,\n               pretraining_steps,\n               teacher_embedding_sizes_list=None,\n               distillation_mask=False,\n               **kwargs):\n    super(MixedEmbedOpComb, self).__init__(**kwargs)\n    print(len(slot_names))\n    print(len(embedding_size_choices_list))\n    assert len(slot_names) == len(embedding_size_choices_list)\n    self._slot_names = slot_names\n    self._embedding_size_choices_list = embedding_size_choices_list\n    self._num_choices_per_embedding = []\n    self._max_choice_per_embedding = []\n    self._max_num_choices = 0\n    self._total_emb_size = 0\n    self._warmup_steps = warmup_steps\n    self._pretraining_steps = pretraining_steps\n    for embedding_size_choices in embedding_size_choices_list:\n      self._num_choices_per_embedding.append(len(embedding_size_choices))\n      self._max_num_choices = max(self._max_num_choices,\n                                  len(embedding_size_choices))\n      self._max_choice_per_embedding.append(sum(embedding_size_choices))\n      self._total_emb_size += self._max_choice_per_embedding[-1]\n    self._teacher_embedding_sizes_list = None\n\n    self._arch_embedding_weights_multipler = None\n    self._arch_embedding_weights = None\n\n    # allowed input specification\n    if self._teacher_embedding_sizes_list is not None:\n      self.input_spec = [InputSpec(ndim=2), InputSpec(ndim=2)]\n    else:\n      self.input_spec = InputSpec(ndim=2)\n\n    self._distillation_mask = distillation_mask\n\n  def build(self, input_shape):\n    assert len(input_shape) == 2\n    if self._teacher_embedding_sizes_list is not None:\n      assert len(input_shape[0]) == 2 and len(input_shape[1]) == 2\n      input_dim = input_shape[0][-1]\n      assert input_shape[1][-1] == sum(self._teacher_embedding_sizes_list)\n    else:\n      input_dim = input_shape[-1]\n    print(input_dim)\n    print(self._total_emb_size)\n    assert input_dim == self._total_emb_size\n\n    # kernel\n    self._arch_embedding_weights = self.add_weight(\n        shape=(sum(self._num_choices_per_embedding),),\n        initializer=tf.random_uniform_initializer(minval=-1e-3, maxval=1e-3),\n        trainable=True,\n        name='arch_embedding_weights')\n    print(\"arch embedding weights: {}\".format(self._arch_embedding_weights))\n\n    current_idx = 0\n    arch_embedding_masks_list = []\n    arch_embedding_weights_multiplier_list = []\n    arch_entropy_list = []\n    expected_emb_dims_list = []\n    expected_zero_embedding_size_weights_list = []\n    arch_embedding_weights_after_softmax_list = []\n\n    for i in range(len(self._slot_names)):\n      num_choices = self._num_choices_per_embedding[i]\n      max_emb_choice = sum(self._embedding_size_choices_list[i])\n      arch_embedding_weights_slice = self._arch_embedding_weights[\n          current_idx:current_idx + num_choices]\n      arch_embedding_weights_after_softmax = tf.nn.softmax(\n          arch_embedding_weights_slice)  #softmax selection\n      #arch_embedding_weights_after_softmax = tf.math.sigmoid(arch_embedding_weights_slice) #sigmoid selection like FairNAS\n      arch_entropy_list.append(\n          tf.compat.v1.nn.softmax_cross_entropy_with_logits_v2(\n              labels=arch_embedding_weights_after_softmax,\n              logits=arch_embedding_weights_slice))\n      expected_emb_dims_list.append(\n          tf.reduce_sum(arch_embedding_weights_after_softmax *\n                        self._embedding_size_choices_list[i]))\n\n      if self._embedding_size_choices_list[i][0] == 0:\n        expected_zero_embedding_size_weights_list.append(\n            arch_embedding_weights_after_softmax[0])\n        arch_embedding_weights_after_softmax = tf.concat([\n            arch_embedding_weights_after_softmax[0:1] * tf.cast(\n                tf.minimum(\n                    tf.maximum(\n                        tf.compat.v1.train.get_global_step() -\n                        self._warmup_steps, 0.0), 1.0), self.dtype),\n            arch_embedding_weights_after_softmax[1:]\n        ],\n                                                         axis=0)\n      embedding_masks = []\n      lower = 0\n      upper = 0\n      for j, embedding_size_choice in enumerate(\n          self._embedding_size_choices_list[i]):\n        name = 'arch_embedding_weights_after_softmax/{}_{}'.format(\n            self._slot_names[i], embedding_size_choice)\n        data = arch_embedding_weights_after_softmax[j]\n        arch_embedding_weights_after_softmax_list.append((name, data))\n        upper += embedding_size_choice\n        mask = tf.constant([\n            1.0 if jj < upper and jj >= lower else 0.0\n            for jj in range(max_emb_choice)\n        ],\n                           dtype=self.dtype)\n        #mask = tf.constant([\n        #    1.0 / embedding_size_choice if jj < upper and jj >= lower else 0.0\n        #    for jj in range(max_emb_choice)\n        #],\n        #                   dtype=self.dtype) # Balance the gradient of each slot choices\n        lower += embedding_size_choice\n        embedding_masks.append(mask)\n      # [self._max_num_choices, max_emb_choice]\n      embedding_mask = tf.pad(\n          tf.stack(embedding_masks,\n                   0), [[0, self._max_num_choices - num_choices], [0, 0]])\n      #print('embedding_mask: {}'.format(tf.keras.backend.eval(embedding_mask)))\n      # [self._max_num_choices]\n      arch_embedding_weights_after_softmax_per_slot_padded = tf.pad(\n          arch_embedding_weights_after_softmax,\n          [[0, self._max_num_choices - num_choices]])\n      probability = tf.where(\n          tf.cast(tf.compat.v1.train.get_or_create_global_step(), tf.float32) <\n          tf.cast(self._pretraining_steps, tf.float32),\n          [0.5, 0.5],  #[0.25, 0.25, 0.25, 0.25]\n          [\n              arch_embedding_weights_after_softmax_per_slot_padded[i]\n              for i in range(self._max_num_choices)\n          ])\n      # [max_emb_choice] # method 1: Sampling-based nws\n      indices = tf.random.categorical(\n          tf.math.log(tf.expand_dims(probability, 0)), 1)\n      index = tf.reduce_sum(indices)\n      index_one_hot = tf.one_hot(index, self._max_num_choices)\n      embedding_masks_selected = tf.reduce_sum(\n          embedding_masks * tf.expand_dims(index_one_hot, -1), 0)\n      arch_embedding_weights_after_softmax_per_slot_padded_chosen = tf.reduce_sum(\n          arch_embedding_weights_after_softmax_per_slot_padded * index_one_hot,\n          0)\n      arch_embedding_masks_list.append(\n          embedding_masks_selected *\n          (1 + arch_embedding_weights_after_softmax_per_slot_padded_chosen -\n           tf.stop_gradient(\n               arch_embedding_weights_after_softmax_per_slot_padded_chosen)))\n\n      # [max_emb_choice] # method 2: MixedOp-based nws\n      #arch_embedding_masks_list.append(embedding_mask)\n      #arch_embedding_weights_multiplier_list.append(\n      #    tf.broadcast_to(\n      #        tf.expand_dims(\n      #            probability,\n      #            -1), tf.shape(embedding_mask)\n      #            )\n      #           )\n      current_idx += num_choices\n\n    # [total_emb_dims]\n    self._arch_embedding_masks_multipler = tf.concat(\n        arch_embedding_masks_list, 0)  # method 1: Sampling-based nws\n    #self._arch_embedding_masks_multipler = tf.concat(\n    #    arch_embedding_masks_list, 1) #method 2: MixedOp-based nws\n    #self._arch_embedding_weights_multipler = tf.concat(\n    #    arch_embedding_weights_multiplier_list, 1)\n    self._arch_entropy = tf.add_n(arch_entropy_list)\n    self._expected_emb_dims = tf.add_n(expected_emb_dims_list)\n    self._expected_zero_embedding_size_weights = tf.add_n(\n        expected_zero_embedding_size_weights_list\n    ) if expected_zero_embedding_size_weights_list else 0\n    self._arch_embedding_weights_after_softmax_list = arch_embedding_weights_after_softmax_list\n    self.built = True\n\n  def call(self, inputs):\n    if self._teacher_embedding_sizes_list is not None:\n      embedding = inputs[0]\n      teacher_embedding = inputs[1]\n    else:\n      embedding = inputs\n\n    # [batch_size, total_emb_dims]\n    masked_embedding = embedding * self._arch_embedding_masks_multipler  # method 1: Sampling-based nws\n    #masked_embedding = tf.expand_dims(\n    #    embedding, 1) * self._arch_embedding_masks_multipler # method 2: MixedOp-based nws\n    #mixed_embedding = tf.reduce_sum(\n    #    masked_embedding * self._arch_embedding_weights_multipler, 1)\n\n    if self._teacher_embedding_sizes_list is not None:\n      print(\"TeacherEmbeddingTransform: {} {}\".format(\n          self._max_choice_per_embedding, self._teacher_embedding_sizes_list))\n      teacher_embedding_transform = TeacherEmbeddingTransform(\n          self._max_choice_per_embedding,\n          self._teacher_embedding_sizes_list,\n          dtype=self.dtype)\n      teacher_embedding_transformed = teacher_embedding_transform(\n          teacher_embedding)\n      # [batch_size, total_emb_dims] -> [batch_size, 1, total_emb_dims]\n      # -> [batch_size, self._max_num_choices, total_emb_dims]\n      if not self._distillation_mask:\n        distillation_loss = tf.losses.mean_squared_error(\n            tf.broadcast_to(tf.expand_dims(teacher_embedding_transformed, 1),\n                            tf.shape(masked_embedding)), masked_embedding)\n      else:\n        masked_teacher_embedding_transformed = tf.expand_dims(\n            teacher_embedding_transformed,\n            1) * self._arch_embedding_masks_multipler\n        distillation_loss = tf.losses.mean_squared_error(\n            masked_teacher_embedding_transformed, masked_embedding)\n      return mixed_embedding, distillation_loss, teacher_embedding_transform.name\n    else:\n      return masked_embedding  # method 1: Sampling-based nws\n      #return mixed_embedding # method 2: MixedOp-based nws\n\n  def compute_output_shape(self, input_shape):\n    raise NotImplementedError(\"I don't think I need to implement this one.\")\n\n  def get_config(self):\n    config = {\n        'slot_names': self._slot_names,\n        'embedding_size_choices_list': self._embedding_size_choices_list,\n        'warmup_steps': self._warmup_steps,\n        'teacher_embedding_sizes_list': self._teacher_embedding_sizes_list,\n    }\n    base_config = super(MixedEmbedOpComb, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n  def get_arch_embedding_weights(self):\n    return self._arch_embedding_weights\n\n  def get_summaries(self):\n    return {\n        'arch_entropy':\n            self._arch_entropy,\n        'expected_emb_dims':\n            self._expected_emb_dims,\n        'arch_weights_after_softmax_list':\n            self._arch_embedding_weights_after_softmax_list,\n    }\n"
  },
  {
    "path": "monolith/core/model.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Check in TPU embedding feature from TensorFlow.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport functools\nimport math\n\nfrom absl import logging\nimport tensorflow as tf\nfrom tensorflow.python.tpu import tpu_embedding\n\nfrom monolith.core.feature import FeatureSlot, FeatureColumnV1, Env\n\n_FLOAT32_BYTES = 4\n\n\n### This code will be deprecated. Please update and usebase_embedding_task.py.\nclass Model(object):\n  \"\"\"Sail-like TPU Model.\"\"\"\n\n  def __init__(self, params):\n    self._params = params\n    self._vocab_size_per_slot = params[\"vocab_size_per_slot\"]\n    if self._vocab_size_per_slot is not None:\n      logging.info(\"Set fixed vocab_size: {} for all the slots.\".format(\n          self._vocab_size_per_slot))\n    vocab_size_dict = self._create_vocab_dict(params[\"vocab_file_path\"],\n                                              self._vocab_size_per_slot)\n    self._env = Env(vocab_size_dict=vocab_size_dict)\n    # Run this to initialize slot, embedding dim information\n    self.init_slot_to_dims()\n\n  def _create_vocab_dict(self, file_path, vocab_size_per_slot=None):\n    \"\"\"Create vocab dict from a tsv file.\n\n        Args:\n            file_path: the path to the vocab dict\n            vocab_size_per_slot: If None, this is set to the number of unique\n                FIDs for each slot, obtained from the vocab_size file.\n                Otherwise, this value is used to manually set the vocab sise per\n                slot (this option is to speed up testing and modeling iteration).\n\n        \"\"\"\n    vocab_size_dict = {}\n    with open(file_path) as f:\n      for line in f:\n        fields = line.strip().split(\"\\t\")\n        assert len(fields) == 2, \"each line in {} must have 2 fields\".format(\n            fields)\n        if fields[0].isdigit() == False:\n          continue\n\n        slot_id = int(fields[0])\n\n        distinct_count = vocab_size_per_slot\n        if vocab_size_per_slot is None:\n          distinct_count = int(fields[1])\n\n        vocab_size_dict[slot_id] = distinct_count\n\n    return vocab_size_dict\n\n  def _get_feature_map(self):\n    \"\"\"Returns data format of the serialized tf record file.\"\"\"\n    # Inherated class must implement this function.\n    raise NotImplementedError\n\n  def _post_process_example(self, example):\n    \"\"\"Postprocess example.\"\"\"\n    # build tensors for each embeddings in each slot\n    for slot_id, dims in self._env.slot_to_dims.items():\n      # If the vocab size per slot is set, we need to adjust the\n      # vocab_id so that no vocab_id exceed this vocab size per slot\n      if self._vocab_size_per_slot:\n        embedding_tensor = example[\"slot_{}_0\".format(slot_id)]\n        new_embedding_tensor = tf.SparseTensor(\n            indices=embedding_tensor.indices,\n            values=tf.math.mod(embedding_tensor.values,\n                               self._vocab_size_per_slot),\n            dense_shape=embedding_tensor.dense_shape)\n        example[\"slot_{}_0\".format(slot_id)] = new_embedding_tensor\n\n      for i in range(1, len(dims)):\n        example[\"slot_{}_{}\".format(slot_id,\n                                    i)] = example[\"slot_{}_0\".format(slot_id)]\n    return example\n\n  def create_input_fn(self, file_pattern, repeat=True):\n\n    def tf_example_parser(examples):\n      \"\"\"Parse multiple examples.\"\"\"\n      feature_map = self._get_feature_map()\n      example = tf.io.parse_example(serialized=examples, features=feature_map)\n      return self._post_process_example(example)\n\n    def input_fn(params):\n      \"\"\"Returns training or eval examples, batched as specified in params.\"\"\"\n      logging.info(\"Model input_fn\")\n\n      # By shuffle=False, list_files will get all files already in time sorted order.\n      files = tf.data.Dataset.list_files(file_pattern, shuffle=False)\n      # This function will get called once per TPU task. Each task will process the files\n      # with indexs which modulo num_calls equals to call_index.\n      _, call_index, num_calls, _ = (\n          params[\"context\"].current_input_fn_deployment())\n      files = files.shard(num_calls, call_index)\n\n      skip_files_number = 0\n      if params[\"shard_skip_file_number\"] is not None:\n        skip_files_number = params[\"shard_skip_file_number\"][call_index]\n\n      logging.info(\"Shard {} skipped {} files.\".format(call_index,\n                                                       skip_files_number))\n      files = files.skip(skip_files_number)\n\n      def fetch_dataset(filename):\n        dataset = tf.data.TFRecordDataset(\n            filename,\n            compression_type=params[\"compression_type\"],\n            buffer_size=None)\n        return dataset\n\n      # Read the data from disk in parallel.\n      # Files will be process from the beginning to the end. With a local parallel of interleaving\n      # multiple files currently. Number of interleaving files are defined by the cycle.\n      dataset = files.interleave(\n          fetch_dataset,\n          cycle_length=params[\"cycle_length\"],\n          num_parallel_calls=params[\"num_parallel_calls\"],\n          deterministic=False)\n      dataset = dataset.batch(params[\"batch_size\"], drop_remainder=True).map(\n          tf_example_parser,\n          num_parallel_calls=tf.data.experimental.AUTOTUNE,\n          deterministic=False)\n\n      # The tensors returned from this dataset will be directly used as the ids\n      # for the embedding lookup. If you want to have a separate vocab, apply a\n      # '.map' here to the dataset which contains you vocab lookup.\n      if repeat:\n        dataset = dataset.repeat()\n\n      dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)\n\n      return dataset\n\n    return input_fn\n\n  def _padding_8(self, dim):\n    return math.ceil(dim / 8) * 8\n\n  def _get_slot_number(self, optimizer, use_gradient_accumulation):\n    slot_num = 0\n    if isinstance(optimizer, tf.compat.v1.tpu.experimental.FtrlParameters):\n      slot_num = 3\n    elif isinstance(optimizer, tf.compat.v1.tpu.experimental.AdagradParameters):\n      slot_num = 2\n    elif isinstance(optimizer, tf.compat.v1.tpu.experimental.AdamParameters):\n      slot_num = 3\n    elif isinstance(\n        optimizer,\n        tf.compat.v1.tpu.experimental.StochasticGradientDescentParameters):\n      slot_num = 1\n    else:\n      assert (\"We don't support this optimizer type yet: {}\".format(\n          type(optimizer)))\n\n    if use_gradient_accumulation == True:\n      slot_num += 1\n\n    return slot_num\n\n  def _get_max_slot_number(self):\n    max_slot_number = 0\n    for slot_id, dims in self._env.slot_to_dims.items():\n      feature_slot = self._env.slot_to_config[slot_id]\n      for index, dim in enumerate(dims):\n        optimizer = None\n        # If index is 0 and feature slot uses bias, then we will use bias optimizer and table initializer.\n        # Also please note if slot has bias, bias will always use index = 0.\n        if index == 0 and feature_slot.use_bias:\n          optimizer = feature_slot.bias_optimizer\n        else:\n          optimizer = feature_slot.vec_optimizer\n        max_slot_number = max(\n            max_slot_number,\n            self._get_slot_number(optimizer,\n                                  self._params[\"use_gradient_accumulation\"]))\n    return max_slot_number\n\n  def create_feature_and_table_config_dict(self):\n    \"\"\"Prepares the table and feature config given the parameters.\"\"\"\n    env = self._env\n    assert env.is_finalized()\n    feature_to_config_dict = {}\n    table_to_config_dict = {}\n\n    embedding_table_size = 0\n    embedding_table_size_after_padding_8 = 0\n    embedding_table_size_after_padding_8_and_use_max_auxiliary_parameters = 0\n    max_slot_number = self._get_max_slot_number()\n\n    for slot_id, dims in env.slot_to_dims.items():\n      assert slot_id in env.vocab_size_dict, \"slot_id {} must be in vocab file\".format(\n          slot_id)\n      vocab_size = env.vocab_size_dict[slot_id]\n\n      assert slot_id in env.slot_to_config, \"slot_id {} must be in slot_to_config\".format(\n          slot_id)\n      feature_slot = env.slot_to_config[slot_id]\n\n      for index, dim in enumerate(dims):\n        optimizer = None\n        table_initializer = None\n\n        # If index is 0 and feature slot uses bias, then we will use bias optimizer and table initializer.\n        # Also please note if slot has bias, bias will always use index = 0.\n        if index == 0 and feature_slot.use_bias():\n          optimizer = feature_slot.bias_optimizer\n          table_initializer = feature_slot.bias_initializer\n        else:\n          optimizer = feature_slot.vec_optimizer\n          table_initializer = feature_slot.vec_initializer\n\n        table = tpu_embedding.TableConfig(vocabulary_size=vocab_size,\n                                          dimension=dim,\n                                          initializer=table_initializer,\n                                          combiner=\"sum\",\n                                          optimization_parameters=optimizer)\n        table_name = \"table_{}_{}\".format(slot_id, index)\n        table_to_config_dict[table_name] = table\n        feature_name = \"slot_{}_{}\".format(slot_id, index)\n        feature_to_config_dict[feature_name] = tpu_embedding.FeatureConfig(\n            table_name)\n\n        slot_num = self._get_slot_number(\n            optimizer, self._params[\"use_gradient_accumulation\"])\n        embedding_table_size += vocab_size * dim * _FLOAT32_BYTES * slot_num\n        embedding_table_size_after_padding_8 += vocab_size * self._padding_8(\n            dim) * _FLOAT32_BYTES * slot_num\n        embedding_table_size_after_padding_8_and_use_max_auxiliary_parameters += vocab_size * self._padding_8(\n            dim) * _FLOAT32_BYTES * max_slot_number\n\n    logging.info(\"Size of all embedding tables in bytes: {}\".format(\n        embedding_table_size))\n    logging.info(\n        \"Size after padding the width of all tables to 8 float multiples in bytes: {}\"\n        .format(embedding_table_size_after_padding_8))\n    logging.info(\n        \"Size after padding to 8 float multiples and using max auxiliary parameters: {}\"\n        .format(\n            embedding_table_size_after_padding_8_and_use_max_auxiliary_parameters\n        ))\n\n    return feature_to_config_dict, table_to_config_dict\n\n  def sum_pooling(self,\n                  fc_dict,\n                  input_map,\n                  features,\n                  dim,\n                  total_embeddings,\n                  add_into_embeddings=True):\n    slot_embeddings = []\n    dims = 0\n    for slot in features:\n      #allocate embedding\n      embedding = fc_dict[slot].add_vector(dim)\n      dims += dim\n      if add_into_embeddings:\n        total_embeddings.append((embedding, dim))\n      slot_embeddings.append(embedding)\n      if slot in input_map:\n        input_slots = input_map.keys()\n        c = 0\n        for item in input_slots:\n          if isinstance(item, str):\n            if str(slot) + '_' in item:\n              c += 1\n          if isinstance(item, int):\n            if item == slot:\n              c += 1\n        input_map[str(slot) + '_' + str(c)] = embedding\n      else:\n        input_map[slot] = embedding\n    if len(features) == 1:  #单特征无需sum\n      return slot_embeddings[0]\n    return tf.add_n(slot_embeddings)\n\n  def logits_fn(self):\n    \"\"\"Calculate logits.\"\"\"\n    # Inherated class must implement this function.\n    raise NotImplementedError\n\n  def init_slot_to_dims(self):\n    \"\"\"Run this in the beginning to initialize the slot and its embedding dims information.\"\"\"\n    logging.info(\"Model init_slot_to_dims\")\n    self.logits_fn()\n    self._env.finalize()\n    logging.info(\"_slot_to_dims: {}\".format(self._env.slot_to_dims))\n\n  def create_model_fn(self):\n    \"\"\"Creates the model_fn to be used by the TPUEstimator.\"\"\"\n    # Inherated class must implement this function.\n    raise NotImplementedError\n"
  },
  {
    "path": "monolith/core/model_imports.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport importlib\nimport sys\n\nfrom absl import logging\n\n\ndef _Import(name):\n  \"\"\"Imports the python module of the given name.\"\"\"\n  logging.info('Attempt to import {} ...'.format(name))\n  try:\n    importlib.import_module(name)\n    logging.info('Imported {}'.format(name))\n    return True\n  except ImportError as e:\n    # It is expected that some imports may be missing.\n    logging.error('Could not import: {}\\n'.format(e))\n  return False\n\n\n_ROOT = 'monolith.tasks'\n\n_DIRS = ()\n\n\ndef ImportAllParams(task_root=_ROOT, task_dirs=_DIRS, require_success=False):\n  \"\"\"Import all ModelParams to add to the global registry.\"\"\"\n  success = False\n  for task in task_dirs:\n    # By our code repository convention, there is a params.py under the task's\n    # params directory. params.py imports _all_ modules that may registers a\n    # model param.\n\n    module_str = '{}.{}.params.{}'.format(task_root, task, path)\n    success = _Import('{}.{}.params.params'.format(task_root, task)) or success\n  if require_success and not success:\n    raise LookupError('Could not import any task params. Make sure task params '\n                      'are linked into the binary.')\n  return success\n\n\ndef ImportParams(model_name,\n                 task_root=_ROOT,\n                 task_dirs=_DIRS,\n                 require_success=True):\n  \"\"\"Attempts to only import the files that may contain the model.\"\"\"\n  # 'model_name' follows <task>.<path>.<class name>\n  if '.' not in model_name:\n    raise ValueError('Invalid model name %s' % model_name)\n  model_module = model_name.rpartition('.')[0]\n  logging.info(\"model_module:{}\".format(model_module))\n\n  # Try importing the module directly, in case it's a local import.\n  logging.info(\"Searching local import ...\")\n  success = _Import(model_module)\n\n  # Try built-in tasks imports.\n  logging.info(\"Searching built-in tasks ...\")\n  for task in sorted(task_dirs):\n    logging.info('{} || {}'.format(task, model_module))\n    if model_module.startswith(task + '.'):\n      logging.info(\"Found built-in task: {}\".format(task))\n      path = model_module[len(task) + 1:]\n      module_str = '{}.{}.params.{}'.format(task_root, task, path)\n      success = _Import(module_str) or success\n\n  if require_success and not success:\n    raise LookupError(\n        'Could not find any valid import paths for module %s. Check the logs '\n        'above to see if there were errors importing the module, and make sure '\n        'the relevant params files are linked into the binary.' % model_module)\n  return success\n"
  },
  {
    "path": "monolith/core/model_registry.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport traceback\nimport inspect\nimport sys\n\nimport tensorflow as tf\nfrom absl import logging\n\nfrom monolith.core import model_imports\nfrom monolith.core.base_model_params import SingleTaskModelParams\n\n\nclass _ModelRegistryHelper(object):\n  # Global dictionary mapping subclass name to registered ModelParam subclass.\n  _MODEL_PARAMS = {}\n  # Global set of modules from which ModelParam subclasses have been registered.\n  _REGISTERED_MODULES = set()\n\n  @classmethod\n  def _ClassPathPrefix(cls):\n    return 'monolith.tasks.'\n\n  @classmethod\n  def _ModelParamsClassKey(cls, src_cls, shortcut=False):\n    \"\"\"Returns a string key used for `src_cls` in the model registry.\n    Args:\n      src_cls: A subclass of `BaseModel`.\n      shortcut: (Deprecated) generate shortcut version of given task.\n    \"\"\"\n    path = src_cls.__module__\n    if shortcut:\n      # Removes the prefix.\n      path_prefix = cls._ClassPathPrefix()\n      path = path.replace(path_prefix, '')\n      # Removes 'params.' if exists.\n      if 'params.' in path:\n        path = path.replace('params.', '')\n    return '{}.{}'.format(path, src_cls.__name__)\n\n  @classmethod\n  def _GetSourceInfo(cls, src_cls):\n    \"\"\"Gets a source info string given a source class.\"\"\"\n    return '%s@%s:%d' % (cls._ModelParamsClassKey(src_cls),\n                         inspect.getsourcefile(src_cls),\n                         inspect.getsourcelines(src_cls)[-1])\n\n  @classmethod\n  def _RegisterModel(cls, src_cls):\n    \"\"\"Registers a ModelParams subclass in the global registry.\"\"\"\n    for key in set([\n        cls._ModelParamsClassKey(src_cls, shortcut=False),\n        cls._ModelParamsClassKey(src_cls, shortcut=True)\n    ]):\n      module = src_cls.__module__\n      if key in cls._MODEL_PARAMS:\n        raise ValueError('Duplicate model registered for key {}: {}.{}'.format(\n            key, module, src_cls.__name__))\n\n      logging.debug('Registering model %s', key)\n      # Log less frequently (once per module) but at a higher verbosity level.\n      if module not in cls._REGISTERED_MODULES:\n        logging.info('Registering models from module: %s', module)\n        cls._REGISTERED_MODULES.add(module)\n\n      # Decorate param methods to add source info metadata.\n      cls._MODEL_PARAMS[key] = src_cls\n    return cls._ModelParamsClassKey(src_cls, shortcut=False)\n\n  @classmethod\n  def RegisterSingleTaskModel(cls, src_cls):\n    \"\"\"Class decorator that registers a `.SingleTaskModelParams` subclass.\"\"\"\n    logging.info(\"Register {} Start\".format(src_cls.__name__))\n    if not issubclass(src_cls, SingleTaskModelParams):\n      raise TypeError('src_cls %s is not a SingleTaskModelParams!' %\n                      src_cls.__name__)\n\n    cls._RegisterModel(src_cls)\n    all_params = _ModelRegistryHelper._MODEL_PARAMS\n    logging.info(\"Register {} successfully\".format(src_cls.__name__))\n    return src_cls\n\n  @staticmethod\n  def GetAllRegisteredClasses():\n    \"\"\"Returns global registry map from model names to their param classes.\"\"\"\n    all_params = _ModelRegistryHelper._MODEL_PARAMS\n    if not all_params:\n      logging.warning('No classes registered.')\n    return all_params\n\n  @classmethod\n  def GetClass(cls, class_key):\n    \"\"\"Returns a ModelParams subclass with the given `class_key`.\n\n    Args:\n      class_key: string key of the ModelParams subclass to return.\n\n    Returns:\n      A subclass of `SingleTaskModelParams`.\n\n    Raises:\n      LookupError: If no class with the given key has been registered.\n    \"\"\"\n    all_params = cls.GetAllRegisteredClasses()\n    if class_key not in all_params:\n      for k in sorted(all_params):\n        logging.info('Known model: %s', k)\n      raise LookupError('Model %s not found from list of above known models.' %\n                        class_key)\n    return all_params[class_key]\n\n  @classmethod\n  def GetParams(cls, class_key):\n    \"\"\"Constructs a `Params` object for given model.\n\n    Args:\n      class_key: String class key.\n\n    Returns:\n      Full `~.hyperparams.Params` for the model class.\n    \"\"\"\n    model_params_cls = cls.GetClass(class_key)\n    model_params = model_params_cls()\n    cfg = model_params.task()\n    return cfg\n\n\nRegisterSingleTaskModel = _ModelRegistryHelper.RegisterSingleTaskModel\n\n\ndef GetAllRegisteredClasses():\n  model_imports.ImportAllParams()\n  return _ModelRegistryHelper.GetAllRegisteredClasses()\n\n\ndef GetClass(class_key):\n  model_imports.ImportParams(class_key)\n  return _ModelRegistryHelper.GetClass(class_key)\n\n\ndef GetParams(class_key):\n  model_imports.ImportParams(class_key)\n  return _ModelRegistryHelper.GetParams(class_key)\n"
  },
  {
    "path": "monolith/core/optimizers.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom tensorflow.compat.v1.train import AdagradOptimizer\nfrom tensorflow.compat.v1.train import MomentumOptimizer\nfrom tensorflow.compat.v1.train import RMSPropOptimizer\nfrom tensorflow.compat.v1.train import AdamOptimizer\n\noptimizers = {\n    'adagrad': AdagradOptimizer,\n    'momentum': MomentumOptimizer,\n    'rmsprop': RMSPropOptimizer,\n    'adam': AdamOptimizer,\n}\n"
  },
  {
    "path": "monolith/core/py_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\"\"\"Common utilities.\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport numpy as np\nimport re\nimport six\n\n_NAME_PATTERN = re.compile('[A-Za-z_][A-Za-z0-9_]*')\n\n\nclass NestedMap(dict):\n  \"\"\"A simple helper to maintain a dict.\n  It is a sub-class of dict with the following extensions/restrictions:\n    - It supports attr access to its members (see examples below).\n    - Member keys have to be valid identifiers.\n  E.g.::\n      >>> foo = NestedMap()\n      >>> foo['x'] = 10\n      >>> foo.y = 20\n      >>> assert foo.x * 2 == foo.y\n  \"\"\"\n\n  # Disable pytype attribute checking.\n  _HAS_DYNAMIC_ATTRIBUTES = True\n  # keys in this list are not allowed in a NestedMap.\n  _RESERVED_KEYS = set(dir(dict))\n  # sentinel value for deleting keys used in Filter.\n  _DELETE = object()\n\n  def __init__(self, *args, **kwargs):\n    super(NestedMap, self).__init__(*args, **kwargs)\n    for key in self.keys():\n      assert isinstance(key, six.string_types), (\n          'Key in a NestedMap has to be a six.string_types. Currently type: %s,'\n          ' value: %s' % (str(type(key)), str(key)))\n      NestedMap.CheckKey(key)\n      assert key not in NestedMap._RESERVED_KEYS, ('%s is a reserved key' % key)\n\n  def __setitem__(self, key, value):\n    # Make sure key is a valid expression and is not one of the reserved\n    # attributes.\n    assert isinstance(key, six.string_types), (\n        'Key in a NestedMap has to be a six.string_types. Currently type: %s, '\n        'value: %s' % (str(type(key)), str(key)))\n    NestedMap.CheckKey(key)\n    assert key not in NestedMap._RESERVED_KEYS, ('%s is a reserved key' % key)\n    super(NestedMap, self).__setitem__(key, value)\n\n  def __setattr__(self, name, value):\n    self.__setitem__(name, value)\n\n  def __getattr__(self, name):\n    try:\n      return self[name]\n    except KeyError as e:\n      raise AttributeError('%s; available attributes: %s' %\n                           (e, sorted(list(self.keys()))))\n\n  def __delattr__(self, name):\n    try:\n      del self[name]\n    except KeyError as e:\n      raise AttributeError('%s; available attributes: %s' %\n                           (e, sorted(list(self.keys()))))\n\n  def copy(self):  # Don't delegate w/ super: dict.copy() -> dict.\n    return NestedMap(self)\n\n  def __deepcopy__(self, unused_memo):\n    \"\"\"Deep-copies the structure but not the leaf objects.\"\"\"\n    return self.DeepCopy()\n\n  def DeepCopy(self):\n    \"\"\"Deep-copies the structure but not the leaf objects.\"\"\"\n    return self.Pack(self.Flatten())\n\n  @staticmethod\n  def FromNestedDict(x):\n    \"\"\"Converts every dict in nested structure 'x' to a NestedMap.\"\"\"\n    if isinstance(x, dict):\n      res = NestedMap()\n      for k, v in six.iteritems(x):\n        res[k] = NestedMap.FromNestedDict(v)\n      return res\n    elif isinstance(x, (list, tuple)):\n      return type(x)(NestedMap.FromNestedDict(v) for v in x)\n    else:\n      return x\n\n  @staticmethod\n  def CheckKey(key):\n    \"\"\"Asserts that key is valid NestedMap key.\"\"\"\n    if not (isinstance(key, six.string_types) and _NAME_PATTERN.match(key)):\n      raise ValueError('Invalid NestedMap key \\'{}\\''.format(key))\n\n  def GetItem(self, key):\n    \"\"\"Gets the value for the nested `key`.\n    Note that indexing lists is not supported, names with underscores will be\n    considered as one key.\n    Args:\n      key: str of the form\n        `([A-Za-z_][A-Za-z0-9_]*)(.[A-Za-z_][A-Za-z0-9_]*)*.`.\n    Returns:\n      The value for the given nested key.\n    Raises:\n      KeyError if a key is not present.\n    \"\"\"\n    current = self\n    # Note: This can't support lists. List keys are ambiguous as underscore is\n    # not reserved for list indexing but also allowed to be used in keys.\n    # E.g., this is a valid nested map where the key 'a_0' is not well defined\n    # {'a_0': 3, 'a': [4]}.\n    for k in key.split('.'):\n      current = current[k]\n    return current\n\n  def Get(self, key, default=None):\n    \"\"\"Gets the value for nested `key`, returns `default` if key does not exist.\n    Note that indexing lists is not supported, names with underscores will be\n    considered as one key.\n    Args:\n      key: str of the form\n        `([A-Za-z_][A-Za-z0-9_]*)(.[A-Za-z_][A-Za-z0-9_]*)*.`.\n      default: Optional default value, defaults to None.\n    Returns:\n      The value for the given nested key or `default` if the key does not exist.\n    \"\"\"\n    try:\n      return self.GetItem(key)\n    # TypeError is raised when an intermediate item is a list and we try to\n    # access an element of it with a string.\n    except (KeyError, TypeError):\n      return default\n\n  def Set(self, key, value):\n    \"\"\"Sets the value for a nested key.\n    Note that indexing lists is not supported, names with underscores will be\n    considered as one key.\n    Args:\n      key: str of the form\n        `([A-Za-z_][A-Za-z0-9_]*)(.[A-Za-z_][A-Za-z0-9_]*)*.`.\n      value: The value to insert.\n    Raises:\n      ValueError if a sub key is not a NestedMap or dict.\n    \"\"\"\n    current = self\n    sub_keys = key.split('.')\n    for i, k in enumerate(sub_keys):\n      self.CheckKey(k)\n      # We have reached the terminal node, set the value.\n      if i == (len(sub_keys) - 1):\n        current[k] = value\n      else:\n        if k not in current:\n          current[k] = NestedMap()\n        if not isinstance(current[k], (dict, NestedMap)):\n          raise ValueError('Error while setting key {}. Sub key \"{}\" is of type'\n                           ' {} but must be a dict or NestedMap.'\n                           ''.format(key, k, type(current[k])))\n        current = current[k]\n\n  def _RecursiveMap(self, fn, flatten=False):\n    \"\"\"Traverse recursively into lists and NestedMaps applying `fn`.\n    Args:\n      fn: The function to apply to each item (leaf node).\n      flatten: If true, the result should be a single flat list. Otherwise the\n        result will have the same structure as this NestedMap.\n    Returns:\n      The result of applying fn.\n    \"\"\"\n\n    def Recurse(v, key=''):\n      \"\"\"Helper function for _RecursiveMap.\"\"\"\n      if isinstance(v, NestedMap):\n        ret = [] if flatten else NestedMap()\n        deleted = False\n        for k in sorted(v.keys()):\n          res = Recurse(v[k], key + '.' + k if key else k)\n          if res is self._DELETE:\n            deleted = True\n            continue\n          elif flatten:\n            ret += res\n          else:\n            ret[k] = res\n        if not ret and deleted:\n          return self._DELETE\n        return ret\n      elif isinstance(v, list):\n        ret = []\n        deleted = False\n        for i, x in enumerate(v):\n          res = Recurse(x, '%s[%d]' % (key, i))\n          if res is self._DELETE:\n            deleted = True\n            continue\n          elif flatten:\n            ret += res\n          else:\n            ret.append(res)\n        if not ret and deleted:\n          return self._DELETE\n        return ret\n      else:\n        ret = fn(key, v)\n        if flatten:\n          ret = [ret]\n        return ret\n\n    res = Recurse(self)\n    if res is self._DELETE:\n      return [] if flatten else NestedMap()\n    return res\n\n  def Flatten(self):\n    \"\"\"Returns a list containing the flattened values in the `.NestedMap`.\n    Unlike py_utils.Flatten(), this will only descend into lists and NestedMaps\n    and not dicts, tuples, or namedtuples.\n    \"\"\"\n    return self._RecursiveMap(lambda _, v: v, flatten=True)\n\n  def FlattenItems(self):\n    \"\"\"Flatten the `.NestedMap` and returns <key, value> pairs in a list.\n    Returns:\n      A list of <key, value> pairs, where keys for nested entries will be\n      represented in the form of `foo.bar[10].baz`.\n    \"\"\"\n    return self._RecursiveMap(lambda k, v: (k, v), flatten=True)\n\n  def Pack(self, lst):\n    \"\"\"Returns a copy of this with each value replaced by a value in lst.\"\"\"\n    assert len(self.FlattenItems()) == len(lst)\n    v_iter = iter(lst)\n    return self._RecursiveMap(lambda unused_k, unused_v: next(v_iter))\n\n  def Transform(self, fn):\n    \"\"\"Returns a copy of this `.NestedMap` with fn applied on each value.\"\"\"\n    return self._RecursiveMap(lambda _, v: fn(v))\n\n  def IsCompatible(self, other):\n    \"\"\"Returns true if self and other are compatible.\n    If x and y are two compatible `.NestedMap`, `x.Pack(y.Flatten())` produces y\n    and vice versa.\n    Args:\n      other: Another `.NestedMap`.\n    \"\"\"\n    items = self._RecursiveMap(lambda k, _: k, flatten=True)\n    other_items = other._RecursiveMap(lambda k, _: k, flatten=True)  # pylint: disable=protected-access\n    return items == other_items\n\n  def Filter(self, fn):\n    \"\"\"Returns a copy with entries where fn(entry) is True.\"\"\"\n    return self.FilterKeyVal(lambda _, v: fn(v))\n\n  def FilterKeyVal(self, fn):\n    \"\"\"Returns a copy of this `.NestedMap` filtered by fn.\n    If fn(key, entry) is True, the entry is copied into the returned NestedMap.\n    Otherwise, it is not copied.\n    Args:\n      fn: a callable of (string, entry)->boolean.\n    Returns:\n      A `.NestedMap` contains copied entries from this `'.NestedMap`.\n    \"\"\"\n    return self._RecursiveMap(lambda k, v: v if fn(k, v) else self._DELETE)\n\n  def _ToStrings(self):\n    \"\"\"Returns debug strings in a list for this `.NestedMap`.\"\"\"\n    kv = self.FlattenItems()\n    maxlen = max([len(k) for k, _ in kv]) if kv else 0\n    return sorted([k + ' ' * (4 + maxlen - len(k)) + str(v) for k, v in kv])\n\n  def DebugString(self):\n    \"\"\"Returns a debug string for this `.NestedMap`.\"\"\"\n    return '\\n'.join(self._ToStrings())\n\n  def VLog(self, level=None, prefix=None):\n    \"\"\"Logs the debug string at the level.\"\"\"\n    if level is None:\n      level = 0\n    if prefix is None:\n      prefix = 'nmap: '\n    for l in self._ToStrings():\n      tf.logging.vlog(level, '%s %s', prefix, l)\n"
  },
  {
    "path": "monolith/core/testing_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\"\"\"Utilities for unit-testing layers.\n\nThe implementation for these utilities is similar to that in\nhttps://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/testing_utils.py\n\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport functools\nimport threading\n\nimport numpy as np\n\nimport tensorflow as tf\nfrom tensorflow.python import tf2\nfrom tensorflow.python.eager import context\nfrom tensorflow.python.framework import dtypes\nfrom tensorflow.python.framework import tensor_shape\nfrom tensorflow.python.framework import tensor_spec\nfrom tensorflow.python.framework import test_util\nfrom tensorflow.python.keras import backend\nfrom tensorflow.python.keras import layers\nfrom tensorflow.python.keras import models\nfrom tensorflow.python.keras import testing_utils\nfrom tensorflow.python.keras.engine import base_layer_utils\nfrom tensorflow.python.keras.engine import keras_tensor\nfrom tensorflow.python.util import tf_contextlib\nfrom tensorflow.python.util import tf_decorator\nfrom tensorflow.python.util import tf_inspect\n# from tensorflow.python.keras import testing_utils\n\n\n@test_util.disable_cudnn_autotune\ndef layer_test(layer_cls,\n               kwargs=None,\n               input_shape=None,\n               input_dtype=None,\n               input_data=None,\n               expected_output=None,\n               expected_output_dtype=None,\n               expected_output_shape=None,\n               validate_training=True,\n               adapt_data=None,\n               custom_objects=None,\n               test_harness=None):\n  \"\"\"Test routine for a BaseLayer with a single input and single output.\n\n    Args:\n        layer_cls: BaseLayer class object.\n        kwargs: Optional dictionary of keyword arguments for instantiating the\n            layer.\n        input_shape: Input shape tuple.\n        input_dtype: Data type of the input data.\n        input_data: Numpy array of input data.\n        expected_output: Numpy array of the expected output.\n        expected_output_dtype: Data type expected for the output.\n        expected_output_shape: Shape tuple for the expected shape of the output.\n        validate_training: Whether to attempt to validate training on this layer.\n            This might be set to False for non-differentiable layers that output\n            string or integer values.\n        adapt_data: Optional data for an 'adapt' call. If None, adapt() will not\n            be tested for this layer. This is only relevant for PreprocessingLayers.\n            custom_objects: Optional dictionary mapping name strings to custom objects\n            in the layer class. This is helpful for testing custom layers.\n            test_harness: The Tensorflow test, if any, that this function is being\n            called in.\n\n    Returns:\n        The output data (Numpy array) returned by the layer, for additional\n        checks to be done by the calling code.\n    Raises:\n        ValueError: if `input_shape is None`.\n    \"\"\"\n  if input_data is None:\n    if input_shape is None:\n      raise ValueError('input_shape is None')\n    if not input_dtype:\n      input_dtype = 'float32'\n    input_data_shape = list(input_shape)\n    for i, e in enumerate(input_data_shape):\n      if e is None:\n        input_data_shape[i] = np.random.randint(1, 4)\n    input_data = 10 * np.random.random(input_data_shape)\n    if input_dtype[:5] == 'float':\n      input_data -= 0.5\n    input_data = input_data.astype(input_dtype)\n  elif input_shape is None:\n    input_shape = input_data.shape\n  if input_dtype is None:\n    input_dtype = input_data.dtype\n  if expected_output_dtype is None:\n    expected_output_dtype = input_dtype\n\n  if dtypes.as_dtype(expected_output_dtype) == dtypes.string:\n    if test_harness:\n      assert_equal = test_harness.assertAllEqual\n    else:\n      assert_equal = string_test\n  else:\n    if test_harness:\n      assert_equal = test_harness.assertAllClose\n    else:\n      # assert_equal = tf.python.keras.testing_utils.numeric_test\n      assert_equal = testing_utils.numeric_test\n\n  # instantiation\n  kwargs = kwargs or {}\n  layer = layer_cls(**kwargs)\n\n  # Test adapt, if data was passed.\n  if adapt_data is not None:\n    layer.adapt(adapt_data)\n\n  # test get_weights , set_weights at layer level\n  weights = layer.get_weights()\n  layer.set_weights(weights)\n\n  # test and instantiation from weights\n  if 'weights' in tf_inspect.getargspec(layer_cls.__init__):\n    kwargs['weights'] = weights\n    layer = layer_cls(**kwargs)\n\n  # test in functional API\n  x = layers.Input(shape=input_shape[1:], dtype=input_dtype)\n  y = layer(x)\n  if backend.dtype(y) != expected_output_dtype:\n    raise AssertionError('When testing layer %s, for input %s, found output '\n                         'dtype=%s but expected to find %s.\\nFull kwargs: %s' %\n                         (layer_cls.__name__, x, backend.dtype(y),\n                          expected_output_dtype, kwargs))\n\n  def assert_shapes_equal(expected, actual):\n    \"\"\"Asserts that the output shape from the layer matches the actual shape.\"\"\"\n    if len(expected) != len(actual):\n      raise AssertionError(\n          'When testing layer %s, for input %s, found output_shape='\n          '%s but expected to find %s.\\nFull kwargs: %s' %\n          (layer_cls.__name__, x, actual, expected, kwargs))\n\n    for expected_dim, actual_dim in zip(expected, actual):\n      if isinstance(expected_dim, tensor_shape.Dimension):\n        expected_dim = expected_dim.value\n      if isinstance(actual_dim, tensor_shape.Dimension):\n        actual_dim = actual_dim.value\n      if expected_dim is not None and expected_dim != actual_dim:\n        raise AssertionError(\n            'When testing layer %s, for input %s, found output_shape='\n            '%s but expected to find %s.\\nFull kwargs: %s' %\n            (layer_cls.__name__, x, actual, expected, kwargs))\n\n  if expected_output_shape is not None:\n    assert_shapes_equal(tensor_shape.TensorShape(expected_output_shape),\n                        y.shape)\n\n  # check shape inference\n  model = models.Model(x, y)\n  computed_output_shape = tuple(\n      layer.compute_output_shape(\n          tensor_shape.TensorShape(input_shape)).as_list())\n  computed_output_signature = layer.compute_output_signature(\n      tensor_spec.TensorSpec(shape=input_shape, dtype=input_dtype))\n  actual_output = model.predict(input_data)\n  actual_output_shape = actual_output.shape\n  assert_shapes_equal(computed_output_shape, actual_output_shape)\n  assert_shapes_equal(computed_output_signature.shape, actual_output_shape)\n  if computed_output_signature.dtype != actual_output.dtype:\n    raise AssertionError(\n        'When testing layer %s, for input %s, found output_dtype='\n        '%s but expected to find %s.\\nFull kwargs: %s' %\n        (layer_cls.__name__, x, actual_output.dtype,\n         computed_output_signature.dtype, kwargs))\n  if expected_output is not None:\n    assert_equal(actual_output, expected_output)\n"
  },
  {
    "path": "monolith/core/tpu_variable.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Distributed variable implementation for TPU.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport contextlib\n\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.ops import control_flow_ops\nfrom tensorflow.python.ops import gen_resource_variable_ops\n\ntry:\n  from tensorflow.python.types import core  # pylint:disable=g-import-not-at-top,g-direct-tensorflow-import\n  TF_23 = True\nexcept ImportError:\n  TF_23 = False\n\nif TF_23:\n  VariableBase = core.Tensor\nelse:\n  VariableBase = object\n\n\n@contextlib.contextmanager\ndef _handle_graph(handle):\n  with handle.graph.as_default():\n    yield\n\n\ndef _enclosing_tpu_context():\n  # pylint: disable=protected-access\n  context = ops.get_default_graph()._get_control_flow_context()\n  # pylint: enable=protected-access\n  while context is not None and not isinstance(\n      context, control_flow_ops.XLAControlFlowContext):\n    context = context.outer_context\n  return context\n\n\nclass ReplicatedVariable(VariableBase):\n  \"\"\"A replicated variable for use on TPUs.\n  When accessed inside a tpu.replicate() context, this variable acts as if it\n  is a single variable whose handle is a replicated input to the computation.\n  Outside a tpu.replicate() context currently this object has pretty murky\n  semantics, especially with respect to things such as\n  * initialization\n  * colocation.\n  \"\"\"\n\n  def __init__(self, name, variables):\n    self._name = name\n    self._primary_var = variables[0]\n    self._vars = variables\n    self._cached_value = None\n    self._dtype = variables[0].dtype\n\n  @property\n  def handle(self):\n    tpu_context = _enclosing_tpu_context()\n    if tpu_context is None:\n      return self._primary_var.handle\n\n    return tpu_context.get_replicated_var_handle(self._name, self._vars)\n\n  @contextlib.contextmanager\n  def _assign_dependencies(self):\n    \"\"\"Makes assignments depend on the cached value, if any.\n    This prevents undefined behavior with reads not ordered wrt writes.\n    Yields:\n      None.\n    \"\"\"\n    if self._cached_value is not None:\n      with ops.control_dependencies([self._cached_value]):\n        yield\n    else:\n      yield\n\n  @property\n  def initializer(self):\n    return control_flow_ops.group([v.initializer for v in self._vars])\n\n  @property\n  def graph(self):\n    return self._primary_var.graph\n\n  @property\n  def _shared_name(self):\n    return self._common_name\n\n  @property\n  def _unique_id(self):\n    return self._primary_var._unique_id  # pylint: disable=protected-access\n\n  @property\n  def name(self):\n    return self._name\n\n  @property\n  def dtype(self):\n    return self._primary_var.dtype\n\n  @property\n  def shape(self):\n    return self._primary_var.shape\n\n  def get_shape(self):\n    return self._primary_var.get_shape()\n\n  def to_proto(self, export_scope=None):\n    return self._primary_var.to_proto(export_scope=export_scope)\n\n  @property\n  def constraint(self):\n    return None\n\n  @property\n  def op(self):\n    return self.get().op\n\n  def _read_variable_op(self):\n    if _enclosing_tpu_context() is None:\n      return self._primary_var.read_value()\n    v = gen_resource_variable_ops.read_variable_op(self.handle, self._dtype)\n    return v\n\n  def read_value(self):\n    return self._read_variable_op()\n\n  def assign(self, value, use_locking=None, name=None, read_value=False):\n    del use_locking\n    with _handle_graph(self.handle), self._assign_dependencies():\n      value_tensor = ops.convert_to_tensor(value, dtype=self.dtype)\n      assign_op = gen_resource_variable_ops.assign_variable_op(self.handle,\n                                                               value_tensor,\n                                                               name=name)\n    if read_value:\n      return self._read_variable_op()\n    return assign_op\n\n  def assign_add(self, delta, use_locking=None, name=None, read_value=True):\n    del use_locking\n    with _handle_graph(self.handle), self._assign_dependencies():\n      assign_add_op = gen_resource_variable_ops.assign_add_variable_op(\n          self.handle,\n          ops.convert_to_tensor(delta, dtype=self.dtype),\n          name=name)\n    if read_value:\n      return self._read_variable_op()\n    return assign_add_op\n\n  def assign_sub(self, delta, use_locking=None, name=None, read_value=True):\n    del use_locking\n    with _handle_graph(self.handle), self._assign_dependencies():\n      assign_sub_op = gen_resource_variable_ops.assign_sub_variable_op(\n          self.handle,\n          ops.convert_to_tensor(delta, dtype=self.dtype),\n          name=name)\n    if read_value:\n      return self._read_variable_op()\n    return assign_sub_op\n\n  def get(self):\n    return self._primary_var\n\n  @property\n  def _in_graph_mode(self):\n    return self._primary_var._in_graph_mode  # pylint: disable=protected-access\n\n  def _should_act_as_resource_variable(self):\n    \"\"\"Pass resource_variable_ops.is_resource_variable check.\"\"\"\n    pass\n\n  def _dense_var_to_tensor(self, dtype=None, name=None, as_ref=False):\n    \"\"\"Converts a variable to a tensor.\"\"\"\n    # pylint: disable=protected-access\n    if _enclosing_tpu_context() is None:\n      if hasattr(self._primary_var, '_dense_var_to_tensor'):\n        return self._primary_var._dense_var_to_tensor(dtype, name, as_ref)\n      else:\n        return ops.convert_to_tensor(self._primary_var)\n    # pylint: enable=protected-access\n    if dtype is not None and dtype != self.dtype:\n      return NotImplemented\n    if as_ref:\n      return self.handle\n    else:\n      return self.read_value()\n\n\n# Register a conversion function which reads the value of the variable,\n# allowing instances of the class to be used as tensors.\ndef _tensor_conversion(var, dtype=None, name=None, as_ref=False):\n  return var._dense_var_to_tensor(dtype=dtype, name=name, as_ref=as_ref)  # pylint: disable=protected-access\n\n\nops.register_tensor_conversion_function(ReplicatedVariable, _tensor_conversion)\n\nif not TF_23:\n  ops.register_dense_tensor_like_type(ReplicatedVariable)\n"
  },
  {
    "path": "monolith/core/util.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom os import path\nimport subprocess\n\nfrom absl import logging\nfrom google.cloud import storage\n\nimport tensorflow.compat.v1 as tf\n\n_GS_PREFIX = \"gs://\"\n_CORE_NUMBER_PER_HOST = 8\n\n_DATE_FORMAT_LEN = 8\n_MIN_DATE = \"00000000\"\n_MAX_DATE = \"99999999\"\n\n\ndef get_bucket_name_and_relavite_path(gs_file_path):\n  \"\"\" Given gs file path, return gs bucket name and relavite gs path (not include gs bucket).\"\"\"\n\n  assert gs_file_path.find(_GS_PREFIX) != -1, \"File name: {}\".format(\n      gs_file_path)\n\n  bucket_name_start = len(_GS_PREFIX)\n  bucket_name_end = gs_file_path.find(\"/\", bucket_name_start)\n  bucket_name = gs_file_path[bucket_name_start:bucket_name_end]\n  relavite_blob_path = gs_file_path[bucket_name_end + 1:]\n  return bucket_name, relavite_blob_path\n\n\ndef download_gcs_file(gs_file_path, local_file_name):\n  \"\"\" Download gs file to local disk by giving gs path and local file name.\"\"\"\n\n  logging.info(\"Start downloading {} => {} ...\".format(gs_file_path,\n                                                       local_file_name))\n  bucket_name, relavite_blob_path = get_bucket_name_and_relavite_path(\n      gs_file_path)\n  download_gcs_file_with_relative_path(bucket_name, relavite_blob_path,\n                                       local_file_name)\n\n\ndef download_gcs_file_with_relative_path(bucket_name, gs_file_relative_path,\n                                         local_file_name):\n  \"\"\" Download gs file to local disk by giving gs relavite path and local file name.\"\"\"\n\n  storage_client = storage.Client()\n  bucket = storage_client.bucket(bucket_name)\n  blob = bucket.blob(gs_file_relative_path)\n  blob.download_to_filename(local_file_name)\n\n\ndef list_gcs_files_with_prefix(gs_path_prefix):\n  \"\"\" Given a gs path prefix, return the gs bucket name and relavite paths (not include gs bucket) for all matching blobs.\"\"\"\n\n  storage_client = storage.Client()\n  bucket_name, relavite_blob_path_prefix = get_bucket_name_and_relavite_path(\n      gs_path_prefix)\n  blob_relative_paths = storage_client.list_blobs(\n      bucket_name, prefix=relavite_blob_path_prefix)\n\n  return bucket_name, blob_relative_paths\n\n\ndef parse_example_number_meta_file(meta_file, seperator):\n  \"\"\"Parse the meta file which contains file name and its tr record number.\"\"\"\n\n  file_index = 0\n  file_example_number_list = []\n  with open(meta_file) as f:\n    previous_file_name = \"\"\n    lines = f.readlines()\n    for line in lines:\n      if line.find(\",\") == -1:\n        continue\n      split_str = line.split(\",\")\n      file_name = split_str[0]\n      assert previous_file_name < file_name, \"File name must be in dictionary ascending order. Previous file name: {}, current file file name: {}\".format(\n          previous_file_name, file_name)\n      previous_file_name = file_name\n      count = int(split_str[1])\n      file_example_number_list.append((file_name, count))\n  return file_example_number_list\n\n\ndef calculate_shard_skip_file_number(file_example_number, shard_num,\n                                     completed_steps_number,\n                                     batch_size_per_core):\n  \"\"\"Calculate for each shard (host), how many files it has completed processing from last check point.\"\"\"\n\n  processed_example_number_per_host = batch_size_per_core * completed_steps_number * _CORE_NUMBER_PER_HOST\n  shard_index = 0\n  # Keep number of completed files for each shard (host) in last checkpoint.\n  shard_skip_file_number = [0] * shard_num\n  # Keep number of completed examples for each shard (host) in last checkpoint.\n  shard_accumulated_example_count = [0] * shard_num\n  for example_number in file_example_number:\n    if example_number + shard_accumulated_example_count[\n        shard_index] <= processed_example_number_per_host:\n      shard_accumulated_example_count[shard_index] += example_number\n      shard_skip_file_number[shard_index] += 1\n\n    shard_index = (shard_index + 1) % shard_num\n\n  return shard_skip_file_number\n\n\ndef get_checkpoint_completed_step_number(checkpoint_path):\n  \"\"\"Get the completed steps number in the latest checkpoint under checkpoint path.\"\"\"\n\n  completed_steps_number = 0\n  bucket_name, blob_relative_paths = list_gcs_files_with_prefix(\n      path.join(checkpoint_path, \"model.ckpt\"))\n  for blob in blob_relative_paths:\n    blob_relative_path = blob.name\n    if blob_relative_path.endswith(\".meta\") == False:\n      continue\n\n    blob_name = blob_relative_path[blob_relative_path.rfind(\"/\") + 1:]\n    logging.info(\"Found checkpoint file {} under path {}\".format(\n        blob_name, checkpoint_path))\n    checkpoint_processed_steps = int(blob_name[blob_name.find(\"-\") +\n                                               1:blob_name.rfind(\".meta\")])\n    completed_steps_number = max(completed_steps_number,\n                                 checkpoint_processed_steps)\n\n  return completed_steps_number\n\n\ndef update_params(params, tpu_cluster_resolver):\n  shard_num = tpu_cluster_resolver.cluster_spec().num_tasks(\"worker\")\n\n  assert (\"batch_size_per_core\" in params and params[\"batch_size_per_core\"] is not None) \\\n   or (\"global_batch_size\" in params and params[\"global_batch_size\"] is not None), \\\n      \"batch_size_per_core and global_batch_size can't be both None.\"\n\n  if \"batch_size_per_core\" not in params or params[\n      \"batch_size_per_core\"] is None:\n    params[\"batch_size_per_core\"] = params[\n        \"global_batch_size\"] / shard_num / _CORE_NUMBER_PER_HOST\n  elif \"global_batch_size\" not in params or params[\"global_batch_size\"] is None:\n    params[\"global_batch_size\"] = params[\n        \"batch_size_per_core\"] * shard_num * _CORE_NUMBER_PER_HOST\n  else:\n    assert params[\"batch_size_per_core\"] * shard_num * _CORE_NUMBER_PER_HOST == params[\"global_batch_size\"], \\\n       \"Batch size per core: {} and global batch size:{} doesn't align.\".format(params[\"batch_size_per_core\"], params[\"global_batch_size\"])\n\n  logging.info(\"Batch size per core: {}, global batch size: {}\".format(\n      params[\"batch_size_per_core\"], params[\"global_batch_size\"]))\n\n  # Get the completed steps number from the latest checkpoint.\n  completed_step_number = get_checkpoint_completed_step_number(\n      params[\"model_dir\"])\n  logging.info(\n      \"Completed steps from last checkpoint: {}\".format(completed_step_number))\n  if completed_step_number > 0:\n    file_example_number = get_per_file_example_numbers_for_checkpoint_reload(\n        params[\"train_dataset_path\"], params[\"gcs_file_example_number\"], \",\")\n\n    shard_skip_file_number = calculate_shard_skip_file_number(\n        file_example_number, shard_num, completed_step_number,\n        params[\"batch_size_per_core\"])\n    params[\"shard_skip_file_number\"] = shard_skip_file_number\n    logging.info(\n        \"Set shard skip file number, shard number: {}, batch size per core: {}, completed steps of last chckpoint: {}, \\\n            processed file number of each shard: {}\".format(\n            shard_num, params[\"batch_size_per_core\"], completed_step_number,\n            shard_skip_file_number))\n\n\ndef get_per_file_example_numbers_for_checkpoint_reload(\n    train_dataset_path, file_example_number_meta,\n    file_example_number_meta_seperator):\n  # Firstly, we need verify whether checkpoint can be reloaded. To reload checkpoint, we need make sure\n  # the training data is contained as continous subset as in file example number meta.\n  # Currently only gsutil supports query gcs with regex path. Google storage client looks like does't support regex.\n  # We will use gsutil tool directly here as a workaround to work with regex path. Later we will switch back\n  # to google storage client once it supports regex path.\n  logging.info(\"Querying train data set to validate checkpoint reload...\")\n  proc = subprocess.Popen([\"gsutil\", \"ls\", train_dataset_path],\n                          stdout=subprocess.PIPE)\n\n  train_file_path_list = []\n  previous_relative_path = \"\"\n  while True:\n    line = proc.stdout.readline()\n    if not line:\n      break\n\n    train_file_path = line.decode(\"utf-8\").strip()\n    bucket_name, relative_path = get_bucket_name_and_relavite_path(\n        train_file_path)\n    train_file_path_list.append(relative_path)\n    assert previous_relative_path < relative_path, \"train file path must be in ascend order. \\\n            previous file path: {}, current file path: {}\".format(\n        previous_relative_path, relative_path)\n    previous_relative_path = relative_path\n\n  file_example_number_list = parse_example_number_meta_file(\n      file_example_number_meta, file_example_number_meta_seperator)\n\n  # Skip the files which are not in trained data set.\n  assert len(\n      train_file_path_list) > 0, \"Train data set size must be greater than 0.\"\n\n  # Find the first train file in file example meta\n  for file_example_number_index, file_example_number in enumerate(\n      file_example_number_list):\n    file_path = file_example_number[0]\n    count = file_example_number[1]\n    if train_file_path_list[0] <= file_path:\n      break\n\n  assert len(train_file_path_list) <= len(file_example_number_list) - file_example_number_index, \\\n      \"Train file path list length {} can't be greater than the remaining length of file example number list length {} starting at index {}\".format(len(train_file_path_list), len(file_example_number_list) - file_example_number_index,\n      file_example_number_index)\n\n  example_number_list = []\n  for train_file_index in range(0, len(train_file_path_list)):\n    assert train_file_path_list[train_file_index] == file_example_number_list[file_example_number_index][0], \\\n        \"File {} in train data can not be found in file example meta {}\".format(train_file_path_list[train_file_index],\n         file_example_number_meta)\n    example_number_list.append(\n        file_example_number_list[file_example_number_index][1])\n    file_example_number_index += 1\n\n  logging.info(\"Checkpoint reload verification done.\")\n  return example_number_list\n\n\ndef range_dateset(dataset: tf.data.Dataset,\n                  root_path: str,\n                  start_date: str = None,\n                  end_date: str = None):\n\n  if start_date is None:\n    start_date = _MIN_DATE\n\n  if end_date is None:\n    end_date = _MAX_DATE\n\n  logging.info(\"start_date: {}, end_date: {}.\".format(start_date, end_date))\n\n  def filter_fn(x):\n    path_prefix_len = len(root_path)\n    return tf.math.logical_and(\n        tf.math.greater_equal(\n            tf.strings.to_number(tf.strings.substr(x, path_prefix_len,\n                                                   _DATE_FORMAT_LEN),\n                                 out_type=tf.int32), int(start_date)),\n        tf.math.less_equal(\n            tf.strings.to_number(tf.strings.substr(x, path_prefix_len,\n                                                   _DATE_FORMAT_LEN),\n                                 out_type=tf.int32), int(end_date)),\n    )\n\n  return dataset.filter(filter_fn)\n"
  },
  {
    "path": "monolith/core/util_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow.compat.v1 as tf\n\nimport monolith.core.util as util\n\n\nclass UtilTest(tf.test.TestCase):\n  \"\"\"Base class for tpu test.\"\"\"\n\n  root_path = \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/\"\n\n  def test_range_dataset_single(self):\n    expected_results = [\n        \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200502/00/part\",\n    ]\n    with self.session() as sess:\n      input_dataset = tf.data.Dataset.from_tensor_slices([\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200501/00/part\",\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200502/00/part\",\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200503/00/part\",\n      ])\n      dataset = util.range_dateset(input_dataset, self.root_path, \"20200502\",\n                                   \"20200502\")\n      iterator = tf.compat.v1.data.make_one_shot_iterator(dataset)\n      next_element = iterator.get_next()\n      i = 0\n      try:\n        while True:\n          self.assertEqual(sess.run(next_element).decode(), expected_results[i])\n          i += 1\n      except tf.errors.OutOfRangeError:\n        pass\n      self.assertEqual(i, len(expected_results))\n\n  def test_range_dataset_multiple(self):\n    expected_results = [\n        \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200502/00/part\",\n        \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200503/00/part\",\n        \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200503/01/part\",\n    ]\n    with self.session() as sess:\n      input_dataset = tf.data.Dataset.from_tensor_slices([\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200501/00/part\",\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200502/00/part\",\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200503/00/part\",\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200503/01/part\",\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200504/01/part\",\n      ])\n      dataset = util.range_dateset(input_dataset, self.root_path, \"20200502\",\n                                   \"20200503\")\n      iterator = tf.compat.v1.data.make_one_shot_iterator(dataset)\n      next_element = iterator.get_next()\n      i = 0\n      try:\n        while True:\n          self.assertEqual(sess.run(next_element).decode(), expected_results[i])\n          i += 1\n      except tf.errors.OutOfRangeError:\n        pass\n      self.assertEqual(i, len(expected_results))\n\n  def test_range_dataset_out_of_boundary(self):\n    expected_results = [\n        \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200501/00/part\",\n        \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200502/00/part\",\n    ]\n    with self.session() as sess:\n      input_dataset = tf.data.Dataset.from_tensor_slices([\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200501/00/part\",\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200502/00/part\",\n      ])\n      dataset = util.range_dateset(input_dataset, self.root_path, \"20200401\",\n                                   \"20200505\")\n      iterator = tf.compat.v1.data.make_one_shot_iterator(dataset)\n      next_element = iterator.get_next()\n      i = 0\n      try:\n        while True:\n          self.assertEqual(sess.run(next_element).decode(), expected_results[i])\n          i += 1\n      except tf.errors.OutOfRangeError:\n        pass\n      self.assertEqual(i, len(expected_results))\n\n  def test_range_dataset_no_start_date(self):\n    expected_results = [\n        \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200501/00/part\",\n        \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200502/00/part\",\n    ]\n    with self.session() as sess:\n      input_dataset = tf.data.Dataset.from_tensor_slices([\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200501/00/part\",\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200502/00/part\",\n      ])\n      dataset = util.range_dateset(input_dataset,\n                                   self.root_path,\n                                   start_date=None,\n                                   end_date=\"20200505\")\n      iterator = tf.compat.v1.data.make_one_shot_iterator(dataset)\n      next_element = iterator.get_next()\n      i = 0\n      try:\n        while True:\n          self.assertEqual(sess.run(next_element).decode(), expected_results[i])\n          i += 1\n      except tf.errors.OutOfRangeError:\n        pass\n      self.assertEqual(i, len(expected_results))\n\n  def test_range_dataset_no_end_date(self):\n    expected_results = [\n        \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200502/00/part\",\n    ]\n    with self.session() as sess:\n      input_dataset = tf.data.Dataset.from_tensor_slices([\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200501/00/part\",\n          \"gs://test_folder/unzipped_tf_records_corrected_repartitioned/20200502/00/part\",\n      ])\n      dataset = util.range_dateset(input_dataset,\n                                   self.root_path,\n                                   start_date=\"20200502\",\n                                   end_date=None)\n      iterator = tf.compat.v1.data.make_one_shot_iterator(dataset)\n      next_element = iterator.get_next()\n      i = 0\n      try:\n        while True:\n          self.assertEqual(sess.run(next_element).decode(), expected_results[i])\n          i += 1\n      except tf.errors.OutOfRangeError:\n        pass\n      self.assertEqual(i, len(expected_results))\n\n\nif __name__ == \"__main__\":\n  tf.disable_eager_execution()\n  tf.test.main()"
  },
  {
    "path": "monolith/core/variance_scaling.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\"Code to implement a custom Variance Scaling initializer that returns numpy arrays.\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport numpy as np\nimport scipy.stats as stats\n\n\ndef _compute_fans(shape, data_format='channels_last'):\n  \"\"\"Computes the number of input and output units for a weight shape.\n\n    Args:\n        shape: Integer shape tuple.\n        data_format: Image data format to use for convolution kernels.\n            Note that all kernels in Keras are standardized on the\n            `channels_last` ordering (even when inputs are set\n            to `channels_first`).\n\n    Returns:\n        A tuple of scalars, `(fan_in, fan_out)`.\n\n    # Raises\n        ValueError: in case of invalid `data_format` argument.\n    \"\"\"\n  if len(shape) == 2:\n    fan_in = shape[0]\n    fan_out = shape[1]\n  elif len(shape) in {3, 4, 5}:\n    # Assuming convolution kernels (1D, 2D or 3D).\n    # TH kernel shape: (depth, input_depth, ...)\n    # TF kernel shape: (..., input_depth, depth)\n    if data_format == 'channels_first':\n      receptive_field_size = np.prod(shape[2:])\n      fan_in = shape[1] * receptive_field_size\n      fan_out = shape[0] * receptive_field_size\n    elif data_format == 'channels_last':\n      receptive_field_size = np.prod(shape[:-2])\n      fan_in = shape[-2] * receptive_field_size\n      fan_out = shape[-1] * receptive_field_size\n    else:\n      raise ValueError('Invalid data_format: ' + data_format)\n  else:\n    # No specific assumptions.\n    fan_in = np.sqrt(np.prod(shape))\n    fan_out = np.sqrt(np.prod(shape))\n  return fan_in, fan_out\n\n\nclass VarianceScaling():\n  \"\"\"Initializer capable of adapting its scale to the shape of weights.\n\n    With `distribution=\"truncated_normal\"`, samples are drawn from a truncated\n    normal distribution centered on zero, with `stddev = sqrt(scale / n)`\n    where n is:\n\n        - number of input units in the weight tensor, if mode = \"fan_in\"\n        - number of output units, if mode = \"fan_out\"\n        - average of the numbers of input and output units, if mode = \"fan_avg\"\n\n    With `distribution=\"uniform\"`,\n    samples are drawn from a uniform distribution\n    within [-limit, limit], with `limit = sqrt(3 * scale / n)`.\n\n    With `distribution=\"untrucated_normal\"`, samples are drawn from a truncated\n    normal distribution centered on zero, with `stddev = sqrt(scale / n)`.\n\n    When called, this initializer produces a numpy array, instead of Tensorflow\n    tensors.\n\n    Args:\n        scale (float, optional): Scaling factor (positive float).\n        mode (str, optional): One of \"fan_in\", \"fan_out\", \"fan_avg\".\n        distribution (str, optional): Random distribution to use. One of\n            \"truncated_normal\", \"untruncated_normal\", and \"uniform\".\n        seed (int, optional): A Python integer. Used to seed the random generator.\n\n    Raises:\n        ValueError: In case of an invalid value for the \"scale\", mode\" or\n          \"distribution\" arguments.\n    \"\"\"\n\n  def __init__(self,\n               scale=1.0,\n               mode='fan_in',\n               distribution='truncated_normal',\n               seed=None):\n    if scale <= 0.:\n      raise ValueError('`scale` must be a positive float. Got:', scale)\n    mode = mode.lower()\n    if mode not in {'fan_in', 'fan_out', 'fan_avg'}:\n      raise ValueError(\n          'Invalid `mode` argument: '\n          'expected on of {\"fan_in\", \"fan_out\", \"fan_avg\"} '\n          'but got', mode)\n    distribution = distribution.lower()\n    if distribution not in {\n        'truncated_normal', 'untruncated_normal', 'uniform'\n    }:\n      raise ValueError(\n          'Invalid `distribution` argument: '\n          'expected one of {\"truncated_normal\", \"untruncated_normal\", \"uniform\"} '\n          'but got', distribution)\n    self.scale = scale\n    self.mode = mode\n    self.distribution = distribution\n    self.seed = seed\n\n  def __call__(self, shape, dtype=np.float32):\n    fan_in, fan_out = _compute_fans(shape)\n    scale = self.scale\n    if self.mode == 'fan_in':\n      scale /= max(1., fan_in)\n    elif self.mode == 'fan_out':\n      scale /= max(1., fan_out)\n    else:\n      scale /= max(1., float(fan_in + fan_out) / 2)\n\n    np.random.seed(self.seed)\n    if self.distribution == 'truncated_normal':\n      mean = 0.0\n      # 0.879... = scipy.stats.truncnorm.std(a=-2, b=2, loc=0., scale=1.)\n      stddev = np.sqrt(scale) / .87962566103423978\n      # Mimic the behavior of tf.random.truncated_normal, which truncates\n      # at mean +/- 2 standard deviations\n      lower_clip = mean - 2 * stddev\n      upper_clip = mean + 2 * stddev\n      a = (lower_clip - mean) / stddev\n      b = (upper_clip - mean) / stddev\n      return stats.truncnorm.rvs(\n          a=a,\n          b=b,\n          loc=mean,\n          scale=stddev,\n          size=shape,\n      ).astype(dtype)\n    elif self.distribution == 'untruncated_normal':\n      mean = 0.0\n      stddev = np.sqrt(scale)\n      return np.random.normal(\n          loc=mean,\n          scale=stddev,\n          size=shape,\n      ).astype('float32')\n    else:\n      limit = np.sqrt(3. * scale)\n      return np.random.uniform(\n          low=-limit,\n          high=limit,\n          size=shape,\n      ).astype(dtype)\n\n  def get_config(self):\n    return {\n        'scale': self.scale,\n        'mode': self.mode,\n        'distribution': self.distribution,\n        'seed': self.seed\n    }\n"
  },
  {
    "path": "monolith/gpu_runner.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"GPU Runner.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport sys\nimport os\nimport time\n\nimport tensorflow as tf\nimport horovod.tensorflow as hvd\nfrom mpi4py import MPI\n\nfrom monolith.base_runner import BaseRunner\nfrom monolith.core import model_registry\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_string(\"task\", default=None, help=\"Name of the task class to run.\")\n\nflags.DEFINE_string(\n    \"model_dir\",\n    default=None,\n    help=(\"The directory where the model and summaries are stored.\"))\n\nflags.DEFINE_integer(\n    \"save_checkpoints_steps\",\n    default=None,\n    help=\n    (\"Save checkpoint every save_checkpoints_steps. If None, no checkpoint saved.\"\n    ))\n\nflags.DEFINE_enum(\"mode\", \"train\", [\"train_and_eval\", \"train\", \"eval\"],\n                  \"Job mode.\")\n\n\nclass GPURunner(BaseRunner):\n\n  def __init__(self, task_param, *args, **kwargs):\n    super(GPURunner, self).__init__(*args, **kwargs)\n    # TODO(youlong.cheng): all the parse logic should genearte a hyperparam class.\n    self._model_dir = FLAGS.model_dir\n    self._save_checkpoints_steps = FLAGS.save_checkpoints_steps\n    #TODO(hemang.jangle) Allow subclass task_params to override tpu_runner params\n    self._task_param = task_param\n    self._mode = FLAGS.mode\n\n  def create_estimator(self, model_fn):\n    \"\"\"Creates the Estimator.\"\"\"\n    if self._task_param.accelerator == \"horovod\":\n      # Horovod: save checkpoints only on worker 0 to prevent other workers from\n      # corrupting them. @Hao.sheng: However, we still need to use the same\n      # model_dir so each worker where to load the checkpoint in the train_and_eval\n      # mode.\n      model_dir = self._model_dir  #if hvd.rank() == 0 else None\n      save_checkpoints_steps = self._save_checkpoints_steps if hvd.rank(\n      ) == 0 else None\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.optimizer_options.global_jit_level = tf.compat.v1.OptimizerOptions.ON_1\n      config.gpu_options.allow_growth = True\n      config.gpu_options.visible_device_list = str(hvd.local_rank())\n      config = tf.estimator.RunConfig(\n          model_dir=model_dir,\n          save_checkpoints_steps=save_checkpoints_steps,\n          session_config=config)\n      num_gpus = hvd.size()\n    else:\n      num_gpus = 1\n      config = tf.compat.v1.estimator.RunConfig(\n          model_dir=self._model_dir,\n          save_checkpoints_steps=self._save_checkpoints_steps)\n\n    return tf.compat.v1.estimator.Estimator(\n        model_fn=model_fn,\n        params={\n            \"train_batch_size\":\n                self._task_param.train.per_replica_batch_size,\n            \"eval_batch_size\":\n                self._task_param.eval.per_replica_batch_size,\n            \"accelerator\":\n                self._task_param.accelerator,\n            \"num_replicas\":\n                num_gpus,\n            \"hvd_rank\":\n                hvd.rank() if self._task_param.accelerator == \"horovod\" else 0\n        },\n        config=config)\n\n  def run(self):\n    try:\n      current_step = tf.train.load_variable(self._model_dir,\n                                            tf.compat.v1.GraphKeys.GLOBAL_STEP)\n    except (TypeError, ValueError, tf.errors.NotFoundError):\n      current_step = 0\n    logging.info(\"Current step :{}\".format(current_step))\n\n    task = self._task_param.instantiate()\n    input_fn_train = task.create_input_fn(tf.estimator.ModeKeys.TRAIN)\n    input_fn_eval = task.create_input_fn(tf.estimator.ModeKeys.EVAL)\n    model_fn = task.create_model_fn()\n    if self._task_param.accelerator == \"horovod\":\n      # Horovod: initialize Horovod.\n      hvd.init()\n\n      # Horovod: pin GPU to be used to process local rank (one GPU per process)\n      gpus = tf.config.experimental.list_physical_devices('GPU')\n      for gpu in gpus:\n        tf.config.experimental.set_memory_growth(gpu, True)\n        if gpus:\n          tf.config.experimental.set_visible_devices(gpus[hvd.local_rank()],\n                                                     'GPU')\n\n    est = self.create_estimator(model_fn)\n    start_timestamp = time.time()  # This time will include compilation time\n\n    if self._mode == 'train':\n      if self._task_param.accelerator == \"horovod\":\n        # Horovod: BroadcastGlobalVariablesHook broadcasts initial variable states from\n        # rank 0 to all other processes. This is necessary to ensure consistent\n        # initialization of all workers when training is started with random weights or\n        # restored from a checkpoint.\n        bcast_hook = hvd.BroadcastGlobalVariablesHook(0)\n        est.train(input_fn_train,\n                  max_steps=self._task_param.train.max_steps,\n                  hooks=[bcast_hook])\n      else:\n        est.train(input_fn_train, max_steps=self._task_param.train.max_steps)\n    elif self._mode == 'eval':\n      eval_output_dir = os.path.join(self._model_dir, 'eval')\n      tf.io.gfile.makedirs(eval_output_dir)\n      total_examples = self._task_param.input.eval_examples\n      eval_batch_size = self._task_param.eval.per_replica_batch_size\n      num_steps = total_examples // eval_batch_size\n      logging.info(\n          \"Evaluation: total_examples:{} eval_batch_size:{} num_steps: {}\".\n          format(total_examples, eval_batch_size, num_steps))\n      eval_results = est.evaluate(input_fn_eval, steps=num_steps)\n      logging.info(\"Eval results: {}\".format(eval_results))\n      # Summary writer writes out eval metrics.\n      summary_writer = tf.compat.v1.summary.FileWriter(eval_output_dir)\n      self.write_summary(eval_results, summary_writer, current_step)\n      summary_writer.close()\n    else:  # train_and_eval\n      steps_per_eval = self._task_param.eval.steps_per_eval\n      max_steps = self._task_param.train.max_steps\n      eval_output_dir = os.path.join(self._model_dir, 'eval')\n      tf.io.gfile.makedirs(eval_output_dir)\n      while current_step < self._task_param.train.max_steps:\n        # Train for up to steps_per_eval number of steps.\n        # At the end of training, a checkpoint will be written to --model_dir.\n\n        next_checkpoint = min(current_step + steps_per_eval, max_steps)\n        if self._task_param.accelerator == \"horovod\":\n          bcast_hook = hvd.BroadcastGlobalVariablesHook(0)\n          est.train(input_fn_train,\n                    max_steps=next_checkpoint,\n                    hooks=[bcast_hook])\n        else:\n          est.train(input_fn_train, max_steps=next_checkpoint)\n        current_step = next_checkpoint\n\n        logging.info(\n            \"Finished training up to step {}. Elapsed seconds {}.\".format(\n                next_checkpoint,\n                time.time() - start_timestamp))\n        total_examples = self._task_param.input.eval_examples\n        eval_batch_size = self._task_param.eval.per_replica_batch_size\n\n        num_steps = total_examples // eval_batch_size\n\n        if self._task_param.accelerator != \"horovod\" or hvd.rank() == 0:\n\n          logging.info(\"Starting to evaluate.\")\n          time.sleep(10)\n\n          #eval_results = hvd.allreduce(eval_results)\n          eval_results = est.evaluate(input_fn_eval, steps=num_steps)\n          logging.info(\"Eval results at step {}: {}\".format(\n              next_checkpoint, eval_results))\n          # Summary writer writes out eval metrics.\n          summary_writer = tf.compat.v1.summary.FileWriter(eval_output_dir)\n          self.write_summary(eval_results, summary_writer, current_step)\n          summary_writer.close()\n\n        # Hovorod: Make sure all workers are synced at the end of one round\n        # https://github.com/horovod/horovod/issues/159\n        # https://github.com/horovod/horovod/issues/1380\n        if self._task_param.accelerator == \"horovod\":\n          MPI.COMM_WORLD.barrier()\n      elapsed_time = int(time.time() - start_timestamp)\n      logging.info(\n          \"Finished training up to step {}. Elapsed seconds {}.\".format(\n              max_steps, elapsed_time))\n\n\ndef main(unused_argv):\n  task_name = FLAGS.task\n  task_param = model_registry.GetParams(task_name)\n\n  logging.info(\"task_param: {}\".format(str(task_param)))\n  runner = GPURunner(task_param)\n  runner.run()\n\n\nif __name__ == '__main__':\n  logging.set_verbosity(logging.INFO)\n  tf.compat.v1.disable_v2_behavior()\n  app.run(main)\n"
  },
  {
    "path": "monolith/monolith_workspace.bzl",
    "content": "load(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\nload(\"@bazel_tools//tools/build_defs/repo:git.bzl\", \"new_git_repository\")\nload(\"@rules_python//python:pip.bzl\", \"pip_install\")\n\ndef monolith_workspace():\n    \"\"\"Adds monolith workspace's dependencies.\"\"\"\n    http_archive(\n        name = \"msgpack\",\n        build_file = \"//third_party:msgpack/msgpack.BUILD\",\n        strip_prefix = \"msgpack-3.3.0\",\n        sha256 = \"6e114d12a5ddb8cb11f669f83f32246e484a8addd0ce93f274996f1941c1f07b\",\n        urls = [\"https://github.com/msgpack/msgpack-c/releases/download/cpp-3.3.0/msgpack-3.3.0.tar.gz\"],\n    )\n\n    pip_install(\n        name = \"pip_deps\",\n        requirements = \"//third_party/pip_deps:requirements.txt\",\n        python_interpreter = \"python3\",\n    )\n\n    http_archive(\n        name = \"gperftools\",\n        build_file = \"//third_party:gperftools/gperftools.BUILD\",\n        sha256 = \"81bb34f546ac8cddd064f8935805f8eb19e3c9661188e127b4c90526e944ebff\",\n        urls = [\n            \"https://github.com/gperftools/gperftools/releases/download/gperftools-2.7/gperftools-2.7.zip\",\n        ],\n        patches = [\"//third_party:gperftools/gperftools.patch\"],\n        patch_args = [\"-p1\"],\n        strip_prefix = \"gperftools-2.7\",\n    )\n\n"
  },
  {
    "path": "monolith/native_training/BUILD",
    "content": "load(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\nload(\"@pip_deps//:requirements.bzl\", \"requirement\")\nload(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_cc_test\", \"tf_custom_op_library\")\n\npackage(\n    default_visibility = [\n        \"//monolith:__subpackages__\",\n    ],\n)\n\npy_library(\n    name = \"monolith_export\",\n    srcs = [\"monolith_export.py\"],\n    visibility = [\"//visibility:public\"],\n)\n\npy_binary(\n    name = \"demo\",\n    srcs = [\"demo.py\"],\n    deps = [\n        \":cpu_training\",\n        \":model\",\n        \":native_task\",\n    ],\n)\n\npy_library(\n    name = \"model\",\n    srcs = [\"model.py\"],\n    deps = [\n        \":feature\",\n        \":input\",\n        \":native_task\",\n        \"//monolith/core:base_model_params\",\n        \"//monolith/core:model_registry\",\n        \"//monolith/native_training/metric:deep_insight_ops\",\n    ],\n)\n\npy_library(\n    name = \"input\",\n    srcs = [\"input.py\"],\n)\n\npy_library(\n    name = \"test_utils\",\n    testonly = 1,\n    srcs = [\"test_utils.py\"],\n    deps = [\n        \":entry\",\n        \":utils\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_py_proto\",\n    ],\n)\n\npy_library(\n    name = \"clip_ops\",\n    srcs = [\"clip_ops.py\"],\n    deps = [\n        \":device_utils\",\n        \"//monolith:utils\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"clip_ops_test\",\n    srcs = [\"clip_ops_test.py\"],\n    deps = [\n        \":clip_ops\",\n    ],\n)\n\npy_proto_library(\n    name = \"hash_table_ops_py_proto\",\n    srcs = [\"hash_table_ops.proto\"],\n)\n\npy_library(\n    name = \"hash_table_ops\",\n    srcs = [\"hash_table_ops.py\"],\n    deps = [\n        \":basic_restore_hook\",\n        \":distributed_serving_ops\",\n        \":entry\",\n        \":feature\",\n        \":graph_meta\",\n        \":hash_filter_ops\",\n        \":hash_table_ops_py_proto\",\n        \":hash_table_utils\",\n        \":save_utils\",\n        \":utils\",\n        \"//monolith:utils\",\n        \"//monolith/native_training/model_export:export_context\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_py_proto\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_test(\n    name = \"hash_table_ops_test\",\n    srcs = [\"hash_table_ops_test.py\"],\n    deps = [\n        \":hash_filter_ops\",\n        \":hash_table_ops\",\n        \":learning_rate_functions\",\n    ],\n)\n\npy_proto_library(\n    name = \"multi_hash_table_ops_py_proto\",\n    srcs = [\"multi_hash_table_ops.proto\"],\n)\n\npy_library(\n    name = \"multi_hash_table_ops\",\n    srcs = [\"multi_hash_table_ops.py\"],\n    deps = [\n        \":basic_restore_hook\",\n        \":distributed_serving_ops\",\n        \":entry\",\n        \":feature\",\n        \":graph_meta\",\n        \":hash_filter_ops\",\n        \":hash_table_utils\",\n        \":multi_hash_table_ops_py_proto\",\n        \":multi_type_hash_table\",\n        \":save_utils\",\n        \":utils\",\n        \"//monolith:utils\",\n        \"//monolith/native_training/model_export:export_context\",\n        \"//monolith/native_training/proto:ckpt_info_py_proto\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_py_proto\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_test(\n    name = \"multi_hash_table_ops_test\",\n    srcs = [\"multi_hash_table_ops_test.py\"],\n    deps = [\n        \":hash_filter_ops\",\n        \":learning_rate_functions\",\n        \":multi_hash_table_ops\",\n        \":test_utils\",\n        \":utils\",\n    ],\n)\n\npy_binary(\n    name = \"hash_table_utils\",\n    srcs = [\"hash_table_utils.py\"],\n)\n\npy_test(\n    name = \"hash_table_utils_test\",\n    srcs = [\"hash_table_utils_test.py\"],\n    deps = [\n        \":hash_table_ops\",\n        \":hash_table_utils\",\n    ],\n)\n\npy_binary(\n    name = \"hash_table_ops_benchmark\",\n    srcs = [\"hash_table_ops_benchmark.py\"],\n    deps = [\n        \":hash_filter_ops\",\n        \":hash_table_ops\",\n    ],\n)\n\npy_library(\n    name = \"distribution_ops\",\n    srcs = [\"distribution_ops.py\"],\n    deps = [\n        \"//idl:example_py_proto\",\n        \"//monolith:utils\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"distribution_ops_test\",\n    srcs = [\"distribution_ops_test.py\"],\n    deps = [\n        \":distribution_ops\",\n    ],\n)\n\npy_test(\n    name = \"fused_embedding_to_layout_test\",\n    srcs = [\"fused_embedding_to_layout_test.py\"],\n    deps = [\n        \":distribution_ops\",\n        \"//monolith/native_training/data:parsers_py\",\n    ],\n)\n\npy_test(\n    name = \"distribution_ops_fused_test\",\n    srcs = [\"distribution_ops_fused_test.py\"],\n    deps = [\n        \":distribution_ops\",\n    ],\n)\n\npy_binary(\n    name = \"distribution_ops_benchmark\",\n    srcs = [\"distribution_ops_benchmark.py\"],\n    deps = [\n        \":distribution_ops\",\n    ],\n)\n\npy_binary(\n    name = \"distribution_ops_fused_benchmark\",\n    srcs = [\"distribution_ops_fused_benchmark.py\"],\n    deps = [\n        \":distribution_ops\",\n    ],\n)\n\npy_library(\n    name = \"distributed_ps\",\n    srcs = [\"distributed_ps.py\"],\n    deps = [\n        \":distribution_ops\",\n        \":hash_table_ops\",\n        \":hash_table_utils\",\n        \":logging_ops\",\n        \":multi_type_hash_table\",\n        \":native_task_context\",\n        \":tensor_utils\",\n        \":utils\",\n        \"//monolith/native_training/data:parsers_py\",\n    ],\n)\n\npy_test(\n    name = \"distributed_ps_test\",\n    srcs = [\"distributed_ps_test.py\"],\n    deps = [\n        \":distributed_ps\",\n        \":distributed_ps_factory\",\n        \":hash_filter_ops\",\n        \":learning_rate_functions\",\n        \":multi_hash_table_ops\",\n        \":test_utils\",\n        \"//monolith/native_training/data:feature_utils_py\",\n    ],\n)\n\npy_library(\n    name = \"distributed_ps_factory\",\n    srcs = [\"distributed_ps_factory.py\"],\n    deps = [\n        \":distributed_ps\",\n        \":distributed_ps_sync\",\n        \":distribution_ops\",\n        \":embedding_combiners\",\n        \":entry\",\n        \":hash_filter_ops\",\n        \":hash_table_ops\",\n        \":multi_hash_table_ops\",\n        \":multi_type_hash_table\",\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"distributed_ps_factory_test\",\n    srcs = [\"distributed_ps_factory_test.py\"],\n    deps = [\n        \":distributed_ps_factory\",\n        \":hash_filter_ops\",\n        \":test_utils\",\n    ],\n)\n\npy_binary(\n    name = \"distributed_ps_benchmark\",\n    srcs = [\"distributed_ps_benchmark.py\"],\n    deps = [\n        \":distributed_ps\",\n        \":hash_filter_ops\",\n        \":multi_type_hash_table\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_py_proto\",\n    ],\n)\n\npy_library(\n    name = \"embedding_combiners\",\n    srcs = [\"embedding_combiners.py\"],\n    deps = [\n        \":device_utils\",\n        \":distribution_ops\",\n        \":ragged_utils\",\n    ],\n)\n\npy_test(\n    name = \"embedding_combiners_test\",\n    srcs = [\"embedding_combiners_test.py\"],\n    deps = [\n        \":embedding_combiners\",\n    ],\n)\n\npy_library(\n    name = \"distributed_ps_sync\",\n    srcs = [\"distributed_ps_sync.py\"],\n    deps = [\n        \":distributed_ps\",\n        \":distribution_ops\",\n        \":feature_utils\",\n        \":multi_type_hash_table\",\n        \":prefetch_queue\",\n    ],\n)\n\npy_test(\n    name = \"distributed_ps_sync_test\",\n    srcs = [\"distributed_ps_sync_test.py\"],\n    deps = [\n        \":distributed_ps\",\n        \":distributed_ps_sync\",\n        \":learning_rate_functions\",\n        \":multi_hash_table_ops\",\n        \":test_utils\",\n    ],\n)\n\npy_library(\n    name = \"multi_type_hash_table\",\n    srcs = [\"multi_type_hash_table.py\"],\n    deps = [\n        \":device_utils\",\n        \":distribution_ops\",\n        \":entry\",\n        \":hash_filter_ops\",\n        \":hash_table_ops\",\n        \":hash_table_utils\",\n        \":prefetch_queue\",\n    ],\n)\n\npy_test(\n    name = \"multi_type_hash_table_test\",\n    srcs = [\"multi_type_hash_table_test.py\"],\n    deps = [\n        \":hash_filter_ops\",\n        \":hash_table_ops\",\n        \":learning_rate_functions\",\n        \":multi_type_hash_table\",\n        \":test_utils\",\n        \":utils\",\n    ],\n)\n\npy_library(\n    name = \"hash_filter_ops\",\n    srcs = [\"hash_filter_ops.py\"],\n    deps = [\n        \":basic_restore_hook\",\n        \":save_utils\",\n        \":utils\",\n        \"//monolith/native_training/model_export:export_context\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_test(\n    name = \"hash_filter_ops_test\",\n    srcs = [\"hash_filter_ops_test.py\"],\n    deps = [\n        \":hash_filter_ops\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_py_proto\",\n    ],\n)\n\npy_library(\n    name = \"touched_key_set_ops\",\n    srcs = [\"touched_key_set_ops.py\"],\n    deps = [\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"touched_key_set_ops_test\",\n    srcs = [\"touched_key_set_ops_test.py\"],\n    deps = [\":touched_key_set_ops\"],\n)\n\npy_library(\n    name = \"distributed_serving_ops\",\n    srcs = [\"distributed_serving_ops.py\"],\n    deps = [\n        \"//monolith/agent_service:agent\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n        \"//monolith/native_training/runtime/parameter_sync:parameter_sync_py_proto\",\n    ],\n)\n\npy_test(\n    name = \"distributed_serving_ops_test\",\n    srcs = [\"distributed_serving_ops_test.py\"],\n    deps = [\n        \":distributed_serving_ops\",\n        \":hash_table_ops\",\n    ],\n)\n\npy_library(\n    name = \"entry\",\n    srcs = [\"entry.py\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":monolith_export\",\n        \"//monolith:utils\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_py_proto\",\n    ],\n)\n\npy_test(\n    name = \"entry_test\",\n    srcs = [\"entry_test.py\"],\n    deps = [\n        \":entry\",\n        \":learning_rate_functions\",\n    ],\n)\n\npy_library(\n    name = \"feature\",\n    srcs = [\"feature.py\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"embedding_combiners\",\n        \":device_utils\",\n        \":distribution_ops\",\n        \":entry\",\n        \":learning_rate_functions\",\n        \":monolith_export\",\n        \":prefetch_queue\",\n        \":ragged_utils\",\n        \"//monolith:utils\",\n        \"//monolith/native_training/model_export:export_context\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_py_proto\",\n    ],\n)\n\npy_library(\n    name = \"distribution_utils\",\n    srcs = [\"distribution_utils.py\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//monolith/native_training/metric:metric_hook\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_library(\n    name = \"feature_utils\",\n    srcs = [\"feature_utils.py\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":clip_ops\",\n        \":feature\",\n        \":native_task\",\n        \":prefetch_queue\",\n    ],\n)\n\npy_test(\n    name = \"feature_utils_test\",\n    srcs = [\"feature_utils_test.py\"],\n    deps = [\n        \":feature_utils\",\n        \":prefetch_queue\",\n    ],\n)\n\npy_test(\n    name = \"feature_test\",\n    srcs = [\"feature_test.py\"],\n    deps = [\n        \":feature\",\n        \":learning_rate_functions\",\n    ],\n)\n\npy_library(\n    name = \"native_task\",\n    srcs = [\"native_task.py\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":feature\",\n        \":prefetch_queue\",\n        \"//monolith/core:base_task\",\n        \"//monolith/native_training/model_export:export_context\",\n    ],\n)\n\npy_library(\n    name = \"native_model\",\n    srcs = [\"native_model.py\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":dense_reload_utils\",\n        \":entry\",\n        \":estimator\",\n        \":feature\",\n        \":file_ops\",\n        \":graph_utils\",\n        \":mlp_utils\",\n        \":monolith_export\",\n        \":native_task\",\n        \":native_task_context\",\n        \":utils\",\n        \"//monolith:utils\",\n        \"//monolith/core:base_layer\",\n        \"//monolith/native_training:feature_utils\",\n        \"//monolith/native_training/data:feature_list\",\n        \"//monolith/native_training/data:utils\",\n        \"//monolith/native_training/layers\",\n        \"//monolith/native_training/metric:metric_hook\",\n        \"//monolith/native_training/metric:utils\",\n        \"//monolith/native_training/model_dump:dump_utils\",\n    ],\n)\n\npy_library(\n    name = \"cpu_training_additional_deps\",\n)\n\npy_library(\n    name = \"cpu_training\",\n    srcs = [\"cpu_training.py\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":barrier_ops\",\n        \":basic_restore_hook\",\n        \":cluster_manager\",\n        \":cpu_training_additional_deps\",\n        \":device_utils\",\n        \":distributed_ps_factory\",\n        \":distribution_ops\",\n        \":distribution_utils\",\n        \":env_utils\",\n        \":feature\",\n        \":gflags_utils\",\n        \":hash_table_ops\",\n        \":hash_table_utils\",\n        \":hvd_lib\",\n        \":logging_ops\",\n        \":mlp_utils\",\n        \":multi_type_hash_table\",\n        \":native_task\",\n        \":native_task_context\",\n        \":net_utils\",\n        \":prefetch_queue\",\n        \":ps_benchmark\",\n        \":save_utils\",\n        \":service_discovery\",\n        \":session_run_hooks\",\n        \":signal_utils\",\n        \":sync_hooks\",\n        \":sync_training_hooks\",\n        \":tensor_utils\",\n        \":utils\",\n        \":variables\",\n        \":yarn_runtime\",\n        \"//monolith/agent_service:replica_manager\",\n        \"//monolith/core:hyperparams\",\n        \"//monolith/native_training:dense_reload_utils\",\n        \"//monolith/native_training:hash_filter_ops\",\n        \"//monolith/native_training/alert\",\n        \"//monolith/native_training/data/training_instance:parser_utils\",\n        \"//monolith/native_training/hooks:ckpt_hooks\",\n        \"//monolith/native_training/hooks:ckpt_info\",\n        \"//monolith/native_training/hooks:feature_engineering_hooks\",\n        \"//monolith/native_training/hooks:hook_utils\",\n        \"//monolith/native_training/hooks:ps_check_hooks\",\n        \"//monolith/native_training/hooks:session_hooks\",\n        \"//monolith/native_training/hooks/server:server_lib\",\n        \"//monolith/native_training/metric:deep_insight_ops\",\n        \"//monolith/native_training/metric:metric_hook\",\n        \"//monolith/native_training/model_dump:dump_utils\",\n        \"//monolith/native_training/model_export\",\n        \"//monolith/native_training/model_export:export_context\",\n        \"//monolith/native_training/model_export:export_hooks\",\n        \"//monolith/native_training/model_export:export_utils\",\n        \"//monolith/native_training/model_export:saved_model_exporters\",\n        \"//monolith/native_training/proto:debugging_info_py_proto\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_py_proto\",\n        requirement(\"numpy\"),\n        requirement(\"pyarrow\"),\n        requirement(\"cityhash\"),\n    ],\n)\n\npy_test(\n    name = \"cpu_training_test\",\n    size = \"large\",\n    srcs = [\"cpu_training_test.py\"],\n    data = [\"cpu_training_distributed_test_binary\"],\n    shard_count = 5,\n    deps = [\n        \":cpu_training\",\n        \":native_task\",\n        \":service_discovery\",\n        \"//monolith/native_training/debugging:debugging_server\",\n    ],\n)\n\npy_test(\n    name = \"cpu_training_multi_hash_table_test\",\n    size = \"large\",\n    srcs = [\"cpu_training_test.py\"],\n    args = [\"--use_native_multi_hash_table\"],\n    main = \"cpu_training_test.py\",\n    shard_count = 5,\n    deps = [\n        \":cpu_training_test\",\n    ],\n)\n\npy_binary(\n    name = \"cpu_training_distributed_test_binary\",\n    srcs = [\"cpu_training_distributed_test_binary.py\"],\n    deps = [\n        \":cluster_manager\",\n        \":cpu_training\",\n        \":native_task\",\n        \":service_discovery\",\n    ],\n)\n\npy_test(\n    name = \"cpu_sync_training_test\",\n    srcs = [\"cpu_sync_training_test.py\"],\n    deps = [\n        \":cpu_training\",\n        \":native_task\",\n    ],\n)\n\npy_test(\n    name = \"model_comp_test\",\n    srcs = [\"model_comp_test.py\"],\n    deps = [\n        \":cpu_training\",\n        \":estimator\",\n        \":native_model\",\n    ],\n)\n\npy_library(\n    name = \"native_task_context\",\n    srcs = [\"native_task_context.py\"],\n    deps = [\"//monolith/agent_service:backends\"],\n)\n\npy_library(\n    name = \"utils\",\n    srcs = [\"utils.py\"],\n    deps = [\n        \"//idl:proto_parser_py_proto\",\n        \"//monolith/core:base_layer\",\n        \"//monolith/core:hyperparams\",\n        \"//monolith/core:py_utils\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_test(\n    name = \"utils_test\",\n    srcs = [\"utils_test.py\"],\n    deps = [\":utils\"],\n)\n\npy_library(\n    name = \"file_ops\",\n    srcs = [\"file_ops.py\"],\n    deps = [\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"file_ops_test\",\n    srcs = [\"file_ops_test.py\"],\n    deps = [\n        \":file_ops\",\n        \"//monolith:utils\",\n    ],\n)\n\npy_library(\n    name = \"env_utils\",\n    srcs = [\"env_utils.py\"],\n    visibility = [\"//visibility:public\"],\n)\n\npy_test(\n    name = \"env_utils_test\",\n    srcs = [\"env_utils_test.py\"],\n    deps = [\n        \":env_utils\",\n    ],\n)\n\npy_library(\n    name = \"logging_ops\",\n    srcs = [\"logging_ops.py\"],\n    deps = [\n        \"//monolith:utils\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n        \"//monolith/native_training/runtime/ops:logging_ops_py_proto\",\n    ],\n)\n\npy_test(\n    name = \"logging_ops_test\",\n    srcs = [\"logging_ops_test.py\"],\n    deps = [\n        \":logging_ops\",\n    ],\n)\n\npy_library(\n    name = \"session_run_hooks\",\n    srcs = [\"session_run_hooks.py\"],\n)\n\npy_test(\n    name = \"session_run_hooks_test\",\n    srcs = [\"session_run_hooks_test.py\"],\n    deps = [\n        \":session_run_hooks\",\n        requirement(\"freezegun\"),\n    ],\n)\n\npy_library(\n    name = \"hvd_lib\",\n    srcs = [\"hvd_lib.py\"],\n)\n\npy_library(\n    name = \"static_reshape_op\",\n    srcs = [\"static_reshape_op.py\"],\n    deps = [\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"static_reshape_op_test\",\n    srcs = [\"static_reshape_op_test.py\"],\n    deps = [\n        \":static_reshape_op\",\n    ],\n)\n\npy_library(\n    name = \"sync_training_hooks\",\n    srcs = [\"sync_training_hooks.py\"],\n    deps = [\n        \":distributed_serving_ops\",\n        \":hash_table_ops\",\n        \":hvd_lib\",\n        \":native_task\",\n        \"//monolith/agent_service:backends\",\n        \"//monolith/native_training/data:datasets_py\",\n    ],\n)\n\npy_test(\n    name = \"sync_training_hooks_test\",\n    srcs = [\"sync_training_hooks_test.py\"],\n    deps = [\n        \":sync_training_hooks\",\n    ],\n)\n\npy_library(\n    name = \"service_discovery\",\n    srcs = [\"service_discovery.py\"],\n    deps = [\n        \":consul\",\n        \":mlp_utils\",\n        \":zk_utils\",\n    ],\n)\n\npy_test(\n    name = \"service_discovery_test\",\n    srcs = [\"service_discovery_test.py\"],\n    deps = [\n        \":service_discovery\",\n    ],\n)\n\npy_library(\n    name = \"ragged_utils\",\n    srcs = [\"ragged_utils.py\"],\n    deps = [\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"ragged_utils_test\",\n    srcs = [\"ragged_utils_test.py\"],\n    deps = [\n        \":ragged_utils\",\n    ],\n)\n\npy_library(\n    name = \"tensor_utils\",\n    srcs = [\"tensor_utils.py\"],\n    deps = [\n        \":static_reshape_op\",\n    ],\n)\n\npy_test(\n    name = \"tensor_utils_test\",\n    srcs = [\"tensor_utils_test.py\"],\n    deps = [\n        \":tensor_utils\",\n    ],\n)\n\npy_library(\n    name = \"graph_meta\",\n    srcs = [\"graph_meta.py\"],\n)\n\npy_library(\n    name = \"graph_utils\",\n    srcs = [\"graph_utils.py\"],\n)\n\npy_library(\n    name = \"consul\",\n    srcs = [\"consul.py\"],\n)\n\npy_test(\n    name = \"consul_test\",\n    srcs = [\"consul_test.py\"],\n    deps = [\n        \":consul\",\n    ],\n)\n\n\npy_library(\n    name = \"barrier_ops\",\n    srcs = [\"barrier_ops.py\"],\n    deps = [\n        \":basic_restore_hook\",\n    ],\n)\n\npy_test(\n    name = \"barrier_ops_test\",\n    srcs = [\"barrier_ops_test.py\"],\n    deps = [\n        \":barrier_ops\",\n    ],\n)\n\npy_library(\n    name = \"basic_restore_hook\",\n    srcs = [\"basic_restore_hook.py\"],\n)\n\npy_test(\n    name = \"basic_restore_hook_test\",\n    srcs = [\"basic_restore_hook_test.py\"],\n    deps = [\n        \":basic_restore_hook\",\n    ],\n)\n\npy_library(\n    name = \"prefetch_queue\",\n    srcs = [\"prefetch_queue.py\"],\n    deps = [\n        \":nested_tensors\",\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"prefetch_queue_test\",\n    srcs = [\"prefetch_queue_test.py\"],\n    deps = [\n        \":prefetch_queue\",\n    ],\n)\n\npy_proto_library(\n    name = \"monolith_checkpoint_state_proto\",\n    srcs = [\":monolith_checkpoint_state.proto\"],\n)\n\npy_library(\n    name = \"save_utils\",\n    srcs = [\"save_utils.py\"],\n    deps = [\n        \":dense_reload_utils\",\n        \":monolith_checkpoint_state_proto\",\n        \":session_run_hooks\",\n        \":utils\",\n        \"//monolith/native_training:native_task_context\",\n        \"//monolith/native_training/metric:cli\",\n    ],\n)\n\npy_test(\n    name = \"save_utils_test\",\n    srcs = [\"save_utils_test.py\"],\n    deps = [\n        \":save_utils\",\n        requirement(\"freezegun\"),\n    ],\n)\n\npy_library(\n    name = \"sync_hooks\",\n    srcs = [\"sync_hooks.py\"],\n)\n\npy_test(\n    name = \"sync_hooks_test\",\n    srcs = [\"sync_hooks_test.py\"],\n    deps = [\n        \":sync_hooks\",\n    ],\n)\n\npy_test(\n    name = \"restore_test\",\n    srcs = [\"restore_test.py\"],\n    deps = [\n        \":hash_table_ops\",\n        \":save_utils\",\n        \":utils\",\n    ],\n)\n\npy_library(\n    name = \"variables\",\n    srcs = [\"variables.py\"],\n    deps = [\n        \":graph_meta\",\n    ],\n)\n\npy_test(\n    name = \"variables_test\",\n    srcs = [\"variables_test.py\"],\n    deps = [\n        \":test_utils\",\n        \":variables\",\n    ],\n)\n\npy_library(\n    name = \"ps_benchmark\",\n    srcs = [\"ps_benchmark.py\"],\n    deps = [\n        \":logging_ops\",\n        \":native_task\",\n        \":utils\",\n        \"//monolith/native_training/optimizers:adamom\",\n    ],\n)\n\npy_test(\n    name = \"ps_benchmark_test\",\n    srcs = [\"ps_benchmark_test.py\"],\n    deps = [\n        \":cpu_training\",\n        \":ps_benchmark\",\n    ],\n)\n\npy_library(\n    name = \"estimator\",\n    srcs = [\"estimator.py\"],\n    deps = [\n        \":cpu_training\",\n        \":distribution_utils\",\n        \":env_utils\",\n        \":monolith_export\",\n        \":native_task\",\n        \":runner_utils\",\n        \":service_discovery\",\n        \":zk_utils\",\n        \"//monolith:utils\",\n        \"//monolith/agent_service:backends\",\n        \"//monolith/agent_service:replica_manager\",\n        \"//monolith/agent_service:utils\",\n        \"//monolith/native_training/data:item_pool_hook\",\n        \"//monolith/native_training/model_export:saved_model_exporters\",\n    ],\n)\n\n# This test is buggy.\npy_test(\n    name = \"estimator_test\",\n    srcs = [\"estimator_test.py\"],\n    deps = [\n        \":cpu_training\",\n        \":estimator\",\n        \":input\",\n        \":model\",\n        \":native_task\",\n        \":service_discovery\",\n        \":utils\",\n        \"//monolith/native_training/data/training_instance:instance_dataset_ops_py\",\n        \"//monolith/native_training/data/training_instance:parse_instance_ops_py\",\n        \"//monolith/native_training/model_export:saved_model_exporters\",\n    ],\n)\n\n\n# This test is buggy.\n# py_test(\n#     name = \"estimator_dist_test\",\n#     srcs = [\"estimator_dist_test.py\"],\n#     deps = [\n#         \":cpu_training\",\n#         \":estimator\",\n#         \":input\",\n#         \":model\",\n#         \":native_task\",\n#         \":service_discovery\",\n#         \":utils\",\n#         \"//monolith/native_training/data/training_instance:instance_dataset_ops_py\",\n#         \"//monolith/native_training/data/training_instance:parse_instance_ops_py\",\n#         \"//monolith/native_training/model_export:saved_model_exporters\",\n#         \"//monolith/native_training/tasks/reckon:params\",\n#     ],\n# )\n\npy_library(\n    name = \"zk_utils\",\n    srcs = [\"zk_utils.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":env_utils\",\n        requirement(\"kazoo\"),\n    ],\n)\n\n\npy_library(\n    name = \"device_utils\",\n    srcs = [\"device_utils.py\"],\n    deps = [\n        \":distribution_utils\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_test(\n    name = \"device_utils_test\",\n    srcs = [\"device_utils_test.py\"],\n    deps = [\n        \":device_utils\",\n    ],\n)\n\npy_library(\n    name = \"gflags_utils\",\n    srcs = [\"gflags_utils.py\"],\n)\n\npy_test(\n    name = \"gflags_utils_test\",\n    srcs = [\"gflags_utils_test.py\"],\n    deps = [\n        \":gflags_utils\",\n    ],\n)\n\npy_library(\n    name = \"runner_utils\",\n    srcs = [\"runner_utils.py\"],\n    deps = [\n        \":cpu_training\",\n        \":env_utils\",\n        \":gflags_utils\",\n        \":monolith_checkpoint_state_proto\",\n        \":service_discovery\",\n    ],\n)\n\npy_test(\n    name = \"runner_utils_test\",\n    srcs = [\"runner_utils_test.py\"],\n    deps = [\n        \":runner_utils\",\n    ],\n)\n\npy_library(\n    name = \"learning_rate_functions\",\n    srcs = [\"learning_rate_functions.py\"],\n)\n\npy_test(\n    name = \"learning_rate_functions_test\",\n    srcs = [\"learning_rate_functions_test.py\"],\n    deps = [\n        \":learning_rate_functions\",\n    ],\n)\n\npy_library(\n    name = \"yarn_runtime\",\n    srcs = [\"yarn_runtime.py\"],\n    deps = [\n        \":net_utils\",\n        \"//monolith/native_training/proto:primus_am_service_py_proto\",\n        \"//monolith/native_training/proto:primus_am_service_py_proto_grpc\",\n    ],\n)\n\npy_test(\n    name = \"yarn_runtime_test\",\n    srcs = [\"yarn_runtime_test.py\"],\n    deps = [\n        \"yarn_runtime\",\n    ],\n)\n\npy_library(\n    name = \"net_utils\",\n    srcs = [\"net_utils.py\"],\n)\n\npy_test(\n    name = \"net_utils_test\",\n    srcs = [\"net_utils_test.py\"],\n    deps = [\n        \":net_utils\",\n    ],\n)\n\npy_library(\n    name = \"cluster_manager\",\n    srcs = [\"cluster_manager.py\"],\n    deps = [\n        \":service_discovery\",\n        \"//monolith/native_training/metric:cli\",\n    ],\n)\n\npy_test(\n    name = \"cluster_manager_test\",\n    srcs = [\"cluster_manager_test.py\"],\n    deps = [\n        \":cluster_manager\",\n    ],\n)\n\npy_library(\n    name = \"signal_utils\",\n    srcs = [\"signal_utils.py\"],\n)\n\npy_test(\n    name = \"signal_utils_test\",\n    srcs = [\"signal_utils_test.py\"],\n    deps = [\n        \":signal_utils\",\n    ],\n)\n\npy_library(\n    name = \"gen_seq_mask\",\n    srcs = [\"gen_seq_mask.py\"],\n    deps = [\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"gen_seq_mask_test\",\n    srcs = [\"gen_seq_mask_test.py\"],\n    deps = [\n        \":gen_seq_mask\",\n        \"//monolith:utils\",\n    ],\n)\n\npy_test(\n    name = \"serving_ps_test\",\n    srcs = [\"serving_ps_test.py\"],\n    data = [\"//idl:example_cc_proto\"],\n    deps = [\n        \":distribution_ops\",\n        \"//idl:example_py_proto\",\n        \"//monolith:utils\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_library(\n    name = \"dense_reload_utils\",\n    srcs = [\"dense_reload_utils.py\"],\n    deps = [\n        \":basic_restore_hook\",\n        \"//monolith/native_training/model_export:export_context\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_test(\n    name = \"dense_reload_utils_test\",\n    srcs = [\"dense_reload_utils_test.py\"],\n    deps = [\n        \":dense_reload_utils\",\n        \"//monolith:utils\",\n    ],\n)\n\npy_library(\n    name = \"nested_tensors\",\n    srcs = [\"nested_tensors.py\"],\n)\n\npy_test(\n    name = \"nested_tensors_test\",\n    srcs = [\"nested_tensors_test.py\"],\n    deps = [\n        \":nested_tensors\",\n    ],\n)\n\npy_library(\n    name = \"mlp_utils\",\n    srcs = [\"mlp_utils.py\"],\n    deps = [\n        \":distribution_utils\",\n        \":yarn_runtime\",\n        \"//monolith/native_training/model_export:export_context\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/alert/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_library\", \"py_test\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\n\npackage(default_visibility = [\"//monolith/native_training/alert:__subpackages__\"])\n\n# Including all public interfaces.\npy_library(\n    name = \"alert\",\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":alert_manager\",\n        \":alert_py_proto\",\n    ],\n)\n\npy_proto_library(\n    name = \"alert_py_proto\",\n    srcs = [\"alert.proto\"],\n)\n\npy_library(\n    name = \"alert_manager_internal_deps\",\n)\n\npy_library(\n    name = \"alert_manager\",\n    srcs = [\"alert_manager.py\"],\n    deps = [\n        \":alert_manager_internal_deps\",\n        \":alert_py_proto\",\n    ],\n)\n\npy_test(\n    name = \"alert_manager_test\",\n    srcs = [\"alert_manager_test.py\"],\n    deps = [\n        \":alert_manager\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/alert/alert.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\n\npackage monolith;\n\nmessage KafkaAlertProto {\n  optional string topic = 1;\n  optional string group = 2;\n}\n\nmessage TrainingAlertProto { optional string prefix = 1; }\n\nmessage AlertMessageProto { optional string user = 1; }\n\nmessage AlertProto {\n  optional AlertMessageProto alert_message = 1;\n  optional KafkaAlertProto kafka_alert = 2;\n  optional TrainingAlertProto training_alert = 3;\n\n  // How long the monitoring will be started\n  optional int64 start_delay_sec = 1000;\n  optional int64 check_interval_sec = 10001 [ default = 1800 ];\n}\n"
  },
  {
    "path": "monolith/native_training/alert/alert_manager.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport copy\nimport threading\nimport time\nimport traceback\nfrom typing import List, NamedTuple\n\nfrom absl import flags\nfrom absl import logging\nfrom google.protobuf import text_format\n\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_string(\"monolith_alert_proto\", \"\",\n                    \"The text format of alert proto.\")\ndef get_default_alert_manager():\n  return None\n"
  },
  {
    "path": "monolith/native_training/alert/alert_manager_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport threading\nimport unittest\nfrom unittest import mock\n\nfrom absl.testing import absltest\nfrom absl.testing import flagsaver\nfrom absl import flags\nfrom absl import app\nfrom google.protobuf import text_format\n\nfrom monolith.native_training.alert import alert_pb2\nfrom monolith.native_training.alert import alert_manager\n\nFLAGS = flags.FLAGS\n\n\nif __name__ == \"__main__\":\n  absltest.main()\n"
  },
  {
    "path": "monolith/native_training/barrier_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n#coding:utf-8\nimport time\nimport threading\n\nfrom absl import logging\n\nimport tensorflow as tf\n\nfrom monolith.native_training import basic_restore_hook\n\n\nclass BarrierAlreadyPlacedError(Exception):\n  pass\n\n\nclass BarrierOp:\n  \"\"\"\n  A barrier operation that used to blocking worker by chief.\n  Thread safe.\n  \"\"\"\n\n  def __init__(self,\n               capacity,\n               is_chief=True,\n               wait_seconds=1,\n               name_prefix=\"default\",\n               barrier_callbacks=None):\n    self._capacity = capacity\n    self._wait_seconds = wait_seconds\n    with tf.name_scope(name_prefix + \"_barrier_op\"):\n      # For non-chief workers, barrier vars are treated as global variables.\n      collections = [tf.compat.v1.GraphKeys.LOCAL_VARIABLES\n                    ] if is_chief else [tf.compat.v1.GraphKeys.VARIABLES]\n      self._barrier_vars = tf.compat.v1.get_variable(\"barrier_var\",\n                                                     initializer=[False] *\n                                                     capacity,\n                                                     collections=collections)\n      self._idx_ph = tf.compat.v1.placeholder(tf.int32,\n                                              shape=[],\n                                              name=\"index_placeholder\")\n      self._place_op = self._barrier_vars[self._idx_ph].assign(True)\n      self._remove_op = self._barrier_vars[self._idx_ph].assign(False)\n      self._barrier_placed_tensor = self._barrier_vars[0]\n      self._barrier_callbacks = barrier_callbacks or []\n      self._action = tf.compat.v1.get_variable(\n          \"barrier_op_action\",\n          dtype=tf.string,\n          initializer=\"\",\n          trainable=False,\n          collections=[tf.compat.v1.GraphKeys.LOCAL_VARIABLES])\n      self._action_placeholder = tf.compat.v1.placeholder(\n          tf.string, [], \"barrier_op_action_placeholder\")\n      self._action_assign = self._action.assign(self._action_placeholder,\n                                                read_value=False)\n      self._lock = threading.Lock()\n\n  def place_barrier(self, session, action: str = \"\"):\n    with self._lock:\n      if self.is_barrier_placed(session):\n        raise BarrierAlreadyPlacedError()\n      session.run([self._place_op, self._action_assign],\n                  feed_dict={\n                      self._action_placeholder: action,\n                      self._idx_ph: 0\n                  })\n      self._run_barrier_callbacks(action, session)\n\n  def remove_barrier(self, session):\n    with self._lock:\n      # We are more generous about removing barrier\n      # We don't check barrier state here\n      session.run(self._remove_op, feed_dict={self._idx_ph: 0})\n\n  def is_barrier_placed(self, session):\n    return session.run(self.barrier_placed_tensor)\n\n  @property\n  def barrier_placed_tensor(self):\n    return self._barrier_placed_tensor\n\n  @property\n  def capacity(self):\n    return self._capacity\n\n  def is_barrier_removed(self, session):\n    return not self.is_barrier_placed(session)\n\n  def wait_until_barrier_removed(self, session, index):\n    with self._lock:\n      if index <= 0 or index >= self._capacity:\n        raise ValueError(\n            \"Index [{}] must be non-negative and less than capacity [{}]. \".\n            format(index, self._capacity))\n      session.run(self._place_op, feed_dict={self._idx_ph: index})\n      action = session.run(self._action).decode()\n      self._run_barrier_callbacks(action, session)\n\n    while not self.is_barrier_removed(session):\n      logging.log_every_n_seconds(\n          logging.INFO,\n          \"The worker {} waits until barrier removed.\".format(index), 60)\n      time.sleep(self._wait_seconds)\n\n    session.run(self._remove_op, feed_dict={self._idx_ph: index})\n\n  def is_all_blocked(self, session):\n    barriers = session.run(self._barrier_vars)\n    count = sum(barriers)\n    return count == self._capacity\n\n  def is_none_blocked(self, session):\n    barriers = session.run(self._barrier_vars)\n    count = sum(barriers)\n    return count == 0\n\n  def get_unblocked_indices(self, session):\n    barriers = session.run(self._barrier_vars)\n    return [i for i in range(self._capacity) if not barriers[i]]\n\n  def get_blocked_indices(self, session):\n    barriers = session.run(self._barrier_vars)\n    return [i for i in range(self._capacity) if barriers[i]]\n\n  def _run_barrier_callbacks(self, action: str, session: tf.compat.v1.Session):\n    for callback in self._barrier_callbacks:\n      callback(action, session)\n\n\nclass BarrierHook(tf.estimator.SessionRunHook):\n  \"\"\"During training, check the barrier condition for worker.\"\"\"\n\n  def __init__(self, index, barrier_op: BarrierOp):\n    self._index = index\n    self._barrier_op = barrier_op\n\n  def before_run(self, run_context):\n    return tf.estimator.SessionRunArgs(self._barrier_op.barrier_placed_tensor)\n\n  def after_run(self, run_context, run_values):\n    barrier_placed_value = run_values.results\n\n    if self._index > 0 and barrier_placed_value:\n      self._barrier_op.wait_until_barrier_removed(run_context.session,\n                                                  self._index)\n"
  },
  {
    "path": "monolith/native_training/barrier_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tempfile\nimport threading\nimport time\n\nimport tensorflow as tf\nfrom tensorflow.python.training import monitored_session\n\nfrom monolith.native_training import barrier_ops\n\n\nclass BarrierOpsTest(tf.test.TestCase):\n\n  def test_basic(self):\n    barrier_op = barrier_ops.BarrierOp(2, False)\n    with tf.compat.v1.Session() as sess:\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      self.evaluate(tf.compat.v1.local_variables_initializer())\n      barrier_op.place_barrier(sess)\n      self.assertTrue(barrier_op.is_barrier_placed(sess))\n      with self.assertRaises(barrier_ops.BarrierAlreadyPlacedError):\n        barrier_op.place_barrier(sess)\n      barrier_op.remove_barrier(sess)\n      self.assertTrue(barrier_op.is_barrier_removed(sess))\n\n  def _run(self, train_op, sess, step=1):\n    for i in range(step):\n      sess.run(train_op)\n\n  def test_barrier_hook_not_blocked(self):\n    with tf.compat.v1.Graph().as_default():\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      train_op = tf.compat.v1.assign_add(global_step, 1)\n      barrier_op = barrier_ops.BarrierOp(2, False)\n      hook = barrier_ops.BarrierHook(1, barrier_op)\n\n      with tf.compat.v1.Session() as sess:\n        self.evaluate(tf.compat.v1.global_variables_initializer())\n        self.evaluate(tf.compat.v1.local_variables_initializer())\n        mon_sess = monitored_session._HookedSession(sess, [hook])\n        worker = threading.Thread(target=self._run,\n                                  args=(train_op, mon_sess, 5))\n        worker.daemon = True\n        worker.start()\n        worker.join()\n\n        self.assertEqual(5, sess.run(global_step))\n\n  def test_barrier_hook_blocked(self):\n    with tf.compat.v1.Graph().as_default():\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      train_op = tf.compat.v1.assign_add(global_step, 1)\n\n      called_variable = tf.Variable(False, trainable=False)\n      barrier_action = \"test_action\"\n\n      def action_callback(action, session):\n        if action == barrier_action:\n          session.run(called_variable.assign(True))\n\n      barrier_op = barrier_ops.BarrierOp(2,\n                                         False,\n                                         barrier_callbacks=[action_callback])\n      hook = barrier_ops.BarrierHook(1, barrier_op)\n\n      with tf.compat.v1.Session() as sess:\n        self.evaluate(tf.compat.v1.global_variables_initializer())\n        self.evaluate(tf.compat.v1.local_variables_initializer())\n        mon_sess = monitored_session._HookedSession(sess, [hook])\n\n        barrier_op.place_barrier(sess, action=barrier_action)\n        worker = threading.Thread(target=self._run,\n                                  args=(train_op, mon_sess, 5))\n        worker.daemon = True\n        worker.start()\n\n        while not barrier_op.is_all_blocked(sess):\n          time.sleep(0.1)\n        # Hook is pending.\n        self.assertEqual(1, sess.run(global_step))\n        self.assertEqual(sess.run(called_variable), True)\n\n        barrier_op.remove_barrier(sess)\n        worker.join()\n        self.assertTrue(barrier_op.is_none_blocked(sess))\n        self.assertEqual(5, sess.run(global_step))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/basic_restore_hook.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n#coding:utf-8\nfrom absl import logging\n\nfrom tensorflow.python.training import session_run_hook\n\n\nclass CheckpointRestorerListener():\n  \"\"\"Interface for listeners that take action before or after restore.\"\"\"\n\n  def begin(self):\n    pass\n\n  def before_restore(self, session):\n    pass\n\n  def after_restore(self, session):\n    pass\n\n  def end(self, session):\n    pass\n\n\nclass CheckpointRestorerHook(session_run_hook.SessionRunHook):\n  \"\"\"\n  Restores checkpoints at the begining. Use to call 'CheckpointRestorerListener'.\n  The real restore action is implemented in 'CheckpointRestorerListener'.\n\n  \"\"\"\n\n  def __init__(self, listeners=None):\n    \"\"\"Initializes a `CheckpointRestorerHook`.\n\n    Args:\n      listeners: List of `CheckpointRestorerListener` subclass instances. Used for\n        callbacks that run immediately before or after this hook restores the\n        checkpoint.\n\n    \"\"\"\n    logging.info(\"Create CheckpointRestorerHook.\")\n    self._listeners = listeners or []\n\n  def begin(self):\n    for l in self._listeners:\n      l.begin()\n\n  def after_create_session(self, session, coord):\n    self._restore(session)\n\n  def _restore(self, session):\n    \"\"\"Restores the latest checkpoint.\"\"\"\n    logging.info(\"Calling checkpoint restorer listeners.\")\n    for l in self._listeners:\n      l.before_restore(session)\n\n    # None restore actions in this hook.\n\n    for l in self._listeners:\n      l.after_restore(session)\n"
  },
  {
    "path": "monolith/native_training/basic_restore_hook_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom tensorflow.python.training import session_run_hook\n\nfrom monolith.native_training import basic_restore_hook\n\n\nclass CountCheckpointRestorerListener(\n    basic_restore_hook.CheckpointRestorerListener):\n\n  def __init__(self):\n    self.begin_count = 0\n    self.before_restore_count = 0\n    self.after_restore_count = 0\n\n  def begin(self):\n    self.begin_count += 1\n\n  def before_restore(self, session):\n    self.before_restore_count += 1\n\n  def after_restore(self, session):\n    self.after_restore_count += 1\n\n  def get_counts(self):\n    return {\n        'begin': self.begin_count,\n        'before_restore': self.before_restore_count,\n        'after_restore': self.after_restore_count\n    }\n\n\nclass CountHook(session_run_hook.SessionRunHook):\n\n  def __init__(self):\n    self.after_create_session_count = 0\n    self.before_run_count = 0\n    self.after_run_count = 0\n    self.end_count = 0\n\n  def after_create_session(self, session, coord):\n    self.after_create_session_count += 1\n\n  def before_run(self, run_context):\n    self.before_run_count += 1\n\n  def after_run(self, run_context, run_values):\n    self.after_run_count += 1\n\n  def end(self, session):\n    self.end_count += 1\n\n  def get_counts(self):\n    return {\n        'after_create_session': self.after_create_session_count,\n        'before_run': self.before_run_count,\n        'after_run': self.after_run_count,\n        'end': self.end_count,\n    }\n\n\nclass CheckpointRestorerHookTest(tf.test.TestCase):\n\n  def test_restore_only_in_after_create_session(self):\n    with tf.compat.v1.Graph().as_default():\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      train_op = tf.compat.v1.assign_add(global_step, 1)\n      listener = CountCheckpointRestorerListener()\n      hook1 = basic_restore_hook.CheckpointRestorerHook(listeners=[listener])\n      hook2 = CountHook()\n\n      with tf.compat.v1.train.SingularMonitoredSession(\n          hooks=[hook1, hook2]) as sess:\n        # after_create_session\n        self.assertEqual({\n            'begin': 1,\n            'before_restore': 1,\n            'after_restore': 1,\n        }, listener.get_counts())\n        self.assertEqual(\n            {\n                'after_create_session': 1,\n                'before_run': 0,\n                'after_run': 0,\n                'end': 0,\n            }, hook2.get_counts())\n\n        for _ in range(2):\n          sess.run(train_op)\n\n    self.assertEqual({\n        'begin': 1,\n        'before_restore': 1,\n        'after_restore': 1,\n    }, listener.get_counts())\n    self.assertEqual(\n        {\n            'after_create_session': 1,\n            'before_run': 2,\n            'after_run': 2,\n            'end': 1,\n        }, hook2.get_counts())\n\n  def test_two_listeners_with_restorer(self):\n    with tf.compat.v1.Graph().as_default():\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      train_op = tf.compat.v1.assign_add(global_step, 1)\n      listener1 = CountCheckpointRestorerListener()\n      listener2 = CountCheckpointRestorerListener()\n      hook = basic_restore_hook.CheckpointRestorerHook(\n          listeners=[listener1, listener2])\n\n      with tf.compat.v1.train.SingularMonitoredSession(hooks=[hook]) as sess:\n        self.assertEqual({\n            'begin': 1,\n            'before_restore': 1,\n            'after_restore': 1,\n        }, listener1.get_counts())\n        self.assertEqual(listener1.get_counts(), listener1.get_counts())\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/clip_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nfrom typing import List, Tuple\n\nimport tensorflow as tf\nfrom monolith.native_training import device_utils\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\ngen_clip_ops = gen_monolith_ops\n\n\ndef _global_norm(t_list: List[tf.Tensor]) -> tf.Tensor:\n  \"\"\"Computes the global norm of multiple tensors.\"\"\"\n  if len(t_list) == 0:\n    return None\n  l2_sum = gen_clip_ops.global_l2_reduce(t_list)\n  return tf.sqrt(l2_sum)\n\n\ndef clip_by_global_norm(t_list: List[tf.Tensor],\n                        clip_norm: tf.Tensor,\n                        use_norm=None) -> Tuple[List[tf.Tensor], tf.Tensor]:\n  \"\"\"Clips values of multiple tensors by the ratio of the sum of their norms.\n\n  Given a tuple or list of tensors `t_list`, and a clipping ratio `clip_norm`,\n  this operation returns a list of clipped tensors `list_clipped`\n  and the global norm (`global_norm`) of all tensors in `t_list`. Optionally,\n  if you've already computed the global norm for `t_list`, you can specify\n  the global norm with `use_norm`.\n\n  To perform the clipping, the values `t_list[i]` are set to:\n      t_list[i] * clip_norm / max(global_norm, clip_norm)\n  where:\n      global_norm = sqrt(sum([l2norm(t)**2 for t in t_list]))\n\n  If `clip_norm > global_norm` then the entries in `t_list` remain as they are,\n  otherwise they're all shrunk by the global ratio.\n  If `global_norm == infinity` then the entries in `t_list` are all set to `NaN`\n  to signal that an error occurred.\n\n  Args:\n    t_list: A list of mixed `Tensors`.\n    clip_norm: A 0-D (scalar) `Tensor` > 0. The clipping ratio.\n    use_norm: A 0-D (scalar) `Tensor` of type `float` (optional). The global\n      norm to use. If not provided, TensorFlow `global_norm()` is used to compute the norm.\n  Returns:\n    list_clipped: A list of `Tensors` of the same type as `list_t`.\n    global_norm: A 0-D (scalar) `Tensor` representing the global norm.\n  Raises:\n    TypeError: If `t_list` is not a sequence.\n  \"\"\"\n  with tf.name_scope('clip_by_global_norm'):\n    if not isinstance(t_list, list):\n      raise TypeError(\"t_list should be a list\")\n    if len(t_list) == 0:\n      return t_list, 0\n    if use_norm is not None:\n      return gen_clip_ops.monolith_clip_by_global_norm(\n        t_list, use_norm, clip_norm), use_norm\n    if device_utils.within_placement_context_of(\"GPU\"):\n      return gen_clip_ops.monolith_clip_by_global_norm_fused(t_list, clip_norm)\n    norm_fn = _global_norm if device_utils.within_placement_context_of(\n        \"GPU\") else tf.linalg.global_norm\n    global_norm = norm_fn(t_list)\n    list_clipped = gen_clip_ops.monolith_clip_by_global_norm(\n        t_list, global_norm, clip_norm)\n  return list_clipped, global_norm\n"
  },
  {
    "path": "monolith/native_training/clip_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.framework import test_util\n\nfrom monolith.native_training import clip_ops\n\n\nclass ClipOpsTest(tf.test.TestCase):\n\n  def _test_clip_by_global_norm(self, inputs, clip_norm, expected=None):\n    with tf.compat.v1.Session() as sess, test_util.use_gpu():\n      t_list = [ops.convert_to_tensor(t, dtype=tf.float32) for t in inputs]\n      clipped = clip_ops.clip_by_global_norm(t_list, clip_norm)\n      r, second_branch_check_input_soundness = sess.run([clipped, t_list])\n      result, _ = r\n      if expected is None:\n        expected, _ = sess.run(tf.clip_by_global_norm(t_list, clip_norm))\n      self.assertAllClose(result, expected)\n      # second_branch_check_input_soundness will break allclose,\n      #   if input mem (t_list) gets modified inplace (clipped).\n      self.assertAllClose(second_branch_check_input_soundness, inputs)\n\n  def test_clip_by_global_norm(self):\n    # Simple example\n    self._test_clip_by_global_norm([[-3.0, 0.0, 0.0], [4.0, 0.0, 0.0]], 4.0,\n                                   [[-2.4, 0.0, 0.0], [3.2, 0.0, 0.0]])\n    # Uneven shape example\n    self._test_clip_by_global_norm([[-3.0, 0.0, 0.0], [0.0, 0.0, 4.0, 0.0]],\n                                   4.0,\n                                   [[-2.4, 0.0, 0.0], [0.0, 0.0, 3.2, 0.0]])\n    # No clipping.\n    self._test_clip_by_global_norm([[1.0, 0.0, 0.0], [1.0, 0.0, 0.0]], 4.0,\n                                   [[1.0, 0.0, 0.0], [1.0, 0.0, 0.0]])\n    # Zero norm.\n    self._test_clip_by_global_norm([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], 4.0,\n                                   [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]])\n    # Exploded grad.\n    nan_arr = np.empty((2, 3))\n    nan_arr[:] = np.nan\n    self._test_clip_by_global_norm(\n        [[float('inf'), float('inf'), float('inf')],\n         [float('inf'), float('inf'), float('inf')]], 4.0, nan_arr)\n    # Large grad.\n    DENSE_SHAPES = [(328, 128), (128,), (128,), (128, 64), (64,), (64,), (1,),\n                    (256, 256), (256,), (256,), (256, 128), (128,), (128,),\n                    (128, 1), (1,), (1,), (2488, 256), (256,), (256,),\n                    (3184, 256), (256,), (256,), (96, 128), (128,), (128,),\n                    (128, 64), (64,), (64,), (1,), (64, 16), (16,), (16,),\n                    (1609, 2048), (2048,), (2048,), (2048, 1024), (1024,),\n                    (1024,), (1024, 512), (512,), (512,), (512, 256), (256,),\n                    (256,), (256, 1), (1,), (1,), (96, 64), (64,), (64,),\n                    (64, 1), (1,), (1,)]\n    grads = [np.random.uniform(size=s) for s in DENSE_SHAPES]\n    self._test_clip_by_global_norm(grads, 1.0)\n\n\nclass NormOpsTest(tf.test.TestCase):\n\n  def _test_global_norm(self, inputs, expected):\n    with tf.compat.v1.Session() as sess, test_util.use_gpu():\n      inputs = [ops.convert_to_tensor(t, dtype=tf.float32) for t in inputs]\n      g = sess.run(clip_ops._global_norm(inputs))\n      self.assertAllClose(g, expected)\n\n  @test_util.run_gpu_only\n  def test_it(self):\n    self._test_global_norm(\n        [[float('inf'), float('inf'), float('inf')],\n         [float('inf'), float('inf'), float('inf')]], float('inf'))\n    self._test_global_norm([[-3.0, 0.0, 0.0], [4.0, 0.0, 0.0]], 5.0)\n    self._test_global_norm([[-3.0, 0.0, 0.0], [0.0, 0.0, 4.0, 0.0]], 5.0)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/cluster_manager.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nfrom typing import Dict, List, Tuple\n\nfrom absl import logging\nimport tensorflow as tf\n\nfrom monolith.native_training.metric import cli\nfrom monolith.native_training.service_discovery import ServiceDiscovery\n\n_MCLI = cli.get_cli(prefix=\"monolith.containers\")\n\n\ndef emit_store(name, value, tagkv=None):\n  _MCLI.emit_store(name, value, tagkv)\n\n\ndef generate_session_config(cluster_and_task=None):\n  if cluster_and_task is None:\n    session_config = tf.compat.v1.ConfigProto(allow_soft_placement=True)\n  else:\n    cluster = cluster_and_task[0]\n    task = cluster_and_task[1]\n    spec = tf.train.ClusterSpec(cluster)\n    device_filters = [\"/job:ps\", \"/job:chief\"]\n    if task[\"type\"] != \"chief\":\n      device_filters += [\"/job:{}/task:{}\".format(task[\"type\"], task[\"index\"])]\n    session_config = tf.compat.v1.ConfigProto(cluster_def=spec.as_cluster_def(),\n                                              allow_soft_placement=True,\n                                              device_filters=device_filters)\n  session_config.share_cluster_devices_in_session = True\n  session_config.experimental.share_session_state_in_clusterspec_propagation = True\n  # grappler doesn't really understand RaggedTensor.\n  session_config.graph_options.rewrite_options.disable_meta_optimizer = True\n  return session_config\n\n\ndef get_training_cluster(\n    discovery: ServiceDiscovery,\n    worker_addr: str,\n    index: int,\n    num_redundant_ps: int,\n    num_required_ps: int,\n    num_workers: int,\n    model_dir: str,\n    uuid: str,\n    model_name: str = None,\n    cluster_type: str = \"stable\") -> Tuple[Dict[str, List], Dict]:\n  if index == 0:\n    if num_redundant_ps:\n      file_name = _get_ps_cluster_file_name(model_dir, uuid)\n      # In the case of chief restart, first obtain the ps cluster from the file.\n      ps_addrs = _fetch_ps_cluster_from_file(file_name, timeout=0)\n      if len(ps_addrs) != num_required_ps:\n        # The ps cluster cannot be obtained from the file, so it is queried\n        # through service discovery. Then assign the ps cluster to the file.\n        ps_addrs = _query_ps_cluster(discovery, num_required_ps, model_name,\n                                     cluster_type)\n        _save_ps_cluster_to_file(file_name, ps_addrs)\n    else:\n      # By default, the ps cluster is queried by discovery.\n      ps_addrs = _query_ps_cluster(discovery, num_required_ps, model_name,\n                                   cluster_type)\n\n    fake_worker_list = [\"0.0.0.0:{}\".format(i) for i in range(1, num_workers)]\n    cluster = {\n        \"chief\": [worker_addr],\n        \"worker\": fake_worker_list,\n        \"ps\": ps_addrs,\n    }\n    task = {\"type\": \"chief\", \"index\": 0}\n\n  else:\n    chief_addr = _query_chief_addr(discovery)\n    # Due to current TF limitation (TF_CONFIG doesn't support dict),\n    # we need to provide a fake worker list in cluster\n    worker_index = index - 1\n    fake_worker_list = [\"0.0.0.0:{}\".format(i) for i in range(1, num_workers)]\n    fake_worker_list[worker_index] = worker_addr\n\n    if num_redundant_ps:\n      file_name = _get_ps_cluster_file_name(model_dir, uuid)\n      # Get the ps cluster from the file.\n      ps_addrs = _fetch_ps_cluster_from_file(file_name)\n    else:\n      # By default, the ps cluster is queried by discovery.\n      ps_addrs = _query_ps_cluster(discovery, num_required_ps)\n\n    cluster = {\n        \"chief\": [chief_addr],\n        \"worker\": fake_worker_list,\n        \"ps\": ps_addrs,\n    }\n    task = {\"type\": \"worker\", \"index\": worker_index}\n\n  assert len(cluster[\"ps\"]) == num_required_ps\n  return cluster, task\n\n\ndef _cluster_query_failure_handler():\n  time.sleep(5)\n\n\ndef _query_chief_addr(discovery: ServiceDiscovery):\n  worker_addr_dict = None\n  while True:\n    worker_addr_dict = discovery.query(\"worker\")\n    if 0 in worker_addr_dict:\n      break\n    _cluster_query_failure_handler()\n\n  return worker_addr_dict[0]\n\n\ndef _query_ps_cluster(discovery: ServiceDiscovery,\n                      num_required_ps: int,\n                      model_name: str = None,\n                      cluster_type: str = \"stable\"):\n  start = time.time()\n  ps_addr_dict = None\n  while True:\n    ps_addr_dict = discovery.query(\"ps\")\n    num_left_ps = max(0, num_required_ps - len(ps_addr_dict))\n    logging.info(\"Got {} ps, {} left!\".format(len(ps_addr_dict), num_left_ps))\n    if model_name:\n      tags = {\n          \"model_name\": model_name,\n          \"cluster_type\": cluster_type,\n      }\n      emit_store(\"num_left_ps\", num_left_ps, tags)\n      emit_store(\"job_waiting\", 1, tags)\n    if len(ps_addr_dict) >= num_required_ps:\n      break\n    _cluster_query_failure_handler()\n\n  ps_addrs = [addr for index, addr in sorted(ps_addr_dict.items())\n             ][:num_required_ps]\n  return ps_addrs\n\n\ndef _save_ps_cluster_to_file(file_name: str, ps_addrs: List[str]):\n  ps_str = \",\".join(ps_addrs)\n  tf.io.gfile.makedirs(os.path.dirname(file_name))\n  tmp_name = file_name + \"-tmp\"\n  with tf.io.gfile.GFile(tmp_name, mode=\"w\") as f:\n    f.write(ps_str)\n  tf.io.gfile.rename(tmp_name, file_name, overwrite=True)\n\n\ndef _fetch_ps_cluster_from_file(file_name: str, timeout=1800):\n  ps_str = \"\"\n  start_time = time.time()\n\n  while True:\n    try:\n      with tf.io.gfile.GFile(file_name) as f:\n        ps_str = f.read()\n    except tf.errors.NotFoundError:\n      pass\n\n    if bool(ps_str) or time.time() - start_time > timeout:\n      break\n    _cluster_query_failure_handler()\n\n  ps_addrs = ps_str.split(\",\") if ps_str else []\n  return ps_addrs\n\n\ndef _get_ps_cluster_file_name(model_dir: str, uuid: str):\n  return os.path.join(model_dir, \"ps_cluster_dir\", uuid or \"ps_info\")\n"
  },
  {
    "path": "monolith/native_training/cluster_manager_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport unittest\n\nfrom monolith.native_training import cluster_manager\n\n\nclass ClusterManagerTest(unittest.TestCase):\n\n  def testBasic(self):\n    ps_addrs = [\"0.0.0.0:{}\".format(i) for i in range(3)]\n    file_name = cluster_manager._get_ps_cluster_file_name(\n        model_dir=os.path.join(os.environ[\"TEST_TMPDIR\"], \"ClusterManagerTest\",\n                               self._testMethodName),\n        uuid=self._testMethodName)\n    cluster_manager._save_ps_cluster_to_file(file_name, ps_addrs)\n    new_ps_addrs = cluster_manager._fetch_ps_cluster_from_file(file_name)\n    self.assertEqual(ps_addrs, new_ps_addrs)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/consul.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Consul client from bytedance pylib.\n\"\"\"\nimport json\nimport logging\nimport os\nimport socket\nimport sys\nimport threading\nimport time\nimport traceback\nfrom typing import Dict\n\nfrom six.moves.http_client import HTTPConnection\n\n\nclass ConsulException(Exception):\n  pass\n\n\nclass UnixHTTPConnection(HTTPConnection):\n\n  def __init__(self, path, **kwargs):\n    kwargs[\"host\"] = \"localhost\"\n    HTTPConnection.__init__(self, **kwargs)\n    self.path = path\n\n  def connect(self):\n    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)\n    sock.connect(self.path)\n    self.sock = sock\n\n\nclass Client:\n\n  def __init__(self):\n    self._lock = threading.Lock()\n    self._cache = {}\n    self._consul_sock = \"/opt/tmp/sock/consul.sock\"\n    self._consul_host = os.environ.get(\"CONSUL_HTTP_HOST\") or os.environ.get(\n        \"TCE_HOST_IP\")\n    if not self._consul_host:\n      if os.path.isfile(self._consul_sock):\n        self._consul_host = self._consul_sock\n      else:\n        self._consul_host = \"127.0.0.1\"\n    self._consul_port = int(os.environ.get(\"CONSUL_HTTP_PORT\") or 2280)\n\n  def lookup(self, name, timeout=3, cachetime=0):\n    now = time.time()\n    if cachetime > 0:\n      cache = self._cache.get(name)\n      if cache and now - cache[\"cachetime\"] <= cachetime:\n        return cache[\"ret\"]\n      timeout = timeout if cache else 30\n      with self._lock:\n        ret = self.lookup(name, timeout)\n    else:\n      ret = self._lookup(name, timeout)\n    with self._lock:\n      self._cache[name] = {\n          \"ret\": ret,\n          \"cachetime\": now,\n      }\n    return ret\n\n  def _lookup(self, name, timeout):\n    if self._consul_host.startswith(\"/\"):\n      conn = UnixHTTPConnection(self._consul_host)\n    else:\n      conn = HTTPConnection(self._consul_host,\n                            self._consul_port,\n                            timeout=timeout)\n    conn.request(\"GET\",\n                 \"/v1/lookup/name?name=\" + name + \"&addr-family=dual-stack\")\n    response = conn.getresponse()\n    status = response.status\n    data = response.read()\n    conn.close()\n    if status != 200:\n      logging.error(\"consul: %s %s\", status, data.decode(\"utf8\"))\n      return []\n    return json.loads(data.decode(\"utf8\"))\n\n  def register(self, name, port, tags=None, check_script=None, host=None):\n    d = {\n        \"id\": \"%s-%s\" % (name, port),\n        \"name\": name,\n        \"port\": int(port),\n        \"check\": {\n            \"ttl\": \"60s\",\n        }\n    }\n    if tags is not None:\n      d[\"tags\"] = [\"%s:%s\" % (k, v) for k, v in tags.items()]\n    if check_script:\n      d[\"check\"] = {\"interval\": \"30s\", \"script\": check_script}\n    if not host:\n      host = self._consul_host\n    conn = HTTPConnection(host, self._consul_port, timeout=15)\n    conn.request(\"PUT\", \"/v1/agent/service/register\", json.dumps(d))\n    response = conn.getresponse()\n    status = response.status\n    data = response.read()\n    if status != 200:\n      raise ConsulException(data.decode(\"utf8\"))\n\n    def _health_check():\n      while True:\n        now = time.time()\n        try:\n          conn.request(\"GET\", f\"/v1/agent/check/pass/service:{name}-{port}\")\n          conn.getresponse().read()\n        except socket.error:\n          print(traceback.format_exc(), file=sys.stderr)\n          time.sleep(2)\n          # Immediately retry\n          now -= 30\n        time.sleep(max(30 + now - time.time(), 0))\n\n    th = threading.Thread(name=f\"ConsulHealthCheck-{name}-{port}\",\n                          target=_health_check,\n                          daemon=True)\n    th.start()\n    # Maybe in the future, we want to garbage collect threads.\n\n  def deregister(self, name, port, host=None):\n    host = host or self._consul_host\n    conn = HTTPConnection(host, self._consul_port, timeout=15)\n    conn.request(\"PUT\", \"/v1/agent/service/deregister/%s-%s\" % (name, port))\n    response = conn.getresponse()\n    status = response.status\n    data = response.read()\n    if status != 200:\n      raise ConsulException(data.decode(\"utf8\"))\n    conn.close()\n"
  },
  {
    "path": "monolith/native_training/consul_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nimport unittest\nfrom unittest import mock\n\nimport numpy as np\nfrom absl import logging\nfrom six.moves.http_client import OK\n\nfrom monolith.native_training import consul\n\n_HTTP_CONNECTION_TARGET = \"monolith.native_training.consul.HTTPConnection\"\n\n\nclass ConsulTest(unittest.TestCase):\n\n  def test_lookup(self):\n    with mock.patch(_HTTP_CONNECTION_TARGET) as MockHttpConnection:\n      resp = mock.MagicMock()\n      resp.status = OK\n      data = [{\"Port\": 1234, \"Host\": \"192.168.0.1\", \"Tags\": {\"index\": \"0\"}}]\n      resp.read.return_value = json.dumps(data).encode(\"utf-8\")\n      MockHttpConnection.return_value.getresponse.return_value = resp\n      client = consul.Client()\n      result = client.lookup(\"test_name\")\n      self.assertEqual(result, data)\n\n  def test_register(self):\n    with mock.patch(_HTTP_CONNECTION_TARGET) as MockHttpConnection:\n      resp = mock.MagicMock()\n      resp.status = OK\n      MockHttpConnection.return_value.getresponse.return_value = resp\n      client = consul.Client()\n      client = client.register(\"test_name\", 12345)\n\n  def test_deregister(self):\n    with mock.patch(_HTTP_CONNECTION_TARGET) as MockHttpConnection:\n      resp = mock.MagicMock()\n      resp.status = OK\n      MockHttpConnection.return_value.getresponse.return_value = resp\n      client = consul.Client()\n      client = client.deregister(\"test_name\", 12345)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/cpu_sync_training_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\n\nimport numpy as np\nimport tensorflow as tf\n\n# Note: this needs to be set before monolith.native_training, to ensure the\n# imports work correctly.\nos.environ[\"MONOLITH_WITH_HOROVOD\"] = \"True\"\n\nfrom monolith.native_training import cpu_training, embedding_combiners, feature, device_utils\nfrom monolith.native_training.native_task import NativeTask\nfrom monolith.native_training import entry\nfrom monolith.native_training.data.training_instance.python.parser_utils import advanced_parse\n\nimport horovod.tensorflow as hvd\n\ntest_folder = os.environ[\"TEST_TMPDIR\"]\n\n\nclass FeatureTask(NativeTask):\n  \"\"\"A test task that will collect some information in model_fn.\"\"\"\n\n  def create_input_fn(self, _):\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors({\n          \"feature\": tf.ragged.constant([[1, 2, 3, 4]], dtype=np.int64)\n      }).map(advanced_parse).repeat(5)\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(features, mode, config, **kwargs):\n      slot = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(name=\"slot\"))\n      s = slot.add_feature_slice(5)\n      fc = feature.FeatureColumnV1(slot, \"feature\")\n      embedding = fc.embedding_lookup(s)\n      if mode == tf.estimator.ModeKeys.PREDICT:\n        return tf.estimator.EstimatorSpec(mode, predictions=tf.constant(0))\n      all_embeddings = [fc.get_all_embeddings_concat()]\n      loss = tf.reduce_sum(embedding)\n      grads = tf.gradients(loss, all_embeddings)\n      print1 = tf.print(\"embedding: \", embedding)\n      print2 = tf.print(\"all_embeddings: \", all_embeddings)\n      with tf.control_dependencies([print1, print2]):\n        train_op = tf.group(\n            self._ctx.feature_factory.apply_gradients(zip(\n                grads, all_embeddings)))\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=train_op,\n                                        loss=loss,\n                                        predictions=tf.constant(0))\n\n    return model_fn\n\n\nclass EmbeddingUpdateTask(NativeTask):\n  \"\"\"A test task that will compare TF and monolith embedding update.\"\"\"\n\n  def create_input_fn(self, _):\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors({\n          \"feature\": tf.ragged.constant([[1, 2, 3, 4]], dtype=np.int64),\n          \"tf_feature\": tf.constant([[0, 1, 2, 3]], dtype=np.int64),\n      }).map(advanced_parse).repeat(10)\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(features, mode, config):\n      slot = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(\n              name=\"slot\",\n              default_vec_initializer=entry.ConstantsInitializer(0),\n              default_vec_optimizer=entry.AdagradOptimizer(\n                  learning_rate=0.1, initial_accumulator_value=1)))\n      s = slot.add_feature_slice(5)\n      fc = feature.FeatureColumnV1(slot, \"feature\")\n      embedding = fc.embedding_lookup(s)\n      tf_embeddings = tf.Variable(initial_value=tf.zeros(shape=(4, 5)),\n                                  name='embedding')\n      tf_embedding = tf.reduce_sum(tf.nn.embedding_lookup(\n          params=tf_embeddings, ids=features[\"tf_feature\"]),\n                                   axis=1)\n      if mode == tf.estimator.ModeKeys.PREDICT:\n        return tf.estimator.EstimatorSpec(mode, predictions=tf.constant(0))\n      all_embeddings = [fc.get_all_embeddings_concat()]\n      loss = tf.reduce_sum(embedding)\n      tf_loss = tf.reduce_sum(tf_embedding)\n      grads = tf.gradients(loss, all_embeddings)\n      tf_grads = tf.gradients(tf_loss, tf_embeddings)\n      gs = tf.compat.v1.train.get_or_create_global_step()\n      print1 = tf.print(gs, \"embedding: \", embedding)\n      print2 = tf.print(gs, \"tf_embedding: \", tf_embedding)\n      assert_equal = tf.compat.v1.assert_equal(embedding, tf_embedding)\n      with tf.control_dependencies([print1, print2, assert_equal]):\n        train_op = tf.group(\n            self._ctx.feature_factory.apply_gradients(zip(\n                grads, all_embeddings)),\n            tf.compat.v1.train.AdagradOptimizer(\n                learning_rate=0.1, initial_accumulator_value=1).apply_gradients(\n                    zip(tf_grads, [tf_embeddings])), gs.assign_add(1))\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=train_op,\n                                        loss=loss + tf_loss,\n                                        predictions=tf.constant(0))\n\n    return model_fn\n\n\nclass FloatFeatureTask(NativeTask):\n  \"\"\"A test task that will use float feature in model_fn.\"\"\"\n\n  def create_input_fn(self, _):\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors({\n          \"ragged_feature\": tf.ragged.constant([[0, 0]], dtype=np.int64),\n          \"float_feature\": tf.constant([[1.]], dtype=tf.float32)\n      }).map(advanced_parse)\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(features, mode, **kwargs):\n      slot = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(name=\"slot\"))\n      s = slot.add_feature_slice(5)\n      fc = feature.FeatureColumnV1(slot, \"ragged_feature\")\n      embedding = fc.embedding_lookup(s)\n      float_feature = features[\"float_feature\"]\n      predictions = tf.reduce_sum(float_feature, axis=-1)\n      if mode == tf.estimator.ModeKeys.PREDICT:\n        return tf.estimator.EstimatorSpec(mode, predictions=predictions)\n      all_embeddings = [fc.get_all_embeddings_concat()]\n      loss = tf.reduce_sum(embedding)\n      grads = tf.gradients(loss, all_embeddings)\n      train_op = tf.group(\n          self._ctx.feature_factory.apply_gradients(zip(grads, all_embeddings)))\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=train_op,\n                                        loss=loss,\n                                        predictions=predictions)\n\n    return model_fn\n\n\nclass NonFeatureTask(NativeTask):\n\n  def create_input_fn(self, _):\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors([1])\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(features, mode, config):\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=tf.group(features),\n                                        loss=tf.constant(0.0),\n                                        predictions=tf.constant(0))\n\n    return model_fn\n\n\nclass SequenceFeatureTask(NativeTask):\n  \"\"\"A test task that will use float feature in model_fn.\"\"\"\n\n  def create_input_fn(self, mode):\n    del mode\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors({\n          \"sequence_feature\":\n              tf.ragged.constant([[1, 2], [], [3, 4, 5]], dtype=np.int64),\n      }).map(advanced_parse)\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(features, mode, **kwargs):\n      slot = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(name=\"slot\"))\n      s = slot.add_feature_slice(5)\n      fc = feature.FeatureColumnV1(slot,\n                                   \"sequence_feature\",\n                                   combiner=embedding_combiners.FirstN(2))\n      embedding = fc.embedding_lookup(s)\n      sequence_feature = features[\"sequence_feature\"]\n      predictions = tf.reduce_sum(sequence_feature, axis=-1)\n      if mode == tf.estimator.ModeKeys.PREDICT:\n        return tf.estimator.EstimatorSpec(mode, predictions=predictions)\n      all_embeddings = [fc.get_all_embeddings_concat()]\n      loss = tf.reduce_sum(all_embeddings)\n      grads = tf.gradients(loss, all_embeddings)\n      train_op = tf.group(\n          self._ctx.feature_factory.apply_gradients(zip(grads, all_embeddings)))\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=train_op,\n                                        loss=loss,\n                                        predictions=predictions)\n\n    return model_fn\n\n\nclass CpuSyncTrainTest(tf.test.TestCase):\n\n  def test_cpu_training_feature(self):\n    hvd.init()\n    p = FeatureTask.params()\n    p.name = \"feature_task\"\n    task = FeatureTask(p)\n    training = cpu_training.CpuTraining(\n        cpu_training.CpuTrainingConfig(num_workers=hvd.size(),\n                                       num_ps=0,\n                                       reorder_fids_in_data_pipeline=True,\n                                       embedding_prefetch_capacity=1,\n                                       enable_sync_training=True), task)\n    run_config = tf.estimator.RunConfig(\n        model_dir=os.path.join(test_folder, \"test_cpu_sync_training_feature\"),\n        device_fn=device_utils.default_device_fn)\n    est = tf.estimator.Estimator(training.create_model_fn(), config=run_config)\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN), steps=2)\n\n  def test_embedding_update(self):\n    hvd.init()\n    p = EmbeddingUpdateTask.params()\n    p.name = \"embedding_update_task\"\n    task = EmbeddingUpdateTask(p)\n    training = cpu_training.CpuTraining(\n        cpu_training.CpuTrainingConfig(num_workers=hvd.size(),\n                                       num_ps=0,\n                                       reorder_fids_in_data_pipeline=True,\n                                       embedding_prefetch_capacity=0,\n                                       enable_sync_training=True), task)\n    run_config = tf.estimator.RunConfig(\n        model_dir=os.path.join(test_folder, \"test_embedding_update\"),\n        device_fn=device_utils.default_device_fn)\n    est = tf.estimator.Estimator(training.create_model_fn(), config=run_config)\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN), steps=10)\n\n  def test_cpu_training_float_feature(self):\n    hvd.init()\n    p = FloatFeatureTask.params()\n    p.name = \"float_feature_task\"\n    task = FloatFeatureTask(p)\n    training = cpu_training.CpuTraining(\n        cpu_training.CpuTrainingConfig(num_workers=hvd.size(),\n                                       num_ps=0,\n                                       reorder_fids_in_data_pipeline=True,\n                                       embedding_prefetch_capacity=1,\n                                       enable_sync_training=True), task)\n    run_config = tf.estimator.RunConfig(\n        model_dir=os.path.join(test_folder,\n                               \"test_cpu_sync_training_float_feature\"),\n        device_fn=device_utils.default_device_fn)\n    est = tf.estimator.Estimator(training.create_model_fn(), config=run_config)\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN), steps=2)\n\n  def test_cpu_training_sequence_feature(self):\n    hvd.init()\n    p = SequenceFeatureTask.params()\n    p.name = \"sequence_feature_task\"\n    task = SequenceFeatureTask(p)\n    training = cpu_training.CpuTraining(\n        cpu_training.CpuTrainingConfig(num_workers=hvd.size(),\n                                       num_ps=0,\n                                       reorder_fids_in_data_pipeline=True,\n                                       embedding_prefetch_capacity=1,\n                                       enable_sync_training=True,\n                                       hashtable_init_capacity=100000,\n                                       enable_embedding_postpush=True), task)\n    run_config = tf.estimator.RunConfig(\n        model_dir=os.path.join(test_folder,\n                               \"test_cpu_training_sequence_feature\"),\n        device_fn=device_utils.default_device_fn)\n    est = tf.estimator.Estimator(training.create_model_fn(), config=run_config)\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN), steps=2)\n\n  def test_cpu_training_non_feature(self):\n    hvd.init()\n    p = NonFeatureTask.params()\n    p.name = \"non_feature_task\"\n    task = NonFeatureTask(p)\n    training = cpu_training.CpuTraining(\n        cpu_training.CpuTrainingConfig(num_workers=hvd.size(),\n                                       num_ps=0,\n                                       embedding_prefetch_capacity=1,\n                                       hashtable_init_capacity=100000,\n                                       enable_sync_training=True), task)\n    run_config = tf.estimator.RunConfig(\n        model_dir=os.path.join(test_folder,\n                               \"test_cpu_sync_training_non_feature\"),\n        device_fn=device_utils.default_device_fn)\n    est = tf.estimator.Estimator(training.create_model_fn(), config=run_config)\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN), steps=2)\n\n\nclass DistributedSyncTrainTest(tf.test.TestCase):\n\n  def test_basic(self):\n    hvd.init()\n    model_dir = os.path.join(test_folder, \"sync_training_basic\")\n    params = FeatureTask.params()\n    params.name = \"test_task\"\n    params.train.max_steps = 2\n    # TODO(zouxuan): async push breaks the test, needs further triage.\n    cpu_training.distributed_sync_train(\n        cpu_training.DistributedCpuTrainingConfig(\n            model_dir=model_dir,\n            enable_sync_training=True,\n            reorder_fids_in_data_pipeline=True,\n            embedding_prefetch_capacity=1,\n            hashtable_init_capacity=100000,\n            enable_embedding_postpush=False), params)\n\n  def test_sparse_pipelining(self):\n    hvd.init()\n    model_dir = os.path.join(test_folder, \"sync_training_pipelined\")\n    params = FeatureTask.params()\n    params.name = \"test_task\"\n    params.train.max_steps = 4\n    cpu_training.distributed_sync_train(\n        cpu_training.DistributedCpuTrainingConfig(\n            model_dir=model_dir,\n            enable_sync_training=True,\n            reorder_fids_in_data_pipeline=True,\n            embedding_prefetch_capacity=1,\n            enable_pipelined_bwda2a=True,\n            enable_pipelined_fwda2a=True,\n            hashtable_init_capacity=100000,\n            enable_embedding_postpush=False), params)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/cpu_training.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"This module defines how to run a native task in CPU training environment.\n\nCpuTraining defines those conversion.\n\"\"\"\nimport contextlib\nimport copy\nimport dataclasses\nimport getpass\nimport json\nimport os\nimport io\nimport platform\nimport socket\nimport threading\nimport timeit\n\nimport sys\nimport traceback\nfrom datetime import datetime\nfrom typing import Callable, Dict, Iterable, List, Set, Tuple, Union\nfrom urllib.parse import urlparse\n\nimport time\nfrom absl import logging\nfrom absl import flags\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.python.lib.io import file_io\nfrom tensorflow.python.training.summary_io import SummaryWriterCache\nfrom tensorflow.python.ops import resources\nfrom tensorflow.python.ops import variables as tfvariables\nfrom tensorflow.python.ops.control_flow_ops import NoOp\n\nfrom monolith.agent_service.agent_service_pb2 import ServerType\nfrom monolith.agent_service.backends import SyncBackend\nfrom monolith.core.hyperparams import InstantiableParams\nfrom monolith.native_training import barrier_ops\nfrom monolith.native_training import basic_restore_hook\nfrom monolith.native_training import cluster_manager\nfrom monolith.native_training import device_utils\nfrom monolith.native_training import distributed_ps_factory, distributed_ps\nfrom monolith.native_training import distribution_ops\nfrom monolith.native_training import distributed_ps_sync\nfrom monolith.native_training import embedding_combiners\nfrom monolith.native_training import entry\nfrom monolith.native_training import feature\nfrom monolith.native_training import gflags_utils\nfrom monolith.native_training import hash_filter_ops\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import hvd_lib\nfrom monolith.native_training import multi_hash_table_ops\nfrom monolith.native_training import logging_ops\nfrom monolith.native_training import mlp_utils\nfrom monolith.native_training import monolith_checkpoint_state_pb2\nfrom monolith.native_training import multi_type_hash_table\nfrom monolith.native_training import native_task\nfrom monolith.native_training import native_task_context\nfrom monolith.native_training import net_utils\nfrom monolith.native_training import ps_benchmark\nfrom monolith.native_training import save_utils\nfrom monolith.native_training import session_run_hooks\nfrom monolith.native_training import sync_hooks\nfrom monolith.native_training import sync_training_hooks\nfrom monolith.native_training import tensor_utils\nfrom monolith.native_training import utils\nfrom monolith.native_training import variables\nfrom monolith.native_training import distributed_serving_ops\nfrom monolith.native_training import yarn_runtime\nfrom monolith.native_training.alert import alert_manager\nfrom monolith.native_training.data import datasets\nfrom monolith.native_training.hash_table_utils import infer_dim_size\nfrom monolith.native_training.distributed_serving_ops import ParameterSyncClient\nfrom monolith.native_training.hash_filter_ops import FilterType\nfrom monolith.native_training.hooks import ckpt_hooks\nfrom monolith.native_training.hooks import ckpt_info\nfrom monolith.native_training.hooks import ps_check_hooks\nfrom monolith.native_training.hooks import hook_utils\nfrom monolith.native_training.hooks import session_hooks\nfrom monolith.native_training.hooks import feature_engineering_hooks\nfrom monolith.native_training.hooks.server import server_lib as server_hook_lib\nfrom monolith.native_training.metric import cli\nfrom monolith.native_training.metric.metric_hook import Tf2ProfilerHook, NVProfilerHook\nfrom monolith.native_training.metric.metric_hook import ByteCCLTelemetryHook\nfrom monolith.native_training.metric.metric_hook import ThroughputMetricHook\nfrom monolith.native_training.model_export import export_hooks\nfrom monolith.native_training.model_export import export_utils\nfrom monolith.native_training.model_export import saved_model_exporters\nfrom monolith.native_training.model_export import export_context\nfrom monolith.native_training.model_export.export_context import \\\n    is_exporting, is_exporting_distributed, is_dry_run_or_exporting, ExportMode\nfrom monolith.native_training.native_task import NativeTask\nfrom monolith.native_training.prefetch_queue import \\\n    enqueue_dicts_with_queue_return, EnqueueHook\nfrom monolith.native_training import prefetch_queue\nfrom monolith.native_training.proto import debugging_info_pb2\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\nfrom monolith.native_training.runtime.parameter_sync import \\\n    parameter_sync_pb2\nfrom monolith.native_training.service_discovery import ServiceDiscovery\nfrom monolith.native_training.service_discovery import TfConfigServiceDiscovery\nfrom monolith.native_training.service_discovery import MLPServiceDiscovery\nfrom monolith.native_training.data.training_instance.python import parser_utils\nfrom monolith.native_training.model_dump.dump_utils import DumpUtils, DRY_RUN\nfrom monolith.native_training.data.parsers import ParserCtx, get_default_parser_ctx\nfrom monolith.native_training.dense_reload_utils import CustomRestoreListenerKey, CustomRestoreListener\nfrom monolith.native_training.data.item_pool_hook import ItemPoolSaveRestoreHook, POOL_KEY\nfrom monolith.native_training.distribution_utils import get_sync_run_hooks, \\\n  update_session_config_for_gpu, get_mpi_rank, get_mpi_size, get_mpi_local_rank\n\nflags.DEFINE_string(\n    \"monolith_chief_alert_proto\", \"\",\n    \"The text format of alert proto. Will only be activated by chief.\")\n\nFLAGS = flags.FLAGS\n\n\ndef _combine_slices_as_table(\n    slices: List[feature.SliceConfig],\n    hashtable_config: entry.HashTableConfig) -> entry.HashTableConfigInstance:\n  table_config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n  entry_config = table_config.entry_config\n  learning_rate_fns = list()\n\n  if is_exporting():\n    entry_config.entry_type = embedding_hash_table_pb2.EntryConfig.EntryType.SERVING\n  for s in slices:\n    entry_config.segments.append(s.segment)\n    learning_rate_fns.append(s.learning_rate_fn)\n  hashtable_config.mutate_table(table_config)\n  return entry.HashTableConfigInstance(table_config, learning_rate_fns)\n\n\ndef _lookup_embedding_ids(\n    hash_table: multi_type_hash_table.BaseMultiTypeHashTable,\n    name_to_embedding_ids: Dict[str, tf.RaggedTensor]) -> Dict[str, tf.Tensor]:\n  name_to_ids = {k: v.values for k, v in name_to_embedding_ids.items()}\n  return hash_table.lookup(name_to_ids)\n\n\ndef _convert_parquets_to_instance(parquets_path, instance_path):\n  import pyarrow.parquet as pq\n  from struct import pack\n  from cityhash import CityHash64\n  from idl.matrix.proto.proto_parser_pb2 import Instance\n\n  # choose latest date in parquets_path\n  if not tf.io.gfile.isdir(parquets_path):\n    raise ValueError(f\"Argument parquet_path is not a directory. {parquets_path}\")\n  valid_dates = [fn for fn in tf.io.gfile.listdir(parquets_path) if fn.isdigit() and len(fn)==8 and tf.io.gfile.isdir(os.path.join(parquets_path, fn))]\n  if len(valid_dates) == 0:\n    raise ValueError(f\"No vaild subdirectory in parquet_path: {parquets_path}\")\n  selected_date = max(valid_dates)\n  parquets_path = os.path.join(parquets_path, selected_date)\n  logging.info(f\"start to convert parquets files to a instance pb file, latest parquet_path={parquets_path}\")\n\n  # collect item_to_fids dict\n  item_to_fids = {}\n  parquet_files = [os.path.join(parquets_path, fn) for fn in tf.io.gfile.listdir(parquets_path) if fn.endswith(\".snappy.parquet\")]\n  if len(parquet_files) == 0:\n    raise ValueError(f\"None of .snappy.parquet file found in {parquets_path}\")\n  logging.info(f\"{len(parquet_files)} .snappy.paruqet file found.\")\n  for file_id, file_path in enumerate(parquet_files):\n    logging.info(f\"{file_id+1}/{len(parquet_files)} start to parse parquet file: {file_path}\")\n    with tf.io.gfile.GFile(file_path, \"rb\") as f:\n      f_bin = f.read()\n      pq_data = pq.read_table(io.BytesIO(f_bin))\n    logging.info(f\"{len(pq_data)} items detected.\")\n    item_id_col = pq_data['item_id'].to_pylist()\n    fids_col = pq_data['fids'].to_pylist()\n    for i in range(len(pq_data)):\n      item_id = CityHash64(str(item_id_col[i])) & ((1<<63)-1)\n      fids = [int(fid) for fid in fids_col[i].split()]\n      if item_id in item_to_fids:\n        logging.info(f\"{item_id} already in dict, use latest\")\n      item_to_fids[item_id] = fids\n\n  logging.info(f\"convert finished, totally {len(item_to_fids)} items collected.\")\n\n  # generate instance pb file\n  logging.info(f\"start to generate items instance pb file to {instance_path}.\")\n  with tf.io.gfile.GFile(instance_path, \"wb\") as f:\n    for item_id, fids in item_to_fids.items():\n      inst = Instance()\n      inst.line_id.item_id = item_id\n      for fid in fids:\n        inst.fid.append(fid)\n      serialized = inst.SerializeToString()\n      f.write(pack(\"<Q\", len(serialized)))\n      f.write(serialized)\n  logging.info(f\"items instance pb file generated in {instance_path}.\")\n\n\ndef create_exporter(task,\n                    model_dir,\n                    warmup_file,\n                    export_dir_base,\n                    dense_only,\n                    include_graphs=None,\n                    export_context_list=None):\n  if task._params.serving.export_mode == ExportMode.STANDALONE:\n    exporter = saved_model_exporters.StandaloneExporter(\n        task.create_model_fn(),\n        model_dir=model_dir,\n        export_dir_base=export_dir_base,\n        shared_embedding=task._params.serving.shared_embedding,\n        warmup_file=warmup_file,\n        export_context_list=export_context_list)\n  elif task._params.serving.export_mode == ExportMode.DISTRIBUTED:\n    exporter = saved_model_exporters.DistributedExporter(\n        task.create_model_fn(),\n        model_dir=model_dir,\n        export_dir_base=export_dir_base,\n        shared_embedding=task._params.serving.shared_embedding,\n        warmup_file=warmup_file,\n        export_context_list=export_context_list,\n        dense_only=dense_only,\n        allow_gpu=task._params.serving.export_with_gpu_allowed,\n        clear_entry_devices=task._params.serving.\n        export_with_cleared_entry_devices,\n        include_graphs=include_graphs,\n        global_step_as_timestamp=task.config.enable_sync_training,\n        with_remote_gpu=task._params.serving.with_remote_gpu)\n  else:\n    raise ValueError(\"Invalid export_mode: {}\".format(\n        task._params.serving.export_mode))\n\n  return exporter\n\n\nclass _CpuFeatureFactory(feature.FeatureFactoryFromEmbeddings):\n\n  def __init__(self,\n               hash_table: multi_type_hash_table.BaseMultiTypeHashTable,\n               embedding_ids: Dict[str, tf.RaggedTensor],\n               embeddings: Dict[str, tf.Tensor],\n               embedding_slices: Dict[str, tf.Tensor],\n               req_time: tf.Tensor,\n               async_function_mgr: prefetch_queue.AsyncFunctionMgr,\n               async_push: bool = False):\n    super().__init__(embeddings, embedding_slices)\n    self._hash_table = hash_table\n    self._req_time = req_time\n    self._embedding_ids = embedding_ids\n    self._embeddings = embeddings\n    self._async_function_mgr = async_function_mgr\n    self._async_push = async_push\n\n  def _push(self, slot_to_emb_ids, slot_to_emb_grads, global_step, req_time):\n    # TODO(leqi.zou): This the hack. Finally we need to figure out a way to\n    # resolve this var leaking issue.\n    slot_to_ids_and_grads = {\n        k: (v, slot_to_emb_grads[k])\n        for k, v in slot_to_emb_ids.items()\n        if slot_to_emb_grads[k] is not None\n    }\n    return self._hash_table.apply_gradients(slot_to_ids_and_grads,\n                                            global_step,\n                                            req_time=req_time).as_op()\n\n  def apply_gradients(\n      self,\n      grads_and_vars: Iterable[Tuple[tf.Tensor, tf.Tensor]],\n      req_time: tf.Tensor = None,\n      # scale is ignored in async training\n      scale=1):\n    if req_time is None:\n      req_time = self._req_time\n    emb_grads = utils.propagate_back_gradients(grads_and_vars,\n                                               self._embeddings.values())\n    slot_to_emb_ids = {k: v.values for k, v in self._embedding_ids.items()}\n    slot_to_emb_grads = dict(zip(self._embeddings.keys(), emb_grads))\n    global_step = tf.identity(tf.compat.v1.train.get_or_create_global_step())\n    return self._async_function_mgr.add_async_function(\n        self._push, (slot_to_emb_ids, slot_to_emb_grads, global_step, req_time),\n        is_async=self._async_push,\n        queue_name=\"postpush_queue\")\n\n\nclass _FusedCpuFeatureFactory(feature.FeatureFactoryFromEmbeddings):\n\n  def __init__(\n      self,\n      hash_table: Union[\n          multi_type_hash_table.\n          MergedMultiTypeHashTable,  # when use_native_multi_hash_table=False\n          distributed_ps_sync.\n          DistributedMultiTypeHashTableMpi  # when use_native_multi_hash_table=True\n      ],\n      name_to_embeddings: Dict[str, tf.Tensor],\n      name_to_embedding_slices: Dict[str, tf.Tensor],\n      req_time: tf.Tensor,\n      auxiliary_bundle: Dict[str, tf.RaggedTensor],\n      use_native_multi_hash_table: bool):\n    super().__init__(name_to_embeddings, name_to_embedding_slices)\n    self._hash_table = hash_table\n    self._embeddings = name_to_embeddings\n    self._auxiliary_bundle = auxiliary_bundle\n    self._req_time = req_time\n    self.use_native_multi_hash_table = use_native_multi_hash_table\n\n  def apply_gradients(self,\n                      grads_and_vars: Iterable[Tuple[tf.Tensor, tf.Tensor]],\n                      req_time: tf.Tensor = None,\n                      scale: tf.Tensor = 1):\n    if req_time is None:\n      req_time = self._req_time\n    with tf.device(\"/device:GPU:0\"):\n      emb_grads = utils.propagate_back_gradients(grads_and_vars,\n                                                 self._embeddings.values())\n    slot_to_emb_grads = dict(zip(self._embeddings.keys(), emb_grads))\n    global_step = tf.identity(tf.compat.v1.train.get_or_create_global_step())\n    # Restore back to ID/Embedding mapping and flatten.\n    if self.use_native_multi_hash_table:\n      apply_op = self._hash_table.apply_gradients(slot_to_emb_grads,\n                                                  self._auxiliary_bundle,\n                                                  global_step,\n                                                  req_time=req_time,\n                                                  scale=scale)\n    else:\n      apply_op = self._hash_table.apply_gradients(slot_to_emb_grads,\n                                                  self._auxiliary_bundle,\n                                                  global_step,\n                                                  req_time=req_time,\n                                                  skip_merge_id=True,\n                                                  scale=scale)\n    return apply_op.as_op()\n\n\nclass _MetricsHeartBeatThread():\n\n  def __init__(self, interval=30):\n    self._interval = interval\n    self._thread = None\n    self._stopped = True\n    self._mcli = cli.get_cli(utils.get_metric_prefix())\n\n  def _heart_beat(self):\n    while not self._stopped:\n      time.sleep(self._interval)\n      self._mcli.emit_store(\"training_heart_beat\", \n                            int(time.time()),\n                            {\"type\": \"running\"})\n      self._mcli.flush()\n    self._mcli.emit_store(\"training_heart_beat\", \n                          int(time.time()), \n                          {\"type\": \"stopped\"})\n    self._mcli.flush()\n\n  def start(self):\n    logging.info(\"start MetricsHeartBeat thread\")\n    self._stopped = False\n    self._thread = threading.Thread(target=self._heart_beat)\n    self._thread.start()\n\n  def stop(self):\n    self._stopped = True\n    if self._thread:\n      self._thread.join(timeout=10*self._interval)\n    logging.info(\"MetricsHeartBeat thread stopped\")\n\n\ndef get_req_time(features):\n  if \"req_time\" in features:\n    return features[\"req_time\"][0]\n  else:\n    return None\n\n@gflags_utils.LinkDataclassToFlags(linked_map={\"use_dataservice\": \"dataset_use_dataservice\"})\n@dataclasses.dataclass\nclass CpuTrainingConfig:\n  \"\"\"The CPU training config.\n\n  attributes:\n    :param server_type: The type of this process. Can be 'ps' or 'worker'.\n    :param index: The index of the current process in servers.\n    :param model_name: The model name. If empty, will be overridden by deepinsight_name.\n    :param num_ps: The number of ps.\n    :param num_workers: The number of worker.\n    :param enable_variable_prefetch: Whether enable variable prefetch.\n    :param filter_capacity: Sliding hash filter capacity.\n    :param filter_split_num: Number of hash filter.\n    :param filter_equal_probability: Probabilistic modeling type.\n    :param filter_type: Sliding hash filter or probabilistic filter.\n    :param hashtable_init_capacity: hashtable init capacity.\n    :param use_native_multi_hash_table: Use native MultiHashTable.\n    :param embedding_prefetch_capacity: The queue capacity to prefetch lookuped embeddings.\n    :param enable_embedding_postpush: Whether enable embedding post push.\n    :param enable_variable_postpush: Whether enable variable post push.\n    :param enable_sync_training: Whether use MPI or RPC for the distributed training.\n    :param enable_partial_sync_training: Whether use sparse_dense mode to train.\n    :param enable_gpu_training: Whethere to also use GPU for training besides CPU.\n    :param processes_per_gpu: Integer number of mpi processes on GPU (for sync training).\n    :param merge_sync_training_ckpt: Whether merge sync ckpt and skip non-worker0 dense variable save.\n    :param mode: The running mode, must be train/eval/infer.\n    :param partial_recovery: Whether enable partial recovery when failover.\n    :param tide_start_hour: tide start hour\n    :param tide_start_minute: tide start minute\n    :param tide_end_hour: tide end hour\n    :param tide_end_minute: tide end minute\n    :param tide_save_secs: tide save seconds\n    :param enable_async_optimize: Whether enable async optimize.\n    :param enable_pipelined_fwda2a: Whether enable async fwd a2a after lookup at prefetch.\n    :param enable_pipelined_bwda2a: Whether enable async bwd a2a before sparse optimize stage.\n    :param profile_some_steps_from: Whether profile to save timeline from step to step+10.\n    :param profile_with_nvprof_from_to: Whether to profile with nvprof at certain step in-between\n    :param enable_realtime_training: Whether enable realtime training. Some default value will be changed if enabled.\n    :param reorder_fids_in_data_pipeline: reorder fids in data pipeline.\n    :param chief_timeout_secs: chief timeout in secs\n    :param save_checkpoints_secs: Save checkpoint every save_checkpoints_secs\n    :param save_checkpoints_steps: Save checkpoint every save_checkpoints_steps\n    :param warmup_file: The warmup file name.\n    :param skip_zero_embedding_when_serving: Whether skip to restore zero embedding(L2 norm = 0) when serving\n    :param max_rpc_deadline_millis: Timeout for remote predict op in millisenconds.\n    :param dense_only_save_checkpoints_secs: Save dense checkpoint every save_checkpoints_secs\n    :param dense_only_save_checkpoints_steps: Save dense checkpoint every save_checkpoints_steps\n    :param dense_only_stop_training_when_save: If barrier will be put when chief saves the dense params.\n    :param checkpoints_max_to_keep: The maximum number of recent checkpoint files to keep.\n    :param submit_time_secs: The time when job submitted.\n    :param containers_ready_time_secs: The time when all containers are ready.\n    :param max_slow_start_wait_minute: max slow start waiting time, default 10 min.\n    :param cluster_type: Type of cluster. Can be 'stable', 'tide' or 'k8s'\n    :param enable_model_ckpt_info: If we generate the model ckpt info when save checkpoint.\n    :param feature_eviction_on_save: If we remove stale hash table entries when do the save.\n    :param only_feature_engineering: only run feature engineering\n    :param enable_variable_partition: Enable Variable Partition, default True.\n    :param enable_fused_layout: Enable Fused Layout, default False.\n    :param force_shutdown_ps: Sometimes should shutdown ps even with enable realtime training.\n    :param clear_nn: Whether clean dense part of model\n    :param continue_training: Whether the global step continue increase when clear nn\n    :param reload_alias_map: A dict map the old variable name to the new one\n    :param enable_alias_map_auto_gen: Whether enable alias_map auto generate\n    :param enable_model_dump: Whether enable model dump for scurelty purpose\n    :param enable_resource_constrained_roughsort: Whether enable resource constrained roughsort\n    :param roughsort_candidate_items_path: candidate item data file path\n    :param roughsort_items_use_parquet: if item candidate data format is parquet\n    :param items_input_lagrangex_header: If items input file has lagrangex_header flag\n    :param items_input_has_sort_id: If items input file has sort_id flag\n    :param items_input_kafka_dump: If items input file has kafka_dump flag\n    :param items_input_kafka_dump_prefix: If items input file has kafka_dump_prefix flag\n    :param num_extra_dsworker_on_gpu_worker: The number of extra dsworker on GPU worker.\n    :param save_summary_steps: Save summaries every this many steps\n    :param log_step_count_steps: The frequency, in number of global steps, that the global step and the loss will be logged during training\n  \"\"\"\n\n  server_type: str = \"worker\"\n  index: int = 0\n  num_ps: int = 0\n  num_workers: int = 1\n  model_name: str = \"\"\n  filter_capacity: int = 300000000\n  filter_split_num: int = 7\n  filter_type: str = FilterType.SLIDING_HASH_FILTER\n  filter_equal_probability: bool = True\n  hashtable_init_capacity: int = 0\n  use_native_multi_hash_table: bool = None\n  embedding_prefetch_capacity: int = 0\n  enable_embedding_postpush: bool = False\n  enable_variable_prefetch: bool = False\n  enable_variable_postpush: bool = False\n  enable_sync_training: bool = False\n  enable_partial_sync_training: bool = False\n  enable_gpu_training: bool = False\n  processes_per_gpu: int = 1\n  merge_sync_training_ckpt: bool = True\n  mode: str = tf.estimator.ModeKeys.TRAIN\n  partial_recovery: bool = None\n  tide_start_hour: int = None\n  tide_start_minute: int = None\n  tide_end_hour: int = None\n  tide_end_minute: int = None\n  tide_save_secs: int = None\n  enable_realtime_training: bool = False\n  enable_async_optimize: bool = False\n  enable_pipelined_fwda2a: bool = False\n  enable_pipelined_bwda2a: bool = False\n  profile_some_steps_from: int = None\n  profile_save_steps_interval: int = 5000\n  profile_with_nvprof_from_to: str = None\n  # Sync training optimization\n  reorder_fids_in_data_pipeline: bool = False\n  chief_timeout_secs: int = 1800\n  save_checkpoints_secs: int = None\n  save_checkpoints_steps: int = None\n  dense_only_save_checkpoints_secs: int = None\n  dense_only_save_checkpoints_steps: int = None\n  dense_only_stop_training_when_save: bool = False\n  warmup_file: str = './warmup_file'\n  skip_zero_embedding_when_serving: bool = False\n  max_rpc_deadline_millis: int = 30000\n  checkpoints_max_to_keep: int = 10\n  submit_time_secs: int = None\n  containers_ready_time_secs: int = None\n  cluster_type: str = \"stable\"\n  max_slow_start_wait_minute: int = 10  # 10 min\n  enable_model_ckpt_info: bool = False\n  feature_eviction_on_save: bool = False\n  only_feature_engineering: bool = False\n  enable_variable_partition: bool = True\n  enable_fused_layout: bool = False\n  force_shutdown_ps: bool = False\n  clear_nn: bool = False\n  continue_training: bool = False\n  reload_alias_map: Dict[str, int] = None\n  enable_alias_map_auto_gen: bool = None\n  enable_model_dump: bool = False\n  enable_resource_constrained_roughsort: bool = False\n  roughsort_candidate_items_path: str = None\n  roughsort_items_use_parquet: bool = False\n  items_input_lagrangex_header: bool = False\n  items_input_has_sort_id: bool = False\n  items_input_kafka_dump: bool = False\n  items_input_kafka_dump_prefix: bool = False\n  device_fn: Callable[[tf.Operation], str] = None\n  use_dataservice: bool = None\n  num_extra_dsworker_on_gpu_worker: int = 0\n  save_summary_steps: int = 100\n  log_step_count_steps: int = 100\n\n  @property\n  def enable_full_sync_training(self):\n    return self.enable_sync_training and not self.enable_partial_sync_training\n\n\ndef _make_serving_config_from_training_config(\n    training_config: CpuTrainingConfig):\n  serving_config = copy.deepcopy(training_config)\n  serving_config.embedding_prefetch_capacity = 0\n  serving_config.enable_embedding_postpush = False\n  serving_config.enable_variable_prefetch = False\n  serving_config.enable_variable_postpush = False\n  serving_config.reorder_fids_in_data_pipeline = False\n  serving_config.enable_model_ckpt_info = False\n  if serving_config.enable_sync_training:\n    serving_config.enable_sync_training = False\n    if not serving_config.enable_partial_sync_training:\n      serving_config.num_ps = training_config.num_workers\n  if serving_config.enable_partial_sync_training:\n    serving_config.enable_partial_sync_training = False\n  return serving_config\n\n\ndef _make_serving_feature_configs_from_training_configs(\n    feature_configs, skip_zero_embedding: bool):\n  serving_feature_configs = copy.deepcopy(feature_configs)\n  for config in serving_feature_configs[0].values():\n    # config: entry.HashTableConfigInstance\n    config.table_config.entry_config.entry_type = embedding_hash_table_pb2.EntryConfig.EntryType.SERVING\n    config.table_config.skip_zero_embedding = skip_zero_embedding\n    config.table_config.cuckoo.SetInParent()\n  return serving_feature_configs\n\n\ndef make_native_task_context(config: CpuTrainingConfig,\n                             sync_backend: SyncBackend = None):\n  return native_task_context.NativeTaskContext(\n      num_ps=config.num_ps,\n      ps_index=config.index if config.server_type == 'ps' else 0,\n      num_workers=config.num_workers,\n      worker_index=config.index if config.server_type == 'worker' else 0,\n      model_name=config.model_name,\n      sync_backend=sync_backend,\n      server_type=config.server_type)\n\n\ndef is_chief(config: CpuTrainingConfig):\n  if config.enable_sync_training or config.enable_partial_sync_training:\n    return config.server_type == \"worker\" and get_mpi_rank() == 0\n  else:\n    return config.server_type == \"worker\" and config.index == 0\n\n\nclass CpuTraining:\n  \"\"\"Wraps a native task to be runnable on CPU.\"\"\"\n\n  def __init__(self,\n               config: CpuTrainingConfig,\n               task: native_task.NativeTask,\n               sync_backend: SyncBackend = None):\n    if config.server_type != \"worker\":\n      raise ValueError(\"server_type in CpuTraining must be `worker`\")\n    if not config.model_name:\n      if isinstance(task, InstantiableParams):\n        default_name = f'di_name_{task.cls.__name__}'\n      else:\n        default_name = f'di_name_{task.__class__.__name__}'\n      config.model_name = task.p.metrics.deep_insight_name or default_name\n    if config.enable_realtime_training:\n      # Set some default value in streaming training if not set.\n      if config.partial_recovery is None:\n        config.partial_recovery = True\n      if config.dense_only_save_checkpoints_secs is None and config.dense_only_save_checkpoints_steps is None:\n        config.dense_only_save_checkpoints_secs = 30 * 60\n\n    if config.use_native_multi_hash_table is None:\n      config.use_native_multi_hash_table = True\n\n    get_default_parser_ctx().enable_fused_layout = config.enable_fused_layout\n    ParserCtx.enable_resource_constrained_roughsort = config.enable_resource_constrained_roughsort\n    FLAGS.dataset_worker_idx = config.index\n    FLAGS.dataset_num_workers = config.num_workers\n\n    self._config_do_not_refer_directly = copy.deepcopy(config)\n    self._serving_config_do_not_refer_directly = _make_serving_config_from_training_config(\n        self._config_do_not_refer_directly)\n    self._task = task\n    self._params = task.p\n    self._enable_hash_filter = False\n    self._slot_to_occurrence_threshold = {}\n    self._slot_to_expire_time = {}\n    self._sync_backend = sync_backend\n\n    # Gather extra configs for initialization earlier here.\n    with native_task_context.with_ctx(\n        make_native_task_context(self.config, sync_backend)):\n      dump_utils = DumpUtils()\n      if hasattr(self._task, 'is_dumped'):\n        assert dump_utils.has_collected\n        feature_name_config: Dict[\n            str, entry.HashTableConfigInstance] = dump_utils.table_configs\n        feature_to_unmerged_slice_dims: Dict[\n            str, List[int]] = dump_utils.feature_slice_dims\n        feature_to_combiner = dump_utils.feature_combiners  # Dict[str, embedding_combiners.Combiner]\n        self._slot_to_occurrence_threshold = dump_utils.get_slot_to_occurrence_threshold(\n        )\n        self._slot_to_expire_time = dump_utils.get_slot_to_expire_time()\n        self._feature_configs_do_not_refer_directly = (\n            feature_name_config, feature_to_unmerged_slice_dims,\n            feature_to_combiner)\n      else:\n        self._feature_configs_do_not_refer_directly = self._collect_feature_name_to_table_config(\n        )\n        if dump_utils.enable:\n          dump_utils.table_configs = self._feature_configs_do_not_refer_directly[\n              0]\n          dump_utils.feature_slice_dims = self._feature_configs_do_not_refer_directly[\n              1]\n          dump_utils.feature_combiners = self._feature_configs_do_not_refer_directly[\n              2]\n      self._serving_feature_configs_do_not_refer_directly = _make_serving_feature_configs_from_training_configs(\n          self._feature_configs_do_not_refer_directly,\n          self.config.skip_zero_embedding_when_serving)\n\n      if not self.config.use_native_multi_hash_table:\n        self._dummy_merged_table = multi_type_hash_table.MergedMultiTypeHashTable(\n            self.feature_configs[0], lambda *args, **kwargs: None)\n\n      #for training\n      self._init_fused_layout_params()\n\n      class ExportAuxiliaryCtx(ParserCtx):\n\n        def __enter__(ctx_self):\n          ctx_self.use_gpu_emb_table = self._params.train.use_gpu_emb_table\n          self._params.train.use_gpu_emb_table = False\n\n          ctx_self.enable_gpu = device_utils.is_gpu_training()\n          if export_context.get_current_export_ctx().with_remote_gpu:\n            device_utils.enable_gpu_training()\n          else:\n            device_utils.disable_gpu_training()\n\n          super().__enter__()\n          self._init_fused_layout_params()\n\n          return ctx_self\n\n        def __exit__(ctx_self, exc_type, exc_val, exc_tb):\n          super().__exit__(exc_type, exc_val, exc_tb)\n\n          if ctx_self.enable_gpu:\n            device_utils.enable_gpu_training()\n          else:\n            device_utils.disable_gpu_training()\n\n          self._params.train.use_gpu_emb_table = ctx_self.use_gpu_emb_table\n\n      self._export_context_list = [ExportAuxiliaryCtx]\n\n  @property\n  def config(self) -> CpuTrainingConfig:\n    if export_context.is_exporting():\n      return self._serving_config_do_not_refer_directly\n    return self._config_do_not_refer_directly\n\n  @property\n  def feature_configs(\n      self\n  ) -> Tuple[Dict[str, entry.HashTableConfigInstance], Dict[str, List[int]],\n             Dict[str, embedding_combiners.Combiner]]:\n    if export_context.is_exporting():\n      return self._serving_feature_configs_do_not_refer_directly\n    return self._feature_configs_do_not_refer_directly\n\n  def _init_fused_layout_params(self) -> None:\n\n    parse_ctx = get_default_parser_ctx()\n\n    parse_ctx.enable_fused_layout = self.config.enable_fused_layout\n    if parse_ctx.enable_fused_layout:\n      parse_ctx.sharding_sparse_fids_op_params = None\n      # same param to fused_layout\n      (feature_name_config, feature_to_unmerged_slice_dims,\n       feature_to_combiner) = self.feature_configs\n      parse_ctx.sharding_sparse_fids_op_params = distributed_ps.PartitionedHashTable.gen_feature_configs(\n          num_ps=self.config.num_workers\n          if self._params.train.use_gpu_emb_table else self.config.num_ps,\n          feature_name_to_config=feature_name_config,\n          layout_configs=self._task.layout_dict,\n          feature_to_combiner=feature_to_combiner,\n          feature_to_unmerged_slice_dims=feature_to_unmerged_slice_dims,\n          use_native_multi_hash_table=self.config.use_native_multi_hash_table,\n          unique=lambda: False if is_exporting() else True,\n          transfer_float16=False,\n          enable_gpu_emb=self._params.train.use_gpu_emb_table,\n          use_gpu=export_context.get_current_export_ctx().with_remote_gpu\n          if export_context.is_exporting() else self.config.enable_gpu_training)\n      logging.info(\n          f\"_init_fused_layout_params {export_context.is_exporting()} {self._params.train.use_gpu_emb_table} {parse_ctx.sharding_sparse_fids_op_params.enable_gpu_emb}  {parse_ctx.sharding_sparse_fids_op_params.use_gpu}\"\n      )\n\n  def create_input_fn(self, mode):\n\n    input_fn = self._task.create_input_fn(mode)\n    enable_reorder = (mode != tf.estimator.ModeKeys.PREDICT and\n                      self.config.reorder_fids_in_data_pipeline and\n                      not self.config.enable_fused_layout)\n    use_dataservice = self.config.use_dataservice\n    feature_name_config = self.feature_configs[0]\n    embedding_feature_names = feature_name_config.keys()\n\n    def input_fn_factory(input_fn, enable_reorder, use_dataservice, feature_name_config,\n                         embedding_feature_names):\n\n      def reorder_parse_fn(*args):\n        logging.info(\n            'Wrapping parser to dedup and reorder fids in data pipeline...')\n        # features = parse_fn(*args, **kwargs)\n        features = args[0]\n        # CpuTraining.create_model_fn: def model_fn\n        embedding_ragged_ids = {\n            k: v for k, v in features.items() if k in embedding_feature_names\n        }\n        dense_features = {\n            k: v\n            for k, v in features.items()\n            if k not in embedding_feature_names\n        }\n        if self.config.use_native_multi_hash_table:\n          # when multi hash table is used, this is unmerged\n          merged_slot_dims = multi_hash_table_ops.infer_dims(\n              feature_name_config)\n          sorted_slot_keys = sorted(embedding_feature_names)\n          sorted_input = [\n              embedding_ragged_ids[k].values for k in sorted_slot_keys\n          ]\n        else:\n          merged_slot_to_id, merged_slot_to_sizes = self._dummy_merged_table._get_merged_to_indexed_tensor(\n              {k: v.values for k, v in embedding_ragged_ids.items()})\n          merged_slot_dims = self._dummy_merged_table.get_table_dim_sizes()\n          sorted_slot_keys = sorted(merged_slot_to_id.keys())\n          sorted_input = [merged_slot_to_id[k] for k in sorted_slot_keys]\n        reordered_pack = distribution_ops.fused_reorder_by_indices(\n            sorted_input, self.config.num_workers, merged_slot_dims)\n        reordered_pack = (*reordered_pack, get_req_time(dense_features))\n        if self.config.use_native_multi_hash_table:\n          # DistributedMultiTypeHashTableMpi.lookup\n          lookup_args = reordered_pack\n        else:\n          # merged_multi_type_hash_table.lookup\n          lookup_args = (\n              merged_slot_to_sizes,\n              # DistributedMultiTypeHashTableMpi.lookup\n              reordered_pack)\n        # Results include the following intermediate tensors\n        res = (\n            dense_features,  # Dense features\n            # CpuTraining.create_model_fn: def model_fn\n            (\n                parser_utils.RaggedEncodingHelper.expand(\n                    embedding_ragged_ids,  # Sparse Features\n                    with_precomputed_nrows=True,\n                    with_precomputed_value_rowids=False\n                    # Because most GPU-downstream poolings\n                    # are not using value_rowids anymore,\n                    # we choose not to precompute it here.\n                ),\n                lookup_args))\n        # Use dict here to prevent tf.Estimator from automatically treating the second in the return tuple as labels\n        return {\"1\": res}\n\n      def wrapped_input_fn():\n\n        with native_task_context.with_ctx(\n            make_native_task_context(self.config, self._sync_backend)):\n          ds = input_fn()\n          if isinstance(ds, tf.data.Dataset):\n            if enable_reorder:\n              ds = ds.map(reorder_parse_fn, num_parallel_calls=tf.data.AUTOTUNE)\n            if use_dataservice and not is_dry_run_or_exporting():\n              # This is a temporary hack. Will revisit here once we decided to\n              # do the remanagement.\n              tmp_mlp_env = mlp_utils.MLPEnv()\n              ds = datasets.distribute(ds, \n                                       target=tmp_mlp_env.dispatcher_target(),\n                                       num_worker=tmp_mlp_env.num_replicas(role='worker'),\n                                       worker_idx=tmp_mlp_env.index)\n            # Always enable prefetch since input_fn might be wrapped by\n            # many other decorators.\n            ds = ds.prefetch(tf.data.AUTOTUNE)\n          return ds\n\n      return wrapped_input_fn\n\n    return input_fn_factory(input_fn, enable_reorder, use_dataservice, feature_name_config,\n                            embedding_feature_names)\n\n  def create_model_fn(self):\n\n    def create_hash_table_and_filters_fn():\n      (feature_name_config, feature_to_unmerged_slice_dims,\n       feature_to_combiner) = self.feature_configs\n\n      logging.vlog(\n          1, \"feature_to_unmerged_slice_dims: {}\".format(\n              feature_to_unmerged_slice_dims))\n      slot_occurrence_threshold_config = embedding_hash_table_pb2 \\\n        .SlotOccurrenceThresholdConfig()\n\n      for slot, occurrence_threshold in self._slot_to_occurrence_threshold.items(\n      ):\n        slot_occurrence_threshold = slot_occurrence_threshold_config.slot_occurrence_thresholds.add(\n        )\n        slot_occurrence_threshold.slot = slot\n        slot_occurrence_threshold.occurrence_threshold = occurrence_threshold\n        if occurrence_threshold > 0:\n          self._enable_hash_filter = True\n\n      # In the sync training, hash filter and hashtables are inside worker.\n      if is_exporting():\n        hash_filters = [None] * max(1, self.config.num_ps)\n      else:\n        with device_utils.maybe_device_if_allowed(\n            '/device:GPU:0'\n        ) if self._params.train.use_gpu_emb_table else contextlib.nullcontext():\n          hash_filters = hash_filter_ops.create_hash_filters(\n              self.config.num_ps,\n              self._enable_hash_filter,\n              config=slot_occurrence_threshold_config.SerializeToString(),\n              filter_capacity=self.config.filter_capacity,\n              filter_split_num=self.config.filter_split_num,\n              filter_type=self.config.filter_type)\n\n        slot_to_expire_time_config = embedding_hash_table_pb2.SlotExpireTimeConfig(\n        )\n\n        for slot, expire_time in self._slot_to_expire_time.items():\n          slot_expire_time = slot_to_expire_time_config.slot_expire_times.add()\n          slot_expire_time.slot = slot\n          slot_expire_time.expire_time = expire_time\n\n        for config in feature_name_config.values():\n          config.table_config.slot_expire_time_config.CopyFrom(\n              slot_to_expire_time_config)\n\n      sync_clients = [None] * max(1, self.config.num_ps)\n      if self.config.enable_realtime_training and not is_exporting():\n        sync_clients = distributed_serving_ops.create_parameter_sync_clients(\n            self.config.num_ps)\n      if not self.config.enable_full_sync_training:\n        if self.config.enable_fused_layout:\n          return distributed_ps_factory.create_partitioned_hash_table(\n              num_ps=self.config.num_ps,\n              use_native_multi_hash_table=self.config.\n              use_native_multi_hash_table,\n              max_rpc_deadline_millis=self.config.max_rpc_deadline_millis,\n              hash_filters=hash_filters,\n              sync_clients=sync_clients), hash_filters\n        elif self.config.use_native_multi_hash_table:\n          return distributed_ps_factory.create_native_multi_hash_table(\n              self.config.num_ps,\n              feature_name_config,\n              hash_filters,\n              sync_clients=sync_clients,\n              max_rpc_deadline_millis=self.config.max_rpc_deadline_millis,\n          ), hash_filters\n        else:\n          return distributed_ps_factory.create_multi_type_hash_table(\n              self.config.num_ps,\n              feature_name_config,\n              hash_filters,\n              sync_clients=sync_clients,\n              reduce_network_packets=True,\n              max_rpc_deadline_millis=self.config.max_rpc_deadline_millis,\n          ), hash_filters\n      else:\n        queue_configs = {\n            k: int(getattr(self.config, k))\n            for k in (\"embedding_prefetch_capacity\", \"enable_async_optimize\",\n                      \"enable_pipelined_fwda2a\", \"enable_pipelined_bwda2a\")\n        }\n\n        if self.config.enable_fused_layout:\n          return distributed_ps_factory.create_partitioned_hash_table(\n              num_ps=self.config.num_workers\n              if self._params.train.use_gpu_emb_table else self.config.num_ps,\n              use_native_multi_hash_table=self.config.\n              use_native_multi_hash_table,\n              max_rpc_deadline_millis=self.config.max_rpc_deadline_millis,\n              hash_filters=hash_filters * self.config.num_workers\n              if self._params.train.use_gpu_emb_table else hash_filters,\n              sync_clients=sync_clients * self.config.num_workers\n              if self._params.train.use_gpu_emb_table else sync_clients,\n              enable_gpu_emb=self._params.train.use_gpu_emb_table,\n              queue_configs=queue_configs), hash_filters\n        elif self.config.use_native_multi_hash_table:\n          return distributed_ps_factory.create_in_worker_native_multi_hash_table(\n              self.config.num_workers,\n              feature_name_config,\n              hash_filter=hash_filters[0],\n              sync_client=sync_clients[0],\n              queue_configs=queue_configs), hash_filters\n        else:\n          return distributed_ps_factory.create_in_worker_multi_type_hash_table(\n              self.config.num_workers,\n              feature_name_config,\n              hash_filters[0],\n              sync_client=sync_clients[0],\n              queue_configs=queue_configs), hash_filters\n\n    with native_task_context.with_ctx(\n        make_native_task_context(self.config, self._sync_backend)):\n      return self._get_pipelined_model_fn(create_hash_table_and_filters_fn)\n\n  def _generate_valid_features(self) -> Dict[str, tf.Tensor]:\n    \"\"\"Generates a valid feature dict which can be fed into model_fn in TRAIN mode.\"\"\"\n    input_fn = self._task.create_input_fn(tf.estimator.ModeKeys.TRAIN)\n    dataset = input_fn()\n    return tf.data.experimental.get_single_element(dataset)\n\n  def _collect_feature_name_to_table_config(\n      self\n  ) -> Tuple[Dict[str, entry.HashTableConfigInstance], Dict[str, List[int]],\n             Dict[str, embedding_combiners.Combiner]]:\n    per_replica_batch_size = self._params.train.per_replica_batch_size\n    with tf.Graph().as_default() as g, ParserCtx(enable_fused_layout=False):\n      setattr(g, DRY_RUN, True)\n      feature_factory = feature.DummyFeatureFactory(per_replica_batch_size)\n      self._task.ctx.feature_factory = feature_factory\n      self._task.ctx.layout_factory = None\n      self._task.ctx.async_function_mgr = prefetch_queue.AsyncFunctionMgr(\n          is_async=False)\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      model_fn = self._task.create_model_fn()\n      features = self._generate_valid_features()\n      model_fn(features=features,\n               mode=tf.estimator.ModeKeys.TRAIN,\n               config=tf.estimator.RunConfig())\n\n    table_to_config = feature_factory.get_table_name_to_table_config()\n    feature_to_config: Dict[str, entry.HashTableConfigInstance] = {}\n    feature_to_unmerged_slice_dims: Dict[str, List[int]] = {}\n    feature_to_combiner: Dict[str, embedding_combiners.Combiner] = {}\n    for k, table_config in table_to_config.items():\n      for feature_name in table_config.feature_names:\n        assert not feature_name in feature_to_config, \"Feature must only belongs to one table.\"\n        feature_to_config.update({\n            feature_name:\n                _combine_slices_as_table(table_config.slice_configs,\n                                         table_config.hashtable_config)\n        })\n        feature_to_unmerged_slice_dims[\n            feature_name] = table_config.unmerged_slice_dims\n        feature_to_combiner[feature_name] = table_config.feature_to_combiners[\n            feature_name]\n\n    if self.config.hashtable_init_capacity > 0:\n      for conf in feature_to_config.values():\n        conf.table_config.initial_capacity = self.config.hashtable_init_capacity\n\n    self._slot_to_occurrence_threshold = feature_factory.slot_to_occurrence_threshold\n    self._slot_to_expire_time = feature_factory.slot_to_expire_time\n\n    if self.config.enable_full_sync_training:\n      # To improve hash table performance\n      for config in feature_to_config.values():\n        config.table_config.entry_type = embedding_hash_table_pb2.EmbeddingHashTableConfig.RAW\n\n    return feature_to_config, feature_to_unmerged_slice_dims, feature_to_combiner\n\n  # TODO(leqi.zou): Add a function to disable pipelining.\n  def _get_pipelined_model_fn(self, create_hash_table_and_filters_fn: Callable[\n      [], Tuple[multi_type_hash_table.MultiTypeHashTable, List[tf.Tensor]]]):\n    (feature_name_config, feature_to_unmerged_slice_dims,\n     feature_to_combiner) = self.feature_configs\n    embedding_feature_names: Iterable[str] = feature_name_config.keys()\n    if not embedding_feature_names:\n      # We need to skip pipeline phase since dequeue might never be called if\n      # embedding feature is not used.\n      self._task.ctx.feature_factory = None\n      return self._task.create_model_fn()\n\n    def get_hooks_for_restore(model_dir: str, hash_filters: List[tf.Tensor],\n                              ps_monitor: save_utils.PsMonitor):\n      if not model_dir:\n        return ()\n      basename = os.path.join(model_dir, \"model.ckpt\")\n\n      restore_listeners = [\n          hash_table_ops.HashTableCheckpointRestorerListener(\n              basename, ps_monitor),\n          multi_hash_table_ops.MultiHashTableCheckpointRestorerListener(\n              basename, ps_monitor),\n          hash_filter_ops.HashFilterCheckpointRestorerListener(\n              basename,\n              hash_filters,\n              self._enable_hash_filter,\n              enable_save_restore=(\n                  not self.config.enable_full_sync_training and\n                  self.config.filter_type != FilterType.PROBABILISTIC_FILTER)),\n          CustomRestoreListener(\n              self.config.reload_alias_map,\n              self.config.clear_nn,\n              self.config.continue_training,\n              model_dir=model_dir,\n              enable_alias_map_auto_gen=self.config.enable_alias_map_auto_gen)\n      ]\n      return (basic_restore_hook.CheckpointRestorerHook(\n          listeners=restore_listeners),)\n\n    def get_saver_listeners_for_exporting(save_path: str,\n                                          export_dir_base: str = None,\n                                          dense_only=False,\n                                          exempt_checkpoint_paths=None,\n                                          include_graphs=None):\n      # TODO(leqi.zou): Add a test for this when graceful shutdown is implemented.\n      if is_exporting():\n        # Safety check. To prevent infinite recursion.\n        raise ValueError(\n            \"Logic corrupted. Try to call exporting listeners inside exporting.\"\n        )\n      if dense_only and self._params.serving.export_mode is not ExportMode.DISTRIBUTED:\n        raise ValueError(\n            \"Please set params.serving.export_mode = ExportMode.DISTRIBUTED. \"\n            \"Only DISTRIBUTED mode is allowed when dense_only=True, got\",\n            self._params.serving.export_mode)\n      if not self._params.serving.export_when_saving:\n        return []\n      model_dir = os.path.dirname(save_path)\n      serving_input_receiver_fn = self.create_serving_input_receiver_fn()\n      if not serving_input_receiver_fn:\n        raise ValueError(\"A valid serving_input_receiver_fn must be provided \",\n                         \"if exporting is enabled. Got \",\n                         serving_input_receiver_fn)\n      if not export_dir_base:\n        export_dir_base = os.path.join(model_dir,\n                                       self._params.serving.export_dir_base)\n      # TODO(leqi.zou): Needs to do lifecycle management for exported model.\n\n      exporter = create_exporter(self,\n                                 model_dir=model_dir,\n                                 warmup_file=self.config.warmup_file,\n                                 export_dir_base=export_dir_base,\n                                 dense_only=dense_only,\n                                 include_graphs=include_graphs,\n                                 export_context_list=self._export_context_list)\n      barrier_listeners = []\n      if self.config.enable_sync_training and not dense_only and not self.config.enable_partial_sync_training:\n        barrier_listeners.append(\n            sync_training_hooks.SyncTrainingBarrierSaverListener())\n      return barrier_listeners + [\n          export_hooks.ExportSaverListener(\n              save_path,\n              serving_input_receiver_fn,\n              exporter,\n              exempt_checkpoint_paths=exempt_checkpoint_paths,\n              dense_only=dense_only)\n      ]\n\n    def get_hooks_for_save(model_dir: str, hash_filters: List[tf.Tensor],\n                           barrier_op: barrier_ops.BarrierOp,\n                           ps_monitor: save_utils.PsMonitor):\n      logging.info(\"get_hooks_for_save model_dir is \" + model_dir)\n      if not model_dir:\n        raise ValueError(\"model_dir must be provided\")\n      hooks = []\n\n      exempt_checkpoint_paths = list()\n      monolith_ckpt_state = save_utils.get_monolith_checkpoint_state(\n          model_dir, remove_invalid_path=True)\n      ckpt_state = tf.train.get_checkpoint_state(model_dir)\n      if monolith_ckpt_state and monolith_ckpt_state.exempt_model_checkpoint_paths:\n        exempt_checkpoint_paths = [\n            os.path.basename(p)\n            for p in monolith_ckpt_state.exempt_model_checkpoint_paths\n        ]\n        logging.info(\n            'Exempt checkpoint paths: {}'.format(exempt_checkpoint_paths))\n        existing_checkpoint_paths = set([\n            os.path.basename(p) for p in ckpt_state.all_model_checkpoint_paths\n        ])\n        assert all(\n            [p in existing_checkpoint_paths for p in exempt_checkpoint_paths])\n\n      is_root_node = not self.config.enable_sync_training or self.config.index == 0\n\n      def create_saver():\n        return save_utils.PartialRecoverySaver(\n            sharded=not self.config.enable_full_sync_training,\n            max_to_keep=self.config.checkpoints_max_to_keep,\n            keep_checkpoint_every_n_hours=24,\n            ps_monitor=ps_monitor,\n            exempt_checkpoint_paths=exempt_checkpoint_paths,\n            skip_save=not is_root_node,\n            model_dir=model_dir)\n\n      saver = create_saver()\n\n      tf.compat.v1.add_to_collection(tf.compat.v1.GraphKeys.SAVERS, saver)\n      basename = os.path.join(model_dir, \"model.ckpt\")\n\n      save_listeners = [\n          hash_table_ops.HashTableCheckpointSaverListener(basename),\n          multi_hash_table_ops.MultiHashTableCheckpointSaverListener(\n              basename, write_ckpt_info=is_root_node),\n          hash_filter_ops.HashFilterCheckpointSaverListener(\n              basename,\n              hash_filters,\n              self._enable_hash_filter,\n              enable_save_restore=(not self.config.enable_full_sync_training))\n      ]\n\n      include_graphs = None\n      if self.config.enable_full_sync_training:\n        include_graphs = [f\"ps_{self.config.index}\"]\n        if is_root_node:\n          include_graphs.append(\"entry\")\n          include_graphs.append(\"dense_0\")\n\n      save_listeners += get_saver_listeners_for_exporting(\n          basename,\n          exempt_checkpoint_paths=exempt_checkpoint_paths,\n          include_graphs=include_graphs)\n\n      if self.config.enable_model_ckpt_info and is_root_node:\n        save_listeners.append(ckpt_info.FidSlotCountSaverListener(model_dir))\n\n      if self.config.feature_eviction_on_save:\n        save_listeners.extend([\n            hash_table_ops.HashTableRestorerSaverListener(basename),\n            multi_hash_table_ops.MultiHashTableRestorerSaverListener(basename),\n        ])\n\n\n      save_checkpoints_secs = self.config.save_checkpoints_secs or self._params.train.save_checkpoints_secs\n      save_checkpoints_steps = self.config.save_checkpoints_steps or self._params.train.save_checkpoints_steps\n      if save_checkpoints_secs is None and save_checkpoints_steps is None:\n        save_checkpoints_steps = 100000000\n      if (save_checkpoints_secs is not None) and (save_checkpoints_steps\n                                                  is not None):\n        raise ValueError(\n            \"Can not provide both save_checkpoints_secs and save_checkpoints_steps.\"\n        )\n\n      # We do not use barrier for the sync training.\n      guard_listeners = []\n      if not self.config.enable_sync_training:\n        guard_listeners.append(\n            ckpt_hooks.BarrierSaverListener(\n                barrier_op,\n                max_pending_seconds=self._params.train.\n                max_pending_seconds_for_barrier))\n\n      # In the rare case, we need to do the first save.\n      # Otherwise, partial_recovery won't work and will go through initialization phase.\n      # It is supposed to be\n      # should_do_first_save = self.config.partial_recovery and ckpt_state is None\n      # Here we just make it false because there are issues with uninitialized iterator.\n      should_do_first_save = False\n      if self.config.enable_model_dump:\n        save_utils.NoFirstSaveCheckpointSaverHook._in_model_dump_mode = True\n      saver_hook = save_utils.NoFirstSaveCheckpointSaverHook(\n          model_dir,\n          save_secs=save_checkpoints_secs,\n          save_steps=save_checkpoints_steps,\n          saver=saver,\n          listeners=save_listeners,\n          guard_saver_listeners=guard_listeners,\n          save_graph_def=is_root_node,\n          tide_start_hour=self.config.tide_start_hour,\n          tide_start_minute=self.config.tide_start_minute,\n          tide_end_hour=self.config.tide_end_hour,\n          tide_end_minute=self.config.tide_end_minute,\n          tide_save_secs=self.config.tide_save_secs,\n          ignore_save_errors=self.config.enable_realtime_training,\n          is_dense_only=False,\n          use_native_multi_hash_table=self.config.use_native_multi_hash_table,\n          no_first_save=not should_do_first_save)\n\n      hooks.append(saver_hook)\n\n      if not self.config.enable_sync_training:\n        server_hook = server_hook_lib.ServerHook(model_dir, barrier_op,\n                                                 saver_hook)\n        hooks.extend([server_hook])\n\n      dense_only_save_checkpoints_steps = self.config.dense_only_save_checkpoints_steps or self._params.train.dense_only_save_checkpoints_steps\n      dense_only_save_checkpoints_secs = self.config.dense_only_save_checkpoints_secs or self._params.train.dense_only_save_checkpoints_secs\n      if (dense_only_save_checkpoints_steps or\n          dense_only_save_checkpoints_secs) and is_root_node:\n        dense_saver = create_saver()\n\n        dense_model_dir = os.path.join(model_dir, \"dense_only\")\n        stats = tf.train.get_checkpoint_state(dense_model_dir)\n        if stats:\n          dense_saver.recover_last_checkpoints(stats.all_model_checkpoint_paths)\n        dense_basename = os.path.join(dense_model_dir, \"model.ckpt\")\n        export_dir_base = os.path.join(model_dir,\n                                       self._params.serving.export_dir_base)\n        save_utils.NoFirstSaveCheckpointSaverHook._has_dense_only = True\n        dense_guard_listeners = guard_listeners if self.config.dense_only_stop_training_when_save else []\n\n        dense_saver_hook = save_utils.NoFirstSaveCheckpointSaverHook(\n            dense_model_dir,\n            save_secs=dense_only_save_checkpoints_secs,\n            save_steps=dense_only_save_checkpoints_steps,\n            saver=dense_saver,\n            listeners=get_saver_listeners_for_exporting(\n                dense_basename,\n                export_dir_base=export_dir_base,\n                dense_only=True,\n                exempt_checkpoint_paths=exempt_checkpoint_paths),\n            guard_saver_listeners=dense_guard_listeners,\n            tide_start_hour=self.config.tide_start_hour,\n            tide_start_minute=self.config.tide_start_minute,\n            tide_end_hour=self.config.tide_end_hour,\n            tide_end_minute=self.config.tide_end_minute,\n            tide_save_secs=self.config.tide_save_secs,\n            ignore_save_errors=self.config.enable_realtime_training,\n            is_dense_only=True)\n\n        if self.config.enable_sync_training and self.config.enable_realtime_training:\n          hooks.append(\n              sync_training_hooks.SyncTrainingSaverControlHook(\n                  model_dir, dense_saver_hook.timer))\n\n        hooks.append(dense_saver_hook)\n\n      return tuple(hooks)\n\n    def get_slow_start_hook(slow_start_steps: int):\n\n      if slow_start_steps:\n        return (session_run_hooks.CustomGlobalStepWaiterHook(\n            int(slow_start_steps * np.log(1 + self.config.index)),\n            max_non_tide_wait_minute=self.config.max_slow_start_wait_minute),)\n      return ()\n\n    def get_tide_stopping_hook():\n      if self.config.tide_start_hour is not None and self.config.tide_end_hour is not None:\n        return (session_run_hooks.TideStoppingHook(\n            self.config.tide_start_hour, self.config.tide_start_minute,\n            self.config.tide_end_hour, self.config.tide_end_minute),)\n      return ()\n\n    def get_hooks_for_metrics(model_dir: str, save_steps: int):\n      hooks = []\n      if self._params.metrics.enable_tf2_profiler_hook and is_chief(self.config):\n        start_step = self.config.profile_some_steps_from\n        end_step = None if start_step is None else start_step + 10\n        save_steps = self.config.profile_save_steps_interval\n        hooks.append(\n            Tf2ProfilerHook(\n                logdir=model_dir, init_step_range=[start_step, end_step], save_steps=save_steps))\n\n      if self.config.profile_with_nvprof_from_to and is_chief(self.config):\n        s, e = self.config.profile_with_nvprof_from_to.split(',')\n        save_steps = self.config.profile_save_steps_interval\n        hooks.append(\n            NVProfilerHook(init_step_range=[int(s), int(e)], save_steps=save_steps))\n\n      if self._params.metrics.enable_throughput_hook and is_chief(self.config):\n        hooks.append(\n            ThroughputMetricHook(\n                model_name=self.config.model_name,\n                start_time_secs=self.config.containers_ready_time_secs,\n                cluster_type=self.config.cluster_type))\n      return tuple(hooks)\n\n    def variable_prefetch_enabled():\n      return not self.config.enable_sync_training and self.config.enable_variable_prefetch\n\n    def get_cached_variable_context():\n      if variable_prefetch_enabled():\n        return tf.variable_creator_scope(variables.cached_variable_creator)\n      return contextlib.nullcontext()\n\n    def get_partitioner_variable_context():\n      if not is_exporting() and self.config.enable_variable_partition:\n        # TODO(leqi.zou): This only works for tf.compat.v1.get_variable,\n        # but not for tf.Variable.\n        #\n        # Finally, we can use something similar to PSStrategy to solve\n        # this problem.\n        logging.info(\"partition max_shards={}\".format(self.config.num_ps))\n        return tf.compat.v1.variable_scope(\n            \"\",\n            partitioner=tf.compat.v1.variable_axis_size_partitioner(\n                max_shard_bytes=1 << 17, max_shards=self.config.num_ps))\n      return contextlib.nullcontext()\n\n    def get_variable_prefetch_hooks():\n      if variable_prefetch_enabled():\n        return (variables.FetchAllCachedVariablesHook(),)\n      return ()\n\n    def get_itempool_hook(model_dir, mode):\n      pools = tf.compat.v1.get_collection(POOL_KEY)\n      if pools and mode != tf.estimator.ModeKeys.PREDICT:\n        logging.info(\"append itempool_save_restore_hook in training_hooks\")\n        save_checkpoints_secs = self.config.save_checkpoints_secs or self._params.train.save_checkpoints_secs\n        save_checkpoints_steps = self.config.save_checkpoints_steps or self._params.train.save_checkpoints_steps\n        if save_checkpoints_secs is None and save_checkpoints_steps is None:\n          save_checkpoints_steps = 100000000\n        item_pool_hook = ItemPoolSaveRestoreHook(\n            model_dir=model_dir, save_steps=save_checkpoints_steps, mode=mode)\n        if hasattr(self._task, 'add_training_hook'):\n          self._task.add_training_hook(item_pool_hook)\n\n    def model_fn(features: Dict[str, tf.Tensor], mode: str,\n                 config: tf.estimator.RunConfig):\n      hash_table, hash_filters = create_hash_table_and_filters_fn()\n      logging.info(\n          f'\\n> hash_table: {hash_table}\\n> hash_filters: {hash_filters}')\n      # For prefetch queue, collect auxiliary tensors\n\n      get_itempool_hook(config.model_dir, mode=mode)\n\n      auxiliary_bundle = {}\n      async_function_mgr = prefetch_queue.AsyncFunctionMgr(\n          is_async=self.config.enable_variable_postpush)\n      self._task.ctx.async_function_mgr = async_function_mgr\n      eof_key = sync_training_hooks.EofAwareTask.EOF_KEY\n      if '2' in features:\n        auxiliary_bundle[eof_key] = features.pop('2')\n        logging.info(f'eof: {auxiliary_bundle[eof_key]}, {id(auxiliary_bundle[eof_key])}')\n        features = features.pop('1')\n      elif eof_key in features:\n        auxiliary_bundle[eof_key] = features.pop(eof_key)\n        logging.info(f'eof: {auxiliary_bundle[eof_key]}, {id(auxiliary_bundle[eof_key])}')\n\n      def call_raw_model_fn(features):\n        raw_model_fn = self._task.create_model_fn()\n        with get_cached_variable_context(), get_partitioner_variable_context():\n          spec = raw_model_fn(features=features, mode=mode, config=config)\n\n          return spec\n\n      if self.config.enable_fused_layout:\n        self._task.ctx.feature_factory = None\n        lookup_callable_fn = hash_table.lookup(\n            features,\n            auxiliary_bundle,\n            ret_lookup_callable_fn=True,\n            embedding_prefetch_capacity=self.config.embedding_prefetch_capacity)\n        # args are data we will transfer to remote deivce if needed.\n        args = (auxiliary_bundle, features)\n        logging.info(\n            f\"remote input: auxiliary_bundle[{auxiliary_bundle}], features:[{features}]\"\n        )\n\n        def call_model_fn(args):\n          # add lookup_callable_fn here to support with_remote_gpu\n          auxiliary_bundle_ = args[0]\n          features_ = args[1]\n          layout_embeddings = lookup_callable_fn(auxiliary_bundle_, features_)\n          logging.info(\n              f\"hash_table lookup when enable_fused_layout res: {layout_embeddings} {auxiliary_bundle_} {features_}\"\n          )\n          auxiliary_bundle_.update(features_)\n\n          # set layout_factory, this step must after embedding_prefetch\n          self._task.ctx.layout_factory = feature.EmbeddingLayoutFactory(\n              hash_table,\n              layout_embeddings,\n              auxiliary_bundle=auxiliary_bundle_,\n              async_function_mgr=async_function_mgr,\n              async_push=self.config.enable_embedding_postpush)\n          return call_raw_model_fn(features_)\n      else:\n        if self.config.reorder_fids_in_data_pipeline:\n          features, res_pack = features[\"1\"]\n          features = {\n              k: v\n              for k, v in features.items()\n              if not isinstance(v, tf.RaggedTensor)\n          }\n          embedding_ragged_ids, res_pack = res_pack\n          name_to_ids = {\n              k: None for k in embedding_ragged_ids.keys()\n              # None to keep interface, we will only use the keys\n          }\n          auxiliary_bundle[\"features\"] = features\n          auxiliary_bundle[\n              \"embedding_ragged_ids\"] = parser_utils.RaggedEncodingHelper.contract(\n                  embedding_ragged_ids)\n          logging.info(\n            f\"input: auxiliary_bundle[{auxiliary_bundle}], features:[{features}]\"\n          )\n          embeddings, auxiliary_bundle = hash_table.lookup(\n              name_to_ids, auxiliary_bundle, res_pack)\n          dequeued_embeddings = embeddings\n        else:\n          embedding_ragged_ids: Dict[str, tf.RaggedTensor] = {\n              k: v for k, v in features.items() if k in embedding_feature_names\n          }\n          features: Dict[str, tf.Tensor] = {  # Dense features or labels\n              k: v\n              for k, v in features.items()\n              if not isinstance(v, tf.RaggedTensor)\n          }\n          auxiliary_bundle[\"features\"] = features\n          auxiliary_bundle[\"embedding_ragged_ids\"] = embedding_ragged_ids\n          # 'feature_name' -> <tf.Tensor 'RaggedFromVariant:1' shape=(None,) dtype=int64>\n          name_to_ids: Dict[str, tf.Tensor] = {\n              k: v.values for k, v in embedding_ragged_ids.items()\n          }\n          # for MergedMultiTypeHashTable, lookup returns:\n          embeddings: Dict[str, tf.Tensor] = hash_table.lookup(name_to_ids)\n          logging.info(\n            f\"input: auxiliary_bundle[{auxiliary_bundle}], features:[{features}]\"\n          )\n          (dequeued_embeddings,\n           auxiliary_bundle), q = enqueue_dicts_with_queue_return(\n               (embeddings, auxiliary_bundle),\n               capacity=self.config.embedding_prefetch_capacity)\n          if q:\n            hash_table.add_queue_hook(EnqueueHook(q))\n\n        dequeued_embedding_ragged_ids = auxiliary_bundle.pop(\n            \"embedding_ragged_ids\")\n        dequeued_features = auxiliary_bundle.pop(\"features\")\n\n        # record dequeued features, primarily for evaluation purposes\n        for name, tensor in dequeued_embedding_ragged_ids.items():\n          tensor.feature_name = name\n          tf.compat.v1.add_to_collection('dequeued_sparse_features', tensor)\n\n        for name, tensor in dequeued_features.items():\n          try:\n            tensor.feature_name = name\n            tf.compat.v1.add_to_collection('dequeued_features', tensor)\n          except Exception as e:\n            logging.error(f'tensor name is {name}')\n            logging.error(f'exception is {str(e)}')\n\n        embedding_slices = feature.create_embedding_slices(\n            dequeued_embeddings, dequeued_embedding_ragged_ids,\n            feature_to_combiner, feature_to_unmerged_slice_dims)\n\n        args = (\n            dequeued_features,\n            embedding_slices,\n        )\n\n        # To enable remote inference, we need to transfer all tensors\n        # which are needed by using remote predict. All tensors used should be\n        # listed in the parameter, otherwise export graph will complain that\n        # tensor may come from another graph.\n        #\n        # Notice this is only for the inference. In the training, TensorFlow\n        # will automatically add send/recv if tensors are on the different graph.\n        def call_model_fn(args):\n          self._task.ctx.layout_factory = None\n          if self.config.enable_full_sync_training:\n            # TODO(zouxuan): enable this for async training later on.\n            self._task.ctx.feature_factory = _FusedCpuFeatureFactory(\n                hash_table,\n                dequeued_embeddings,\n                args[1],\n                get_req_time(args[0]),\n                auxiliary_bundle=auxiliary_bundle,\n                use_native_multi_hash_table=self.config.\n                use_native_multi_hash_table)\n          else:\n            self._task.ctx.feature_factory = _CpuFeatureFactory(\n                hash_table,\n                dequeued_embedding_ragged_ids,\n                dequeued_embeddings,\n                args[1],\n                get_req_time(args[0]),\n                async_function_mgr=async_function_mgr,\n                async_push=self.config.enable_embedding_postpush)\n          return call_raw_model_fn({**args[0], **dequeued_embeddings})\n\n      spec = None\n      if export_context.is_exporting(\n      ) and export_context.get_current_export_ctx().with_remote_gpu:\n\n        def remote_call(tensors):\n          with tf.device(\"/device:GPU:0\"):\n            spec = call_model_fn(tensors)\n          return spec.predictions\n\n        g = export_context.get_current_export_ctx().sub_graph(\"dense_0\")\n        with g.as_default():\n          helper = export_utils.RemotePredictHelper(\"gpu_remote_call\", args,\n                                                    remote_call)\n        predictions = helper.call_remote_predict(\n            model_name=f\"{native_task_context.get().model_name or ''}:dense_0\",\n            old_model_name=\"dense_0\")\n        spec = tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)\n      else:\n        spec = call_model_fn(args)\n\n      if not is_exporting():\n        ps_monitor = save_utils.PsMonitor(\n            self.config.num_ps\n        ) if self.config.partial_recovery and self.config.num_ps > 1 else None\n\n        training_hooks = ()\n        training_chief_hooks = ()\n        ckpt_helper = ckpt_hooks.WorkerCkptHelper(config.model_dir,\n                                                  self.config.index)\n        # SetCurrentSessionHook must present first.\n        training_hooks += (session_hooks.SetCurrentSessionHook(),)\n        barrier_op = None\n        enable_sync_hook = self.config.num_workers > 1 and not self.config.enable_sync_training\n        sync_hook_helper = sync_hooks.TrainingHooksHelper(\n            enable_sync_hook,\n            self.config.num_workers,\n            self.config.index,\n            chief_timeout_seconds=self.config.chief_timeout_secs)\n        if not self.config.enable_sync_training:\n          barrier_op = barrier_ops.BarrierOp(\n              self.config.num_workers,\n              is_chief=is_chief(self.config),\n              barrier_callbacks=[\n                  ckpt_helper.create_save_iterator_callback(),\n              ])\n          training_hooks += sync_hook_helper.training_hooks + (\n              barrier_ops.BarrierHook(self.config.index, barrier_op),)\n          if self._params.mode == tf.estimator.ModeKeys.TRAIN:\n            training_hooks += get_slow_start_hook(\n                self._params.train.slow_start_steps)\n          training_hooks += (ckpt_helper.create_restorer_hook(),)\n\n          training_chief_hooks += (ps_check_hooks.PsHealthCheckerHook(\n              ps_check_hooks.Config(barrier_op=barrier_op,\n                                    num_ps=self.config.num_ps)),)\n\n        # Prefetch hooks should be put after control hooks (like slow start hook).\n        # Just in case, we have shuffle in dataset and we start reading too much\n        # data before we actually start the training.\n        training_hooks += tuple(hash_table.get_queue_hooks())\n        training_hooks += get_variable_prefetch_hooks()\n        training_hooks += tuple(self._task.ctx.async_function_mgr.hooks)\n        \"\"\"\n        Make sure sync hook running after restore hook and before save hook.\n        The running order is similar to:\n          'after_create_session' : restore, chief start, worker start\n          'end'                  : worker end, save, chief end\n        \"\"\"\n        training_chief_hooks += (\n            get_hooks_for_restore(config.model_dir, hash_filters, ps_monitor) +\n            sync_hook_helper.training_chief_hooks + get_hooks_for_save(\n                config.model_dir, hash_filters, barrier_op, ps_monitor) +\n            get_hooks_for_metrics(config.model_dir, config.save_summary_steps))\n\n        predicting_hooks = (get_hooks_for_restore(config.model_dir,\n                                                  hash_filters, ps_monitor))\n        if self.config.enable_partial_sync_training and self.config.index != 0:\n          elements = []\n          local_init_ops = tf.compat.v1.get_collection(\n              tf.compat.v1.GraphKeys.LOCAL_INIT_OP)\n          if local_init_ops:\n            elements.extend(local_init_ops)\n          else:\n            local_init_op = tf.compat.v1.train.Scaffold.get_or_default(\n                'local_init_op', tf.compat.v1.GraphKeys.LOCAL_INIT_OP,\n                tf.compat.v1.train.Scaffold.default_local_init_op)\n            elements.append(local_init_op)\n\n          init_ops = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.INIT_OP)\n          if init_ops:\n            elements.extend(init_ops)\n          else:\n\n            def default_init_op():\n              return tf.group(\n                  tfvariables.global_variables_initializer(),\n                  resources.initialize_resources(resources.shared_resources()))\n\n            init_op = tf.compat.v1.train.Scaffold.get_or_default(\n                'init_op', tf.compat.v1.GraphKeys.INIT_OP, default_init_op)\n            elements.append(init_op)\n\n          logging.info(f'local_init_op is {elements}')\n          scaffold = tf.compat.v1.train.Scaffold(\n              local_init_op=tf.group(elements) if elements else None,\n              ready_for_local_init_op=NoOp())\n        else:\n          scaffold = None\n        spec = spec._replace(\n            training_chief_hooks=training_chief_hooks +\n            spec.training_chief_hooks,\n            training_hooks=training_hooks + spec.training_hooks,\n            prediction_hooks=predicting_hooks + spec.prediction_hooks,\n            scaffold=scaffold)\n\n        logging.info(\"Training Chief Hooks: {}\".format(\n            spec.training_chief_hooks))\n        logging.info(\"Training Hooks: {}\".format(spec.training_hooks))\n\n      logging.info(f'dequeue input: auxiliary_bundle[{auxiliary_bundle}], features:[{features}]')\n      dequeued_eof = auxiliary_bundle.pop(eof_key, None)\n      if dequeued_eof is not None:\n        tf.compat.v1.add_to_collection(eof_key, dequeued_eof)\n        logging.info(f'eof dequeue: {dequeued_eof}, {id(dequeued_eof)}')\n      return spec\n\n    def wrapped_model_fn(features: Dict[str, tf.Tensor], mode: str,\n                         config: tf.estimator.RunConfig):\n      with native_task_context.with_ctx(\n          make_native_task_context(self.config, self._sync_backend)):\n        return model_fn(features, mode, config)\n\n    return wrapped_model_fn\n\n  def create_serving_input_receiver_fn(self):\n    return self._task.create_serving_input_receiver_fn()\n\n\n@dataclasses.dataclass\nclass DistributedCpuTrainingConfig(CpuTrainingConfig):\n  \"\"\"The training config for distributed training.\n\n  attributes:\n    :param model_dir: The directory where the model is load/saved.\n    :param tensorboard_log_path: The logdir of tensorboard, use model_dir\n                                 instead if empty\n    :param intra_op_parallelism_threads: intra_op parallelism threads.\n    :param inter_op_parallelism_threads: inter_op parallelism threads.\n    :param num_extra_ps: The number of extra ps for ps benchmark.\n    :param num_redundant_ps: The number of redundant ps for quickly starting.\n                             We will pick |num_ps| from |num_ps + num_extra_ps + num_redundant_ps| ps\n    :param uuid: uuid of cpu training.\n    :param operation_timeout_in_ms: Global timeout for all blocking operations in this session.\n    :param session_creation_timeout_secs: Max time workers should wait for a session to become available.\n    :param max_retry_times: Maximum retry times for workers to start train.\n    :param retry_wait_in_secs: Sleep time interval to wait for worker retry.\n    :param fountain_zk_host: zk_host for fountain service.\n    :param fountain_model_name: model_name for fountain service.\n    :param dc_aware: data-center aware or not.\n  \"\"\"\n\n  model_dir: str = \"\"\n  tensorboard_log_path: str = \"\"\n  intra_op_parallelism_threads: int = 8\n  inter_op_parallelism_threads: int = 16\n  num_extra_ps: int = 0\n  num_redundant_ps: int = 0\n  uuid: str = \"\"\n\n  operation_timeout_in_ms: int = -1\n  session_creation_timeout_secs: int = 7200\n\n  max_retry_times: int = 0\n  retry_wait_in_secs: int = 30\n\n  fountain_zk_host: str = \"\"\n  fountain_model_name: str = \"\"\n  dc_aware: bool = False\n\n\ndef _prepare_server(target: str, config: DistributedCpuTrainingConfig):\n  \"\"\"Do some preparation before we register the server to the server discovery\"\"\"\n  if config.server_type == \"ps\":\n    session_config = cluster_manager.generate_session_config()\n    with tf.compat.v1.Session(target, config=session_config) as sess:\n      # Creates machine info so the following access won't create new machine info.\n      sess.run(\n          logging_ops.machine_info(\n              shared_name=ps_check_hooks.get_ps_machine_info_shared_name(\n                  config.index)))\n\n\ndef _shutdown_ps(target, cluster, task, num_ps):\n  cluster = copy.deepcopy(cluster)\n  # Worker has already shutdowned.\n  if \"worker\" in cluster:\n    del cluster[\"worker\"]\n  session_config = cluster_manager.generate_session_config((cluster, task))\n  with tf.compat.v1.Session(target, config=session_config) as sess:\n    for i in range(num_ps):\n      logging.info('Try to shutdown ps {}'.format(i))\n      with tf.device(utils.ps_device(i)):\n        queue = tf.queue.FIFOQueue(1,\n                                   tf.int32,\n                                   shared_name=\"ps_queue_\" + str(i))\n      sess.run(queue.enqueue(1))\n      logging.info('Shutdown ps {} successfully!'.format(i))\n\n\ndef _join_ps(target, ps_index, sync_backend: SyncBackend = None):\n  session_config = cluster_manager.generate_session_config()\n  with tf.compat.v1.Session(target, config=session_config) as sess:\n    queue = tf.queue.FIFOQueue(1,\n                               tf.int32,\n                               shared_name=\"ps_queue_\" + str(ps_index))\n    finished = False\n    t = None\n    if sync_backend is not None:\n      sync_client = ParameterSyncClient(\n          distributed_serving_ops.parameter_sync_client_from_config(\n              name_suffix=str(ps_index)))\n      sync_config_str = tf.compat.v1.placeholder(tf.string,\n                                                 shape=(),\n                                                 name=\"sync_config_str\")\n      sync_run_step = sync_client.create_sync_op(sync_config_str)\n\n      def parameter_sync_job(sess, sync_run_step: tf.Tensor):\n        with sess.graph.as_default(\n        ):  # To make sure the graphs in/out thread are same\n          nonlocal finished\n          while not finished:\n            start = timeit.default_timer()\n            try:\n              sess.run(sync_run_step,\n                       feed_dict={\n                           sync_config_str:\n                               distributed_serving_ops.refresh_sync_config(\n                                   sync_backend, ps_index)\n                       },\n                       options=tf.compat.v1.RunOptions(timeout_in_ms=1000 * 60))\n            except tf.errors.OpError as e:\n              logging.error('Error occurred when synchronizing parameter: %s',\n                            str(e))\n              exc_type, exc_value, exc_traceback_obj = sys.exc_info()\n              logging.error(f\"exc_type: {exc_type}\")\n              logging.error(f\"exc_value: {exc_value}\")\n              traceback.print_tb(exc_traceback_obj, limit=10)\n            total_cost = timeit.default_timer() - start\n            # Synchronizing parameter per 10 seconds\n            time.sleep(max(0, 10 - total_cost))\n          logging.info(\n              \"Ps {} received chief's shutdown signal...\".format(ps_index))\n\n      t = threading.Thread(target=parameter_sync_job,\n                           args=(sess, sync_run_step))\n      t.start()\n      logging.info(\n          'Ps {} started a thread for parameter sync!'.format(ps_index))\n\n    # Try to dequeue, if success means chief will finish soon.\n    sess.run(queue.dequeue())\n    finished = True\n    if t:\n      t.join()\n    logging.info(\"Ps {} shutdown successfully!\".format(ps_index))\n\n\ndef _get_blocked_addrs(cluster: Dict, ignored_jobs: Set = {}):\n  cluster_spec = tf.train.ClusterSpec(cluster)\n  addrs = set()\n  for job in cluster_spec.jobs:\n    if job not in ignored_jobs:\n      for addr in cluster_spec.job_tasks(job):\n        addrs.add(addr)\n  return list(addrs)\n\n\nclass NodeAliveCheckerError(Exception):\n\n  def __init__(self, msg):\n    super(NodeAliveCheckerError, self).__init__(self)\n    self.msg = msg\n\n  def __str__(self):\n    return self.msg\n\n\ndef _do_worker_train(config: DistributedCpuTrainingConfig,\n                     params: InstantiableParams, \n                     cluster: Dict, \n                     task: Dict,\n                     user_hooks = None):\n  params.mode = config.mode\n  native_task = params.instantiate()\n  if not isinstance(native_task, NativeTask):\n    raise ValueError(\n        \"distributed train only support NativeTask. Got {}\".format(native_task))\n\n  if params.serving.with_remote_gpu and config.enable_model_dump:\n    # 当with_remote_gpu=True时，export时会在dense subgraph中运行model_fn\n    # 导致dump下来的infer model不完整，缺少serving_input_receiver_fn信息\n    raise ValueError(\"unsupport enable_model_dump while with_remote_gpu=True\")\n\n  session_config = cluster_manager.generate_session_config((cluster, task))\n  session_config.operation_timeout_in_ms = config.operation_timeout_in_ms\n\n  check_addrs = _get_blocked_addrs(cluster=cluster, ignored_jobs={'worker'})\n  alive_checker = net_utils.NodeAliveChecker(check_addrs, timeout=60)\n  if not alive_checker.all_nodes_alive():\n    raise NodeAliveCheckerError(\"{} is unreachable\".format(','.join(\n        alive_checker.get_dead_nodes())))\n  os.environ[\"TF_CONFIG\"] = json.dumps({\"cluster\": cluster, \"task\": task})\n  try:\n    update_session_config_for_gpu(session_config)\n    run_config = tf.estimator.RunConfig(\n        model_dir=config.model_dir,\n        session_config=session_config,\n        save_summary_steps=config.save_summary_steps * config.num_workers,\n        log_step_count_steps=config.log_step_count_steps * config.num_workers,\n        session_creation_timeout_secs=config.session_creation_timeout_secs,\n        device_fn=config.device_fn)\n\n    training = CpuTraining(config, native_task)\n    if config.enable_partial_sync_training or config.use_dataservice:\n      training = sync_training_hooks.EofAwareTask(training, config.use_dataservice)\n    estimator = tf.estimator.Estimator(training.create_model_fn(),\n                                       config=run_config)\n\n    if is_chief(config):\n      _save_debugging_info(config, cluster, training)\n    run_hooks = get_sync_run_hooks(False)\n    if user_hooks is not None:\n      run_hooks += user_hooks\n    estimator.train(training.create_input_fn(config.mode),\n                hooks=run_hooks,\n                max_steps=params.train.max_steps)\n\n    if is_chief(config) and config.enable_resource_constrained_roughsort and config.mode==tf.estimator.ModeKeys.TRAIN:\n      logging.info(f\"roughsort_items_use_parquet: {config.roughsort_items_use_parquet}\")\n      if config.roughsort_items_use_parquet:\n        items_path = os.path.join(config.model_dir, \"candidate_items.pb\")\n        _convert_parquets_to_instance(config.roughsort_candidate_items_path, items_path)\n      else:\n        items_path = config.roughsort_candidate_items_path\n\n      logging.info(\"Start to evaluate item data...\")\n      # params.p.only_save_item_cache_hashtable = True\n      params.mode = tf.estimator.ModeKeys.PREDICT\n      native_task = params.instantiate()\n      training = CpuTraining(config, native_task)\n      if config.enable_partial_sync_training or config.use_dataservice:\n        training = sync_training_hooks.EofAwareTask(training, config.use_dataservice)\n      estimator = tf.estimator.Estimator(training.create_model_fn(), config=run_config)\n      estimator.train(training._task.create_item_input_fn(\n          items_path), max_steps=params.train.max_steps)\n\n  finally:\n    # TODO(leqi.zou): we have some thread safety issue in the test.\n    if \"TF_CONFIG\" in os.environ:\n      del os.environ[\"TF_CONFIG\"]\n\n  return estimator\n\n\n_EXTRA_PS_BENCHMARK_SECS = 120\n\n\ndef _run_ps_benchmark(config: DistributedCpuTrainingConfig,\n                      num_ps_required: int, cluster: dict, task: dict, user_hooks):\n  config = copy.deepcopy(config)\n  cluster = copy.deepcopy(cluster)\n  bm_params = ps_benchmark.PsBenchMarkTask.params()\n  ps_list = copy.copy(cluster[\"ps\"])\n  bm_params.bm_config = ps_benchmark.BenchmarkConfig(\n      ps_list=ps_list,\n      num_ps_required=num_ps_required,\n      num_workers=config.num_workers,\n      index=config.index,\n      benchmark_secs=_EXTRA_PS_BENCHMARK_SECS)\n  config.num_ps += config.num_extra_ps\n  config.model_dir = os.path.join(config.model_dir, \"benchmark_dir\")\n  config.operation_timeout_in_ms = int(_EXTRA_PS_BENCHMARK_SECS * 1000 +\n                                       30 * 1000)\n  logging.info(\"Run PS benchmark\")\n  _do_worker_train(config, bm_params, cluster, task, user_hooks)\n  cluster[\"ps\"] = ps_list\n  return cluster\n\n\ndef _save_debugging_info(config: DistributedCpuTrainingConfig, cluster: dict,\n                         training: CpuTraining):\n  debugging_info = debugging_info_pb2.DebuggingInfo()\n  debugging_info.cluster.chief_addr = cluster[\"chief\"][0]\n  for addr in cluster[\"ps\"]:\n    debugging_info.cluster.ps_addrs.append(addr)\n  debugging_info.num_workers = config.num_workers\n  for k, v in training.feature_configs[0].items():\n    feature_name_config = debugging_info.feature_name_configs.add()\n    feature_name_config.feature_name = k\n    feature_name_config.config_str = str(v)\n\n  debugging_info_file_name = utils.get_debugging_info_file_name(\n      config.model_dir)\n  tf.io.gfile.makedirs(os.path.dirname(debugging_info_file_name))\n  file_io.atomic_write_string_to_file(debugging_info_file_name,\n                                      debugging_info.SerializeToString())\n\n\ndef _get_replica_device_setter(config):\n  if config.task_type:\n    worker_device = '/job:%s/task:%d' % (config.task_type, config.task_id)\n  else:\n    worker_device = '/job:worker'\n\n  if config.num_ps_replicas > 0:\n    from tensorflow.python.training import device_setter\n    return tf.compat.v1.train.replica_device_setter(\n        ps_tasks=config.num_ps_replicas,\n        worker_device=worker_device,\n        merge_devices=True,\n        ps_ops=list(device_setter.STANDARD_PS_OPS),\n        cluster=config.cluster_spec)\n  else:\n    return None\n\n\ndef _do_worker_feature_engineering(target, config: DistributedCpuTrainingConfig,\n                                   params: InstantiableParams, cluster: Dict,\n                                   task: Dict):\n  logging.info(\"do worker feature engineering. mode: %s.\", config.mode)\n  params.mode = config.mode\n  native_task = params.instantiate()\n  if not isinstance(native_task, NativeTask):\n    raise ValueError(\n        \"distributed train only support NativeTask. Got {}\".format(native_task))\n\n  session_config = cluster_manager.generate_session_config((cluster, task))\n  session_config.operation_timeout_in_ms = config.operation_timeout_in_ms\n  check_addrs = _get_blocked_addrs(cluster=cluster, ignored_jobs={'worker'})\n  alive_checker = net_utils.NodeAliveChecker(check_addrs, timeout=60)\n  if not alive_checker.all_nodes_alive():\n    raise NodeAliveCheckerError(\"{} is unreachable\".format(','.join(\n        alive_checker.get_dead_nodes())))\n  os.environ[\"TF_CONFIG\"] = json.dumps({\"cluster\": cluster, \"task\": task})\n\n  run_config = tf.estimator.RunConfig(\n      model_dir=config.model_dir,\n      session_config=session_config,\n      save_summary_steps=config.save_summary_steps * config.num_workers,\n      log_step_count_steps=config.log_step_count_steps * config.num_workers,\n      session_creation_timeout_secs=config.session_creation_timeout_secs)\n\n  device_fn = _get_replica_device_setter(run_config)\n\n  with tf.Graph().as_default() as g, g.device(device_fn):\n    dataset = native_task.input_fn(config.mode)\n    itr = tf.compat.v1.data.make_initializable_iterator(dataset)\n    nxt_elem = itr.get_next()\n\n    # TODO(ltli): 后续转 ExampleBatch 的逻辑转移到 C++，效率更高\n    fe_save_hook = feature_engineering_hooks.FeatureEngineeringSaveHook(\n        config, nxt_elem)\n    with tf.compat.v1.train.MonitoredTrainingSession(\n        target, hooks=[fe_save_hook], config=session_config) as sess:\n      sess.run(itr.initializer)\n      while not sess.should_stop():\n        sess.run(nxt_elem)\n\n  if \"TF_CONFIG\" in os.environ:\n    del os.environ[\"TF_CONFIG\"]\n\n  logging.info(\"finish worker feature engineering. mode: %s.\", config.mode)\n  return None\n\n\ndef make_config_backward_compatible(model_dir: str, config: CpuTrainingConfig):\n  # Will remove this compatible logic after 1/1/2023\n  if config.use_native_multi_hash_table is None:\n    monolith_ckpt = save_utils.get_monolith_checkpoint_state(model_dir)\n    if monolith_ckpt is not None and monolith_ckpt.builtin_hash_table_type in (\n        monolith_checkpoint_state_pb2.MonolithCheckpointState.UNKNOWN,\n        monolith_checkpoint_state_pb2.MonolithCheckpointState.CUCKOO_HASH_MAP):\n      config.use_native_multi_hash_table = False\n\n\ndef distributed_train(config: DistributedCpuTrainingConfig,\n                      discovery: ServiceDiscovery,\n                      params: InstantiableParams,\n                      sync_backend: SyncBackend = None,\n                      user_hooks = None):\n  \"\"\"Trains the server in a distributed fashion.\"\"\"\n  if config.index is None:\n    raise ValueError(\"Index can't be none.\")\n  if config.num_ps is None:\n    raise ValueError(\"Num ps can't be none.\")\n  if config.num_workers is None:\n    raise ValueError(\"Num workers can't be none.\")\n  if not config.server_type in [\"ps\", \"worker\"]:\n    raise ValueError(\"Unknown server type. type: {}\".format(config.server_type))\n  if not config.model_dir:\n    raise ValueError(\"model dir can't be empty.\")\n\n  if is_chief(config):\n    FLAGS.monolith_alert_proto = FLAGS.monolith_chief_alert_proto\n\n  make_config_backward_compatible(config.model_dir, config)\n\n  server_config = tf.compat.v1.ConfigProto(\n      intra_op_parallelism_threads=config.intra_op_parallelism_threads,\n      inter_op_parallelism_threads=config.inter_op_parallelism_threads)\n  if isinstance(discovery, (MLPServiceDiscovery, TfConfigServiceDiscovery)):\n    addr = discovery.addr\n    config.index = discovery.index\n    server = tf.distribute.Server({\"local\": [addr]}, config=server_config)\n  else:\n    assert isinstance(discovery, ServiceDiscovery)\n    ip = yarn_runtime.get_local_host()\n    server = tf.distribute.Server(\n        {\"local\": [net_utils.concat_ip_and_port(ip, 0)]}, config=server_config)\n    addr = urlparse(server.target).netloc\n\n  _prepare_server(server.target, config)\n  discovery.register(config.server_type, config.index, addr)\n  logging.info(\"Started %s %d at %s.\", config.server_type, config.index, addr)\n\n  estimator = None\n  metric_heart_beat_thread = None\n\n  if config.server_type == \"ps\":\n    if not config.model_name:\n      if isinstance(params, InstantiableParams):\n        default_name = f'di_name_{params.cls.__name__}'\n      else:\n        default_name = f'di_name_{params.__class__.__name__}'\n      config.model_name = params.metrics.deep_insight_name or default_name\n    with native_task_context.with_ctx(\n        make_native_task_context(config, sync_backend)):\n      _join_ps(server.target, config.index, sync_backend)\n  elif config.server_type == \"worker\":\n    num_retries, worker_failover_cnt = 0, 0\n    max_retries = config.max_retry_times or (\n        6 if config.partial_recovery and not config.enable_sync_training\n        else 0)\n    cluster, task = {}, {}\n\n    num_required_ps = config.num_ps + config.num_extra_ps\n\n    def _get_cluster_and_task():\n      cluster, task = cluster_manager.get_training_cluster(\n          discovery, addr, config.index, config.num_redundant_ps,\n          num_required_ps, config.num_workers, config.model_dir, config.uuid,\n          params.metrics.deep_insight_name, config.cluster_type)\n      filtered_cluster = copy.copy(cluster)\n      if config.submit_time_secs and config.index == 0 and params.metrics.deep_insight_name:\n        container_ready_elapsed_time = int(\n            time.time()) - config.submit_time_secs\n        logging.info(\n            \"Containers ready took {}s.\".format(container_ready_elapsed_time))\n        tags = {\n            \"model_name\": config.model_name or params.metrics.deep_insight_name,\n            \"cluster_type\": config.cluster_type\n        }\n        cli.get_cli(utils.get_metric_prefix()).emit_timer(\n            \"container_ready_elapsed_time.all\", container_ready_elapsed_time,\n            tags)\n        config.containers_ready_time_secs = int(time.time())\n      if config.num_extra_ps:\n        filtered_cluster = _run_ps_benchmark(config, config.num_ps,\n                                             filtered_cluster, task, \n                                             user_hooks)\n      return filtered_cluster, task\n\n    cluster, task = _get_cluster_and_task()\n    if is_chief(config):\n      metric_heart_beat_thread = _MetricsHeartBeatThread()\n      metric_heart_beat_thread.start()\n    captured_exception = None\n    start_ts = datetime.timestamp(datetime.now())\n    logging.info(\"Worker Start %s\", str(start_ts))\n    logging.info(\"only_feature_engineering: {}.\".format(\n        config.only_feature_engineering))\n    try:\n      while True:\n        try:\n          if config.only_feature_engineering:\n            estimator = _do_worker_feature_engineering(server.target, config,\n                                                       params, cluster, task)\n          else:\n            if config.enable_gpu_training:\n              device_utils.enable_gpu_training()\n              params.train.use_gpu_emb_table = False\n            estimator = _do_worker_train(config, params, cluster, task, user_hooks)\n          break\n        except (tf.errors.DeadlineExceededError, tf.errors.UnavailableError,\n                NodeAliveCheckerError) as e:\n          worker_failover_cnt += 1\n          tags = {\n              \"model_name\": config.model_name,\n              \"worker_index\": str(config.index)\n          }\n          cli.get_cli(utils.get_metric_prefix()).emit_timer(\n              \"worker_failover_cnt\",\n              f'worker_failover_cnt: {worker_failover_cnt}, msg: {e}', tags)\n          time.sleep(config.retry_wait_in_secs)\n          old_cluster = cluster\n          cluster, task = _get_cluster_and_task()\n          if cluster == old_cluster:\n            logging.info('Temporary error: %s. Retrying...', str(e))\n            continue\n          num_retries += 1\n          if num_retries <= max_retries:\n            logging.error(\n                'error is \"{}\", we try to the {}-th retry, sleep for {} seconds!'\n                .format(e, num_retries, config.retry_wait_in_secs))\n          else:\n            captured_exception = e\n            raise e\n        except Exception as e:\n          captured_exception = e\n          raise e\n    finally:\n      if is_chief(config):\n        try:\n          if metric_heart_beat_thread:\n            metric_heart_beat_thread.stop()\n          if config.num_redundant_ps or config.num_extra_ps:\n            num_required_ps += config.num_redundant_ps\n            # Query the total ps cluster for shutdown.\n            cluster, task = cluster_manager.get_training_cluster(\n                discovery, addr, config.index, config.num_redundant_ps,\n                num_required_ps, config.num_workers, config.model_dir,\n                config.uuid)\n          # In the realtime training, we want to keep ps alive so we can\n          # restart chief without side effect.\n          if not config.enable_realtime_training or config.force_shutdown_ps:\n            _shutdown_ps(server.target, cluster, task, num_required_ps)\n        finally:\n          if captured_exception is None:\n            yarn_runtime.maybe_finish_application()\n          else:\n            success = yarn_runtime.maybe_kill_application(\n                str(captured_exception))\n    end_ts = datetime.timestamp(datetime.now())\n    logging.info(\"Worker End %s, Cost: %s(s)\", str(end_ts),\n                 str(end_ts - start_ts))\n\n  logging.info(\"Finished %s %d.\", config.server_type, config.index)\n  return estimator\n\n\ndef distributed_sync_train(config: DistributedCpuTrainingConfig,\n                           params: InstantiableParams,\n                           sync_backend: SyncBackend = None,\n                           user_hooks = None):\n  \"\"\"\n  This is the entry point for synchronous distributed training.\n  This system allows the model to train in a half sync manner as well, when set\n  embedding_prefetch_capacity value > 0.\n\n  All the dense parameters are synced via allreduce and no asynchronicity is\n  allowed for dense paramters.\n\n  No Worker num is needed, the system derives the number of workers via MPI API.\n\n  Args:\n    config: the configs for monolith cpu training.\n    params: the parameters for the model and other modules.\n\n  \"\"\"\n\n  assert get_mpi_rank() == config.index, \\\n    \"Given RunConfig.index should be consistent with hvd.rank().\"\n\n  # To remove this contraint future\n  if config.enable_gpu_training:\n    device_utils.enable_gpu_training()\n    params.train.use_gpu_emb_table = True\n\n  task = params.instantiate()\n  if not isinstance(task, NativeTask):\n    raise ValueError(\n        \"distributed train only support NativeTask. Got {}\".format(task))\n  training = CpuTraining(config, task)\n  training = sync_training_hooks.EofAwareTask(training, config.use_dataservice)\n  session_config = tf.compat.v1.ConfigProto(allow_soft_placement=False,\n                                            log_device_placement=False)\n  # CPU Configs\n  session_config.intra_op_parallelism_threads = config.intra_op_parallelism_threads\n  session_config.inter_op_parallelism_threads = config.inter_op_parallelism_threads\n  # GPU Configs\n  update_session_config_for_gpu(session_config)\n  # By default the grappler (meta_optimizer) is enabled.\n  # session_config.graph_options.rewrite_options.disable_meta_optimizer = True\n  session_config.graph_options.rewrite_options.memory_optimization = 1\n  if os.environ.get('TF_XLA_FLAGS', None):\n    session_config.graph_options.optimizer_options.global_jit_level = 1\n\n  # We reduce the frequency of saving to HDFS summary, otherwise it slows down\n  # the training.\n  # TODO(zouxuan): always use the TF v2 summary with flush will fix this issue.\n\n  class Nop(object):\n\n    def nop(*args, **kwargs):\n      pass\n\n    def __getattr__(self, _):\n      return self.nop\n\n  # only rank 0 writes events\n  if config.index != 0:\n    SummaryWriterCache.get = lambda _: Nop()\n\n  run_config = tf.estimator.RunConfig(\n      model_dir=config.model_dir,\n      device_fn=device_utils.default_device_fn,\n      session_config=session_config,\n      save_summary_steps=None if config.index != 0 else int(\n          os.environ.get('MONOLITH_SAVE_SUMMARY_INTERVAL', config.save_summary_steps)),\n      log_step_count_steps=params.train.max_steps if config.index != 0 else int(\n          os.environ.get('MONOLITH_ROOT_LOG_INTERVAL', config.log_step_count_steps)))\n\n  estimator = tf.estimator.Estimator(training.create_model_fn(),\n                                     config=run_config)\n  run_hooks = get_sync_run_hooks(True)\n\n  if sync_backend is not None:\n    run_hooks.append(\n        sync_training_hooks.ParameterSyncHook(sync_backend, config.index))\n  run_hooks.append(sync_training_hooks.SyncTrainingInfoHook())\n\n  if user_hooks is not None:\n    run_hooks += user_hooks\n\n  estimator.train(training.create_input_fn(config.mode),\n                  hooks=run_hooks,\n                  max_steps=params.train.max_steps)\n\n  logging.info(\"Finished worker %d.\", config.index)\n  return estimator\n\n\ndef local_train_internal(params: InstantiableParams,\n                         conf: CpuTrainingConfig,\n                         model_dir: str,\n                         steps: int = 100,\n                         profiling: bool = False,\n                         user_hooks = None) -> tf.estimator.Estimator:\n  \"\"\"Do a local training. Especially useful in the local demo.\"\"\"\n  if tf.compat.v1.executing_eagerly():\n    raise EnvironmentError(\n        \"Local train is not supported in the eager mode. Please call `tf.compat.v1.disable_eager_execution()`\"\n    )\n\n  task = params.instantiate()\n\n  if conf.num_ps <= 0:\n    session_config = tf.compat.v1.ConfigProto()\n    training = CpuTraining(conf, task)\n    if \"TF_CONFIG\" in os.environ:\n      del os.environ[\"TF_CONFIG\"]\n  else:\n    training = CpuTraining(conf, task)\n    ps_servers = []\n    for _ in range(conf.num_ps):\n      ps_servers.append(tf.distribute.Server.create_local_server())\n    master = tf.distribute.Server.create_local_server()\n\n    def get_addr(server: tf.distribute.Server):\n      return server.target[len('grpc://'):]\n\n    cluster = {\n        \"chief\": [get_addr(master)],\n        \"ps\": [get_addr(server) for server in ps_servers],\n    }\n    os.environ[\"TF_CONFIG\"] = json.dumps({\n        \"cluster\": cluster,\n        \"task\": {\n            \"type\": \"chief\",\n            \"index\": 0\n        }\n    })\n\n    spec = tf.train.ClusterSpec(cluster)\n    session_config = tf.compat.v1.ConfigProto(\n        cluster_def=spec.as_cluster_def(),\n        allow_soft_placement=True,\n        share_cluster_devices_in_session=True)\n\n    session_config.experimental.share_session_state_in_clusterspec_propagation = True\n  # grappler doesn't really understand RaggedTensor.\n  session_config.graph_options.rewrite_options.disable_meta_optimizer = True\n\n  config = tf.estimator.RunConfig(model_dir=model_dir,\n                                  session_config=session_config,\n                                  save_summary_steps=conf.save_summary_steps,\n                                  log_step_count_steps=conf.log_step_count_steps)\n  estimator = tf.estimator.Estimator(training.create_model_fn(), config=config)\n  estimator.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN),\n                  hooks=user_hooks,\n                  steps=steps)\n\n  if conf.enable_resource_constrained_roughsort and conf.mode == tf.estimator.ModeKeys.TRAIN:\n    logging.info(f\"roughsort_items_use_parquet: {conf.roughsort_items_use_parquet}\")\n    if conf.roughsort_items_use_parquet:\n      items_path = os.path.join(conf.model_dir, \"candidate_items.pb\")\n      _convert_parquets_to_instance(conf.roughsort_candidate_items_path, items_path)\n    else:\n      items_path = conf.roughsort_candidate_items_path\n\n    params.mode = tf.estimator.ModeKeys.PREDICT\n    # params.p.only_save_item_cache_hashtable = True\n    task = params.instantiate()\n    training = CpuTraining(conf, task)\n    config = tf.estimator.RunConfig(model_dir=model_dir,\n                                    session_config=session_config,\n                                    save_summary_steps=conf.save_summary_steps,\n                                    log_step_count_steps=conf.log_step_count_steps)\n    estimator = tf.estimator.Estimator(training.create_model_fn(), config=config)\n    estimator.train(training._task.create_item_input_fn(\n        items_path), steps=steps)\n\n  if \"TF_CONFIG\" in os.environ:\n    del os.environ[\"TF_CONFIG\"]\n\n  return estimator\n\n\ndef local_feature_engineering_internal(\n    params: InstantiableParams,\n    conf: CpuTrainingConfig,\n    model_dir: str,\n    profiling: bool = False) -> tf.estimator.Estimator:\n  \"\"\"Do a local feature engineer. Especially useful in the local demo.\"\"\"\n  if tf.compat.v1.executing_eagerly():\n    raise EnvironmentError(\n        \"Local train is not supported in the eager mode. Please call `tf.compat.v1.disable_eager_execution()`\"\n    )\n\n  task = params.instantiate()\n\n  if conf.num_ps <= 0:\n    session_config = tf.compat.v1.ConfigProto()\n    if \"TF_CONFIG\" in os.environ:\n      del os.environ[\"TF_CONFIG\"]\n  else:\n    ps_servers = []\n    for _ in range(conf.num_ps):\n      ps_servers.append(tf.distribute.Server.create_local_server())\n    master = tf.distribute.Server.create_local_server()\n\n    def get_addr(server: tf.distribute.Server):\n      return server.target[len('grpc://'):]\n\n    cluster = {\n        \"chief\": [get_addr(master)],\n        \"ps\": [get_addr(server) for server in ps_servers],\n    }\n    os.environ[\"TF_CONFIG\"] = json.dumps({\n        \"cluster\": cluster,\n        \"task\": {\n            \"type\": \"chief\",\n            \"index\": 0\n        }\n    })\n\n    spec = tf.train.ClusterSpec(cluster)\n    session_config = tf.compat.v1.ConfigProto(\n        cluster_def=spec.as_cluster_def(),\n        allow_soft_placement=True,\n        share_cluster_devices_in_session=True)\n\n    session_config.experimental.share_session_state_in_clusterspec_propagation = True\n  # grappler doesn't really understand RaggedTensor.\n  session_config.graph_options.rewrite_options.disable_meta_optimizer = True\n\n  if not model_dir:\n    model_dir = \"/tmp/{}/{}\".format(getpass.getuser(), params.name)\n  run_config = tf.estimator.RunConfig(model_dir=model_dir,\n                                      session_config=session_config,\n                                      save_summary_steps=conf.save_summary_steps,\n                                      log_step_count_steps=conf.log_step_count_steps)\n\n  device_fn = _get_replica_device_setter(run_config)\n\n  if profiling:\n    tf.profiler.experimental.start(model_dir)\n\n  with tf.Graph().as_default() as g, g.device(device_fn):\n    dataset = task.input_fn(conf.mode)\n    itr = tf.compat.v1.data.make_initializable_iterator(dataset)\n    nxt_elem = itr.get_next()\n\n    fe_save_hook = feature_engineering_hooks.FeatureEngineeringSaveHook(\n        conf, nxt_elem)\n    with tf.compat.v1.train.MonitoredTrainingSession(\n        master.target, hooks=[fe_save_hook], config=session_config) as sess:\n      sess.run(itr.initializer)\n      while not sess.should_stop():\n        sess.run(nxt_elem)\n\n  if profiling:\n    tf.profiler.experimental.stop()\n\n  if \"TF_CONFIG\" in os.environ:\n    del os.environ[\"TF_CONFIG\"]\n\n  return None\n\n\ndef local_train(params: InstantiableParams,\n                num_ps=0,\n                model_dir: str = None,\n                steps=100,\n                save_checkpoints_steps=50,\n                profiling=False,\n                enable_embedding_prefetch: bool = True,\n                enable_embedding_postpush: bool = True,\n                remove_model_dir_if_exists: bool = True,\n                only_feature_engineering: bool = False):\n  embedding_prefetch_capacity = 1 if enable_embedding_prefetch else 0\n  conf = CpuTrainingConfig(\n      model_name=params.name,\n      num_ps=num_ps,\n      embedding_prefetch_capacity=embedding_prefetch_capacity,\n      enable_embedding_postpush=enable_embedding_postpush,\n      save_checkpoints_steps=save_checkpoints_steps)\n  if not model_dir:\n    model_dir = \"/tmp/{}/{}\".format(getpass.getuser(), params.name)\n  if remove_model_dir_if_exists:\n    try:\n      tf.io.gfile.rmtree(model_dir)\n    except tf.errors.NotFoundError:\n      pass\n  make_config_backward_compatible(model_dir, conf)\n  if only_feature_engineering:\n    return local_feature_engineering_internal(params, conf, model_dir,\n                                              profiling)\n  else:\n    return local_train_internal(params, conf, model_dir, steps, profiling)\n"
  },
  {
    "path": "monolith/native_training/cpu_training_distributed_test_binary.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport io\nimport os\nimport time\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport tensorflow as tf\n\nfrom monolith.native_training import cluster_manager\nfrom monolith.native_training import cpu_training\nfrom monolith.native_training import feature\nfrom monolith.native_training import native_task\nfrom monolith.native_training import service_discovery\nfrom monolith.native_training import utils\n\nflags.DEFINE_integer(\"test_case\", None, \"The number of test case.\")\nflags.DEFINE_string(\"test_dir\", None, \"The test folder.\")\n\nflags.DEFINE_string(\"server_type\", None,\n                    \"The type of this process. Can be 'ps' or 'worker'\")\nflags.DEFINE_integer(\"index\", None,\n                     \"The index of the current process in servers.\")\nflags.DEFINE_integer(\"num_ps\", None, \"The number of ps\")\nflags.DEFINE_integer(\"num_workers\", None, \"The number of worker\")\nflags.DEFINE_integer(\"num_extra_ps\", 0, \"The number of extra ps.\")\nflags.DEFINE_integer(\"num_redundant_ps\", 0, \"The number of redundant ps.\")\nflags.DEFINE_string(\"uuid\", \"\", \"uuid\")\nflags.DEFINE_bool(\"use_native_multi_hash_table\", False,\n                  \"Use native MultiHashTable.\")\n\nFLAGS = flags.FLAGS\n\n\ndef _sleep_short():\n  time.sleep(0.1)\n\n\n# In the test, we want query as fast as possible.\ncluster_manager._cluster_query_failure_handler = _sleep_short\ncpu_training._EXTRA_PS_BENCHMARK_SECS = 0.5\n\n\nclass SyncHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, num_workers, index):\n    self._num_workers = num_workers\n    self._index = index\n    self._var = None\n    self._assign_op = None\n\n  def begin(self):\n    collections = [tf.compat.v1.GraphKeys.LOCAL_VARIABLES\n                  ] if self._index == 0 else [tf.compat.v1.GraphKeys.VARIABLES]\n    self._var = tf.compat.v1.get_variable(\n        \"TEST_SYNC_VAR\",\n        initializer=[False] * self._num_workers,\n        dtype=tf.bool,\n        trainable=False,\n        collections=collections,\n    )\n    self._assign_op = self._var[self._index].assign(True)\n\n  def after_create_session(self, session, coord):\n    session.run(self._assign_op)\n    if self._index == 0:\n      # To prevent chief finishing before other workers start\n      while True:\n        if sum(session.run(self._var)) == self._num_workers:\n          break\n        time.sleep(0.5)\n\n\nclass FeatureTask(native_task.NativeTask):\n  \"\"\"A test task that will collect some information in model_fn.\"\"\"\n\n  @classmethod\n  def params(cls):\n    p = super().params()\n    p.define(\"training_hooks\", [], \"Training hooks\")\n    return p\n\n  def create_input_fn(self, mode):\n    del mode\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors(\n          {\"feature\": tf.ragged.constant([[0, 1]], dtype=tf.int64)})\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(mode, features, config):\n      slot = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(name=\"slot\"))\n      s = slot.add_feature_slice(5)\n      fc = feature.FeatureColumnV1(slot, \"feature\")\n      embedding = fc.embedding_lookup(s)\n      if mode == tf.estimator.ModeKeys.PREDICT:\n        return tf.estimator.EstimatorSpec(mode, predictions=tf.constant(0))\n      all_embeddings = [fc.get_all_embeddings_concat()]\n      grads = tf.gradients(-embedding, all_embeddings)\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      train_op = tf.group(\n          global_step.assign_add(1),\n          self._ctx.feature_factory.apply_gradients(zip(grads, all_embeddings)),\n          features[\"feature\"])\n      return tf.estimator.EstimatorSpec(\n          mode,\n          train_op=train_op,\n          loss=tf.constant(0.0),\n          training_hooks=[SyncHook(FLAGS.num_workers, FLAGS.index)] +\n          self.p.training_hooks)\n\n    return model_fn\n\n\nclass HostServiceDiscovery(service_discovery.ServiceDiscovery):\n\n  def __init__(self, base_path: str):\n    self._base_path = base_path\n\n  def register(self, name: str, index: int, addr: str):\n    os.makedirs(self._named_path(name), exist_ok=True)\n    with io.open(os.path.join(self._named_path(name), str(index)),\n                 \"w\") as writer:\n      writer.write(addr)\n\n  def deregister(self, name: str, index: int, addr: str):\n    pass\n\n  def query(self, name: str):\n    basepath = self._named_path(name)\n    if not os.path.exists(basepath):\n      return {}\n    indexes = os.listdir(basepath)\n    result = {}\n    for index in indexes:\n      f = os.path.join(basepath, index)\n      with io.open(f, \"r\") as reader:\n        addr = reader.read()\n      result[int(index)] = addr\n    return result\n\n  def _named_path(self, name: str):\n    return os.path.join(self._base_path, name)\n\n\ndef test_run(params):\n  model_dir = os.path.join(FLAGS.test_dir, f\"{FLAGS.uuid}/model\")\n  config = cpu_training.DistributedCpuTrainingConfig(\n      server_type=FLAGS.server_type,\n      index=FLAGS.index,\n      num_ps=FLAGS.num_ps,\n      num_extra_ps=FLAGS.num_extra_ps,\n      num_redundant_ps=FLAGS.num_redundant_ps,\n      num_workers=FLAGS.num_workers,\n      model_dir=model_dir,\n      uuid=FLAGS.uuid,\n      enable_model_ckpt_info=True,\n      use_native_multi_hash_table=FLAGS.use_native_multi_hash_table)\n  # It is not easy to prevent worker doing things too fast\n  params.train.max_pending_seconds_for_barrier = 2\n  discovery = HostServiceDiscovery(\n      os.path.join(FLAGS.test_dir, f\"{FLAGS.uuid}/service_discovery\"))\n  cpu_training.distributed_train(config, discovery, params)\n\n\ndef test0():\n  params = FeatureTask.params()\n  params.name = \"test_task\"\n  test_run(params)\n\n\ndef test1():\n\n  def no_shutdown(*args, **kwargs):\n    while True:\n      time.sleep(1)\n\n  cpu_training._shutdown_ps = no_shutdown\n  test0()\n\n\nclass RaiseErrorHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, first):\n    self._first = first\n\n  def before_run(self, run_context):\n    if self._first:\n      self._first = False\n      raise tf.errors.DeadlineExceededError(None, None,\n                                            \"test ddl exceeded error\")\n\n\ndef test2():\n  params = FeatureTask.params()\n  params.name = \"test_task\"\n  first = True\n  params.training_hooks = [RaiseErrorHook(first)]\n  test_run(params)\n\n\ndef main(_):\n  test_cases = [test0, test1, test2]\n  test_cases[FLAGS.test_case]()\n\n\nif __name__ == \"__main__\":\n  app.run(main)\n"
  },
  {
    "path": "monolith/native_training/cpu_training_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom collections import defaultdict\nimport copy\nimport os\nimport subprocess\nimport threading\nimport time\nfrom google.protobuf import text_format\nfrom typing import Dict, List\nfrom unittest import mock\n\nfrom absl import app\nfrom absl import flags\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.python.lib.io import file_io\n\nfrom monolith.native_training import cpu_training\nfrom monolith.native_training import entry\nfrom monolith.native_training import feature\nfrom monolith.native_training import utils\nfrom monolith.native_training.debugging import debugging_server\nfrom monolith.native_training.model_export import saved_model_exporters\nfrom monolith.native_training.model_export.export_context import ExportMode\nfrom monolith.native_training.native_task import NativeTask\nfrom monolith.native_training.proto import debugging_info_pb2\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\nfrom monolith.native_training.service_discovery import ServiceDiscovery\n\nFLAGS = flags.FLAGS\n# TODO(leqi.zou): Finally remove this or rework with a better gflag util.\nflags.DEFINE_bool(\"use_native_multi_hash_table\", False,\n                  \"The test flag to control if use multi hash table.\")\n\n\ndef inc_global_step_op() -> tf.Operation:\n  global_step = tf.compat.v1.train.get_or_create_global_step()\n  global_step = tf.compat.v1.assign_add(global_step, 1)\n  return tf.group(global_step)\n\n\nclass FeatureTask(NativeTask):\n  \"\"\"A test task that will collect some information in model_fn.\"\"\"\n\n  def create_input_fn(self, mode):\n    del mode\n\n    def input_fn():\n      tensor = tf.ragged.constant([[0, 0]], dtype=tf.int64)\n      return tf.data.Dataset.from_tensors({\"feature\": tensor})\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(mode, features, config):\n      slot = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(name=\"slot\"))\n      s = slot.add_feature_slice(5)\n      fc = feature.FeatureColumnV1(slot, \"feature\")\n      embedding = fc.embedding_lookup(s)\n      all_embeddings = [fc.get_all_embeddings_concat()]\n      if mode == tf.estimator.ModeKeys.PREDICT:\n        return tf.estimator.EstimatorSpec(\n            mode, predictions=tf.math.reduce_sum(embedding))\n      grads = tf.gradients(-embedding, all_embeddings)\n      train_op = tf.group(\n          inc_global_step_op(),\n          self._ctx.feature_factory.apply_gradients(zip(grads, all_embeddings)),\n          features[\"feature\"])\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=train_op,\n                                        loss=tf.constant(0.0),\n                                        predictions=tf.constant(0))\n\n    return model_fn\n\n  def create_serving_input_receiver_fn(self):\n\n    def serving_input_receiver_fn():\n      return tf.estimator.export.ServingInputReceiver(\n          {\"feature\": tf.ragged.constant([[0, 0]], dtype=tf.int64)},\n          tf.compat.v1.placeholder(tf.string))\n\n    return serving_input_receiver_fn\n\n\nclass FloatFeatureTask(NativeTask):\n  \"\"\"A test task that will use float feature in model_fn.\"\"\"\n\n  def create_input_fn(self, mode):\n    del mode\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors({\n          \"ragged_feature\": tf.ragged.constant([[0, 0]], dtype=np.int64),\n          \"float_feature\": tf.constant([[1.]], dtype=tf.float32)\n      })\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(features, mode, **kwargs):\n      slot = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(name=\"slot\"))\n      s = slot.add_feature_slice(5)\n      fc = feature.FeatureColumnV1(slot, \"ragged_feature\")\n      embedding = fc.embedding_lookup(s)\n      float_feature = features[\"float_feature\"]\n      predictions = tf.reduce_sum(float_feature, axis=-1)\n      all_embeddings = [fc.get_all_embeddings_concat()]\n      grads = tf.gradients(-embedding, all_embeddings)\n      train_op = tf.group(\n          inc_global_step_op(),\n          self._ctx.feature_factory.apply_gradients(zip(grads, all_embeddings)))\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=train_op,\n                                        loss=tf.constant(0.0),\n                                        predictions=predictions)\n\n    return model_fn\n\n\nclass SequenceFeatureTask(NativeTask):\n  \"\"\"A test task that will use float feature in model_fn.\"\"\"\n\n  def create_input_fn(self, mode):\n    del mode\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors({\n          \"sequence_feature\":\n              tf.ragged.constant([[1, 2], [], [3, 4, 5]], dtype=np.int64),\n      })\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(features, mode, **kwargs):\n      slot = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(name=\"slot\"))\n      s = slot.add_feature_slice(5)\n      fc = feature.FeatureColumnV1(slot,\n                                   \"sequence_feature\",\n                                   combiner=feature.FeatureColumnV1.first_n(2))\n      embedding = fc.embedding_lookup(s)\n      sequence_feature = features[\"sequence_feature\"]\n      predictions = tf.reduce_sum(sequence_feature, axis=-1)\n      all_embeddings = [fc.get_all_embeddings_concat()]\n      grads = tf.gradients(-embedding, all_embeddings)\n      train_op = tf.group(\n          inc_global_step_op(),\n          self._ctx.feature_factory.apply_gradients(zip(grads, all_embeddings)))\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=train_op,\n                                        loss=tf.constant(0.0),\n                                        predictions=predictions)\n\n    return model_fn\n\n\nclass FeatureWithSlotOccurrenceThresholdTask(NativeTask):\n  \"\"\"A test task that will collect some information in model_fn.\"\"\"\n\n  def create_input_fn(self, mode):\n    del mode\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors(\n          {\"feature\": tf.ragged.constant([[0, 0]], dtype=np.int64)})\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(mode, **kwargs):\n\n      slot = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(name=\"slot\",\n                                    slot_id=2021,\n                                    occurrence_threshold=3))\n      s = slot.add_feature_slice(5)\n      fc = feature.FeatureColumnV1(slot, \"feature\")\n      embedding = fc.embedding_lookup(s)\n      all_embeddings = [fc.get_all_embeddings_concat()]\n      grads = tf.gradients(-embedding, all_embeddings)\n      train_op = tf.group(\n          inc_global_step_op(),\n          self._ctx.feature_factory.apply_gradients(zip(grads, all_embeddings)))\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=train_op,\n                                        loss=tf.constant(0.0),\n                                        predictions=tf.constant(0))\n\n    return model_fn\n\n\nclass FeatureWithExpireTimeTask(NativeTask):\n  \"\"\"A test task that will collect some information in model_fn.\"\"\"\n\n  def create_input_fn(self, mode):\n    del mode\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors({\n          \"feature_1\":\n              tf.ragged.constant([[1 << 48, (1 << 48) + 1]], dtype=np.int64),\n          \"feature_2\":\n              tf.ragged.constant([[2 << 48, (2 << 48) + 1]], dtype=np.int64),\n          \"req_time\":\n              tf.constant([[100]], dtype=tf.int64),\n      })\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(mode, features, **kwargs):\n      slot_1 = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(\n              name=\"slot_1\",\n              slot_id=1,\n              expire_time=0,\n              default_vec_initializer=entry.ZerosInitializer()))\n      s_1 = slot_1.add_feature_slice(5)\n      fc_1 = feature.FeatureColumnV1(slot_1, \"feature_1\")\n      embedding_1 = fc_1.embedding_lookup(s_1)\n\n      slot_2 = self.ctx.feature_factory.create_feature_slot(\n          feature.FeatureSlotConfig(\n              name=\"slot_2\",\n              slot_id=2,\n              expire_time=1,\n              default_vec_initializer=entry.ZerosInitializer()))\n      s_2 = slot_2.add_feature_slice(5)\n      fc_2 = feature.FeatureColumnV1(slot_2, \"feature_2\")\n      embedding_2 = fc_2.embedding_lookup(s_2)\n\n      predictions = tf.concat([embedding_1, embedding_2], axis=0)\n      all_embeddings = [\n          fc_1.get_all_embeddings_concat(),\n          fc_2.get_all_embeddings_concat(),\n      ]\n      grads = tf.gradients([embedding_1, embedding_2], all_embeddings)\n\n      train_op = tf.group(\n          inc_global_step_op(),\n          self._ctx.feature_factory.apply_gradients(zip(grads, all_embeddings)))\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=train_op,\n                                        loss=tf.constant(0.0),\n                                        predictions=predictions)\n\n    return model_fn\n\n\nclass NonFeatureTask(NativeTask):\n\n  def create_input_fn(self, mode):\n    del mode\n\n    def input_fn():\n      return tf.data.Dataset.from_tensors([1])\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(features, mode, config):\n      return tf.estimator.EstimatorSpec(mode,\n                                        train_op=tf.group(\n                                            inc_global_step_op(), features),\n                                        loss=tf.constant(0.0),\n                                        predictions=tf.constant(0))\n\n    return model_fn\n\n\nclass CpuTrainTest(tf.test.TestCase):\n\n  def test_cpu_training_feature(self):\n    p = FeatureTask.params()\n    p.name = \"feature_task\"\n    task = FeatureTask(p)\n    training = cpu_training.CpuTraining(cpu_training.CpuTrainingConfig(), task)\n    est = tf.estimator.Estimator(\n        training.create_model_fn(),\n        os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_cpu_training_feature\"))\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n\n  def test_with_misc_features(self):\n    p = FeatureTask.params()\n    p.name = \"misc_features\"\n    task = FeatureTask(p)\n    training = cpu_training.CpuTraining(\n        cpu_training.CpuTrainingConfig(feature_eviction_on_save=True), task)\n    est = tf.estimator.Estimator(\n        training.create_model_fn(),\n        os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_with_misc_features\"))\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n\n  def test_with_export_when_saving(self):\n    p = FeatureTask.params()\n    p.serving.export_when_saving = True\n    task = FeatureTask(p)\n    training = cpu_training.CpuTraining(cpu_training.CpuTrainingConfig(), task)\n    est = tf.estimator.Estimator(\n        training.create_model_fn(),\n        os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_with_export_when_saving\"))\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n\n  def test_dense_only_export(self):\n    p = FeatureTask.params()\n    p.serving.export_when_saving = True\n    p.serving.export_mode = ExportMode.DISTRIBUTED\n    task = FeatureTask(p)\n    training = cpu_training.CpuTraining(\n        cpu_training.CpuTrainingConfig(dense_only_save_checkpoints_steps=10),\n        task)\n    est = tf.estimator.Estimator(\n        training.create_model_fn(),\n        os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_dense_only_export\"))\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n\n  def test_with_prefetch_postpush(self):\n    p = FeatureTask.params()\n    p.name = \"feature_task\"\n    task = FeatureTask(p)\n    training = cpu_training.CpuTraining(\n        cpu_training.CpuTrainingConfig(enable_variable_prefetch=True,\n                                       enable_variable_postpush=True,\n                                       enable_embedding_postpush=True,\n                                       embedding_prefetch_capacity=1), task)\n    est = tf.estimator.Estimator(\n        training.create_model_fn(),\n        os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_with_prefetch_postpush\"))\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n\n  def test_cpu_training_float_feature(self):\n    p = FloatFeatureTask.params()\n    p.name = \"float_feature_task\"\n    task = FloatFeatureTask(p)\n    training = cpu_training.CpuTraining(cpu_training.CpuTrainingConfig(), task)\n    est = tf.estimator.Estimator(\n        training.create_model_fn(),\n        os.path.join(os.environ[\"TEST_TMPDIR\"],\n                     \"test_cpu_training_float_feature\"))\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n\n  def test_cpu_training_sequence_feature(self):\n    p = SequenceFeatureTask.params()\n    p.name = \"sequence_feature_task\"\n    task = SequenceFeatureTask(p)\n    training = cpu_training.CpuTraining(cpu_training.CpuTrainingConfig(), task)\n    est = tf.estimator.Estimator(\n        training.create_model_fn(),\n        os.path.join(os.environ[\"TEST_TMPDIR\"],\n                     \"test_cpu_training_sequence_feature\"))\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n\n  def test_cpu_training_with_slot_occurrence_threshold(self):\n    p = FeatureWithSlotOccurrenceThresholdTask.params()\n    p.name = \"feature_with_slot_occurrence_task\"\n    task = FeatureWithSlotOccurrenceThresholdTask(p)\n    training = cpu_training.CpuTraining(cpu_training.CpuTrainingConfig(), task)\n    est = tf.estimator.Estimator(\n        training.create_model_fn(),\n        os.path.join(os.environ[\"TEST_TMPDIR\"],\n                     \"test_cpu_training_with_slot_occurrence_threshold\"))\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n    slot_to_occurrence_threshold = training._slot_to_occurrence_threshold\n    self.assertEqual(len(slot_to_occurrence_threshold), 1)\n    self.assertTrue(2021 in slot_to_occurrence_threshold)\n    self.assertEqual(slot_to_occurrence_threshold[2021], 3)\n\n  def test_cpu_training_with_expire_time(self):\n    p = FeatureWithExpireTimeTask.params()\n    p.name = \"feature_with_expire_time_task\"\n    task = FeatureWithExpireTimeTask(p)\n    training = cpu_training.CpuTraining(cpu_training.CpuTrainingConfig(), task)\n    base_name = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                             \"test_cpu_training_with_expire_time\")\n    # train\n    est = tf.estimator.Estimator(training.create_model_fn(), base_name)\n    est = est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n    slot_to_expire_time = training._slot_to_expire_time\n    self.assertEqual(len(slot_to_expire_time), 2)\n    self.assertTrue(1 in slot_to_expire_time)\n    self.assertTrue(2 in slot_to_expire_time)\n    self.assertEqual(slot_to_expire_time[1], 0)\n    self.assertEqual(slot_to_expire_time[2], 1)\n\n    #predict\n    result = est.predict(training.create_input_fn(\n        tf.estimator.ModeKeys.PREDICT))\n\n    result = list(result)\n    expected = [[0, 0, 0, 0, 0],\n                [-0.001414, -0.001414, -0.001414, -0.001414, -0.001414]]\n    self.assertAllClose(result, expected)\n\n  def test_cpu_training_non_feature(self):\n    p = NonFeatureTask.params()\n    p.name = \"non_feature_task\"\n    task = NonFeatureTask(p)\n    training = cpu_training.CpuTraining(cpu_training.CpuTrainingConfig(), task)\n    est = tf.estimator.Estimator(\n        training.create_model_fn(),\n        os.path.join(os.environ[\"TEST_TMPDIR\"],\n                     \"test_cpu_training_non_feature\"))\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n\n  def test_gpu_export(self):\n    p = FeatureTask.params()\n    p.name = \"gpu_export\"\n    task = FeatureTask(p)\n    training = cpu_training.CpuTraining(cpu_training.CpuTrainingConfig(), task)\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_gpu_export\")\n    est = tf.estimator.Estimator(training.create_model_fn(), model_dir)\n    est.train(training.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n    export_dir_base = os.path.join(model_dir, \"saved_models\")\n    exporter = saved_model_exporters.DistributedExporter(\n        training.create_model_fn(),\n        model_dir,\n        export_dir_base,\n        with_remote_gpu=True)\n    exporter.export_saved_model(training.create_serving_input_receiver_fn())\n\n\n_DISTRIBUTED_TRAIN_BINARY = \"monolith/native_training/cpu_training_distributed_test_binary\"\n\n\nclass DistributedTrainTest(tf.test.TestCase):\n\n  def _run_process(self, args_tmpl: List, num_ps: int, num_workers: int):\n    processes = []\n    for i in range(num_ps):\n      args = copy.copy(args_tmpl)\n      args.append(\"--server_type=ps\")\n      args.append(\"--index={}\".format(i))\n\n      process = subprocess.Popen(args)\n      processes.append(process)\n\n    for i in range(num_workers):\n      args = copy.copy(args_tmpl)\n      args.append(\"--server_type=worker\")\n      args.append(\"--index={}\".format(i))\n      process = subprocess.Popen(args)\n      processes.append(process)\n      if i == 0:\n        # this is best effort waiting, otherwise test may take 30 secs to finish.\n        # The goal here is to wait for chief to initialize global variables.\n        time.sleep(1)\n    processes.reverse()\n    return processes\n\n  def _run_test(self, args_tmpl: List, num_ps: int, num_workers: int):\n    processes = self._run_process(args_tmpl, num_ps, num_workers)\n    print(\" \".join(args_tmpl), num_ps, num_workers)\n    for process in processes:\n      # We give 70 secs to timeout because of 30 secs querying interval.\n      self.assertEqual(process.wait(timeout=150), 0)\n\n  def _test_dir(self):\n    return os.path.join(os.environ[\"TEST_TMPDIR\"], \"DistributedTrainTest\",\n                        self._testMethodName)\n\n  def _test_args(self, num_ps, num_workers, case=0):\n    args = [\n        _DISTRIBUTED_TRAIN_BINARY, \"--test_case={}\".format(case),\n        \"--test_dir={}\".format(self._test_dir()), \"--num_ps={}\".format(num_ps),\n        \"--num_workers={}\".format(num_workers),\n        \"--uuid={}\".format(self._testMethodName),\n        f\"--use_native_multi_hash_table={True if FLAGS.use_native_multi_hash_table else False}\"\n    ]\n    return args\n\n  # TODO(leqi.zou): Currently, this test mocks too much, should find a way to elegantly solve\n  # the shutdown problem both in test and training\n  # This test may takes 30 secs to be finished because variable initialization problem.\n  def test0_basic(self):\n    num_ps = 4\n    # We have 2 workers and 1 chief\n    num_workers = 3\n    args_tmpl = self._test_args(num_ps, num_workers)\n    self._run_test(args_tmpl, num_ps, num_workers)\n\n  def test0_with_extra_ps(self):\n    num_ps = 2\n    num_workers = 1\n    num_extra_ps = 2\n    args_tmpl = self._test_args(num_ps, num_workers)\n    args_tmpl.append(\"--num_extra_ps={}\".format(num_extra_ps))\n    self._run_test(args_tmpl, num_ps + num_extra_ps, num_workers)\n\n  def test0_with_redundant_ps(self):\n    num_ps = 4\n    num_workers = 2\n    num_redundant_ps = 2\n    args_tmpl = self._test_args(num_ps, num_workers)\n    args_tmpl.append(\"--num_redundant_ps={}\".format(num_redundant_ps))\n    self._run_test(args_tmpl, num_ps + num_redundant_ps, num_workers)\n\n  def test1_with_debugging_server(self):\n    if FLAGS.use_native_multi_hash_table:\n      # Debugging server doesnt support multi hash table.\n      return\n    num_ps = 2\n    num_workers = 1\n    args_tmpl = self._test_args(num_ps, num_workers, case=1)\n    processes = self._run_process(args_tmpl, num_ps, num_workers)\n\n    model_dir = os.path.join(self._test_dir(),\n                             \"test1_with_debugging_server/model\")\n    while True:\n      ckpt_state = tf.train.get_checkpoint_state(model_dir)\n      if ckpt_state:\n        break\n      time.sleep(1)\n\n    debugging_info_str = file_io.read_file_to_string(\n        utils.get_debugging_info_file_name(model_dir), binary_mode=True)\n    debugging_info = debugging_info_pb2.DebuggingInfo()\n    debugging_info.ParseFromString(debugging_info_str)\n    self.assertEqual(debugging_info.num_workers, num_workers)\n    self.assertLen(debugging_info.cluster.ps_addrs, num_ps)\n    self.assertLen(debugging_info.feature_name_configs, 1)\n    self.assertEqual(debugging_info.feature_name_configs[0].feature_name,\n                     \"feature\")\n\n    worker = debugging_server.DebuggingWorker(model_dir)\n    self.assertEqual(worker.fetch_variables([\"global_step:0\", \"test\"]),\n                     {'global_step:0': '1'})\n    fids = [\"0\", \"1\", \"2\", \"0\"]\n    result = worker.fetch_features([\"feature\"] * 3 + [\"test\"], fids)\n    for idx in range(2):\n      fid = fids[idx]\n      entry_dump = embedding_hash_table_pb2.EntryDump()\n      text_format.Parse(result[\"feature\"][fid], entry_dump)\n      self.assertLen(entry_dump.num, 5)\n    self.assertNotIn(\"2\", result[\"feature\"])\n    self.assertNotIn(\"test\", result)\n\n    for process in processes:\n      process.kill()\n\n  def test2_temporary_error(self):\n    num_ps = 1\n    num_workers = 1\n    args_tmpl = self._test_args(num_ps, num_workers, case=2)\n    self._run_test(args_tmpl, num_ps, num_workers)\n\n\nclass LocalTrainTest(tf.test.TestCase):\n\n  def testBasic(self):\n    print(tf.compat.v1.get_default_graph().as_graph_def())\n    p = FeatureTask.params()\n    p.name = \"feature_task\"\n    p.train.max_steps = 1\n    cpu_training.local_train(p,\n                             model_dir=os.path.join(os.environ[\"TEST_TMPDIR\"],\n                                                    \"local_train_basic\"),\n                             profiling=False)\n\n  def testWithPs(self):\n    print(tf.compat.v1.get_default_graph().as_graph_def())\n    p = FeatureTask.params()\n    p.name = \"feature_task\"\n    p.train.max_steps = 1\n    cpu_training.local_train(p,\n                             num_ps=2,\n                             model_dir=os.path.join(os.environ[\"TEST_TMPDIR\"],\n                                                    \"local_train_with_ps\"),\n                             profiling=False)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  app.run(tf.test.main)\n"
  },
  {
    "path": "monolith/native_training/data/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_cc_binary\", \"tf_cc_test\", \"tf_custom_op_library\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"cc_proto_library\", \"py_proto_library\")\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_test\")\nload(\"@pip_deps//:requirements.bzl\", \"requirement\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_proto_library(\n    name = \"data_op_config_cc_proto\",\n    srcs = [\"data_op_config.proto\"],\n)\n\npy_proto_library(\n    name = \"data_op_config_py_proto\",\n    srcs = [\"data_op_config.proto\"],\n    srcs_version = \"PY2AND3\",\n)\n\ncc_library(\n    name = \"pb_data_internal_lib\",\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"pb_data_lib\",\n    srcs = [\n        \"kernels/add_action_kernel.cc\",\n        \"kernels/add_label_kernel.cc\",\n        \"kernels/cache_one_dataset_kernel.cc\",\n        \"kernels/cache_one_dataset_kernel.h\",\n        \"kernels/df_resource_kernel.cc\",\n        \"kernels/df_resource_kernel.h\",\n        \"kernels/dynamic_match_file_dataset_kernel.cc\",\n        \"kernels/extract_fid_kernel.cc\",\n        \"kernels/feature_hash.cc\",\n        \"kernels/feature_name_mapper_tf_bridge.cc\",\n        \"kernels/feature_name_mapper_tf_bridge.h\",\n        \"kernels/fill_multi_rank_output_kernel.cc\",\n        \"kernels/filter_by_label_kernel.cc\",\n        \"kernels/instance_reweight_dataset_kernel.cc\",\n        \"kernels/instance_reweight_dataset_kernel.h\",\n        \"kernels/item_pool_kernels.cc\",\n        \"kernels/item_pool_kernels.h\",\n        \"kernels/kafka_kernels.cc\",\n        \"kernels/label_normalization_kernel.cc\",\n        \"kernels/label_upper_bound_kernel.cc\",\n        \"kernels/map_id_kernels.cc\",\n        \"kernels/merge_flow_dataset_kernel.cc\",\n        \"kernels/multi_label_gen_kernel.cc\",\n        \"kernels/negative_gen_dataset_kernel.cc\",\n        \"kernels/negative_gen_dataset_kernel.h\",\n        \"kernels/parquet_dataset_kernel.cc\",\n        \"kernels/parse_example_lib.cc\",\n        \"kernels/parse_example_lib.h\",\n        \"kernels/parse_input_data_kernel.cc\",\n        \"kernels/parse_sparse_feature.cc\",\n        \"kernels/parse_sparse_feature.h\",\n        \"kernels/pb_dataset_kernel.cc\",\n        \"kernels/ragged_feature_kernel.cc\",\n        \"kernels/scatter_label_kernel.cc\",\n        \"kernels/split_flow_dataset_kernel.cc\",\n        \"kernels/string_to_variant.cc\",\n        \"kernels/tf_example_to_example_kernel.cc\",\n        \"kernels/transform_dataset_kernel.cc\",\n        \"kernels/transform_dataset_kernel.h\",\n        \"kernels/variant_filter_kernel.cc\",\n        \"kernels/gen_fid_mask.cc\",\n    ],\n    deps = [\n        \":data_op_config_cc_proto\",\n        \":pb_data_internal_lib\",\n        \"//monolith/native_training/data/kernels/internal:cache_mgr\",\n        \"//monolith/native_training/data/kernels/internal:datasource_utils\",\n        \"//monolith/native_training/data/kernels/internal:file_match_split_provider\",\n        \"//monolith/native_training/data/kernels/internal:label_utils\",\n        \"//monolith/native_training/data/kernels/internal:value_filter_by_line_id\",\n        \"//monolith/native_training/data/kernels/internal:value_filter_by_feature\",\n        \"//monolith/native_training/data/kernels/internal:parquet_example_reader\",\n        \"//monolith/native_training/data/kernels/internal:relational_utils\",\n        \"//monolith/native_training/data/kernels/internal:uniq_hashtable\",\n        \"//monolith/native_training/data/training_instance:data_reader\",\n        \"//monolith/native_training/data/training_instance:fid\",\n        \"//monolith/native_training/data/training_instance:instance_utils\",\n        \"//monolith/native_training/data/training_instance:parse_instance_lib\",\n        \"//monolith/native_training/data/training_instance:reader_util\",\n        \"//monolith/native_training/data/transform:transforms\",\n        \"//monolith/native_training/runtime/common:metrics\",\n        \"//monolith/native_training/runtime/common:linalg_utils\",\n        \"//monolith/native_training/runtime/concurrency:queue\",\n        \"//monolith/native_training/runtime/ops:traceme\",\n        \"//third_party/nlohmann:json\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/hash:city\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@kafka\",\n    ],\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"pb_data_ops\",\n    srcs = [\n        \"ops/feature_utils_ops.cc\",\n        \"ops/parse_input_data_ops.cc\",\n        \"ops/pb_dataset_ops.cc\",\n    ],\n    copts = [\"-DNDEBUG\"],\n    deps = [\n        \":pb_data_lib\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n    alwayslink = 1,\n)\n\npy_library(\n    name = \"datasets_py\",\n    srcs = [\n        \"datasets.py\",\n    ],\n    deps = [\n        \":feature_list\",\n        \":feature_utils_py\",\n        \":parsers_py\",\n        \"//monolith:utils\",\n        \"//monolith/native_training:mlp_utils\",\n        \"//monolith/native_training:monolith_export\",\n        \"//monolith/native_training/data/transform:transforms_py\",\n        \"//monolith/native_training/distribute:distributed_dataset\",\n        \"//monolith/native_training/hooks:ckpt_hooks\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n        requirement(\"kafka_python\"),\n    ],\n)\n\npy_library(\n    name = \"parsers_py\",\n    srcs = [\n        \"parsers.py\",\n    ],\n    deps = [\n        \":data_op_config_py_proto\",\n        \":feature_list\",\n        \"//idl:example_py_proto\",\n        \"//idl:proto_parser_py_proto\",\n        \"//monolith:utils\",\n        \"//monolith/native_training:logging_ops\",\n        \"//monolith/native_training:monolith_export\",\n        \"//monolith/native_training:native_task_context\",\n        \"//monolith/native_training:utils\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_library(\n    name = \"feature_utils_py\",\n    srcs = [\n        \"feature_utils.py\",\n    ],\n    deps = [\n        \":data_op_config_py_proto\",\n        \":feature_list\",\n        \":parsers_py\",\n        \"//idl:example_py_proto\",\n        \"//idl:proto_parser_py_proto\",\n        \"//monolith:utils\",\n        \"//monolith/native_training:monolith_export\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\n\npy_library(\n    name = \"feature_list\",\n    srcs = [\"feature_list.py\"],\n    deps = [\n        \":utils\",\n        \"//monolith/native_training:utils\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_test(\n    name = \"feature_list_test\",\n    srcs = [\"feature_list_test.py\"],\n    data = [\"//monolith/native_training/data/test_data:test_feature_lists\"],\n    deps = [\n        \":feature_list\",\n    ],\n)\n\n\npy_library(\n    name = \"data\",\n    srcs = [\n        \"__init__.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":datasets_py\",\n        \":feature_utils_py\",\n        \":parsers_py\",\n    ],\n)\n\npy_test(\n    name = \"extract_fid_test\",\n    srcs = [\n        \"extract_fid_test.py\",\n    ],\n    main = \"extract_fid_test.py\",\n    deps = [\n        \"//idl:example_py_proto\",\n        \"//idl:proto_parser_py_proto\",\n        \"//monolith:utils\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_library(\n    name = \"utils\",\n    srcs = [\"utils.py\"],\n)\n\npy_library(\n    name = \"item_pool_hook\",\n    srcs = [\"item_pool_hook.py\"],\n    deps = [\n        \":datasets_py\",\n        \":feature_utils_py\",\n    ],\n)\n\npy_test(\n    name = \"item_pool_test\",\n    srcs = [\n        \"item_pool_test.py\",\n    ],\n    deps = [\n        \":feature_utils_py\",\n        \":feature_list\",\n        \"//monolith:utils\",\n    ],\n)\n\npy_test(\n    name = \"multi_flow_test\",\n    srcs = [\n        \"multi_flow_test.py\",\n    ],\n    main = \"multi_flow_test.py\",\n    deps = [\n        \":datasets_py\",\n        \":feature_utils_py\",\n        \":parsers_py\",\n        \"//idl:example_py_proto\",\n        \"//idl:proto_parser_py_proto\",\n    ],\n)\n\npy_test(\n    name = \"negative_gen_test\",\n    srcs = [\n        \"negative_gen_test.py\",\n    ],\n    main = \"negative_gen_test.py\",\n    deps = [\n        \":datasets_py\",\n        \":feature_utils_py\",\n        \":parsers_py\",\n        \"//idl:example_py_proto\",\n        \"//idl:proto_parser_py_proto\",\n    ],\n)\n\n\npy_binary(\n    name = \"kafka_dataset_test\",\n    srcs = [\n        \"kafka_dataset_test.py\",\n    ],\n    deps = [\n        \":datasets_py\",\n        \":feature_utils_py\",\n        \":parsers_py\",\n        \"//idl:example_py_proto\",\n        \"//idl:proto_parser_py_proto\",\n        \"//monolith/native_training/model_export:data_gen_utils\",\n    ],\n)\n\npy_binary(\n    name = \"data_service_test\",\n    srcs = [\n        \"data_service_test.py\",\n    ],\n    deps = [\n        \":datasets_py\",\n        \":feature_utils_py\",\n    ],\n)\n\npy_binary(\n    name = \"data_service_parquet_test\",\n    srcs = [\n        \"data_service_parquet_test.py\",\n    ],\n    deps = [\n        \":datasets_py\",\n        \":feature_utils_py\",\n    ],\n)\n\n\nexports_files([\n    \"kernels/add_action_kernel.cc\",\n    \"kernels/instance_reweight_dataset_kernel.cc\",\n    \"kernels/instance_reweight_dataset_kernel.h\",\n    \"kernels/negative_gen_dataset_kernel.cc\",\n    \"kernels/negative_gen_dataset_kernel.h\",\n    \"kernels/df_resource_kernel.h\",\n    \"kernels/df_resource_kernel.cc\",\n    \"kernels/split_flow_dataset_kernel.cc\",\n    \"kernels/merge_flow_dataset_kernel.cc\",\n    \"kernels/parse_example_lib.cc\",\n    \"kernels/parse_example_lib.h\",\n    \"kernels/parse_input_data_kernel.cc\",\n    \"kernels/pb_dataset_kernel.cc\",\n    \"kernels/ragged_feature_kernel.cc\",\n    \"kernels/variant_filter_kernel.cc\",\n    \"kernels/parquet_dataset_kernel.cc\",\n    \"kernels/item_pool_kernels.h\",\n    \"kernels/item_pool_kernels.cc\",\n    \"ops/feature_utils_ops.cc\",\n    \"ops/parse_input_data_ops.cc\",\n    \"ops/pb_dataset_ops.cc\",\n])\n"
  },
  {
    "path": "monolith/native_training/data/__init__.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport monolith.native_training.data.datasets as datasets\n\nfrom monolith.native_training.data.datasets import PBDataset, InstanceReweightDataset, NegativeGenDataset, PbType\nfrom monolith.native_training.data.parsers import parse_examples, parse_instances, parse_example_batch\nfrom monolith.native_training.data.feature_utils import filter_by_fids, filter_by_feature_value, filter_by_value, \\\n    feature_combine, negative_sample, switch_slot, special_strategy\n"
  },
  {
    "path": "monolith/native_training/data/data_op_config.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\n\npackage monolith.native_training.data.config;\n\n\nmessage TaskLabelConf {\n  repeated int32 pos_actions = 1;\n  repeated int32 neg_actions = 2;\n  optional float sample_rate = 3 [default = 1.0];\n}\n\nmessage LabelConf {\n  repeated TaskLabelConf conf = 1;\n}\n\nmessage TFRecordFeatureDescription {\n  map<string, int32> sparse_features = 1;\n  repeated string dense_features = 2;\n  optional string label = 3 [default = \"\"];\n  optional string instance_weight = 4 [default = \"\"];\n}\n"
  },
  {
    "path": "monolith/native_training/data/data_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nimport tensorflow as tf\n\nfrom absl import logging, flags\nimport numpy as np\nfrom struct import unpack\nimport zlib\nimport gzip\n\nfrom monolith.native_training.data.datasets import PBDataset, InstanceReweightDataset, PbType, CompressType, \\\n  FilePBDataset, KafkaDataset, CacheOneDataset\nfrom monolith.native_training.data.parsers import parse_instances, parse_examples, parse_example_batch\nfrom monolith.native_training.data.feature_utils import filter_by_fids, filter_by_value, negative_sample, \\\n  switch_slot, feature_combine, special_strategy\nfrom idl.matrix.proto.example_pb2 import Example, ExampleBatch\nfrom monolith.native_training.model_export.data_gen_utils import gen_random_data_file, ParserArgs\nfrom tensorflow.python.framework import sparse_tensor\nfrom monolith.native_training.estimator import RunConfig\nfrom monolith.native_training.hooks import session_hooks\n\nFLAGS = flags.FLAGS\nfeatures = {\n    'f_spm_1': 301,\n    'f_spm_3': 303,\n    'f_spm_2': 302,\n    'f_spm_4': 304,\n    'f_user_id': 1,\n    'f_user_ctx_network': 61,\n    'f_user_id-f_page': 504,\n    'f_scm': 306,\n    'f_goods_id': 200,\n    'f_goods_sale_number_1000': 225,\n    'f_goods_praise_cnt': 229,\n    'f_spm': 300,\n    'f_page': 305,\n    'f_is_dup': 310,\n    'f_user_ctx_platform': 52,\n    'f_goods_title_terms': 209,\n    'f_goods_tags_terms': 211,\n    'f_user_test09_array_int32': 554,\n    'f_user_test15_array_float': 540,\n    'f_user_test14_array_bool': 543,\n    'f_user_test12_array_uint64': 551,\n    'f_user_test10_array_int64': 549\n}\n\n\ngroup_slots = [200,201,202,203,204,205,206,210,211,212,213,214,215,\\\n               216,217,218,219,220,221,222,223,224,225,230,231,232,233,234,235,236,237,238,239,240,241,242]\n\n\ndef parse_inst_exam(tensor: tf.Tensor, out_type):\n  fidv1_features = [\n      1, 2, 32, 33, 36, 38, 42, 50, 54, 56, 60, 66, 120, 150, 180, 182, 192,\n      220, 333, 410, 412, 422, 446\n  ]\n  if out_type == PbType.INSTANCE:\n    return parse_instances(tensor,\n                           fidv1_features,\n                           dense_features=['label'],\n                           dense_feature_shapes=[2],\n                           dense_feature_types=[tf.float32],\n                           extra_features=['uid', 'req_time', 'item_id'],\n                           extra_feature_shapes=[1, 1, 1])\n  else:\n    return parse_examples(\n        tensor,\n        sparse_features=[f'fc_slot_{slot}' for slot in fidv1_features],\n        dense_features=['label'],\n        dense_feature_shapes=[2],\n        dense_feature_types=[tf.float32],\n        extra_features=['uid', 'req_time', 'item_id'],\n        extra_feature_shapes=[1, 1, 1])\n\n\ndef parse_eb(tensor: tf.Tensor, out_type):\n  if out_type == PbType.INSTANCE:\n    feature_dict = parse_instances(\n        tensor,\n        fidv1_features=list(features.values()),\n        dense_features=['label'],\n        dense_feature_shapes=[2],\n        dense_feature_types=[tf.float32],\n        extra_features=['uid', 'req_time', 'item_id'],\n        extra_feature_shapes=[1, 1, 1])\n  else:\n    feature_dict = parse_examples(\n        tensor,\n        sparse_features=list(features.keys()),\n        dense_features=['label'],\n        dense_feature_shapes=[2],\n        dense_feature_types=[tf.float32],\n        extra_features=['uid', 'req_time', 'item_id', 'actions'],\n        extra_feature_shapes=[1, 1, 1, 1])\n    feature_dict['f_page'] = switch_slot(feature_dict['f_page'], slot=306)\n    feature_dict['f_user_id-f_goods_tags_terms'] = feature_combine(\n        feature_dict['f_user_id'], feature_dict['f_goods_tags_terms'], slot=505)\n  return feature_dict\n\n\nclass DataOpsTest(tf.test.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    cwd = os.getcwd()\n    cls.patterns = [os.path.join(cwd, \"tmp_data\", \"part-*\")]\n    cls._files = []\n    args = ParserArgs(fidv1_features=[i for i in range(1, 10)],\n                      extra_features=[\n                          'uid', 'sample_rate', 'req_time', 'actions',\n                          'stay_time'\n                      ],\n                      extra_feature_shapes=[1, 1, 1, 1, 1],\n                      batch_size=16,\n                      variant_type='instance')\n    for i in range(3):\n      tf.io.gfile.makedirs(os.path.join(cwd, \"tmp_data\"))\n      file_name = os.path.join(cwd, \"tmp_data\", f\"part-{i}\")\n      gen_random_data_file(file_name,\n                           args,\n                           num_batch=10,\n                           sort_id=True,\n                           kafka_dump_prefix=False)\n      cls._files.append(file_name)\n\n  @classmethod\n  def tearDownClass(cls):\n    for file_name in cls._files:\n      tf.io.gfile.remove(file_name)\n\n  def pb_dataset_target(self, input_pb_type, output_pb_type, filter_fn=None):\n    if input_pb_type == PbType.INSTANCE:\n      lagrangex_header = False\n      has_sort_id, kafka_dump, kafka_dump_prefix = True, True, False\n      file_name = \"monolith/native_training/data/training_instance/instance.pb\"\n    elif input_pb_type == PbType.EXAMPLE:\n      lagrangex_header = False\n      has_sort_id, kafka_dump, kafka_dump_prefix = True, True, False\n      file_name = \"monolith/native_training/data/training_instance/example.pb\"\n    else:\n      lagrangex_header = True\n      has_sort_id, kafka_dump, kafka_dump_prefix = False, False, False\n      file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n\n    def parser(tensor: tf.Tensor):\n      if output_pb_type == PbType.PLAINTEXT:\n        return parse_inst_exam(tensor, input_pb_type)\n      elif input_pb_type != PbType.EXAMPLEBATCH:\n        return parse_inst_exam(tensor, output_pb_type)\n      else:\n        return parse_eb(tensor, output_pb_type)\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        dataset = PBDataset(file_name=file_name,\n                            lagrangex_header=lagrangex_header,\n                            has_sort_id=has_sort_id,\n                            kafka_dump=kafka_dump,\n                            kafka_dump_prefix=kafka_dump_prefix,\n                            input_pb_type=input_pb_type,\n                            output_pb_type=output_pb_type)\n        if input_pb_type == PbType.EXAMPLEBATCH:\n          variant_type = 'instance' if output_pb_type == PbType.INSTANCE else 'example'\n          dataset = dataset.instance_reweight(\n              action_priority=\"2,7,0,1,3,4,5,6,8,9,10,11\",\n              reweight=\n              \"0:0:1,1:0:1,2:3:-1,3:0:1,4:0:1,5:0:1,6:0:1,7:6:1,8:0:1,9:0:1,10:0:1,11:0:-1\",\n              variant_type=variant_type)\n        if filter_fn is not None:\n          dataset = dataset.filter(filter_fn)\n        dataset = dataset.batch(8, drop_remainder=True).map(parser)\n        it = tf.compat.v1.data.make_initializable_iterator(dataset)\n        element = it.get_next()\n        sess.run(it.initializer)\n        count = 0\n        while True:\n          try:\n            element_num = sess.run(element)\n            # print(element_num)\n            count += 8\n          except tf.errors.OutOfRangeError:\n            break\n        logging.info(\"The number of batch is: {}\".format(count))\n\n  def testInstance2Instance(self):\n    self.pb_dataset_target(input_pb_type=PbType.INSTANCE,\n                           output_pb_type=PbType.INSTANCE)\n\n  def testInstance2Example(self):\n    self.pb_dataset_target(input_pb_type=PbType.INSTANCE,\n                           output_pb_type=PbType.EXAMPLE)\n\n  def testExample2Example(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLE,\n                           output_pb_type=PbType.EXAMPLE)\n\n  def testExample2Instance(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLE,\n                           output_pb_type=PbType.INSTANCE)\n\n  def testExampleBatch2Example(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLEBATCH,\n                           output_pb_type=PbType.EXAMPLE)\n\n  def testExampleBatch2Instance(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLEBATCH,\n                           output_pb_type=PbType.INSTANCE)\n\n  def testInstanceWithPBInstanceDataset(self):\n    self.pb_dataset_target(input_pb_type=PbType.INSTANCE,\n                           output_pb_type=PbType.PLAINTEXT)\n\n  def testExampleWithPBInstanceDataset(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLE,\n                           output_pb_type=PbType.PLAINTEXT)\n\n  def testSetFilterInstance(self):\n    self.pb_dataset_target(\n        input_pb_type=PbType.EXAMPLEBATCH,\n        output_pb_type=PbType.INSTANCE,\n        filter_fn=lambda variant: filter_by_fids(variant, has_actions=[1, 2]))\n\n  def testSetFilterExample(self):\n    self.pb_dataset_target(\n        input_pb_type=PbType.EXAMPLEBATCH,\n        output_pb_type=PbType.EXAMPLE,\n        filter_fn=lambda variant: filter_by_fids(\n            variant, has_actions=[1, 2], variant_type='example'))\n\n  def testValueFilterInstance(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLEBATCH,\n                           output_pb_type=PbType.INSTANCE,\n                           filter_fn=lambda variant: filter_by_value(\n                               variant, \"sample_rate\", \"ge\", 0.8))\n\n  def testValueFilterInInstance(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLEBATCH,\n                           output_pb_type=PbType.INSTANCE,\n                           filter_fn=lambda variant: filter_by_value(\n                               variant, \"chnid\", \"in\", [0, 2, 5]))\n\n  def testValueFilterEqInstance(self):\n    self.pb_dataset_target(\n        input_pb_type=PbType.EXAMPLEBATCH,\n        output_pb_type=PbType.INSTANCE,\n        filter_fn=lambda variant: filter_by_value(variant, \"chnid\", \"eq\", 0))\n\n  def testValueFilterBewteenInstance(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLEBATCH,\n                           output_pb_type=PbType.INSTANCE,\n                           filter_fn=lambda variant: filter_by_value(\n                               variant, \"sample_rate\", \"between\", [0.1, 0.9]))\n\n  def testValueFilterStrInstance(self):\n    self.pb_dataset_target(\n        input_pb_type=PbType.EXAMPLEBATCH,\n        output_pb_type=PbType.INSTANCE,\n        filter_fn=lambda variant: filter_by_value(variant, \"vid\", \"eq\", 'scm'))\n\n  def testValueFilterAnyInstance(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLEBATCH,\n                           output_pb_type=PbType.INSTANCE,\n                           filter_fn=lambda variant: filter_by_value(\n                               variant, \"actions\", \"any\", [2, 5, 7]))\n\n  def testValueFilterAllInstance(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLEBATCH,\n                           output_pb_type=PbType.INSTANCE,\n                           filter_fn=lambda variant: filter_by_value(\n                               variant, \"actions\", \"all\", [2, 5, 7]))\n\n  def testValueFilterDiffInstance(self):\n    self.pb_dataset_target(input_pb_type=PbType.EXAMPLEBATCH,\n                           output_pb_type=PbType.INSTANCE,\n                           filter_fn=lambda variant: filter_by_value(\n                               variant, \"actions\", \"diff\", [2, 5, 7]))\n\n  def testSpecialStrategyInstance(self):\n    self.pb_dataset_target(\n        input_pb_type=PbType.EXAMPLEBATCH,\n        output_pb_type=PbType.INSTANCE,\n        filter_fn=lambda variant: special_strategy(\n            variant, [2, 5, 7], \"2:0.7:-1,5:0.9:1,4:0.2:0,7:1.0:1\"))\n\n  def testValueFilterExample(self):\n    self.pb_dataset_target(\n        input_pb_type=PbType.EXAMPLEBATCH,\n        output_pb_type=PbType.EXAMPLE,\n        filter_fn=lambda variant: filter_by_value(\n            variant, \"sample_rate\", \"ge\", 0.8, variant_type='example'))\n\n  def testExampleBatchPredScalar(self):\n    eb = ExampleBatch()\n    file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      examples_placeholder = tf.compat.v1.placeholder(dtype=tf.string,\n                                                      shape=(None,))\n      parsed_results = parse_example_batch(\n          examples_placeholder,\n          sparse_features=list(features.keys()),\n          dense_features=['label'],\n          dense_feature_shapes=[2],\n          dense_feature_types=[tf.float32],\n          extra_features=['uid', 'req_time', 'item_id'],\n          extra_feature_shapes=[1, 1, 1])\n\n      with self.session(config=config) as sess:\n        with open(file_name, 'rb') as stream:\n          stream.read(8)  # strip lagrangex_header\n          size = unpack(\"<Q\", stream.read(8))[0]\n          eb_str = stream.read(size)\n          results = sess.run(fetches=[parsed_results],\n                             feed_dict={examples_placeholder: [eb_str]})\n\n  def testExampleBatchPredBatch(self):\n    eb = ExampleBatch()\n    file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      examples_placeholder = tf.compat.v1.placeholder(dtype=tf.string)\n      parsed_results = parse_example_batch(\n          examples_placeholder,\n          sparse_features=list(features.keys()),\n          dense_features=['label'],\n          dense_feature_shapes=[2],\n          dense_feature_types=[tf.float32],\n          extra_features=['uid', 'req_time', 'item_id'],\n          extra_feature_shapes=[1, 1, 1])\n\n      with self.session(config=config) as sess:\n        with open(file_name, 'rb') as stream:\n          stream.read(8)  # strip lagrangex_header\n          size = unpack(\"<Q\", stream.read(8))[0]\n          eb_str = stream.read(size)\n          results = sess.run(fetches=[parsed_results],\n                             feed_dict={examples_placeholder: eb_str})\n\n  def testPBDataset(self):\n    self.assertTrue(isinstance(PBDataset(''), FilePBDataset))\n\n    FLAGS.kafka_topics = 'abc,def'\n    FLAGS.kafka_group_id = 'test'\n    FLAGS.kafka_servers = 'test'\n    self.assertTrue(isinstance(PBDataset(), KafkaDataset))\n    self.assertTrue(\n        isinstance(PBDataset(['ab'], group_id='c', servers='d'), KafkaDataset))\n    FLAGS.kafka_topics = None\n    FLAGS.kafka_group_id = None\n    FLAGS.kafka_servers = None\n\n  def _init_session(self):\n    config = tf.compat.v1.ConfigProto()\n    config.graph_options.rewrite_options.disable_meta_optimizer = True\n    return tf.compat.v1.train.SingularMonitoredSession(\n        hooks=[session_hooks.SetCurrentSessionHook()], config=config)\n\n  def testCreateInstanceDatasetHdfs(self):\n    with self.session() as sess:\n      dataset = PBDataset(topics_or_files=self.patterns,\n                          has_sort_id=True,\n                          kafka_dump_prefix=False,\n                          use_snappy=False)\n\n      def parse(serialized: tf.Tensor):\n        return parse_instances(serialized, fidv1_features=list(range(1, 10)))\n\n      dataset = dataset.batch(16, drop_remainder=True).map(parse)\n      it = tf.compat.v1.data.make_initializable_iterator(dataset)\n      element = it.get_next()\n      element_num = None\n      self._init_session()\n      sess.run(it.initializer)\n      for _ in range(10):\n        try:\n          element_num = sess.run(element)\n        except tf.errors.OutOfRangeError:\n          break\n\n  def testGenPatterns(self):\n    patterns = PBDataset.gen_patterns(input_path='/abc',\n                                      start_date='20220901',\n                                      end_date='20220920',\n                                      is_hourly=False)\n    self.assertEqual(len(patterns), 19)\n\n\nclass CahceOneDatasetTest(tf.test.TestCase):\n\n  def test_basic(self):\n    ds = tf.data.Dataset.from_tensor_slices([1, 2])\n    ds = CacheOneDataset(ds)\n    it = tf.compat.v1.data.make_one_shot_iterator(ds)\n\n    self.assertEqual(self.evaluate(it.get_next()), (1, False))\n    self.assertEqual(self.evaluate(it.get_next()), (2, True))\n\n\nclass DecompressTest(tf.test.TestCase):\n  raw_file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n  file_name = \"examplebatch.data.copy\"\n\n  @classmethod\n  def setUpClass(cls):\n    logging.info(\n        f\"cp resp: {os.system(f'cp -rL {cls.raw_file_name} {cls.file_name}')}\")\n\n  def prase_base(self, compress_file_name, compression_type):\n    with self.session() as sess:\n      dataset = PBDataset(topics_or_files=[compress_file_name],\n                          lagrangex_header=True,\n                          has_sort_id=False,\n                          input_pb_type=PbType.EXAMPLEBATCH,\n                          output_pb_type=PbType.EXAMPLEBATCH,\n                          compression_type=compression_type)\n\n      def parse(serialized: tf.Tensor):\n        return parse_example_batch(\n            serialized,\n            sparse_features=list(features.keys()),\n            dense_features=['label'],\n            dense_feature_shapes=[2],\n            dense_feature_types=[tf.float32],\n            extra_features=['uid', 'req_time', 'item_id'],\n            extra_feature_shapes=[1, 1, 1])\n\n      dataset = dataset.batch(16, drop_remainder=True).map(parse)\n      it = tf.compat.v1.data.make_initializable_iterator(dataset)\n      element = it.get_next()\n      element_num = None\n\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      tf.compat.v1.train.SingularMonitoredSession(\n          hooks=[session_hooks.SetCurrentSessionHook()], config=config)\n      sess.run(it.initializer)\n      for _ in range(10):\n        try:\n          element_num = sess.run(element)\n          print(element_num)\n          #assert False\n        except tf.errors.OutOfRangeError:\n          break\n\n  def testDecompressZstd(self):\n    #FLAGS.data_type = 'examplebatch'\n    compress_file_name = self.file_name + \".zstd\"\n    logging.info(\n        f\"zstd resp: {os.system(f'/opt/tiger/ss_bin/zstd {self.file_name} -o {compress_file_name}')}\"\n    )\n    logging.info(f\"zstd resp: {os.system(f'ls -l {self.file_name}*')}\")\n    self.prase_base(compress_file_name, CompressType.ZSTD)\n\n  def testDecompressZlib(self):\n    #FLAGS.data_type = 'examplebatch'\n    compress_file_name = self.file_name + \".zlib\"\n\n    with open(self.file_name, 'rb') as in_f:\n      s = in_f.read()\n      z = zlib.compress(s)\n    with open(compress_file_name, 'wb') as f:\n      f.write(z)\n    logging.info(f\"zstd resp: {os.system(f'ls -l {self.file_name}*')}\")\n    self.prase_base(compress_file_name, CompressType.ZLIB)\n\n  def testDecompressGzip(self):\n    #FLAGS.data_type = 'examplebatch'\n    compress_file_name = self.file_name + \".gzip\"\n\n    with open(self.file_name, 'rb') as in_f:\n      s = in_f.read()\n      z = gzip.compress(s)\n    with open(compress_file_name, 'wb') as f:\n      f.write(z)\n    logging.info(f\"zstd resp: {os.system(f'ls -l {self.file_name}*')}\")\n    self.prase_base(compress_file_name, CompressType.GZIP)\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/data_service_parquet_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import flags, logging\nfrom typing import get_type_hints\nfrom enum import Enum\nimport dataclasses\nimport threading\n\nimport tensorflow as tf\nimport tensorflow.python.data.experimental.service as dsvc\nfrom monolith.native_training.data.datasets import DynamicMatchingFilesDataset, PBDataset, PbType\nfrom idl.matrix.proto.example_pb2 import Example, ExampleBatch\nimport json\nimport os\n# import pyarrow.parquet as pq\n\nNUM_WORKER = 3\n\n\nclass DataServiceTest2(tf.test.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    cls.dispatcher = dsvc.DispatchServer(\n        tf.data.experimental.service.DispatcherConfig(port=7080))\n    cls.target = cls.dispatcher.target\n    dispatcher_address = cls.target.split(\"://\")[1]\n    logging.info(f'start dispatcher at {cls.target}')\n\n    cls.workers = []\n    for i in range(NUM_WORKER):\n      worker = dsvc.WorkerServer(\n          dsvc.WorkerConfig(dispatcher_address=dispatcher_address))\n      cls.workers.append(worker)\n      logging.info(f'start worker {i} at {worker._address}')\n\n  @classmethod\n  def tearDownClass(cls):\n    cls.target = None\n\n    if cls.dispatcher is not None:\n      del cls.dispatcher\n      cls.dispatcher = None\n      logging.info('del dispatcher done!')\n\n    for i, worker in enumerate(cls.workers):\n      del worker\n      logging.info(f'del worker {i} done!')\n    cls.workers.clear()\n\n  def testDataServiceWithParquetDataset(self):\n    # check file exist, if not exist, return test pass\n    if os.environ.get(\"META_JSON_PATH\"):\n      meta_json_path = os.environ.get(\"META_JSON_PATH\")\n    else:\n      meta_json_path = os.path.join(os.environ.get(\"HOME\"), \"temp\", \"fountain_meta.json\")\n    if os.environ.get(\"PARQUET_DIR\"):\n      parquet_dir = os.environ.get(\"PARQUET_DIR\")\n    else:\n      parquet_dir =  os.path.join(os.environ.get(\"HOME\"), \"temp\", \"parquet_files\")\n    path_pattern = os.path.join(parquet_dir, \"*\")\n    if not os.path.exists(meta_json_path) or not os.path.exists(parquet_dir):\n      logging.warning(\"meta_json_path or path_pattern not exist, pls check.\") \n\n    # calc col_name and col_type\n    with open(meta_json_path, \"r\") as f:\n      json_data = f.read()\n    j = json.loads(json_data)[\"default_0\"]\n    all_cols = [k for k in j.keys()]\n    col_type_dict = {2: \"fid_v2\", 3: \"float\", 5: \"int\"}\n    all_cols_type = [col_type_dict.get(j[k][\"data_type\"][0], \"invalid\") for k in all_cols]\n\n    # create, register dataset\n    dataset = PBDataset(patterns=[path_pattern],\n                        use_data_service=True,\n                        use_parquet=True,\n                        output_pb_type=PbType.PLAINTEXT,\n                        select_columns=all_cols,\n                        select_columns_type=all_cols_type,\n                        batch_size=1024,\n                        cycle_length=1,\n                        num_parallel_calls=1)\n    dataset_id = dsvc.register_dataset(self.target, dataset)\n\n    comsumer1 = dsvc.from_dataset_id(processing_mode=\"distributed_epoch\",\n                                     service=self.target,\n                                     dataset_id=dataset_id,\n                                     job_name=\"test_data_service_with_parquet_dataset\",\n                                     element_spec=dataset.element_spec,\n                                     max_outstanding_requests=1)\n    comsumer2 = dsvc.from_dataset_id(processing_mode=\"distributed_epoch\",\n                                     service=self.target,\n                                     dataset_id=dataset_id,\n                                     job_name=\"test_data_service_with_parquet_dataset\",\n                                     element_spec=dataset.element_spec,\n                                     max_outstanding_requests=1)\n\n    # read data from data service and calculate sum of batch_size\n    comsumers = [iter(comsumer1), iter(comsumer2)]\n    idx, row_read = 0, 0\n    while True:\n      i = idx % 2\n      idx += 1\n      comsumer = comsumers[i]\n      if comsumer is None:\n        if any(c is not None for c in comsumers):\n          continue\n        else:\n          break\n      try:\n        element = next(comsumer)\n        example_batch = ExampleBatch()\n        example_batch.ParseFromString(element.numpy())\n        row_read += example_batch.batch_size\n        print(f'read batch_size from comsumer{i} is {example_batch.batch_size}', flush=True)\n      except StopIteration as e:\n        comsumers[i] = None\n        del comsumer\n\n    # use py_parquet api calculate sum of rows of all files\n    # total_row = 0\n    # for pf in os.listdir(parquet_dir):\n    #   file_full_path = os.path.join(parquet_dir, pf)\n    #   parquet_file = pq.ParquetFile(file_full_path)\n    #   total_row += parquet_file.metadata.num_rows\n\n    # self.assertEqual(row_read, total_row)\n    print(f\"{row_read} rows read\")\n    \n\nif __name__ == \"__main__\":\n  # tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/data_service_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import flags, logging\nfrom typing import get_type_hints\nfrom enum import Enum\nimport dataclasses\nimport threading\n\nimport tensorflow as tf\nimport tensorflow.python.data.experimental.service as dsvc\nfrom monolith.native_training.data.datasets import DynamicMatchingFilesDataset\n\nNUM_WORKER = 3\n\n\nclass DataServiceTest(tf.test.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    cls.dispatcher = dsvc.DispatchServer(\n        tf.data.experimental.service.DispatcherConfig(port=7080))\n    cls.target = cls.dispatcher.target\n    dispatcher_address = cls.target.split(\"://\")[1]\n    logging.info(f'start dispatcher at {cls.target}')\n\n    cls.workers = []\n    for i in range(NUM_WORKER):\n      worker = dsvc.WorkerServer(\n          dsvc.WorkerConfig(dispatcher_address=dispatcher_address))\n      cls.workers.append(worker)\n      logging.info(f'start worker {i} at {worker._address}')\n\n  @classmethod\n  def tearDownClass(cls):\n    cls.target = None\n\n    if cls.dispatcher is not None:\n      del cls.dispatcher\n      cls.dispatcher = None\n      logging.info('del dispatcher done!')\n\n    for i, worker in enumerate(cls.workers):\n      del worker\n      logging.info(f'del worker {i} done!')\n    cls.workers.clear()\n\n  def testSplitProvider(self):\n    dataset = DynamicMatchingFilesDataset(patterns=[''])\n    dataset_id = dsvc.register_dataset(self.target, dataset)\n\n    comsumer1 = dsvc.from_dataset_id(processing_mode=\"distributed_epoch\",\n                                     service=self.target,\n                                     dataset_id=dataset_id,\n                                     job_name=\"test_dynamic_match_file_dataset\",\n                                     element_spec=dataset.element_spec,\n                                     max_outstanding_requests=1)\n    comsumer2 = dsvc.from_dataset_id(processing_mode=\"distributed_epoch\",\n                                     service=self.target,\n                                     dataset_id=dataset_id,\n                                     job_name=\"test_dynamic_match_file_dataset\",\n                                     element_spec=dataset.element_spec,\n                                     max_outstanding_requests=1)\n    comsumers = [iter(comsumer1), iter(comsumer2)]\n    idx, cnt = 0, 0\n    while True:\n      i = idx % 2\n      idx += 1\n      comsumer = comsumers[i]\n      if comsumer is None:\n        if any(c is not None for c in comsumers):\n          continue\n        else:\n          break\n      try:\n        element = next(comsumer)\n        print(f'element for comsumer{i} is ', element, flush=True)\n        cnt += 1\n      except StopIteration as e:\n        comsumers[i] = None\n        del comsumer\n    self.assertEqual(cnt, 19)\n\n\nif __name__ == \"__main__\":\n  # tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/datasets.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging, flags\nfrom enum import Enum\nimport os, sys, six\nimport types\nimport json\nfrom datetime import datetime, timedelta\nfrom typing import Dict, List, Iterable, Callable, Optional, Union\nimport re\n\nimport tensorflow as tf\nfrom tensorflow.python.eager import context\nfrom tensorflow.python.data.experimental.ops import matching_files\nfrom tensorflow.python.data.ops import dataset_ops\nfrom tensorflow.python.data.ops.dataset_ops import Dataset\nfrom tensorflow.python.data.util import convert\nfrom tensorflow.python.framework import dtypes\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.platform import resource_loader\nfrom tensorflow.python.framework import tensor_shape\nfrom tensorflow.python.framework import tensor_spec\nfrom tensorflow.python.util.tf_export import tf_export\nfrom tensorflow.python.framework import load_library\nfrom tensorflow.python.data.util import nest\nfrom tensorflow.python.data.util import structure\nimport tensorflow.python.data.experimental.service as dsvc\nfrom tensorflow.python.ops import gen_experimental_dataset_ops\nfrom tensorflow.python.data.experimental.ops import compression_ops\nfrom tensorflow.python.data.experimental.ops.data_service_ops import \\\n  _parse_service, _DataServiceDataset, ProcessingMode\nfrom tensorflow.python.data.experimental.ops.distribute_options import ExternalStatePolicy, AutoShardPolicy\n\nfrom monolith.native_training.hooks import ckpt_hooks\nfrom monolith.utils import get_libops_path\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.data.feature_utils import create_item_pool, string_to_variant, \\\n  has_variant, kafka_resource_init, kafka_read_next, kafka_read_next_v2, string_to_variant_with_transform\nfrom monolith.native_training.data.feature_list import FeatureList, add_feature\nfrom monolith.native_training.data.parsers import get_default_parser_ctx\nfrom monolith.native_training.data.transform.transforms import Transform\nfrom monolith.native_training import native_task_context\nfrom monolith.native_training.distribute import distributed_dataset\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\nfrom kafka import KafkaConsumer\nfrom threading import Thread, RLock\nfrom queue import Queue\nfrom monolith.native_training.distribution_utils import enable_sync_training\nfrom tensorflow.core.framework.attr_value_pb2 import AttrValue\nfrom tensorflow.core.framework.tensor_pb2 import TensorProto\n\npb_datasource_ops = gen_monolith_ops\n\nFLAGS = flags.FLAGS\nflags.DEFINE_string('data_service_dispatcher', '',\n                    'string, data_service_dispatcher')\nflags.DEFINE_bool('dataset_use_dataservice', False,\n                  'bool, whether use dataservice')\nflags.DEFINE_string(\n    'dataset_input_patterns', None,\n    'string, input patterns list of files, format data_{DATE(20201120, 20201210)}/hour_{INT(0, 5)}/part_{INT(0, 3)}'\n)\nflags.DEFINE_bool('dataset_input_use_snappy', None,\n                  'bool, dataset_input_use_snappy')\nflags.DEFINE_string('dataset_input_compression_type', None,\n                    'string, dataset_input_compression_type')\nflags.DEFINE_bool('dataset_input_use_parquet', None,\n                  'bool dataset_input_use_parquet')\nflags.DEFINE_bool('dataset_input_use_tfrecord', None,\n                  'bool dataset_input_use_tfrecord')\nflags.DEFINE_integer('dataset_worker_idx', None, 'int dataset_worker_idx')\nflags.DEFINE_integer('dataset_num_workers', None, 'int dataset_num_workers')\nflags.DEFINE_string('kafka_other_metadata', None,\n                    'string, kafka_other_metadata')\nPOOL_KEY = \"TF_ITEMPOOL\"\nOUTPUT_PB_TYPE_GRAPH_KEY = \"monolith_dataset_output_pb_type\"\n\n\nclass FeaturePruningType(object):\n  AS_IS = 0\n  PRUNING_FEATURE = 1\n  PRUNING_RAW_FEATURE = 2\n\n\n@monolith_export\nclass PbType(Enum):\n  INSTANCE = 1\n  EXAMPLEBATCH = 2\n  EXAMPLE = 3\n  PLAINTEXT = 4\n\n  def to_name(self):\n    return self.name.lower()\n\n\ndef _get_params(name, default=None):\n  try:\n    if name == 'data_type':\n      attr_val = getattr(FLAGS, name)\n      if attr_val:\n        attr_val = attr_val.upper()\n        if attr_val == 'EXAMPL_EBATCH':\n          return PbType.EXAMPLEBATCH\n        else:\n          return PbType[attr_val]\n      else:\n        return default\n    else:\n      return getattr(FLAGS, name)\n  except:\n    return default\n\n\nclass DatasetMetaclass(type):\n\n  def __call__(cls, *args, **kwargs):\n    logging.info('---args: %s', args)\n    logging.info('---kwargs: %s', kwargs)\n    if kwargs.get('topics_or_files', None):\n      value = kwargs['topics_or_files']\n      if isinstance(value, str):\n        kwargs['file_name'] = kwargs.get('file_name') or value\n      else:\n        kwargs['patterns'] = kwargs.get('patterns') or value\n        kwargs['topics'] = kwargs.get('topics') or value\n    if kwargs.get('buffer_size_or_group_id', None):\n      value = kwargs['buffer_size_or_group_id']\n      if isinstance(value, int):\n        kwargs['buffer_size'] = kwargs.get('buffer_size') or value\n      else:\n        kwargs['group_id'] = kwargs.get('group_id') or value\n    if kwargs.get('input_pb_type_or_servers', None):\n      value = kwargs['input_pb_type_or_servers']\n      if isinstance(value, (str, list)):\n        kwargs['servers'] = kwargs.get('servers') or value\n      else:\n        kwargs['input_pb_type'] = kwargs.get('input_pb_type') or value\n\n    if FLAGS.dataset_input_patterns:\n      #test_str = \"data_{DATE(20201120, 20201210)}/hour_{INT(0, 5)}/part_{INT(0, 3)}\"\n      dataset_input_patterns = FLAGS.dataset_input_patterns.replace(\" \", \"\")\n      logging.info(\"The original string is : \" + dataset_input_patterns)\n\n      input_pattern = re.sub(r'\\{.*?\\}', \"{}\", dataset_input_patterns)\n      logging.info(f\"input_pattern {input_pattern}\")\n\n      all_pattern_tmp = re.findall(r'{.*?}', dataset_input_patterns)\n      all_pattern_tmp = [sub[1:-1] for sub in all_pattern_tmp]\n      logging.info(f\"all_pattern_tmp {all_pattern_tmp}\")\n      all_pattern_format = []\n      for pattern in all_pattern_tmp:\n        pattern_part = re.split(\"[(,)]\", pattern)\n        assert len(pattern_part) == 4\n        pattern_type = pattern_part[0]\n        start = pattern_part[1]\n        end = pattern_part[2]\n        #print(pattern_type, start, end)\n        pattern_list = []\n        assert pattern_type == \"DATE\" or pattern_type == \"INT\"\n        if pattern_type == \"DATE\":\n          assert len(start) == len(end)\n          if len(start) == 8:\n            start_date = datetime.strptime(start, '%Y%m%d').date()\n            end_date = datetime.strptime(end, '%Y%m%d').date()\n            delta = end_date - start_date\n            pattern_list = [(start_date + timedelta(days=i)).strftime(\"%Y%m%d\")\n                            for i in range(delta.days + 1)]\n          elif len(start) == 10:\n            start_date = datetime.strptime(start, '%Y%m%d%H')\n            end_date = datetime.strptime(end, '%Y%m%d%H')\n            delta = end_date - start_date\n            pattern_list = [\n                (start_date + timedelta(hours=i)).strftime(\"%Y%m%d%H\")\n                for i in range(delta.days * 24 + delta.seconds // 3600 + 1)\n            ]\n          else:\n            assert False, f\"not support {start} {end}\"\n        elif pattern_type == \"INT\":\n          pattern_list = list(range(int(start), int(end) + 1))\n        #print(pattern_list)\n        all_pattern_format.append(pattern_list)\n\n      logging.info(f\"all_pattern_format {all_pattern_format}\")\n\n      all_input_files = []\n\n      def pattern_recurse(pattern_format_list, *args):\n        if len(pattern_format_list) == 0:\n          one_pattern = input_pattern.format(*args)\n          try:\n            if len(tf.io.gfile.glob(one_pattern)) > 0:\n              all_input_files.append(one_pattern)\n            else:\n              logging.warning(f\"pattern not match any files: {one_pattern}\")\n          except Exception as e:\n            logging.warning(\n                f\"pattern not match any files: {one_pattern} with error: {e}\")\n        else:\n          for pattern in pattern_format_list[0]:\n            pattern_recurse(pattern_format_list[1:], *args, pattern)\n\n      pattern_recurse(all_pattern_format)\n\n      logging.info(f\"all_input_files {all_input_files}\")\n      assert len(all_input_files) > 0, \"no match files\"\n      kwargs['patterns'] = all_input_files\n\n      # dataset_input_patterns will use DistributedFilePBDataset, \"file_name\" param will cause conflict\n      # meanwhile \"file_name\" param is useless, but user code fill this param as default in model.py\n      # to fix this problem, pop this params\n      if \"file_name\" in kwargs:\n        kwargs.pop(\"file_name\")\n\n    if FLAGS.dataset_input_use_parquet is not None:\n      kwargs['use_parquet'] = FLAGS.dataset_input_use_parquet\n    if FLAGS.dataset_input_use_tfrecord is not None:\n      kwargs['use_tfrecord'] = FLAGS.dataset_input_use_tfrecord\n    assert not (\n        FLAGS.dataset_input_use_parquet and FLAGS.dataset_input_use_tfrecord\n    ), \"It's not allowed to specify dataset_input_use_parquet=True and dataset_input_use_tfrecord=True\"\n    if kwargs.get('kafka_other_metadata',\n                  None) is None and FLAGS.kafka_other_metadata is not None:\n      kwargs['kafka_other_metadata'] = FLAGS.kafka_other_metadata\n    try:\n      # the first param is str, batch to streaming, use kafka params for cmd\n      kafka_args = [\n          kwargs.pop('topics', FLAGS.kafka_topics.split(',')),\n          kwargs.pop('group_id', FLAGS.kafka_group_id),\n          kwargs.pop('servers', FLAGS.kafka_servers)\n      ]\n      assert all(x is not None for x in kafka_args)\n      logging.info('use KafkaDataset!')\n      return KafkaDataset(*kafka_args, **kwargs)\n    except Exception as e:\n      logging.error(str(e))\n      logging.info(\"it's not streaming training\")\n\n    tf_record_args = {\n        'file_name', 'compression_type', 'buffer_size', 'num_parallel_reads'\n    }\n\n    def is_kafka_dataset():\n      # 'topics', 'group_id' and 'servers' are for KafkaDataset\n      return 'topics' in kwargs and 'group_id' in kwargs and 'servers' in kwargs\n\n    if args is None or len(args) == 0:  # all arguments are in kwargs\n      # 'patterns' for DistributedFilePBDataset\n      if 'patterns' in kwargs and not is_kafka_dataset():\n        logging.info('use DistributedFilePBDataset!')\n        return DistributedFilePBDataset(**kwargs)\n      elif is_kafka_dataset():\n        logging.info('use KafkaDataset!')\n        return KafkaDataset(**kwargs)\n      elif kwargs.get('use_parquet'):\n        return ParquetDataset(**kwargs)\n      elif kwargs.get('use_tfrecord'):\n        logging.info('use TFRecordDataset!')\n        invalid_args = list(k for k in kwargs if k not in tf_record_args)\n        for k in invalid_args:\n          kwargs.pop(k)\n        logging.info('---kwargs: %s', kwargs)\n        return TFRecordDatasetWrapper(**kwargs)\n      elif 'file_name' in kwargs or len(kwargs) == 0:\n        return FilePBDataset(*args, **kwargs)\n      else:\n        return super(DatasetMetaclass, cls).__call__(*args, **kwargs)\n    elif isinstance(args[0], str):  # The first arg is a filename\n      if kwargs.get('use_parquet'):\n        return ParquetDataset(*args, **kwargs)\n      elif kwargs.get('use_tfrecord'):\n        logging.info('use TFRecordDataset!')\n        invalid_args = list(k for k in kwargs if k not in tf_record_args)\n        for k in invalid_args:\n          kwargs.pop(k)\n        logging.info('---kwargs: %s', kwargs)\n        return TFRecordDatasetWrapper(*args, **kwargs)\n      else:\n        logging.info('use FilePBDataset!')\n        return FilePBDataset(*args, **kwargs)\n    elif isinstance(args[0], (list, tuple)):  # The first arg is a list, never reach here\n      if len(args) > 1:\n        if isinstance(args[1], str):\n          logging.info('use KafkaDataset!')\n          return KafkaDataset(*args, **kwargs)\n        else:\n          logging.info('use DistributedFilePBDataset!')\n          return DistributedFilePBDataset(*args, **kwargs)\n      else:\n        if 'group_id' in kwargs or 'servers' in kwargs:\n          logging.info('use KafkaDataset!')\n          return KafkaDataset(*args, **kwargs)\n        else:\n          logging.info('use DistributedFilePBDataset!')\n          return DistributedFilePBDataset(*args, **kwargs)\n    else:\n      return super(DatasetMetaclass, cls).__call__(*args, **kwargs)\n\n\nclass PBDataset(metaclass=DatasetMetaclass):\n\n  def __init__(\n      self,\n      topics_or_files: Union[str, List[str]] = '',\n      buffer_size_or_group_id: Union[int, str] = None,\n      input_pb_type_or_servers: Union[PbType, str] = None,\n      output_pb_type: PbType = None,\n      feature_pruning_type: int = FeaturePruningType.PRUNING_RAW_FEATURE,\n      disable_iterator_save_restore: bool = True,\n      *,\n      has_header=True,\n      variant_type: str = None,\n      stream_timeout=-1,\n      message_poll_timeout=10000,\n      poll_batch_size: int = None,\n      filter_empty: bool = False,\n      configuration=None,\n      container: str = '',\n      shared_name: str = '',\n      cycle_length=None,\n      block_length=None,\n      num_parallel_calls=None,\n      deterministic=None,\n      **kwargs):\n    pass\n\n  @classmethod\n  def gen_patterns(cls,\n                   input_path: str = None,\n                   start_date: int = None,\n                   start_hour: int = None,\n                   end_date: int = None,\n                   end_hour: int = None,\n                   is_hourly: bool = False,\n                   wildcard: str = '*') -> List[str]:\n    input_path = input_path or _get_params('input_path', None)\n    if not input_path:\n      return []\n\n    start_date = start_date or _get_params('start_date', None)\n    if not start_date:\n      return []\n    end_date = end_date or _get_params('end_date', None)\n    if not end_date:\n      end_date = datetime.today().strftime('%Y%m%d')\n\n    is_hourly = is_hourly if is_hourly is not None else _get_params(\n        'is_hourly', False)\n    start_hour = start_hour or _get_params('start_hour', 0) or 0\n    end_hour = end_hour or _get_params('end_hour', 0) or 0\n    wildcard = wildcard or _get_params('wildcard', '*')\n    start = datetime.strptime(f'{start_date}:{start_hour:02d}', '%Y%m%d:%H')\n    if is_hourly:\n      end = datetime.strptime(f'{end_date}:{end_hour:02d}', '%Y%m%d:%H')\n    else:\n      end = datetime.strptime(f'{end_date}:00', '%Y%m%d:%H')\n\n    delta = timedelta(hours=1) if is_hourly else timedelta(days=1)\n\n    cur = start\n    patterns = []\n    while cur < end:\n      if is_hourly:\n        pat = f\"{cur.strftime('%Y%m%d/%H')}{wildcard}\"\n      else:\n        pat = os.path.join(cur.strftime('%Y%m%d'), wildcard)\n      patterns.append(os.path.join(input_path, pat))\n      cur = cur + delta\n\n    return patterns\n\n\nclass DynamicMatchingFilesDataset(dataset_ops.DatasetSource):\n  \"\"\"A `Dataset` that list the files according to the input patterns.\"\"\"\n\n  def __init__(self, patterns: List[str]):\n    assert patterns is not None and len(patterns) > 0\n    self._patterns = ops.convert_to_tensor(patterns,\n                                           dtype=dtypes.string,\n                                           name=\"patterns\")\n    variant_tensor = pb_datasource_ops.dynamic_matching_files_dataset(\n        self._patterns)\n    super(DynamicMatchingFilesDataset, self).__init__(variant_tensor)\n\n  @property\n  def element_spec(self):\n    return tensor_spec.TensorSpec([], dtypes.string)\n\n\nclass TFRecordDatasetWrapper(tf.data.TFRecordDataset):\n\n  def __init__(self,\n               file_name,\n               compression_type=None,\n               buffer_size=None,\n               num_parallel_reads=None,\n               **kwargs):\n    super().__init__(file_name,\n                     compression_type=compression_type,\n                     buffer_size=buffer_size,\n                     num_parallel_reads=num_parallel_reads)\n\n\nclass ParquetDataset(dataset_ops.DatasetSource):\n\n  def __init__(self,\n               file_name,\n               output_pb_type: PbType,\n               select_columns: List[str],\n               select_columns_type: List[str],\n               batch_size=512,\n               drop_remainder=True,\n               **kwargs):\n    # assert isinstance(file_name, str)\n    assert output_pb_type in [\n        PbType.EXAMPLE, PbType.EXAMPLEBATCH, PbType.PLAINTEXT\n    ]\n    assert output_pb_type != 'example_batch' or (isinstance(batch_size, int) and\n                                                 batch_size > 0)\n    batch_size = 0 if output_pb_type == 'example' else batch_size\n    assert isinstance(select_columns, list) and all(\n        isinstance(c, str) for c in select_columns)\n    assert isinstance(select_columns_type, list) and all(\n        t in [\"int\", \"fid_v1\", \"fid_v2\", \"float\"] for t in select_columns_type)\n    for feature in select_columns:\n      add_feature(feature)\n\n    if output_pb_type == PbType.EXAMPLEBATCH and batch_size > 0 and drop_remainder:\n      get_default_parser_ctx().set('batch_size', batch_size)\n\n    self._out_type = tf.string if output_pb_type == PbType.PLAINTEXT else tf.variant\n\n    tf.compat.v1.add_to_collection(name=OUTPUT_PB_TYPE_GRAPH_KEY,\n                                   value=output_pb_type.to_name())\n    variant_tensor = pb_datasource_ops.parquet_dataset(\n        file_name=file_name,\n        output_pb_type=output_pb_type.to_name(),\n        batch_size=batch_size,\n        select_columns=select_columns,\n        select_columns_type=select_columns_type,\n        drop_remainder=drop_remainder)\n\n    super().__init__(variant_tensor)\n\n  @property\n  def element_spec(self):\n    return tensor_spec.TensorSpec([], self._out_type)\n\n\n@monolith_export\nclass CompressType(Enum):\n  UNKNOW = 0\n  NO = 1\n  SNAPPY = 2\n  ZSTD = 3\n  ZLIB = 4\n  GZIP = 5\n\n\n@monolith_export\nclass FilePBDataset(dataset_ops.DatasetSource):\n  \"\"\"从标准输入/pb文件中读取序列化数据, 并将其反序列化存于TF的Variant类型中. 这样做的好处是可以直接对PB对象进行过滤与修改, \n  不用等到parse以后. Monolith提供了一系列工具操作Variant变量, 如filter_by_fids, filter_by_value, negative_sample等\n  \n  另外, InstanceReweightDataset/NegativeGenDataset 这些DataSet也可以直接作用于Variant\n\n  Args:\n    file_name (:obj:`str`): 文件名, 如果为空, 则从stdin读取数据\n    buffer_size (:obj:`int`): 读取文件时缓存大小, 默认100MB\n    input_pb_type (:obj:`str`): 输入pb类型, 可以是example/example_batch/instance\n    output_pb_type (:obj:`str`): 输入pb类型, 可以是example/instance/plaintext\n    \n  Raises:\n    TypeError: 如果有任何参数与类型不匹配, 则抛TypeError\n    ValueError: 如果有任何值与期望不匹配, 则抛ValueError\n  \n  \"\"\"\n\n  def __init__(\n      self,\n      file_name: str = \"\",\n      buffer_size: int = None,\n      input_pb_type: PbType = None,\n      output_pb_type: PbType = None,\n      feature_pruning_type: int = FeaturePruningType.PRUNING_RAW_FEATURE,\n      disable_iterator_save_restore: bool = True,\n      use_snappy: bool = None,\n      compression_type: CompressType = CompressType.UNKNOW,\n      **kwargs):\n\n    input_pb_type = input_pb_type or _get_params('data_type', PbType.INSTANCE)\n    output_pb_type = output_pb_type or (PbType.INSTANCE if input_pb_type\n                                        == PbType.INSTANCE else PbType.EXAMPLE)\n\n    feature_name_list = []\n    feature_id_list = []\n    if input_pb_type in [PbType.EXAMPLEBATCH, PbType.EXAMPLE]:\n      try:\n        feature_list = FeatureList.parse()\n        for feature in feature_list:\n          name, slot = feature.feature_name, feature.slot\n          assert None not in [name, slot]\n          feature_name_list.append(name)\n          feature_id_list.append(slot)\n      except Exception as e:\n        logging.warning('Failed to parse feature_list.conf, %s', e)\n\n    self._file_name = file_name\n    self._buffer_size = buffer_size\n    self._input_pb_type = input_pb_type\n    self._output_pb_type = output_pb_type\n    self._out_type = tf.string if output_pb_type == PbType.PLAINTEXT else tf.variant\n\n    self._has_sort_id = kwargs.get('has_sort_id', _get_params('sort_id', True))\n    self._kafka_dump = kwargs.get('kafka_dump',\n                                  _get_params('kafka_dump', False))\n    logging.info('input_pb_type: %s, kafka_dump: %s, output_pb_type: %s',\n                 self._input_pb_type, self._kafka_dump, self._output_pb_type)\n    self._kafka_dump_prefix = kwargs.get(\n        'kafka_dump_prefix', _get_params('kafka_dump_prefix', False))\n    self._lagrangex_header = kwargs.get('lagrangex_header',\n                                        _get_params('lagrangex_header', False))\n\n    if disable_iterator_save_restore and isinstance(file_name, str):\n      # This is the special case that dataset uses stdin as the input.\n      # In this case, we should diable the ckpt save/restore.\n      if context.default_execution_mode == context.GRAPH_MODE:\n        ckpt_hooks.disable_iterator_save_restore()\n\n    default_buffer_size = 128 * 1024 * 1024 if input_pb_type == PbType.EXAMPLEBATCH else 64 * 1024 * 1024\n\n    logging.info(\n        f\"FilePBDataset input compression_type: {compression_type} {FLAGS.dataset_input_compression_type} {use_snappy} {FLAGS.dataset_input_use_snappy}\"\n    )\n    if compression_type == CompressType.UNKNOW and FLAGS.dataset_input_compression_type is not None:\n      compression_type = CompressType[\n          FLAGS.dataset_input_compression_type.upper()]\n      logging.info(f\"FilePBDataset change compression_type {compression_type}\")\n\n    logging.info(f\"FilePBDataset compression_type {compression_type}\")\n    use_snappy = use_snappy or FLAGS.dataset_input_use_snappy\n    if use_snappy is None:\n      if isinstance(file_name, str) and file_name.endswith('.snappy'):\n        use_snappy = True\n        logging.info(f\"FilePBDataset change use_snappy {use_snappy}\")\n    if use_snappy is None:\n      use_snappy = False\n\n    tf.compat.v1.add_to_collection(name=OUTPUT_PB_TYPE_GRAPH_KEY,\n                                   value=output_pb_type.to_name())\n    variant_tensor = pb_datasource_ops.pb_dataset(\n        file_name=file_name,\n        use_snappy=use_snappy,\n        buffer_size=buffer_size or default_buffer_size,\n        input_pb_type=input_pb_type.to_name(),\n        output_pb_type=output_pb_type.to_name(),\n        has_sort_id=self._has_sort_id,\n        kafka_dump=self._kafka_dump,\n        kafka_dump_prefix=self._kafka_dump_prefix,\n        lagrangex_header=self._lagrangex_header,\n        feature_pruning_type=feature_pruning_type,\n        feature_name_list=feature_name_list,\n        feature_id_list=feature_id_list,\n        out_type=self._out_type,\n        compression_type=compression_type.value,\n    )\n    logging.info(\"Start init of the pb instance dataset base.\")\n    super().__init__(variant_tensor)\n\n  @property\n  def element_spec(self):\n    return tensor_spec.TensorSpec([], self._out_type)\n\n\nclass DistributedFilePBDataset(dataset_ops.DatasetSource):\n\n  def __init__(\n      self,\n      patterns: Union[str, List[str]],\n      buffer_size: int = None,\n      input_pb_type: PbType = None,\n      output_pb_type: PbType = None,\n      feature_pruning_type: int = FeaturePruningType.PRUNING_RAW_FEATURE,\n      exclude_fn: Callable[[tf.Tensor], bool] = None,\n      cycle_length=2,\n      block_length=None,\n      num_parallel_calls=tf.data.AUTOTUNE,\n      deterministic=None,\n      use_parquet: bool = False,\n      use_tfrecord: bool = False,\n      **kwargs):\n    if not patterns:\n      patterns = [\"\"]\n    elif isinstance(patterns, str):\n      patterns = [patterns]\n    else:\n      logging.info(\n          f'patterns: len {len(patterns)}, frist is {patterns[0]}, last is {patterns[-1]}'\n      )\n    patterns.sort()\n    enable_dynamic_sharding = kwargs.get(\n        'enable_dynamic_sharding', _get_params('enable_dynamic_sharding',\n                                               False))\n    logging.info(f\"enable_dynamic_sharding: {enable_dynamic_sharding}\")\n\n    assert not (\n        use_parquet and use_tfrecord\n    ), \"It's not allowed to specify use_parquet=True and use_tfrecord=True simultaneously!\"\n    if use_parquet:\n      map_func = lambda file_name: ParquetDataset(\n          file_name=file_name, output_pb_type=output_pb_type, **kwargs)\n    elif use_tfrecord:\n      map_func = lambda file_name: tf.data.TFRecordDataset(filenames=\n                                                           [file_name])\n    else:\n      map_func = lambda file_name: FilePBDataset(\n          file_name=file_name,\n          buffer_size=buffer_size,\n          input_pb_type=input_pb_type,\n          output_pb_type=output_pb_type,\n          feature_pruning_type=feature_pruning_type,\n          disable_iterator_save_restore=not enable_dynamic_sharding,\n          **kwargs)\n    graph = tf.compat.v1.get_default_graph()\n    if FLAGS.data_service_dispatcher and not hasattr(graph, 'dry_run'):\n      files_list = DynamicMatchingFilesDataset(patterns)\n      # files_list = tf.data.Dataset.from_tensor_slices(patterns)\n      if exclude_fn is not None:\n        files_list = files_list.filter(predicate=exclude_fn)\n      dataset = files_list.interleave(map_func,\n                                      cycle_length=cycle_length,\n                                      block_length=block_length,\n                                      num_parallel_calls=num_parallel_calls,\n                                      deterministic=deterministic)\n    elif enable_dynamic_sharding:\n      files_list = distributed_dataset.create_dynamic_sharding_dataset(patterns)\n      if exclude_fn is not None:\n        files_list = files_list.filter(predicate=exclude_fn)\n      dataset = files_list.flat_map(map_func)\n    else:\n      files_list = matching_files.MatchingFilesDataset(patterns)\n      if exclude_fn is not None:\n        files_list = files_list.filter(predicate=exclude_fn)\n      ctx = native_task_context.get()\n      if ctx is not None:\n        if ctx.num_workers > 1:\n          files_list = files_list.shard(ctx.num_workers, ctx.worker_index)\n      else:\n        shard_num = kwargs.get('shard_num', 1)\n        shard_index = kwargs.get('shard_index', 0)\n        if shard_num > 1:\n          files_list = files_list.shard(shard_num, shard_index)\n\n      cycle_length = kwargs.get('cycle_length',\n                                _get_params('max_task_num_per_worker', 4))\n      num_parallel_calls = kwargs.get('num_parallel_calls',\n                                      _get_params('max_task_num_per_worker', 4))\n      block_length = kwargs.get('block_length', _get_params('block_length', 1))\n      dataset = files_list.interleave(map_func=map_func,\n                                      cycle_length=cycle_length,\n                                      block_length=block_length,\n                                      num_parallel_calls=num_parallel_calls,\n                                      deterministic=False)\n    self._dataset = dataset\n    super(DistributedFilePBDataset,\n          self).__init__(variant_tensor=self._dataset._variant_tensor)\n\n  @property\n  def element_spec(self):\n    return self._dataset.element_spec\n\n\n@monolith_export\nclass InstanceReweightDataset(dataset_ops.UnaryUnchangedStructureDataset):\n  \"\"\"样本重加权, 并根据action给样本打标签, 使用方式为 dataset.instance_reweight\n  \n  一个样本可能有多个action, 按`action_priority`, 找到最高优的action. 再用action找到对应的 `action:weight:label`, \n  让样本重复weight次(也有可能是0次, 即删除样本), 然后给样本打上label指定的标签 \n\n  Args:\n    input_dataset (:obj:`dataset`): 输入数据集\n    action_priority (:obj:`str`): action用int表示, 以逗号分隔的int数组, 排在前面的优先级高\n    reweight (:obj:`str`): 基本单元是`action:weight:label`, 可以用逗号分隔多个基本单元\n      1) action: 动作, 用int表示, 与业务相关, 如download, install, click, exposure等\n      2) weight: 权重, 用int表示, 表示样本重复的次数\n      3) label: 标签, 一般用1/-1表示. \n    variant_type (:obj:`str`): 输入数据是variant类型的, 支持两种格式, instance/example\n    \n  Raises:\n    TypeError: 如果有任何参数与类型不匹配, 则抛TypeError\n    ValueError: 如果有任何值与期望不匹配, 则抛ValueError\n  \n  \"\"\"\n\n  def __init__(self,\n               input_dataset,\n               action_priority: str = None,\n               reweight: str = None,\n               variant_type: str = 'example'):\n    self._label_priority = action_priority\n    self._reweight = reweight\n    self._variant_type = variant_type\n\n    actions, weights, labels = [], [], []\n    for item in reweight.strip().split(','):\n      (action, weight, label) = item.strip().split(':')\n      actions.append(int(action))\n      weights.append(int(weight))\n      labels.append(int(label))\n\n    priorities = [int(p) for p in action_priority.strip().split(',')]\n    variant_tensor = pb_datasource_ops.instance_reweight_dataset(\n        input=input_dataset._variant_tensor,\n        method=0,\n        actions=actions,\n        weights=weights,\n        labels=labels,\n        priorities=priorities,\n        variant_type=variant_type)\n    logging.info(\"Start init of the pb instance dataset base.\")\n    super(InstanceReweightDataset, self).__init__(input_dataset, variant_tensor)\n\n  @property\n  def element_spec(self):\n    return tensor_spec.TensorSpec([], dtypes.variant)\n\n\n@monolith_export\nclass NegativeGenDataset(dataset_ops.UnaryUnchangedStructureDataset):\n  \"\"\"负例生成. 有时, 样本中只有正例, 没有负例, 需要随机生成负例\n  \n  推荐系统中的样本通常是由user侧, item侧两部分组成. 这里的做法是: \n    - 先收集每个样本的item侧信息, 生成一个item池子\n    - item池子并不是平铺的, 而是按某个特征(channel_slot)分类组织的. 如果在同一个channel随机取item得到的是hard负例, 在其它channel中抽样得到的是easy负例\n    - 并不是一开始就生成负例, 而是要等item池子积累到一定大小才开始生成负例\n\n  Args:\n    input_dataset (:obj:`dataset`): 输入数据集\n    neg_num (:obj:`int`): 为一个正例生成`neg_num`个负例\n    channel_feature (:obj:`string`): 用于当item分类的字段\n    per_channel (:obj:`bool`): 是否分类 \n    start_num (:obj:`int`): 在item池子中积累多少个后才开始采样\n    max_iten_num (:obj:`int`): 每一个channel最多收集多注个item\n    item_features: (:obj:`List[str]`): item侧的特征名列表\n    positive_label: 正例的label, 仅为正例生成负例\n    negative_label: 生成的负例的被打上的label\n    easy_hard_ratio: (:obj:`float`): 当使用 per_channel 的时候, hard和easy负例之间的比例。取值在 0 ~ 1 之间。举例：0.8就是大致80% easy负例\n\n  Raises:\n    TypeError: 如果有任何参数与类型不匹配, 则抛TypeError\n    ValueError: 如果有任何值与期望不匹配, 则抛ValueError\n  \n  \"\"\"\n\n  def __init__(self,\n               input_dataset,\n               neg_num: int,\n               per_channel: bool = False,\n               channel_feature: Union[int, str] = '',\n               item_features: Union[List[int], List[str]] = [],\n               start_num: int = 500,\n               max_item_num: int = 100000,\n               positive_label: int = 1,\n               negative_label: int = -1,\n               negative_action: int = -99999,\n               positive_actions: List[int] = [],\n               label_index: int = 0,\n               action_priority: str = '',\n               index_feature: Union[int, str] = '',\n               throw_origin: bool = False,\n               throw_origin_neg: bool = False,\n               cache_only_pos: bool = True,\n               cache_negative_actions: List[int] = [],\n               real_neg_instance_weight: float = 1.0,\n               sampled_neg_instance_weight: float = -1.0,\n               unbias_sampled_neg: bool = True,\n               origin_neg_in_pool_proba: float = 1.0,\n               neg_sample_declay_factor: float = 1.0,\n               easy_hard_ratio: float = 0.0,\n               variant_type: str = 'example'):\n    pool = create_item_pool(start_num=start_num,\n                            max_item_num_per_channel=max_item_num)\n    tf.compat.v1.add_to_collection(POOL_KEY, pool)\n    channel_feature = str(channel_feature)\n    item_features = [str(item) for item in item_features]\n    action_priority_items = action_priority.strip().split(',')\n    assert len(action_priority_items) == len(set(action_priority_items))\n    index_feature = str(index_feature)\n    assert variant_type in {'instance', 'example'}\n    assert label_index >= 0\n\n    assert isinstance(cache_negative_actions, list) and \\\n      all(isinstance(x, int) for x in cache_negative_actions)\n    assert len(set(positive_actions) & set(cache_negative_actions)) == 0, \\\n      \"positive_actions and cache_negative_actions have intersection, pls check\"\n\n    variant_tensor = pb_datasource_ops.instance_negative_gen_dataset(\n        input=input_dataset._variant_tensor,\n        pool=pool,\n        neg_num=neg_num,\n        per_channel=per_channel,\n        channel_feature=channel_feature,\n        item_features=item_features,\n        label_index=label_index,\n        positive_label=positive_label,\n        negative_label=negative_label,\n        negative_action=negative_action,\n        action_priority=action_priority,\n        positive_actions=positive_actions,\n        index_feature=index_feature,\n        throw_origin=throw_origin,\n        throw_origin_neg=throw_origin_neg,\n        cache_only_pos=cache_only_pos,\n        cache_negative_actions=cache_negative_actions,\n        real_neg_instance_weight=real_neg_instance_weight,\n        sampled_neg_instance_weight=sampled_neg_instance_weight,\n        unbias_sampled_neg=unbias_sampled_neg,\n        origin_neg_in_pool_proba=origin_neg_in_pool_proba,\n        neg_sample_declay_factor=neg_sample_declay_factor,\n        easy_hard_ratio=easy_hard_ratio,\n        variant_type=variant_type)\n    super(NegativeGenDataset, self).__init__(input_dataset, variant_tensor)\n\n  @property\n  def element_spec(self):\n    return tensor_spec.TensorSpec([], dtypes.variant)\n\n\ndef instance_reweight(self, action_priority: str, reweight: str, **kwargs):\n  value = tf.compat.v1.get_collection(OUTPUT_PB_TYPE_GRAPH_KEY)\n  assert len(value) == 1\n  variant_type = value[0]\n  assert variant_type in {\"instance\", \"example\"}\n  return InstanceReweightDataset(self,\n                                 action_priority,\n                                 reweight,\n                                 variant_type=variant_type)\n\n\n@monolith_export\nclass CacheOneDataset(dataset_ops.UnaryDataset):\n\n  def __init__(self, input_dataset):\n    self._input_dataset = input_dataset\n    variant_tensor = pb_datasource_ops.monolith_cache_one_dataset(\n        input_dataset._variant_tensor)\n\n    super().__init__(input_dataset, variant_tensor)\n\n  @property\n  def element_spec(self):\n    return (self._input_dataset.element_spec,\n            tensor_spec.TensorSpec([], dtypes.bool))\n\n\n@monolith_export\nclass SplitFlowDataset(dataset_ops.UnaryUnchangedStructureDataset):\n\n  def __init__(self,\n               input_dataset,\n               data_flow: List[str],\n               index: int,\n               max_queue_size: int = 1024,\n               variant_type: str = 'example'):\n    variant_tensor = pb_datasource_ops.split_flow_dataset(\n        input_dataset._variant_tensor,\n        data_flow=data_flow,\n        index=index,\n        max_queue_size=max_queue_size,\n        variant_type=variant_type)\n    super(SplitFlowDataset, self).__init__(input_dataset, variant_tensor)\n\n  @property\n  def element_spec(self):\n    return tensor_spec.TensorSpec([], dtypes.variant)\n\n\n@monolith_export\nclass MergeFlowDataset(dataset_ops.DatasetV2):\n\n  def __init__(self,\n               input_dataset,\n               dataset_to_merge,\n               max_queue_size: int = 1024,\n               variant_type: str = 'example'):\n    self._input_dataset = input_dataset\n    self._dataset_to_merge = dataset_to_merge\n\n    output_types = dataset_ops.get_legacy_output_types(input_dataset)\n    for ds in dataset_to_merge:\n      ds_types = dataset_ops.get_legacy_output_types(ds)\n      if output_types != ds_types:\n        raise TypeError(\"Datasets to merge have different types %s and %s\" %\n                        (output_types, ds_types))\n\n    input_shapes = dataset_ops.get_legacy_output_shapes(input_dataset)\n\n    flat_sequence = None\n    input_shapes_flatten = nest.flatten(input_shapes)\n    for ds in dataset_to_merge:\n      ds_shapes_flatten = nest.flatten(dataset_ops.get_legacy_output_shapes(ds))\n      if flat_sequence is None:\n        flat_sequence = [\n            ts1.most_specific_compatible_shape(ts2)\n            for (ts1, ts2) in zip(input_shapes_flatten, ds_shapes_flatten)\n        ]\n      else:\n        tmp = [\n            ts1.most_specific_compatible_shape(ts2)\n            for (ts1, ts2) in zip(input_shapes_flatten, ds_shapes_flatten)\n        ]\n        assert all(ts1 == ts2 for (ts1, ts2) in zip(flat_sequence, tmp))\n    output_shapes = nest.pack_sequence_as(input_shapes, flat_sequence)\n\n    output_classes = dataset_ops.get_legacy_output_classes(input_dataset)\n    for ds in dataset_to_merge:\n      ds_classes = dataset_ops.get_legacy_output_classes(ds)\n      if output_classes != ds_classes:\n        raise TypeError(\"Datasets to merge have different classes %s and %s\" %\n                        (output_classes, ds_classes))\n\n    self._structure = structure.convert_legacy_structure(\n        output_types, output_shapes, output_classes)\n\n    self._input_datasets = [input_dataset] + dataset_to_merge\n    input_dataset_variant = [ds._variant_tensor for ds in self._input_datasets]\n    data_flow = ['input_ds'] + [\n        'ds_to_merge_{}'.format(i + 1)\n        for i in range(len(self._dataset_to_merge))\n    ]\n\n    variant_tensor = pb_datasource_ops.merge_flow_dataset(\n        input_dataset_variant,\n        data_flow=data_flow,\n        max_queue_size=max_queue_size,\n        variant_type=variant_type)\n    super(MergeFlowDataset, self).__init__(variant_tensor)\n\n  def _inputs(self):\n    return self._input_datasets\n\n  @property\n  def element_spec(self):\n    return self._structure\n\n\ndef negative_gen(self,\n                 neg_num: int,\n                 per_channel: bool = False,\n                 channel_feature: Union[int, str] = '',\n                 item_features: Union[List[int], List[str]] = [],\n                 start_num: int = 500,\n                 max_item_num: int = 100000,\n                 positive_label: int = 1,\n                 negative_label: int = -1,\n                 negative_action: int = -99999,\n                 positive_actions: List[int] = [],\n                 label_index: int = 0,\n                 action_priority: str = '',\n                 index_feature: Union[int, str] = '',\n                 throw_origin: bool = False,\n                 throw_origin_neg: bool = False,\n                 cache_only_pos: bool = False,\n                 cache_negative_actions: List[int] = [],\n                 real_neg_instance_weight: float = 1.0,\n                 sampled_neg_instance_weight: float = -1.0,\n                 unbias_sampled_neg: bool = True,\n                 origin_neg_in_pool_proba: float = 1.0,\n                 neg_sample_declay_factor: float = 1.0,\n                 easy_hard_ratio: float = 0.0,\n                 **kwargs):\n  value = tf.compat.v1.get_collection(OUTPUT_PB_TYPE_GRAPH_KEY)\n  assert len(value) == 1\n  variant_type = value[0]\n  assert variant_type in {\"instance\", \"example\"}\n  return NegativeGenDataset(\n      self,\n      neg_num=neg_num,\n      per_channel=per_channel,\n      channel_feature=channel_feature,\n      item_features=item_features,\n      start_num=start_num,\n      max_item_num=max_item_num,\n      label_index=label_index,\n      positive_label=positive_label,\n      negative_label=negative_label,\n      negative_action=negative_action,\n      action_priority=action_priority,\n      positive_actions=positive_actions,\n      index_feature=index_feature,\n      throw_origin=throw_origin,\n      throw_origin_neg=throw_origin_neg,\n      cache_only_pos=cache_only_pos,\n      cache_negative_actions=cache_negative_actions,\n      real_neg_instance_weight=real_neg_instance_weight,\n      sampled_neg_instance_weight=sampled_neg_instance_weight,\n      unbias_sampled_neg=unbias_sampled_neg,\n      origin_neg_in_pool_proba=origin_neg_in_pool_proba,\n      neg_sample_declay_factor=neg_sample_declay_factor,\n      easy_hard_ratio=easy_hard_ratio,\n      variant_type=variant_type)\n\n\ndef split_flow(self,\n               data_flow: List[str],\n               index: int,\n               max_queue_size: int = 1024,\n               **kwargs):\n  value = tf.compat.v1.get_collection(OUTPUT_PB_TYPE_GRAPH_KEY)\n  assert len(value) == 1\n  variant_type = value[0]\n  assert variant_type in {\"instance\", \"example\"}\n  return SplitFlowDataset(self,\n                          data_flow=data_flow,\n                          index=index,\n                          max_queue_size=max_queue_size,\n                          variant_type=variant_type)\n\n\ndef merge_flow(self, dataset_to_merge, max_queue_size: int = 1024, **kwargs):\n  value = tf.compat.v1.get_collection(OUTPUT_PB_TYPE_GRAPH_KEY)\n  assert len(value) == 1\n  variant_type = value[0]\n  assert variant_type in {\"instance\", \"example\"}\n  return MergeFlowDataset(self,\n                          dataset_to_merge,\n                          max_queue_size=max_queue_size,\n                          variant_type=variant_type)\n\n\nclass KafkaGen(object):\n\n  def __init__(self,\n               topics: List[str],\n               group_id: str,\n               servers: Union[str, List[str]],\n               stream_timeout: int = -1,\n               message_poll_timeout: int = 10000,\n               poll_batch_size: int = 1024):\n    if stream_timeout == -1:\n      stream_timeout = sys.maxsize\n    elif stream_timeout >= 0:\n      stream_timeout = max(stream_timeout, message_poll_timeout)\n    else:\n      raise ValueError('stream_timeout must bigger then -1')\n\n    if isinstance(topics, str):\n      topics = [topics]\n\n    self.topics, self.group_id, self.servers = topics, group_id, servers\n    self._lock = RLock()\n    self._stop_iteration = False  # lock\n    self._consumer: KafkaConsumer = None  # lock\n    self._queue = Queue(maxsize=1024)\n    self.message_poll_timeout = message_poll_timeout\n    self.poll_batch_size = poll_batch_size\n    self._max_stream_timeout_polls = int(stream_timeout / message_poll_timeout)\n    self._stream_timeout_polls = -1\n\n  @property\n  def consumer(self):\n    with self._lock:\n      if self._consumer is None:\n        self._consumer = KafkaConsumer(*self.topics,\n                                       group_id=self.group_id,\n                                       bootstrap_servers=self.servers)\n        thread = Thread(target=self._poll)\n        thread.start()\n    return self._consumer\n\n  def __iter__(self):\n    return self\n\n  def __next__(self):\n    assert self.consumer is not None\n    while True:\n      data = self._queue.get(timeout=self.message_poll_timeout)\n      if data:\n        return data\n      with self._lock:\n        if self._stop_iteration:\n          raise StopIteration\n\n  def __call__(self):\n    return self\n\n  def _poll(self):\n    while self._stream_timeout_polls < self._max_stream_timeout_polls:\n      try:\n        msg = self._consumer.poll(timeout_ms=self.message_poll_timeout,\n                                  max_records=self.poll_batch_size,\n                                  update_offsets=True)\n        if msg:\n          poll_values = []\n          for part, values in msg.items():\n            part_vals = [value.value for value in values if value.value]\n            if part_vals:\n              poll_values.extend(part_vals)\n          if poll_values:\n            self._stream_timeout_polls = 0\n            self._queue.put(poll_values)\n          else:\n            self._stream_timeout_polls += 1\n            continue\n        else:\n          self._stream_timeout_polls += 1\n      except Exception as e:\n        logging.error(f'poll error: {e}')\n        break\n\n    with self._lock:\n      self._consumer.close()\n      self._stop_iteration = True\n\n\nclass PyKafkaDataset(dataset_ops.DatasetSource):\n\n  def __init__(self,\n               topics,\n               group_id,\n               servers,\n               *,\n               has_header=True,\n               variant_type: str = None,\n               stream_timeout=-1,\n               message_poll_timeout=10000,\n               poll_batch_size: int = 1024,\n               filter_empty: bool = False,\n               **kwargs):\n    variant_type = variant_type or _get_params('data_type',\n                                               PbType.INSTANCE).to_name()\n    self._has_sort_id = kwargs.get('has_sort_id', _get_params('sort_id', False))\n    self._kafka_dump = kwargs.get('kafka_dump',\n                                  _get_params('kafka_dump', False))\n    logging.info(f'pb_type: {variant_type}, kafka_dump: {self._kafka_dump}')\n    self._kafka_dump_prefix = kwargs.get(\n        'kafka_dump_prefix', _get_params('kafka_dump_prefix', False))\n    self._lagrangex_header = kwargs.get('lagrangex_header',\n                                        _get_params('lagrangex_header', False))\n\n    if context.default_execution_mode == context.GRAPH_MODE:\n      ckpt_hooks.disable_iterator_save_restore()\n\n    kafka_gen = KafkaGen(topics, group_id, servers, stream_timeout,\n                         message_poll_timeout, poll_batch_size)\n    dataset = tf.data.Dataset.from_generator(generator=kafka_gen,\n                                             output_types=tf.string,\n                                             output_shapes=None)\n    dataset = dataset.map(\n        lambda v: string_to_variant(v,\n                                    variant_type=variant_type.lower(),\n                                    has_header=has_header,\n                                    lagrangex_header=self._lagrangex_header,\n                                    has_sort_id=self._has_sort_id,\n                                    kafka_dump=self._kafka_dump,\n                                    kafka_dump_prefix=self._kafka_dump_prefix),\n        num_parallel_calls=tf.data.AUTOTUNE)\n    dataset = dataset.prefetch(buffer_size=tf.data.AUTOTUNE).unbatch()\n    if filter_empty:\n      dataset = dataset.filter(predicate=lambda x: has_variant(\n          input=x, variant_type=variant_type.lower()))\n\n    self._dataset = dataset\n    super().__init__(self._dataset._variant_tensor)\n\n  @property\n  def element_spec(self):\n    return self._dataset.element_spec\n\n\ndef create_plain_kafka_dataset(topics: List[str],\n                               group_id: str,\n                               servers: str,\n                               stream_timeout=-1,\n                               message_poll_timeout=10000,\n                               poll_batch_size: int = 1024,\n                               configuration=None,\n                               container: str = '',\n                               shared_name: str = '',\n                               kafka_other_metadata: str = None):\n  metadata = list(configuration or [])\n  if group_id is not None:\n    metadata.append(f\"group.id={group_id}\")\n  if servers is not None:\n    metadata.append(f\"bootstrap.servers={servers}\")\n  if poll_batch_size is not None:\n    assert isinstance(poll_batch_size, int) and poll_batch_size > 0\n    metadata.append(f\"batch.num.messages={poll_batch_size}\")\n\n  if kafka_other_metadata:\n    kafka_other_metadata_list = kafka_other_metadata.split(',')\n    for meta in kafka_other_metadata_list:\n      metadata.append(meta)\n  resource = kafka_resource_init(topics=topics,\n                                 metadata=metadata,\n                                 container=container,\n                                 shared_name=shared_name)\n\n  dataset = tf.data.experimental.Counter()\n  dataset = dataset.map(lambda i: kafka_read_next(\n      input=resource,\n      index=i,\n      message_poll_timeout=message_poll_timeout,\n      stream_timeout=stream_timeout,\n  ))\n  dataset = dataset.apply(\n      tf.data.experimental.take_while(\n          lambda v: tf.greater(v.continue_fetch, 0)))\n  return dataset\n\n\nclass KafkaDataset(dataset_ops.DatasetSource):\n\n  def __init__(self,\n               topics: List[str],\n               group_id: str,\n               servers: str,\n               *,\n               has_header=True,\n               variant_type: PbType = None,\n               output_pb_type: PbType = None,\n               stream_timeout=-1,\n               message_poll_timeout=10000,\n               poll_batch_size: int = None,\n               filter_empty: bool = False,\n               configuration=None,\n               container: str = '',\n               shared_name: str = '',\n               kafka_other_metadata: str = None,\n               **kwargs):\n    variant_type = (variant_type or\n                    _get_params('data_type', PbType.INSTANCE)).to_name()\n    if output_pb_type is None:\n      output_pb_type = variant_type\n    else:\n      output_pb_type = output_pb_type.to_name()\n    self._out_type = tf.string if output_pb_type == PbType.PLAINTEXT else tf.variant\n\n    self._has_sort_id = kwargs.get('has_sort_id', _get_params('sort_id', False))\n    self._kafka_dump = kwargs.get('kafka_dump',\n                                  _get_params('kafka_dump', False))\n    logging.info(f'pb_type: {variant_type}, kafka_dump: {self._kafka_dump}')\n    self._kafka_dump_prefix = kwargs.get(\n        'kafka_dump_prefix', _get_params('kafka_dump_prefix', False))\n    self._lagrangex_header = kwargs.get('lagrangex_header',\n                                        _get_params('lagrangex_header', False))\n\n    if context.default_execution_mode == context.GRAPH_MODE:\n      ckpt_hooks.disable_iterator_save_restore()\n    self._chnids = kwargs.get('chnids', _get_params('chnids', None))\n    self._datasources = kwargs.get('datasources',\n                                   _get_params('datasources', None))\n    self._default_datasource = kwargs.get('default_datasource',\n                                          _get_params('default_datasource', ''))\n\n    with tf.name_scope(\"MonolithKafkaDataset\"):\n      if stream_timeout == -1:\n        stream_timeout = sys.maxsize\n      elif stream_timeout >= 0:\n        stream_timeout = max(stream_timeout, message_poll_timeout)\n      else:\n        raise ValueError(\n            f\"Invalid stream_timeout value: {stream_timeout} ,set it to -1 to block indefinitely.\"\n        )\n      metadata = list(configuration or [])\n      if group_id is not None:\n        metadata.append(f\"group.id={group_id}\")\n      if servers is not None:\n        metadata.append(f\"bootstrap.servers={servers}\")\n      if poll_batch_size is None:\n        if variant_type == \"examplebatch\":\n          poll_batch_size = 16\n        else:\n          poll_batch_size = 128\n      if poll_batch_size is not None:\n        assert isinstance(poll_batch_size, int) and poll_batch_size > 0\n        metadata.append(f\"batch.num.messages={poll_batch_size}\")\n      if kafka_other_metadata:\n        kafka_other_metadata_list = kafka_other_metadata.split(',')\n        for meta in kafka_other_metadata_list:\n          metadata.append(meta)\n\n      tf.compat.v1.add_to_collection(name=OUTPUT_PB_TYPE_GRAPH_KEY,\n                                     value=output_pb_type)\n      resource = kafka_resource_init(\n          topics=topics,\n          metadata=metadata,\n          input_pb_type=variant_type,  #\"\", step 1\n          output_pb_type=output_pb_type,  #\"\", step 2\n          has_sort_id=self._has_sort_id,\n          kafka_dump=self._kafka_dump,\n          kafka_dump_prefix=self._kafka_dump_prefix,\n          lagrangex_header=self._lagrangex_header,\n          container=container,\n          shared_name=shared_name)\n      self._resource = resource\n\n      dataset = tf.data.experimental.Counter()\n      dataset = dataset.map(\n          lambda i: kafka_read_next_v2(  #kafka_read_next step 3\n              input=self._resource,\n              index=i,\n              message_poll_timeout=message_poll_timeout,\n              stream_timeout=stream_timeout,\n          ))\n      dataset = dataset.apply(\n          tf.data.experimental.take_while(\n              lambda v: tf.greater(v.continue_fetch, 0)))\n      '''\n      dataset = dataset.map(lambda v: string_to_variant(\n          v.message,\n          variant_type=variant_type.lower(),\n          has_header=has_header,\n          lagrangex_header=self._lagrangex_header,\n          has_sort_id=self._has_sort_id,\n          kafka_dump=self._kafka_dump,\n          kafka_dump_prefix=self._kafka_dump_prefix,\n          chnids=self._chnids,\n          datasources=self._datasources,\n          default_datasource=self._default_datasource),\n                            num_parallel_calls=tf.data.AUTOTUNE)\n      '''\n      '''\n      # step 4 \n      dataset = dataset.flat_map(lambda v: tf.data.Dataset.from_tensors(\n          string_to_variant_with_transform(\n              v.message,\n              input_type=variant_type.lower(),\n              output_type=output_pb_type,\n              has_header=has_header,\n              lagrangex_header=self._lagrangex_header,\n              has_sort_id=self._has_sort_id,\n              kafka_dump=self._kafka_dump,\n              kafka_dump_prefix=self._kafka_dump_prefix,\n              chnids=self._chnids,\n              datasources=self._datasources,\n              default_datasource=self._default_datasource)))\n      '''\n      dataset = dataset.map(lambda v: v.message)\n      dataset = dataset.prefetch(buffer_size=tf.data.AUTOTUNE).unbatch()\n      if filter_empty:\n        dataset = dataset.filter(predicate=lambda x: has_variant(\n            input=x, variant_type=variant_type.lower()))\n\n      self._dataset = dataset\n      super().__init__(self._dataset._variant_tensor)\n\n  @property\n  def element_spec(self):\n    return tensor_spec.TensorSpec([], self._out_type)\n\n\ndef register_dataset(service, dataset, buffer_size=32):\n  protocol, address = _parse_service(service)\n  external_state_policy = dataset.options().experimental_external_state_policy\n  if external_state_policy is None:\n    external_state_policy = ExternalStatePolicy.WARN\n  logging.info('external_state_policy: %s', external_state_policy)\n  dataset = dataset.map(\n      lambda *x: compression_ops.compress(x),\n      # num_parallel_calls=dataset_ops.AUTOTUNE)\n      num_parallel_calls=None)\n  logging.info('num_parallel_calls: None')\n  # dataset = dataset.prefetch(buffer_size=buffer_size)\n  dataset = dataset._apply_options()\n\n  dataset_id = gen_experimental_dataset_ops.register_dataset(\n      dataset._variant_tensor,\n      address=address,\n      protocol=protocol,\n      external_state_policy=external_state_policy.value)\n\n  return dataset_id\n\n\ndef from_dataset_id(processing_mode,\n                    service,\n                    dataset_id,\n                    element_spec,\n                    job_name=None,\n                    max_outstanding_requests=None,\n                    task_refresh_interval_hint_ms=None,\n                    buffer_size: int = 16):\n  ProcessingMode.validate(processing_mode)\n  protocol, address = _parse_service(service)\n\n  dataset = _DataServiceDataset(\n      dataset_id=dataset_id,\n      processing_mode=processing_mode,\n      address=address,\n      protocol=protocol,\n      job_name=job_name,\n      max_outstanding_requests=max_outstanding_requests,\n      task_refresh_interval_hint_ms=task_refresh_interval_hint_ms)\n  dataset = dataset.prefetch(buffer_size=buffer_size).map(\n      lambda x: compression_ops.uncompress(x, output_spec=element_spec),\n      num_parallel_calls=dataset_ops.AUTOTUNE)\n\n  # Disable autosharding for shared jobs.\n  if job_name:\n    options = dataset_ops.Options()\n    options.experimental_distribute.auto_shard_policy = AutoShardPolicy.OFF\n    dataset = dataset.with_options(options)\n  return dataset\n\n\ndef merged_window(self: tf.data.Dataset,\n                  size: int = 2,\n                  drop_remainder: bool = True):\n  dataset = self.window(size=size, drop_remainder=drop_remainder)\n\n  def re_shape(ts: Union[tf.Tensor, tf.RaggedTensor]):\n    if isinstance(ts, tf.Tensor):\n      shape = ts._shape_as_list()\n      if shape:\n        if shape[0] is None or shape[1] is None:\n          shape[1] = -1\n        else:\n          shape[1] = shape[0] * shape[1]\n        del shape[0]\n        return tf.reshape(ts, shape=shape)\n      else:\n        return ts\n    else:\n      return ts.values\n\n  element_spec = self.element_spec\n  if isinstance(element_spec, (tf.TensorSpec, tf.RaggedTensor)):\n    return dataset.flat_map(map_func=lambda window: window.batch(\n        size, drop_remainder=drop_remainder).map(map_func=re_shape))\n  elif isinstance(element_spec, (tuple, list)):\n    return dataset.flat_map(map_func=lambda *window: tf.data.Dataset.zip(\n        tuple(\n            value.batch(size, drop_remainder=drop_remainder).map(\n                map_func=re_shape) for value in window)))\n  elif isinstance(element_spec, dict):\n    return dataset.flat_map(map_func=lambda window: tf.data.Dataset.zip({\n        key: value.batch(size, drop_remainder=drop_remainder).map(\n            map_func=re_shape) for key, value in window.items()\n    }))\n  else:\n    raise Exception(f\"element_spec {element_spec} is not support!\")\n\n\ndef distribute(self,\n               *,\n               target: str = None,\n               job_name: str = \"monolith_dataservice_task\",\n               num_worker: int = None,\n               worker_idx: int = None,\n               queue_device: str = \"/job:ps/task:0/device:CPU:0\",\n               max_outstanding_requests: int = dataset_ops.AUTOTUNE,\n               window_size: int = None):\n  graph = tf.compat.v1.get_default_graph()\n  if hasattr(graph, 'dry_run') or not FLAGS.data_service_dispatcher:\n    return self\n\n  if worker_idx is None:\n    worker_idx = FLAGS.dataset_worker_idx\n  if num_worker is None:\n    num_worker = FLAGS.dataset_num_workers\n  if target is None:\n    target = FLAGS.data_service_dispatcher\n\n  assert worker_idx is not None and num_worker is not None and target is not None\n\n  if max_outstanding_requests is None:\n    max_outstanding_requests = min(num_worker, 8)\n  if FLAGS.is_local:\n    dataset_id = register_dataset(target, self)\n    dataset = dsvc.from_dataset_id(\n        processing_mode=\"distributed_epoch\",\n        service=target,\n        dataset_id=dataset_id,\n        job_name=job_name,\n        element_spec=self.element_spec,\n        max_outstanding_requests=max_outstanding_requests)\n    return dataset\n  elif num_worker is None or num_worker <= 0:\n    logging.warning(f'num_worker is {num_worker}, error')\n    return self\n  elif worker_idx is None or worker_idx < 0:\n    logging.warning(f'worker_idx is {worker_idx}, error')\n    return self\n\n  try:\n    if FLAGS.kafka_topics is not None and FLAGS.kafka_group_id is not None:\n      return self\n  except Exception as e:\n    pass\n\n  logging.info(\n      f'dataset.distribute worker_idx {worker_idx}, num_worker {num_worker}, target {target}'\n  )\n  tf_config = os.environ.get('TF_CONFIG')\n  if tf_config is not None:\n    tf_config = json.loads(tf_config)\n    roles = set(map(lambda x: x.lower(), tf_config['cluster']))\n    if queue_device is None:\n      if 'ps' in roles:\n        queue_device = \"/job:ps/task:0/device:CPU:0\"\n      elif 'worker' in roles:\n        queue_device = \"/job:worker/task:0/device:CPU:0\"\n      else:\n        raise Exception('role error')\n\n  element_spec = self.element_spec\n  if enable_sync_training():\n    has_error = False\n    try:\n      enable_bps = int(os.getenv(\"MONOLITH_WITH_BYTEPS\", \"0\"))\n      if enable_bps:\n        import byteps.tensorflow as hvd\n      else:\n        import horovod.tensorflow as hvd\n    except (ImportError, tf.errors.NotFoundError) as e:\n      logging.info(f'ImportError is {e}')\n      has_error = True\n\n    if has_error:\n      dataset_id = register_dataset(target, self)\n    else:\n      dataset_id = tf.constant(value=1000,\n                               dtype=tf.int64,\n                               shape=tuple(),\n                               name='default_dataset_id')\n      if hvd.rank() == 0:\n        tf.compat.v1.add_to_collection(name=\"registed_dataset_id\",\n                                       value=register_dataset(target, self))\n      else:\n        tf.compat.v1.add_to_collection(name=\"registed_dataset_id\",\n                                       value=dataset_id)\n\n    dataset = dsvc.from_dataset_id(\n        processing_mode=\"distributed_epoch\",\n        service=target,\n        dataset_id=dataset_id,\n        job_name=job_name,\n        element_spec=element_spec,\n        max_outstanding_requests=max_outstanding_requests)\n    if window_size is not None:\n      dataset = dataset.merged_window(size=window_size)\n  elif tf_config is not None and 'ps' in map(lambda x: x.lower(),\n                                             tf_config['cluster']):\n    logging.info('PS/Worker mode, use queue to broadcast dataset_id')\n    with tf.compat.v1.device(queue_device):\n      queue = tf.compat.v1.FIFOQueue(capacity=num_worker,\n                                     dtypes=[tf.int64],\n                                     shared_name=f'{job_name}_queue',\n                                     shapes=tuple())\n    if worker_idx == 0:\n      # data service try to register dataset, if the dataset has been registed, return dataset_id drectily\n      # that means get or register dataset. for data parallel, the data pipeline assure to be identity\n      # here we ues queue to ensure the same data pipeline for a job\n      dataset_id = register_dataset(target, self)\n      stacked_dids = tf.stack(values=[dataset_id for _ in range(num_worker)],\n                              name='stacked_dids')\n      enqueue_op = queue.enqueue_many(vals=stacked_dids)\n      with tf.compat.v1.control_dependencies(control_inputs=[enqueue_op]):\n        # to share pipeline, job_name must be specified\n        dataset = dsvc.from_dataset_id(\n            processing_mode=\"distributed_epoch\",\n            service=target,\n            dataset_id=dataset_id,\n            job_name=job_name,\n            element_spec=element_spec,\n            max_outstanding_requests=max_outstanding_requests)\n    else:\n      dataset_id = queue.dequeue()\n      dataset = dsvc.from_dataset_id(\n          processing_mode=\"distributed_epoch\",\n          service=target,\n          dataset_id=dataset_id,\n          job_name=job_name,\n          element_spec=element_spec,\n          max_outstanding_requests=max_outstanding_requests)\n    if window_size is not None:\n      dataset = dataset.merged_window(size=window_size)\n  else:\n    logging.info(f'enable_sync_training is {enable_sync_training()}')\n    return self\n\n  return dataset\n\n\ndef transform(self, t: Transform, **kwargs):\n  value = tf.compat.v1.get_collection(OUTPUT_PB_TYPE_GRAPH_KEY)\n  assert len(value) == 1\n  variant_type = value[0]\n  assert variant_type in {\"instance\", \"example\"}\n  return TransformDataset(self, t, variant_type=variant_type)\n\n\n@monolith_export\nclass TransformDataset(dataset_ops.UnaryUnchangedStructureDataset):\n  \"\"\"样本过滤/改写\n\n  Args:\n    input_dataset (:obj:`dataset`): 输入数据集\n    transform (:obj:`Transform`): 改写方式\n    variant_type (:obj:`str`): 输入数据是variant类型的, 支持两种格式, instance/example\n\n  Raises:\n    TypeError: 如果有任何参数与类型不匹配, 则抛TypeError\n    ValueError: 如果有任何值与期望不匹配, 则抛ValueError\n\n  \"\"\"\n\n  def __init__(self, input_dataset, transform: Transform, variant_type: str):\n    assert variant_type in {\"instance\", \"example\"}\n    self._transform = transform\n\n    variant_tensor = pb_datasource_ops.transform_dataset(\n        input=input_dataset._variant_tensor,\n        config=transform.as_proto().SerializeToString(),\n        variant_type=variant_type)\n    logging.info(\"Start init of the pb instance dataset base.\")\n    super(TransformDataset, self).__init__(input_dataset, variant_tensor)\n\n  @property\n  def element_spec(self):\n    return tensor_spec.TensorSpec([], dtypes.variant)\n\n\nDataset.instance_reweight = instance_reweight\nDataset.negative_gen = negative_gen\nDataset.split_flow = split_flow\nDataset.merge_flow = merge_flow\nDataset.distribute = lambda ds, *args, **kwargs: ds\nDataset.merged_window = merged_window\nDataset.transform = transform\n"
  },
  {
    "path": "monolith/native_training/data/docker-compose.yaml",
    "content": "version: \"3\"\nservices:\n  zookeeper:\n    image: 'bitnami/zookeeper:latest'\n    ports:\n      - '2181:2181'\n    environment:\n      - ALLOW_ANONYMOUS_LOGIN=yes\n  kafka:\n    image: 'bitnami/kafka:latest'\n    ports:\n      - '9092:9092'\n    environment:\n      - KAFKA_BROKER_ID=1\n      - KAFKA_LISTENERS=PLAINTEXT://:9092\n      - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://127.0.0.1:9092\n      - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181\n      - ALLOW_PLAINTEXT_LISTENER=yes\n    depends_on:\n      - zookeeper\n"
  },
  {
    "path": "monolith/native_training/data/eager_mode_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport tensorflow as tf\n\nfrom monolith.native_training.data.datasets import PBDataset, PbType, DynamicMatchingFilesDataset\nfrom monolith.native_training.data.parsers import parse_instances, parse_examples, parse_example_batch\nfrom monolith.native_training.data.feature_utils import switch_slot, feature_combine\n\nfeatures = {\n    'f_spm_1': 301,\n    'f_spm_3': 303,\n    'f_spm_2': 302,\n    'f_spm_4': 304,\n    'f_user_id': 1,\n    'f_user_ctx_network': 61,\n    'f_user_id-f_page': 504,\n    'f_scm': 306,\n    'f_goods_id': 200,\n    'f_goods_sale_number_1000': 225,\n    'f_goods_praise_cnt': 229,\n    'f_spm': 300,\n    'f_page': 305,\n    'f_is_dup': 310,\n    'f_user_ctx_platform': 52,\n    'f_goods_title_terms': 209,\n    'f_goods_tags_terms': 211,\n    'f_user_test09_array_int32': 554,\n    'f_user_test15_array_float': 540,\n    'f_user_test14_array_bool': 543,\n    'f_user_test12_array_uint64': 551,\n    'f_user_test10_array_int64': 549\n}\n\n\ngroup_slots = [200,201,202,203,204,205,206,210,211,212,213,214,215,\\\n               216,217,218,219,220,221,222,223,224,225,230,231,232,233,234,235,236,237,238,239,240,241,242]\n\n\ndef parse_inst_exam(tensor: tf.Tensor, out_type):\n  fidv1_features = [\n      1, 2, 32, 33, 36, 38, 42, 50, 54, 56, 60, 66, 120, 150, 180, 182, 192,\n      220, 333, 410, 412, 422, 446\n  ]\n  if out_type == PbType.INSTANCE:\n    return parse_instances(tensor,\n                           fidv1_features,\n                           dense_features=['label'],\n                           dense_feature_shapes=[2],\n                           dense_feature_types=[tf.float32],\n                           extra_features=['uid', 'req_time', 'item_id'],\n                           extra_feature_shapes=[1, 1, 1])\n  else:\n    return parse_examples(\n        tensor,\n        sparse_features=[f'fc_slot_{slot}' for slot in fidv1_features],\n        dense_features=['label'],\n        dense_feature_shapes=[2],\n        dense_feature_types=[tf.float32],\n        extra_features=['uid', 'req_time', 'item_id'],\n        extra_feature_shapes=[1, 1, 1])\n\n\ndef parse_eb(tensor: tf.Tensor, out_type):\n  if out_type == PbType.INSTANCE:\n    feature_dict = parse_instances(\n        tensor,\n        fidv1_features=list(features.values()),\n        dense_features=['label'],\n        dense_feature_shapes=[2],\n        dense_feature_types=[tf.float32],\n        extra_features=['uid', 'req_time', 'item_id'],\n        extra_feature_shapes=[1, 1, 1])\n  else:\n    feature_dict = parse_examples(tensor,\n                                  sparse_features=list(features.keys()),\n                                  dense_features=['label'],\n                                  dense_feature_shapes=[2],\n                                  dense_feature_types=[tf.float32],\n                                  extra_features=['uid', 'req_time', 'item_id'],\n                                  extra_feature_shapes=[1, 1, 1])\n    feature_dict['f_page'] = switch_slot(feature_dict['f_page'], slot=306)\n    feature_dict['f_user_id-f_goods_tags_terms'] = feature_combine(\n        feature_dict['f_user_id'], feature_dict['f_goods_tags_terms'], slot=505)\n  return feature_dict\n\n\nclass DataOpsTest(tf.test.TestCase):\n\n  def target(self, input_pb_type, output_pb_type):\n    filter_fn = None\n\n    if input_pb_type == PbType.INSTANCE:\n      lagrangex_header = False\n      has_sort_id, kafka_dump, kafka_dump_prefix = True, True, False\n      file_name = \"monolith/native_training/data/training_instance/instance.pb\"\n    elif input_pb_type == PbType.EXAMPLE:\n      lagrangex_header = False\n      has_sort_id, kafka_dump, kafka_dump_prefix = True, True, False\n      file_name = \"monolith/native_training/data/training_instance/example.pb\"\n    else:\n      lagrangex_header = True\n      has_sort_id, kafka_dump, kafka_dump_prefix = False, False, False\n      file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n\n    def parser(tensor: tf.Tensor):\n      if output_pb_type == PbType.PLAINTEXT:\n        return parse_inst_exam(tensor, input_pb_type)\n      elif input_pb_type != PbType.EXAMPLEBATCH:\n        return parse_inst_exam(tensor, output_pb_type)\n      else:\n        return parse_eb(tensor, output_pb_type)\n\n    dataset = PBDataset(file_name=file_name,\n                        lagrangex_header=lagrangex_header,\n                        has_sort_id=has_sort_id,\n                        kafka_dump=kafka_dump,\n                        kafka_dump_prefix=kafka_dump_prefix,\n                        input_pb_type=input_pb_type,\n                        output_pb_type=output_pb_type)\n    if input_pb_type == PbType.EXAMPLEBATCH:\n      variant_type = 'instance' if output_pb_type == PbType.INSTANCE else 'example'\n      dataset = dataset.instance_reweight(\n          action_priority=\"2,7,0,1,3,4,5,6,8,9,10,11\",\n          reweight=\n          \"0:0:1,1:0:1,2:3:-1,3:0:1,4:0:1,5:0:1,6:0:1,7:6:1,8:0:1,9:0:1,10:0:1,11:0:-1\",\n          variant_type=variant_type)\n    if filter_fn is not None:\n      dataset = dataset.filter(filter_fn)\n    dataset = dataset.batch(8, drop_remainder=True).map(parser)\n\n    for feature in dataset.take(5):\n      self.assertIn(len(feature), {26, 27})\n\n  def testExampleBatch2Instance(self):\n    self.target(PbType.EXAMPLEBATCH, PbType.INSTANCE)\n\n  def testExample2Instance(self):\n    self.target(PbType.EXAMPLE, PbType.INSTANCE)\n\n  def testInstance2Instance(self):\n    self.target(PbType.INSTANCE, PbType.INSTANCE)\n\n  def testExampleBatch(self):\n    lagrangex_header = True\n    has_sort_id, kafka_dump, kafka_dump_prefix = False, False, False\n    file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n    input_pb_type, output_pb_type = PbType.EXAMPLEBATCH, PbType.EXAMPLEBATCH\n    dataset = PBDataset(file_name=file_name,\n                        lagrangex_header=lagrangex_header,\n                        has_sort_id=has_sort_id,\n                        kafka_dump=kafka_dump,\n                        kafka_dump_prefix=kafka_dump_prefix,\n                        input_pb_type=input_pb_type,\n                        output_pb_type=output_pb_type)\n\n    def parser(tensor):\n      freatues = parse_example_batch(\n          tensor,\n          sparse_features=list(features.keys()),\n          dense_features=['label'],\n          dense_feature_shapes=[2],\n          dense_feature_types=[tf.float32],\n          extra_features=['uid', 'req_time', 'item_id'],\n          extra_feature_shapes=[1, 1, 1])\n      return freatues\n\n    dataset = dataset.map(parser)\n    for feature in dataset.take(5):\n      self.assertIn(len(feature), {26, 27})\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/extract_fid_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\nragged_data_ops = gen_monolith_ops\n\n\nclass ExtraFidTest(tf.test.TestCase):\n\n  def test_parse_search(self):\n    fid = ragged_data_ops.extract_fid(185, 4).numpy()\n    self.assertTrue(fid == 1153447759131936)\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/feature_list.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging, flags\nfrom dataclasses import dataclass\nimport inspect\nimport threading\nimport numpy as np\nfrom collections import defaultdict\nfrom typing import List, Dict, Optional, Set, Tuple, Union, get_type_hints\nimport tensorflow as tf\nfrom monolith.native_training.data.utils import get_slot_feature_name, get_slot_from_feature_name\nfrom monolith.native_training.utils import add_to_collections\n\n_BOOL_FLAGS = {'true', 'yes', 't', 'y', '1'}\n_cache = {}\nFID_MASK = (1 << 64) - 1\nFLAGS = flags.FLAGS\n\n\ndef new_instance(cls, args):\n  signature = inspect.signature(cls.__init__)\n  valid_args = {}\n  for key, param in signature.parameters.items():\n    if key not in {'cls', 'self'}:\n      if param.name in args:\n        valid_args[param.name] = args[param.name]\n\n  return cls(**valid_args)\n\n\n@dataclass\nclass Feed:\n  feed_name: str = None\n  shared: bool = None\n  feature_id: int = None\n\n  def __post_init__(self):\n    if self.shared is not None:\n      self.shared = self.shared.lower() in _BOOL_FLAGS\n    else:\n      self.shared = False\n    if isinstance(self.feature_id, str):\n      self.feature_id = int(self.feature_id)\n\n  @property\n  def name(self):\n    return self.feed_name\n\n\n@dataclass\nclass Cache:\n  cache_column: str = None\n  cache_name: str = None\n  capacity: int = None\n  timeout: int = None\n  cache_type: str = None\n  cache_key_class: str = None\n\n  def __post_init__(self):\n    if isinstance(self.capacity, str):\n      self.capacity = int(self.capacity)\n    if isinstance(self.timeout, str):\n      self.timeout = int(self.timeout)\n\n  @property\n  def name(self):\n    if self.cache_name is not None:\n      return self.cache_name\n    elif self.cache_key_class is not None:\n      return self.cache_key_class\n    elif self.cache_column is not None:\n      return 'cache_column'\n    else:\n      raise Exception('no name for cache')\n\n\n@dataclass\nclass Feature:\n  feature_name: str = None\n  depend: List[str] = None\n  method: str = None\n  slot: int = None\n  args: List[str] = None\n  feature_version: int = None\n  shared: bool = False\n  cache_keys: List[str] = None\n  need_raw: bool = False\n  feature_id: int = None\n  input_optional: List[bool] = None\n  feature_group: List[str] = None\n\n  def __post_init__(self):\n    if isinstance(self.feature_group, str):\n      self.feature_group = [\n          item.strip().replace('\"', '').replace(\"'\", '')\n          for item in self.feature_group.strip().split(',')\n      ]\n\n    if isinstance(self.depend, str):\n      self.depend = [\n          item.strip().replace('\"', '').replace(\"'\", '')\n          for item in self.depend.strip().split(',')\n      ]\n\n    if isinstance(self.input_optional, str):\n      self.input_optional = [\n          item.strip().replace('\"', '').replace(\"'\", '') == 'true'\n          for item in self.input_optional.strip().split(',')\n      ]\n\n    if isinstance(self.args, str):\n      self.args = [\n          item.strip().replace('\"', '').replace(\"'\", '')\n          for item in self.args.strip().split(',')\n      ]\n\n    if isinstance(self.cache_keys, str):\n      self.cache_keys = [\n          item.strip().replace('\"', '').replace(\"'\", '')\n          for item in self.cache_keys.strip().split(',')\n      ]\n\n    if isinstance(self.slot, str):\n      self.slot = int(self.slot)\n\n    if isinstance(self.shared, str):\n      self.shared = self.shared.lower() in _BOOL_FLAGS\n\n    if isinstance(self.need_raw, str):\n      self.need_raw = self.need_raw.lower() in _BOOL_FLAGS\n\n    if isinstance(self.feature_id, str):\n      self.feature_id = int(self.feature_id)\n\n    if isinstance(self.feature_version, str):\n      self.feature_version = int(self.feature_version)\n\n  def __str__(self):\n    terms = []\n    for name, clz in get_type_hints(Feature).items():\n      value = getattr(self, name)\n      if value is not None:\n        if clz == str:\n          terms.append(\"{}={}\".format(name, value))\n        elif clz == int:\n          terms.append(\"{}={}\".format(name, value))\n        elif clz == bool:\n          if value:\n            terms.append(\"{}=true\".format(name))\n        elif clz._name == 'List' and len(clz.__args__) == 1:\n          if clz.__args__[0] == str:\n            terms.append(\"{}={}\".format(name, ','.join(value)))\n          elif clz.__args__[0] == bool:\n            format_value = [str(b).lower() for b in value]\n            terms.append(\"{}={}\".format(name, ','.join(format_value)))\n        else:\n          raise ValueError(\"Type Error\")\n    return ';'.join(terms)\n\n  @property\n  def name(self):\n    term_list = []\n    for term in self.feature_name.split('-'):\n      if term.startswith('fc_'):\n        term = term[3:]\n      elif self.feature_name.startswith('f_'):\n        term = term[2:]\n      term_list.append(term)\n\n    return '-'.join(term_list).lower()\n\n  @property\n  def depend_strip_prefix(self):\n    depend = []\n    for dep in self.depend:\n      term_list = []\n      for term in dep.split('-'):\n        if term.startswith('fc_'):\n          term = term[3:]\n        elif term.startswith('f_'):\n          term = term[2:]\n        term_list.append(term)\n\n      depend.append('-'.join(term_list).lower())\n    return depend\n\n\nclass FeatureList(object):\n  _lock = threading.Lock()\n\n  def __init__(self, column_name: Optional[Set[str]], feeds: Dict[str, Feed],\n               caches: Dict[str, Cache], features: Dict[str, Feature]):\n    self.column_name = column_name\n    self.feeds = feeds\n    self.caches = caches\n    self.features = features\n\n    self.__slots = defaultdict(list)\n    for feat in features.values():\n      self.__slots[feat.slot].append(feat)\n    add_to_collections('feature_list', self)\n\n  def __getitem__(self, item) -> Feature:\n    if isinstance(item, int):\n      if item in self.__slots:\n        return self.__slots[item][0]\n      else:\n        raise Exception('there is no feature {}'.format(item))\n    else:\n      assert isinstance(item, str)\n      item = item.strip()\n      if item in self.features:\n        return self.features[item]\n      elif f'f_{item}' in self.features:\n        return self.features[f'f_{item}']\n      elif f'fc_{item}' in self.features:\n        return self.features[f'fc_{item}']\n      else:\n        if '-' in item:\n          new_item = '-'.join([f'fc_{term}' for term in item.split('-')])\n          if new_item in self.features:\n            return self.features[new_item]\n\n          new_item = '-'.join([f'f_{term}' for term in item.split('-')])\n          if new_item in self.features:\n            return self.features[new_item]\n\n        raise Exception('there is no feature {}'.format(item))\n\n  def get(self, item, default=None):\n    try:\n      return self.__getitem__(item)\n    except:\n      return default\n\n  def get_with_slot(self, slot):\n    if slot in self.__slots:\n      return self.__slots[slot]\n    else:\n      return []\n\n  def __len__(self):\n    return len(self.features)\n\n  def __contains__(self, item):\n    return item in self.features or f'f_{item}' in self.features or f'fc_{item}' in self.features or item in self.__slots\n\n  def __iter__(self):\n    return iter(self.features.values())\n\n  @classmethod\n  def parse(cls, fname: str = None, use_old_name: bool = True) -> 'FeatureList':\n    fname = fname or FLAGS.feature_list\n    assert fname is not None\n    with cls._lock:\n      if fname in _cache:\n        return _cache[fname]\n      column_name = None\n      feeds, caches, features = {}, {}, {}\n      with open(fname) as stream:\n        for line in stream:\n          line = line.strip()\n          if len(line) == 0 or line.startswith(\"#\"):\n            continue\n\n          if line.startswith('column_name'):\n            start = len('column_name:')\n            column_name = {item.strip() for item in line[start:].split(',')}\n            continue\n\n          if line.startswith('cache_column'):\n            cache = Cache(cache_column=line[len('cache_column:'):].strip())\n            caches[cache.name] = cache\n            continue\n\n          params = {}\n          items = line.split('=')\n          for i in range(len(items) - 1):\n            if i == 0:\n              key = items[i].strip()\n            else:\n              start = items[i].rindex(\" \")\n              key = items[i][start:].strip()\n\n            if i == len(items) - 2:\n              value = items[i + 1]\n            else:\n              end = items[i + 1].rindex(\" \")\n              value = items[i + 1][0:end]\n\n            params[key] = value.strip().rstrip(',').rstrip(';').rstrip()\n\n          try:\n            if line.startswith('feed'):\n              feed = new_instance(Feed, params)\n              feeds[feed.name] = feed\n            elif line.startswith('cache'):\n              cache = new_instance(Cache, params)\n              caches[cache.name] = cache\n            else:\n              feat = new_instance(Feature, params)\n              if use_old_name:\n                features[feat.feature_name] = feat\n              else:\n                features[feat.name] = feat\n          except Exception as e:\n            print(line)\n            raise e\n\n      feat_list = cls(column_name, feeds, caches, features)\n      _cache[fname] = feat_list\n\n      return feat_list\n\n\ndef get_feature_name_and_slot(item) -> Tuple[str, Optional[int]]:\n  if isinstance(item, int):\n    try:\n      feature_list = FeatureList.parse()\n      return feature_list.get(item).feature_name, item\n    except:\n      return get_slot_feature_name(item), item\n  elif isinstance(item, str):\n    try:\n      feature_list = FeatureList.parse()\n      assert item in feature_list\n      return item, feature_list[item].slot\n    except:\n      return item, get_slot_from_feature_name(item)\n  else:\n    # for FeatureColumn\n    assert hasattr(item, 'feature_name') and hasattr(item, 'feature_slot')\n    return item.feature_name, item.feature_slot\n\n\n_VALID_FNAMES = set()\n\n\ndef is_example_batch():\n  # only example batch need column prune, this function is design for example_batch\n  is_example_batch = False\n  if hasattr(FLAGS, 'data_type') and FLAGS.data_type:\n    if FLAGS.data_type.lower() in {'example_batch', 'examplebatch'}:\n      is_example_batch = True\n  return is_example_batch\n\n\ndef add_feature(feature: Union[str, int, List[str], List[int]]):\n  global _VALID_FNAMES\n  if not isinstance(feature, (list, tuple)):\n    feature = [feature]\n  if feature:\n    for element in feature:\n      if isinstance(element, str):\n        _VALID_FNAMES.add(element)\n      else:\n        assert isinstance(element, int)\n        _VALID_FNAMES.add(get_slot_feature_name(element))\n\n\ndef add_feature_by_fids(fids: Union[int, List[int]], feature_list: FeatureList = None):\n  if not is_example_batch():\n    return\n  if isinstance(fids, int):\n    fids = [fids]\n\n  if feature_list is None:\n    # for example_batch, there is a feature_list.conf\n    feature_list = FeatureList.parse()\n\n  if feature_list:\n    for fid in fids:\n      find_feature = False\n      if isinstance(fid, int):\n        fid = fid & FID_MASK\n      else:\n        assert isinstance(fid, np.int64)\n        fid = fid & np.uint64(FID_MASK).astype(np.int64)\n      for feature in feature_list.get_with_slot(fid >> 54):\n        if feature.feature_version is None or feature.feature_version == 1:\n          add_feature(feature.feature_name)\n          find_feature = True\n\n      for feature in feature_list.get_with_slot(fid >> 48):\n        if feature.feature_version == 2:\n          add_feature(feature.feature_name)\n          find_feature = True\n\n      if not find_feature:\n        raise Exception(f'Cannot find feature name for fid: {fid}')\n  else:\n    raise Exception('Cannot create feature_list')\n\n\ndef get_valid_features() -> List[str]:\n  global _VALID_FNAMES\n  return list(_VALID_FNAMES)\n"
  },
  {
    "path": "monolith/native_training/data/feature_list_test.py",
    "content": ""
  },
  {
    "path": "monolith/native_training/data/feature_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom enum import Enum\nimport string\nimport numpy as np\nfrom typing import Any, List, Union, Dict, Tuple\n\nimport tensorflow as tf\n\nfrom monolith.utils import get_libops_path\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\nfrom idl.matrix.proto.line_id_pb2 import LineId\nfrom monolith.native_training.data.feature_list import add_feature, add_feature_by_fids\nfrom monolith.native_training.data.data_op_config_pb2 import (\n    LabelConf, TaskLabelConf, TFRecordFeatureDescription)\n\nragged_data_ops = gen_monolith_ops\n\n\n@monolith_export\ndef filter_by_fids(variant: tf.Tensor,\n                   filter_fids: List[int] = None,\n                   has_fids: List[int] = None,\n                   select_fids: List[int] = None,\n                   has_actions: List[int] = None,\n                   req_time_min: int = 0,\n                   select_slots: List[int] = None,\n                   variant_type: str = 'instance'):\n  \"\"\"通过特征ID (FID) 过滤, 离散特征过滤\n  \n  Args:\n    variant (:obj:`Tensor`): 输入数据, 必须是variant类型\n    filter_fids (:obj:`List[int]`): 任意一个FID出现`filter_fids`中, 样本被过滤\n    has_fids (:obj:`List[int]`): 任意一个FID出现在`has_fids`中, 则样本被选择\n    select_fids (:obj:`List[int]`): 所有`select_fids`均出现在样本中, 则样本被选择\n    has_actions (:obj:`List[int]`): 任意一个action出现在`has_actions`中, 则样本被选择\n    req_time_min (:obj:`int`): 请求时间最小值\n    select_slots (:obj:`List[int]`): 所有`select_slots`均出现在样本中, 样本才被选择\n    variant_type (:obj:`str`): variant类型, 可以为instance/example\n  \n  Returns:\n    variant tensor, 过滤后的数据, variant类型\n  \n  \"\"\"\n\n  filter_fids = [] if filter_fids is None else [\n      np.uint64(fid).astype(np.int64) for fid in filter_fids\n  ]\n  has_fids = [] if has_fids is None else [\n      np.uint64(fid).astype(np.int64) for fid in has_fids\n  ]\n  select_fids = [] if select_fids is None else [\n      np.uint64(fid).astype(np.int64) for fid in select_fids\n  ]\n  select_slots = [] if select_slots is None else select_slots\n  assert all([slot > 0 for slot in select_slots])\n  if variant_type != 'instance':\n    add_feature_by_fids(filter_fids)\n    add_feature_by_fids(has_fids)\n    add_feature_by_fids(select_fids)\n\n  return ragged_data_ops.set_filter(variant, filter_fids, has_fids, select_fids,\n                                    has_actions or [], req_time_min,\n                                    select_slots, variant_type)\n\n\n@monolith_export\ndef filter_by_feature_value(variant: tf.Tensor,\n                            field_name: str,\n                            op: str,\n                            operand: Union[float, int, str, List[float],\n                                           List[int], List[str]],\n                            field_type: str,\n                            keep_empty: bool = False,\n                            operand_filepath: str = None):\n  \"\"\"通过值过滤, 连续特征过滤, \n  \n  Args:\n    variant (:obj:`Tensor`): 输入数据, 必须是variant类型\n    field_name (:obj:`List[int]`): 当field_name, 样本被过滤\n    op (:obj:`str`): 比较运算符, 可以是 gt/ge/eq/lt/le/neq/between/in/not-in 等\n      布尔运算，也可以是 all/any/diff 等集合布尔运算\n    operand (:obj:`float`): 操作数, 用于比较, 可以为值或者List\n    keep_empty (:obj:`bool`): False\n    field_type (:obj:`str`): 需要显式指定字段类型, 可以为int64/float/double/bytes\n\n  Returns:\n    variant tensor, 过滤后的数据, variant类型\n  \"\"\"\n\n  assert op in {\n      'gt', 'ge', 'eq', 'lt', 'le', 'neq', 'between', 'in', 'not-in', 'all',\n      'any', 'diff', 'startswith', 'endswith'\n  }\n\n  assert (operand is None and operand_filepath) or (operand is not None and\n                                                    not operand_filepath)\n  assert field_type in {\n      'int64', 'float', 'double', 'bytes'\n  }, 'You must specify field_type for feature value_filter!'\n\n  string_operand = []\n  operand_filepath = '' if operand_filepath is None else operand_filepath\n\n  if operand_filepath:\n    assert op in {'in', 'not-in'}\n    assert (isinstance(operand_filepath, str) and\n            tf.io.gfile.exists(operand_filepath))\n    int_operand, float_operand = [], []\n  elif op in {'all', 'any', 'diff'}:\n    assert field_type == 'int64', 'all/any/diff op only support int64 list'\n    if not isinstance(operand, (list, tuple)):\n      assert isinstance(operand, int)\n      int_operand, float_operand = [operand], []\n    else:\n      assert all(isinstance(o, int) for o in operand)\n      int_operand, float_operand = list(operand), []\n  elif field_type in {'float', 'double'}:\n    if op == 'between':\n      assert all(isinstance(o, (int, float)) for o in operand)\n      int_operand, float_operand = [], [float(o) for o in operand]\n    else:\n      int_operand, float_operand = [], [float(operand)]\n  elif field_type == 'int64':\n    if op in {'in', 'not-in', 'between'}:\n      assert all(isinstance(o, int) for o in operand)\n      int_operand, float_operand = list(operand), []\n    else:\n      int_operand, float_operand = [int(operand)], []\n  elif field_type == 'bytes':\n    int_operand, float_operand = [], []\n    if isinstance(operand, str):\n      string_operand.append(operand)\n    elif isinstance(operand, (list, tuple)):\n      assert all(isinstance(o, str) for o in operand)\n      string_operand.extend(operand)\n    else:\n      raise RuntimeError(\"params error!\")\n  else:\n    raise RuntimeError(\"params error!\")\n\n  return ragged_data_ops.feature_value_filter(variant,\n                                              field_name=field_name,\n                                              op=op,\n                                              float_operand=float_operand,\n                                              int_operand=int_operand,\n                                              string_operand=string_operand,\n                                              operand_filepath=operand_filepath,\n                                              field_type=field_type,\n                                              keep_empty=keep_empty)\n\n\n@monolith_export\ndef filter_by_value(variant: tf.Tensor,\n                    field_name: str,\n                    op: str,\n                    operand: Union[float, int, str, List[float], List[int],\n                                   List[str]],\n                    variant_type: str = 'instance',\n                    keep_empty: bool = False,\n                    operand_filepath: str = None):\n  \"\"\"通过值过滤, 连续特征过滤, \n  \n  Args:\n    variant (:obj:`Tensor`): 输入数据, 必须是variant类型\n    field_name (:obj:`List[int]`): 需要执行过滤逻辑的字段名\n    op (:obj:`str`): 比较运算符, 可以是 gt/ge/eq/lt/le/neq/between/in/not-in 等\n      布尔运算，也可以是 all/any/diff 等集合布尔运算，也可以是 startswith/endswith 等\n      字符串判断逻辑\n    operand (:obj:`float`): 操作数, 用于比较\n    variant_type (:obj:`str`): variant类型, 可以为instance/example\n    keep_empty (:obj:`bool`): False\n\n  Returns:\n    variant tensor, 过滤后的数据, variant类型\n  \"\"\"\n\n  if variant_type != 'instance':\n    add_feature('__LINE_ID__')\n\n  assert op in {\n      'gt', 'ge', 'eq', 'lt', 'le', 'neq', 'between', 'in', 'not-in', 'all',\n      'any', 'diff', 'startswith', 'endswith'\n  }\n  fields = LineId.DESCRIPTOR.fields_by_name\n  assert field_name in fields\n  assert (operand is None and operand_filepath) or (operand is not None and\n                                                    not operand_filepath)\n  field = fields[field_name]\n  string_operand = []\n  operand_filepath = '' if operand_filepath is None else operand_filepath\n  if operand_filepath:\n    assert op in {'in', 'not-in'}\n    assert (isinstance(operand_filepath, str) and\n            tf.io.gfile.exists(operand_filepath))\n    int_operand, float_operand = [], []\n  elif field.has_options:\n    assert op in {'all', 'any', 'diff'}\n    assert field.cpp_type in {\n        field.CPPTYPE_INT32, field.CPPTYPE_INT64, field.CPPTYPE_UINT32,\n        field.CPPTYPE_UINT64\n    }\n    if not isinstance(operand, (list, tuple)):\n      assert isinstance(operand, int)\n      int_operand, float_operand = [operand], []\n    else:\n      assert all(isinstance(o, int) for o in operand)\n      int_operand, float_operand = list(operand), []\n  elif field.cpp_type in {field.CPPTYPE_DOUBLE, field.CPPTYPE_FLOAT}:\n    if op == 'between':\n      assert all(isinstance(o, (int, float)) for o in operand)\n      int_operand, float_operand = [], [float(o) for o in operand]\n    else:\n      int_operand, float_operand = [], [float(operand)]\n  elif field.cpp_type in {\n      field.CPPTYPE_INT32, field.CPPTYPE_INT64, field.CPPTYPE_UINT32,\n      field.CPPTYPE_UINT64\n  }:\n    if op in {'in', 'not-in', 'between'}:\n      assert all(isinstance(o, int) for o in operand)\n      int_operand, float_operand = list(operand), []\n    else:\n      int_operand, float_operand = [int(operand)], []\n  elif field.cpp_type == field.CPPTYPE_STRING:\n    int_operand, float_operand = [], []\n    if isinstance(operand, str):\n      string_operand.append(operand)\n    elif isinstance(operand, (list, tuple)):\n      assert all(isinstance(o, str) for o in operand)\n      string_operand.extend(operand)\n    else:\n      raise RuntimeError(\"params error!\")\n  else:\n    raise RuntimeError(\"params error!\")\n\n  return ragged_data_ops.value_filter(variant,\n                                      field_name=field_name,\n                                      op=op,\n                                      float_operand=float_operand,\n                                      int_operand=int_operand,\n                                      string_operand=string_operand,\n                                      operand_filepath=operand_filepath,\n                                      keep_empty=keep_empty,\n                                      variant_type=variant_type)\n\n\n@monolith_export\ndef add_action(\n    variant: tf.Tensor,\n    field_name: str,\n    op: str,\n    operand: Union[float, int, str, List[float], List[int], List[str]],\n    action: int,\n    variant_type: str = 'example',\n):\n  \"\"\"根据指定 LineId 字段经过简单的关系运算，决定是否为 actions 字段增加值\n\n  Args:\n    variant (:obj:`Tensor`): 输入数据，必须是 variant 类型\n    field_name (:obj:`List[int]`): 根据 field_name 对应值进行条件判断\n    op (:obj:`str`): 比较运算符，可以是 gt/ge/eq/lt/le/neq/between/in\n    operand (:obj:`float`): 操作数，用于比较\n    action (:obj:`int`): 当条件满足时，需要往 LineId.actions 添加的值\n    variant_type (:obj:`str`): 'instance' 或 'example'\n\n  Returns:\n    variant tensor, 改写后的数据，variant 类型\n  \"\"\"\n\n  if variant_type != 'instance':\n    add_feature('__LINE_ID__')\n\n  assert op in {'gt', 'ge', 'eq', 'lt', 'le', 'neq', 'between', 'in'}\n  assert variant_type in {'instance', 'example'}\n  fields = LineId.DESCRIPTOR.fields_by_name\n  assert field_name in fields\n  field = fields[field_name]\n  string_operand = []\n\n  if field.cpp_type in {field.CPPTYPE_DOUBLE, field.CPPTYPE_FLOAT}:\n    if op == 'between':\n      assert all(isinstance(o, (int, float)) for o in operand)\n      int_operand, float_operand = [], [float(o) for o in operand]\n    else:\n      int_operand, float_operand = [], [float(operand)]\n  elif field.cpp_type in {\n      field.CPPTYPE_INT32, field.CPPTYPE_INT64, field.CPPTYPE_UINT32,\n      field.CPPTYPE_UINT64\n  }:\n    if op in {'in', 'between'}:\n      assert all(isinstance(o, int) for o in operand)\n      int_operand, float_operand = list(operand), []\n    else:\n      int_operand, float_operand = [int(operand)], []\n  elif field.cpp_type == field.CPPTYPE_STRING:\n    int_operand, float_operand = [], []\n    if isinstance(operand, str):\n      string_operand.append(operand)\n    elif isinstance(operand, (list, tuple)):\n      assert all(isinstance(o, str) for o in operand)\n      string_operand.extend(operand)\n    else:\n      raise RuntimeError(\"params error!\")\n  else:\n    raise RuntimeError(\"params error!\")\n\n  return ragged_data_ops.add_action(variant,\n                                    field_name=field_name,\n                                    op=op,\n                                    float_operand=float_operand,\n                                    int_operand=int_operand,\n                                    string_operand=string_operand,\n                                    actions=[action],\n                                    variant_type=variant_type)\n\n\n@monolith_export\ndef add_label(\n    variant: tf.Tensor,\n    config: str,\n    negative_value: float,\n    new_sample_rate: float,\n    variant_type: str = 'example',\n):\n  \"\"\"根据给定配置决定是否添加 label，支持 multi-task label 生成，请务必配合\n     filter_by_label 过滤算子同时使用，否则可能会有无效样本被喂入训练器。\n  举例 config='1,2:3:1.0;4::0.5'，表示一共有两个 task（;分隔），\n  task1 pos_actions = {1,2}, neg_actions = {3}, sample_rate = 1.0，而\n  task2 pos_actions = {4}, neg_actions 为空，sample_rate = 0.5\n  add_label 的执行逻辑如下\n    - 对于 task1，如果当前样本的 actions 包含 {1, 2} 任一个则判定为正例，否则根据给定\n      采样率决定是否采样（sample_rate < 1.0 方可触发采样），若触发采样且在采样范围内\n      标为负例，不在采样范围内置为无效 label，若未触发采样直接标记为负例。这个例子里由于\n      task1 的 sample_rate=1.0，因此不会触发负采样\n    - 对于 task2，如果当前样本的 actions 包含 {4} 则判定为正例，由于未指定 neg_actions\n      对于不包含 {4} 的样本直接进行负采样，在采样范围内标为负例，不在采样范围内置为\n      无效 label。这个例子里由于 task2 的 sample_rate=0.5，因此会对于不包含 {4} 的样本\n      触发负采样\n\n  Args:\n    variant (:obj:`Tensor`): 输入数据，必须是 variant 类型\n    config (:obj:`str`): 形如 '1,2:3:1.0;4::0.5'\n    negative_value (:obj:`float`): 如 -1.0 或 0.0\n    new_sample_rate (:obj:`float`): 为 LineId.sample_rate 赋值\n    variant_type (:obj:`str`): 'instance' 或 'example'\n\n  Returns:\n    variant tensor, 改写后的数据，variant 类型\n  \"\"\"\n\n  assert variant_type in {'instance', 'example'}\n  if variant_type != 'instance':\n    add_feature('__LINE_ID__')\n  assert config, 'Please specify config and retry!'\n  assert 0 < new_sample_rate <= 1.0, 'new_sample_rate should be in (0, 1.0]'\n\n  label_conf = LabelConf()\n  for task in config.split(';'):\n    # skip empty parts, e.g. config = '1,2:3:1.0;'\n    if len(task) == 0:\n      continue\n\n    task_conf = label_conf.conf.add()\n    pos_actions, neg_actions, sample_rate = task.split(':')\n    pos_actions_list = [\n        int(pos) for pos in pos_actions.split(',') if len(pos) > 0\n    ]\n    neg_actions_list = [\n        int(neg) for neg in neg_actions.split(',') if len(neg) > 0\n    ]\n    task_conf.pos_actions.extend(pos_actions_list)\n    task_conf.neg_actions.extend(neg_actions_list)\n    task_conf.sample_rate = float(sample_rate)\n\n  return ragged_data_ops.add_label(variant,\n                                   config=label_conf.SerializeToString(),\n                                   negative_value=negative_value,\n                                   sample_rate=new_sample_rate,\n                                   variant_type=variant_type)\n\n\n@monolith_export\ndef scatter_label(\n    variant: tf.Tensor,\n    config: str,\n    variant_type: str = 'example',\n):\n  \"\"\"根据给定配置 scatter label 以支持 multi-task label 生成，配置形如\n  'chnid0:index0,chnid1:index1'，请务必配合 filter_by_label 过滤算子使用，\n  否则可能会有无效样本被喂入训练器。举例 config='100:3,200:1,300:4'，\n  表示一共有 5 个 task（最大的 index=4），scatter_label 的执行逻辑如下\n    1. 获取 label_value = label[0]，亦即默认待处理样本的 label.size() > 0\n    2. 重置待处理样本的 label 长度为 5，并全部初始化为 INVALID_LABEL\n    3. if 样本的 chnid = 100，label[3] = label_value\n    4. else if 样本的 chnid = 200，label[1] = label_value\n    5. else if 样本的 chnid = 300，label[4] = label_value\n    6. else 样本的 chnid not in {100, 200, 300}，则 label 中全部值为 INVALID_LABEL\n\n  Args:\n    variant (:obj:`Tensor`): 输入数据，必须是 variant 类型\n    config (:obj:`str`): 形如 '100:3,200:1,300:4'\n    variant_type (:obj:`str`): 'instance' 或 'example'\n\n  Returns:\n    variant tensor, 改写后的数据，variant 类型\n  \"\"\"\n\n  assert variant_type in {'instance', 'example'}\n  if variant_type != 'instance':\n    add_feature('__LABEL__')\n    add_feature('__LINE_ID__')\n  assert config, 'Please specify config and retry!'\n\n  return ragged_data_ops.scatter_label(variant,\n                                       config=config,\n                                       variant_type=variant_type)\n\n\n@monolith_export\ndef filter_by_label(\n    variant: tf.Tensor,\n    label_threshold: List[float],\n    filter_equal: bool = False,\n    variant_type: str = 'example',\n) -> bool:\n  \"\"\"根据给定配置决定是否保留当前样本，支持 multi-task\n\n  Args:\n    variant (:obj:`Tensor`): 输入数据，必须是 variant 类型\n    label_threshold (:obj:`List[float]`): 样本任一 label 值 >= 相应 label_threshold\n    值则样本被保留，否则被丢弃。举例 label_threshold = [-100.0, 0.0]，假设样本\n      - label = [-1000, -1]，则该样本被丢弃，即不存在任何合法 label 值\n      - label = [-1000, 0]，则该样本被保留，即第 2 个 label 值合法\n      - label = [-1, -1]，则该样本被保留，即第 1 个 label 值合法\n      - label = [-1, 1]，则该样本被保留，即第 1, 2 个 label 值均合法\n    filter_equal (:obj:`bool`): Whether to filter when label equals to threshold.\n    variant_type (:obj:`str`): 'instance' 或 'example'\n\n  Returns:\n    valid tensor, 是否保留当前样本\n  \"\"\"\n\n  assert variant_type in {'instance', 'example'}\n  if variant_type != 'instance':\n    add_feature('__LABEL__')\n  assert len(label_threshold) > 0, 'Please specify label_threshold and retry!'\n\n  return ragged_data_ops.filter_by_label(variant,\n                                         label_threshold=label_threshold,\n                                         filter_equal=filter_equal,\n                                         variant_type=variant_type)\n\n\n@monolith_export\ndef special_strategy(variant: tf.Tensor,\n                     strategy_list: List[int],\n                     strategy_conf: str = None,\n                     variant_type: str = 'instance',\n                     keep_empty_strategy=True):\n  \"\"\"用LineID中的special_strategy进行过滤, \n  \n  Args:\n    variant (:obj:`Tensor`): 输入数据, 必须是variant类型\n    strategy_list (:obj:`List[int]`): strategy列表\n    strategy_conf (:obj:`str`): 配置方式为 `strategy:sample_rate:label`, 如果有多个可以用逗号分割.\n                                用于实现采样, 包括对正例/负例/所有样本采样, 并修改样本标签 \n    variant_type (:obj:`str`): variant类型, 可以为instance/example\n    keep_empty_strategy (:obj:`bool`): 是否保留strategy为空的样本, 默认为False\n  \n  Returns:\n    variant tensor, 过滤后的数据, variant类型\n  \"\"\"\n\n  if variant_type != 'instance':\n    add_feature('__LABEL__')\n    add_feature('__LINE_ID__')\n\n  items = [] if strategy_conf is None else strategy_conf.strip().split(',')\n  special_strategies, sample_rates, labels = [], [], []\n  if len(items) > 0:\n    for item in items:\n      tl = item.strip().split(':')\n      if len(tl) == 2:\n        special_strategies.append(int(tl[0]))\n        sample_rates.append(float(tl[1]))\n      elif len(tl) == 3:\n        special_strategies.append(int(tl[0]))\n        sample_rates.append(float(tl[1]))\n        labels.append(float(tl[2]))\n\n  assert len(special_strategies) == len(sample_rates)\n  assert len(special_strategies) == len(labels) or len(labels) == 0\n  assert all(0 <= sr <= 1 for sr in sample_rates)\n  return ragged_data_ops.special_strategy(\n      variant,\n      special_strategies=special_strategies,\n      sample_rates=sample_rates,\n      labels=labels,\n      strategy_list=strategy_list,\n      keep_empty_strategy=keep_empty_strategy,\n      variant_type=variant_type)\n\n\n@monolith_export\ndef negative_sample(variant: tf.Tensor,\n                    drop_rate: float,\n                    label_index: int = 0,\n                    threshold: float = 0.0,\n                    variant_type: str = 'instance',\n                    action_priority: str = None,\n                    per_action_drop_rate: str = None):\n  \"\"\"负例采样\n  \n  Args:\n    variant (:obj:`Tensor`): 输入数据, 必须是variant类型\n    drop_rate (:obj:`float`): 负例丢弃比例, 取值区间为[0, 1), sample_rate = 1 - drop_rate. \n    label_index (:obj:`int`): 样本中labels是一个列表, label_index表示本次启用哪一个index对应的label\n    threshold (:obj:`float`): label是一个实数, 大于`threshold`的是正样本\n    variant_type (:obj:`str`): variant类型, 可以为instance/example\n    action_priority (:obj:`str`): action的优先级列表, action用int表示, 以逗号分隔, 排在前面的优先级高\n    per_action_drop_rate (:obj:`str`): 基本单元是`action:drop_rate`, 可以用逗号分隔多个基本单元\n  \n  Returns:\n    variant tensor, 过滤后的数据, variant类型\n  \"\"\"\n\n  if variant_type != 'instance':\n    add_feature('__LABEL__')\n\n  assert action_priority is None or isinstance(action_priority, str)\n  assert per_action_drop_rate is None or isinstance(per_action_drop_rate, str)\n  priority = []\n  actions, action_drop_rate = [], []\n\n  if action_priority and per_action_drop_rate:\n    priority = [int(i) for i in action_priority.strip().split(\",\")]\n    for item in per_action_drop_rate.strip().split(\",\"):\n      action, dr = item.strip().split(\":\")\n      actions.append(int(action))\n      action_drop_rate.append(float(dr))\n\n  return ragged_data_ops.negative_sample(variant,\n                                         drop_rate=drop_rate,\n                                         label_index=label_index,\n                                         threshold=threshold,\n                                         variant_type=variant_type,\n                                         priorities=priority,\n                                         actions=actions,\n                                         per_action_drop_rate=action_drop_rate)\n\n\n@monolith_export\ndef feature_combine(src1: tf.RaggedTensor, src2: tf.RaggedTensor,\n                    slot: int) -> tf.RaggedTensor:\n  \"\"\"特征交叉, 用于对已抽取Sparse特征的交叉\n  \n  Args:\n    src1 (:obj:`RaggedTensor`): 参与交叉的sparse特征, 可以是简单特征, 也可以是序列特征\n    src1 (:obj:`RaggedTensor`): 参与交叉的sparse特征, 可以是简单特征, 也可以是序列特征\n    slot (:obj:`int`): 输出特征的slot\n  \n  Returns:\n    RaggedTensor, 交叉后的特征\n  \n  \"\"\"\n\n  assert isinstance(src1, tf.RaggedTensor)\n  assert isinstance(src2, tf.RaggedTensor)\n\n  splits, values = ragged_data_ops.feature_combine(\n      rt_nested_splits_src1=src1.nested_row_splits,\n      rt_dense_values_src1=src1.flat_values,\n      rt_nested_splits_src2=src2.nested_row_splits,\n      rt_dense_values_src2=src2.flat_values,\n      slot=slot,\n      fid_version=2)\n\n  if splits[0].dtype == tf.float32:\n    return tf.RaggedTensor.from_row_splits(values=values,\n                                           row_splits=splits[1],\n                                           validate=False)\n  else:\n    return tf.RaggedTensor.from_nested_row_splits(flat_values=values,\n                                                  nested_row_splits=splits,\n                                                  validate=False)\n\n\n@monolith_export\ndef switch_slot(ragged: tf.RaggedTensor, slot: int) -> tf.RaggedTensor:\n  \"\"\"对Sparse特征切换slot\n  \n  Args:\n    ragged (:obj:`RaggedTensor`): 输入sparse特征, 可以是简单特征, 也可以是序列特征\n    slot (:obj:`int`): 输出特征的slot\n  \n  Returns:\n    RaggedTensor, 切换后的特征\n  \n  \"\"\"\n\n  assert isinstance(ragged, tf.RaggedTensor)\n  nested_row_splits = ragged.nested_row_splits\n\n  splits, values = ragged_data_ops.switch_slot(\n      rt_nested_splits=nested_row_splits,\n      rt_dense_values=ragged.flat_values,\n      slot=slot,\n      fid_version=2)\n\n  if splits[0].dtype == tf.float32:\n    return tf.RaggedTensor.from_row_splits(values=values,\n                                           row_splits=splits[1],\n                                           validate=False)\n  else:\n    return ragged.with_flat_values(values)\n\n\n@monolith_export\ndef switch_slot_batch(variant: tf.Tensor,\n                      features: Dict[str, Tuple[bool, int]],\n                      variant_type: str = 'example_batch',\n                      suffix: str = 'share') -> tf.Tensor:\n  \"\"\"对Sparse特征批量切换slot\n\n  Args:\n    variant (:obj:`VariantTensor`): 输入特征, 目前只支持pb格式\n    features (:obj:`dict`): 特征配置, 特征名 -> (是否原地修改, 新slot)\n    variant_type (:obj:`str`): 输入variant的类型, 目前支持'example', 'example_batch'这两种\n\n  Returns:\n    Variant Tensor, 切换后的特征\n\n  \"\"\"\n  feats, slots, inplaces = [], [], []\n  for name, (inplace, slot) in features.items():\n    feats.append(name)\n    inplaces.append(inplace)\n    slots.append(slot)\n\n  assert variant_type in {'example', 'example_batch'}\n  output = ragged_data_ops.switch_slot_batch(variant,\n                                             features=feats,\n                                             slots=slots,\n                                             inplaces=inplaces,\n                                             suffix=suffix,\n                                             variant_type=variant_type)\n  return output\n\n\n@monolith_export\ndef label_upper_bound(variant: tf.Tensor,\n                      label_upper_bounds: List[float],\n                      variant_type: str = 'instance'):\n  \"\"\"给label设置upper_bound, instance的label超过upper_bound的会被设置成upper_bound.\n  Args:\n    variant (:obj:`Tensor`): 输入数据，必须是 variant 类型\n    label_upper_bounds (:obj:`List[float]`): 样本任一 label 值 >= 相应 label_upper_bounds\n    时，该label会被设置为upper_bound\n    variant_type (:obj:`str`): 'instance' 或 'example'\n\n  Returns:\n    variant tensor, label根据upper_bound调整后的数据, variant类型\n  \"\"\"\n  assert variant_type in {'instance', 'example'}\n  assert len(\n      label_upper_bounds) > 0, 'Please specify label_threshold and retry!'\n\n  return ragged_data_ops.label_upper_bound(\n      variant, label_upper_bounds=label_upper_bounds, variant_type=variant_type)\n\n\n@monolith_export\ndef label_normalization(variant: tf.Tensor,\n                        norm_methods: List[str],\n                        norm_values: List[float],\n                        variant_type: str = 'instance'):\n  \"\"\"对Label进行normalization, instance的label会被修改为norm之后的数值.\n  Args:\n    variant (:obj:`Tensor`): 输入数据，必须是 variant 类型\n    norm_methods (:obj:`List[str]`): normlization的方法，例如log,scale,repow,scalelog\n    norm_values (:obj:`List[float]`): 对应normalization方法使用的norm_value, 长度需要与norm_methods保持一致\n    variant_type (:obj:`str`): 'instance' 或 'example'\n\n  Returns:\n    variant tensor, label根据upper_bound调整后的数据, variant类型\n  \"\"\"\n  assert variant_type in {'instance', 'example'}\n  assert len(norm_methods) == len(\n      norm_values), 'norm_methods and norm_values should have the same length'\n\n  return ragged_data_ops.label_normalization(variant,\n                                             norm_methods=norm_methods,\n                                             norm_values=norm_values,\n                                             variant_type=variant_type)\n\n\n@monolith_export\ndef use_field_as_label(variant: tf.Tensor,\n                       field_name: str,\n                       overwrite_invalid_value: bool = False,\n                       label_threshold: float = 7200,\n                       variant_type: str = 'instance'):\n  \"\"\"用line_id里的field作为新的label。\n  Args:\n    variant (:obj:`Tensor`): 输入数据，必须是 variant 类型\n    overwrite_invalid_value (:obj:`bool`): 是否对新field进行overwrite，如果overwrite会在value >= label_threshold时overwrite成0.\n    label_threshold (:obj:`List[float]`): 对新field进行overwrite的threshold值，如果value >= label_threshold则改写为0.\n    variant_type (:obj:`str`): 'instance' 或 'example'\n\n  Returns:\n    variant tensor, label根据upper_bound调整后的数据, variant类型\n  \"\"\"\n  assert variant_type in {'instance', 'example'}\n\n  return ragged_data_ops.use_field_as_label(\n      variant,\n      field_name=field_name,\n      overwrite_invalid_value=overwrite_invalid_value,\n      label_threshold=label_threshold,\n      variant_type=variant_type)\n\n\ndef create_item_pool(start_num: int,\n                     max_item_num_per_channel: int,\n                     container: str = '',\n                     shared_name: str = '') -> tf.Tensor:\n  assert start_num >= 0 and max_item_num_per_channel > 0\n  handle = ragged_data_ops.ItemPoolCreate(\n      start_num=start_num,\n      max_item_num_per_channel=max_item_num_per_channel,\n      container=container,\n      shared_name=shared_name)\n  return handle\n\n\ndef item_pool_random_fill(pool: tf.Tensor) -> tf.Tensor:\n  handle = ragged_data_ops.ItemPoolRandomFill(ipool=pool)\n  return handle\n\n\ndef item_pool_check(pool: tf.Tensor,\n                    model_path: str,\n                    global_step: int,\n                    nshards: int = 1,\n                    buffer_size: int = 10 * 1024 * 1024) -> tf.Tensor:\n  handle = ragged_data_ops.ItemPoolCheck(ipool=pool,\n                                         model_path=model_path,\n                                         nshards=nshards,\n                                         buffer_size=buffer_size,\n                                         global_step=global_step)\n  return handle\n\n\ndef save_item_pool(pool: tf.Tensor,\n                   global_step: tf.Tensor,\n                   model_path: str,\n                   nshards: int = 1) -> tf.Tensor:\n  handle = ragged_data_ops.ItemPoolSave(ipool=pool,\n                                        global_step=global_step,\n                                        model_path=model_path,\n                                        nshards=nshards)\n  return handle\n\n\ndef restore_item_pool(pool: tf.Tensor,\n                      global_step: tf.Tensor,\n                      model_path: str,\n                      nshards: int = 1,\n                      buffer_size: int = 10 * 1024 * 1024) -> tf.Tensor:\n  handle = ragged_data_ops.ItemPoolRestore(ipool=pool,\n                                           global_step=global_step,\n                                           model_path=model_path,\n                                           nshards=nshards,\n                                           buffer_size=buffer_size)\n  return handle\n\n\ndef fill_multi_rank_output(\n    variant: tf.Tensor,\n    enable_draw_as_rank: bool = False,\n    enable_chnid_as_rank: bool = False,\n    enable_lineid_rank_as_rank: bool = False,\n    rank_num: int = 18,\n    variant_type: str = 'instance',\n):\n  \"\"\"When use_rank_multi_output flag is set.\n  \"\"\"\n  assert variant_type in {'instance', 'example'}\n  if variant_type != 'instance':\n    add_feature('__LINE_ID__')\n\n  return ragged_data_ops.fill_multi_rank_output(\n      input=variant,\n      variant_type=variant_type,\n      enable_draw_as_rank=enable_draw_as_rank,\n      enable_chnid_as_rank=enable_chnid_as_rank,\n      enable_lineid_rank_as_rank=enable_lineid_rank_as_rank,\n      rank_num=rank_num)\n\n\ndef use_f100_multi_head(\n    variant: tf.Tensor,\n    variant_type: str = 'instance',\n):\n  \"\"\"When use_f100_multihead flag is set.\n  \"\"\"\n  assert variant_type in {'instance', 'example'}\n\n  return ragged_data_ops.use_f100_multi_head(input=variant,\n                                             variant_type=variant_type)\n\n\ndef map_id(tensor: tf.Tensor, map_dict: Dict[int, int], default: int = -1):\n  assert map_dict is not None and len(map_dict) > 0\n  from_value, to_value = zip(*map_dict.items())\n\n  return ragged_data_ops.MapId(input=tensor,\n                               from_value=list(from_value),\n                               to_value=list(to_value),\n                               default_value=default)\n\n\ndef multi_label_gen(variant: tf.Tensor,\n                    head_to_index: Dict[Any, int],\n                    head_field: str = 'chnid',\n                    pos_actions: List[int] = None,\n                    neg_actions: List[int] = None,\n                    use_origin_label: bool = False,\n                    pos_label: float = 1.0,\n                    neg_label: float = 0.0,\n                    action_priority: str = None,\n                    task_num: int = None,\n                    variant_type: str = 'example'):\n  task_num = 0 if task_num is None else task_num\n  head_to_index_list, max_idx = [], 0\n  for head, idx in head_to_index.items():\n    head_to_index_list.append(f'{head}:{idx}')\n    max_idx = max(idx, max_idx)\n  if task_num != 0:\n    assert max_idx < task_num\n  else:\n    task_num = max_idx + 1\n\n  action_priority = action_priority or \"\"\n  pos_actions, neg_actions = pos_actions or [], neg_actions or []\n  if use_origin_label:\n    assert len(pos_actions) == 0 and len(neg_actions) == 0\n  else:\n    assert len(pos_actions) > 0\n\n  fields = LineId.DESCRIPTOR.fields_by_name\n  assert head_field in fields\n  field = fields[head_field]\n  assert field.cpp_type in {\n      field.CPPTYPE_INT32, field.CPPTYPE_INT64, field.CPPTYPE_UINT32,\n      field.CPPTYPE_UINT64, field.CPPTYPE_STRING\n  }\n\n  assert variant_type in {'instance', 'example'}\n  return ragged_data_ops.multi_label_gen(\n      variant,\n      task_num=task_num,\n      head_to_index=','.join(head_to_index_list),\n      head_field=head_field,\n      action_priority=action_priority,\n      pos_actions=pos_actions,\n      neg_actions=neg_actions,\n      use_origin_label=use_origin_label,\n      pos_label=pos_label,\n      neg_label=neg_label,\n      variant_type=variant_type)\n\n\ndef string_to_variant(tensor: tf.Tensor,\n                      variant_type: str = 'example',\n                      has_header: bool = False,\n                      has_sort_id: bool = False,\n                      lagrangex_header: bool = False,\n                      kafka_dump_prefix: bool = False,\n                      kafka_dump: bool = False,\n                      chnids: List[int] = None,\n                      datasources: List[str] = None,\n                      default_datasource: str = ''):\n  assert variant_type in {\n      'instance', 'example', 'examplebatch', 'example_batch'\n  }\n  return ragged_data_ops.string_to_variant(\n      input=tensor,\n      has_header=has_header,\n      has_sort_id=has_sort_id,\n      lagrangex_header=lagrangex_header,\n      kafka_dump_prefix=kafka_dump_prefix,\n      kafka_dump=kafka_dump,\n      input_type=variant_type,\n      chnids=chnids or [],\n      datasources=datasources or [],\n      default_datasource=default_datasource)\n\n\n#string_to_variant_with_transform example\n'''\ndataset = dataset.flat_map(lambda v: tf.data.Dataset.from_tensors(\n    string_to_variant_with_transform(\n        v.message,\n        input_type=variant_type.lower(),\n        output_type=output_pb_type,\n        has_header=has_header,\n        lagrangex_header=self._lagrangex_header,\n        has_sort_id=self._has_sort_id,\n        kafka_dump=self._kafka_dump,\n        kafka_dump_prefix=self._kafka_dump_prefix,\n        chnids=self._chnids,\n        datasources=self._datasources,\n        default_datasource=self._default_datasource)))\n'''\n\n\ndef string_to_variant_with_transform(tensor: tf.Tensor,\n                                     input_type: str = 'example',\n                                     output_type: str = 'example',\n                                     has_header: bool = False,\n                                     has_sort_id: bool = False,\n                                     lagrangex_header: bool = False,\n                                     kafka_dump_prefix: bool = False,\n                                     kafka_dump: bool = False,\n                                     chnids: List[int] = None,\n                                     datasources: List[str] = None,\n                                     default_datasource: str = ''):\n  assert input_type in {'instance', 'example', 'examplebatch', 'example_batch'}\n  assert output_type in {'instance', 'example', 'examplebatch', 'example_batch'}\n  return ragged_data_ops.string_to_variant_with_transform(\n      input=tensor,\n      has_header=has_header,\n      has_sort_id=has_sort_id,\n      lagrangex_header=lagrangex_header,\n      kafka_dump_prefix=kafka_dump_prefix,\n      kafka_dump=kafka_dump,\n      input_type=input_type,\n      output_type=output_type,\n      chnids=chnids or [],\n      datasources=datasources or [],\n      default_datasource=default_datasource)\n\n\ndef variant_to_zeros(tensor: tf.Tensor):\n  return ragged_data_ops.variant_to_zeros(tensor)\n\n\ndef kafka_resource_init(topics: List[str],\n                        metadata: List[str],\n                        input_pb_type: str = \"\",\n                        output_pb_type: str = \"\",\n                        has_sort_id: bool = False,\n                        lagrangex_header: bool = False,\n                        kafka_dump_prefix: bool = False,\n                        kafka_dump: bool = False,\n                        container: str = '',\n                        shared_name: str = ''):\n  return ragged_data_ops.KafkaGroupReadableInit(\n      topics=topics,\n      metadata=metadata,\n      has_sort_id=has_sort_id,\n      lagrangex_header=lagrangex_header,\n      kafka_dump_prefix=kafka_dump_prefix,\n      kafka_dump=kafka_dump,\n      input_pb_type=input_pb_type,\n      output_pb_type=output_pb_type,\n      container=container,\n      shared_name=shared_name)\n\n\ndef kafka_read_next(input, index: int, message_poll_timeout: int,\n                    stream_timeout: int):\n  return ragged_data_ops.KafkaGroupReadableNext(\n      input=input,\n      index=index,\n      message_poll_timeout=message_poll_timeout,\n      stream_timeout=stream_timeout)\n\n\ndef kafka_read_next_v2(input, index: int, message_poll_timeout: int,\n                       stream_timeout: int):\n  return ragged_data_ops.KafkaGroupReadableNextV2(\n      input=input,\n      index=index,\n      message_poll_timeout=message_poll_timeout,\n      stream_timeout=stream_timeout)\n\n\ndef has_variant(input, variant_type: str = 'example'):\n  return ragged_data_ops.HasVariant(input=input, variant_type=variant_type)\n\n\ndef gen_fid_mask(tenosr: tf.RaggedTensor, fid: int) -> tf.Tensor:\n  fid = np.uint64(fid).astype(np.int64)\n  return ragged_data_ops.monolith_gen_fid_mask(tenosr.row_splits,\n                                               tenosr.flat_values,\n                                               fid=fid)\n\n\n@monolith_export\ndef tf_example_to_example(serialized: tf.Tensor,\n                          sparse_features: Dict[str, int],\n                          dense_features: List[str],\n                          label: str,\n                          instance_weight: str = None):\n  \"\"\" 将序列化的 tf.example 转换为 Monolith Example，在转换的同时，指定的 sparse_features\n      会被抽取成 FID\n  Args:\n    serialized (:obj:`Tensor`): tf.example 的序列化数据，string 类型\n    sparse_features (:obj:`Dict[str, int]`): sparse feature name 到 slot id 的映射，\n      举例：sparse_features = {\"user_id\": 1, \"item_id\": 2, \"posterior_ctr\": 3}，\n        1. \"user_id\" 原始类型为 int64，它将被抽取成 FID，存入 Monolith Example 的 fid_v2_list，对应 slot_id=1\n        2. \"item_id\" 原始类型为 int64，它将被抽取成 FID，存入 Monolith Example 的 fid_v2_list，对应 slot_id=2\n        3. \"posterior_ctr\" 原始类型为 float32，它将被抽取成 FID，存入 Monolith Example 的 fid_v2_list，对应 slot_id=3\n    dense_features (:obj:`List[str]`): 指定的这些字段将直接 Copy 到 Monolith Example 中\n    label (:obj:`str`): 存储在 tf.example 中的哪个字段是 label\n    instance_weight (:obj:`str`): 存储在 tf.example 中的哪个字段是 instance_weight\n\n  Returns:\n    variant tensor: Monolith Example 格式的 variant tensor\n  \"\"\"\n\n  ## default value setting\n  sparse_features = sparse_features or []\n  dense_features = dense_features or []\n  label = label or \"\"\n  instance_weight = instance_weight or \"\"\n\n  ## validity check\n  intersection = set(sparse_features.keys()) & set(dense_features)\n  assert len(\n      intersection\n  ) == 0, f\"{intersection} occur in sparse_features and dense_features simultaneously, please investigate and retry!\"\n  assert label not in sparse_features, f\"label: {label} should NOT occur in sparse_features, please investigate and retry!\"\n  assert label not in dense_features, f\"label: {label} should NOT occur in dense_features, please investigate and retry!\"\n  assert instance_weight not in sparse_features, f\"instance_weight: {instance_weight} should NOT occur in sparse_features, please investigate and retry!\"\n  assert instance_weight not in dense_features, f\"instance_weight: {instance_weight} should NOT occur in dense_features, please investigate and retry!\"\n\n  slot_ids = list(sparse_features.values())\n  duplicates = {slot for slot in slot_ids if slot_ids.count(slot) > 1}\n  assert len(\n      duplicates\n  ) == 0, f\"{duplicates} have multiple sparse feature name mapping, please investigate and retry!\"\n  for slot_id in slot_ids:\n    assert 0 < slot_id < 32768, \"slot_id should be in [1, 32768)\"\n\n  ## generate feature_description proto\n  feature_description = TFRecordFeatureDescription()\n  for k, v in sparse_features.items():\n    feature_description.sparse_features[k] = v\n  feature_description.dense_features.extend(dense_features)\n  feature_description.label = label\n  feature_description.instance_weight = instance_weight\n  return ragged_data_ops.MonolithTFExampleToExample(\n      input=serialized,\n      feature_description=feature_description.SerializeToString())\n"
  },
  {
    "path": "monolith/native_training/data/feature_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport io\nfrom absl import logging\nimport os\nimport uuid\nimport random\nimport struct\nimport tensorflow as tf\nimport tempfile\nimport threading\nfrom typing import List, BinaryIO\nfrom idl.matrix.proto import proto_parser_pb2, example_pb2, feature_pb2\nfrom monolith.native_training.data.datasets import PBDataset, PbType\nfrom monolith.native_training.data.parsers import parse_instances, \\\n  parse_examples\nfrom monolith.native_training.model_export.data_gen_utils import lg_header, sort_header\nfrom monolith.native_training.data.feature_utils import (\n    add_action, add_label, feature_combine, filter_by_fids, filter_by_label, filter_by_feature_value,\n    filter_by_value, scatter_label, switch_slot, switch_slot_batch, map_id, use_field_as_label,\n    label_upper_bound, label_normalization, multi_label_gen, string_to_variant,\n    variant_to_zeros, has_variant, negative_sample, gen_fid_mask)\n\nfid_v1_mask = (1 << 54) - 1\nfid_v2_mask = (1 << 48) - 1\n\n\ndef get_fid_v1(slot: int, signautre: int):\n  return (slot << 54) | (signautre & fid_v1_mask)\n\n\ndef get_fid_v2(slot: int, signature: int):\n  return (slot << 48) | (signature & fid_v2_mask)\n\n\nfeatures = {\n    'f_spm_1': 301,\n    'f_spm_3': 303,\n    'f_spm_2': 302,\n    'f_spm_4': 304,\n    'f_user_id': 1,\n    'f_user_ctx_network': 61,\n    'f_user_id-f_page': 504,\n    'f_scm': 306,\n    'f_goods_id': 200,\n    'f_goods_sale_number_1000': 225,\n    'f_goods_praise_cnt': 229,\n    'f_spm': 300,\n    'f_page': 305,\n    'f_is_dup': 310,\n    'f_user_ctx_platform': 52,\n    'f_goods_title_terms': 209,\n    'f_goods_tags_terms': 211,\n    'f_user_test09_array_int32': 554,\n    'f_user_test15_array_float': 540,\n    'f_user_test14_array_bool': 543,\n    'f_user_test12_array_uint64': 551,\n    'f_user_test10_array_int64': 549\n}\n\ngroup_slots = [\n    200, 201, 202, 203, 204, 205, 206, 210, 211, 212, 213, 214, 215, 216, 217,\n    218, 219, 220, 221, 222, 223, 224, 225, 230, 231, 232, 233, 234, 235, 236,\n    237, 238, 239, 240, 241, 242\n]\n\n\ndef parse_instance_or_example(tensor: tf.Tensor, out_type,\n                              extra_sparse_features: List[str] = None):\n  fidv1_features = [\n      1, 2, 32, 33, 36, 38, 42, 50, 54, 56, 60, 66, 120, 150, 180, 182, 192,\n      220, 333, 410, 412, 422, 446\n  ]\n  if out_type == PbType.INSTANCE:\n    return parse_instances(tensor,\n                           fidv1_features,\n                           dense_features=['label'],\n                           dense_feature_shapes=[2],\n                           dense_feature_types=[tf.float32],\n                           extra_features=[\n                               'uid', 'req_time', 'item_id', 'actions',\n                               'video_finish_percent'\n                           ],\n                           extra_feature_shapes=[1, 1, 1, 2, 1])\n  else:\n    return parse_examples(\n        tensor,\n        sparse_features=[f'fc_slot_{slot}' for slot in fidv1_features] + \\\n          extra_sparse_features if extra_sparse_features else [],\n        dense_features=['label'],\n        dense_feature_shapes=[2],\n        dense_feature_types=[tf.float32],\n        extra_features=[\n            'uid', 'req_time', 'item_id', 'actions', 'video_finish_percent'\n        ],\n        extra_feature_shapes=[1, 1, 1, 3, 1])\n\n\ndef parse_example_batch(tensor: tf.Tensor, out_type,\n                        extra_sparse_features: List[str] = None):\n  if out_type == PbType.INSTANCE:\n    feature_dict = parse_instances(tensor,\n                                   fidv1_features=list(features.values()),\n                                   dense_features=['label'],\n                                   dense_feature_shapes=[2],\n                                   dense_feature_types=[tf.float32],\n                                   extra_features=[\n                                       'uid', 'req_time', 'item_id', 'actions',\n                                       'video_finish_percent'\n                                   ],\n                                   extra_feature_shapes=[1, 1, 1, 3, 1])\n  else:\n    feature_dict = parse_examples(tensor,\n                                  sparse_features=list(features.keys()) + \\\n                                    extra_sparse_features if extra_sparse_features else [],\n                                  dense_features=['label'],\n                                  dense_feature_shapes=[2],\n                                  dense_feature_types=[tf.float32],\n                                  extra_features=[\n                                      'uid', 'req_time', 'item_id', 'actions',\n                                      'video_finish_percent'\n                                  ],\n                                  extra_feature_shapes=[1, 1, 1, 3, 1])\n    # print(feature_dict)\n    # feature_dict['f_page'] = switch_slot(feature_dict['f_page'], slot=306)\n    # feature_dict['f_user_id-f_goods_tags_terms'] = feature_combine(\n    #     feature_dict['f_user_id'], feature_dict['f_goods_tags_terms'], slot=505)\n  return feature_dict\n\ndef line_id_to_feature(name, value):\n  tmp_feature = feature_pb2.Feature()\n  tmp_feature.name = name\n  value_mapping = {\n      str: lambda val: tmp_feature.bytes_value.append(val.encode()),\n      int: tmp_feature.int64_value.append,\n      float: tmp_feature.float_value.append,\n  }\n  if isinstance(value, list):\n    if len(value) > 0:\n      value_type = type(value[0])\n  else:\n    value_type = type(value)\n  append_func = value_mapping.get(value_type)\n  if append_func:\n    if isinstance(value, list):\n      for val in value:\n          append_func(val)\n    else:\n      append_func(value)\n  return tmp_feature\n\ndef generate_instance(labels: List[int],\n                      actions: List[int],\n                      chnid: int = None,\n                      did: str = None,\n                      fid_v1_list: List[int] = None,\n                      device_type: str = None,\n                      req_id: str = None,\n                      chnids: List[int] = None,\n                      video_play_time: float = None,\n                      write_line_id_to_feature: bool = False,\n                      shuffle_features: bool = False):\n  instance = proto_parser_pb2.Instance()\n  instance.fid.extend(fid_v1_list if fid_v1_list else [])\n  instance.label.extend(labels)\n  instance.line_id.user_id = \"test_{}\".format(uuid.uuid4())\n  instance.line_id.uid = 100\n  instance.line_id.sample_rate = 0.5\n  instance.line_id.actions.extend(actions)\n  features = []\n  if chnid is not None:\n    instance.line_id.chnid = chnid\n    if write_line_id_to_feature:\n      features.append(line_id_to_feature('chnid', chnid))\n  if did is not None:\n    instance.line_id.did = did\n    if write_line_id_to_feature:\n      features.append(line_id_to_feature('did', did))\n  if device_type is not None:\n    instance.line_id.device_type = device_type\n  if req_id is not None:\n    instance.line_id.req_id = req_id\n    if write_line_id_to_feature:\n      features.append(line_id_to_feature('req_id', req_id))\n  if chnids is not None and write_line_id_to_feature:\n    features.append(line_id_to_feature('chnids', chnids))\n  if video_play_time is not None:\n    instance.line_id.video_play_time = video_play_time\n    if write_line_id_to_feature:\n      features.append(line_id_to_feature('video_play_time', video_play_time))\n  if shuffle_features:\n    random.shuffle(features)\n  instance.feature.extend(features)\n  return instance\n\n\ndef write_instance_into_file(file: BinaryIO, instance):\n  sort_id = str(instance.line_id.user_id)\n  file.write(struct.pack('<Q', len(sort_id)))\n  file.write(sort_id.encode())\n  instance_serialized = instance.SerializeToString()\n  file.write(struct.pack('<Q', len(instance_serialized)))\n  file.write(instance_serialized)\n  \n# file_name = \"monolith/native_training/data/training_instance/instance.pb\"\n# with tf.io.gfile.GFile(file_name, \"rb\") as f:\n#   instance = parse_instance_from_file(f, True, False, True)\n#   print(instance)\n  \ndef parse_instance_from_file(stream, has_kafka_dump, has_kafka_dump_prefix, has_sort_id):\n  size_t = 8\n  # kafka_prefix part, skip 16 bytes.\n  if has_kafka_dump_prefix:\n    stream.read(size_t * 2)\n  # sort id part, skip 8 bytes.\n  if has_sort_id:\n    size_binary = stream.read(size_t)[::-1]\n    size = struct.unpack('>Q', size_binary)[0]\n    sort_id = stream.read(size)\n  else:\n    sort_id = \"\"\n  # proto.\n  if has_kafka_dump:\n    stream.read(size_t)\n\n  # size + proto_binary\n  # This is the proto part.\n  size_binary = stream.read(size_t)[::-1]\n  size = struct.unpack('>Q', size_binary)[0]\n  proto_binary = stream.read(size)\n  instance = proto_parser_pb2.Instance()\n  instance.ParseFromString(proto_binary)\n  return instance\n\n\nclass DataOpsTest(tf.test.TestCase):\n  def pb_dataset_target(self,\n                        input_pb_type,\n                        output_pb_type,\n                        filter_fn=None,\n                        add_action_fn=None,\n                        return_result_key='actions',\n                        num_return_items=2):\n    if input_pb_type == PbType.INSTANCE:\n      lagrangex_header = False\n      has_sort_id, kafka_dump, kafka_dump_prefix = True, True, False\n      file_name = \"monolith/native_training/data/training_instance/instance.pb\"\n    elif input_pb_type == PbType.EXAMPLE:\n      lagrangex_header = False\n      has_sort_id, kafka_dump, kafka_dump_prefix = True, True, False\n      file_name = \"monolith/native_training/data/training_instance/example.pb\"\n    else:\n      lagrangex_header = True\n      has_sort_id, kafka_dump, kafka_dump_prefix = False, False, False\n      file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n\n    def parser(tensor: tf.Tensor):\n      if output_pb_type == PbType.PLAINTEXT:\n        return parse_instance_or_example(tensor, input_pb_type)\n      elif input_pb_type != PbType.EXAMPLEBATCH:\n        return parse_instance_or_example(tensor, output_pb_type)\n      else:\n        return parse_example_batch(tensor, output_pb_type)\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        dataset = PBDataset(file_name=file_name,\n                            lagrangex_header=lagrangex_header,\n                            has_sort_id=has_sort_id,\n                            kafka_dump=kafka_dump,\n                            kafka_dump_prefix=kafka_dump_prefix,\n                            input_pb_type=input_pb_type,\n                            output_pb_type=output_pb_type)\n        if add_action_fn is not None:\n          dataset = dataset.map(add_action_fn)\n\n        if input_pb_type == PbType.EXAMPLEBATCH:\n          variant_type = 'instance' if output_pb_type == PbType.INSTANCE else 'example'\n          dataset = dataset.instance_reweight(\n              action_priority=\"2,7,0,1,3,4,5,6,8,9,10,11\",\n              reweight=\n              \"0:0:1,1:0:1,2:3:-1,3:0:1,4:0:1,5:0:1,6:0:1,7:6:1,8:0:1,9:0:1,10:0:1,11:0:-1\",\n              variant_type=variant_type)\n        if filter_fn is not None:\n          dataset = dataset.filter(filter_fn)\n\n        batch_size = 4\n        dataset = dataset.batch(batch_size, drop_remainder=True).map(parser)\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n        element = it.get_next()\n\n        results = list()\n        for _ in range(num_return_items):\n          try:\n            element_result = sess.run(element)\n            results.append(element_result[return_result_key])\n          except tf.errors.OutOfRangeError:\n            break\n        return results\n\n  def test_input_instance_output_instance(self):\n    actions = self.pb_dataset_target(input_pb_type=PbType.INSTANCE,\n                                     output_pb_type=PbType.INSTANCE)\n    self.assertAllEqual(actions[0], [[1, 0], [1, 0], [1, 0], [1, 0]])\n    self.assertAllEqual(actions[1], [[1, 0], [1, 0], [1, 0], [1, 0]])\n\n  def test_input_instance_output_instance_add_action(self):\n    actions = self.pb_dataset_target(\n        input_pb_type=PbType.INSTANCE,\n        output_pb_type=PbType.INSTANCE,\n        add_action_fn=lambda variant: add_action(\n            variant, 'sample_rate', 'ge', 0, 2, variant_type='instance'))\n    self.assertAllEqual(actions[0], [[1, 2], [1, 2], [1, 2], [1, 2]])\n    self.assertAllEqual(actions[1], [[1, 2], [1, 2], [1, 2], [1, 2]])\n\n  def test_input_instance_output_example(self):\n    actions = self.pb_dataset_target(input_pb_type=PbType.INSTANCE,\n                                     output_pb_type=PbType.EXAMPLE)\n    self.assertAllEqual(actions[0],\n                        [[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]])\n    self.assertAllEqual(actions[1],\n                        [[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]])\n\n  def test_input_instance_output_example_add_action(self):\n    actions = self.pb_dataset_target(input_pb_type=PbType.INSTANCE,\n                                     output_pb_type=PbType.EXAMPLE,\n                                     add_action_fn=lambda variant: add_action(\n                                         variant,\n                                         'req_time',\n                                         'between', [1622667900, 1622667911],\n                                         2,\n                                         variant_type='example'))\n    self.assertAllEqual(actions[0],\n                        [[1, 2, 0], [1, 2, 0], [1, 2, 0], [1, 2, 0]])\n    self.assertAllEqual(actions[1],\n                        [[1, 2, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]])\n\n  def test_input_example_output_instance(self):\n    actions = self.pb_dataset_target(input_pb_type=PbType.EXAMPLE,\n                                     output_pb_type=PbType.INSTANCE)\n    self.assertAllEqual(actions[0], [[1, 0], [1, 0], [1, 0], [1, 0]])\n    self.assertAllEqual(actions[1], [[1, 0], [1, 0], [1, 0], [1, 0]])\n\n  def test_input_example_output_instance_add_action(self):\n    actions = self.pb_dataset_target(\n        input_pb_type=PbType.EXAMPLE,\n        output_pb_type=PbType.INSTANCE,\n        add_action_fn=lambda variant: add_action(variant,\n                                                 'req_time',\n                                                 'in', [1622667900, 1622667911],\n                                                 2,\n                                                 variant_type='instance'))\n    self.assertAllEqual(actions[0], [[1, 2], [1, 2], [1, 0], [1, 2]])\n    self.assertAllEqual(actions[1], [[1, 2], [1, 2], [1, 2], [1, 0]])\n\n  def test_input_example_output_example(self):\n    actions = self.pb_dataset_target(input_pb_type=PbType.EXAMPLE,\n                                     output_pb_type=PbType.EXAMPLE)\n    self.assertAllEqual(actions[0],\n                        [[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]])\n    self.assertAllEqual(actions[1],\n                        [[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]])\n\n  def test_input_example_output_example_add_action(self):\n    actions = self.pb_dataset_target(\n        input_pb_type=PbType.EXAMPLE,\n        output_pb_type=PbType.EXAMPLE,\n        add_action_fn=lambda variant: add_action(\n            variant, 'uid', 'eq', 62975225690081677, 2, variant_type='example'))\n    self.assertAllEqual(actions[0],\n                        [[1, 0, 0], [1, 0, 0], [1, 2, 0], [1, 0, 0]])\n    self.assertAllEqual(actions[1],\n                        [[1, 0, 0], [1, 0, 0], [1, 2, 0], [1, 0, 0]])\n\n  def test_input_example_batch_output_instance(self):\n    actions = self.pb_dataset_target(input_pb_type=PbType.EXAMPLEBATCH,\n                                     output_pb_type=PbType.INSTANCE)\n    self.assertAllEqual(actions[0],\n                        [[2, 0, 0], [2, 0, 0], [2, 0, 0], [2, 0, 0]])\n    self.assertAllEqual(actions[1],\n                        [[2, 0, 0], [2, 0, 0], [2, 0, 0], [2, 0, 0]])\n\n  def test_input_example_batch_output_instance_add_action(self):\n    actions = self.pb_dataset_target(\n        input_pb_type=PbType.EXAMPLEBATCH,\n        output_pb_type=PbType.INSTANCE,\n        add_action_fn=lambda variant: add_action(variant,\n                                                 'video_finish_percent',\n                                                 'ge',\n                                                 0,\n                                                 3,\n                                                 variant_type='instance'))\n    self.assertAllEqual(actions[0],\n                        [[2, 3, 0], [2, 3, 0], [2, 3, 0], [2, 3, 0]])\n    self.assertAllEqual(actions[1],\n                        [[2, 3, 0], [2, 3, 0], [2, 3, 0], [2, 3, 0]])\n\n  def test_input_example_batch_output_example(self):\n    actions = self.pb_dataset_target(input_pb_type=PbType.EXAMPLEBATCH,\n                                     output_pb_type=PbType.EXAMPLE)\n    self.assertAllEqual(actions[0],\n                        [[2, 0, 0], [2, 0, 0], [2, 0, 0], [2, 0, 0]])\n    self.assertAllEqual(actions[1],\n                        [[2, 0, 0], [2, 0, 0], [2, 0, 0], [2, 0, 0]])\n\n  def test_input_example_batch_output_example_add_action(self):\n    actions = self.pb_dataset_target(\n        input_pb_type=PbType.EXAMPLEBATCH,\n        output_pb_type=PbType.EXAMPLE,\n        add_action_fn=lambda variant: add_action(\n            variant, 'video_finish_percent', 'le', 0, 3, variant_type='example')\n    )\n    self.assertAllEqual(actions[0],\n                        [[2, 3, 0], [2, 3, 0], [2, 3, 0], [2, 3, 0]])\n    self.assertAllEqual(actions[1],\n                        [[2, 3, 0], [2, 3, 0], [2, 3, 0], [2, 3, 0]])\n\n  def test_input_instance_output_instance_add_label(self):\n    mock_batch_num = 100\n    add_label_config = '1,2:3:1.0;4::0.5'\n\n    def mock_instance_for_add_label(batch_num: int = 200):\n      tmpfile = tempfile.mkstemp()[1]\n      labels = [[], [], [], []]\n\n      # for task1: 1,2 -> positive, 3     -> negative\n      # for task2: 4   -> positive, other -> negative/invalid(depends on sampling)\n\n      # instance1: task1 -> positive, task2 -> positive\n      # instance2: task1 -> positive, task2 -> negative/invalid(depends on sampling)\n      # instance3: task1 -> negative, task2 -> positive\n      # instance4: task1 -> invalid,  task2 -> negative/invalid(depends on sampling)\n      actions = [[1, 2, 4], [1], [3, 4], [5]]\n      with io.open(tmpfile, 'wb') as writer:\n        for _ in range(batch_num):\n          for label, action in zip(labels, actions):\n            instance = generate_instance(label, action)\n            write_instance_into_file(writer, instance)\n      return tmpfile\n\n    file_name = mock_instance_for_add_label(mock_batch_num)\n    logging.info('file_name: %s', file_name)\n\n    def parser(tensor: tf.Tensor):\n      return parse_instances(tensor,\n                             fidv1_features=list(features.values()),\n                             dense_features=['label'],\n                             dense_feature_shapes=[2],\n                             dense_feature_types=[tf.float32],\n                             extra_features=[\n                                 'uid', 'req_time', 'item_id', 'actions',\n                                 'video_finish_percent'\n                             ],\n                             extra_feature_shapes=[1, 1, 1, 3, 1])\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        dataset = PBDataset(file_name=file_name,\n                            lagrangex_header=False,\n                            has_sort_id=True,\n                            kafka_dump=False,\n                            kafka_dump_prefix=False,\n                            input_pb_type=PbType.INSTANCE,\n                            output_pb_type=PbType.INSTANCE)\n        dataset = dataset.map(\n            lambda variant: add_label(variant,\n                                      config=add_label_config,\n                                      negative_value=-1.0,\n                                      new_sample_rate=1.0,\n                                      variant_type='instance'))\n        dataset = dataset.filter(lambda variant: filter_by_label(\n            variant, label_threshold=[-100, -100], variant_type='instance'))\n\n        batch_size = 4\n        dataset = dataset.batch(batch_size, drop_remainder=False).map(parser)\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n\n        valid_instance_num = 0\n        for _ in range(mock_batch_num):\n          try:\n            element = it.get_next()\n            element_result = sess.run(element)\n            valid_instance_num += len(element_result['label'])\n          except tf.errors.OutOfRangeError:\n            break\n    logging.info('Valid instance number: %d', valid_instance_num)\n    self.assertAllInRange(valid_instance_num, 340, 360)\n    os.remove(file_name)\n\n  def test_input_instance_output_instance_label_upper_bound(self):\n    labels = self.pb_dataset_target(\n        input_pb_type=PbType.INSTANCE,\n        output_pb_type=PbType.INSTANCE,\n        add_action_fn=lambda variant: label_upper_bound(\n            variant, label_upper_bounds=[0.5, 0.5], variant_type='instance'),\n        return_result_key='label')\n    self.assertAllEqual(labels[0], [[0, 0.5], [0, 0.5], [0, 0.5], [0, 0.5]])\n    self.assertAllEqual(labels[1], [[0, 0.5], [0, 0.5], [0, 0.5], [0, 0.5]])\n\n  def test_input_instance_output_instance_label_normalization(self):\n    labels = self.pb_dataset_target(\n        input_pb_type=PbType.INSTANCE,\n        output_pb_type=PbType.INSTANCE,\n        add_action_fn=lambda variant: label_normalization(\n            variant,\n            norm_methods=['scale', 'repow'],\n            norm_values=[0.5, 3],\n            variant_type='instance'),\n        return_result_key='label')\n    self.assertAllEqual(labels[0], [[0, 8], [0, 8], [0, 8], [0, 8]])\n    self.assertAllEqual(labels[1], [[0, 8], [0, 8], [0, 8], [0, 8]])\n\n  def test_input_examplebatch_output_instance_use_field_as_label(self):\n    labels = self.pb_dataset_target(\n        input_pb_type=PbType.INSTANCE,\n        output_pb_type=PbType.INSTANCE,\n        add_action_fn=lambda variant: use_field_as_label(\n            variant, 'sample_rate', False, 0, variant_type='instance'),\n        return_result_key='label')\n    self.assertAllEqual(labels[0], [[1, 1], [1, 1], [1, 1], [1, 1]])\n    self.assertAllEqual(labels[1], [[1, 1], [1, 1], [1, 1], [1, 1]])\n\n    labels = self.pb_dataset_target(\n        input_pb_type=PbType.INSTANCE,\n        output_pb_type=PbType.INSTANCE,\n        add_action_fn=lambda variant: use_field_as_label(label_upper_bound(\n            variant, label_upper_bounds=[0.5, 0.5], variant_type='instance'),\n                                                         'sample_rate',\n                                                         True,\n                                                         1.1,\n                                                         variant_type='instance'\n                                                        ),\n        return_result_key='label')\n    # Original label is [0, 0.5], new label = max(original_label, [1, 1])\n    self.assertAllEqual(labels[0], [[1, 1], [1, 1], [1, 1], [1, 1]])\n    self.assertAllEqual(labels[1], [[1, 1], [1, 1], [1, 1], [1, 1]])\n\n    labels = self.pb_dataset_target(\n        input_pb_type=PbType.INSTANCE,\n        output_pb_type=PbType.INSTANCE,\n        add_action_fn=lambda variant: use_field_as_label(label_upper_bound(\n            variant, label_upper_bounds=[0.5, 0.5], variant_type='instance'),\n                                                         'sample_rate',\n                                                         True,\n                                                         0.9,\n                                                         variant_type='instance'\n                                                        ),\n        return_result_key='label')\n    # Original label is [0, 0.5], new label = max(original_label, [0, 0])\n    self.assertAllEqual(labels[0], [[0, 0.5], [0, 0.5], [0, 0.5], [0, 0.5]])\n    self.assertAllEqual(labels[1], [[0, 0.5], [0, 0.5], [0, 0.5], [0, 0.5]])\n\n  def test_input_instance_output_instance_filter_by_label_equals(self):\n    labels = self.pb_dataset_target(\n        input_pb_type=PbType.INSTANCE,\n        output_pb_type=PbType.INSTANCE,\n        filter_fn=lambda variant: filter_by_label(variant,\n                                                  label_threshold=[0, 1],\n                                                  filter_equal=False,\n                                                  variant_type='instance'),\n        return_result_key='label',\n        num_return_items=100)\n    self.assertEqual(len(labels), 100)\n    self.assertAllEqual(labels[0], [[0, 1], [0, 1], [0, 1], [0, 1]])\n    self.assertAllEqual(labels[1], [[0, 1], [0, 1], [0, 1], [0, 1]])\n\n    labels = self.pb_dataset_target(\n        input_pb_type=PbType.INSTANCE,\n        output_pb_type=PbType.INSTANCE,\n        filter_fn=lambda variant: filter_by_label(variant,\n                                                  label_threshold=[0, 1],\n                                                  filter_equal=True,\n                                                  variant_type='instance'),\n        return_result_key='label',\n        num_return_items=100)\n    self.assertEqual(len(labels), 49)\n    self.assertAllEqual(labels[0], [[0, 2], [0, 2], [0, 2], [0, 2]])\n    self.assertAllEqual(labels[1], [[0, 2], [0, 2], [0, 2], [0, 2]])\n\n  def test_input_instance_output_instance_scatter_label(self):\n    mock_batch_num = 1\n    scatter_label_config = '100:3,200:1,300:4'\n\n    def mock_instance_for_scatter_label(batch_num: int = 200):\n      tmpfile = tempfile.mkstemp()[1]\n      labels = [[1], [2], [3], []]\n      actions = [[], [], [], []]\n      chnids = [0, 100, 200, 300]\n      with io.open(tmpfile, 'wb') as writer:\n        for _ in range(batch_num):\n          for label, action, chnid in zip(labels, actions, chnids):\n            instance = generate_instance(label, action, chnid)\n            write_instance_into_file(writer, instance)\n      return tmpfile\n\n    file_name = mock_instance_for_scatter_label(mock_batch_num)\n    logging.info('file_name: %s', file_name)\n\n    def parser(tensor: tf.Tensor):\n      return parse_instances(tensor,\n                             fidv1_features=list(features.values()),\n                             dense_features=['label'],\n                             dense_feature_shapes=[5],\n                             dense_feature_types=[tf.float32],\n                             extra_features=[\n                                 'uid', 'req_time', 'item_id', 'actions',\n                                 'video_finish_percent'\n                             ],\n                             extra_feature_shapes=[1, 1, 1, 3, 1])\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        dataset = PBDataset(file_name=file_name,\n                            lagrangex_header=False,\n                            has_sort_id=True,\n                            kafka_dump=False,\n                            kafka_dump_prefix=False,\n                            input_pb_type=PbType.INSTANCE,\n                            output_pb_type=PbType.INSTANCE)\n        dataset = dataset.map(lambda variant: scatter_label(\n            variant, config=scatter_label_config, variant_type='instance'))\n        dataset = dataset.filter(lambda variant: filter_by_label(\n            variant,\n            label_threshold=[-100, -100, -100, -100, -100],\n            variant_type='instance'))\n\n        batch_size = 4\n        dataset = dataset.batch(batch_size, drop_remainder=False).map(parser)\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n\n        try:\n          element = it.get_next()\n          element_result = sess.run(element)\n          self.assertAllEqual(len(element_result['label']), 2)\n          self.assertAllClose(\n              element_result['label'],\n              [[\n                  -3.4028235e+38, -3.4028235e+38, -3.4028235e+38, 2.0000000e+00,\n                  -3.4028235e+38\n              ],\n               [\n                   -3.4028235e+38, 3.0000000e+00, -3.4028235e+38,\n                   -3.4028235e+38, -3.4028235e+38\n               ]])\n        except tf.errors.OutOfRangeError:\n          self.assertTrue(False)\n    os.remove(file_name)\n\n  def test_filter_by_bytes_value(self):\n    mock_batch_num = 200\n    \n    def mock_instance_for_filter_by_bytes_value(batch_num: int = 200):\n      tmpfile = tempfile.mkstemp()[1]\n      labels = [[1], [2], [3], []]\n      actions = [[], [], [], []]\n      req_ids = ['abckjhfjh', 'kjhfjh', 'huggfyfixyz', '']\n      chnids = [10, 20, 30, 40]\n      dids = ['hello', 'world', '300', '400']\n      with io.open(tmpfile, 'wb') as writer:\n        for _ in range(batch_num):\n          for label, action, chnid, did, req_id in zip(labels, actions, chnids, dids, req_ids):\n            instance = generate_instance(label, action, chnid=chnid, did=did, req_id=req_id,\n                                         write_line_id_to_feature=True, shuffle_features=True)\n            write_instance_into_file(writer, instance)\n      return tmpfile\n\n    file_name = mock_instance_for_filter_by_bytes_value(mock_batch_num)\n    logging.info('file_name: %s', file_name)\n    \n    def parser(tensor: tf.Tensor):\n      return parse_examples(tensor,\n                            sparse_features=list(features.keys()),\n                            dense_features=['label', 'req_id'],\n                            dense_feature_shapes=[5, 1],\n                            dense_feature_types=[tf.float32, tf.string],\n                            extra_features=['uid', 'req_time', 'did'],\n                            extra_feature_shapes=[1, 1, 1])\n\n    config = tf.compat.v1.ConfigProto()\n    config.graph_options.rewrite_options.disable_meta_optimizer = True\n\n    def filter_fn(ts):\n      return filter_by_value(ts,\n                             field_name='req_id',\n                             op='endswith',\n                             variant_type='example',\n                             operand=['kjhfjh', 'huggfyfi'])\n\n    def feature_filter_fn(ts):\n      return filter_by_feature_value(ts,\n                             field_name='req_id',\n                             op='endswith',\n                             operand=['kjhfjh', 'huggfyfi'],\n                             field_type='bytes')\n\n    with self.session(config=config) as sess:\n      dataset = PBDataset(file_name=file_name,\n                          lagrangex_header=False,\n                          has_sort_id=True,\n                          kafka_dump=False,\n                          kafka_dump_prefix=False,\n                          input_pb_type=PbType.INSTANCE,\n                          output_pb_type=PbType.EXAMPLE)\n      dataset_filter = dataset.filter(filter_fn)\n      dataset_feature_filter = dataset.filter(feature_filter_fn)\n      batch_size = 4\n      dataset_filter = dataset_filter.batch(batch_size, drop_remainder=False).map(parser)\n      dataset_feature_filter = dataset_feature_filter.batch(batch_size, drop_remainder=False).map(parser)\n\n      num_parallel_calls = 4\n      dataset_feature_filter_parallel = dataset.map(map_func=lambda x: x, num_parallel_calls=num_parallel_calls) \\\n                                               .filter(feature_filter_fn) \\\n                                               .batch(100, drop_remainder=False).map(parser)\n\n      try:\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset_filter)\n        element = it.get_next()\n        result = sess.run(element)\n        self.assertAllEqual(len(result['req_id']), 4)\n        self.assertAllEqual(result['req_id'], [[b'abckjhfjh'], [b'kjhfjh'], [b'abckjhfjh'], [b'kjhfjh']])\n\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset_feature_filter)\n        element = it.get_next()\n        result = sess.run(element)\n        self.assertAllEqual(len(result['req_id']), 4)\n        self.assertAllEqual(result['req_id'], [[b'abckjhfjh'], [b'kjhfjh'], [b'abckjhfjh'], [b'kjhfjh']])\n        \n        # test for parallelism (\"cached_feature_index\" in kernel impl)\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset_feature_filter_parallel)\n        element = it.get_next()\n        result = sess.run(element)\n        self.assertAllEqual(len(result['req_id']), 100)\n        self.assertAllEqual(result['req_id'], [[b'abckjhfjh'], [b'kjhfjh']] * 50)\n\n      except tf.errors.OutOfRangeError:\n        self.assertTrue(False)\n\n    os.remove(file_name)\n    \n  def test_filter_by_float_value(self):\n    mock_batch_num = 200\n    \n    def mock_instance_for_filter_by_float_value(batch_num: int = 200):\n      tmpfile = tempfile.mkstemp()[1]\n      labels = [[1], [2], [3], []]\n      actions = [[], [], [], []]\n      req_ids = ['abckjhfjh', 'kjhfjh', 'huggfyfixyz', 'mbzc']\n      video_play_times = [1.0, 2.0, 3.0, 4.0]\n      with io.open(tmpfile, 'wb') as writer:\n        for _ in range(batch_num):\n          for label, action, req_id, video_play_time in zip(labels, actions, req_ids, video_play_times):\n            instance = generate_instance(label, action, req_id=req_id, video_play_time=video_play_time,\n                                         write_line_id_to_feature=True, shuffle_features=True)\n            write_instance_into_file(writer, instance)\n      return tmpfile\n\n    file_name = mock_instance_for_filter_by_float_value(mock_batch_num)\n    logging.info('file_name: %s', file_name)\n    \n    def parser(tensor: tf.Tensor):\n      return parse_examples(tensor,\n                            sparse_features=list(features.keys()),\n                            dense_features=['label', 'req_id'],\n                            dense_feature_shapes=[5, 1],\n                            dense_feature_types=[tf.float32, tf.string],\n                            extra_features=['uid', 'req_time', 'did'],\n                            extra_feature_shapes=[1, 1, 1])\n\n    config = tf.compat.v1.ConfigProto()\n    config.graph_options.rewrite_options.disable_meta_optimizer = True\n\n    def feature_filter_fn(ts):\n      return filter_by_feature_value(ts,\n                             field_name='video_play_time',\n                             op='gt',\n                             operand=2.5,\n                             field_type='float')\n\n    with self.session(config=config) as sess:\n      dataset = PBDataset(file_name=file_name,\n                          lagrangex_header=False,\n                          has_sort_id=True,\n                          kafka_dump=False,\n                          kafka_dump_prefix=False,\n                          input_pb_type=PbType.INSTANCE,\n                          output_pb_type=PbType.EXAMPLE)\n      dataset_feature_filter = dataset.filter(feature_filter_fn)\n      batch_size = 4\n      dataset_feature_filter = dataset_feature_filter.batch(batch_size, drop_remainder=False).map(parser)\n\n\n      try:\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset_feature_filter)\n        element = it.get_next()\n        result = sess.run(element)\n        self.assertAllEqual(len(result['req_id']), 4)\n        self.assertAllEqual(result['req_id'], [[b'huggfyfixyz'], [b'mbzc'], [b'huggfyfixyz'], [b'mbzc']])\n\n      except tf.errors.OutOfRangeError:\n        self.assertTrue(False)\n\n    os.remove(file_name)\n\n\n  def test_filter_by_value_not_in(self):\n    mock_batch_num = 1\n\n    def mock_instance_for_filter_by_value(batch_num: int = 200):\n      tmpfile = tempfile.mkstemp()[1]\n      labels = [[1], [2], [3], [], []]\n      actions = [[], [], [], [], []]\n      chnids = [10, 20, 30, 40, 666]\n      dids = ['hello', 'world', 'excluded', '300', '400']\n      with io.open(tmpfile, 'wb') as writer:\n        for _ in range(batch_num):\n          for label, action, chnid, did in zip(labels, actions, chnids, dids):\n            instance = generate_instance(label, action, chnid, did, write_line_id_to_feature=True)\n            write_instance_into_file(writer, instance)\n      return tmpfile\n\n    file_name = mock_instance_for_filter_by_value(mock_batch_num)\n    logging.info('file_name: %s', file_name)\n\n    # generate FilterValues serialized files\n    tmp_filter_values_file_string = tempfile.mkstemp()[1]\n    with tf.io.gfile.GFile(tmp_filter_values_file_string, 'w') as f:\n      filter_values = example_pb2.FilterValues()\n      filter_values.bytes_list.value.extend([b'hello', b'world', b'excluded'])\n      f.write(filter_values.SerializeToString())\n    tmp_filter_values_file_int64 = tempfile.mkstemp()[1]\n    with tf.io.gfile.GFile(tmp_filter_values_file_int64, 'w') as f:\n      filter_values = example_pb2.FilterValues()\n      filter_values.int64_list.value.extend([20, 30, 666])\n      f.write(filter_values.SerializeToString())\n\n    def parser(tensor: tf.Tensor):\n      return parse_examples(tensor,\n                            sparse_features=list(features.keys()),\n                            dense_features=['label'],\n                            dense_feature_shapes=[5],\n                            dense_feature_types=[tf.float32],\n                            extra_features=['uid', 'req_time', 'did'],\n                            extra_feature_shapes=[1, 1, 1])\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        dataset_base = PBDataset(file_name=file_name,\n                                 lagrangex_header=False,\n                                 has_sort_id=True,\n                                 kafka_dump=False,\n                                 kafka_dump_prefix=False,\n                                 input_pb_type=PbType.INSTANCE,\n                                 output_pb_type=PbType.EXAMPLE)\n        dataset_filter_by_list = dataset_base.filter(\n            lambda variant: filter_by_value(variant,\n                                            field_name='did',\n                                            op='not-in',\n                                            operand=['hello', 'world'],\n                                            variant_type='example'))\n        dataset_filter_by_file_string = dataset_base.filter(\n            lambda variant: filter_by_value(variant,\n                                            field_name='did',\n                                            op='not-in',\n                                            operand=None,\n                                            operand_filepath=\n                                            tmp_filter_values_file_string,\n                                            variant_type='example'))\n        dataset_filter_by_file_int64 = dataset_base.filter(\n            lambda variant: filter_by_value(variant,\n                                            field_name='chnid',\n                                            op='in',\n                                            operand=None,\n                                            operand_filepath=\n                                            tmp_filter_values_file_int64,\n                                            variant_type='example'))\n        dataset_feature_filter_by_list = dataset_base.filter(\n            lambda variant: filter_by_feature_value(variant,\n                                                    field_name='did',\n                                                    op='not-in',\n                                                    operand=['hello', 'world'],\n                                                    field_type='bytes'))\n        dataset_feature_filter_by_file_string = dataset_base.filter(\n            lambda variant: filter_by_feature_value(variant,\n                                                    field_name='did',\n                                                    op='not-in',\n                                                    operand=None,\n                                                    operand_filepath=\n                                                    tmp_filter_values_file_string,\n                                                    field_type='bytes'))\n        dataset_feature_filter_by_file_int64 = dataset_base.filter(\n            lambda variant: filter_by_feature_value(variant,\n                                                    field_name='chnid',\n                                                    op='in',\n                                                    operand=None,\n                                                    operand_filepath=\n                                                    tmp_filter_values_file_int64,\n                                                    field_type='int64'))\n\n        batch_size = 5\n        dataset_filter_by_list = dataset_filter_by_list.batch(\n            batch_size, drop_remainder=False).map(parser)\n        dataset_filter_by_file_string = dataset_filter_by_file_string.batch(\n            batch_size, drop_remainder=False).map(parser)\n        dataset_filter_by_file_int64 = dataset_filter_by_file_int64.batch(\n            batch_size, drop_remainder=False).map(parser)\n        dataset_feature_filter_by_list = dataset_feature_filter_by_list.batch(\n            batch_size, drop_remainder=False).map(parser)\n        dataset_feature_filter_by_file_string = dataset_feature_filter_by_file_string.batch(\n            batch_size, drop_remainder=False).map(parser)\n        dataset_feature_filter_by_file_int64 = dataset_feature_filter_by_file_int64.batch(\n            batch_size, drop_remainder=False).map(parser)\n\n        try:\n          # test for filter by not-in list\n          it = tf.compat.v1.data.make_one_shot_iterator(dataset_filter_by_list)\n          element = it.get_next()\n          element_result = sess.run(element)\n          self.assertAllEqual(len(element_result['did']), 3)\n          self.assertAllEqual(element_result['did'], [[b'excluded'], [b'300'], [b'400']])\n\n          it = tf.compat.v1.data.make_one_shot_iterator(dataset_feature_filter_by_list)\n          element = it.get_next()\n          element_result = sess.run(element)\n          self.assertAllEqual(len(element_result['did']), 3)\n          self.assertAllEqual(element_result['did'], [[b'excluded'], [b'300'], [b'400']])\n\n          # test for filter by not-in file\n          it = tf.compat.v1.data.make_one_shot_iterator(\n              dataset_filter_by_file_string)\n          element = it.get_next()\n          element_result = sess.run(element)\n          self.assertAllEqual(len(element_result['did']), 2)\n          self.assertAllEqual(element_result['did'], [[b'300'], [b'400']])\n          \n          it = tf.compat.v1.data.make_one_shot_iterator(\n              dataset_feature_filter_by_file_string)\n          element = it.get_next()\n          element_result = sess.run(element)\n          self.assertAllEqual(len(element_result['did']), 2)\n          self.assertAllEqual(element_result['did'], [[b'300'], [b'400']])\n\n          # test for filter by in file\n          it = tf.compat.v1.data.make_one_shot_iterator(\n              dataset_filter_by_file_int64)\n          element = it.get_next()\n          element_result = sess.run(element)\n          self.assertAllEqual(len(element_result['did']), 3)\n          self.assertAllEqual(element_result['did'], [[b'world'], [b'excluded'], [b'400']])\n\n          it = tf.compat.v1.data.make_one_shot_iterator(\n              dataset_feature_filter_by_file_int64)\n          element = it.get_next()\n          element_result = sess.run(element)\n          self.assertAllEqual(len(element_result['did']), 3)\n          self.assertAllEqual(element_result['did'], [[b'world'], [b'excluded'], [b'400']])\n\n        except tf.errors.OutOfRangeError:\n          self.assertTrue(False)\n\n    os.remove(file_name)\n    os.remove(tmp_filter_values_file_string)\n    os.remove(tmp_filter_values_file_int64)\n    \n  def test_filter_by_value_all(self):\n    mock_batch_num = 1\n\n    def mock_instance_for_filter_by_value(batch_num: int = 200):\n      tmpfile = tempfile.mkstemp()[1]\n      labels = [[1], [2], [3], [], []]\n      actions = [[], [], [], [], []]\n      multi_chnids = [[10], [20, 30], [20, 30, 666], [40], [666]]\n      dids = ['hello', 'world', 'excluded', '300', '400']\n      with io.open(tmpfile, 'wb') as writer:\n        for _ in range(batch_num):\n          for label, action, chnids, did in zip(labels, actions, multi_chnids, dids):\n            instance = generate_instance(label, action, chnid=None, did=did, chnids=chnids, write_line_id_to_feature=True)\n            write_instance_into_file(writer, instance)\n      return tmpfile\n\n    file_name = mock_instance_for_filter_by_value(mock_batch_num)\n    logging.info('file_name: %s', file_name)\n\n    def parser(tensor: tf.Tensor):\n      return parse_examples(tensor,\n                            sparse_features=list(features.keys()),\n                            dense_features=['label'],\n                            dense_feature_shapes=[5],\n                            dense_feature_types=[tf.float32],\n                            extra_features=['uid', 'req_time', 'did'],\n                            extra_feature_shapes=[1, 1, 1])\n      \n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        dataset_base = PBDataset(file_name=file_name,\n                                 lagrangex_header=False,\n                                 has_sort_id=True,\n                                 kafka_dump=False,\n                                 kafka_dump_prefix=False,\n                                 input_pb_type=PbType.INSTANCE,\n                                 output_pb_type=PbType.EXAMPLE)\n        dataset_feature_filter_by_file_int64 = dataset_base.filter(\n              lambda variant: filter_by_feature_value(variant,\n                                                      field_name='chnids',\n                                                      op='all',\n                                                      operand=[20, 30, 666],\n                                                      operand_filepath=None,\n                                                      field_type='int64'))\n        batch_size = 5\n        dataset_feature_filter_by_file_int64 = dataset_feature_filter_by_file_int64.batch(\n            batch_size, drop_remainder=False).map(parser)\n\n        try:\n          it = tf.compat.v1.data.make_one_shot_iterator(\n              dataset_feature_filter_by_file_int64)\n          element = it.get_next()\n          element_result = sess.run(element)\n          self.assertAllEqual(len(element_result['did']), 1)\n          self.assertAllEqual(element_result['did'], [[b'excluded']])\n\n        except tf.errors.OutOfRangeError:\n          self.assertTrue(False)\n    \n    os.remove(file_name)\n\n  def test_map_id(self):\n    inputs = tf.constant([123, 456, 789, 912], dtype=tf.int32)\n    map_dict = {123: 0, 456: 1, 789: 2}\n    config = tf.compat.v1.ConfigProto()\n    config.graph_options.rewrite_options.disable_meta_optimizer = True\n    with self.session(config=config) as sess:\n      out_ts = map_id(tensor=inputs, map_dict=map_dict)\n      out = sess.run(out_ts)\n      self.assertListEqual(list(out), [0, 1, 2, -1])\n\n  def test_filter_by_fids(self):\n    mock_batch_num = 1\n    batch_size = 4\n\n    def mock_instance(batch_num: int = 200):\n      tmpfile = tempfile.mkstemp()[1]\n      with io.open(tmpfile, 'wb') as writer:\n        for _ in range(batch_num):\n          for i in range(batch_size + 1):\n            instance = generate_instance(\n                [], [],\n                fid_v1_list=[get_fid_v1(2, i),\n                             get_fid_v1(3, i)] if i > 0 else [get_fid_v1(2, i)])\n            write_instance_into_file(writer, instance)\n      return tmpfile\n\n    file_name = mock_instance(mock_batch_num)\n    logging.info('file_name: %s', file_name)\n\n    def parser(tensor: tf.Tensor):\n      return parse_instances(tensor, fidv1_features=[2, 3])\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        dataset = PBDataset(file_name=file_name,\n                            lagrangex_header=False,\n                            has_sort_id=True,\n                            kafka_dump=False,\n                            kafka_dump_prefix=False,\n                            input_pb_type=PbType.INSTANCE,\n                            output_pb_type=PbType.INSTANCE)\n        dataset = dataset.filter(lambda variant: filter_by_fids(\n            variant, select_slots=[2, 3], variant_type='instance'))\n\n        dataset = dataset.batch(batch_size, drop_remainder=False).map(parser)\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n\n        try:\n          element = it.get_next()\n          element_result = sess.run(element)\n          self.assertAllEqual(element_result['slot_2'].values,\n                              [get_fid_v1(2, i + 1) for i in range(batch_size)])\n          self.assertAllEqual(element_result['slot_3'].values,\n                              [get_fid_v1(3, i + 1) for i in range(batch_size)])\n        except tf.errors.OutOfRangeError:\n          self.assertTrue(False)\n    os.remove(file_name)\n\n  def test_multi_label_gen(self):\n    mock_batch_num = 1\n    head_to_idx = {'ios': 3, 'wp': 1, 'android': 4, 'other': 0}\n\n    def mock_instance_for_multi_label_gen(batch_num: int = 10):\n      tmpfile = tempfile.mkstemp()[1]\n      labels = [[1], [2], [3], [1]]\n      actions = [[1, 2], [3], [2], [1]]\n      chnids = [0, 100, 200, 300]\n      device_types = ['ios', 'wp', 'android', 'ios']\n      with io.open(tmpfile, 'wb') as writer:\n        for _ in range(batch_num):\n          for label, action, chnid, device_type in zip(labels, actions, chnids,\n                                                       device_types):\n            instance = generate_instance(label,\n                                         action,\n                                         chnid,\n                                         device_type=device_type)\n            write_instance_into_file(writer, instance)\n      return tmpfile\n\n    file_name = mock_instance_for_multi_label_gen(mock_batch_num)\n    logging.info('file_name: %s', file_name)\n\n    def parser(tensor: tf.Tensor):\n      return parse_instances(tensor,\n                             dense_features=['label'],\n                             dense_feature_shapes=[5],\n                             dense_feature_types=[tf.float32],\n                             extra_features=[\n                                 'uid', 'req_time', 'item_id', 'actions',\n                                 'device_type'\n                             ],\n                             extra_feature_shapes=[1, 1, 1, 3, 1])\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        dataset = PBDataset(file_name=file_name,\n                            lagrangex_header=False,\n                            has_sort_id=True,\n                            kafka_dump=False,\n                            kafka_dump_prefix=False,\n                            input_pb_type=PbType.INSTANCE,\n                            output_pb_type=PbType.INSTANCE)\n        dataset = dataset.map(\n            lambda variant: multi_label_gen(variant,\n                                            head_to_index=head_to_idx,\n                                            head_field='device_type',\n                                            use_origin_label=False,\n                                            pos_actions=[3, 2],\n                                            neg_actions=[1],\n                                            action_priority='4,3,2,1,0',\n                                            variant_type='instance'))\n\n        batch_size = 4\n        dataset = dataset.batch(batch_size, drop_remainder=False).map(parser)\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n        try:\n          element = it.get_next()\n          element_result = sess.run(element)\n          self.assertAllClose(\n              element_result['label'],\n              [[\n                  -3.4028235e+38, -3.4028235e+38, -3.4028235e+38, 1.0000000e+00,\n                  -3.4028235e+38\n              ],\n               [\n                   -3.4028235e+38, 1.0000000e+00, -3.4028235e+38,\n                   -3.4028235e+38, -3.4028235e+38\n               ],\n               [\n                   -3.4028235e+38, -3.4028235e+38, -3.4028235e+38,\n                   -3.4028235e+38, 1.0000000e+00\n               ],\n               [\n                   -3.4028235e+38, -3.4028235e+38, -3.4028235e+38,\n                   0.0000000e+00, -3.4028235e+38\n               ]])\n        except tf.errors.OutOfRangeError:\n          self.assertTrue(False)\n    os.remove(file_name)\n\n  def test_string_to_variant(self):\n    insts = []\n    has_header, lg_header_flag = True, False\n    sort_id, kafka_dump, kafka_dump_prefix = True, False, True\n    for i in range(10):\n      inst = proto_parser_pb2.Instance()\n      inst.fid.extend([i for i in range(1, 20)])\n      inst.line_id.chnid = 1\n      inst_str = inst.SerializeToString()\n      if lg_header_flag:\n        header = lg_header(None)\n      else:\n        header = sort_header(sort_id, kafka_dump, kafka_dump_prefix)\n      if i == 3:\n        inst_str = b''\n      if has_header:\n        data = struct.pack(f'<{len(header)}sQ{len(inst_str)}s', header,\n                           len(inst_str), inst_str)\n      else:\n        data = inst_str\n      insts.append(data)\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      ips = tf.constant(value=insts, dtype=tf.string, shape=(10,), name='insts')\n      ops = string_to_variant(ips,\n                              variant_type='instance',\n                              has_header=has_header,\n                              lagrangex_header=lg_header_flag,\n                              has_sort_id=sort_id,\n                              kafka_dump=kafka_dump,\n                              kafka_dump_prefix=kafka_dump_prefix,\n                              chnids=[1, 2],\n                              datasources=[\"1\", \"2\"],\n                              default_datasource='3')\n      zeros = variant_to_zeros(ops)\n      with self.session(config=config) as sess:\n        element_result = sess.run(zeros)\n    self.assertAllEqual(ips.shape, ops.shape)\n\n  def test_has_variant(self):\n    inst = proto_parser_pb2.Instance()\n    inst.fid.extend([i for i in range(1, 20)])\n    inst_str = inst.SerializeToString()\n    data = struct.pack(f'<Q{len(inst_str)}s', len(inst_str), inst_str)\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      ips = tf.constant(value=[data], dtype=tf.string, shape=tuple())\n      ops = string_to_variant(ips,\n                              variant_type='instance',\n                              has_header=True,\n                              lagrangex_header=False,\n                              has_sort_id=False,\n                              kafka_dump=False,\n                              kafka_dump_prefix=False)\n      out = has_variant(ops, variant_type='instance')\n      with self.session(config=config) as sess:\n        element_result = sess.run(out)\n        self.assertTrue(element_result)\n\n  def test_switch_slot_batch(self):\n    input_pb_type = PbType.EXAMPLE\n    output_pb_type = PbType.EXAMPLE\n    if input_pb_type == PbType.EXAMPLE:\n      lagrangex_header = False\n      has_sort_id, kafka_dump, kafka_dump_prefix = True, True, False\n      file_name = \"monolith/native_training/data/training_instance/example.pb\"\n      variant_type = 'example'\n    else:\n      lagrangex_header = True\n      has_sort_id, kafka_dump, kafka_dump_prefix = False, False, False\n      file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n      variant_type = 'example_batch'\n\n    ss_meta, shared_slot = {}, 10\n    selected_slots = [50, 54, 56, 60, 66, 120, 150, 180, 182]\n    for slot in selected_slots:\n      if slot in {60, 66, 120, 150}:\n        ss_meta[f'fc_slot_{slot}'] = (False, shared_slot)\n      else:\n        ss_meta[f'fc_slot_{slot}'] = (True, shared_slot)\n\n    def parser(tensor: tf.Tensor):\n      extra_sf = []\n      for name, (inplace, _) in ss_meta.items():\n        if not inplace:\n          extra_sf.append(f'{name}_share')\n      if output_pb_type == PbType.PLAINTEXT:\n        return parse_instance_or_example(tensor, input_pb_type, extra_sf)\n      elif input_pb_type != PbType.EXAMPLEBATCH:\n        return parse_instance_or_example(tensor, output_pb_type, extra_sf)\n      else:\n        return parse_example_batch(tensor, output_pb_type, extra_sf)\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        dataset = PBDataset(file_name=file_name,\n                            lagrangex_header=lagrangex_header,\n                            has_sort_id=has_sort_id,\n                            kafka_dump=kafka_dump,\n                            kafka_dump_prefix=kafka_dump_prefix,\n                            input_pb_type=input_pb_type,\n                            output_pb_type=output_pb_type)\n        if output_pb_type == PbType.EXAMPLE:\n          batch_size = 4\n          dataset = dataset.batch(batch_size, drop_remainder=True)\n        dataset = dataset.map(lambda batch: switch_slot_batch(batch, ss_meta, variant_type))\n        dataset = dataset.map(parser)  # convert fid v1 -> v2\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n        element = it.get_next()\n\n        for _ in range(10):\n          try:\n            element_result = sess.run(element)\n            for name, (inplace, slot) in ss_meta.items():\n              if not inplace:\n                ragged_tensor = element_result[f'{name}_share']\n                for value in ragged_tensor.values:\n                  self.assertEqual(value >> 48, shared_slot)\n                ragged_tensor = element_result[name]\n                for value in ragged_tensor.values:\n                  self.assertNotEqual(value >> 48, shared_slot)\n              else:\n                ragged_tensor = element_result[name]\n                for value in ragged_tensor.values:\n                  self.assertEqual(value >> 48, shared_slot)\n          except tf.errors.OutOfRangeError:\n            break\n\n  def test_gen_fid_mask_int64(self):\n    config = tf.compat.v1.ConfigProto()\n    config.graph_options.rewrite_options.disable_meta_optimizer = True\n    with self.session(config=config) as sess:\n      ragged: tf.RaggedTensor = tf.ragged.constant([[1, 2, 3], [3], [], [4, 5, 6]], dtype=tf.int64)\n      mask_ts = gen_fid_mask(ragged, 3)\n      mask = sess.run(mask_ts)\n      exp_res = [1., 1., 0., 0.]\n      self.assertListEqual(list(mask), exp_res)\n\n  def test_gen_fid_mask_int32(self):\n    config = tf.compat.v1.ConfigProto()\n    config.graph_options.rewrite_options.disable_meta_optimizer = True\n    with self.session(config=config) as sess:\n      ragged: tf.RaggedTensor = tf.ragged.constant([[1, 2, 3], [3], [], [4, 5, 6]],\n        dtype=tf.int64, row_splits_dtype=tf.int32)\n      mask_ts = gen_fid_mask(ragged, 3)\n      mask = sess.run(mask_ts)\n      exp_res = [1., 1., 0., 0.]\n      self.assertListEqual(list(mask), exp_res)\n\n\n  def test_negative_sample_with_positive_actions(self):\n    neg_counter = 0\n    filt_counter = 0\n    neg_counter_mismatch = 0\n    filt_counter_mismatch = 0\n    for i in range(1000):\n      inst = proto_parser_pb2.Instance()\n      if i % 11 == 0:\n        inst.label.append(1)\n      else:\n        inst.label.append(0)\n      if i % 5 == 0:\n        # match action=2, drop_rate=0\n        inst.line_id.actions.extend([1, 2, 4, 5])\n      elif i % 5 == 1:\n        # match action=3, drop_rate=1\n        inst.line_id.actions.extend([1, 3, 5])\n      elif i % 5 == 2:\n        # match action=5, drop_rate=0.22\n        inst.line_id.actions.extend([5, 6])\n      elif i % 5 == 3:\n        # match priority, but not in per_action_drop_rate\n        inst.line_id.actions.extend([6])\n      elif i % 5 == 4:\n        # mismatch\n        inst.line_id.actions.extend([10, 11, 12])\n      inst_str = inst.SerializeToString()\n      data = struct.pack(f\"<Q{len(inst_str)}s\", len(inst_str), inst_str)\n\n      with tf.Graph().as_default():\n        config = tf.compat.v1.ConfigProto()\n        config.graph_options.rewrite_options.disable_meta_optimizer = True\n        ipts = tf.constant(value=[data], dtype=tf.string, shape=tuple())\n        variant_op = string_to_variant(ipts,\n                                      variant_type=\"instance\",\n                                      has_header=True,\n                                      lagrangex_header=False,\n                                      has_sort_id=False,\n                                      kafka_dump=False,\n                                      kafka_dump_prefix=False)\n        filter_op = negative_sample(variant_op,\n                                    drop_rate=0.88,\n                                    label_index=0,\n                                    threshold=0.5,\n                                    variant_type=\"instance\",\n                                    action_priority=\"2,3,4,1,5,6\",\n                                    per_action_drop_rate=\"1:1.0,2:0.0,3:1.0,4:0.5,5:0.22\")\n        with self.session(config=config) as sess:\n          filter_result = sess.run(filter_op)\n          if i % 11 == 0:\n            self.assertTrue(filter_result)\n          elif i % 5 == 0:\n            self.assertTrue(filter_result)\n          elif i % 5 == 1:\n            self.assertFalse(filter_result)\n          elif i % 5 == 2:\n            neg_counter += 1\n            if not filter_result:\n              filt_counter += 1\n          else:\n            neg_counter_mismatch += 1\n            if not filter_result:\n              filt_counter_mismatch += 1\n    logging.info(\"drop_rate_match: {:2f}, drop_rate_mismatch: {:.2f}\".format(filt_counter/neg_counter, filt_counter_mismatch/neg_counter_mismatch))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/item_pool_hook.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport re\nimport os\nfrom absl import logging\nimport tensorflow as tf\n\nfrom tensorflow.python.training.session_run_hook import SessionRunHook, \\\n  SessionRunContext, SessionRunValues\nfrom monolith.native_training.data.feature_utils import save_item_pool, restore_item_pool\nfrom monolith.native_training.data.datasets import POOL_KEY\nfrom monolith.native_training.utils import get_local_host\n\n\nclass ItemPoolSaveRestoreHook(SessionRunHook):\n\n  def __init__(self, model_dir: str, save_steps: int, mode: str = 'train'):\n    self._model_dir = model_dir\n    self._mode = mode\n    self._last_global_step = None\n    self._save_steps = save_steps\n    self._ckpt_state = None\n\n    self._save_op = None\n    self._restore_op = None\n    self._global_step_tensor = None\n\n  def begin(self):\n    pools = tf.compat.v1.get_collection(POOL_KEY)\n    self._global_step_tensor = tf.compat.v1.train.get_or_create_global_step()\n    if pools:\n      self._save_global_step = tf.compat.v1.placeholder(dtype=tf.int64)\n      self._restore_global_step = tf.compat.v1.placeholder(dtype=tf.int64)\n      # find the corresponding ckpt dir\n      logging.info(\"get the ckpt_state from model_dir: {}\".format(\n          self._model_dir))\n      self._ckpt_state = tf.train.get_checkpoint_state(self._model_dir)\n      if self._ckpt_state:\n        model_ckpt_path = self._ckpt_state.model_checkpoint_path\n        logging.info(\"the path to model.ckpt: {}\".format(model_ckpt_path))\n        restore_dir = os.path.dirname(self._ckpt_state.model_checkpoint_path)\n        self._restore_op = restore_item_pool(\n            pool=pools[0],\n            global_step=self._restore_global_step,\n            model_path=restore_dir)\n      self._save_op = save_item_pool(pool=pools[0],\n                                     global_step=self._save_global_step,\n                                     model_path=self._model_dir)\n\n  def after_create_session(self, session, coord):  # pylint: disable=unused-argument\n    if self._mode == tf.estimator.ModeKeys.PREDICT:\n      return\n\n    self._last_global_step = session.run(self._global_step_tensor)\n    if self._restore_op is not None:\n      logging.info(\"the last global step is {}\".format(\n          str(self._last_global_step)))\n      if self._ckpt_state:\n        step = int(\n            self._ckpt_state.model_checkpoint_path.split('/')[-1].split('-')\n            [-1])\n        logging.info(\"the step from the last checkpoint is {}\".format(\n            str(step)))\n        session.run(self._restore_op,\n                    feed_dict={self._restore_global_step: step})\n        logging.info(\n            \"after_create_session retore the itempool from ckpt: {}\".format(\n                str(step)))\n\n  def after_run(\n      self,\n      run_context: SessionRunContext,  # pylint: disable=unused-argument\n      run_values: SessionRunValues):  # pylint: disable=unused-argument\n    if self._mode != tf.estimator.ModeKeys.TRAIN:\n      return\n\n    if self._save_op is not None and self._save_steps is not None and self._save_steps > 0:\n      cur_global_step = run_context.session.run(self._global_step_tensor)\n      if cur_global_step > self._last_global_step + self._save_steps:\n        logging.info(\"after_run start to save item_pool at step {}\".format(\n            str(cur_global_step)))\n        run_context.session.run(\n            self._save_op, feed_dict={self._save_global_step: cur_global_step})\n        self._last_global_step = cur_global_step\n\n  def end(self, session):  # pylint: disable=unused-argument\n    if self._mode != tf.estimator.ModeKeys.TRAIN:\n      return\n\n    if self._save_op is not None:\n      cur_global_step = session.run(self._global_step_tensor)\n      if cur_global_step > self._last_global_step:\n        logging.info(\"session_end start to save item_pool at step {}\".format(\n            str(cur_global_step)))\n        session.run(self._save_op,\n                    feed_dict={self._save_global_step: cur_global_step})\n        self._last_global_step = cur_global_step\n"
  },
  {
    "path": "monolith/native_training/data/item_pool_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport os\nimport getpass\nimport tensorflow as tf\nfrom tensorflow.python.framework import load_library\n\nfrom monolith.utils import get_libops_path\nfrom monolith.native_training.data.feature_utils import create_item_pool, \\\n  save_item_pool, restore_item_pool, item_pool_random_fill, item_pool_check\n\n\nclass ItemPoolTest(tf.test.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    cls.model_path = f\"{os.environ.get('HOME')}/{getpass.getuser()}/tmp/monolith/data/test\"\n    global_step = tf.constant(1, dtype=tf.int64)\n    pool = create_item_pool(start_num=20,\n                            max_item_num_per_channel=100,\n                            shared_name='first')\n    pool = item_pool_random_fill(pool)\n    pool = save_item_pool(pool,\n                          model_path=cls.model_path,\n                          global_step=global_step,\n                          nshards=2)\n\n  def test_create_item_pool(self):\n    global_step = tf.constant(1, dtype=tf.int64)\n    pool = create_item_pool(start_num=20,\n                            max_item_num_per_channel=100,\n                            shared_name='second')\n    pool = restore_item_pool(pool,\n                             model_path=self.model_path,\n                             global_step=global_step,\n                             nshards=2)\n    pool = item_pool_check(pool,\n                           global_step=global_step,\n                           model_path=self.model_path,\n                           nshards=2)\n    logging.info(f\"model_path is {self.model_path}\")\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/kafka_dataset_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nimport getpass\nfrom absl import flags, app\nfrom random import choice\nfrom struct import pack\nfrom absl.testing import parameterized\nfrom kafka import KafkaProducer\nimport random\n\nimport tensorflow as tf\nfrom monolith.native_training.data.parsers import parse_instances, parse_examples\nfrom monolith.native_training.model_export.data_gen_utils import gen_example, gen_instance, gen_example_batch, FeatureMeta\nfrom monolith.native_training.data.datasets import KafkaDataset, PbType\nfrom monolith.native_training.data.feature_utils import add_label, filter_by_label\n\n# flags.DEFINE_string('feature_list', None, 'string, feature_list')\nflags.DEFINE_bool('lagrangex_header', False, 'bool, lagrangex_header')\nflags.DEFINE_bool('sort_id', False, 'bool, sort_id')\nflags.DEFINE_bool('kafka_dump', False, 'bool, kafka_dump')\nflags.DEFINE_bool('kafka_dump_prefix', False, 'bool, kafka_dump_prefix')\n\nflags.DEFINE_string('topic', 'test1', 'string, topic')\nflags.DEFINE_string('group_id', None, 'string, group_id')\nflags.DEFINE_string(\n    'kafka_servers',\n    'kafka-cnaittauujjoe7a9.kafka.volces.com:9492,kafka-cnaittauujjoe7a9.kafka.volces.com:9493,kafka-cnaittauujjoe7a9.kafka.volces.com:9494',\n    'string, kafka_servers')\nflags.DEFINE_bool('data_gen', True, 'bool, data_gen')\nflags.DEFINE_integer('num_batch', 3, 'bool, num_batch')\n\nother_meta = \"security.protocol=sasl_ssl,enable.ssl.certificate.verification=0,sasl.mechanisms=SCRAM-SHA-256,sasl.username=hupu_stream_test1_user1,sasl.password=hupu_stream_test1_user1\"\n\nFLAGS = flags.FLAGS\nBATCH_SIZE = 8\nUSE_CLICK_HEAD = False\nVALID_FNAMES = [\n    1, 2, 3, 4, 5, 6, 7, 8, 81, 82, 83, 84, 86, 87, 88, 89, 92, 93, 110, 115,\n    205, 208, 209, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,\n    313, 314, 315, 316, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511,\n    512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 526, 527, 528,\n    529, 530, 531, 532, 533, 534, 536, 537, 538, 540, 542, 543, 544, 549, 562,\n    564, 565, 567, 568, 569, 573, 576, 577, 700, 701, 707, 708, 709, 710, 711,\n    712, 719, 720, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811,\n    812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826,\n    828, 829, 830, 832, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844,\n    845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859,\n    860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874,\n    875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889,\n    890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 903, 905, 906,\n    907, 908, 909, 910, 911, 912, 913, 914, 915, 918, 924, 925, 926, 927, 928,\n    929, 930, 932, 933, 934, 935, 937, 938, 939, 940, 941, 942, 944, 946, 947,\n    948, 949, 950, 951, 952, 954, 955, 956, 958, 959, 960, 961, 962, 963, 964,\n    965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979,\n    980, 981, 982, 983, 984, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1022\n]\n\nFNAME_TO_SLOT = {\n    f\"fc_slot_{slot}\": slot for i, slot in enumerate(range(10000, 10002))\n}\n\nVALID_SLOTS_V2_NAMES = list(sorted(set(FNAME_TO_SLOT)))\n\n\ndef start_producer(input_type):\n  FLAGS.lagrangex_header = False\n  FLAGS.sort_id = False\n  FLAGS.kafka_dump = False\n  if True or FLAGS.data_gen:\n    producer = KafkaProducer(bootstrap_servers=FLAGS.kafka_servers,\n                             security_protocol=\"SASL_SSL\",\n                             sasl_mechanism=\"SCRAM-SHA-256\",\n                             sasl_plain_username=\"hupu_stream_test1_user1\",\n                             sasl_plain_password=\"hupu_stream_test1_user1\")\n    dense_features = [FeatureMeta(name='label', shape=4, dtype=tf.float32)]\n    extra_features = [\n        FeatureMeta(name='req_time', shape=1),\n        FeatureMeta(name='uid', shape=1),\n        FeatureMeta(name='sample_rate', shape=1)\n    ]\n    actions = [-7, -9, 75, -103, 74, 101, 102, -41]\n    time.sleep(10)\n    for i in range(FLAGS.num_batch):\n      all_len = 0\n      if input_type == PbType.EXAMPLEBATCH:\n        inst = gen_example_batch(sparse_features=VALID_SLOTS_V2_NAMES,\n                                 dense_features=dense_features,\n                                 extra_features=extra_features,\n                                 actions=actions,\n                                 batch_size=BATCH_SIZE)\n        inst_str = inst.SerializeToString()\n        fmt = f'<Q{len(inst_str)}s'\n        producer.send(topic=FLAGS.topic,\n                      value=pack(fmt, len(inst_str), inst_str))\n        all_len += len(inst_str)\n      else:\n        for j in range(BATCH_SIZE):\n          if input_type == PbType.INSTANCE:\n            inst = gen_instance(fidv1_features=VALID_FNAMES,\n                                dense_features=dense_features,\n                                extra_features=extra_features,\n                                actions=actions)\n          elif input_type == PbType.EXAMPLE:\n            inst = gen_example(sparse_features=VALID_SLOTS_V2_NAMES,\n                               dense_features=dense_features,\n                               extra_features=extra_features,\n                               actions=actions)\n          else:\n            assert False, \"not support\"\n          inst_str = inst.SerializeToString()\n          fmt = f'<Q{len(inst_str)}s'\n          val = pack(fmt, len(inst_str), inst_str)\n          print(f\"produce {i} {j} {len(inst_str)} {len(val)}\", flush=True)\n          producer.send(topic=FLAGS.topic, value=val)\n          all_len += len(inst_str)\n      print(f\"produce {i} {all_len}\")\n      time.sleep(1)\n    producer.close()\n  #start_producer()\n\n\nclass KafkaDatasetTest(tf.test.TestCase, parameterized.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    pass\n\n  @parameterized.parameters([\n      (PbType.EXAMPLE, PbType.EXAMPLE),\n      (PbType.EXAMPLEBATCH, PbType.EXAMPLE),\n      (PbType.INSTANCE, PbType.INSTANCE),\n  ])\n  def test_kafka_dataset(self, input_type, output_type):\n    import threading\n    td = threading.Thread(target=start_producer, args=(input_type,))\n    td.start()\n    dataset = KafkaDataset(topics=FLAGS.topic,\n                           group_id=FLAGS.group_id if FLAGS.group_id else\n                           f\"monolith_base_test_{random.randint(0,10000000)}\",\n                           servers=FLAGS.kafka_servers,\n                           variant_type=input_type,\n                           output_pb_type=output_type,\n                           message_poll_timeout=30000,\n                           poll_batch_size=2,\n                           kafka_other_metadata=other_meta)\n    # label(0): staytime, not used\n    label_vec_size = 4\n\n    # 4 tasks\n    # task1: interact, task2: convert, task3: vr, task4: click\n    if USE_CLICK_HEAD:\n      add_label_config = '-7,-9:-41:0.3;75,-103,74:-41:0.3;101,102:-41:0.3;-41::0.1'\n    else:\n      add_label_config = '-7,-9:-41:0.3;75,-103,74:-41:0.3;101,102:-41:0.3'\n    dataset = dataset.map(\n        lambda variant: add_label(variant,\n                                  config=add_label_config,\n                                  negative_value=0,\n                                  new_sample_rate=1.0,\n                                  variant_type=output_type.to_name()))\n\n    #dataset = dataset.shuffle(BATCH_SIZE * 2)\n    dataset = dataset.batch(batch_size=BATCH_SIZE, drop_remainder=False)\n\n    def map_fn(tensor):\n      if output_type == PbType.EXAMPLE:\n        features = parse_examples(\n            tensor,\n            #fidv1_features=VALID_FNAMES,\n            sparse_features=VALID_SLOTS_V2_NAMES,\n            dense_features=['label'],\n            dense_feature_shapes=[label_vec_size],\n            extra_features=['req_time', 'uid', 'sample_rate'],\n            extra_feature_shapes=[1, 1, 1])\n      elif output_type == PbType.INSTANCE:\n        features = parse_instances(\n            tensor,\n            fidv1_features=VALID_FNAMES,\n            fidv2_features=None,\n            dense_features=['label'],\n            dense_feature_shapes=[label_vec_size],\n            extra_features=['req_time', 'uid', 'sample_rate'],\n            extra_feature_shapes=[1, 1, 1])\n      else:\n        assert False, \"not support\"\n\n      if USE_CLICK_HEAD:\n        (_, interact_label, convert_label, vr_label,\n         clk_label) = tf.split(features['label'],\n                               num_or_size_splits=label_vec_size,\n                               axis=1)\n        features['clk_label'] = tf.reshape(clk_label, shape=(-1,))\n      else:\n        (_, interact_label, convert_label,\n         vr_label) = tf.split(features['label'],\n                              num_or_size_splits=label_vec_size,\n                              axis=1)\n      features['interact_label'] = tf.reshape(interact_label, shape=(-1,))\n      features['convert_label'] = tf.reshape(convert_label, shape=(-1,))\n      features['vr_label'] = tf.reshape(vr_label, shape=(-1,))\n      features['sample_rate'] = tf.reshape(features['sample_rate'], shape=(-1,))\n      return features\n\n    dataset = dataset.map(map_fn, num_parallel_calls=tf.data.AUTOTUNE)\n    dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)\n\n    config = tf.compat.v1.ConfigProto()\n    config.graph_options.rewrite_options.disable_meta_optimizer = True\n    with tf.compat.v1.Session(config=config) as sess:\n      it = tf.compat.v1.data.make_initializable_iterator(dataset)\n      element = it.get_next()\n      sess.run(it.initializer)\n      for x in range(FLAGS.num_batch):\n        res = sess.run(fetches=element)\n        print(\n            f\"print result get one {input_type.to_name()} {output_type.to_name()} {x} {res}\"\n        )\n    print(f\" exit \")\n    td.join()\n    print(f\"finish exit \")\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/kernels/add_action_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstdio>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/data/kernels/internal/relational_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing LineId = ::idl::matrix::proto::LineId;\n\nclass AddActionOp : public OpKernel {\n public:\n  explicit AddActionOp(OpKernelConstruction *context) : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"field_name\", &field_name_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"op\", &op_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"float_operand\", &float_operand_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"int_operand\", &int_operand_));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"string_operand\", &string_operand_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"actions\", &actions_));\n\n    uint_operand_.insert(uint_operand_.end(), int_operand_.begin(),\n                         int_operand_.end());\n\n    if (!internal::VALID_OPS.count(op_)) {\n      LOG(FATAL) << absl::StrFormat(\n          \"Invalid op: %s, please choose one from [%s]\", op_,\n          absl::StrJoin(internal::VALID_OPS, \", \"));\n    }\n\n    if (variant_type_ != \"instance\" && variant_type_ != \"example\") {\n      LOG(FATAL) << \"Invalid 'variant_type', please choose on from \"\n                    \"['instance', 'example']!\";\n    }\n\n    if (actions_.empty()) {\n      LOG(FATAL) << \"Please specify 'actions' to add!\";\n    }\n\n    if (op_ == internal::IN || op_ == internal::NOT_IN) {\n      float_operand_set_.insert(float_operand_.begin(), float_operand_.end());\n      int_operand_set_.insert(int_operand_.begin(), int_operand_.end());\n      uint_operand_set_.insert(uint_operand_.begin(), uint_operand_.end());\n      string_operand_set_.insert(string_operand_.begin(),\n                                 string_operand_.end());\n    }\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n\n    bool is_instance = variant_type_ == \"instance\";\n    if (is_instance) {\n      Instance instance;\n      instance.CopyFrom(*input_tensor.scalar<Variant>()().get<Instance>());\n      output_tensor->scalar<Variant>()() = std::move(instance);\n    } else {\n      Example example;\n      example.CopyFrom(*input_tensor.scalar<Variant>()().get<Example>());\n      output_tensor->scalar<Variant>()() = std::move(example);\n    }\n\n    const google::protobuf::Descriptor *descriptor = LineId::GetDescriptor();\n    const google::protobuf::Reflection *reflection = LineId::GetReflection();\n    const google::protobuf::FieldDescriptor *field =\n        descriptor->FindFieldByName(field_name_);\n\n    if (field == nullptr || field->is_repeated()) {\n      return;\n    }\n\n    LineId &line_id = *GetLineId(output_tensor, is_instance);\n\n    bool to_add_action = false;\n    switch (field->cpp_type()) {\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_FLOAT: {\n        float value = reflection->GetFloat(line_id, field);\n        to_add_action =\n            internal::COMPARE_OPS.count(op_)\n                ? internal::compare(op_, value, float_operand_)\n                : internal::contains(op_, value, float_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_DOUBLE: {\n        double value = reflection->GetDouble(line_id, field);\n        to_add_action =\n            internal::COMPARE_OPS.count(op_)\n                ? internal::compare(op_, value, float_operand_)\n                : internal::contains(op_, value, float_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT32: {\n        int64 value = reflection->GetInt32(line_id, field);\n        to_add_action = internal::COMPARE_OPS.count(op_)\n                            ? internal::compare(op_, value, int_operand_)\n                            : internal::contains(op_, value, int_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT64: {\n        int64 value = reflection->GetInt64(line_id, field);\n        to_add_action = internal::COMPARE_OPS.count(op_)\n                            ? internal::compare(op_, value, int_operand_)\n                            : internal::contains(op_, value, int_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT32: {\n        int64 value = reflection->GetUInt32(line_id, field);\n        to_add_action = internal::COMPARE_OPS.count(op_)\n                            ? internal::compare(op_, value, int_operand_)\n                            : internal::contains(op_, value, int_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT64: {\n        uint64 value = reflection->GetUInt64(line_id, field);\n        to_add_action = internal::COMPARE_OPS.count(op_)\n                            ? internal::compare(op_, value, uint_operand_)\n                            : internal::contains(op_, value, uint_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_STRING: {\n        std::string value = reflection->GetString(line_id, field);\n        to_add_action =\n            internal::COMPARE_OPS.count(op_)\n                ? internal::compare(op_, value, string_operand_)\n                : internal::contains(op_, value, string_operand_set_);\n        break;\n      }\n      default:\n        to_add_action = false;\n        LOG(INFO) << \"dtype is \" << field->cpp_type();\n        break;\n    }\n\n    if (to_add_action) {\n      for (int32 value : actions_) {\n        line_id.mutable_actions()->Add(value);\n      }\n    }\n  }\n\n private:\n  static LineId *GetLineId(Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_line_id();\n    } else {\n      return output_tensor->scalar<Variant>()()\n          .get<Example>()\n          ->mutable_line_id();\n    }\n  }\n\n  std::string field_name_;\n  std::string op_;\n\n  std::vector<float> float_operand_;\n  std::vector<int64> int_operand_;\n  std::vector<uint64> uint_operand_;\n  std::vector<std::string> string_operand_;\n\n  std::unordered_set<float> float_operand_set_;\n  std::unordered_set<int64> int_operand_set_;\n  std::unordered_set<uint64> uint_operand_set_;\n  std::unordered_set<std::string> string_operand_set_;\n\n  std::string variant_type_;\n  std::vector<int32> actions_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"AddAction\").Device(DEVICE_CPU), AddActionOp)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/add_label_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstdio>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/runtime/common/linalg_utils.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing LineId = ::idl::matrix::proto::LineId;\nusing ::monolith::common::IsAlmostEqual;\n\nclass AddLabelOp : public OpKernel {\n public:\n  explicit AddLabelOp(OpKernelConstruction *context) : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"config\", &config_));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"negative_value\", &negative_value_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"sample_rate\", &sample_rate_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n\n    if (variant_type_ != \"instance\" && variant_type_ != \"example\") {\n      LOG(FATAL) << \"Invalid 'variant_type', please choose on from \"\n                    \"['instance', 'example']!\";\n    }\n\n    internal::ParseTaskConfig(config_, &task_configs_);\n    for (size_t i = 0; i < task_configs_.size(); ++i) {\n      LOG(INFO) << absl::StrFormat(\"Task #%d config: %s\", i + 1,\n                                   task_configs_[i].ToString());\n    }\n    LOG(INFO) << absl::StrFormat(\"sample_rate = %.4f\", sample_rate_);\n    std::size_t seed =\n        std::chrono::system_clock::now().time_since_epoch().count();\n    random_generator_.seed(seed);\n    random_neg_sample_ = std::uniform_real_distribution<float>(0.0, 1.0);\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n\n    bool is_instance = variant_type_ == \"instance\";\n    if (is_instance) {\n      Instance instance;\n      instance.CopyFrom(*input_tensor.scalar<Variant>()().get<Instance>());\n      output_tensor->scalar<Variant>()() = std::move(instance);\n    } else {\n      Example example;\n      example.CopyFrom(*input_tensor.scalar<Variant>()().get<Example>());\n      output_tensor->scalar<Variant>()() = std::move(example);\n    }\n\n    LineId *line_id = GetLineId(output_tensor, is_instance);\n    auto label = GetLabel(output_tensor, is_instance);\n    std::set<int32_t> actions(line_id->actions().begin(),\n                              line_id->actions().end());\n\n    if (!label->empty() && label->Get(0) <= 0) {\n      label->Set(0, internal::INVALID_LABEL);\n    }\n\n    for (const auto &t : task_configs_) {\n      bool has_pos = internal::HasIntersection(actions, t.pos_actions);\n      bool has_neg = internal::HasIntersection(actions, t.neg_actions);\n\n      if (!t.neg_actions.empty()) {\n        // If there is given neg_actions\n        if (!has_pos && !has_neg) {\n          label->Add(internal::INVALID_LABEL);\n        } else if (has_pos) {\n          // (has_pos && !has_neg) || (has_pos && has_neg)\n          label->Add(internal::POSITIVE_LABEL);\n        } else {\n          // !has_pos && has_neg\n          if (SelectedByNegativeSampling(t)) {\n            label->Add(negative_value_);\n          } else {\n            label->Add(internal::INVALID_LABEL);\n          }\n        }\n      } else {\n        // If there is no given neg_actions\n        if (has_pos) {\n          label->Add(internal::POSITIVE_LABEL);\n        } else {\n          if (SelectedByNegativeSampling(t)) {\n            label->Add(negative_value_);\n          } else {\n            label->Add(internal::INVALID_LABEL);\n          }\n        }\n      }\n    }\n\n    line_id->set_sample_rate(sample_rate_);\n  }\n\n private:\n  bool SelectedByNegativeSampling(const internal::TaskConfig &t) {\n    return IsAlmostEqual(t.sample_rate, 1.0f) ||\n           random_neg_sample_(random_generator_) < t.sample_rate;\n  }\n\n  static LineId *GetLineId(Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_line_id();\n    } else {\n      return output_tensor->scalar<Variant>()()\n          .get<Example>()\n          ->mutable_line_id();\n    }\n  }\n\n  static ::google::protobuf::RepeatedField<float> *GetLabel(\n      Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_label();\n    } else {\n      return output_tensor->scalar<Variant>()().get<Example>()->mutable_label();\n    }\n  }\n\n  float negative_value_;\n  float sample_rate_;\n  std::string config_;\n  std::string variant_type_;\n  std::vector<internal::TaskConfig> task_configs_;\n  std::default_random_engine random_generator_;\n  std::uniform_real_distribution<float> random_neg_sample_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"AddLabel\").Device(DEVICE_CPU), AddLabelOp)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/cache_one_dataset_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/cache_one_dataset_kernel.h\"\n\n#include <deque>\n\n#include \"tensorflow/core/framework/dataset.h\"\n#include \"tensorflow/core/framework/partial_tensor_shape.h\"\n#include \"tensorflow/core/framework/stats_aggregator.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/protobuf/error_codes.pb.h\"\n\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/str_cat.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\n\nclass CacheOneDatasetOp::Dataset : public DatasetBase {\n public:\n  Dataset(OpKernelContext* ctx, const DatasetBase* input)\n      : DatasetBase(DatasetContext(ctx)), input_(input) {\n    input_->Ref();\n    output_dtypes_ = input->output_dtypes();\n    output_dtypes_.push_back(DT_BOOL);\n    output_shapes_ = input->output_shapes();\n    output_shapes_.push_back({});\n  }\n\n  ~Dataset() override { input_->Unref(); }\n\n  std::unique_ptr<IteratorBase> MakeIteratorInternal(\n      const string& prefix) const override {\n    return absl::make_unique<Iterator>(\n        Iterator::Params{this, absl::StrCat(prefix, \":: CacheOneDataset\")});\n  }\n\n  const DataTypeVector& output_dtypes() const override {\n    return output_dtypes_;\n  }\n\n  const std::vector<PartialTensorShape>& output_shapes() const override {\n    return output_shapes_;\n  }\n\n  string DebugString() const override {\n    return \"This is the customized Dataset: CacheOneDataset\";\n  }\n\n  int64 Cardinality() const override { return input_->Cardinality(); }\n\n  Status InputDatasets(std::vector<const DatasetBase*>* inputs) const override {\n    inputs->push_back(input_);\n    return Status::OK();\n  }\n\n  Status CheckExternalState() const override {\n    return input_->CheckExternalState();\n  }\n\n private:\n  Status AsGraphDefInternal(SerializationContext* ctx,\n                            DatasetGraphDefBuilder* b,\n                            Node** output) const override {\n    Node* input_graph_node = nullptr;\n    TF_RETURN_IF_ERROR(b->AddInputDataset(ctx, input_, &input_graph_node));\n    TF_RETURN_IF_ERROR(b->AddDataset(this, {input_graph_node}, {}, output));\n    return Status::OK();\n  }\n\n  class Iterator : public DatasetIterator<Dataset> {\n   public:\n    explicit Iterator(const Params& params)\n        : DatasetIterator<Dataset>(params) {}\n\n    Status Initialize(IteratorContext* ctx) override {\n      absl::MutexLock l(&mu_);\n      return dataset()->input_->MakeIterator(ctx, this, prefix(), &input_impl_);\n    }\n\n    Status GetNextInternal(IteratorContext* ctx,\n                           std::vector<Tensor>* out_tensors,\n                           bool* end_of_sequence) override {\n      absl::MutexLock l(&mu_);\n      if (first_element_) {\n        first_element_ = false;\n        TF_RETURN_IF_ERROR(\n            input_impl_->GetNext(ctx, &buffered_tensors_, end_of_sequence));\n        if (*end_of_sequence) {\n          // This is the special case that input dataset contains no data.\n          // Here we just throw it out.\n          return Status::OK();\n        }\n      }\n\n      // We run out of the data.\n      if (eof_) {\n        *end_of_sequence = true;\n        return Status::OK();\n      }\n\n      *out_tensors = std::move(buffered_tensors_);\n      buffered_tensors_.clear();\n      TF_RETURN_IF_ERROR(input_impl_->GetNext(ctx, &buffered_tensors_, &eof_));\n      Tensor eof_tensor(ctx->allocator({}), DT_BOOL, {});\n      eof_tensor.scalar<bool>()() = eof_;\n      out_tensors->push_back(eof_tensor);\n      *end_of_sequence = false;\n      return Status::OK();\n    }\n\n    std::shared_ptr<model::Node> CreateNode(\n        IteratorContext* ctx, model::Node::Args args) const override {\n      return model::MakeKnownRatioNode(std::move(args), 1);\n    }\n\n    Status SaveInternal(SerializationContext* ctx,\n                        IteratorStateWriter* writer) override {\n      return errors::Unimplemented(\"Not Implemented\");\n    }\n\n    Status RestoreInternal(IteratorContext* ctx,\n                           IteratorStateReader* reader) override {\n      return errors::Unimplemented(\"Not Implemented\");\n    }\n\n    absl::Mutex mu_;\n    std::unique_ptr<IteratorBase> input_impl_;\n    std::vector<Tensor> buffered_tensors_;\n    bool first_element_ = true;\n    bool eof_ = false;\n  };\n\n private:\n  const DatasetBase* const input_;\n  DataTypeVector output_dtypes_;\n  std::vector<PartialTensorShape> output_shapes_;\n};\n\nvoid CacheOneDatasetOp::MakeDataset(OpKernelContext* ctx, DatasetBase* input,\n                                    DatasetBase** output) {\n  *output = new Dataset(ctx, input);\n}\n\nCacheOneDatasetOp::CacheOneDatasetOp(OpKernelConstruction* ctx)\n    : UnaryDatasetOpKernel(ctx) {}\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"MonolithCacheOneDataset\").Device(DEVICE_CPU),\n                        CacheOneDatasetOp);\n}  // namespace\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/cache_one_dataset_kernel.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_KERNELS_CACHE_ONE_DATASET_KERNEL_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_KERNELS_CACHE_ONE_DATASET_KERNEL_H_\n\n#include \"tensorflow/core/framework/dataset.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\n\nclass CacheOneDatasetOp : public UnaryDatasetOpKernel {\n public:\n  explicit CacheOneDatasetOp(OpKernelConstruction* ctx);\n\n protected:\n  void MakeDataset(OpKernelContext* ctx, DatasetBase* input,\n                   DatasetBase** output) override;\n\n private:\n  class Dataset;\n};\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_KERNELS_CACHE_ONE_DATASET_KERNEL_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/df_resource_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/df_resource_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing Queue = ::monolith::concurrency::Queue<Item>;\n\nStatus RegisterCancellationCallback(CancellationManager* cancellation_manager,\n                                    CancelCallback callback,\n                                    std::function<void()>* deregister_fn) {\n  if (cancellation_manager) {\n    CancellationToken token = cancellation_manager->get_cancellation_token();\n    if (!cancellation_manager->RegisterCallback(token, std::move(callback))) {\n      return errors::Cancelled(\"Operation was cancelled\");\n    }\n    *deregister_fn = [cancellation_manager, token]() {\n      cancellation_manager->DeregisterCallback(token);\n    };\n  } else {\n    VLOG(1) << \"Cancellation manager is not set. Cancellation callback will \"\n               \"not be registered.\";\n    *deregister_fn = []() {};\n  }\n  return Status::OK();\n}\n\nclass CreateQueueOp : public ResourceOpKernel<QueueResource> {\n public:\n  explicit CreateQueueOp(OpKernelConstruction* c) : ResourceOpKernel(c) {\n    OP_REQUIRES_OK(c, c->GetAttr(\"max_size\", &max_size_));\n  }\n\n  ~CreateQueueOp() override {}\n\n private:\n  Status CreateResource(QueueResource** queue)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    *queue = new QueueResource(max_size_);\n    return Status::OK();\n  }\n\n  int max_size_;\n};\n\nREGISTER_OP(\"CreateQueue\")\n    .Output(\"handle: resource\")\n    .Attr(\"max_size: int\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"CreateQueue\").Device(DEVICE_CPU), CreateQueueOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/df_resource_kernel.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_KERNELS_DF_RESOURCE_KERNEL_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_KERNELS_DF_RESOURCE_KERNEL_H_\n\n#include <chrono>\n#include <thread>\n#include \"absl/synchronization/mutex.h\"\n#include \"monolith/native_training/runtime/concurrency/queue.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nenum class VariantType { PBInstance, PBExample };\n\ntypedef struct {\n  std::vector<Tensor> out_tensors;\n  bool end_of_sequence;\n} Item;\n\n// It is a thin wrapper of GFile. Make it compatible with ResourceKernelOp\n// and thread safe.\nclass QueueResource : public ResourceBase {\n public:\n  explicit QueueResource(size_t max_size = 100) {\n    queue_ = std::make_unique<::monolith::concurrency::Queue<Item>>(max_size);\n  }\n\n  ~QueueResource() = default;\n\n  std::string DebugString() const override { return \"QueueResource\"; }\n\n  void Push(const Item &item) {\n    bool pushed = false;\n    do {\n      pushed = queue_->try_push(item, std::chrono::milliseconds(100));\n      if (!pushed) {\n        std::this_thread::sleep_for(std::chrono::milliseconds(100));\n      }\n    } while (!pushed);\n  }\n\n  bool TryPush(const Item &item, int64_t timeout = 100) {\n    return queue_->try_push(item, std::chrono::milliseconds(timeout));\n  }\n\n  Item Pop() const {\n    bool poped = false;\n    Item item;\n    do {\n      poped = queue_->try_pop(item, std::chrono::milliseconds(10));\n      if (!poped) {\n        std::this_thread::sleep_for(std::chrono::milliseconds(10));\n      }\n    } while (!poped);\n\n    return item;\n  }\n\n  bool TryPop(Item &item, int64_t timeout = 100) const {\n    return queue_->try_pop(item, std::chrono::milliseconds(timeout));\n  }\n\n  bool Empty() const { return queue_->empty(); }\n\n private:\n  mutable std::unique_ptr<::monolith::concurrency::Queue<Item>> queue_;\n};\n\nStatus RegisterCancellationCallback(CancellationManager *cancellation_manager,\n                                    CancelCallback callback,\n                                    std::function<void()> *deregister_fn);\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_KERNELS_DF_RESOURCE_KERNEL_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/dynamic_match_file_dataset_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <queue>\n#include \"monolith/native_training/data/kernels/internal/file_match_split_provider.h\"\n\n#include \"tensorflow/core/framework/dataset.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/partial_tensor_shape.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/lib/core/blocking_counter.h\"\n#include \"tensorflow/core/lib/core/errors.h\"\n#include \"tensorflow/core/lib/core/threadpool.h\"\n#include \"tensorflow/core/lib/io/buffered_inputstream.h\"\n#include \"tensorflow/core/lib/io/inputbuffer.h\"\n#include \"tensorflow/core/lib/io/path.h\"\n#include \"tensorflow/core/lib/io/random_inputstream.h\"\n#include \"tensorflow/core/lib/io/record_reader.h\"\n#include \"tensorflow/core/lib/io/zlib_compression_options.h\"\n#include \"tensorflow/core/lib/io/zlib_inputstream.h\"\n#include \"tensorflow/core/platform/env.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\n\nclass DynamicMatchingFilesDatasetOp : public DatasetOpKernel {\n public:\n  using DatasetOpKernel::DatasetOpKernel;\n\n  void MakeDataset(OpKernelContext* ctx, DatasetBase** output) override {\n    const Tensor* patterns_t;\n    OP_REQUIRES_OK(ctx, ctx->input(\"patterns\", &patterns_t));\n    const auto patterns = patterns_t->flat<tstring>();\n    size_t num_patterns = static_cast<size_t>(patterns.size());\n    std::vector<std::string> pattern_strs;\n    pattern_strs.reserve(num_patterns);\n\n    for (size_t i = 0; i < num_patterns; i++) {\n      LOG_EVERY_N(INFO, 100) << \"pattern \" << patterns(i) << \", num_patterns \"\n                              << num_patterns;\n      pattern_strs.push_back(patterns(i));\n    }\n\n    *output = new Dataset(ctx, std::move(pattern_strs));\n  }\n\n private:\n  class Dataset : public DatasetBase {\n   public:\n    Dataset(OpKernelContext* ctx, std::vector<std::string> patterns)\n        : DatasetBase(DatasetContext(ctx)), patterns_(std::move(patterns)) {}\n\n    std::unique_ptr<IteratorBase> MakeIteratorInternal(\n        const string& prefix) const override {\n      return absl::make_unique<Iterator>(Iterator::Params{\n          this, strings::StrCat(prefix, \"::DynamicMatchingFiles\")});\n    }\n\n    const DataTypeVector& output_dtypes() const override {\n      static DataTypeVector* dtypes = new DataTypeVector({DT_STRING});\n      return *dtypes;\n    }\n\n    const std::vector<PartialTensorShape>& output_shapes() const override {\n      static std::vector<PartialTensorShape>* shapes =\n          new std::vector<PartialTensorShape>({{}});\n      return *shapes;\n    }\n\n    string DebugString() const override {\n      return \"DynamicMatchingFilesDatasetOp::Dataset\";\n    }\n\n    Status InputDatasets(\n        std::vector<const DatasetBase*>* inputs) const override {\n      return Status::OK();\n    }\n\n    Status CheckExternalState() const override { return Status::OK(); }\n\n    Status MakeSplitProvider(\n        std::unique_ptr<SplitProvider>* split_provider) const override {\n      split_provider->reset(new FileMatchSplitProvider(patterns_));\n      return Status::OK();\n    }\n\n   protected:\n    Status AsGraphDefInternal(SerializationContext* ctx,\n                              DatasetGraphDefBuilder* b,\n                              Node** output) const override {\n      Node* patterns_node = nullptr;\n      TF_RETURN_IF_ERROR(b->AddVector(patterns_, &patterns_node));\n      TF_RETURN_IF_ERROR(b->AddDataset(this, {patterns_node}, output));\n      return Status::OK();\n    }\n\n   private:\n    class Iterator : public DatasetIterator<Dataset> {\n     public:\n      explicit Iterator(const Params& params)\n          : DatasetIterator<Dataset>(params) {}\n\n      Status GetNextInternal(IteratorContext* ctx,\n                             std::vector<Tensor>* out_tensors,\n                             bool* end_of_sequence) override {\n        mutex_lock l(mu_);\n        if (!split_provider_) {\n          LOG(INFO) << \"Begin to get split_provider from ctx!\";\n          split_provider_ = ctx->split_provider();\n          if (!split_provider_) {\n            LOG(INFO) << \"No split_provider in ctx, call MakeSplitProvider!\";\n            std::unique_ptr<SplitProvider> split_provider;\n            TF_RETURN_IF_ERROR(dataset()->MakeSplitProvider(&split_provider));\n            split_provider_.reset(split_provider.release());\n          } else {\n            LOG(INFO) << \"Got split_provider from IteratorContext\";\n          }\n          LOG(INFO) << \"Get split_provider done!\";\n        }\n\n        if (end_of_sequence_) {\n          *end_of_sequence = true;\n          out_tensors->clear();\n          return Status::OK();\n        }\n\n        Tensor split;\n        Status s = split_provider_->GetNext(&split, end_of_sequence);\n        if (errors::IsOutOfRange(s)) {\n          out_tensors->clear();\n          *end_of_sequence = true;\n          end_of_sequence_ = true;\n          LOG(INFO) << s.error_message();\n        } else if (s.ok()) {\n          *end_of_sequence = false;\n          out_tensors->emplace_back(std::move(split));\n        } else {\n          return s;\n        }\n\n        return Status::OK();\n      }\n\n     protected:\n      std::shared_ptr<model::Node> CreateNode(\n          IteratorContext* ctx, model::Node::Args args) const override {\n        return model::MakeSourceNode(std::move(args));\n      }\n\n      Status SaveInternal(SerializationContext* ctx,\n                          IteratorStateWriter* writer) override {\n        mutex_lock l(mu_);\n        if (split_provider_ != nullptr) {\n          split_provider_->Save(\n              [this](std::string name) { return FullName(prefix(), name); },\n              writer);\n        }\n        return Status::OK();\n      }\n\n      Status RestoreInternal(IteratorContext* ctx,\n                             IteratorStateReader* reader) override {\n        mutex_lock l(mu_);\n        if (!split_provider_) {\n          split_provider_ = ctx->split_provider();\n        }\n        split_provider_->Restore(\n            [this](std::string name) { return FullName(prefix(), name); },\n            reader);\n        return Status::OK();\n      }\n\n     private:\n      mutex mu_;\n      bool end_of_sequence_ = false;\n      std::shared_ptr<SplitProvider> split_provider_;\n    };\n\n    const std::vector<std::string> patterns_;\n  };\n};\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"DynamicMatchingFilesDataset\").Device(DEVICE_CPU),\n                        DynamicMatchingFilesDatasetOp);\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/extract_fid_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n\n#include \"absl/hash/internal/city.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass ExtractFidOp : public OpKernel {\n public:\n  using OpKernel::OpKernel;\n  using ConstFlatSplits = typename TTypes<int64>::ConstFlat;\n\n  explicit ExtractFidOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"slot\", &slot_));\n    slot_ = slot_ << 48;\n  }\n\n  void Compute(OpKernelContext* context) override {\n    // Grab the input tensor\n    const Tensor& input_tensor = context->input(0);\n    auto input = input_tensor.flat<int64>();\n\n    // Create an output tensor\n    Tensor* output_tensor = NULL;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n    auto output_flat = output_tensor->flat<int64>();\n\n    // Set all to its fid.\n    const int N = input.size();\n    int64 bits_left = (1ll << 49) - 1;\n    for (int i = 0; i < N; i++) {\n      uint64_t tmp = input(i);\n      int64 hash_val =\n          absl::hash_internal::CityHash64(reinterpret_cast<char*>(&tmp), 8);\n      output_flat(i) = (hash_val & bits_left | slot_);\n    }\n  }\n\n private:\n  int64 slot_;\n};\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"ExtractFid\").Device(DEVICE_CPU), ExtractFidOp);\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/feature_hash.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <algorithm>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include \"absl/hash/internal/city.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nusing NamedRawFeature = ::monolith::io::proto::NamedRawFeature;\nusing RawFeature = ::monolith::io::proto::RawFeature;\nusing NamedFeature = ::monolith::io::proto::NamedFeature;\nusing Feature = ::monolith::io::proto::Feature;\nusing Example = ::monolith::io::proto::Example;\n\nclass FeatureHashOp : public OpKernel {\n public:\n  using OpKernel::OpKernel;\n  using ConstFlatSplits = typename TTypes<int64>::ConstFlat;\n\n  explicit FeatureHashOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    std::vector<std::string> names;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"names\", &names));\n    names_.insert(names.begin(), names.end());\n  }\n\n  void Compute(OpKernelContext *context) override {\n    // Grab the input tensor\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(context, context->input(\"input\", &pb_input));\n    TTypes<Variant>::ConstVec pb_variant_tensor = pb_input->vec<Variant>();\n    const int batch_size = pb_variant_tensor.dimension(0);\n\n    // Create an output tensor\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, pb_input->shape(),\n                                                     &output_tensor));\n    auto output_flat = output_tensor->flat<Variant>();\n\n    for (int i = 0; i < batch_size; ++i) {\n      const Example *in_pb = pb_variant_tensor(i).get<Example>();\n      Example out_pb;\n      out_pb.mutable_line_id()->CopyFrom(in_pb->line_id());\n      out_pb.mutable_label()->CopyFrom(in_pb->label());\n      for (size_t i = 0; i < in_pb->named_raw_feature_size(); ++i) {\n        const NamedRawFeature &named_raw_feature = in_pb->named_raw_feature(i);\n        std::string name = named_raw_feature.name();\n        if (names_.find(name) == names_.end()) continue;\n\n        NamedFeature *out_nf = out_pb.add_named_feature();\n        out_nf->set_id(named_raw_feature.id());\n        out_nf->set_name(name);\n        raw_feature_to_feature(name, named_raw_feature.raw_feature(),\n                               out_nf->mutable_feature());\n      }\n\n      output_flat(i) = std::move(out_pb);\n    }\n  }\n\n private:\n  std::unordered_set<std::string> names_;\n\n  void raw_feature_to_feature(const std::string &name,\n                              const RawFeature &raw_feature, Feature *feature) {\n    for (size_t i = 0; i < raw_feature.feature_size(); ++i) {\n      const auto &rf = raw_feature.feature(i);\n      if (rf.has_float_list()) {\n        feature->mutable_float_list()->MergeFrom(rf.float_list());\n      }\n\n      if (rf.has_double_list()) {\n        feature->mutable_double_list()->MergeFrom(rf.double_list());\n      }\n\n      if (rf.has_int64_list()) {\n        feature->mutable_int64_list()->MergeFrom(rf.int64_list());\n      }\n\n      if (rf.has_bytes_list()) {\n        const auto &bytes_list = rf.bytes_list();\n        auto *out_list = feature->mutable_fid_v2_list();\n        for (size_t j = 0; j < bytes_list.value_size(); ++j) {\n          const std::string &value =\n              absl::StrCat(bytes_list.value(j), \"-\", name);\n          int64 hash_val = absl::hash_internal::CityHash64(value.c_str(), 8);\n          out_list->add_value(hash_val);\n        }\n      }\n\n      if (rf.has_float_lists()) {\n        feature->mutable_float_lists()->MergeFrom(rf.float_lists());\n      }\n\n      if (rf.has_double_lists()) {\n        feature->mutable_double_lists()->MergeFrom(rf.double_lists());\n      }\n\n      if (rf.has_int64_lists()) {\n        feature->mutable_int64_lists()->MergeFrom(rf.int64_lists());\n      }\n\n      if (rf.has_bytes_lists()) {\n        const auto &bytes_lists = rf.bytes_lists();\n        for (size_t j = 0; j < bytes_lists.list_size(); ++j) {\n          const auto &bytes_list = bytes_lists.list(j);\n          auto *out_list = feature->mutable_fid_v2_lists()->add_list();\n          for (size_t k = 0; k < bytes_list.value_size(); ++k) {\n            const std::string &value =\n                absl::StrCat(bytes_list.value(j), \"-\", name);\n            int64 hash_val = absl::hash_internal::CityHash64(value.c_str(), 8);\n            out_list->add_value(hash_val);\n          }\n        }\n      }\n    }\n  }\n};\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"FeatureHash\").Device(DEVICE_CPU), FeatureHashOp);\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/feature_name_mapper_tf_bridge.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/feature_name_mapper_tf_bridge.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nStatus FeatureNameMapperTfBridge::New(FeatureNameMapperTfBridge** new_bridge) {\n  auto bridge = core::RefCountPtr<FeatureNameMapperTfBridge>(\n      new FeatureNameMapperTfBridge());\n  bridge->mapper_ = std::make_unique<FeatureNameMapper>();\n  *new_bridge = bridge.release();\n  return Status::OK();\n}\n\nStatus FeatureNameMapperTfBridge::RegisterValidIds(\n    const std::vector<std::pair<int, int>>& valid_ids) const {\n  try {\n    if (mapper_->RegisterValidIds(valid_ids)) {\n      return Status::OK();\n    } else {\n      return errors::InvalidArgument(\"RegisterValidIds failed!\");\n    }\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/feature_name_mapper_tf_bridge.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_FEATURE_NAME_MAPPER_TF_BRIDGE_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_FEATURE_NAME_MAPPER_TF_BRIDGE_H_\n\n#include <atomic>\n#include <memory>\n#include <mutex>\n#include <thread>\n#include <utility>\n\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/platform/status.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\n// A feature name mapper which can be used in TF runtime.\n// It captures all potential exceptions and convert them into error.\nclass FeatureNameMapperTfBridge : public ResourceBase {\n public:\n  static constexpr const char* const kName = \"FeatureNameMapper\";\n\n  ~FeatureNameMapperTfBridge() override = default;\n\n  static Status New(FeatureNameMapperTfBridge** new_bridge);\n\n  Status RegisterValidIds(\n      const std::vector<std::pair<int, int>>& valid_ids) const;\n\n  std::string DebugString() const override { return mapper_->DebugString(); }\n\n  FeatureNameMapper* GetFeatureNameMapper() const { return mapper_.get(); }\n\n private:\n  FeatureNameMapperTfBridge() = default;\n\n  std::unique_ptr<FeatureNameMapper> mapper_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_FEATURE_NAME_MAPPER_TF_BRIDGE_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/fill_multi_rank_output_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstdio>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/hash/internal/city.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing LineId = ::idl::matrix::proto::LineId;\n\nclass FillMultiRankOutputOp : public OpKernel {\n public:\n  explicit FillMultiRankOutputOp(OpKernelConstruction *context)\n      : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"enable_draw_as_rank\",\n                                             &enable_draw_as_rank_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"enable_chnid_as_rank\",\n                                             &enable_chnid_as_rank_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"enable_lineid_rank_as_rank\",\n                                             &enable_lineid_rank_as_rank_));\n    if (!(enable_draw_as_rank_ || enable_chnid_as_rank_ ||\n          enable_lineid_rank_as_rank_)) {\n      LOG(FATAL)\n          << \"At least one of enable_draw_as_rank, enable_chnid_as_rank, \"\n             \"enable_lineid_rank_as_rank must be set\";\n    }\n\n    OP_REQUIRES_OK(context, context->GetAttr(\"rank_num\", &rank_num_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n\n    if (variant_type_ != \"instance\" && variant_type_ != \"example\") {\n      LOG(FATAL) << \"Invalid 'variant_type', please choose on from \"\n                    \"['instance', 'example']!\";\n    }\n  }\n\n  void Compute(OpKernelContext *context) override {\n    /* Parse data fields from input tensor. */\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n\n    bool is_instance = variant_type_ == \"instance\";\n    if (is_instance) {\n      Instance instance;\n      instance.CopyFrom(*input_tensor.scalar<Variant>()().get<Instance>());\n      output_tensor->scalar<Variant>()() = std::move(instance);\n    } else {\n      Example example;\n      example.CopyFrom(*input_tensor.scalar<Variant>()().get<Example>());\n      output_tensor->scalar<Variant>()() = std::move(example);\n    }\n\n    LineId *line_id = GetLineId(output_tensor, is_instance);\n    auto label = GetLabel(output_tensor, is_instance);\n\n    /* fill_multi_rank_output() from matrix processor:\n     */\n\n    if (enable_draw_as_rank_) {\n      int rank = line_id->is_draw() ? 1 : 0;\n      label->Add(rank);\n      return;\n    }\n    if (enable_chnid_as_rank_) {\n      int rank = 0, chnid = line_id->chnid();\n      if (chnid == 0 || chnid == 1) {\n        rank = chnid;\n      } else {\n        rank = 2;\n      }\n      label->Add(rank);\n      return;\n    }\n    if (enable_lineid_rank_as_rank_) {\n      int rank = line_id->rank();\n      if (rank >= rank_num_) {\n        rank = rank_num_ - 1;\n      }\n      label->Add(rank);\n      return;\n    }\n  }\n\n private:\n  static LineId *GetLineId(Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_line_id();\n    } else {\n      return output_tensor->scalar<Variant>()()\n          .get<Example>()\n          ->mutable_line_id();\n    }\n  }\n\n  static ::google::protobuf::RepeatedField<float> *GetLabel(\n      Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_label();\n    } else {\n      return output_tensor->scalar<Variant>()().get<Example>()->mutable_label();\n    }\n  }\n\n  static std::vector<uint64_t> GetFids(Tensor *output_tensor,\n                                       bool is_instance) {\n    std::vector<uint64_t> fids;\n    if (is_instance) {\n      auto instance = output_tensor->scalar<Variant>()().get<Instance>();\n      for (uint64_t fid : instance->fid()) {\n        fids.push_back(fid);\n      }\n    } else {\n      auto example = output_tensor->scalar<Variant>()().get<Example>();\n      for (const auto &named_feature : example->named_feature()) {\n        if (named_feature.feature().has_fid_v1_list()) {\n          for (const auto &fid :\n               named_feature.feature().fid_v1_list().value()) {\n            fids.push_back(fid);\n          }\n        }\n      }\n    }\n    return fids;\n  }\n\n  bool enable_draw_as_rank_;\n  bool enable_chnid_as_rank_;\n  bool enable_lineid_rank_as_rank_;\n  int rank_num_;\n  std::string variant_type_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"FillMultiRankOutput\").Device(DEVICE_CPU),\n                        FillMultiRankOutputOp)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/filter_by_label_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstdio>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing LineId = ::idl::matrix::proto::LineId;\n\n// filter_invalid_conseq_time:\nclass FilterByLabelOp : public OpKernel {\n public:\n  explicit FilterByLabelOp(OpKernelConstruction *context) : OpKernel(context) {\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"label_threshold\", &label_threshold_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"filter_equal\", &filter_equal_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n\n    if (variant_type_ != \"instance\" && variant_type_ != \"example\") {\n      LOG(FATAL) << \"Invalid 'variant_type', please choose on from \"\n                    \"['instance', 'example']!\";\n    }\n\n    nlohmann::json j;\n    j[\"label_threshold\"] = label_threshold_;\n    LOG(INFO) << absl::StrFormat(\"Label threshold: %s\", j.dump(2));\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n    auto valid = output_tensor->scalar<bool>();\n    bool is_instance = variant_type_ == \"instance\";\n\n    auto labels = GetLabels(&input_tensor, is_instance);\n    if (labels.size() < label_threshold_.size()) {\n      LOG_EVERY_N_SEC(ERROR, 60) << absl::StrFormat(\n          \"Label size(=%ld) should be >= label_threshold size(=%ld), please \"\n          \"investigate!\",\n          labels.size(), label_threshold_.size());\n      valid() = false;\n    } else {\n      bool has_valid_label = false;\n      for (size_t i = 0; i < label_threshold_.size(); ++i) {\n        if ((labels.Get(i) > label_threshold_[i]) ||\n            (labels.Get(i) == label_threshold_[i] && !filter_equal_)) {\n          has_valid_label = true;\n          break;\n        }\n      }\n\n      valid() = has_valid_label;\n    }\n  }\n\n private:\n  static const ::google::protobuf::RepeatedField<float> &GetLabels(\n      const Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()().get<Instance>()->label();\n    } else {\n      return output_tensor->scalar<Variant>()().get<Example>()->label();\n    }\n  }\n\n  std::vector<float> label_threshold_;\n  bool filter_equal_;\n  std::string variant_type_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"FilterByLabel\").Device(DEVICE_CPU),\n                        FilterByLabelOp)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/gen_fid_mask.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <algorithm>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntemplate <typename T>\nclass MonolithGenFidMaskOp : public OpKernel {\n public:\n  using OpKernel::OpKernel;\n\n  explicit MonolithGenFidMaskOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"fid\", &fid_));\n  }\n\n  void Compute(OpKernelContext *context) override {\n    // Grab the input tensor\n    const Tensor *splits, *values;\n    OP_REQUIRES_OK(context, context->input(\"splits\", &splits));\n    auto splits_flat = splits->flat<T>();\n    OP_REQUIRES_OK(context, context->input(\"values\", &values));\n    auto values_flat = values->flat<int64>();\n\n    // Create an output tensor\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context,\n                   context->allocate_output(0, {splits->NumElements() - 1},\n                                            &output_tensor));\n    auto output_flat = output_tensor->flat<float>();\n    output_flat.setZero();\n\n    for (int i = 1; i < splits->NumElements(); ++i) {\n      int32 start = splits_flat(i - 1);\n      int32 end = splits_flat(i);\n      for (int j = start; j < end; ++j) {\n        if (values_flat(j) == fid_) {\n          output_flat(i - 1) = 1.0;\n          break;\n        }\n      }\n    }\n  }\n\n private:\n  int64 fid_;\n};\n\nnamespace {\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithGenFidMask\").Device(DEVICE_CPU).TypeConstraint<int32>(\"T\"),\n    MonolithGenFidMaskOp<int32>);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithGenFidMask\").Device(DEVICE_CPU).TypeConstraint<int64>(\"T\"),\n    MonolithGenFidMaskOp<int64>);\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/instance_reweight_dataset_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"absl/container/flat_hash_map.h\"\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_def_builder.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/partial_tensor_shape.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/stats_aggregator.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/lib/gtl/cleanup.h\"\n#include \"tensorflow/core/lib/io/buffered_inputstream.h\"\n#include \"tensorflow/core/lib/io/inputbuffer.h\"\n#include \"tensorflow/core/lib/io/zlib_compression_options.h\"\n#include \"tensorflow/core/lib/random/random.h\"\n#include \"tensorflow/core/lib/strings/str_util.h\"\n\n#include \"monolith/native_training/data/kernels/instance_reweight_dataset_kernel.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace {\nconst unsigned int NONEXIST_PRIORITY = 2000;\nconst unsigned int UNKNOWN_PRIORITY = 1000;\n}  // namespace\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing EFeature = ::monolith::io::proto::Feature;\nusing LineId = ::idl::matrix::proto::LineId;\nusing Action = google::protobuf::RepeatedField<int>;\n\n// See documentation in ../../ops/dataset_ops.cc for a high-level\n// description of the following op.\n\n/* static */ constexpr const char\n    *const InstanceReweightDatasetOp::kDatasetType;\n/* static */ constexpr const char\n    *const InstanceReweightDatasetOp::kInputDataset;\n/* static */ constexpr const char *const InstanceReweightDatasetOp::kMethod;\n/* static */ constexpr const char *const InstanceReweightDatasetOp::kActions;\n/* static */ constexpr const char *const InstanceReweightDatasetOp::kWeights;\n/* static */ constexpr const char *const InstanceReweightDatasetOp::kLabels;\n/* static */ constexpr const char *const InstanceReweightDatasetOp::kPriority;\n/* static */ constexpr const char\n    *const InstanceReweightDatasetOp::kVariantType;\n\nclass InnerIterator {\n public:\n  InnerIterator(IteratorBase *input_impl, int instance_reweight_method,\n                const std::vector<int32> &actions,\n                const std::vector<int32> &weights,\n                const std::vector<int32> &labels,\n                const std::vector<int32> &priorities, std::string variant_type)\n      : instance_reweight_method_(instance_reweight_method),\n        variant_type_(std::string(variant_type)) {\n    input_impl_.reset(input_impl);\n    for (size_t i = 0; i < actions.size(); ++i) {\n      reweight_[actions[i]] = weights[i];\n      relabel_[actions[i]] = labels[i];\n    }\n    int idx = 1;\n    for (const auto &value : priorities) {\n      action_priority_[value] = idx++;\n    }\n\n    tensors_ = new std::vector<Tensor>();\n    tensors_->reserve(1);\n  }\n\n  ~InnerIterator() { delete tensors_; }\n\n  Status GetNext(IteratorContext *ctx, std::vector<Tensor> *out_tensors,\n                 bool *end_of_sequence) {\n    Status s = NextInternal(ctx);\n    *end_of_sequence = end_of_sequence_;\n    out_tensors->clear();\n    if (s.ok() && !end_of_sequence_) {\n      out_tensors->push_back(tensors_->back());\n    }\n\n    return s;\n  }\n\n private:\n  Status NextInternal(IteratorContext *ctx) {\n    std::lock_guard<std::mutex> lck(mu_);\n    while ((replicas_ == 0 || index_ == replicas_) && !end_of_sequence_) {\n      tensors_->clear();\n      TF_RETURN_IF_ERROR(\n          input_impl_->GetNext(ctx, tensors_, &end_of_sequence_));\n      if (!end_of_sequence_) {\n        if (variant_type_ == \"instance\") {\n          Instance *instance = GetCurrentInstance();\n          if (instance->label_size()) {\n            float *int_label = instance->mutable_label()->Mutable(0);\n            replicas_ = CalReplicas(instance->line_id(), int_label);\n          } else {\n            replicas_ = 0;\n            LOG_EVERY_N_SEC(ERROR, 60) << \"label is empty, please investigate!\";\n          }\n        } else if (variant_type_ == \"example\") {\n          Example *example = GetCurrentExample();\n          if (example->label_size()) {\n            float *int_label = example->mutable_label()->Mutable(0);\n            replicas_ = CalReplicas(example->line_id(), int_label);\n          } else {\n            replicas_ = 0;\n            LOG_EVERY_N_SEC(ERROR, 60) << \"label is empty, please investigate!\";\n          }\n        } else {\n          return errors::InvalidArgument(\n              absl::StrCat(variant_type_, \" variant_type is invalid!\"));\n        }\n      } else {\n        replicas_ = 0;\n        return Status::OK();\n      }\n      index_ = 0;\n    }\n\n    index_++;\n    return Status::OK();\n  }\n\n  inline Instance *GetCurrentInstance() {\n    Variant *variant = &tensors_->back().scalar<Variant>()();\n    return variant->get<Instance>();\n  }\n\n  inline Example *GetCurrentExample() {\n    Variant *variant = &tensors_->back().scalar<Variant>()();\n    return variant->get<Example>();\n  }\n  int CalReplicas(const LineId &lineid, float *ins_label) {\n    // if priority is NONEXIST_PRIORITY or UNKNOWN_PRIORITY, action will be\n    // undefined\n    auto find_most_prior_action = [&](const Action &actions, int64_t *priority,\n                                      int64_t *action) {\n      *priority = NONEXIST_PRIORITY;\n      if (actions.size() != 0) {\n        *priority = UNKNOWN_PRIORITY;\n        for (auto &act : actions) {\n          auto action_iter = action_priority_.find(act);\n          if (action_iter != action_priority_.end() &&\n              action_iter->second < *priority) {\n            *priority = action_iter->second;\n            *action = act;\n          }\n        }\n      }\n    };\n    auto get_pre_priority = [&]() {\n      int64_t priority, action;\n      find_most_prior_action(lineid.pre_actions(), &priority, &action);\n      return priority;\n    };\n    auto get_cur_priority = [&]() {\n      int64_t priority, action;\n      find_most_prior_action(lineid.actions(), &priority, &action);\n      return priority;\n    };\n    auto get_label = [&](const Action &actions, int64_t *label) {\n      int64_t priority, action = 0;\n      find_most_prior_action(actions, &priority, &action);\n      if (priority != NONEXIST_PRIORITY && priority != UNKNOWN_PRIORITY) {\n        auto label_iter = relabel_.find(action);\n        if (label_iter != relabel_.end()) {\n          *label = label_iter->second;\n          return true;\n        }\n      }\n      return false;\n    };\n    // return true if we can actually get the label\n    auto get_pre_label = [&](int64_t *label) {\n      return get_label(lineid.pre_actions(), label);\n    };\n    // return true if we can actually get the label & relabel if needed\n    auto get_cur_label = [&](int64_t *label) {\n      if (get_label(lineid.actions(), label)) {\n        *ins_label = *label;\n      }\n      *label = *ins_label;\n      return true;\n    };\n    auto get_cnt = [&](const Action &actions) {\n      int64_t ins_num = actions.size() != 0 ? 1 : 0;\n      int64_t priority, action = 0;\n      find_most_prior_action(actions, &priority, &action);\n      for (auto act : actions) {\n        if (action_priority_.contains(act) && act != action) {\n          continue;\n        }\n        // set the ins_num if the act need reweight.\n        auto reweight_iter = reweight_.find(act);\n        if (reweight_iter != reweight_.end()) {\n          if (instance_reweight_method_ == 1) {\n            ins_num += reweight_iter->second;\n          } else {\n            ins_num *= reweight_iter->second;\n          }\n        }\n      }\n      return ins_num;\n    };\n    auto get_pre_cnt = [&]() { return get_cnt(lineid.pre_actions()); };\n    auto get_cur_cnt = [&]() { return get_cnt(lineid.actions()); };\n    auto reverse_label = [&]() { *ins_label = -(*ins_label); };\n    // start from here\n    int64_t pre_priority = get_pre_priority();\n    int64_t cur_priority = get_cur_priority();\n    if (pre_priority > cur_priority) {\n      int64_t pre_label;  // fast emit label, it's a negative sample\n      int64_t cur_label;  // the real label, can be positive or negative\n      // Note: the same sample with different label (+1/-1), equal there is no\n      // sample\n      if (get_cur_label(&cur_label) && get_pre_label(&pre_label)) {\n        // for the real sample (the second one) of fast emit\n        if (pre_label != cur_label) {  // for real positive sample\n          // pre_cnt(-1) + pre_cnt(1) + cur_cnt(1) => cur_cnt(1)\n          return get_pre_cnt() + get_cur_cnt();\n        } else {  // the real negative sample\n          auto pre_cnt = get_pre_cnt();\n          auto cur_cnt = get_cur_cnt();\n          if (pre_cnt > cur_cnt) {\n            reverse_label();\n            // pre_cnt(-1) + (pre_cnt(1) - cur_cnt(1)) => cur_cnt(-1)\n            return pre_cnt - cur_cnt;\n          } else {\n            // pre_cnt(-1) + (cur_cnt(-1) - pre_cnt(-1))  => cur_cnt(-1)\n            return cur_cnt - pre_cnt;\n          }\n        }\n      } else {\n        // for fast emit sample (the first one, negative) or non fast emit\n        // sample\n        return get_cur_cnt();\n      }\n    } else if (pre_priority == NONEXIST_PRIORITY &&\n               cur_priority == NONEXIST_PRIORITY) {\n      return 1;\n    } else {\n      return 0;\n    }\n  }\n\n  std::mutex mu_;\n  int index_ = 0;\n  int replicas_ = 0;\n  bool end_of_sequence_ = false;\n  std::vector<Tensor> *tensors_ = nullptr;\n  std::shared_ptr<IteratorBase> input_impl_ = nullptr;\n  int instance_reweight_method_;\n  absl::flat_hash_map<int, int> reweight_;\n  absl::flat_hash_map<int, int> relabel_;\n  absl::flat_hash_map<int, int> action_priority_;\n  std::string variant_type_;\n};\n\nclass InstanceReweightDatasetOp::Dataset : public DatasetBase {\n public:\n  Dataset(OpKernelContext *ctx, const DatasetBase *input,\n          int instance_reweight_method, const std::vector<int32> &actions,\n          const std::vector<int32> &weights, const std::vector<int32> &labels,\n          const std::vector<int32> &priorities, std::string variant_type)\n      : DatasetBase(DatasetContext(ctx)),\n        input_(input),\n        instance_reweight_method_(instance_reweight_method),\n        actions_(actions),\n        weights_(weights),\n        labels_(labels),\n        priorities_(priorities),\n        variant_type_(std::move(variant_type)) {\n    input_->Ref();\n  }\n\n  ~Dataset() override { input_->Unref(); }\n\n  std::unique_ptr<IteratorBase> MakeIteratorInternal(\n      const string &prefix) const override {\n    return absl::make_unique<Iterator>(\n        Iterator::Params{this, strings::StrCat(prefix, \"::\", kDatasetType)});\n  }\n\n  const DataTypeVector &output_dtypes() const override {\n    return input_->output_dtypes();\n  }\n\n  const std::vector<PartialTensorShape> &output_shapes() const override {\n    return input_->output_shapes();\n  }\n\n  string DebugString() const override {\n    return \"This is the customized Dataset: InstanceReweight\";\n  }\n\n  Status InputDatasets(\n      std::vector<const DatasetBase *> *inputs) const override {\n    inputs->push_back(input_);\n    return Status::OK();\n  }\n\n  Status CheckExternalState() const override {\n    return input_->CheckExternalState();\n  }\n\n protected:\n  Status AsGraphDefInternal(SerializationContext *ctx,\n                            DatasetGraphDefBuilder *b,\n                            Node **output) const override {\n    Node *input_graph_node;\n    TF_RETURN_IF_ERROR(b->AddInputDataset(ctx, input_, &input_graph_node));\n\n    AttrValue method_node;\n    b->BuildAttrValue(instance_reweight_method_, &method_node);\n    AttrValue actions_node;\n    b->BuildAttrValue(actions_, &actions_node);\n    AttrValue weights_node;\n    b->BuildAttrValue(weights_, &weights_node);\n    AttrValue labels_node;\n    b->BuildAttrValue(labels_, &labels_node);\n    AttrValue priorities_node;\n    b->BuildAttrValue(priorities_, &priorities_node);\n    AttrValue variant_type_node;\n    b->BuildAttrValue(variant_type_, &variant_type_node);\n\n    TF_RETURN_IF_ERROR(\n        b->AddDataset(this,                // dataset\n                      {input_graph_node},  // inputs\n                      {{kMethod, method_node},\n                       {kActions, actions_node},\n                       {kWeights, weights_node},\n                       {kLabels, labels_node},\n                       {kPriority, priorities_node},\n                       {kVariantType, variant_type_node}},  // attrs\n                      output));                             // Node**\n\n    return Status::OK();\n  }\n\n private:\n  class Iterator : public DatasetIterator<Dataset> {\n   public:\n    explicit Iterator(const Params &params)\n        : DatasetIterator<Dataset>(params) {}\n\n    Status Initialize(IteratorContext *ctx) override {\n      std::unique_ptr<IteratorBase> input_impl;\n      Status s =\n          dataset()->input_->MakeIterator(ctx, this, prefix(), &input_impl);\n      LOG(INFO) << \"Initialize InnerIterator ...\";\n      iter_ = std::make_unique<InnerIterator>(\n          input_impl.release(), dataset()->instance_reweight_method_,\n          dataset()->actions_, dataset()->weights_, dataset()->labels_,\n          dataset()->priorities_, dataset()->variant_type_);\n      return s;\n    }\n\n    Status GetNextInternal(IteratorContext *ctx,\n                           std::vector<Tensor> *out_tensors,\n                           bool *end_of_sequence) override {\n      out_tensors->reserve(1);\n      TF_RETURN_IF_ERROR(iter_->GetNext(ctx, out_tensors, end_of_sequence));\n      return Status::OK();\n    }\n\n   protected:\n    std::shared_ptr<model::Node> CreateNode(\n        IteratorContext *ctx, model::Node::Args args) const override {\n      return model::MakeUnknownRatioNode(std::move(args));\n    }\n\n    Status SaveInternal(SerializationContext *ctx,\n                        IteratorStateWriter *writer) override {\n      return Status::OK();\n    }\n\n    Status RestoreInternal(IteratorContext *ctx,\n                           IteratorStateReader *reader) override {\n      return Status::OK();\n    }\n\n   private:\n    std::unique_ptr<InnerIterator> iter_;\n  };\n\n  const DatasetBase *const input_;\n  int instance_reweight_method_;\n  std::vector<int32> actions_;\n  std::vector<int32> weights_;\n  std::vector<int32> labels_;\n  std::vector<int32> priorities_;\n  std::string variant_type_;\n};\n\nInstanceReweightDatasetOp::InstanceReweightDatasetOp(OpKernelConstruction *ctx)\n    : UnaryDatasetOpKernel(ctx) {\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kMethod, &instance_reweight_method_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kActions, &actions_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kWeights, &weights_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kLabels, &labels_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kPriority, &priorities_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kVariantType, &variant_type_));\n\n  nlohmann::json j;\n  j[kMethod] = instance_reweight_method_;\n  j[kActions] = actions_;\n  j[kWeights] = weights_;\n  j[kLabels] = labels_;\n  j[kPriority] = priorities_;\n  j[kVariantType] = variant_type_;\n\n  LOG(INFO) << j.dump();\n}\n\nvoid InstanceReweightDatasetOp::MakeDataset(OpKernelContext *ctx,\n                                            DatasetBase *input,\n                                            DatasetBase **output) {\n  *output = new Dataset(ctx, input, instance_reweight_method_, actions_,\n                        weights_, labels_, priorities_, variant_type_);\n}\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"InstanceReweightDataset\").Device(DEVICE_CPU),\n                        InstanceReweightDatasetOp);\n}  // namespace\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/instance_reweight_dataset_kernel.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INSTANCE_REWEIGHT_DATASET_KERNEL_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INSTANCE_REWEIGHT_DATASET_KERNEL_H_\n\n#include \"tensorflow/core/framework/dataset.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\n\nclass InstanceReweightDatasetOp : public UnaryDatasetOpKernel {\n public:\n  static constexpr const char* const kDatasetType = \"instance_reweight\";\n  static constexpr const char* const kInputDataset = \"input_dataset\";\n  static constexpr const char* const kMethod = \"method\";\n  static constexpr const char* const kActions = \"actions\";\n  static constexpr const char* const kWeights = \"weights\";\n  static constexpr const char* const kLabels = \"labels\";\n  static constexpr const char* const kPriority = \"priorities\";\n  static constexpr const char* const kVariantType = \"variant_type\";\n\n  explicit InstanceReweightDatasetOp(OpKernelConstruction* ctx);\n\n protected:\n  void MakeDataset(OpKernelContext* ctx, DatasetBase* input,\n                   DatasetBase** output) override;\n\n private:\n  class Dataset;\n\n  int instance_reweight_method_;\n  std::string variant_type_;\n  std::vector<int32> actions_;\n  std::vector<int32> weights_;\n  std::vector<int32> labels_;\n  std::vector<int32> priorities_;\n};\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n#endif MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INSTANCE_REWEIGHT_DATASET_KERNEL_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_test\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_cc_binary\", \"tf_cc_test\")\n\npackage(default_visibility = [\"//monolith/native_training/data:__subpackages__\"])\n\ncc_library(\n    name = \"relational_utils\",\n    srcs = [],\n    hdrs = [\"relational_utils.h\"],\n    deps = [\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_test(\n    name = \"relational_utils_test\",\n    srcs = [\"relational_utils_test.cc\"],\n    deps = [\n        \":relational_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"label_utils\",\n    srcs = [\"label_utils.cc\"],\n    hdrs = [\"label_utils.h\"],\n    deps = [\n        \"//monolith/native_training/data:data_op_config_cc_proto\",\n        \"//third_party/nlohmann:json\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_test(\n    name = \"label_utils_test\",\n    srcs = [\"label_utils_test.cc\"],\n    deps = [\n        \":label_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"cache_mgr\",\n    srcs = [\n        \"cache_mgr.cc\",\n        \"cache_mgr.h\",\n    ],\n    hdrs = [\"cache_mgr.h\"],\n    deps = [\n        \"//monolith/native_training/data/training_instance:data_reader\",\n        \"//third_party/nlohmann:json\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n    ],\n)\n\ntf_cc_test(\n    name = \"cache_mgr_test\",\n    srcs = [\"cache_mgr_test.cc\"],\n    deps = [\n        \":cache_mgr\",\n        \"@com_google_googletest//:gtest_main\",\n        \"@org_tensorflow//tensorflow/core:test\",\n    ],\n)\n\ncc_library(\n    name = \"datasource_utils\",\n    srcs = [\n        \"datasource_utils.cc\",\n        \"datasource_utils.h\",\n    ],\n    hdrs = [\"datasource_utils.h\"],\n)\n\ntf_cc_test(\n    name = \"datasource_utils_test\",\n    srcs = [\"datasource_utils_test.cc\"],\n    deps = [\n        \":datasource_utils\",\n        \"@com_google_googletest//:gtest_main\",\n        \"@org_tensorflow//tensorflow/core:test\",\n    ],\n)\n\ncc_library(\n    name = \"file_match_split_provider\",\n    srcs = [\"file_match_split_provider.cc\"],\n    hdrs = [\"file_match_split_provider.h\"],\n    deps = [\n        \"//monolith/native_training/runtime/concurrency:queue\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ntf_cc_test(\n    name = \"file_match_split_provider_test\",\n    srcs = [\"file_match_split_provider_test.cc\"],\n    deps = [\n        \":file_match_split_provider\",\n        \"@com_google_googletest//:gtest_main\",\n        \"@org_tensorflow//tensorflow/core:test\",\n    ],\n)\n\ncc_library(\n    name = \"parquet_example_reader\",\n    srcs = [\n        \"arrow_random_access_file.h\",\n        \"sized_random_access_file.h\",\n        \"parquet_column_buffer.h\",\n        \"parquet_example_reader.h\",\n    ],\n    deps = [\n        \"@arrow\",\n    ]\n)\n\ncc_library(\n    name = \"uniq_hashtable\",\n    hdrs = [\"uniq_hashtable.h\"],\n    deps = [\n        \"@com_google_absl//absl/base:core_headers\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ntf_cc_test(\n    name = \"uniq_hashtable_test\",\n    srcs = [\"uniq_hashtable_test.cc\"],\n    deps = [\n        \":uniq_hashtable\",\n        \"@com_google_googletest//:gtest_main\",\n        \"@org_tensorflow//tensorflow/core:test\",\n    ]\n)\n\ncc_library(\n    name = \"value_filter_by_line_id\",\n    hdrs = [\"value_filter_by_line_id.h\"],\n    srcs = [\"value_filter_by_line_id.cc\"],\n    deps = [\n        \":relational_utils\",\n        \"//idl:example_cc_proto\",\n        \"//third_party/nlohmann:json\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_absl//absl/synchronization\",\n        \"@org_tensorflow//tensorflow/core/platform:env\",\n    ],\n)\n\ncc_library(\n    name = \"value_filter_by_feature\",\n    hdrs = [\"value_filter_by_feature.h\"],\n    srcs = [\"value_filter_by_feature.cc\"],\n    deps = [\n        \":relational_utils\",\n        \"//idl:example_cc_proto\",\n        \"//third_party/nlohmann:json\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_absl//absl/synchronization\",\n        \"@org_tensorflow//tensorflow/core/platform:env\",\n    ],\n)\n\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/arrow_random_access_file.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef TENSORFLOW_IO_ARROW_KERNELS_H_\n#define TENSORFLOW_IO_ARROW_KERNELS_H_\n\n#include \"arrow/buffer.h\"\n#include \"arrow/io/api.h\"\n#include \"arrow/type.h\"\n#include \"parquet/windows_compatibility.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n// #include \"tensorflow_io/core/kernels/io_stream.h\"\n\nnamespace tensorflow {\n\nclass RandomAccessFile;\n\nnamespace data {\n\n// NOTE: Both SizedRandomAccessFile and ArrowRandomAccessFile overlap\n// with another PR. Will remove duplicate once PR merged\n\nclass ArrowRandomAccessFile : public ::arrow::io::RandomAccessFile {\n public:\n  explicit ArrowRandomAccessFile(tensorflow::RandomAccessFile* file, int64 size)\n      : file_(file), size_(size), position_(0) {}\n\n  ~ArrowRandomAccessFile() {}\n  arrow::Status Close() override { return arrow::Status::OK(); }\n  bool closed() const override { return false; }\n  arrow::Result<int64_t> Tell() const override { return position_; }\n  arrow::Status Seek(int64_t position) override {\n    return arrow::Status::NotImplemented(\"Seek\");\n  }\n  arrow::Result<int64_t> Read(int64_t nbytes, void* out) override {\n    StringPiece result;\n    Status status =\n        file_->Read(position_, nbytes, &result, reinterpret_cast<char*>(out));\n    if (!(status.ok() || errors::IsOutOfRange(status))) {\n      return arrow::Status::IOError(status.error_message());\n    }\n    position_ += result.size();\n    return result.size();\n  }\n  arrow::Result<std::shared_ptr<arrow::Buffer>> Read(int64_t nbytes) override {\n    arrow::Result<std::shared_ptr<arrow::ResizableBuffer>> result =\n        arrow::AllocateResizableBuffer(nbytes);\n    ARROW_RETURN_NOT_OK(result);\n    std::shared_ptr<arrow::ResizableBuffer> buffer =\n        std::move(result).ValueUnsafe();\n\n    ARROW_ASSIGN_OR_RAISE(int64_t bytes_read,\n                          Read(nbytes, buffer->mutable_data()));\n    RETURN_NOT_OK(buffer->Resize(bytes_read));\n    return buffer;\n  }\n  arrow::Result<int64_t> GetSize() override { return size_; }\n  bool supports_zero_copy() const override { return false; }\n  arrow::Result<int64_t> ReadAt(int64_t position, int64_t nbytes,\n                                void* out) override {\n    StringPiece result;\n    Status status =\n        file_->Read(position, nbytes, &result, reinterpret_cast<char*>(out));\n    if (!(status.ok() || errors::IsOutOfRange(status))) {\n      return arrow::Status::IOError(status.error_message());\n    }\n    return result.size();\n  }\n  arrow::Result<std::shared_ptr<arrow::Buffer>> ReadAt(\n      int64_t position, int64_t nbytes) override {\n    string buffer;\n    buffer.resize(nbytes);\n    StringPiece result;\n    Status status = file_->Read(position, nbytes, &result,\n                                reinterpret_cast<char*>(&buffer[0]));\n    if (!(status.ok() || errors::IsOutOfRange(status))) {\n      return arrow::Status::IOError(status.error_message());\n    }\n    buffer.resize(result.size());\n    return arrow::Buffer::FromString(std::move(buffer));\n  }\n\n private:\n  tensorflow::RandomAccessFile* file_;\n  int64 size_;\n  int64 position_;\n};\n\n}  // namespace data\n}  // namespace tensorflow\n\n#endif  // TENSORFLOW_IO_ARROW_KERNELS_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/cache_mgr.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/cache_mgr.h\"\n\n#include <cstdlib>\n#include <deque>\n#include <random>\n\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nusing json = nlohmann::json;\nusing LineId = ::idl::matrix::proto::LineId;\nusing Action = google::protobuf::RepeatedField<int>;\nusing Example = ::monolith::io::proto::Example;\nusing EFeature = ::monolith::io::proto::NamedFeature;\nusing ChannelCache = ::monolith::io::proto::ChannelCache;\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\n\nstd::shared_ptr<ItemFeatures> MakeItemFeaturesFromProto(\n    const ::monolith::io::proto::FeatureData& feature_data) {\n  std::shared_ptr<ItemFeatures> item_feature_ptr =\n      std::make_shared<ItemFeatures>();\n  item_feature_ptr->item_id = feature_data.gid();\n  for (const auto& fid : feature_data.fids()) {\n    item_feature_ptr->fids.push_back(fid);\n  }\n  for (const auto& fc : feature_data.feature_columns()) {\n    item_feature_ptr->example_features.emplace(fc.name(), fc);\n  }\n  return item_feature_ptr;\n}\n\nbool ItemFeatures::Equal(const ItemFeatures& other) const {\n  if (item_id != other.item_id) {\n    return false;\n  }\n\n  if (fids.size() != other.fids.size()) {\n    return false;\n  } else {\n    std::unordered_set<uint64_t> this_fids(fids.begin(), fids.end());\n    std::unordered_set<uint64_t> other_fids(other.fids.begin(),\n                                            other.fids.end());\n\n    if (this_fids.size() != other_fids.size()) {\n      return false;\n    } else {\n      std::set<uint64_t> intersection;\n      std::set_intersection(this_fids.begin(), this_fids.end(),\n                            other_fids.begin(), other_fids.end(),\n                            std::inserter(intersection, intersection.begin()));\n      if (this_fids.size() != intersection.size()) {\n        return false;\n      }\n    }\n  }\n\n  if (example_features.size() != other.example_features.size()) {\n    for (const auto& it : example_features) {\n      if (other.example_features.count(it.first) == 0) {\n        return false;\n      } else {\n        auto this_feat = it.second.SerializeAsString();\n        auto other_feat =\n            other.example_features.at(it.first).SerializeAsString();\n        if (this_feat != other_feat) {\n          return false;\n        }\n      }\n    }\n  }\n\n  return true;\n}\n\nCacheWithGid::CacheWithGid(int max_item_num, int start_num)\n    : start_num_(start_num), max_item_num_(max_item_num) {}\n\nvoid CacheWithGid::Push(uint64_t item_id,\n                        std::shared_ptr<const ItemFeatures> item,\n                        int64_t origin_cnt, int64_t sample_cnt) {\n  auto it = data_.find(item_id);\n  if (it == data_.end()) {\n    data_queue_.emplace_back(item_id);\n    data_.emplace(item_id, item);\n  }\n\n  auto iit = stats_.find(item_id);\n  if (iit == stats_.end()) {\n    auto stats_ptr = std::make_shared<GroupStat>();\n    stats_ptr->origin_cnt = origin_cnt;\n    stats_ptr->sample_cnt = sample_cnt;\n    stats_.emplace(item_id, stats_ptr);\n  } else {\n    iit->second->origin_cnt += origin_cnt;\n    iit->second->sample_cnt += sample_cnt;\n  }\n\n  if ((int64_t)data_queue_.size() > max_item_num_) {\n    uint64_t item_id = data_queue_.front();\n    data_.erase(item_id);\n    stats_.erase(item_id);\n    data_queue_.pop_front();\n  }\n}\n\nstd::shared_ptr<const ItemFeatures> CacheWithGid::RandomSelectOne(\n    double* freq_factor, double* time_factor) const {\n  if ((int64_t)data_queue_.size() <= start_num_) {\n    return nullptr;\n  }\n  thread_local std::mt19937 gen((std::random_device())());\n  size_t index = gen() % data_queue_.size();\n  uint64_t item_id = data_queue_[index];\n  auto it = data_.find(item_id);\n  if (it != data_.end()) {\n    *freq_factor = 1.0 / ++(stats_[item_id]->sample_cnt);\n    *time_factor = (index + 1.0) / data_queue_.size();\n    return it->second;\n  } else {\n    LOG_EVERY_N_SEC(ERROR, 1)\n        << \"item_id \" << item_id << \"in queue but not in map\";\n  }\n  return nullptr;\n}\n\nvoid CacheWithGid::ToProto(ChannelCache* proto) const {\n  for (auto it : data_) {\n    auto* feature_data = proto->add_feature_datas();\n    feature_data->set_gid(it.first);  // gid\n\n    for (const auto& fid : it.second->fids) {\n      feature_data->add_fids(fid);\n    }\n\n    const auto& example_features =\n        it.second->example_features;  // std::shared_ptr<const ItemFeatures>\n    for (const auto& fc_it : example_features) {\n      auto* feature_columns = feature_data->add_feature_columns();\n      feature_columns->CopyFrom(fc_it.second);\n    }\n\n    const auto& stats = stats_[it.first];\n    feature_data->set_origin_cnt(stats->origin_cnt);\n    feature_data->set_sample_cnt(stats->sample_cnt);\n  }\n  LOG_EVERY_N(INFO, 1000) << \"save size \" << data_queue_.size() << \" \"\n                          << data_.size();\n}\n\nvoid CacheWithGid::FromProto(const ChannelCache& proto) {\n  data_queue_.clear();\n  data_.clear();\n  for (int i = 0; i < proto.feature_datas_size(); ++i) {\n    const auto& feature_data = proto.feature_datas(i);\n    auto gid = feature_data.gid();\n    data_queue_.emplace_back(gid);\n\n    auto group_feature_ptr = std::make_shared<ItemFeatures>();\n    group_feature_ptr->item_id = gid;\n    for (const auto& fid : feature_data.fids()) {\n      group_feature_ptr->fids.push_back(fid);\n    }\n    for (const auto& fc : feature_data.feature_columns()) {\n      group_feature_ptr->example_features.emplace(fc.name(), fc);\n    }\n    data_.emplace(gid, group_feature_ptr);\n\n    std::shared_ptr<GroupStat> stats = std::make_shared<GroupStat>();\n    stats->origin_cnt = feature_data.origin_cnt();\n    stats->sample_cnt = feature_data.sample_cnt();\n    stats_[gid] = stats;\n  }\n  LOG_EVERY_N(INFO, 1000) << \"restore size \" << data_queue_.size() << \" \"\n                          << data_.size();\n}\n\nbool CacheWithGid::Equal(const CacheWithGid& other) const {\n  if (start_num_ != other.start_num_) {\n    return false;\n  }\n\n  if (max_item_num_ != other.max_item_num_) {\n    return false;\n  }\n\n  if (stats_.size() != other.stats_.size()) {\n    return false;\n  } else {\n    for (const auto& it : stats_) {\n      auto oit = other.stats_.find(it.first);\n      if (oit == other.stats_.end()) {\n        return false;\n      } else {\n        if (it.second->Equal(*oit->second.get())) {\n          return false;\n        }\n      }\n    }\n  }\n\n  if (data_.size() != other.data_.size()) {\n    return false;\n  } else {\n    for (const auto& it : data_) {\n      if (other.data_.count(it.first) == 0) {\n        return false;\n      } else {\n        return it.second->Equal(*other.data_.at(it.first).get());\n      }\n    }\n  }\n\n  return true;\n}\n\nCacheManager::CacheManager(int max_item_num_per_channel, int start_num)\n    : start_num_(start_num),\n      max_item_num_per_channel_(max_item_num_per_channel) {}\n\nstd::shared_ptr<const ItemFeatures> CacheManager::RandomSelectOne(\n    uint64_t channel_id, double* freq_factor, double* time_factor) const {\n  auto it = channel_cache_.find(channel_id);\n  if (it != channel_cache_.end()) {\n    return it->second.RandomSelectOne(freq_factor, time_factor);\n  }\n  return nullptr;\n}\n\nvoid CacheManager::Push(uint64_t channel_id, uint64_t item_id,\n                        const std::shared_ptr<const ItemFeatures>& item,\n                        int64_t origin_cnt, int64_t sample_cnt) {\n  auto it = channel_cache_.find(channel_id);\n  if (it == channel_cache_.end()) {\n    LOG(INFO) << \"Create channel(\" << channel_id\n              << \") in ItemPoolResource CacheManager\";\n    auto ret = channel_cache_.emplace(\n        channel_id, CacheWithGid(max_item_num_per_channel_, start_num_));\n    it = ret.first;\n  }\n  it->second.Push(item_id, item, origin_cnt, sample_cnt);\n}\n\nabsl::flat_hash_map<uint64_t, CacheWithGid>& CacheManager::GetCache() {\n  return channel_cache_;\n}\n\nvoid CacheManager::SampleChannelID(uint64_t* channel_id) {\n  std::vector<uint64_t> channel_ids;\n  std::vector<int> cache_size;\n\n  if (channel_cache_.size() >= 2) {\n    for (auto iter = channel_cache_.begin(); iter != channel_cache_.end();\n         ++iter) {\n      channel_ids.emplace_back(iter->first);\n      cache_size.emplace_back(iter->second.Size());\n    }\n\n    std::discrete_distribution<int> discrete_dist(cache_size.begin(),\n                                                  cache_size.end());\n\n    std::mt19937 gen(std::random_device{}());\n    *channel_id = channel_ids[discrete_dist(gen)];\n  }\n}\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/cache_mgr.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_CACHE_MGR_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_CACHE_MGR_H_\n\n#include <algorithm>\n#include <cstdlib>\n#include <deque>\n#include <set>\n#include <string>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/platform/env.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\n\nstruct GroupStat {\n  uint32_t origin_cnt = 0;\n  uint32_t sample_cnt = 0;\n\n  inline bool operator==(const GroupStat &rhs) const {\n    return origin_cnt == rhs.origin_cnt && sample_cnt == rhs.sample_cnt;\n  }\n\n  inline bool Equal(const GroupStat &rhs) const {\n    return origin_cnt == rhs.origin_cnt && sample_cnt == rhs.sample_cnt;\n  }\n};\n\nstruct ItemFeatures {\n  uint64_t item_id;\n  std::vector<uint64_t> fids;\n  absl::flat_hash_map<std::string, ::monolith::io::proto::NamedFeature>\n      example_features;\n\n  bool Equal(const ItemFeatures &other) const;\n};\n\nstd::shared_ptr<ItemFeatures> MakeItemFeaturesFromProto(\n    const ::monolith::io::proto::FeatureData &feature_data);\n\nclass CacheWithGid {\n public:\n  explicit CacheWithGid(int max_item_num, int start_num = 0);\n\n  void Push(uint64_t item_id, std::shared_ptr<const ItemFeatures> item,\n            int64_t origin_cnt = 1, int64_t sample_cnt = 0);\n\n  std::shared_ptr<const ItemFeatures> RandomSelectOne(\n      double *freq_factor, double *time_factor) const;\n\n  void ToProto(::monolith::io::proto::ChannelCache *proto) const;\n\n  void FromProto(const ::monolith::io::proto::ChannelCache &proto);\n\n  bool Equal(const CacheWithGid &other) const;\n\n  inline int Size() const { return data_queue_.size(); }\n\n private:\n  int start_num_;\n  int max_item_num_;\n\n  absl::flat_hash_map<uint64_t, std::shared_ptr<const ItemFeatures>> data_;\n  mutable absl::flat_hash_map<uint64_t, std::shared_ptr<GroupStat>> stats_;\n  std::deque<uint64_t> data_queue_;\n};\n\nclass CacheManager {\n public:\n  explicit CacheManager(int max_item_num_per_channel, int start_num = 0);\n\n  std::shared_ptr<const ItemFeatures> RandomSelectOne(\n      uint64_t channel_id, double *freq_factor, double *time_factor) const;\n\n  void Push(uint64_t channel_id, uint64_t item_id,\n            const std::shared_ptr<const ItemFeatures> &item,\n            int64_t origin_cnt = 1, int64_t sample_cnt = 0);\n\n  absl::flat_hash_map<uint64_t, CacheWithGid> &GetCache();\n\n  void SampleChannelID(uint64_t* channel_id);\n\n private:\n  int start_num_;\n  int max_item_num_per_channel_;\n\n  absl::flat_hash_map<uint64_t, CacheWithGid> channel_cache_;\n};\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_CACHE_MGR_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/cache_mgr_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/cache_mgr.h\"\n#include <cstdlib>\n#include \"gtest/gtest.h\"\n\n#include \"absl/strings/str_cat.h\"\n\nusing NamedFeature = ::monolith::io::proto::NamedFeature;\nusing ChannelCache = ::monolith::io::proto::ChannelCache;\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\nnamespace {\n\nstatic constexpr uint64_t MASK = (1L << 48) - 1;\n\nvoid gen_named_feature(NamedFeature *nf) {\n  int slot = std::rand() % 1024;\n  nf->set_name(absl::StrCat(\"fc_\", slot));\n  auto *fid_v2_list = nf->mutable_feature()->mutable_fid_v2_list();\n  int num_fids = std::abs(std::rand() % 20) + 1;\n  for (int i = 0; i < num_fids; ++i) {\n    uint64_t fid = ((uint64_t)slot << 48) | ((std::rand() % 100000) & MASK);\n    fid_v2_list->add_value(fid);\n  }\n}\n\nvoid gen_item_features(ItemFeatures *item) {\n  int num_feats = std::abs(std::rand() % 20) + 1;\n  for (int i = 0; i < num_feats; ++i) {\n    NamedFeature nf;\n    gen_named_feature(&nf);\n    if (!item->example_features.contains(nf.name())) {\n      item->example_features.insert({nf.name(), nf});\n    }\n  }\n}\n\nvoid fill_cache_with_gid(CacheWithGid *cwg) {\n  for (int i = 0; i < 80; ++i) {\n    std::shared_ptr<ItemFeatures> item = std::make_shared<ItemFeatures>();\n    gen_item_features(item.get());\n\n    int gid = std::abs(std::rand() % 1024) + 1;\n    cwg->Push(gid, item);\n  }\n}\n\nTEST(CACHE_MGR, CacheWithGid) {\n  CacheWithGid cwg(100, 20);\n  fill_cache_with_gid(&cwg);\n\n  ChannelCache cache;\n  cwg.ToProto(&cache);\n\n  CacheWithGid cwg2(100, 20);\n  cwg2.FromProto(cache);\n}\n\nTEST(CACHE_MGR, CacheManager) {\n  CacheManager cm(1000, 20);\n\n  for (int i = 0; i < 50; ++i) {\n    const std::shared_ptr<ItemFeatures> item = std::make_shared<ItemFeatures>();\n    gen_item_features(item.get());\n    int gid = std::abs(std::rand() % 1024) + 1;\n    cm.Push(1, gid, item);\n  }\n  EXPECT_EQ(cm.GetCache().size(), 1);\n}\n\n}  // namespace\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/datasource_utils.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/datasource_utils.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\n\nint32_t java_hash_code(const std::string &data_flow_name) {\n  int32_t h = 0;\n  if (h == 0 && data_flow_name.length() > 0) {\n    for (uint32_t i = 0; i < data_flow_name.length(); ++i) {\n      h = 31 * h + data_flow_name.at(i);\n    }\n  }\n  return h;\n}\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/datasource_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_DATASOURCE_UTILS_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_DATASOURCE_UTILS_H_\n\n#include <string>\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\n\nint32_t java_hash_code(const std::string &data_flow_name);\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_DATASOURCE_UTILS_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/datasource_utils_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/datasource_utils.h\"\n\n#include <memory>\n#include \"gtest/gtest.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\nnamespace {\n\nTEST(DatasourceUtils, JavaHashCode) {\n  int32_t code = java_hash_code(\"datasource_inst\");\n  EXPECT_EQ(code, -1487072768);\n}\n\n}  // namespace\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/file_match_split_provider.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/file_match_split_provider.h\"\n\n#include <chrono>\n#include <memory>\n#include <thread>\n#include \"absl/memory/memory.h\"\n#include \"absl/strings/str_join.h\"\n#include \"absl/strings/str_split.h\"\n\n#include \"tensorflow/core/lib/io/path.h\"\n#include \"tensorflow/core/platform/default/logging.h\"\n#include \"tensorflow/core/platform/env.h\"\n#include \"tensorflow/core/platform/errors.h\"\n\nstatic constexpr char kCurrentPat[] = \"current_pattern\";\nstatic constexpr char kCurrentFile[] = \"current_file\";\nstatic constexpr char kQueueContent[] = \"queue_content\";\nusing std::chrono::milliseconds;\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\n\nStatus FileMatchSplitProvider::GetNext(Tensor *split, bool *end_of_splits) {\n  mutex_lock l(mu_);\n  if (!feeder_) {\n    TF_RETURN_IF_ERROR(EnsureFeederInitialized());\n    LOG(INFO) << \"EnsureFeederInitialized Done!\";\n  }\n\n  *end_of_splits = false;\n  *split = Tensor(DT_STRING, TensorShape{});\n  if (canceled_) {\n    *end_of_splits = true;\n    return errors::Cancelled(\n        \"FileMatchSplitProvider canceled, get an end_of_splits!\");\n  }\n\n  std::string item;\n  while (!results_.try_pop(item, milliseconds(10))) {\n    if (finished_feed_ && results_.empty()) {\n      *end_of_splits = true;\n      std::string info = absl::StrCat(\n          \"finished_feed is \", finished_feed_.load(), \", and results empty is \",\n          results_.empty(), \", get an end_of_splits!\");\n      LOG_EVERY_N_SEC(INFO, 300) << info;\n      return errors::OutOfRange(info);\n    }\n  }\n\n  split->scalar<tstring>()() = item;\n  return Status::OK();\n}\n\nStatus FileMatchSplitProvider::Reset() {\n  mutex_lock l(mu_);\n  // ensure feeder thread join\n  canceled_ = true;\n  finished_feed_ = true;\n  feeder_ = nullptr;\n\n  // clear queue\n  std::string item;\n  while (!results_.empty()) {\n    results_.try_pop(item, milliseconds(1));\n  }\n\n  canceled_ = false;\n  finished_feed_ = false;\n  current_pat_ = \"\";\n  current_file_ = \"\";\n  TF_RETURN_IF_ERROR(EnsureFeederInitialized());\n  return Status::OK();\n}\n\nStatus FileMatchSplitProvider::Save(\n    std::function<std::string(std::string)> key_name_fn,\n    IteratorStateWriter *writer) {\n  TF_RETURN_IF_ERROR(\n      writer->WriteScalar(key_name_fn(kCurrentPat), current_pat_));\n  TF_RETURN_IF_ERROR(\n      writer->WriteScalar(key_name_fn(kCurrentFile), current_file_));\n\n  std::vector<std::string> content;\n  while (!results_.empty()) {\n    std::string item;\n    results_.pop(item);\n    content.push_back(item);\n  }\n  TF_RETURN_IF_ERROR(\n      writer->WriteScalar(key_name_fn(kQueueContent),\n                          absl::StrJoin(content.begin(), content.end(), \",\")));\n  return Status::OK();\n}\n\nStatus FileMatchSplitProvider::Restore(\n    std::function<std::string(std::string)> key_name_fn,\n    IteratorStateReader *reader) {\n  canceled_ = false;\n  finished_feed_ = false;\n  tstring current_pat, current_file, content_str;\n  TF_RETURN_IF_ERROR(\n      reader->ReadScalar(key_name_fn(kCurrentPat), &current_pat));\n  current_pat_ = std::string(current_pat);\n  TF_RETURN_IF_ERROR(\n      reader->ReadScalar(key_name_fn(kCurrentFile), &current_file));\n  current_file_ = std::string(current_file);\n  TF_RETURN_IF_ERROR(\n      reader->ReadScalar(key_name_fn(kQueueContent), &content_str));\n  std::vector<std::string> content_list =\n      absl::StrSplit(absl::string_view(content_str), ',');\n  for (const std::string &item : content_list) {\n    results_.push(item);\n  }\n  return Status::OK();\n}\n\nStatus FileMatchSplitProvider::EnsureFeederInitialized() {\n  finished_feed_ = false;\n  feeder_ = absl::WrapUnique(Env::Default()->StartThread(\n      {}, \"file-match-split-provider-feeder\", [this]() { FeederThread(); }));\n  return Status::OK();\n}\n\nvoid FileMatchSplitProvider::FeederThread() {\n  LOG(INFO) << \"thread file-match-split-provider-feeder started!\";\n  int max_retry = 5, current_try = 0;\n  const auto timeout = milliseconds(10);\n  Env *env = Env::Default();\n\n  // find the start point\n  int start = 0;\n  if (!current_pat_.empty()) {\n    for (const std::string &pattern : patterns_) {\n      start++;\n      if (pattern == current_pat_) {\n        break;\n      }\n    }\n    LOG(INFO) << \"current_pat is \" << current_pat_ << \", start at \"\n              << start - 1;\n  }\n\n  if (start >= patterns_.size() && patterns_.back() != current_pat_) {\n    LOG(WARNING) << \"Cannot find \" << current_pat_ << \" in patterns, skip!\";\n    current_pat_ = \"\";\n    start = 0;\n  }\n\n  // finish the files in current_pat_ if any\n  std::vector<std::string> matched_files;\n  if (!current_pat_.empty()) {\n    current_try = 0;\n    while (!env->GetMatchingPaths(current_pat_, &matched_files).ok()) {\n      if (canceled_) return;\n      current_try++;\n      matched_files.clear();\n      std::this_thread::sleep_for(milliseconds(1000));\n      if (current_try >= max_retry) {\n        LOG(INFO) << \"GetMatchingPaths for pattern \" << current_pat_\n                  << \" fail, retry!\";\n        break;\n      }\n    }\n\n    int idx = 0;\n    if (!current_file_.empty()) {\n      for (const std::string &file : matched_files) {\n        if (file != current_file_) {\n          idx++;\n        } else {\n          break;\n        }\n      }\n      LOG(INFO) << \"current_file is \" << current_file_ << \", start at \" << idx;\n    }\n\n    int num_files = 0;\n    for (size_t i = idx; i < matched_files.size(); ++i) {\n      current_file_ = matched_files[i];\n      while (!results_.try_push(current_file_, timeout)) {\n        if (canceled_) return;\n        std::this_thread::sleep_for(timeout);\n      }\n      num_files++;\n    }\n    LOG_EVERY_N(INFO, 100) << \"Pattern \" << current_pat_ << \" has matched \" << num_files\n                           << \"/\" << matched_files.size() << \" files\";\n  }\n\n  // for the patterns after current_pat_\n  for (size_t i = start; i < patterns_.size(); ++i) {\n    if (canceled_) return;\n    matched_files.clear();\n    current_pat_ = patterns_[i];\n    current_try = 0;\n    while (!env->GetMatchingPaths(current_pat_, &matched_files).ok()) {\n      if (canceled_) return;\n      current_try++;\n      matched_files.clear();\n      std::this_thread::sleep_for(milliseconds(1000));\n      if (current_try >= max_retry) {\n        LOG(INFO) << \"GetMatchingPaths for pattern \" << current_pat_\n                  << \" fail, retry!\";\n      }\n    }\n\n    int num_files = 0;\n    for (const std::string &file : matched_files) {\n      current_file_ = file;\n      while (!results_.try_push(current_file_, timeout)) {\n        if (canceled_) return;\n        std::this_thread::sleep_for(timeout);\n      }\n      num_files++;\n    }\n    LOG_EVERY_N(INFO, 100) << \"Pattern \" << current_pat_ << \" has matched \" << num_files\n                           << \"/\" << matched_files.size() << \" files\";\n  }\n\n  finished_feed_ = true;\n  LOG(INFO) << \"thread file-match-split-provider-feeder finished!\";\n}\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/file_match_split_provider.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_FILE_MATCH_SPLIT_PROVIDER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_FILE_MATCH_SPLIT_PROVIDER_H_\n\n#include <atomic>\n#include \"monolith/native_training/runtime/concurrency/queue.h\"\n#include \"tensorflow/core/framework/dataset.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/platform/status.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\n\n// SplitProvider which reads splits from a tf.data service dispatcher over RPC.\nclass FileMatchSplitProvider : public SplitProvider {\n public:\n  explicit FileMatchSplitProvider(const std::vector<std::string>& patterns,\n                                  int queue_size = 1024)\n      : canceled_(false),\n        finished_feed_(false),\n        patterns_(patterns),\n        results_(queue_size) {}\n\n  Status GetNext(Tensor* split, bool* end_of_splits) override;\n  Status Reset() override;\n  Status Save(std::function<std::string(std::string)> full_name,\n              IteratorStateWriter* writer) override;\n  Status Restore(std::function<std::string(std::string)> full_name,\n                 IteratorStateReader* reader) override;\n\n private:\n  mutex mu_;\n  std::atomic<bool> canceled_;\n  std::atomic<bool> finished_feed_;\n\n  std::string current_pat_ = \"\";\n  std::string current_file_ = \"\";\n  const std::vector<std::string> patterns_;\n  ::monolith::concurrency::Queue<std::string> results_;\n  std::unique_ptr<Thread> feeder_;\n\n  Status EnsureFeederInitialized();\n  void FeederThread();\n};\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_FILE_MATCH_SPLIT_PROVIDER_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/file_match_split_provider_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/file_match_split_provider.h\"\n\n#include <unistd.h>\n#include <memory>\n#include <string>\n#include <vector>\n#include \"absl/strings/str_cat.h\"\n#include \"gtest/gtest.h\"\n\nusing std::chrono::milliseconds;\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\nnamespace {\n\nTEST(FileMatchSplitProvider, Create) {\n  char tmp[256];\n  getcwd(tmp, 256);\n  std::vector<std::string> patterns = {\n      absl::StrCat(tmp, \"/monolith/native_training/data/kernels/*.h\"),\n      absl::StrCat(tmp, \"/monolith/native_training/data/kernels/*.cc\")};\n  FileMatchSplitProvider split_provider(patterns);\n\n  Tensor split;\n  bool end_of_splits = false;\n  int cnt = 0;\n  while (!end_of_splits) {\n    Status s = split_provider.GetNext(&split, &end_of_splits);\n    if (!s.ok() || end_of_splits) {\n      return;\n    } else {\n      cnt++;\n      LOG(INFO) << split.scalar<tstring>()();\n    }\n  }\n\n  EXPECT_GE(cnt, 0);\n}\n\nTEST(FileMatchSplitProvider, Reset) {\n  char tmp[256];\n  getcwd(tmp, 256);\n  std::vector<std::string> patterns = {\n      absl::StrCat(tmp, \"/monolith/native_training/data/kernels/internal/*\")};\n  FileMatchSplitProvider split_provider(patterns);\n  Status s;\n\n  Tensor split;\n  bool end_of_splits = false;\n  s.Update(split_provider.GetNext(&split, &end_of_splits));\n\n  split_provider.Reset();\n  int cnt = 0;\n  end_of_splits = false;\n  while (!end_of_splits) {\n    s.Update(split_provider.GetNext(&split, &end_of_splits));\n    if (!s.ok() || end_of_splits) {\n      return;\n    } else {\n      cnt++;\n      LOG(INFO) << split.scalar<tstring>()();\n    }\n  }\n  EXPECT_GE(cnt, 1);\n}\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/label_utils.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n\n#include <algorithm>\n#include <set>\n#include <string>\n#include <vector>\n\n#include \"absl/strings/numbers.h\"\n#include \"absl/strings/str_split.h\"\n#include \"glog/logging.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\nusing LabelConf = ::monolith::native_training::data::config::LabelConf;\n\nbool HasIntersection(const std::set<int32_t> &lhs,\n                     const std::set<int32_t> &rhs) {\n  std::set<uint64_t> intersection;\n  std::set_intersection(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),\n                        std::inserter(intersection, intersection.begin()));\n  return !intersection.empty();\n}\n\nbool ParseTaskConfig(const std::string &config,\n                     std::vector<TaskConfig> *task_configs) {\n  task_configs->clear();\n  LabelConf label_conf;\n  if (!label_conf.ParseFromString(config)) {\n    LOG(FATAL) << \"Parse label config error: \" << config;\n  }\n\n  CHECK_GT(label_conf.conf_size(), 0);\n  task_configs->reserve(label_conf.conf_size());\n\n  for (const auto &t : label_conf.conf()) {\n    // pos_actions : neg_actions : sample_rate\n\n    std::set<int32_t> pos_actions, neg_actions;\n    CHECK(!t.pos_actions().empty());\n    pos_actions.insert(t.pos_actions().begin(), t.pos_actions().end());\n\n    if (!t.neg_actions().empty()) {\n      neg_actions.insert(t.neg_actions().begin(), t.neg_actions().end());\n    }\n\n    CHECK(!HasIntersection(pos_actions, neg_actions));\n\n    float sample_rate = t.sample_rate();\n    CHECK_GE(sample_rate, 0);\n    CHECK_LE(sample_rate, 1.0);\n\n    task_configs->push_back({pos_actions, neg_actions, sample_rate});\n  }\n\n  return true;\n}\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/label_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_LABEL_UTILS_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_LABEL_UTILS_H_\n\n#include <algorithm>\n#include <set>\n#include <string>\n#include <vector>\n\n#include \"monolith/native_training/data/data_op_config.pb.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\n\nconstexpr float INVALID_LABEL = std::numeric_limits<float>::lowest();\nconstexpr float POSITIVE_LABEL = 1.0;\n\nstruct TaskConfig {\n  std::set<int32_t> pos_actions;\n  std::set<int32_t> neg_actions;\n  float sample_rate;\n\n  std::string ToString() const {\n    nlohmann::json j;\n    j[\"pos_actions\"] = pos_actions;\n    j[\"neg_actions\"] = neg_actions;\n    j[\"sample_rate\"] = sample_rate;\n    return j.dump(2);\n  }\n};\n\nbool HasIntersection(const std::set<int32_t> &lhs,\n                     const std::set<int32_t> &rhs);\n\nbool ParseTaskConfig(const std::string &config,\n                     std::vector<TaskConfig> *task_configs);\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_ADD_LABEL_UTILS_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/label_utils_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n\n#include <memory>\n#include \"gtest/gtest.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\nnamespace {\nusing LabelConf = ::monolith::native_training::data::config::LabelConf;\n\nTEST(LabelUtils, HasIntersection) {\n  std::set<int32_t> lhs = {1, 2, 3}, rhs1 = {3, 4, 5}, rhs2 = {};\n  EXPECT_TRUE(HasIntersection(lhs, rhs1));\n  EXPECT_FALSE(HasIntersection(lhs, rhs2));\n}\n\nTEST(LabelUtils, ParseTaskConfigBasic) {\n  LabelConf label_conf;\n\n  auto *task_conf = label_conf.add_conf();\n  task_conf->add_pos_actions(-7);\n  task_conf->add_pos_actions(-9);\n  task_conf->add_neg_actions(-41);\n  task_conf->set_sample_rate(0.5f);\n\n  task_conf = label_conf.add_conf();\n  task_conf->add_pos_actions(75);\n  task_conf->add_pos_actions(-103);\n  task_conf->add_pos_actions(74);\n  task_conf->add_neg_actions(-41);\n  task_conf->set_sample_rate(1.0f);\n\n  task_conf = label_conf.add_conf();\n  task_conf->add_pos_actions(101);\n  task_conf->add_pos_actions(102);\n  task_conf->set_sample_rate(1.0f);\n\n  std::string config;\n  label_conf.SerializeToString(&config);\n  std::vector<TaskConfig> task_configs;\n  ParseTaskConfig(config, &task_configs);\n\n  EXPECT_EQ(task_configs.size(), 3);\n  std::set<int32_t> pos_actions0 = {-7, -9}, pos_actions1 = {-103, 74, 75},\n                    pos_actions2 = {101, 102};\n  std::set<int32_t> neg_actions0 = {-41}, neg_actions1 = {-41},\n                    neg_actions2 = {};\n  EXPECT_EQ(task_configs[0].pos_actions, pos_actions0);\n  EXPECT_EQ(task_configs[1].pos_actions, pos_actions1);\n  EXPECT_EQ(task_configs[2].pos_actions, pos_actions2);\n\n  EXPECT_EQ(task_configs[0].neg_actions, neg_actions0);\n  EXPECT_EQ(task_configs[1].neg_actions, neg_actions1);\n  EXPECT_EQ(task_configs[2].neg_actions, neg_actions2);\n\n  EXPECT_EQ(task_configs[0].sample_rate, 0.5);\n  EXPECT_EQ(task_configs[1].sample_rate, 1.0);\n  EXPECT_EQ(task_configs[2].sample_rate, 1.0);\n}\n\n}  // namespace\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/parquet_column_buffer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef PARQUET_COLUMN_BUFFER_H_\n#define PARQUET_COLUMN_BUFFER_H_\n\n#include \"parquet/api/reader.h\"\n#include \"tensorflow/core/lib/core/errors.h\"\n#include \"tensorflow/core/profiler/lib/traceme.h\"\n\nnamespace tensorflow {\nnamespace data {\n\nclass ColumnBuffer {\n public:\n  explicit ColumnBuffer(std::shared_ptr<parquet::ColumnReader>& col_reader)\n      : col_reader_(col_reader),\n        buffer_limit_(0),\n        values_limit_(0),\n        values_p_(0),\n        levels_p_(0) {\n    max_definition_level_ = col_reader->descr()->max_definition_level();\n    max_repetition_level_ = col_reader->descr()->max_repetition_level();\n  }\n\n  virtual ~ColumnBuffer() = default;\n\n protected:\n  std::shared_ptr<parquet::ColumnReader> col_reader_;\n  std::unique_ptr<int16_t[]> def_levels_buffer_;\n  std::unique_ptr<int16_t[]> rep_levels_buffer_;\n  int64_t buffer_limit_;\n  int64_t values_limit_;\n  int64_t values_p_;\n  int64_t levels_p_;\n  const int64_t BUFFER_SIZE = 256;\n  int16_t max_definition_level_;\n  int16_t max_repetition_level_;\n};\n\ntemplate <typename DType>\nclass TypedColumnBuffer : public ColumnBuffer {\n public:\n  typedef typename DType::c_type T;\n\n  explicit TypedColumnBuffer(std::shared_ptr<parquet::ColumnReader>& col_reader)\n      : ColumnBuffer(col_reader) {\n    typed_col_reader_ =\n        static_cast<parquet::TypedColumnReader<DType>*>(col_reader.get());\n    value_buffer_.reset(new T[BUFFER_SIZE]);\n    def_levels_buffer_.reset(new int16_t[BUFFER_SIZE]);\n    rep_levels_buffer_.reset(new int16_t[BUFFER_SIZE]);\n  }\n\n  Status GetNextValues(std::vector<T>& values) {\n    T value;\n    int16_t def_value, rep_level;\n    bool is_null;\n\n    if (max_repetition_level_ == 0 && max_definition_level_ == 0) {\n      TF_RETURN_IF_ERROR(\n          ReadNextValue(&value, &def_value, &rep_level, &is_null));\n      values.push_back(value);\n    } else if (max_repetition_level_ == 0 && max_definition_level_ > 0) {\n      TF_RETURN_IF_ERROR(\n          ReadNextValue(&value, &def_value, &rep_level, &is_null));\n      if (!is_null) {\n        values.push_back(value);\n      }\n    } else {\n      do {\n        ReadNextValue(&value, &def_value, &rep_level, &is_null);\n        // debug use\n        // LOG(INFO) << \"In GetNextValues \" << def_value << \" \" << rep_level <<\n        // \" \" << is_null;\n        if (is_null) {\n          break;\n        }\n        values.push_back(value);\n      } while (HasNextRepeatedValue());\n    }\n    return Status::OK();\n  }\n\n  bool HasNextRepeatedValue() {\n    if (levels_p_ >= buffer_limit_) {\n      Status status = ReadBuffer();\n      if (!status.ok()) {\n        return false;\n      }\n    }\n    return rep_levels_buffer_[levels_p_] ? true : false;\n  }\n\n  Status ReadNextValue(T* out, int16_t* def_level, int16_t* rep_level,\n                       bool* is_null) {\n    if (max_repetition_level_ == 0 && max_definition_level_ == 0) {\n      // required column\n      while (values_p_ >= values_limit_) {\n        TF_RETURN_IF_ERROR(ReadBuffer());\n      }\n      *is_null = false;\n      *out = value_buffer_[values_p_++];\n    } else if (max_repetition_level_ == 0 && max_definition_level_ > 0) {\n      // optional column\n      while (levels_p_ >= buffer_limit_) {\n        TF_RETURN_IF_ERROR(ReadBuffer());\n      }\n      *def_level = def_levels_buffer_[levels_p_];\n      if (*def_level == 0) {\n        *is_null = true;\n      } else {\n        *is_null = false;\n        if (values_p_ >= values_limit_) {\n          return errors::InvalidArgument(\"No extra values in buffer.\");\n        }\n        *out = value_buffer_[values_p_++];\n      }\n      levels_p_++;\n    } else {\n      // repeated column\n      while (levels_p_ >= buffer_limit_) {\n        TF_RETURN_IF_ERROR(ReadBuffer());\n      }\n      *def_level = def_levels_buffer_[levels_p_];\n      *rep_level = rep_levels_buffer_[levels_p_];\n      if ((*def_level != max_definition_level_) ||\n          (*def_level == 0 && *rep_level == 0)) {\n        *is_null = true;\n      } else {\n        *is_null = false;\n        if (values_p_ >= values_limit_) {\n          return errors::InvalidArgument(\"No extra values in buffer.\");\n        }\n        *out = value_buffer_[values_p_++];\n      }\n      levels_p_++;\n    }\n    return Status::OK();\n  }\n\n  Status ReadBuffer() {\n    profiler::TraceMe activity([]() { return \"ParquetDatasetOp::ReadBuffer\"; });\n    if (!typed_col_reader_->HasNext()) {\n      return errors::OutOfRange(\"Column values all consumed, out of range\");\n    }\n    int64_t values_read;\n    int64_t levels_read = typed_col_reader_->ReadBatch(\n        BUFFER_SIZE, def_levels_buffer_.get(), rep_levels_buffer_.get(),\n        value_buffer_.get(), &values_read);\n    buffer_limit_ = levels_read;\n    values_limit_ = values_read;\n    values_p_ = 0;\n    levels_p_ = 0;\n    return Status::OK();\n  }\n\n private:\n  parquet::TypedColumnReader<DType>* typed_col_reader_;\n  std::unique_ptr<T[]> value_buffer_;\n};\n\n}  // namespace data\n}  // namespace tensorflow\n\n#endif  // PARQUET_COLUMN_BUFFER_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/parquet_example_reader.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef PARQUET_EXAMPLE_READER_H_\n#define PARQUET_EXAMPLE_READER_H_\n\n#include <regex>\n#include <string>\n#include \"absl/strings/ascii.h\"\n#include \"absl/strings/str_split.h\"\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"monolith/native_training/data/kernels/internal/arrow_random_access_file.h\"\n#include \"monolith/native_training/data/kernels/internal/parquet_column_buffer.h\"\n#include \"monolith/native_training/data/kernels/internal/sized_random_access_file.h\"\n#include \"parquet/api/reader.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/platform/stacktrace.h\"\n#include \"tensorflow/core/profiler/lib/traceme.h\"\n\nnamespace tensorflow {\nnamespace data {\n\nusing idl::matrix::proto::LineId;\nusing monolith::io::proto::BytesList;\nusing monolith::io::proto::DoubleList;\nusing monolith::io::proto::Example;\nusing monolith::io::proto::ExampleBatch;\nusing monolith::io::proto::Feature;\nusing monolith::io::proto::FidList;\nusing monolith::io::proto::FloatList;\nusing monolith::io::proto::Int64List;\nusing monolith::io::proto::NamedFeature;\nusing monolith::io::proto::NamedFeatureList;\n\nenum ParsedDataType { INT = 1, FIDV1 = 2, FIDV2 = 3, FLOAT = 4, BYTES = 5 };\n\nclass ParquetExampleReader {\n public:\n  explicit ParquetExampleReader(Env* env) : env_(env) {}\n\n  virtual ~ParquetExampleReader() {}\n\n  Status Init(std::string file_name,\n              const std::vector<std::string>& selected_col_names,\n              const std::vector<std::string>& selected_col_types) {\n    // open paruqet file, and hold handler\n    file_.reset(new SizedRandomAccessFile(env_, file_name, nullptr, 0));\n    TF_RETURN_IF_ERROR(file_->GetFileSize(&file_size_));\n    parquet_file_.reset(new ArrowRandomAccessFile(file_.get(), file_size_));\n    parquet_reader_ =\n        parquet::ParquetFileReader::Open(std::move(parquet_file_));\n    parquet_metadata_ = parquet_reader_->metadata();\n\n    // register columns names\n    columns_.clear();\n    for (int i = 0; i < parquet_metadata_->num_columns(); i++) {\n      const std::string& full_col_name =\n          parquet_metadata_->schema()->Column(i)->path().get()->ToDotString();\n      std::vector<absl::string_view> split_result =\n          absl::StrSplit(full_col_name, \".\");\n      if (split_result.empty()) {\n        LOG(WARNING) << \"Split column full name \" << full_col_name\n                     << \", get empty result, will skip this column.\";\n        continue;\n      }\n      std::string col_name = {split_result[0].data(), split_result[0].size()};\n      columns_.push_back(col_name);\n      columns_index_map_[col_name] = i;\n      col_pure_name_map_[i] = col_name;\n    }\n    // LOG(INFO) << \"parquet file schema: \";\n    // for (int i = 0; i < parquet_metadata_->num_columns(); i++) {\n    //   parquet::Type::type col_type =\n    //       parquet_metadata_->schema()->Column(i)->physical_type();\n    //   LOG(INFO) << \"column \" << i << \" : \" << columns_[i] << \" : \"\n    //             << ColTypeToString(col_type);\n    // }\n    // LOG(INFO) << \"end of schema\";\n    LOG(INFO) << \"parquet file columns: \" << parquet_metadata_->num_columns();\n    LOG(INFO) << \"parquet file rows: \" << parquet_metadata_->num_rows();\n\n    // select column, and check col type\n    selected_col_ids_.clear();\n    selected_col_feature_type_.clear();\n    TF_RETURN_IF_ERROR(SetSelectedCols(selected_col_names, selected_col_types));\n\n    // init global iter_ and row_group related variables\n    iter_ = 0;\n    row_group_offset_ = -1;\n    row_group_id_ = -1;\n    row_group_reader_.reset();\n    TF_RETURN_IF_ERROR(NextRowGroup());\n\n    // init line_id descriptor\n    descriptor_ = ::idl::matrix::proto::LineId::GetDescriptor();\n    reflection_ = ::idl::matrix::proto::LineId::GetReflection();\n    for (size_t i = 0; i < selected_col_ids_.size(); i++) {\n      int64_t col_id = selected_col_ids_[i];\n      std::string col_name = col_pure_name_map_[col_id];\n      const google::protobuf::FieldDescriptor* field_descriptor =\n          GetLineIdFieldByName(col_name);\n      line_id_discriptor_map_[col_id] = field_descriptor;\n    }\n\n    LOG(INFO) << \"Init of ParquetReader Success. file_name = \" << file_name;\n    return Status::OK();\n  }\n\n  static const char* ColTypeToString(parquet::Type::type type) {\n    switch (type) {\n      case parquet::Type::BOOLEAN:\n        return \"BOOLEAN\";\n      case parquet::Type::INT32:\n        return \"INT32\";\n      case parquet::Type::INT64:\n        return \"INT64\";\n      case parquet::Type::FLOAT:\n        return \"FLOAT\";\n      case parquet::Type::DOUBLE:\n        return \"DOUBLE\";\n      case parquet::Type::BYTE_ARRAY:\n        return \"BYTE_ARRAY\";\n      case parquet::Type::FIXED_LEN_BYTE_ARRAY:\n        return \"FIXED_LEN_BYTE_ARRAY\";\n      default:\n        return \"UNKNOWN\";\n    }\n  }\n\n  Status SetSelectedCols(const std::vector<std::string>& selected_col_names,\n                         const std::vector<std::string>& selected_col_types) {\n    // check size equal\n    if (selected_col_names.size() != selected_col_types.size()) {\n      return errors::InvalidArgument(\n          \"list selected_col_names should have the same size as list \"\n          \"selected_col_types\");\n    }\n\n    // check column names valid, and not duplicated\n    std::unordered_set<uint64_t> selected_col_id_set;\n    for (const std::string& col_name : selected_col_names) {\n      auto it = columns_index_map_.find(col_name);\n      if (it == columns_index_map_.end()) {\n        return errors::InvalidArgument(\"column name: \", col_name,\n                                       \" not in paruquet schema\");\n      }\n      selected_col_ids_.push_back(it->second);\n      selected_col_id_set.insert(it->second);\n    }\n    if (selected_col_ids_.size() != selected_col_id_set.size()) {\n      return errors::InvalidArgument(\n          \"selected_col_names have duplicate columns\");\n    }\n\n    // check seleced_col_types if vaild, and fill enum values in\n    // selected_col_feature_type_\n    for (uint64_t i = 0; i < selected_col_ids_.size(); i++) {\n      uint64_t col_id = selected_col_ids_[i];\n      const std::string& feature_type = selected_col_types[i];\n      const std::string& col_name = selected_col_names[i];\n      parquet::Type::type col_type =\n          parquet_metadata_->schema()->Column(col_id)->physical_type();\n      switch (col_type) {\n        case parquet::Type::INT32:\n          if (feature_type == \"int\") {\n            selected_col_feature_type_.push_back(ParsedDataType::INT);\n          } else {\n            return errors::InvalidArgument(\n                \"invalid selected_col_types, col_name = \", col_name,\n                \", feature type should be int\");\n          }\n          break;\n        case parquet::Type::INT64:\n          if (feature_type == \"int\") {\n            selected_col_feature_type_.push_back(ParsedDataType::INT);\n          } else if (feature_type == \"fid_v1\") {\n            selected_col_feature_type_.push_back(ParsedDataType::FIDV1);\n          } else if (feature_type == \"fid_v2\") {\n            selected_col_feature_type_.push_back(ParsedDataType::FIDV2);\n          } else {\n            return errors::InvalidArgument(\n                \"invalid selected_col_types, col_name = \", col_name,\n                \", feature type should in [int, fid_v1, fid_v2]\");\n          }\n          break;\n        case parquet::Type::FLOAT:\n        case parquet::Type::DOUBLE:\n          if (feature_type == \"float\") {\n            selected_col_feature_type_.push_back(ParsedDataType::FLOAT);\n          } else {\n            return errors::InvalidArgument(\n                \"invalid selected_col_types, col_name = \", col_name,\n                \", feature type should be float\");\n          }\n          break;\n        case parquet::Type::BYTE_ARRAY:\n          if (feature_type == \"bytes\") {\n            selected_col_feature_type_.push_back(ParsedDataType::BYTES);\n          } else {\n            return errors::InvalidArgument(\n                \"invalid selected_col_types, col_name = \", col_name,\n                \", feature type should be bytes\");\n          }\n          break;\n        default:\n          return errors::InvalidArgument(\n              \"invalid column parquet type col_name = \", col_name,\n              \"parquet type is\", ColTypeToString(col_type));\n      }\n    }\n    return Status::OK();\n  }\n\n  const google::protobuf::FieldDescriptor* GetLineIdFieldByName(\n      std::string name) {\n    static std::regex reg(\"__[A-Z_]+__\");\n    bool is_match = std::regex_match(name, reg);\n    if (!is_match) {\n      return nullptr;\n    }\n    std::string subname = name.substr(2, name.length() - 4);\n    std::string lower_subname = absl::AsciiStrToLower(subname);\n    return descriptor_->FindFieldByName(lower_subname);\n  }\n\n  Status GetNextExample(Example& example) {\n    if (IsEOF()) {\n      return errors::OutOfRange(\"GetNextExample out of range, iter = \", iter_);\n    }\n    while (iter_ >=\n           row_group_offset_ + row_group_reader_->metadata()->num_rows()) {\n      TF_RETURN_IF_ERROR(NextRowGroup());\n    }\n\n    for (size_t i = 0; i < selected_col_ids_.size(); i++) {\n      int64_t col_id = selected_col_ids_[i];\n      parquet::Type::type col_type =\n          parquet_metadata_->schema()->Column(col_id)->physical_type();\n      std::string col_name = col_pure_name_map_[col_id];\n\n      // if column is __LABEL__\n      if (col_name == \"__LABEL__\") {\n        if (col_type == parquet::Type::INT32) {\n          TF_RETURN_IF_ERROR(FillLabel<parquet::Int32Type>(i, example));\n        } else if (col_type == parquet::Type::INT64) {\n          TF_RETURN_IF_ERROR(FillLabel<parquet::Int64Type>(i, example));\n        } else if (col_type == parquet::Type::FLOAT) {\n          TF_RETURN_IF_ERROR(FillLabel<parquet::FloatType>(i, example));\n        } else if (col_type == parquet::Type::DOUBLE) {\n          TF_RETURN_IF_ERROR(FillLabel<parquet::DoubleType>(i, example));\n        } else {\n          LOG(FATAL)\n              << \"In parquet: __LABEL__ column has wrong type, pls check\";\n        }\n        continue;\n      }\n\n      // if column in line_id\n      auto it = line_id_discriptor_map_.find(col_id);\n      const google::protobuf::FieldDescriptor* line_field =\n          (it != line_id_discriptor_map_.end()) ? it->second : nullptr;\n      if (line_field != nullptr) {\n        if (line_field->is_repeated()) {\n          // TODO(libo.bob): will support it later\n          LOG(FATAL) << \"Not support repeated line_id field now.\";\n        }\n        switch (line_field->cpp_type()) {\n          case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT32: {\n            CHECK(col_type == parquet::Type::INT32)\n                << \"column: \" << col_name\n                << \" should have the same type as line_id field\";\n            reflection_->SetInt32(example.mutable_line_id(), line_field,\n                                  GetSingleValue<parquet::Int32Type>(i));\n            break;\n          }\n          case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT64: {\n            CHECK(col_type == parquet::Type::INT64)\n                << \"column: \" << col_name\n                << \" should have the same type as line_id field\";\n            reflection_->SetInt64(example.mutable_line_id(), line_field,\n                                  GetSingleValue<parquet::Int64Type>(i));\n            break;\n          }\n          case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT32: {\n            CHECK(col_type == parquet::Type::INT32)\n                << \"column: \" << col_name\n                << \" should have the same type as line_id field\";\n            reflection_->SetUInt32(example.mutable_line_id(), line_field,\n                                   GetSingleValue<parquet::Int32Type>(i));\n            break;\n          }\n          case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT64: {\n            CHECK(col_type == parquet::Type::INT64)\n                << \"column: \" << col_name\n                << \" should have the same type as line_id field\";\n            reflection_->SetUInt64(example.mutable_line_id(), line_field,\n                                   GetSingleValue<parquet::Int64Type>(i));\n            break;\n          }\n          case google::protobuf::FieldDescriptor::CppType::CPPTYPE_FLOAT: {\n            CHECK(col_type == parquet::Type::FLOAT)\n                << \"column: \" << col_name\n                << \" should have the same type as line_id field\";\n            reflection_->SetFloat(example.mutable_line_id(), line_field,\n                                  GetSingleValue<parquet::FloatType>(i));\n            break;\n          }\n          case google::protobuf::FieldDescriptor::CppType::CPPTYPE_DOUBLE: {\n            CHECK(col_type == parquet::Type::DOUBLE)\n                << \"column: \" << col_name\n                << \" should have the same type as line_id field\";\n            reflection_->SetDouble(example.mutable_line_id(), line_field,\n                                   GetSingleValue<parquet::DoubleType>(i));\n            break;\n          }\n          case google::protobuf::FieldDescriptor::CppType::CPPTYPE_STRING: {\n            CHECK(col_type == parquet::Type::BYTE_ARRAY)\n                << \"column: \" << col_name\n                << \" should have the same type as line_id field\";\n            std::string value =\n                ByteArrayToString(GetSingleValue<parquet::ByteArrayType>(i));\n            reflection_->SetString(example.mutable_line_id(), line_field,\n                                   value);\n            break;\n          }\n          default:\n            LOG(FATAL) << \"not support line_id type for column \" << col_name;\n        }\n        continue;\n      }\n\n      NamedFeature* named_feature = example.add_named_feature();\n      named_feature->set_id(col_id + 10000);\n      named_feature->set_name(col_name);\n      Feature* feature = named_feature->mutable_feature();\n      switch (col_type) {\n        case parquet::Type::INT32: {\n          TF_RETURN_IF_ERROR(FillValueList<parquet::Int32Type, Int64List>(\n              i, feature->mutable_int64_list()));\n          break;\n        }\n        case parquet::Type::INT64: {\n          if (selected_col_feature_type_[i] == ParsedDataType::INT) {\n            TF_RETURN_IF_ERROR(FillValueList<parquet::Int64Type, Int64List>(\n                i, feature->mutable_int64_list()));\n          } else if (selected_col_feature_type_[i] == ParsedDataType::FIDV1) {\n            TF_RETURN_IF_ERROR(FillValueList<parquet::Int64Type, FidList>(\n                i, feature->mutable_fid_v1_list()));\n          } else {\n            TF_RETURN_IF_ERROR(FillValueList<parquet::Int64Type, FidList>(\n                i, feature->mutable_fid_v2_list()));\n          }\n          break;\n        }\n        case parquet::Type::FLOAT: {\n          TF_RETURN_IF_ERROR(FillValueList<parquet::FloatType, FloatList>(\n              i, feature->mutable_float_list()));\n          break;\n        }\n        case parquet::Type::DOUBLE: {\n          TF_RETURN_IF_ERROR(FillValueList<parquet::DoubleType, FloatList>(\n              i, feature->mutable_float_list()));\n          break;\n        }\n        case parquet::Type::BYTE_ARRAY: {\n          TypedColumnBuffer<parquet::ByteArrayType>* typed_col_buf =\n              dynamic_cast<TypedColumnBuffer<parquet::ByteArrayType>*>(\n                  col_buffers_[i].get());\n          std::vector<parquet::ByteArray> values;\n          TF_RETURN_IF_ERROR(typed_col_buf->GetNextValues(values));\n          BytesList* bytes_list = feature->mutable_bytes_list();\n          for (const parquet::ByteArray& value : values) {\n            bytes_list->add_value(ByteArrayToString(value));\n          }\n          break;\n        }\n        default:\n          return errors::InvalidArgument(\"not support column type\");\n      }\n    }\n    iter_++;\n    return Status::OK();\n  }\n\n  Status GetNextExampleBatch(ExampleBatch& example_batch, int64_t batch_size) {\n    if (IsEOF()) {\n      return errors::OutOfRange(\"GetNextExampleBatch out of range, iter = \",\n                                iter_);\n    }\n    // cread namedfeaturelist(s)\n    {\n      profiler::TraceMe activity(\n          []() { return \"ParquetDataset::CreateNamedFeatureLists\"; });\n      for (size_t i = 0; i < selected_col_ids_.size(); i++) {\n        int64_t col_id = selected_col_ids_[i];\n        const std::string& col_name = col_pure_name_map_[col_id];\n        NamedFeatureList* named_feature_list =\n            example_batch.add_named_feature_list();\n        named_feature_list->set_id(col_id);\n        named_feature_list->set_name(col_name);\n      }\n    }\n\n    // calculate batch_size\n    int64_t rows_to_read_left =\n        iter_ + batch_size >= parquet_metadata_->num_rows()\n            ? parquet_metadata_->num_rows() - iter_\n            : batch_size;\n    example_batch.set_batch_size(rows_to_read_left);\n\n    // read features for each column\n    while (rows_to_read_left > 0) {\n      // if need to go to next row group\n      while (iter_ >=\n             row_group_offset_ + row_group_reader_->metadata()->num_rows()) {\n        TF_RETURN_IF_ERROR(NextRowGroup());\n      }\n      // calculate max rows can read in current row group\n      int64_t rows_in_row_group =\n          iter_ + rows_to_read_left >=\n                  row_group_offset_ + row_group_reader_->metadata()->num_rows()\n              ? row_group_offset_ + row_group_reader_->metadata()->num_rows() -\n                    iter_\n              : rows_to_read_left;\n      rows_to_read_left -= rows_in_row_group;\n      // read from current row group\n      for (size_t i = 0; i < selected_col_ids_.size(); i++) {\n        profiler::TraceMe activity(\n            []() { return \"ParquetDataset::ReadOneColumnWithBatchSize\"; });\n        int64_t col_id = selected_col_ids_[i];\n        parquet::Type::type col_type =\n            parquet_metadata_->schema()->Column(col_id)->physical_type();\n        NamedFeatureList* named_feature_list =\n            example_batch.mutable_named_feature_list(i);\n        for (int64_t ft = 0; ft < rows_in_row_group; ft++) {\n          Feature* feature = named_feature_list->add_feature();\n          switch (col_type) {\n            case parquet::Type::INT32: {\n              TF_RETURN_IF_ERROR(FillValueList<parquet::Int32Type, Int64List>(\n                  i, feature->mutable_int64_list()));\n              break;\n            }\n            case parquet::Type::INT64: {\n              if (selected_col_feature_type_[i] == ParsedDataType::INT) {\n                TF_RETURN_IF_ERROR(FillValueList<parquet::Int64Type, Int64List>(\n                    i, feature->mutable_int64_list()));\n              } else if (selected_col_feature_type_[i] ==\n                         ParsedDataType::FIDV1) {\n                TF_RETURN_IF_ERROR(FillValueList<parquet::Int64Type, FidList>(\n                    i, feature->mutable_fid_v1_list()));\n              } else {\n                TF_RETURN_IF_ERROR(FillValueList<parquet::Int64Type, FidList>(\n                    i, feature->mutable_fid_v2_list()));\n              }\n              break;\n            }\n            case parquet::Type::FLOAT: {\n              TF_RETURN_IF_ERROR(FillValueList<parquet::FloatType, FloatList>(\n                  i, feature->mutable_float_list()));\n              break;\n            }\n            case parquet::Type::DOUBLE: {\n              TF_RETURN_IF_ERROR(FillValueList<parquet::DoubleType, FloatList>(\n                  i, feature->mutable_float_list()));\n              break;\n            }\n            case parquet::Type::BYTE_ARRAY: {\n              TypedColumnBuffer<parquet::ByteArrayType>* typed_col_buf =\n                  dynamic_cast<TypedColumnBuffer<parquet::ByteArrayType>*>(\n                      col_buffers_[i].get());\n              std::vector<parquet::ByteArrayType::c_type> values;\n              TF_RETURN_IF_ERROR(typed_col_buf->GetNextValues(values));\n              BytesList* bytes_list = feature->mutable_bytes_list();\n              for (const parquet::ByteArrayType::c_type& value : values) {\n                bytes_list->add_value(ByteArrayToString(value));\n              }\n              break;\n            }\n            default:\n              return errors::InvalidArgument(\"not support column type\");\n          }\n        }\n      }\n      iter_ += rows_in_row_group;\n    }\n\n    return Status::OK();\n  }\n\n  template <typename PARQUET_TYPE, typename PB_TLIST>\n  Status FillValueList(int64_t col_buffer_id, PB_TLIST* value_list) {\n    TypedColumnBuffer<PARQUET_TYPE>* typed_col_buf =\n        dynamic_cast<TypedColumnBuffer<PARQUET_TYPE>*>(\n            col_buffers_[col_buffer_id].get());\n    std::vector<typename PARQUET_TYPE::c_type> values;\n    Status status = typed_col_buf->GetNextValues(values);\n    if (!status.ok()) {\n      std::string stack_trace = CurrentStackTrace();\n      LOG(INFO) << stack_trace;\n      return status;\n    }\n    for (const typename PARQUET_TYPE::c_type& value : values) {\n      value_list->add_value(value);\n    }\n    return Status::OK();\n  }\n\n  template <typename PARQUET_TYPE>\n  Status FillLabel(int64_t col_buffer_id, Example& example) {\n    TypedColumnBuffer<PARQUET_TYPE>* typed_col_buf =\n        dynamic_cast<TypedColumnBuffer<PARQUET_TYPE>*>(\n            col_buffers_[col_buffer_id].get());\n    std::vector<typename PARQUET_TYPE::c_type> values;\n    Status status = typed_col_buf->GetNextValues(values);\n    if (!status.ok()) {\n      std::string stack_trace = CurrentStackTrace();\n      LOG(INFO) << stack_trace;\n      return status;\n    }\n    for (const typename PARQUET_TYPE::c_type& value : values) {\n      example.mutable_label()->Add(static_cast<float>(value));\n    }\n    return Status::OK();\n  }\n\n  template <typename PARQUET_TYPE>\n  typename PARQUET_TYPE::c_type GetSingleValue(int64_t col_buffer_id) {\n    TypedColumnBuffer<PARQUET_TYPE>* typed_col_buf =\n        dynamic_cast<TypedColumnBuffer<PARQUET_TYPE>*>(\n            col_buffers_[col_buffer_id].get());\n    std::vector<typename PARQUET_TYPE::c_type> values;\n    Status status = typed_col_buf->GetNextValues(values);\n    if (!status.ok()) {\n      std::string stack_trace = CurrentStackTrace();\n      LOG(FATAL) << stack_trace;\n    }\n    if (values.size() != 1) {\n      LOG(FATAL) << \"Parquet column id = \" << col_buffer_id\n                 << \", should have single value for one row, but got \"\n                 << values.size();\n    }\n    return values[0];\n  }\n\n  Status NextRowGroup() {\n    profiler::TraceMe activity([]() { return \"ParquetDataset::NextRowGroup\"; });\n    if (row_group_id_ + 1 >= parquet_metadata_->num_row_groups()) {\n      return errors::OutOfRange(\"row group out of range\");\n    }\n    if (!row_group_reader_) {\n      // first initialize\n      row_group_reader_ = parquet_reader_->RowGroup(0);\n      row_group_id_ = 0;\n      row_group_offset_ = 0;\n    } else {\n      row_group_offset_ =\n          row_group_offset_ + row_group_reader_->metadata()->num_rows();\n      row_group_id_++;\n      row_group_reader_ = parquet_reader_->RowGroup(row_group_id_);\n    }\n    // update col_buffers\n    col_buffers_.clear();\n    for (uint64_t col_id : selected_col_ids_) {\n      std::shared_ptr<parquet::ColumnReader> column_reader =\n          row_group_reader_->Column(col_id);\n      switch (parquet_metadata_->schema()->Column(col_id)->physical_type()) {\n        case parquet::Type::INT32:\n          col_buffers_.emplace_back(\n              new TypedColumnBuffer<parquet::Int32Type>(column_reader));\n          break;\n        case parquet::Type::INT64:\n          col_buffers_.emplace_back(\n              new TypedColumnBuffer<parquet::Int64Type>(column_reader));\n          break;\n        case parquet::Type::FLOAT:\n          col_buffers_.emplace_back(\n              new TypedColumnBuffer<parquet::FloatType>(column_reader));\n          break;\n        case parquet::Type::DOUBLE:\n          col_buffers_.emplace_back(\n              new TypedColumnBuffer<parquet::DoubleType>(column_reader));\n          break;\n        case parquet::Type::BYTE_ARRAY:\n          col_buffers_.emplace_back(\n              new TypedColumnBuffer<parquet::ByteArrayType>(column_reader));\n          break;\n        default:\n          return errors::InvalidArgument(\"not support column type\");\n      }\n    }\n    return Status::OK();\n  }\n\n  bool IsEOF() {\n    // LOG(INFO) << \"iter_ = \" << iter_;\n    return iter_ >= parquet_metadata_->num_rows();\n  }\n\n private:\n  Env* env_;\n  std::unique_ptr<SizedRandomAccessFile> file_;\n  uint64 file_size_;\n  std::unique_ptr<ArrowRandomAccessFile> parquet_file_;\n\n  std::string file_name_;\n  std::shared_ptr<::parquet::ParquetFileReader> parquet_reader_;\n\n  std::shared_ptr<::parquet::FileMetaData> parquet_metadata_;\n  std::vector<std::string> columns_;\n  std::unordered_map<std::string, int64> columns_index_map_;\n  std::unordered_map<int64_t, std::string> col_pure_name_map_;\n\n  std::vector<uint64_t> selected_col_ids_;\n  std::vector<ParsedDataType> selected_col_feature_type_;\n\n  // iter_ and row_group variables\n  int64_t iter_;\n  std::shared_ptr<parquet::RowGroupReader> row_group_reader_;\n  int64_t row_group_id_;\n  int64_t row_group_offset_;\n\n  std::vector<std::shared_ptr<ColumnBuffer>> col_buffers_;\n\n  // line_id related\n  const google::protobuf::Descriptor* descriptor_;\n  const google::protobuf::Reflection* reflection_;\n  std::unordered_map<int64_t, const google::protobuf::FieldDescriptor*>\n      line_id_discriptor_map_;\n};\n\n}  // namespace data\n}  // namespace tensorflow\n\n#endif  // PARQUET_EXAMPLE_READER_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/relational_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_RELATIONAL_UTILS_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_RELATIONAL_UTILS_H_\n\n#include <algorithm>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include \"glog/logging.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\n\nstatic const std::string GT = \"gt\";\nstatic const std::string GE = \"ge\";\nstatic const std::string EQ = \"eq\";\nstatic const std::string LT = \"lt\";\nstatic const std::string LE = \"le\";\nstatic const std::string NEQ = \"neq\";\nstatic const std::string BETWEEN = \"between\";\nstatic const std::string IN = \"in\";\nstatic const std::string NOT_IN = \"not-in\";\n\nstatic const std::unordered_set<std::string> VALID_OPS = {\n    GT, GE, EQ, LT, LE, NEQ, BETWEEN, IN, NOT_IN};\n\nstatic const std::unordered_set<std::string> COMPARE_OPS = {GT, GE,  EQ,     LT,\n                                                            LE, NEQ, BETWEEN};\n\ntemplate <typename T1, typename T2 = T1>\nbool compare(const std::string& op, const T1& value,\n             const std::vector<T2>& operands) {\n  if (op == GT) {\n    return value > operands[0];\n  } else if (op == GE) {\n    return value >= operands[0];\n  } else if (op == EQ) {\n    return value == operands[0];\n  } else if (op == LT) {\n    return value < operands[0];\n  } else if (op == LE) {\n    return value <= operands[0];\n  } else if (op == NEQ) {\n    return value != operands[0];\n  } else if (op == BETWEEN) {\n    return value >= operands[0] && value < operands[1];\n  } else {\n    LOG(FATAL) << \"Invalid op: \" << op;\n    return false;\n  }\n}\n\ntemplate <typename T1, typename T2 = T1>\nbool contains(const std::string& op, const T1& value,\n              const std::unordered_set<T2>& operand_set) {\n  if (op == IN) {\n    return operand_set.count(value);\n  } else if (op == NOT_IN) {\n    return !operand_set.count(value);\n  } else {\n    LOG(FATAL) << \"Invalid op: \" << op;\n    return false;\n  }\n}\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_RELATIONAL_UTILS_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/relational_utils_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/relational_utils.h\"\n\n#include <cstring>\n#include <memory>\n#include <thread>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\nnamespace {\n\nTEST(RelationalUtils, GT) {\n  EXPECT_TRUE(compare(GT, 1, {0}));\n  EXPECT_TRUE(!compare(GT, 1, {1}));\n  EXPECT_TRUE(!compare(GT, -1, {0LL}));\n  EXPECT_TRUE(compare(GT, std::string(\"1\"), {\"0\"}));\n  EXPECT_TRUE(!compare(GT, std::string(\"1\"), {\"1\"}));\n}\n\nTEST(RelationalUtils, GE) {\n  EXPECT_TRUE(compare(GE, 1, {0}));\n  EXPECT_TRUE(compare(GE, 1, {1}));\n  EXPECT_TRUE(!compare(GE, -1, {0LL}));\n  EXPECT_TRUE(compare(GE, std::string(\"1\"), {\"0\"}));\n  EXPECT_TRUE(compare(GE, std::string(\"1\"), {\"1\"}));\n}\n\nTEST(RelationalUtils, EQ) {\n  EXPECT_TRUE(!compare(EQ, 1, {0}));\n  EXPECT_TRUE(compare(EQ, 1, {1}));\n  EXPECT_TRUE(!compare(EQ, -1, {0LL}));\n  EXPECT_TRUE(!compare(EQ, std::string(\"1\"), {\"0\"}));\n  EXPECT_TRUE(compare(EQ, std::string(\"1\"), {\"1\"}));\n}\n\nTEST(RelationalUtils, LT) {\n  EXPECT_TRUE(!compare(LT, 1, {0}));\n  EXPECT_TRUE(!compare(LT, 1, {1}));\n  EXPECT_TRUE(compare(LT, -1, {0LL}));\n  EXPECT_TRUE(!compare(LT, std::string(\"1\"), {\"0\"}));\n  EXPECT_TRUE(!compare(LT, std::string(\"1\"), {\"1\"}));\n}\n\nTEST(RelationalUtils, LE) {\n  EXPECT_TRUE(!compare(LE, 1, {0}));\n  EXPECT_TRUE(compare(LE, 1, {1}));\n  EXPECT_TRUE(compare(LE, -1, {0LL}));\n  EXPECT_TRUE(!compare(LE, std::string(\"1\"), {\"0\"}));\n  EXPECT_TRUE(compare(LE, std::string(\"1\"), {\"1\"}));\n}\n\nTEST(RelationalUtils, NEQ) {\n  EXPECT_TRUE(compare(NEQ, 1, {0}));\n  EXPECT_TRUE(!compare(NEQ, 1, {1}));\n  EXPECT_TRUE(compare(NEQ, -1, {0LL}));\n  EXPECT_TRUE(compare(NEQ, std::string(\"1\"), {\"0\"}));\n  EXPECT_TRUE(!compare(NEQ, std::string(\"1\"), {\"1\"}));\n}\n\nTEST(RelationalUtils, BETWEEN) {\n  EXPECT_TRUE(!compare(BETWEEN, 1, {0, 1}));\n  EXPECT_TRUE(compare(BETWEEN, 1, {1, 2}));\n  EXPECT_TRUE(!compare(BETWEEN, -1, {0LL, 1LL}));\n  EXPECT_TRUE(!compare(BETWEEN, std::string(\"1\"), {\"0\", \"1\"}));\n  EXPECT_TRUE(compare(BETWEEN, std::string(\"1\"), {\"1\", \"2\"}));\n}\n\nTEST(RelationalUtils, IN) {\n  EXPECT_TRUE(!contains(IN, 1, {0}));\n  EXPECT_TRUE(contains(IN, 1, {1, 2}));\n  EXPECT_TRUE(!contains(IN, -1, {0LL, 1LL}));\n  EXPECT_TRUE(!contains(IN, std::string(\"1\"), {\"0\"}));\n  EXPECT_TRUE(contains(IN, std::string(\"1\"), {\"1\", \"2\"}));\n}\n\nTEST(RelationalUtils, NOT_IN) {\n  EXPECT_TRUE(contains(NOT_IN, 1, {0}));\n  EXPECT_TRUE(!contains(NOT_IN, 1, {1, 2}));\n  EXPECT_TRUE(contains(NOT_IN, -1, {0LL, 1LL}));\n  EXPECT_TRUE(contains(NOT_IN, std::string(\"1\"), {\"0\"}));\n  EXPECT_TRUE(!contains(NOT_IN, std::string(\"1\"), {\"1\", \"2\"}));\n}\n\n}  // namespace\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/sized_random_access_file.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef TENSORFLOW_IO_CORE_KERNELS_STREAM_H_\n#define TENSORFLOW_IO_CORE_KERNELS_STREAM_H_\n\n#include \"tensorflow/core/lib/io/inputstream_interface.h\"\n#include \"tensorflow/core/lib/io/random_inputstream.h\"\n\nnamespace tensorflow {\nnamespace data {\n\n// Note: This SizedRandomAccessFile should only lives within Compute()\n// of the kernel as buffer could be released by outside.\nclass SizedRandomAccessFile : public tensorflow::RandomAccessFile {\n public:\n  SizedRandomAccessFile(Env* env, const string& filename,\n                        const void* optional_memory_buff,\n                        const size_t optional_memory_size)\n      : file_(nullptr),\n        size_(optional_memory_size),\n        buff_((const char*)(optional_memory_buff)),\n        size_status_(Status::OK()) {\n    if (size_ == 0) {\n      size_status_ = env->GetFileSize(filename, &size_);\n      if (size_status_.ok()) {\n        size_status_ = env->NewRandomAccessFile(filename, &file_);\n      }\n    }\n  }\n\n  virtual ~SizedRandomAccessFile() {}\n  Status Read(uint64 offset, size_t n, StringPiece* result,\n              char* scratch) const override {\n    if (file_.get() != nullptr) {\n      return file_.get()->Read(offset, n, result, scratch);\n    }\n    size_t bytes_to_read = 0;\n    if (offset < size_) {\n      bytes_to_read = (offset + n < size_) ? n : (size_ - offset);\n    }\n    if (bytes_to_read > 0) {\n      memcpy(scratch, &buff_[offset], bytes_to_read);\n    }\n    *result = StringPiece(scratch, bytes_to_read);\n    if (bytes_to_read < n) {\n      return errors::OutOfRange(\"EOF reached\");\n    }\n    return Status::OK();\n  }\n  Status GetFileSize(uint64* size) {\n    if (size_status_.ok()) {\n      *size = size_;\n    }\n    return size_status_;\n  }\n\n private:\n  std::unique_ptr<tensorflow::RandomAccessFile> file_;\n  uint64 size_;\n  const char* buff_;\n  Status size_status_;\n};\n\n}  // namespace data\n}  // namespace tensorflow\n\n#endif  // TENSORFLOW_IO_CORE_KERNELS_STREAM_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/uniq_hashtable.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_UNIQ_HASHTABLE_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_UNIQ_HASHTABLE_H_\n\n#include <sys/types.h>\n#include <cstddef>\n#include <cstdint>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include \"tensorflow/core/platform/macros.h\"\n#include \"tensorflow/core/platform/raw_coding.h\"\n\n#include \"absl/base/macros.h\"\n#include \"glog/logging.h\"\n\n\n#define MONOLITH_INLINE __attribute__((always_inline))\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass UniqHashTable {\n  using FID = uint64_t;\n  static constexpr uint32_t MIN_BUCKET_CAP = 1u << 10;\n  static constexpr FID EMPTY_FID = static_cast<FID>(-1);\n  static constexpr uint32_t ILLEGAL_BUCKET =\n      std::numeric_limits<uint32_t>::max();\n  static constexpr float LOAD_FACTOR = 0.75;\n\n  struct HTItem {\n    HTItem() = default;\n    HTItem(const FID fid, uint32_t req_id, uint32_t uniq_idx) : fid(fid), req_id(req_id), uniq_idx(uniq_idx) {}\n    HTItem(const HTItem& other) = default;\n    HTItem& operator=(const HTItem& other) = default;\n\n    bool operator==(const HTItem& other) const { return fid == other.fid && req_id == other.req_id; }\n\n    bool IsEmpty(uint32_t cur_req_id) const { return req_id != cur_req_id || fid == EMPTY_FID;}\n\n    FID fid = EMPTY_FID;\n    uint32_t req_id = 0;\n    uint32_t uniq_idx = 0;\n  };\n\n  struct HTIdx {\n    HTIdx() = default;\n    HTIdx(uint32_t item_pos, uint32_t insert_pos) : item_pos(item_pos), insert_pos(insert_pos) {}\n\n    uint32_t item_pos = ILLEGAL_BUCKET;\n    uint32_t insert_pos = ILLEGAL_BUCKET;\n  };\n\n  using HTItemPtr = HTItem*;\n\n public:\n  UniqHashTable() : capacity_(MIN_BUCKET_CAP), num_elements_(0), cur_req_id_(1) {\n    prob_table_ = CreateProbTable(MIN_BUCKET_CAP);\n    bucket_idx_mask_ = MIN_BUCKET_CAP - 1;\n    expand_threshold_ = static_cast<uint32_t>(capacity_ * LOAD_FACTOR);\n  }\n\n  ~UniqHashTable() {\n    DeleteProbTable(prob_table_, capacity_);\n  }\n\n  uint32_t UniqFid(const FID fid, const uint32_t uniq_idx) {\n    DCHECK_NE(fid, EMPTY_FID);\n    auto idx = FindPosition(fid, prob_table_, bucket_idx_mask_, cur_req_id_);\n    if (idx.item_pos != ILLEGAL_BUCKET) {\n      DCHECK(idx.insert_pos == ILLEGAL_BUCKET);\n      return prob_table_[idx.item_pos].uniq_idx;\n    } else {\n      DCHECK(idx.insert_pos != ILLEGAL_BUCKET);\n      DCHECK_LE(idx.insert_pos, bucket_idx_mask_);\n      prob_table_[idx.insert_pos] = HTItem(fid, cur_req_id_, uniq_idx);\n      num_elements_++;\n    }\n    MaybeExpand();\n    return uniq_idx;\n  }\n\n  void Reset() {\n    num_elements_ = 0;\n    if (++cur_req_id_ == 0) {\n      FillTableWithEmptyItem(prob_table_, capacity_);\n    }\n    // std::cerr << \"abcd \" << cur_req_id_ << std::endl;\n  }\n\n  size_t Size() {\n    return static_cast<size_t>(num_elements_);\n  }\n\n  size_t Capacity() {\n    return static_cast<size_t>(capacity_);\n  }\n\n private:\n  static MONOLITH_INLINE HTIdx FindPosition(const FID fid, HTItemPtr prob_table, uint32_t bucket_idx_mask, uint32_t req_id) {\n    uint32_t bucket_idx = FidHash(fid) & bucket_idx_mask;\n    while (true) {\n      const auto& item = prob_table[bucket_idx];\n      if (item == HTItem(fid, req_id, 0)) {\n        return HTIdx(bucket_idx, ILLEGAL_BUCKET);\n      } else if (item.IsEmpty(req_id)) {\n        return HTIdx(ILLEGAL_BUCKET, bucket_idx);\n      }\n      bucket_idx = (bucket_idx + 1) & bucket_idx_mask;\n    }\n  }\n\n  static MONOLITH_INLINE bool TestEqual(const HTItem& item, const FID fid, uint32_t req_id) {\n    return item.fid == fid && item.req_id == req_id;\n  }\n\n  static MONOLITH_INLINE HTItemPtr CreateProbTable(uint32_t capacity) {\n    auto* prob_table = malloc(sizeof(HTItem) * capacity);\n    FillTableWithEmptyItem(reinterpret_cast<HTItemPtr>(prob_table), capacity);\n    return reinterpret_cast<HTItemPtr>(prob_table);\n  }\n\n  static MONOLITH_INLINE void FillTableWithEmptyItem(HTItemPtr prob_table, uint32_t capacity) {\n    DCHECK(!!prob_table);\n    static HTItem empty_item(EMPTY_FID, 0, 0);\n    std::uninitialized_fill_n(prob_table, capacity, empty_item);\n  }\n\n  void DeleteProbTable(HTItemPtr prob_table, uint32_t capacity) {\n    DCHECK(!!prob_table);\n    if (!std::is_trivial<HTItem>::value) {\n      for (uint32_t i = 0; i < capacity; ++i) {\n        prob_table[i].~HTItem();\n      }\n    }\n    free(prob_table);\n  }\n\n  void MaybeExpand() {\n    if (GOOGLE_PREDICT_TRUE(num_elements_ <= expand_threshold_)) {\n      return;\n    }\n    auto new_capacity = capacity_ << 1;\n    auto new_bucket_idx_mask = new_capacity - 1;\n    auto new_prob_table = CreateProbTable(new_capacity);\n    for (uint32_t i = 0; i < capacity_; ++i) {\n      if (prob_table_[i].IsEmpty(cur_req_id_)) {\n        continue;\n      }\n      uint32_t bucket_idx = FidHash(prob_table_[i].fid) & new_bucket_idx_mask;\n      while (!new_prob_table[bucket_idx].IsEmpty(cur_req_id_)) {\n        bucket_idx = (bucket_idx + 1) & new_bucket_idx_mask;\n      }\n      new_prob_table[bucket_idx] = prob_table_[i];\n    }\n    DeleteProbTable(prob_table_, capacity_);\n    prob_table_ = new_prob_table;\n    capacity_ = new_capacity;\n    expand_threshold_ = static_cast<uint32_t>(capacity_ * LOAD_FACTOR);\n    bucket_idx_mask_ = capacity_ - 1;\n  }\n\n  static MONOLITH_INLINE uint32_t FidHash(const FID fid) {\n    return Hash(reinterpret_cast<const char*>(&fid), sizeof(FID), 0);\n  }\n\n  // Copy from tensorflow/tsl/lib/io/cache.cc\n  // Question: 这里怎么引用比较规范？\n  static uint32_t Hash(const char* data, size_t n, uint32_t seed) {\n    // Similar to murmur hash\n    const uint32_t m = 0xc6a4a793;\n    const uint32_t r = 24;\n    const char* limit = data + n;\n    uint32_t h = seed ^ (n * m);\n\n    // Pick up four bytes at a time\n    while (data + 4 <= limit) {\n      uint32_t w = tensorflow::core::DecodeFixed32(data);\n      data += 4;\n      h += w;\n      h *= m;\n      h ^= (h >> 16);\n    }\n\n    // Pick up remaining bytes\n    switch (limit - data) {\n      case 3:\n        h += static_cast<uint8_t>(data[2]) << 16;\n        ABSL_FALLTHROUGH_INTENDED;\n      case 2:\n        h += static_cast<uint8_t>(data[1]) << 8;\n        ABSL_FALLTHROUGH_INTENDED;\n      case 1:\n        h += static_cast<uint8_t>(data[0]);\n        h *= m;\n        h ^= (h >> r);\n        break;\n    }\n    return h;\n  }\n\n\n  HTItemPtr prob_table_ = nullptr;\n  uint32_t capacity_ = 0;  // must be 2 power;\n  uint32_t expand_threshold_ = 0;\n  uint32_t bucket_idx_mask_ = 0;\n\n  uint32_t num_elements_ = 0;\n  uint32_t cur_req_id_ = 0;\n\n  TF_DISALLOW_COPY_AND_ASSIGN(UniqHashTable);\n};\n\n\nclass MultiShardUniqHashTable {\n  using FID = uint64_t;\n\n public:\n  MultiShardUniqHashTable() = default;\n  ~MultiShardUniqHashTable() = default;\n\n  void init(UniqHashTable *uniq_hashtable) {\n    uniq_hashtable_ = uniq_hashtable;\n  }\n\n  size_t uniq_fid(const FID fid, int shard) {\n    DCHECK_LT(shard, fid_lists_.size());\n    // store all shards' uniq indices in a single uniq_hashtable_\n    auto uniq_idx = uniq_hashtable_->UniqFid(fid, fid_lists_[shard].size());\n    if (uniq_idx == fid_lists_[shard].size()) {\n      fid_lists_[shard].push_back(fid);\n    }\n    return uniq_idx;\n  }\n\n  int fid_num(size_t shard) const {\n    return static_cast<int>(fid_lists_[shard].size());\n  }\n\n  std::vector<FID>& fid_list(int shard) {\n    DCHECK_LT(shard, fid_lists_.size());\n    return fid_lists_[shard];\n  }\n\n  void reset() {\n    uniq_hashtable_->Reset();\n  }\n\n  void resize(size_t shard_num) {\n    fid_lists_.resize(shard_num);\n  }\n\n  void reserve(size_t fid_num) {\n    for (auto& fid_list : fid_lists_) {\n      fid_list.reserve(fid_num);\n    }\n  }\n\n private:\n  UniqHashTable* uniq_hashtable_;\n  std::vector<std::vector<FID>> fid_lists_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#undef MONOLITH_INLINE\n\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_UNIQ_HASHTABLE_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/uniq_hashtable_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/uniq_hashtable.h\"\n\n#include <unistd.h>\n#include <cstdint>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n#include \"absl/strings/str_cat.h\"\n#include \"gtest/gtest.h\"\n\nusing FID = uint64_t;\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nclass UniqHashTableTest {\n public:\n  UniqHashTable uniq_hashtable;\n  std::vector<FID> fids;\n  int fid_num_ = 0;\n  int fid_range_ = 0;\n\n  UniqHashTableTest(int fid_num, int fid_range) {\n    fid_num_ = fid_num;\n    fid_range_ = fid_range;\n  }\n\n  void Reset() {\n    for (size_t fi = 0; fi < fid_num_; ++fi) {\n      fids.resize(fid_num_);\n      fids[fi] = (rand() % fid_range_) << 8;\n    }\n  }\n\n  void Check() {\n    Reset();\n    std::unordered_map<FID, uint32_t> result;\n    std::vector<FID> uniq_fids;\n    for (const auto& fid : fids) {\n      auto uniq_idx1 = result.size();\n      auto uniq_idx2 = uniq_hashtable.UniqFid(fid, uniq_hashtable.Size());\n      auto iter = result.find(fid);\n      if (iter != result.end()) {\n        EXPECT_EQ(iter->second, uniq_idx2) << \"size: \" << uniq_hashtable.Size();\n      } else {\n        result[fid] = uniq_idx1;\n        EXPECT_EQ(uniq_idx1, uniq_idx2);\n        EXPECT_EQ(uniq_idx2 + 1, uniq_hashtable.Size());\n        uniq_fids.push_back(fid);\n      }\n    }\n    EXPECT_EQ(uniq_fids.size(), result.size());\n    EXPECT_EQ(result.size(), uniq_hashtable.Size());\n    // check no repetition\n    result.clear();\n    for (const auto& fid : uniq_fids) {\n      EXPECT_EQ(result.count(fid), 0);\n      result.insert({fid, 0});\n    }\n  }\n};\n\nTEST(UniqHashTableTest, Small) {\n  size_t fid_num = 1e3;\n  size_t fid_range = 1e2;\n  UniqHashTableTest test(fid_num, fid_range);\n  test.Check();\n}\n\nTEST(UniqHashTableTest, Medium) {\n  size_t fid_num = 1e5;\n  size_t fid_range = 1e4;\n  UniqHashTableTest test(fid_num, fid_range);\n  test.Check();\n}\n\nTEST(UniqHashTableTest, Reset) {\n  size_t fid_num = 1e5;\n  size_t fid_range = 1e4;\n  UniqHashTableTest test(fid_num, fid_range);\n  test.Check();\n  test.uniq_hashtable.Reset();\n  test.Check();\n}\n\nTEST(UniqHashTableTest, ReqId) {\n  size_t fid_num = 1e3;\n  size_t fid_range = 1e2;\n  UniqHashTableTest test(fid_num, fid_range);\n  test.Check();\n  for (uint64_t i = 0; i < static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1; i++) {\n    test.uniq_hashtable.Reset();\n  }\n  test.Check();\n}\n\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/value_filter_by_feature.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/value_filter_by_feature.h\"\n\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_join.h\"\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"monolith/native_training/data/kernels/internal/relational_utils.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\n\nusing EFeature = ::monolith::io::proto::Feature;\nusing FilterValues = ::monolith::io::proto::FilterValues;\n\nstd::unordered_set<std::string> FeatureValueFilter::VALID_SET_OPS = {\n    \"any\", \"all\", \"diff\", \"startswith\", \"endswith\"};\n\nFeatureValueFilter::FeatureValueFilter(std::string field_name,\n                                       std::string field_type, std::string op,\n                                       std::vector<float> float_operand,\n                                       std::vector<int64> int_operand,\n                                       std::vector<std::string> string_operand,\n                                       std::string operand_filepath,\n                                       bool keep_empty)\n    : field_name_(std::move(field_name)),\n      field_type_(std::move(field_type)),\n      op_(std::move(op)),\n      feature_index_valid_score_(1.0),\n      float_operand_(std::move(float_operand)),\n      int_operand_(std::move(int_operand)),\n      string_operand_(std::move(string_operand)),\n      operand_filepath_(std::move(operand_filepath)),\n      keep_empty_(keep_empty) {\n  if (!internal::VALID_OPS.count(op_) && !VALID_SET_OPS.count(op_)) {\n    std::string valid_ops_str = absl::StrJoin(internal::VALID_OPS, \", \");\n    std::string valid_set_ops_str = absl::StrJoin(VALID_SET_OPS, \", \");\n    LOG(FATAL) << absl::StrFormat(\n        \"Invalid op: %s, please choose one from [%s] or [%s]\", op_,\n        valid_ops_str, valid_set_ops_str);\n  }\n\n  nlohmann::json j;\n  j[\"field_name\"] = field_name_;\n  j[\"field_type\"] = field_type_;\n  j[\"op\"] = op_;\n  j[\"float_operand_count\"] = float_operand_.size();\n  j[\"int_operand_count\"] = int_operand_.size();\n  j[\"string_operand_count\"] = string_operand_.size();\n  j[\"operand_filepath\"] = operand_filepath_;\n\n  int64_t limit = 1000;\n  if (float_operand_.size() <= limit) {\n    j[\"float_operand\"] = float_operand_;\n  } else {\n    std::vector<float> values(float_operand_.begin(),\n                              float_operand_.begin() + limit);\n    j[\"float_operand_first_1000\"] = values;\n  }\n\n  if (int_operand_.size() <= limit) {\n    j[\"int_operand\"] = int_operand_;\n  } else {\n    std::vector<int> values(int_operand_.begin(), int_operand_.begin() + limit);\n    j[\"int_operand_first_1000\"] = values;\n  }\n\n  if (string_operand_.size() <= limit) {\n    j[\"string_operand\"] = string_operand_;\n  } else {\n    std::vector<std::string> values(string_operand_.begin(),\n                                    string_operand_.begin() + limit);\n    j[\"string_operand_first_1000\"] = values;\n  }\n\n  LOG(INFO) << j.dump(2);\n\n  if ((op_ == internal::IN || op_ == internal::NOT_IN) &&\n      operand_filepath_.empty()) {\n    float_operand_set_.insert(float_operand_.begin(), float_operand_.end());\n    int_operand_set_.insert(int_operand_.begin(), int_operand_.end());\n    string_operand_set_.insert(string_operand_.begin(), string_operand_.end());\n  }\n}\n\nStatus FeatureValueFilter::EnsureLoadFilterValues(tensorflow::Env* env) {\n  absl::MutexLock l(&load_filter_values_mu_);\n  if (load_filter_values_finished_ || operand_filepath_.empty()) {\n    return Status::OK();\n  }\n\n  std::string filter_values_serialized;\n  TF_RETURN_IF_ERROR(\n      ReadFileToString(env, operand_filepath_, &filter_values_serialized));\n  FilterValues filter_values;\n  if (!filter_values.ParseFromString(filter_values_serialized)) {\n    return errors::InvalidArgument(\n        \"Unable to parse filter values, please make sure it is \"\n        \"serialized version of message:FilterValues.\");\n  }\n  switch (static_cast<int>(filter_values.type_case())) {\n    case FilterValues::TypeCase::kFloatList: {\n      if (field_type_ != \"float\") {\n        return errors::InvalidArgument(\n            \"Filter values' type(float) should be the same with field type(\",\n            field_type_, \")\");\n      }\n      float_operand_set_.insert(filter_values.float_list().value().begin(),\n                                filter_values.float_list().value().end());\n      break;\n    }\n    case FilterValues::TypeCase::kInt64List: {\n      if (field_type_ != \"int64\") {\n        return errors::InvalidArgument(\n            \"Filter values' type(int64) should be the same with field type(\",\n            field_type_, \")\");\n      }\n      int_operand_set_.insert(filter_values.int64_list().value().begin(),\n                              filter_values.int64_list().value().end());\n      break;\n    }\n    case FilterValues::TypeCase::kBytesList: {\n      if (field_type_ != \"bytes\") {\n        return errors::InvalidArgument(\n            \"Filter values' type(bytes) should be the same with field type(\",\n            field_type_, \")\");\n      }\n      string_operand_set_.insert(filter_values.bytes_list().value().begin(),\n                                 filter_values.bytes_list().value().end());\n      break;\n    }\n    case FilterValues::TypeCase::TYPE_NOT_SET:\n      return errors::InvalidArgument(\"FilterValue TYPE_NOT_SET, field type(\",\n                                     field_type_, \")\");\n    default:\n      return errors::InvalidArgument(\n          \"Invalid field type for feature value filter, field_type: \",\n          field_type_, \" FilterValues: \", filter_values.ShortDebugString());\n  }\n  load_filter_values_finished_ = true;\n  return Status::OK();\n}\n\nbool FeatureValueFilter::CheckFeatureIndex(const Example& example,\n                                           int* feature_index) {\n  find_feature_index_mu_.ReaderLock();\n  bool result = true;\n  if (cached_feature_index_ == -1 ||\n      cached_feature_index_ >= example.named_feature_size()) {\n    result = false;\n  } else {\n    const auto& feature = example.named_feature(cached_feature_index_);\n    if (feature.name() != field_name_) {\n      result = false;\n    }\n  }\n  if (result) {\n    *feature_index = cached_feature_index_;\n  }\n  find_feature_index_mu_.ReaderUnlock();\n  return result;\n}\n\nbool FeatureValueFilter::IsInstanceOfInterest(tensorflow::Env* env,\n                                              const Example& example) {\n  bool output = false;\n  int feature_index = -1;\n  if (!CheckFeatureIndex(example, &feature_index)) {\n    for (int i = 0; i < example.named_feature_size(); i++) {\n      const auto& feature = example.named_feature(i);\n      if (feature.name() == field_name_) {\n        feature_index = i;\n      }\n    }\n    if (feature_index != -1) {\n      absl::MutexLock l(&find_feature_index_mu_);\n      cached_feature_index_ = feature_index;\n    }\n    double score = feature_index_valid_score_.load();\n    score = 0.99 * score;\n    feature_index_valid_score_.store(score);\n    if (score < 0.7) {\n      LOG_EVERY_N_SEC(ERROR, 15)\n          << \"Potential performance problem! feature index valid score: \"\n          << score;\n    }\n  } else {\n    double score = feature_index_valid_score_.load();\n    feature_index_valid_score_.store(0.99 * score + 0.01);\n  }\n  LOG_EVERY_N_SEC(INFO, 120)\n      << \"Feature index valid score (performance related): \"\n      << feature_index_valid_score_.load();\n  if (feature_index == -1 && !keep_empty_) {\n    output = false;\n    LOG_EVERY_N_SEC(ERROR, 15) << \"Feature not found!\"\n                               << \" field name: \" << field_name_;\n    return output;\n  }\n  const auto& feature = example.named_feature(feature_index).feature();\n  const auto& type_case = feature.type_case();\n  // op是in/not_in，且feature是单值类型的场景\n  if ((op_ == internal::IN || op_ == internal::NOT_IN) &&\n      !operand_filepath_.empty() &&\n      (type_case == EFeature::TypeCase::kFloatList ||\n       type_case == EFeature::TypeCase::kDoubleList ||\n       type_case == EFeature::TypeCase::kInt64List ||\n       type_case == EFeature::TypeCase::kBytesList)) {\n    TF_CHECK_OK(EnsureLoadFilterValues(env));\n  }\n  switch (static_cast<int>(type_case)) {\n    case EFeature::TypeCase::TYPE_NOT_SET: {\n      LOG_EVERY_N_SEC(ERROR, 15) << \"Invalid data: feature not set!\"\n                                 << \" field name: \" << field_name_;\n      break;\n    }\n    case EFeature::TypeCase::kFloatValue: {\n      LOG_EVERY_N_SEC(ERROR, 15) << \"Invalid data: float value is not \"\n                                    \"supported, please use float list!\"\n                                 << \" field name: \" << field_name_;\n      break;\n    }\n    case EFeature::TypeCase::kDoubleValue: {\n      LOG_EVERY_N_SEC(ERROR, 15) << \"Invalid data: double value is not \"\n                                    \"supported, please use double list!\"\n                                 << \" field name: \" << field_name_;\n      break;\n    }\n    case EFeature::TypeCase::kInt64Value: {\n      LOG_EVERY_N_SEC(ERROR, 15) << \"Invalid data: int64 value is not \"\n                                    \"supported, please use double list!\"\n                                 << \" field name: \" << field_name_;\n      break;\n    }\n    case EFeature::TypeCase::kBytesValue: {\n      LOG_EVERY_N_SEC(ERROR, 15) << \"Invalid data: bytes value is not \"\n                                    \"supported, please use bytes list!\"\n                                 << \" field name: \" << field_name_;\n      break;\n    }\n    default:\n      break;\n  }\n  std::vector<int64> values;\n  switch (static_cast<int>(type_case)) {\n    case EFeature::TypeCase::kFloatList: {\n      if (field_type_ != \"float\") {\n        LOG_EVERY_N_SEC(ERROR, 15)\n            << \"Field type not match: field name: \" << field_name_\n            << \" field type: \" << field_type_\n            << \" but feature has float value.\";\n        break;\n      }\n      if (feature.float_list().value_size() == 1) {\n        float value = feature.float_list().value(0);\n        output = internal::COMPARE_OPS.count(op_)\n                     ? internal::compare(op_, value, float_operand_)\n                     : internal::contains(op_, value, float_operand_set_);\n        return output;\n      } else if (feature.float_list().value_size() > 1) {\n        LOG_EVERY_N_SEC(ERROR, 15)\n            << \"Invalid data: float list with multiple elements is not \"\n               \"supported, please investigate and retry!\"\n            << \" field name: \" << field_name_;\n      }\n      break;\n    }\n    case EFeature::TypeCase::kDoubleList: {\n      if (field_type_ != \"double\") {\n        LOG_EVERY_N_SEC(ERROR, 15)\n            << \"Field type not match: field name: \" << field_name_\n            << \" field type: \" << field_type_\n            << \" but feature has double value.\";\n        break;\n      }\n      if (feature.double_list().value_size() == 1) {\n        double value = feature.double_list().value(0);\n        output = internal::COMPARE_OPS.count(op_)\n                     ? internal::compare(op_, value, float_operand_)\n                     : internal::contains(op_, value, float_operand_set_);\n        return output;\n      } else if (feature.double_list().value_size() > 1) {\n        LOG_EVERY_N_SEC(ERROR, 15)\n            << \"Invalid data: double_list with multiple elements is not \"\n               \"supported, please investigate and retry!\"\n            << \" field name: \" << field_name_;\n      }\n      break;\n    }\n    case EFeature::TypeCase::kInt64List: {\n      if (field_type_ != \"int64\") {\n        LOG_EVERY_N_SEC(ERROR, 15)\n            << \"Field type not match: field name: \" << field_name_\n            << \" field type: \" << field_type_\n            << \" but feature has int64 value.\";\n        break;\n      }\n      if (VALID_SET_OPS.count(op_)) {\n        for (const auto& value : feature.int64_list().value()) {\n          values.push_back(value);\n        }\n      } else {\n        if (feature.int64_list().value_size() == 1) {\n          int64 value = feature.int64_list().value(0);\n          output = internal::COMPARE_OPS.count(op_)\n                       ? internal::compare(op_, value, int_operand_)\n                       : internal::contains(op_, value, int_operand_set_);\n          return output;\n        } else if (feature.double_list().value_size() > 1) {\n          LOG_EVERY_N_SEC(ERROR, 15)\n              << \"Invalid data: int64_list with multiple elements when not \"\n                 \"using set_ops is not supported, please investigate and retry!\"\n              << \" field name: \" << field_name_ << \" op: \" << op_;\n        }\n      }\n      break;\n    }\n    case EFeature::TypeCase::kBytesList: {\n      if (field_type_ != \"bytes\") {\n        LOG_EVERY_N_SEC(ERROR, 15)\n            << \"Field type not match: field name: \" << field_name_\n            << \" field type: \" << field_type_\n            << \" but feature has bytes value.\";\n        break;\n      }\n      if (feature.bytes_list().value_size() == 1) {\n        std::string value = feature.bytes_list().value(0);\n        output = false;\n        if (op_ == \"startswith\") {\n          for (const std::string& operand : string_operand_) {\n            if (value.find(operand) == 0) {\n              output = true;\n              break;\n            }\n          }\n        } else if (op_ == \"endswith\") {\n          for (const std::string& operand : string_operand_) {\n            if (operand.size() <= value.size()) {\n              bool found =\n                  std::equal(operand.rbegin(), operand.rend(), value.rbegin());\n              if (found) {\n                output = true;\n                break;\n              }\n            }\n          }\n        } else {\n          output = internal::COMPARE_OPS.count(op_)\n                       ? internal::compare(op_, value, string_operand_)\n                       : internal::contains(op_, value, string_operand_set_);\n        }\n        return output;\n      } else if (feature.bytes_list().value_size() > 1) {\n        LOG_EVERY_N_SEC(ERROR, 15)\n            << \"Invalid data: bytes_list with multiple elements is not \"\n               \"supported, please investigate and retry!\"\n            << \" field name: \" << field_name_;\n      }\n      break;\n    }\n    default: {\n      output = false;\n      const auto descriptor = EFeature::GetDescriptor();\n      const auto reflection = EFeature::GetReflection();\n      const auto oneof_descriptor = descriptor->FindOneofByName(\"type\");\n      std::string feature_dtype = \"\";\n      if (oneof_descriptor != nullptr) {\n        const auto field_descriptor =\n            reflection->GetOneofFieldDescriptor(feature, oneof_descriptor);\n        if (field_descriptor != nullptr) {\n          if (field_descriptor->type() ==\n              google::protobuf::FieldDescriptor::TYPE_MESSAGE) {\n            // 处理嵌套消息类型\n            const auto nested_descriptor = field_descriptor->message_type();\n            if (nested_descriptor != nullptr) {\n              feature_dtype = nested_descriptor->name();\n            }\n          } else if (field_descriptor->type() ==\n                     google::protobuf::FieldDescriptor::TYPE_ENUM) {\n            // 处理枚举类型\n            const auto enum_descriptor = field_descriptor->enum_type();\n            if (enum_descriptor != nullptr) {\n              feature_dtype = enum_descriptor->name();\n            }\n          } else {\n            feature_dtype = field_descriptor->type_name();\n          }\n        }\n      }\n      LOG(INFO) << \"feature not match, feature dtype is: \" << feature_dtype\n                << \", supposed field type is: \" << field_type_\n                << \" type case: \" << int(type_case);\n      break;\n    }\n  }\n  if (values.size() > 0) {\n    output = cmp(values);\n  } else {\n    output = keep_empty_;\n  }\n  return output;\n}\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/value_filter_by_feature.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_FEATURE_VALUE_FILTER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_FEATURE_VALUE_FILTER_H_\n\n#include <set>\n#include <unordered_set>\n#include \"absl/synchronization/mutex.h\"\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"tensorflow/core/platform/env.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\n\nusing Example = ::monolith::io::proto::Example;\n\nclass FeatureValueFilter {\n public:\n  FeatureValueFilter(std::string field_name, std::string field_type,\n                     std::string op, std::vector<float> float_operand,\n                     std::vector<int64> int_operand,\n                     std::vector<std::string> string_operand,\n                     std::string operand_filepath, bool keep_empty);\n\n  bool IsInstanceOfInterest(tensorflow::Env* env, const Example& example);\n\n  static std::unordered_set<std::string> VALID_SET_OPS;\n\n private:\n  Status EnsureLoadFilterValues(tensorflow::Env* env);\n\n  bool CheckFeatureIndex(const Example& example, int* feature_index);\n\n  bool cmp(const std::vector<int64>& values) {\n    std::set<int64> intersection;\n    std::set_intersection(values.begin(), values.end(), int_operand_.begin(),\n                          int_operand_.end(),\n                          std::inserter(intersection, intersection.begin()));\n    if (op_ == \"any\") {\n      return intersection.size() > 0;\n    } else if (op_ == \"all\") {\n      return intersection.size() == int_operand_.size();\n    } else if (op_ == \"diff\") {\n      return intersection.size() == 0;\n    } else {\n      LOG_EVERY_N_SEC(ERROR, 15)\n          << \"Invalid op for int64_list feature: \" << op_;\n      return false;\n    }\n  }\n\n private:\n  mutable absl::Mutex load_filter_values_mu_;\n  bool load_filter_values_finished_ ABSL_GUARDED_BY(load_filter_values_mu_) =\n      false;\n  std::string field_name_;\n  std::string field_type_;\n  mutable absl::Mutex find_feature_index_mu_;\n  int cached_feature_index_ ABSL_GUARDED_BY(find_feature_index_mu_) = -1;\n  std::atomic<double> feature_index_valid_score_;\n  std::string op_;  // gt, ge, eq, lt, le, neq, between\n\n  std::vector<float> float_operand_;\n  std::vector<int64> int_operand_;\n  std::vector<std::string> string_operand_;\n  std::unordered_set<float> float_operand_set_;\n  std::unordered_set<int64> int_operand_set_;\n  std::unordered_set<std::string> string_operand_set_;\n  std::string operand_filepath_;\n  bool keep_empty_ = false;\n};\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_FEATURE_VALUE_FILTER_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/value_filter_by_line_id.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/value_filter_by_line_id.h\"\n\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_join.h\"\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"monolith/native_training/data/kernels/internal/relational_utils.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\n\nLineIdValueFilter::LineIdValueFilter(std::string field_name, std::string op,\n                                     std::vector<float> float_operand,\n                                     std::vector<int64> int_operand,\n                                     std::vector<std::string> string_operand,\n                                     std::string operand_filepath,\n                                     bool keep_empty)\n    : field_name_(std::move(field_name)),\n      op_(std::move(op)),\n      float_operand_(std::move(float_operand)),\n      int_operand_(std::move(int_operand)),\n      string_operand_(std::move(string_operand)),\n      operand_filepath_(std::move(operand_filepath)),\n      keep_empty_(keep_empty) {\n  const auto descriptor = ::idl::matrix::proto::LineId::GetDescriptor();\n  const auto reflection = ::idl::matrix::proto::LineId::GetReflection();\n  field_ = descriptor->FindFieldByName(field_name_);\n  uint_operand_.insert(uint_operand_.end(), int_operand_.begin(),\n                       int_operand_.end());\n\n  std::unordered_set<std::string> valid_set_ops = {\"any\", \"all\", \"diff\",\n                                                   \"startswith\", \"endswith\"};\n  if (!internal::VALID_OPS.count(op_) && !valid_set_ops.count(op_)) {\n    std::string valid_ops_str = absl::StrJoin(internal::VALID_OPS, \", \");\n    std::string valid_set_ops_str = absl::StrJoin(valid_set_ops, \", \");\n    LOG(FATAL) << absl::StrFormat(\n        \"Invalid op: %s, please choose one from [%s] or [%s]\", op_,\n        valid_ops_str, valid_set_ops_str);\n  }\n\n  nlohmann::json j;\n  j[\"field_name\"] = field_name_;\n  j[\"op\"] = op_;\n  j[\"float_operand_count\"] = float_operand_.size();\n  j[\"int_operand_count\"] = int_operand_.size();\n  j[\"string_operand_count\"] = string_operand_.size();\n  j[\"operand_filepath\"] = operand_filepath_;\n\n  int64_t limit = 1000;\n  if (float_operand_.size() <= limit) {\n    j[\"float_operand\"] = float_operand_;\n  } else {\n    std::vector<float> values(float_operand_.begin(),\n                              float_operand_.begin() + limit);\n    j[\"float_operand_first_1000\"] = values;\n  }\n\n  if (int_operand_.size() <= limit) {\n    j[\"int_operand\"] = int_operand_;\n  } else {\n    std::vector<int> values(int_operand_.begin(), int_operand_.begin() + limit);\n    j[\"int_operand_first_1000\"] = values;\n  }\n\n  if (string_operand_.size() <= limit) {\n    j[\"string_operand\"] = string_operand_;\n  } else {\n    std::vector<std::string> values(string_operand_.begin(),\n                                    string_operand_.begin() + limit);\n    j[\"string_operand_first_1000\"] = values;\n  }\n\n  LOG(INFO) << j.dump(2);\n\n  if ((op_ == internal::IN || op_ == internal::NOT_IN) &&\n      operand_filepath_.empty()) {\n    float_operand_set_.insert(float_operand_.begin(), float_operand_.end());\n    int_operand_set_.insert(int_operand_.begin(), int_operand_.end());\n    uint_operand_set_.insert(uint_operand_.begin(), uint_operand_.end());\n    string_operand_set_.insert(string_operand_.begin(), string_operand_.end());\n  }\n}\n\nStatus LineIdValueFilter::EnsureLoadFilterValues(tensorflow::Env *env) {\n  absl::MutexLock l(&mu_);\n  if (load_filter_values_finished_ || operand_filepath_.empty()) {\n    return Status::OK();\n  }\n\n  std::string filter_values_serialized;\n  TF_RETURN_IF_ERROR(\n      ReadFileToString(env, operand_filepath_, &filter_values_serialized));\n  ::monolith::io::proto::FilterValues filter_values;\n  if (!filter_values.ParseFromString(filter_values_serialized)) {\n    return errors::InvalidArgument(\n        \"Unable to parse filter values, please make sure it is \"\n        \"serialized version of message:FilterValues.\");\n  }\n\n  auto field = field_;\n  switch (field->cpp_type()) {\n    case google::protobuf::FieldDescriptor::CppType::CPPTYPE_FLOAT:\n    case google::protobuf::FieldDescriptor::CppType::CPPTYPE_DOUBLE: {\n      if (!filter_values.has_float_list()) {\n        return errors::InvalidArgument(\n            \"Filter values' type should be the same with field type.\");\n      }\n      float_operand_set_.insert(filter_values.float_list().value().begin(),\n                                filter_values.float_list().value().end());\n      break;\n    }\n    case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT32:\n    case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT64: {\n      if (!filter_values.has_int64_list()) {\n        return errors::InvalidArgument(\n            \"Filter values' type should be the same with field type.\");\n      }\n      int_operand_set_.insert(filter_values.int64_list().value().begin(),\n                              filter_values.int64_list().value().end());\n      break;\n    }\n    case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT32:\n    case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT64: {\n      if (!filter_values.has_int64_list()) {\n        return errors::InvalidArgument(\n            \"Filter values' type should be the same with field type.\");\n      }\n      uint_operand_set_.insert(filter_values.int64_list().value().begin(),\n                               filter_values.int64_list().value().end());\n      break;\n    }\n    case google::protobuf::FieldDescriptor::CppType::CPPTYPE_STRING: {\n      if (!filter_values.has_bytes_list()) {\n        return errors::InvalidArgument(\n            \"Filter values' type should be the same with field type.\");\n      }\n      string_operand_set_.insert(filter_values.bytes_list().value().begin(),\n                                 filter_values.bytes_list().value().end());\n      break;\n    }\n    default: {\n      return errors::InvalidArgument(\"Invalid field type for filter.\");\n    }\n  }\n  load_filter_values_finished_ = true;\n\n  return Status::OK();\n}\n\nbool LineIdValueFilter::IsInstanceOfInterest(\n    tensorflow::Env *env, const ::idl::matrix::proto::LineId &line_id) {\n  bool output = false;\n\n  const auto reflection = ::idl::matrix::proto::LineId::GetReflection();\n  auto field = field_;\n  if (field == nullptr) {\n    output = false;\n    return output;\n  }\n\n  if (!field->is_repeated()) {\n    if ((op_ == internal::IN || op_ == internal::NOT_IN) &&\n        !operand_filepath_.empty()) {\n      TF_CHECK_OK(EnsureLoadFilterValues(env));\n    }\n    switch (field->cpp_type()) {\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_FLOAT: {\n        float value = reflection->GetFloat(line_id, field);\n        output = internal::COMPARE_OPS.count(op_)\n                     ? internal::compare(op_, value, float_operand_)\n                     : internal::contains(op_, value, float_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_DOUBLE: {\n        double value = reflection->GetDouble(line_id, field);\n        output = internal::COMPARE_OPS.count(op_)\n                     ? internal::compare(op_, value, float_operand_)\n                     : internal::contains(op_, value, float_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT32: {\n        int64 value = reflection->GetInt32(line_id, field);\n        output = internal::COMPARE_OPS.count(op_)\n                     ? internal::compare(op_, value, int_operand_)\n                     : internal::contains(op_, value, int_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT64: {\n        int64 value = reflection->GetInt64(line_id, field);\n        output = internal::COMPARE_OPS.count(op_)\n                     ? internal::compare(op_, value, int_operand_)\n                     : internal::contains(op_, value, int_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT32: {\n        int64 value = reflection->GetUInt32(line_id, field);\n        output = internal::COMPARE_OPS.count(op_)\n                     ? internal::compare(op_, value, int_operand_)\n                     : internal::contains(op_, value, int_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT64: {\n        uint64 value = reflection->GetUInt64(line_id, field);\n        output = internal::COMPARE_OPS.count(op_)\n                     ? internal::compare(op_, value, uint_operand_)\n                     : internal::contains(op_, value, uint_operand_set_);\n        break;\n      }\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_STRING: {\n        std::string value = reflection->GetString(line_id, field);\n        output = false;\n        if (op_ == \"startswith\") {\n          for (const std::string &operand : string_operand_) {\n            if (value.find(operand) == 0) {\n              output = true;\n              break;\n            }\n          }\n        } else if (op_ == \"endswith\") {\n          for (const std::string &operand : string_operand_) {\n            if (operand.size() <= value.size()) {\n              bool found =\n                  std::equal(operand.rbegin(), operand.rend(), value.rbegin());\n              if (found) {\n                output = true;\n                break;\n              }\n            }\n          }\n        } else {\n          output = internal::COMPARE_OPS.count(op_)\n                       ? internal::compare(op_, value, string_operand_)\n                       : internal::contains(op_, value, string_operand_set_);\n        }\n        break;\n      }\n      default:\n        output = false;\n        LOG(INFO) << \"dtype is \" << field->cpp_type();\n        break;\n    }\n  } else {\n    const int field_size = reflection->FieldSize(line_id, field);\n    std::vector<int64> values;\n    switch (field->cpp_type()) {\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT32:\n        for (int i = 0; i < field_size; ++i) {\n          values.push_back(reflection->GetRepeatedInt32(line_id, field, i));\n        }\n        break;\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT64:\n        for (int i = 0; i < field_size; ++i) {\n          values.push_back(reflection->GetRepeatedInt64(line_id, field, i));\n        }\n        break;\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT32:\n        for (int i = 0; i < field_size; ++i) {\n          values.push_back(reflection->GetRepeatedUInt32(line_id, field, i));\n        }\n        break;\n      case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT64:\n        for (int i = 0; i < field_size; ++i) {\n          values.push_back(reflection->GetRepeatedUInt64(line_id, field, i));\n        }\n        break;\n      default:\n        LOG(INFO) << \"dtype is \" << field->cpp_type();\n        break;\n    }\n\n    if (values.size() > 0) {\n      output = cmp(values);\n    } else {\n      output = keep_empty_;\n    }\n  }\n\n  return output;\n}\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/value_filter_by_line_id.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_LINE_ID_VALUE_FILTER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_LINE_ID_VALUE_FILTER_H_\n\n#include <set>\n#include \"absl/synchronization/mutex.h\"\n#include \"idl/matrix/proto/line_id.pb.h\"\n#include \"tensorflow/core/platform/env.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\n\nclass LineIdValueFilter {\n public:\n  LineIdValueFilter(std::string field_name, std::string op,\n                    std::vector<float> float_operand,\n                    std::vector<int64> int_operand,\n                    std::vector<std::string> string_operand,\n                    std::string operand_filepath, bool keep_empty);\n\n  bool IsInstanceOfInterest(tensorflow::Env* env,\n                            const ::idl::matrix::proto::LineId& line_id);\n\n private:\n  Status EnsureLoadFilterValues(tensorflow::Env* env);\n\n  bool cmp(const std::vector<int64>& values) {\n    std::set<int64> intersection;\n    std::set_intersection(values.begin(), values.end(), int_operand_.begin(),\n                          int_operand_.end(),\n                          std::inserter(intersection, intersection.begin()));\n    if (op_ == \"any\") {\n      return intersection.size() > 0;\n    } else if (op_ == \"all\") {\n      return intersection.size() == int_operand_.size();\n    } else if (op_ == \"diff\") {\n      return intersection.size() == 0;\n    } else {\n      LOG(FATAL) << \"Invalid op: \" << op_;\n      return false;\n    }\n  }\n\n private:\n  mutable absl::Mutex mu_;\n  bool load_filter_values_finished_ ABSL_GUARDED_BY(mu_) = false;\n  const google::protobuf::FieldDescriptor* field_;\n  std::string field_name_;\n  std::string op_;  // gt, ge, eq, lt, le, neq, between\n  bool keep_empty_ = false;\n  std::string operand_filepath_;\n\n  std::vector<float> float_operand_;\n  std::vector<int64> int_operand_;\n  std::vector<uint64> uint_operand_;\n  std::vector<std::string> string_operand_;\n\n  std::unordered_set<float> float_operand_set_;\n  std::unordered_set<int64> int_operand_set_;\n  std::unordered_set<uint64> uint_operand_set_;\n  std::unordered_set<std::string> string_operand_set_;\n};\n\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_INTERNAL_LINE_ID_VALUE_FILTER_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/internal/value_filter_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/value_filter_by_line_id.h\"\n#include \"monolith/native_training/data/kernels/internal/value_filter_by_feature.h\"\n\n#include <memory>\n#include <thread>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"idl/matrix/proto/line_id.pb.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace internal {\nnamespace {\n\nusing ::idl::matrix::proto::LineId;\n\nTEST(LineIdValueFilter, Int) {\n  LineId line_id;\n  line_id.set_uid(2);\n\n  tensorflow::Env* env = tensorflow::Env::Default();\n  LineIdValueFilter filter_eq(\"uid\", \"eq\", {}, {2}, {}, \"\", false);\n  EXPECT_TRUE(filter_eq.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_neq(\"uid\", \"neq\", {}, {2}, {}, \"\", false);\n  EXPECT_FALSE(filter_neq.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_le(\"uid\", \"le\", {}, {3}, {}, \"\", false);\n  EXPECT_TRUE(filter_le.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_ge(\"uid\", \"ge\", {}, {1}, {}, \"\", false);\n  EXPECT_TRUE(filter_ge.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_between(\"uid\", \"between\", {}, {1, 3}, {}, \"\", false);\n  EXPECT_TRUE(filter_between.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_in(\"uid\", \"in\", {}, {1, 2, 3}, {}, \"\", false);\n  EXPECT_TRUE(filter_in.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_notin(\"uid\", \"not-in\", {}, {1, 2, 3}, {}, \"\", false);\n  EXPECT_FALSE(filter_notin.IsInstanceOfInterest(env, line_id));\n}\n\nTEST(LineIdValueFilter, IntArray) {\n  LineId line_id;\n  line_id.mutable_actions()->Add(2);\n  line_id.mutable_actions()->Add(3);\n\n  tensorflow::Env* env = tensorflow::Env::Default();\n  LineIdValueFilter filter_any1(\"actions\", \"any\", {}, {1, 2}, {}, \"\", false);\n  EXPECT_TRUE(filter_any1.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_any2(\"actions\", \"any\", {}, {1, 4}, {}, \"\", false);\n  EXPECT_FALSE(filter_any2.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_all1(\"actions\", \"all\", {}, {2, 3}, {}, \"\", false);\n  EXPECT_TRUE(filter_all1.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_all2(\"actions\", \"all\", {}, {2, 3, 4}, {}, \"\", false);\n  EXPECT_FALSE(filter_all2.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_diff1(\"actions\", \"diff\", {}, {1, 4}, {}, \"\", false);\n  EXPECT_TRUE(filter_diff1.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_diff2(\"actions\", \"diff\", {}, {1, 2, 4}, {}, \"\",\n                                 false);\n  EXPECT_FALSE(filter_diff2.IsInstanceOfInterest(env, line_id));\n}\n\nTEST(LineIdValueFilter, Float) {\n  LineId line_id;\n  line_id.set_q_pred(2.0f);\n\n  tensorflow::Env* env = tensorflow::Env::Default();\n  LineIdValueFilter filter_eq(\"q_pred\", \"eq\", {2.0f}, {}, {}, \"\", false);\n  EXPECT_TRUE(filter_eq.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_neq(\"q_pred\", \"neq\", {2.0f}, {}, {}, \"\", false);\n  EXPECT_FALSE(filter_neq.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_le(\"q_pred\", \"le\", {3.0f}, {}, {}, \"\", false);\n  EXPECT_TRUE(filter_le.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_ge(\"q_pred\", \"ge\", {1.0f}, {}, {}, \"\", false);\n  EXPECT_TRUE(filter_ge.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_between(\"q_pred\", \"between\", {1.0f, 3.0f}, {}, {},\n                                   \"\", false);\n  EXPECT_TRUE(filter_between.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_in(\"q_pred\", \"in\", {1.0f, 2.0f, 3.0f}, {}, {}, \"\",\n                              false);\n  EXPECT_TRUE(filter_in.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_notin(\"q_pred\", \"not-in\", {1.0f, 2.0f, 3.0f}, {}, {},\n                                 \"\", false);\n  EXPECT_FALSE(filter_notin.IsInstanceOfInterest(env, line_id));\n}\n\nTEST(LineIdValueFilter, String) {\n  LineId line_id;\n  line_id.set_vid(\"hello\");\n\n  tensorflow::Env* env = tensorflow::Env::Default();\n  LineIdValueFilter filter_eq(\"vid\", \"eq\", {}, {}, {\"hello\"}, \"\", false);\n  EXPECT_TRUE(filter_eq.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_neq(\"vid\", \"neq\", {}, {}, {\"hello\"}, \"\", false);\n  EXPECT_FALSE(filter_neq.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_le(\"vid\", \"le\", {}, {}, {\"hello1\"}, \"\", false);\n  EXPECT_TRUE(filter_le.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_ge(\"vid\", \"ge\", {}, {}, {\"hell\"}, \"\", false);\n  EXPECT_TRUE(filter_ge.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_between(\"vid\", \"between\", {}, {}, {\"hell\", \"hello1\"},\n                                   \"\", false);\n  EXPECT_TRUE(filter_between.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_in(\"vid\", \"in\", {}, {}, {\"hello\", \"world\"}, \"\",\n                              false);\n  EXPECT_TRUE(filter_in.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_notin(\"vid\", \"not-in\", {}, {}, {\"hello\", \"world\"},\n                                 \"\", false);\n  EXPECT_FALSE(filter_notin.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_startswith(\"vid\", \"startswith\", {}, {}, {\"hell\"}, \"\",\n                                      false);\n  EXPECT_TRUE(filter_startswith.IsInstanceOfInterest(env, line_id));\n\n  LineIdValueFilter filter_endswith(\"vid\", \"endswith\", {}, {}, {\"llo\"}, \"\",\n                                    false);\n  EXPECT_TRUE(filter_endswith.IsInstanceOfInterest(env, line_id));\n}\n\n}  // namespace\n}  // namespace internal\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/item_pool_kernels.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <atomic>\n#include <cstdlib>\n\n#include \"monolith/native_training/data/kernels/item_pool_kernels.h\"\n#include \"tensorflow/core/lib/io/record_reader.h\"\n#include \"tensorflow/core/lib/io/record_writer.h\"\n#include \"tensorflow/core/platform/path.h\"\n\n#include \"absl/random/random.h\"\n#include \"absl/strings/numbers.h\"\n#include \"absl/strings/str_cat.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_split.h\"\n#include \"absl/strings/string_view.h\"\n#include \"absl/synchronization/mutex.h\"\n#include \"tensorflow/core/platform/random.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nusing json = nlohmann::json;\nusing NamedFeature = ::monolith::io::proto::NamedFeature;\nusing ChannelCache = ::monolith::io::proto::ChannelCache;\nstatic const std::string FILE_NAME_PREFIX = \"item_pool_\";\nstatic constexpr uint64_t MASK = (1L << 48) - 1;\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\n// Carries the data through async process.\n// It will ref and unref |p_hash_table|.\nstruct AsyncPack {\n  AsyncPack(OpKernelContext* p_ctx, ItemPoolResource* p_pool,\n            std::function<void()> p_done, int p_thread_num)\n      : ctx(p_ctx),\n        pool(p_pool),\n        done(std::move(p_done)),\n        thread_num(p_thread_num),\n        finish_num(0),\n        status(thread_num) {\n    pool->Ref();\n  }\n\n  ~AsyncPack() { pool->Unref(); }\n\n  OpKernelContext* ctx;\n  ItemPoolResource* pool;\n  std::function<void()> done;\n  const int thread_num;\n  std::atomic_int finish_num;\n  std::vector<Status> status;\n};\n\nItemPoolResource::ItemPoolResource(int max_item_num_per_channel, int start_num)\n    : start_num_(start_num),\n      max_item_num_per_channel_(max_item_num_per_channel),\n      cache_(std::make_unique<internal::CacheManager>(max_item_num_per_channel,\n                                                      start_num)) {}\n\nStatus ItemPoolResource::Add(\n    uint64_t channel_id, uint64_t item_id,\n    const std::shared_ptr<const internal::ItemFeatures>& item) {\n  absl::MutexLock l(&mu_);\n  cache_->Push(channel_id, item_id, item, 1, 0);\n  return Status::OK();\n}\n\nstd::shared_ptr<const internal::ItemFeatures> ItemPoolResource::Sample(\n    uint64_t channel_id, double* freq_factor, double* time_factor) {\n  absl::MutexLock l(&mu_);\n  return cache_->RandomSelectOne(channel_id, freq_factor, time_factor);\n}\n\nStatus ItemPoolResource::Save(WritableFile* ostream, int shard_index,\n                              int shard_num) {\n  absl::MutexLock l(&mu_);\n  const absl::flat_hash_map<uint64_t, internal::CacheWithGid>& channel_cache_ =\n      cache_->GetCache();\n\n  io::RecordWriter writer(ostream);\n  Status write_status = Status::OK();\n  for (const auto& pair : channel_cache_) {\n    if (pair.first % shard_num != shard_index) {\n      continue;\n    }\n    ChannelCache channel_cache;\n    channel_cache.set_channel_id(pair.first);\n    pair.second.ToProto(&channel_cache);\n    Status s = writer.WriteRecord(channel_cache.SerializeAsString());\n    if (TF_PREDICT_FALSE(!s.ok())) {\n      write_status.Update(s);\n      break;\n    }\n  }\n\n  TF_RETURN_IF_ERROR(write_status);\n  TF_RETURN_IF_ERROR(writer.Close());\n\n  return Status::OK();\n}\n\nStatus ItemPoolResource::Restore(RandomAccessFile* istream, int64 buffer_size) {\n  absl::MutexLock l(&mu_);\n  io::RecordReaderOptions opts;\n  opts.buffer_size = buffer_size;\n  io::SequentialRecordReader reader(istream, opts);\n\n  Status restore_status = Status::OK();\n  while (true) {\n    tstring s;\n    ChannelCache channel_cache;\n    // read record\n    Status rs = reader.ReadRecord(&s);\n    if (errors::IsOutOfRange(rs)) {\n      LOG(INFO) << \"EOF, read file done...\";\n      break;\n    } else {\n      restore_status.Update(rs);\n    }\n\n    if (!channel_cache.ParseFromArray(s.data(), s.size())) {\n      restore_status.Update(errors::FailedPrecondition(\n          \"Unable to parse data. Data might be corrupted\"));\n      break;\n    } else {\n      restore_status.Update(Status::OK());\n    }\n\n    for (const auto& feature_data : channel_cache.feature_datas()) {\n      auto item_feature_ptr = internal::MakeItemFeaturesFromProto(feature_data);\n      cache_->Push(channel_cache.channel_id(), feature_data.gid(),\n                   item_feature_ptr, feature_data.origin_cnt(),\n                   feature_data.sample_cnt());\n    }\n    LOG(INFO) << absl::StrFormat(\n        \"ItemPoolResource: after restore, channel %lld restore %llu items\",\n        channel_cache.channel_id(), channel_cache.feature_datas_size());\n  }\n\n  TF_RETURN_IF_ERROR(restore_status);\n\n  return Status::OK();\n}\n\nbool ItemPoolResource::Equal(const ItemPoolResource& other) const {\n  if (other.max_item_num_per_channel_ != max_item_num_per_channel_) {\n    return false;\n  }\n\n  if (other.start_num_ != start_num_) {\n    return false;\n  }\n\n  auto this_cache = cache_->GetCache();\n  auto other_cache = other.cache_->GetCache();\n  if (this_cache.size() != other_cache.size()) {\n    return false;\n  } else {\n    for (const auto& it : this_cache) {\n      if (other_cache.count(it.first) == 0) {\n        return false;\n      } else {\n        auto this_channel = it.second;\n        auto other_channel = other_cache.at(it.first);\n        return this_channel.Equal(other_channel);\n      }\n    }\n  }\n\n  return true;\n}\n\nvoid ItemPoolResource::SampleChannelID(uint64_t* channel_id) {\n  absl::MutexLock l(&mu_);\n  cache_->SampleChannelID(channel_id);\n}\n\nvoid get_index_and_worker_num(int* index, int* worker_num) {\n  const char* env_p = std::getenv(\"TF_CONFIG\");\n  if (env_p == nullptr) {\n    *index = 0;\n    *worker_num = 1;\n  } else {\n    auto tf_config = json::parse(env_p);\n    // assert TF_CONFIG only has ps + chief + worker\n    for (const auto& conf_item : tf_config[\"cluster\"].items()) {\n      if (conf_item.key() != \"ps\" && conf_item.key() != \"chief\" &&\n          conf_item.key() != \"worker\") {\n        LOG(ERROR) << \"Unknown Cluster Type: \" << conf_item.key();\n      }\n    }\n    auto chief = tf_config[\"cluster\"][\"chief\"];\n    auto workers = tf_config[\"cluster\"][\"worker\"];\n    *worker_num = chief.size() + workers.size();\n    if (tf_config[\"task\"][\"type\"] == \"worker\") {\n      *index = static_cast<int>(tf_config[\"task\"][\"index\"]) + 1;\n    } else {\n      *index = 0;\n    }\n  }\n}\n\nclass ItemPoolCreateOp : public ResourceOpKernel<ItemPoolResource> {\n public:\n  explicit ItemPoolCreateOp(OpKernelConstruction* ctx) : ResourceOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"start_num\", &start_num_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"max_item_num_per_channel\",\n                                     &max_item_num_per_channel_));\n  }\n\n private:\n  Status CreateResource(ItemPoolResource** wrapper)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    *wrapper = new ItemPoolResource(max_item_num_per_channel_, start_num_);\n    return Status::OK();\n  }\n\n  int start_num_, max_item_num_per_channel_;\n};\n\n// for test only\nclass ItemPoolRandomFillOp : public OpKernel {\n public:\n  explicit ItemPoolRandomFillOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    ItemPoolResource* pool;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &pool));\n    core::ScopedUnref unref(pool);\n    ctx->set_output(0, ctx->input(0));\n\n    for (int i = 0; i < 10; ++i) {\n      for (int j = 0; j < 50; ++j) {\n        std::shared_ptr<internal::ItemFeatures> item =\n            std::make_shared<internal::ItemFeatures>();\n        GenItemFeatures(item.get());\n        pool->Add(i, j, item);\n      }\n    }\n  }\n\n private:\n  void GenNamedFeature(NamedFeature* nf) {\n    int slot = std::rand() % 1024;\n    nf->set_name(absl::StrCat(\"fc_\", slot));\n    auto* fid_v2_list = nf->mutable_feature()->mutable_fid_v2_list();\n    int num_fids = std::abs(std::rand() % 20) + 1;\n    for (int i = 0; i < num_fids; ++i) {\n      uint64_t fid = ((uint64_t)slot << 48) | ((std::rand() % 100000) & MASK);\n      fid_v2_list->add_value(fid);\n    }\n  }\n\n  void GenItemFeatures(internal::ItemFeatures* item) {\n    int num_feats = std::abs(std::rand() % 20) + 1;\n    for (int i = 0; i < num_feats; ++i) {\n      NamedFeature nf;\n      GenNamedFeature(&nf);\n      if (!item->example_features.contains(nf.name())) {\n        item->example_features.insert({nf.name(), nf});\n      }\n    }\n  }\n};\n\n// for test only\nclass ItemPoolCheckOp : public OpKernel {\n public:\n  explicit ItemPoolCheckOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"model_path\", &model_path_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"nshards\", &nshards_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"buffer_size\", &buffer_size_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    ItemPoolResource* pool;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &pool));\n    core::ScopedUnref unref(pool);\n    ctx->set_output(0, ctx->input(0));\n    const Tensor& global_step_tensor = ctx->input(1);\n    global_step_ = global_step_tensor.scalar<int64>()();\n\n    ItemPoolResource pool2(pool->max_item_num_per_channel(), pool->start_num());\n    for (int idx = 0; idx < nshards_; ++idx) {\n      std::string filename = GetRestoreFileName(ctx, idx);\n      if (!filename.empty()) {\n        std::unique_ptr<RandomAccessFile> istream;\n        OP_REQUIRES_OK(ctx,\n                       ctx->env()->NewRandomAccessFile(filename, &istream));\n        OP_REQUIRES_OK(ctx, pool2.Restore(istream.get(), buffer_size_));\n      }\n    }\n\n    if (!pool->Equal(pool2)) {\n      LOG(INFO) << \"resotre not equal~ ...\";\n    } else {\n      LOG(INFO) << \"resotre equal~ ...\";\n    }\n  }\n\n private:\n  std::string model_path_;\n  int64 buffer_size_;\n  int nshards_;\n  int64 global_step_;\n\n  std::string GetRestoreFileName(OpKernelContext* ctx, int shard_index) {\n    int index, worker_num;\n    get_index_and_worker_num(&index, &worker_num);\n    std::vector<std::string> files;\n\n    Status s = ctx->env()->GetMatchingPaths(\n        absl::StrCat(model_path_, \"/model.ckpt-\", global_step_, \"_\",\n                     FILE_NAME_PREFIX, \"*\"),\n        &files);\n    if (!s.ok()) {\n      LOG(INFO) << \"GetMatchingPaths Error: \" << s;\n      return \"\";\n    }\n\n    int last_worker_num = 1;\n    int64 mtime_nsec = 0;\n    for (const auto& file : files) {\n      FileStatistics stat;\n      ctx->env()->Stat(file, &stat);\n      if (mtime_nsec < stat.mtime_nsec) {\n        std::vector<absl::string_view> items = absl::StrSplit(file, \"_\");\n        absl::SimpleAtoi(items.back(), &last_worker_num);\n        mtime_nsec = stat.mtime_nsec;\n      }\n    }\n\n    if (files.size() > 0) {\n      // {model_path}/item_pool_{index}_{worker_num}\n      return absl::StrCat(model_path_, \"/model.ckpt-\", global_step_, \"_\",\n                          FILE_NAME_PREFIX, index % last_worker_num, \"_\",\n                          shard_index, \"_\", last_worker_num);\n    } else {\n      return \"\";\n    }\n  }\n};\n\nclass ItemPoolSaveOp : public AsyncOpKernel {\n public:\n  explicit ItemPoolSaveOp(OpKernelConstruction* ctx) : AsyncOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"model_path\", &model_path_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"nshards\", &nshards_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"random_sleep_ms\", &random_sleep_ms_));\n  }\n\n  void ComputeAsync(OpKernelContext* ctx, DoneCallback done) override {\n    ItemPoolResource* pool;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &pool));\n    core::ScopedUnref unref(pool);\n    const Tensor& global_step_tensor = ctx->input(1);\n    global_step_ = global_step_tensor.scalar<int64>()();\n\n    ctx->set_output(0, ctx->input(0));\n    if (!ctx->env()->FileExists(model_path_).ok()) {\n      OP_REQUIRES_OK_ASYNC(ctx, ctx->env()->RecursivelyCreateDir(model_path_),\n                           done);\n    }\n    // add multi-thread\n    auto pack = new AsyncPack(ctx, pool, std::move(done), nshards_);\n    for (int i = 0; i < nshards_; ++i) {\n      ctx->device()->tensorflow_cpu_worker_threads()->workers->Schedule(\n          [this, pack, i] { WorkerThread(i, pack); });\n    }\n  }\n\n private:\n  std::string model_path_;\n  int64 global_step_;\n  int nshards_;\n  int64 random_sleep_ms_;\n\n  std::string GetSaveFileName(int shard_index) {\n    int index, worker_num;\n    get_index_and_worker_num(&index, &worker_num);\n    return absl::StrCat(model_path_, \"/model.ckpt-\", global_step_, \"_\",\n                        FILE_NAME_PREFIX, index, \"_\", shard_index, \"_\",\n                        worker_num);\n  }\n\n  void WorkerThread(int shard_index, AsyncPack* p) {\n    absl::BitGen bitgen;\n    p->status[shard_index] = SaveOneShard(shard_index, p);\n    if (p->finish_num.fetch_add(1) == p->thread_num - 1) {\n      Cleanup(p);\n    }\n  }\n\n  Status SaveOneShard(int shard_index, AsyncPack* p) {\n    std::string filename = GetSaveFileName(shard_index);\n    std::string tmp_filename = absl::StrCat(filename, \"_tmp\");\n    std::unique_ptr<WritableFile> ostream;\n    TF_RETURN_IF_ERROR(p->ctx->env()->NewWritableFile(tmp_filename, &ostream));\n    TF_RETURN_IF_ERROR(\n        p->pool->Save(ostream.get(), shard_index, p->thread_num));\n    TF_RETURN_IF_ERROR(ostream->Close());\n    if (p->ctx->env()->FileExists(filename).ok()) {\n      TF_RETURN_IF_ERROR(\n          p->ctx->env()->RenameFile(filename, absl::StrCat(filename, \"_old\")));\n      TF_RETURN_IF_ERROR(p->ctx->env()->RenameFile(tmp_filename, filename));\n      TF_RETURN_IF_ERROR(\n          p->ctx->env()->DeleteFile(absl::StrCat(filename, \"_old\")));\n    } else {\n      TF_RETURN_IF_ERROR(p->ctx->env()->RenameFile(tmp_filename, filename));\n    }\n\n    return Status::OK();\n  }\n\n  // Clean up when all shards are done.\n  void Cleanup(AsyncPack* p) {\n    auto done = [p]() {\n      // We want to delete p first and then call done.\n      auto done = std::move(p->done);\n      delete p;\n      done();\n    };\n    for (int i = 0; i < p->thread_num; ++i) {\n      OP_REQUIRES_OK_ASYNC(p->ctx, p->status[i], done);\n    }\n    done();\n  }\n};\n\nclass ItemPoolRestoreOp : public AsyncOpKernel {\n public:\n  explicit ItemPoolRestoreOp(OpKernelConstruction* ctx) : AsyncOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"model_path\", &model_path_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"buffer_size\", &buffer_size_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"nshards\", &nshards_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"random_sleep_ms\", &random_sleep_ms_));\n  }\n\n  void ComputeAsync(OpKernelContext* ctx, DoneCallback done) override {\n    ItemPoolResource* pool;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &pool));\n    core::ScopedUnref unref(pool);\n    const Tensor& global_step_tensor = ctx->input(1);\n    global_step_ = global_step_tensor.scalar<int64>()();\n\n    ctx->set_output(0, ctx->input(0));\n    auto pack = new AsyncPack(ctx, pool, std::move(done), nshards_);\n    for (int i = 0; i < nshards_; i++) {\n      ctx->device()->tensorflow_cpu_worker_threads()->workers->Schedule(\n          [this, pack, i] { WorkerThread(i, pack); });\n    }\n  }\n\n private:\n  std::string model_path_;\n  int64 global_step_;\n  int64 buffer_size_;\n  int nshards_;\n  int64 random_sleep_ms_;\n\n  void WorkerThread(int shard_index, AsyncPack* p) {\n    absl::BitGen bitgen;\n    p->status[shard_index] = RestoreOneShard(shard_index, p);\n    if (p->finish_num.fetch_add(1) == p->thread_num - 1) {\n      Cleanup(p);\n    }\n  }\n\n  Status RestoreOneShard(int shard_index, AsyncPack* p) {\n    std::string filename = GetRestoreFileName(p->ctx, shard_index);\n    if (filename == \"\") {\n      LOG(INFO) << \"Cannot find file to restore, skip!\";\n    } else if (p->ctx->env()->FileExists(filename).ok()) {\n      LOG(INFO) << \"Restoring file: \" << filename;\n      std::unique_ptr<RandomAccessFile> istream;\n      TF_RETURN_IF_ERROR(\n          p->ctx->env()->NewRandomAccessFile(filename, &istream));\n      TF_RETURN_IF_ERROR(p->pool->Restore(istream.get(), buffer_size_));\n    } else {\n      LOG(INFO) << \"File dose not exist: \" << filename;\n    }\n\n    return Status::OK();\n  }\n\n  // Clean up when all shards are done.\n  void Cleanup(AsyncPack* p) {\n    auto done = [p]() {\n      // We want to delete p first and then call done.\n      auto done = std::move(p->done);\n      delete p;\n      done();\n    };\n    for (int i = 0; i < p->thread_num; ++i) {\n      OP_REQUIRES_OK_ASYNC(p->ctx, p->status[i], done);\n    }\n    done();\n  }\n\n  int FindLastNumber(std::vector<std::string> const &files, OpKernelContext* ctx) {\n    // 支持 restore 时的 worker_num 可以和 save 时不同\n    int last_worker_num = 1;\n    for (const auto& file : files) {\n      if (absl::EndsWith(file, \"tmp\")) {\n        LOG(INFO) << \"Files vector contains file with tmp suffix.\";\n        continue;\n      }\n      std::vector<absl::string_view> items = absl::StrSplit(file, \"_\");\n      if (!items.empty() && absl::SimpleAtoi(items.back(), &last_worker_num)) {\n        break;\n      }\n    }\n    return last_worker_num;\n  }\n\n  int FindFuzzyCkptNumber(const std::vector<std::string>& files) {\n    int max_match_step = -1;\n    for (const std::string& file : files) {\n      LOG(INFO) << \"match fuzzy ckpt:\" << file;\n      if (absl::EndsWith(file, \"tmp\")) {\n        LOG(INFO) << \"Files vector contains file with tmp suffix.\";\n        continue;\n      }\n      // file like \"xxx/model.ckpt-25541095_item_pool_28_0_60\"\n      std::vector<absl::string_view> items =\n          absl::StrSplit(file, absl::ByAnyChar(\"-_\"));\n      CHECK_GT(items.size(), 6) << absl::StrFormat(\n          \"item_pool ckpt's filepath is not correct: %s\", file);\n      int global_step = -1;\n      CHECK(absl::SimpleAtoi(items.at(items.size() - 6), &global_step));\n      if (global_step > max_match_step) {\n        max_match_step = global_step;\n      }\n    }\n    return max_match_step;\n  }\n\n  std::string GetRestoreFileName(OpKernelContext* ctx, int shard_index) {\n    int index, worker_num;\n    get_index_and_worker_num(&index, &worker_num);\n    std::vector<std::string> files_new;\n    std::vector<std::string> files_old;\n\n    // the global step of chief's item_pool ckpt is correct\n    Status s_new = ctx->env()->GetMatchingPaths(\n        absl::StrCat(model_path_, \"/model.ckpt-\", global_step_, \"_\",\n                     FILE_NAME_PREFIX, \"*\"),\n        &files_new);\n\n    if (s_new.ok() && !files_new.empty()) {\n      int last_save_worker_num = FindLastNumber(files_new, ctx);\n      LOG(INFO) << \"last worker num is: \" << last_save_worker_num;\n      std::vector<std::string> files_fuzzy;\n      std::string fuzzy_matching_path =\n          absl::StrCat(model_path_, \"/model.ckpt-\", \"*\", \"_\", FILE_NAME_PREFIX,\n                       index % last_save_worker_num, \"_\", shard_index, \"_\",\n                       last_save_worker_num);\n      Status fuzzy_match =\n          ctx->env()->GetMatchingPaths(fuzzy_matching_path, &files_fuzzy);\n      if (fuzzy_match.ok() && !files_fuzzy.empty()) {\n        int ckpt_num = FindFuzzyCkptNumber(files_fuzzy);\n        if (ckpt_num <= global_step_) {\n          return absl::StrCat(model_path_, \"/model.ckpt-\", ckpt_num, \"_\",\n                              FILE_NAME_PREFIX, index % last_save_worker_num,\n                              \"_\", shard_index, \"_\", last_save_worker_num);\n        } else {\n          LOG(INFO) << absl::StrFormat(\n              \"step not match: fuzzy match step is %d, target global step is \"\n              \"%d\",\n              ckpt_num, global_step_);\n        }\n      } else {\n        LOG(INFO) << absl::StrFormat(\"path not match: %s\", fuzzy_matching_path);\n      }\n    }\n\n    Status s_old = ctx->env()->GetMatchingPaths(\n        absl::StrCat(model_path_, \"/\", FILE_NAME_PREFIX, \"*\"), &files_old);\n    if (s_old.ok() && !files_old.empty()) {\n      LOG(INFO) << \"old version files > 0\";\n      int last_worker_num = FindLastNumber(files_old, ctx);\n      LOG(INFO) << \"last worker num is: \" << last_worker_num;\n      return absl::StrCat(model_path_, \"/\", FILE_NAME_PREFIX,\n                          index % last_worker_num, \"_\", shard_index, \"_\",\n                          last_worker_num);\n    }\n\n    LOG(INFO) << \"GetMatchingPaths Error: [new] \" << s_new << \" and [old] \"\n              << s_old;\n    return \"\";\n  }\n};\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"ItemPoolCreate\").Device(DEVICE_CPU),\n                        ItemPoolCreateOp);\n\n// for test only\nREGISTER_KERNEL_BUILDER(Name(\"ItemPoolCheck\").Device(DEVICE_CPU),\n                        ItemPoolCheckOp);\n\n// for test only\nREGISTER_KERNEL_BUILDER(Name(\"ItemPoolRandomFill\").Device(DEVICE_CPU),\n                        ItemPoolRandomFillOp);\n\nREGISTER_KERNEL_BUILDER(Name(\"ItemPoolSave\").Device(DEVICE_CPU),\n                        ItemPoolSaveOp);\n\nREGISTER_KERNEL_BUILDER(Name(\"ItemPoolRestore\").Device(DEVICE_CPU),\n                        ItemPoolRestoreOp);\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/item_pool_kernels.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_KERNELS_ITEM_POOL_KERNELS_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_KERNELS_ITEM_POOL_KERNELS_H_\n\n#include \"monolith/native_training/data/kernels/internal/cache_mgr.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nclass ItemPoolResource : public ResourceBase {\n public:\n  explicit ItemPoolResource(int max_item_num_per_channel, int start_num = 0);\n\n  std::string DebugString() const override { return \"ItemPoolResource\"; }\n\n  Status Add(uint64_t channel_id, uint64_t item_id,\n             const std::shared_ptr<const internal::ItemFeatures>& item);\n\n  std::shared_ptr<const internal::ItemFeatures> Sample(uint64_t channel_id,\n                                                       double* freq_factor,\n                                                       double* time_factor);\n\n  Status Save(WritableFile* ostream, int shard_index, int shard_num);\n\n  Status Restore(RandomAccessFile* istream, int64 buffer_size);\n\n  inline int start_num() { return start_num_; }\n  inline int max_item_num_per_channel() { return max_item_num_per_channel_; }\n  bool Equal(const ItemPoolResource& other) const;\n  void SampleChannelID(uint64_t* channel_id);\n\n private:\n  absl::Mutex mu_;\n  int start_num_, max_item_num_per_channel_;\n  std::unique_ptr<internal::CacheManager> cache_ ABSL_GUARDED_BY(mu_);\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_KERNELS_ITEM_POOL_KERNELS_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/kafka_kernels.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"rdkafkacpp.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\n#include \"monolith/native_training/data/kernels/feature_name_mapper_tf_bridge.h\"\n#include \"monolith/native_training/data/training_instance/cc/data_reader.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\nusing Example = ::monolith::io::proto::Example;\nusing ExampleBatch = ::monolith::io::proto::ExampleBatch;\nusing Instance = ::parser::proto::Instance;\nusing ::tensorflow::monolith_tf::BaseStreamReader;\nusing ::tensorflow::monolith_tf::DataFormatOptions;\nusing ::tensorflow::monolith_tf::ExampleBatchIterator;\nusing ::tensorflow::monolith_tf::ExampleToInstance;\nusing ::tensorflow::monolith_tf::FeatureNameMapper;\nusing ::tensorflow::monolith_tf::FeatureNameMapperTfBridge;\nusing ::tensorflow::monolith_tf::FeaturePruningType;\nusing ::tensorflow::monolith_tf::FileStreamReader;\nusing ::tensorflow::monolith_tf::InstanceToExample;\nusing ::tensorflow::monolith_tf::PBIterator;\nusing ::tensorflow::monolith_tf::StdinStreamReader;\n\n}  // namespace\n\nclass KafkaEventCb : public RdKafka::EventCb {\n public:\n  KafkaEventCb() : run_(true) {}\n\n  bool run() { return run_; }\n\n  void event_cb(RdKafka::Event& event) {\n    switch (event.type()) {\n      case RdKafka::Event::EVENT_ERROR:\n        LOG(ERROR) << \"EVENT_ERROR: \"\n                   << \"(\" << RdKafka::err2str(event.err())\n                   << \"): \" << event.str();\n        { run_ = !event.fatal(); }\n        break;\n      case RdKafka::Event::EVENT_STATS:\n        LOG(ERROR) << \"EVENT_STATS: \" << event.str();\n        break;\n      case RdKafka::Event::EVENT_LOG:\n        LOG(ERROR) << \"EVENT_LOG: \" << event.severity() << \"-\"\n                   << event.fac().c_str() << \"-\" << event.str().c_str();\n        break;\n      case RdKafka::Event::EVENT_THROTTLE:\n        LOG(ERROR) << \"EVENT_THROTTLE: \" << event.throttle_time() << \"ms by \"\n                   << event.broker_name() << \" id \"\n                   << static_cast<int>(event.broker_id());\n        break;\n      default:\n        LOG(ERROR) << \"EVENT: \" << event.type() << \" (\"\n                   << RdKafka::err2str(event.err()) << \"): \" << event.str();\n        break;\n    }\n  }\n\n private:\n  mutable mutex mu_;\n  bool run_ TF_GUARDED_BY(mu_) = true;\n};\n\nstatic int64 partition_count = 0;\nstatic int64 eof_count = 0;\nclass KafkaRebalanceCb : public RdKafka::RebalanceCb {\n public:\n  KafkaRebalanceCb() : run_(true) {}\n\n  bool run() { return run_; }\n\n  void rebalance_cb(RdKafka::KafkaConsumer* consumer, RdKafka::ErrorCode err,\n                    std::vector<RdKafka::TopicPartition*>& partitions) {\n    LOG(ERROR) << \"REBALANCE: \" << RdKafka::err2str(err);\n    int timeout = 5000;  // milliseconds\n    LOG(ERROR) << \"Retrieved committed offsets with status code: \"\n               << consumer->committed(partitions, timeout);\n\n    for (int partition = 0; partition < partitions.size(); partition++) {\n      // OFFSET MAPPINGS:\n      //\n      // RD_KAFKA_OFFSET_BEGINNING      -2\n      // RD_KAFKA_OFFSET_END            -1\n      // RD_KAFKA_OFFSET_STORED         -1000\n      // RD_KAFKA_OFFSET_INVALID        -1001\n\n      LOG(INFO) << \"REBALANCE: \" << partitions[partition]->topic() << \"[\"\n                << partitions[partition]->partition() << \"], \"\n                << \"OFFSET: \" << partitions[partition]->offset() << \" \"\n                << \"ERROR_CODE: \" << partitions[partition]->err();\n    }\n    if (err == RdKafka::ERR__ASSIGN_PARTITIONS) {\n      // librdkafka does not actually look up the stored offsets before\n      // calling your rebalance callback, the partition offsets are set to\n      // RD_KAFKA_OFFSET_INVALID at this point to allow us to change it to use\n      // some sort of external offset store. But calling assign() with offset\n      // RD_KAFKA_OFFSET_INVALID will cause librdkafka to look up the stored\n      // offset on the broker.\n      // If there was no stored offset it will fall back to `auto.offset.reset`\n      // configuration parameter.\n\n      LOG(INFO) << \"REBALANCE: Assigning partitions\";\n      consumer->assign(partitions);\n      partition_count = static_cast<int>(partitions.size());\n    } else {\n      LOG(INFO) << \"REBALANCE: Unassigning partitions\";\n      consumer->unassign();\n      partition_count = 0;\n    }\n    eof_count = 0;\n  }\n\n private:\n  mutable mutex mu_;\n  bool run_ TF_GUARDED_BY(mu_) = true;\n};\n\nclass KafkaGroupReadableResource : public ResourceBase {\n public:\n  explicit KafkaGroupReadableResource(Env* env) : env_(env) {}\n  virtual ~KafkaGroupReadableResource() {\n    if (consumer_.get()) {\n      consumer_->unassign();\n      consumer_->close();\n      consumer_.reset(nullptr);\n    }\n  }\n\n  virtual Status Init(const std::vector<std::string>& topics,\n                      const std::vector<std::string>& metadata,\n                      const DataFormatOptions& options,\n                      const std::string& input_pb_type,\n                      const std::string& output_pb_type) {\n    mutex_lock l(mu_);\n\n    std::unique_ptr<RdKafka::Conf> conf(\n        RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL));\n    std::unique_ptr<RdKafka::Conf> conf_topic(\n        RdKafka::Conf::create(RdKafka::Conf::CONF_TOPIC));\n\n    string errstr;\n    RdKafka::Conf::ConfResult result = RdKafka::Conf::CONF_UNKNOWN;\n\n    // The default kafka topic configurations are set first before\n    // setting the global confs\n    for (size_t i = 0; i < metadata.size(); i++) {\n      if (metadata[i].find(\"conf.topic.\") == 0) {\n        std::vector<string> parts = str_util::Split(metadata[i], \"=\");\n        if (parts.size() != 2) {\n          return errors::InvalidArgument(\"invalid topic configuration: \",\n                                         metadata[i]);\n        }\n        result = conf_topic->set(parts[0].substr(11), parts[1], errstr);\n        if (result != RdKafka::Conf::CONF_OK) {\n          return errors::Internal(\"failed to do topic configuration:\",\n                                  metadata[i], \"error:\", errstr);\n        }\n        LOG(INFO) << \"Kafka configuration: \" << metadata[i];\n      }\n    }\n    if ((result = conf->set(\"default_topic_conf\", conf_topic.get(), errstr)) !=\n        RdKafka::Conf::CONF_OK) {\n      return errors::Internal(\"failed to set default_topic_conf:\", errstr);\n    }\n\n    // Once the `default_topic_conf` is set, the global confs can now be set\n    // without any risk of being overwritten.\n    // Setting the global confs before setting the `default_topic_conf`\n    // results in erratic behaviour.\n    for (size_t i = 0; i < metadata.size(); i++) {\n      if (metadata[i] != \"\" && metadata[i].find(\"conf.\") == string::npos) {\n        std::vector<string> parts = str_util::Split(metadata[i], \"=\");\n        if (parts.size() != 2) {\n          return errors::InvalidArgument(\"invalid topic configuration: \",\n                                         metadata[i]);\n        }\n        if ((result = conf->set(parts[0], parts[1], errstr)) !=\n            RdKafka::Conf::CONF_OK) {\n          return errors::Internal(\"failed to do global configuration: \",\n                                  metadata[i], \"error:\", errstr);\n        }\n        LOG(INFO) << \"Kafka configuration: \" << metadata[i];\n      }\n    }\n\n    // default consumer.properties:\n    //   bootstrap.servers=localhost:9092\n    //   group.id=test-consumer-group\n\n    string bootstrap_servers;\n    if ((result = conf->get(\"bootstrap.servers\", bootstrap_servers)) !=\n        RdKafka::Conf::CONF_OK) {\n      bootstrap_servers = \"localhost:9092\";\n      if ((result = conf->set(\"bootstrap.servers\", bootstrap_servers,\n                              errstr)) != RdKafka::Conf::CONF_OK) {\n        return errors::Internal(\"failed to set bootstrap.servers [\",\n                                bootstrap_servers, \"]:\", errstr);\n      }\n    }\n    string group_id;\n    if ((result = conf->get(\"group.id\", group_id)) != RdKafka::Conf::CONF_OK) {\n      group_id = \"test-consumer-group\";\n      if ((result = conf->set(\"group.id\", group_id, errstr)) !=\n          RdKafka::Conf::CONF_OK) {\n        return errors::Internal(\"failed to set group.id [\", group_id,\n                                \"]:\", errstr);\n      }\n    }\n\n    // Always set enable.partition.eof=true\n    if ((result = conf->set(\"enable.partition.eof\", \"true\", errstr)) !=\n        RdKafka::Conf::CONF_OK) {\n      return errors::Internal(\"Failed to set enable.partition.eof=true :\",\n                              errstr);\n    }\n\n    if ((result = conf->set(\"event_cb\", &kafka_event_cb_, errstr)) !=\n        RdKafka::Conf::CONF_OK) {\n      return errors::Internal(\"failed to set event_cb:\", errstr);\n    }\n\n    if ((result = conf->set(\"rebalance_cb\", &kafka_rebalance_cb_, errstr)) !=\n        RdKafka::Conf::CONF_OK) {\n      return errors::Internal(\"failed to set rebalance_cb:\", errstr);\n    }\n\n    // set max.poll.records configuration\n    std::string batch_num_messages;\n    if ((result = conf->get(\"batch.num.messages\", batch_num_messages)) !=\n        RdKafka::Conf::CONF_OK) {\n      batch_num_messages = \"1024\";\n      if ((result = conf->set(\"batch.num.messages\", batch_num_messages,\n                              errstr)) != RdKafka::Conf::CONF_OK) {\n        return errors::Internal(\"failed to set batch.num.messages [\",\n                                batch_num_messages, \"]:\", errstr);\n      }\n    }\n    sscanf(batch_num_messages.c_str(), \"%d\", &batch_num_messages_);\n    LOG(INFO) << \"max num of messages per batch: \" << batch_num_messages_;\n\n    LOG(INFO) << \"Creating the kafka consumer\";\n    consumer_.reset(RdKafka::KafkaConsumer::create(conf.get(), errstr));\n    if (!consumer_.get()) {\n      return errors::Internal(\"failed to create consumer:\", errstr);\n    }\n\n    for (int i = 0; i < topics.size(); i++) {\n      LOG(INFO) << \"Subscribing to the kafka topic: \" << topics[i];\n    }\n    RdKafka::ErrorCode err = consumer_->subscribe(topics);\n    if (err != RdKafka::ERR_NO_ERROR) {\n      return errors::Internal(\"failed to subscribe to topics: \",\n                              RdKafka::err2str(err));\n    }\n\n    if (input_pb_type == \"\" && output_pb_type == \"\") {\n      version_ = 1;\n    } else {\n      input_pb_type_ = data_format::StringToDataFormat(input_pb_type);\n      output_pb_type_ = data_format::StringToDataFormat(output_pb_type);\n      if (input_pb_type_ == data_format::UNKNOW ||\n          output_pb_type_ == data_format::UNKNOW) {\n        return errors::Internal(\"input_pb_type or output_pb_type err:\",\n                                input_pb_type, output_pb_type);\n      }\n      version_ = 2;\n    }\n\n    options_ = options;\n    return Status::OK();\n  }\n\n  class CurPBIteratorHandler {\n   public:\n    struct CurOutput : public PBIteratorWithDataFormatTransBaseOutput {\n      std::vector<Example> exa_pb_list;\n      std::vector<Instance> ins_pb_list;\n      std::vector<ExampleBatch> eb_pb_list;\n      std::vector<tstring> string_list;\n    };\n\n    Status HandleReaderNextStauts(const Status& s, const tstring& result) {\n      if (s != Status::OK()) {\n        if (s.code() != error::OUT_OF_RANGE) {\n          LOG(ERROR) << \"pb parse error:\" << s;\n        }\n        return s;\n      }\n      if (result.size() == 0) {\n        LOG(ERROR) << \"tstring size can not be 0\";\n        return errors::FailedPrecondition(\"tstring size=0\");\n      }\n      return Status::OK();\n    }\n\n    template <class TResult>\n    Status HandleReaderNextStauts(const Status& s, const TResult& result) {\n      if (s != Status::OK()) {\n        if (s.code() != error::OUT_OF_RANGE) {\n          LOG(ERROR) << \"pb parse error:\" << s;\n        }\n        return s;\n      }\n\n      if (result.ByteSize() == 0) {\n        LOG(ERROR) << \"pb struct size can not be 0\";\n        return errors::FailedPrecondition(\"pb size=0\");\n      }\n\n      return Status::OK();\n    }\n\n    template <class TResult>\n    Status HandleResult(TResult&& result, CurOutput* output) {\n      return errors::Unimplemented(\"not implement\");\n    }\n\n    Status HandleResult(tstring&& serialized, CurOutput* output) {\n      output->string_list.emplace_back(std::move(serialized));\n      return Status::OK();\n    }\n\n    virtual Status HandleResult(Example&& exa_pb, CurOutput* output) {\n      output->exa_pb_list.emplace_back(std::move(exa_pb));\n      return Status::OK();\n    }\n\n    virtual Status HandleResult(Instance&& ins_pb, CurOutput* output) {\n      output->ins_pb_list.emplace_back(std::move(ins_pb));\n      return Status::OK();\n    }\n\n    virtual Status HandleResult(ExampleBatch&& eb_pb, CurOutput* output) {\n      output->eb_pb_list.emplace_back(std::move(eb_pb));\n      return Status::OK();\n    }\n  };\n\n  Status Next(const int64 index, const int64 message_poll_timeout,\n              const int64 stream_timeout,\n              std::function<Status(const TensorShape& shape, Tensor** message,\n                                   Tensor** key, Tensor** continue_fetch)>\n                  allocate_func) {\n    mutex_lock l(mu_);\n\n    // Initialize necessary variables\n    int64 num_messages = 0;\n    max_stream_timeout_polls_ = stream_timeout / message_poll_timeout;\n\n    // Allocate memory for message_value and key_value vectors\n    std::vector<tstring> message_value, key_value;\n    message_value.reserve(batch_num_messages_);\n    // key_value.reserve(batch_num_messages_);\n\n    std::unique_ptr<RdKafka::Message> message;\n    while (consumer_.get() != nullptr && num_messages < batch_num_messages_) {\n      if (!kafka_event_cb_.run()) {\n        return errors::Internal(\n            \"failed to consume messages due to broker issue\");\n      }\n      message.reset(consumer_->consume(message_poll_timeout));\n      if (message->err() == RdKafka::ERR_NO_ERROR) {\n        // Produce the line as output.\n        message_value.emplace_back(tstring(\n            static_cast<const char*>(message->payload()), message->len()));\n        // key_value.emplace_back(\n        //     (message->key() != nullptr) ? tstring(*message->key()) : \"\");\n        num_messages++;\n        // Once a message has been successfully retrieved, the\n        // `stream_timeout_polls_` is reset to 0. This allows the dataset\n        // to wait for the entire `stream_timeout` duration when a data\n        // slump occurs in the future.\n        stream_timeout_polls_ = 0;\n      } else if (message->err() == RdKafka::ERR__TRANSPORT) {\n        // Not returning an error here as the consumer will try to re-connect.\n        LOG(ERROR) << \"Broker transport failure: \" << message->errstr();\n\n      } else if (message->err() == RdKafka::ERR__PARTITION_EOF) {\n        if (++eof_count == partition_count) {\n          LOG(INFO) << \"EOF reached for all \" << partition_count\n                    << \" partition(s)\";\n          break;\n        }\n      } else if (message->err() == RdKafka::ERR__TIMED_OUT) {\n        LOG(ERROR) << message->errstr();\n        stream_timeout_polls_++;\n        break;\n      } else {\n        LOG(ERROR) << \"ERROR Code \" << message->err() << \", errstr is \"\n                   << message->errstr();\n      }\n    }\n\n    // Prepare the outputs\n\n    PBIteratorWithDataFormatTrans<CurPBIteratorHandler> cur_iter(\n        input_pb_type_, output_pb_type_);\n    CurPBIteratorHandler::CurOutput output;\n    if (version_ == 1) {\n      output.string_list.swap(message_value);\n    } else {\n      // std::ostringstream imploded;\n      // std::copy(message_value.begin(), message_value.end(),\n      //           std::ostream_iterator<std::string>(imploded, \"\"));\n      //  std::string msg;\n      std::unique_ptr<PBIterator> reader;\n      for (auto& mesg : message_value) {\n        auto stream_reader =\n            std::make_unique<StringStreamReader<tstring> >(options_, mesg);\n        if (input_pb_type_ == data_format::INSTANCE ||\n            input_pb_type_ == data_format::EXAMPLE) {\n          reader = absl::make_unique<PBIterator>(\n              std::move(stream_reader),\n              FeaturePruningType::PRUNING_RAW_FEATURE);\n        } else {\n          reader = absl::make_unique<ExampleBatchIterator>(\n              std::move(stream_reader), FeaturePruningType::PRUNING_RAW_FEATURE,\n              &fake_mapper_);\n        }\n\n        uint64 offset_ = 0;\n        while (true) {\n          Status s = cur_iter.GetNext(reader.get(), &output, &offset_);\n          if (!s.ok()) break;\n          offset_ = reader->GetOffset();\n        }\n      }\n    }\n    size_t all_size = 0;\n    if (output_pb_type_ == data_format::EXAMPLE) {\n      all_size = output.exa_pb_list.size();\n    } else if (output_pb_type_ == data_format::EXAMPLEBATCH) {\n      all_size = output.eb_pb_list.size();\n    } else if (output_pb_type_ == data_format::INSTANCE) {\n      all_size = output.ins_pb_list.size();\n    } else {\n      all_size = output.string_list.size();\n    }\n    if (all_size < message_value.size()) {\n      LOG(ERROR) << \"get not enough pb:\" << all_size << \",\"\n                 << message_value.size();\n    }\n    TensorShape shape({static_cast<int64>(all_size)});\n    Tensor* message_tensor;\n    Tensor* key_tensor;\n    Tensor* continue_fetch_tensor;\n    TF_RETURN_IF_ERROR(allocate_func(shape, &message_tensor, &key_tensor,\n                                     &continue_fetch_tensor));\n\n    for (int i = 0; i < all_size; ++i) {\n      if (output_pb_type_ == data_format::EXAMPLE) {\n        message_tensor->flat<Variant>()(i) = std::move(output.exa_pb_list[i]);\n      } else if (output_pb_type_ == data_format::INSTANCE) {\n        message_tensor->flat<Variant>()(i) = std::move(output.ins_pb_list[i]);\n      } else if (output_pb_type_ == data_format::EXAMPLEBATCH) {\n        message_tensor->flat<Variant>()(i) = std::move(output.eb_pb_list[i]);\n      } else {\n        message_tensor->flat<tstring>()(i) = std::move(output.string_list[i]);\n      }\n    }\n    if (stream_timeout_polls_ < max_stream_timeout_polls_) {\n      continue_fetch_tensor->scalar<int64>()() = 1;\n    } else {\n      continue_fetch_tensor->scalar<int64>()() = 0;\n    }\n    LOG_EVERY_N_SEC(INFO, 60)\n        << \"consumer pb:\" << all_size << \",\" << message_value.size();\n    return Status::OK();\n  }\n\n  string DebugString() const override { return \"KafkaBaseResource\"; }\n\n  mutable mutex mu_;\n  Env* env_ TF_GUARDED_BY(mu_);\n  std::unique_ptr<RdKafka::KafkaConsumer> consumer_ TF_GUARDED_BY(mu_);\n  KafkaEventCb kafka_event_cb_ = KafkaEventCb();\n  KafkaRebalanceCb kafka_rebalance_cb_ = KafkaRebalanceCb();\n  int64 max_stream_timeout_polls_ = -1;\n  int64 stream_timeout_polls_ = -1;\n  int batch_num_messages_ = 1024;\n  // std::unique_ptr<PBIterator> reader_;\n  // std::unique_ptr<BaseStreamReader> stream_reader_;\n  data_format::DataFormat output_pb_type_;\n  data_format::DataFormat input_pb_type_;\n  DataFormatOptions options_;\n  FeatureNameMapper fake_mapper_;\n  int version_ = 1;\n};\n\nclass KafkaGroupReadableInitOp\n    : public ResourceOpKernel<KafkaGroupReadableResource> {\n public:\n  explicit KafkaGroupReadableInitOp(OpKernelConstruction* context)\n      : ResourceOpKernel<KafkaGroupReadableResource>(context) {\n    env_ = context->env();\n\n    OP_REQUIRES_OK(context, context->GetAttr(\"lagrangex_header\",\n                                             &options_.lagrangex_header));\n    OP_REQUIRES_OK(context, context->GetAttr(\"kafka_dump_prefix\",\n                                             &options_.kafka_dump_prefix));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"has_sort_id\", &options_.has_sort_id));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"kafka_dump\", &options_.kafka_dump));\n    std::string input_pb_type, output_pb_type;\n    OP_REQUIRES_OK(context, context->GetAttr(\"input_pb_type\", &input_pb_type_));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"output_pb_type\", &output_pb_type_));\n  }\n\n private:\n  void Compute(OpKernelContext* context) override {\n    ResourceOpKernel<KafkaGroupReadableResource>::Compute(context);\n\n    const Tensor* topics_tensor;\n    OP_REQUIRES_OK(context, context->input(\"topics\", &topics_tensor));\n    std::vector<string> topics;\n    for (int64 i = 0; i < topics_tensor->NumElements(); i++) {\n      topics.push_back(topics_tensor->flat<tstring>()(i));\n    }\n\n    const Tensor* metadata_tensor;\n    OP_REQUIRES_OK(context, context->input(\"metadata\", &metadata_tensor));\n    std::vector<string> metadata;\n    for (int64 i = 0; i < metadata_tensor->NumElements(); i++) {\n      metadata.push_back(metadata_tensor->flat<tstring>()(i));\n    }\n\n    OP_REQUIRES_OK(context, resource_->Init(topics, metadata, options_,\n                                            input_pb_type_, output_pb_type_));\n  }\n\n  Status CreateResource(KafkaGroupReadableResource** resource)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    *resource = new KafkaGroupReadableResource(env_);\n    return Status::OK();\n  }\n\n private:\n  mutable mutex mu_;\n  Env* env_ TF_GUARDED_BY(mu_);\n  DataFormatOptions options_;\n  std::string output_pb_type_;\n  std::string input_pb_type_;\n};\n\nclass KafkaGroupReadableNextOp : public OpKernel {\n public:\n  explicit KafkaGroupReadableNextOp(OpKernelConstruction* context,\n                                    int version = 1)\n      : OpKernel(context), version_(version) {\n    env_ = context->env();\n  }\n\n  void Compute(OpKernelContext* context) override {\n    KafkaGroupReadableResource* resource;\n    OP_REQUIRES_OK(context,\n                   GetResourceFromContext(context, \"input\", &resource));\n    core::ScopedUnref unref(resource);\n\n    const Tensor* index_tensor;\n    OP_REQUIRES_OK(context, context->input(\"index\", &index_tensor));\n    const int64 index = index_tensor->scalar<int64>()();\n\n    const Tensor* message_poll_timeout_tensor;\n    OP_REQUIRES_OK(context, context->input(\"message_poll_timeout\",\n                                           &message_poll_timeout_tensor));\n    const int64 message_poll_timeout =\n        message_poll_timeout_tensor->scalar<int64>()();\n\n    const Tensor* stream_timeout_tensor;\n    OP_REQUIRES_OK(context,\n                   context->input(\"stream_timeout\", &stream_timeout_tensor));\n    const int64 stream_timeout = stream_timeout_tensor->scalar<int64>()();\n\n    OP_REQUIRES_OK(\n        context,\n        resource->Next(\n            index, message_poll_timeout, stream_timeout,\n            [&](const TensorShape& shape, Tensor** message, Tensor** key,\n                Tensor** continue_fetch) -> Status {\n              TF_RETURN_IF_ERROR(context->allocate_output(0, shape, message));\n              if (version_ == 2) {\n                TF_RETURN_IF_ERROR(context->allocate_output(1, TensorShape({}),\n                                                            continue_fetch));\n              } else {\n                TF_RETURN_IF_ERROR(context->allocate_output(1, shape, key));\n                TF_RETURN_IF_ERROR(context->allocate_output(2, TensorShape({}),\n                                                            continue_fetch));\n              }\n              return Status::OK();\n            }));\n  }\n\n private:\n  int version_ = 1;\n  mutable mutex mu_;\n  Env* env_ TF_GUARDED_BY(mu_);\n};\n\nclass KafkaGroupReadableNextOpV2 : public KafkaGroupReadableNextOp {\n public:\n  explicit KafkaGroupReadableNextOpV2(OpKernelConstruction* context)\n      : KafkaGroupReadableNextOp(context, 2) {}\n};\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"KafkaGroupReadableInit\").Device(DEVICE_CPU),\n                        KafkaGroupReadableInitOp);\n\nREGISTER_KERNEL_BUILDER(Name(\"KafkaGroupReadableNext\").Device(DEVICE_CPU),\n                        KafkaGroupReadableNextOp);\nREGISTER_KERNEL_BUILDER(Name(\"KafkaGroupReadableNextV2\").Device(DEVICE_CPU),\n                        KafkaGroupReadableNextOpV2);\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/label_normalization_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstdio>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing LineId = ::idl::matrix::proto::LineId;\n\n// label_norm:\nclass LabelNormalizationOp : public OpKernel {\n public:\n  explicit LabelNormalizationOp(OpKernelConstruction *context)\n      : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"norm_methods\", &norm_methods_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"norm_values\", &norm_values_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n\n    if (norm_methods_.size() != norm_values_.size()) {\n      LOG(FATAL) << \"Invalid 'norm_methods_', and 'norm_values', the size \"\n                    \"should match each other.!\";\n    }\n\n    if (variant_type_ != \"instance\" && variant_type_ != \"example\") {\n      LOG(FATAL) << \"Invalid 'variant_type', please choose on from \"\n                    \"['instance', 'example']!\";\n    }\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n    bool is_instance = variant_type_ == \"instance\";\n    if (is_instance) {\n      Instance instance;\n      instance.CopyFrom(*input_tensor.scalar<Variant>()().get<Instance>());\n      output_tensor->scalar<Variant>()() = std::move(instance);\n    } else {\n      Example example;\n      example.CopyFrom(*input_tensor.scalar<Variant>()().get<Example>());\n      output_tensor->scalar<Variant>()() = std::move(example);\n    }\n    auto labels = GetLabel(output_tensor, is_instance);\n    for (int i = 0; i < labels->size(); ++i) {\n      for (int j = 0; j < norm_methods_.size(); ++j) {\n        float label = labels->Get(i);\n        const auto &norm_method = norm_methods_[j];\n        const auto &norm_value = norm_values_[j];\n        if (norm_method == \"log\") {\n          label = std::max(label + norm_value, 0.0f);\n          label = log(label);\n        } else if (norm_method == \"scale\") {\n          label /= norm_value;\n        } else if (norm_method == \"scale2int\") {\n          label = int32_t(label / norm_value);\n        } else if (norm_method == \"pow\") {\n          label = std::pow(label + 1, norm_value);\n        } else if (norm_method == \"repow\") {\n          if (label > 0) {\n            label = std::pow(label, norm_value);\n          } else {\n            label = 0;\n          }\n        } else if (norm_method == \"scalelog\") {\n          label /= norm_value;\n          label = log(std::max(label + 1, 0.0f));\n        } else {\n          assert(false && \"illegal label norm params\");\n        }\n        labels->Set(i, label);\n      }\n    }\n  }\n\n private:\n  static ::google::protobuf::RepeatedField<float> *GetLabel(\n      Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_label();\n    } else {\n      return output_tensor->scalar<Variant>()().get<Example>()->mutable_label();\n    }\n  }\n  std::vector<std::string> norm_methods_;\n  std::vector<float> norm_values_;\n  std::string variant_type_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"LabelNormalization\").Device(DEVICE_CPU),\n                        LabelNormalizationOp)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/label_upper_bound_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstdio>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing LineId = ::idl::matrix::proto::LineId;\n\n// label_upper_bound:\nclass LabelUpperBoundOp : public OpKernel {\n public:\n  explicit LabelUpperBoundOp(OpKernelConstruction *context)\n      : OpKernel(context) {\n    OP_REQUIRES_OK(\n        context, context->GetAttr(\"label_upper_bounds\", &label_upper_bounds_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n\n    if (variant_type_ != \"instance\" && variant_type_ != \"example\") {\n      LOG(FATAL) << \"Invalid 'variant_type', please choose on from \"\n                    \"['instance', 'example']!\";\n    }\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n    bool is_instance = variant_type_ == \"instance\";\n    if (is_instance) {\n      Instance instance;\n      instance.CopyFrom(*input_tensor.scalar<Variant>()().get<Instance>());\n      output_tensor->scalar<Variant>()() = std::move(instance);\n    } else {\n      Example example;\n      example.CopyFrom(*input_tensor.scalar<Variant>()().get<Example>());\n      output_tensor->scalar<Variant>()() = std::move(example);\n    }\n    auto labels = GetLabel(output_tensor, is_instance);\n    if (labels->size() < label_upper_bounds_.size()) {\n      LOG_EVERY_N_SEC(ERROR, 60) << absl::StrFormat(\n          \"Label size(=%ld) should be >= label_upper_bounds size(=%ld), please \"\n          \"investigate!\",\n          labels->size(), label_upper_bounds_.size());\n      return;\n    } else {\n      for (size_t i = 0; i < label_upper_bounds_.size(); ++i) {\n        if (labels->Get(i) > label_upper_bounds_[i]) {\n          labels->Set(i, label_upper_bounds_[i]);\n        }\n      }\n    }\n  }\n\n private:\n  static ::google::protobuf::RepeatedField<float> *GetLabel(\n      Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_label();\n    } else {\n      return output_tensor->scalar<Variant>()().get<Example>()->mutable_label();\n    }\n  }\n\n  std::vector<float> label_upper_bounds_;\n  std::string variant_type_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"LabelUpperBound\").Device(DEVICE_CPU),\n                        LabelUpperBoundOp)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/map_id_kernels.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <string>\n#include <vector>\n\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\ntemplate <typename T>\nclass MapIdOp : public OpKernel {\n public:\n  explicit MapIdOp(OpKernelConstruction *context) : OpKernel(context) {\n    std::vector<T> from, to;\n    OP_REQUIRES_OK(context, context->GetAttr(\"from_value\", &from));\n    OP_REQUIRES_OK(context, context->GetAttr(\"to_value\", &to));\n    OP_REQUIRES_OK(context, context->GetAttr(\"default_value\", &default_value_));\n\n    CHECK_EQ(from.size(), to.size());\n    for (size_t i = 0; i < from.size(); ++i) {\n      map_.insert({from[i], to[i]});\n    }\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n    auto input_flat = input_tensor.flat<T>();\n    auto output_flat = output_tensor->flat<T>();\n    for (size_t i = 0; i < input_flat.size(); ++i) {\n      const T &value = input_flat(i);\n      auto iter = map_.find(value);\n      if (iter == map_.end()) {\n        output_flat(i) = default_value_;\n      } else {\n        output_flat(i) = iter->second;\n      }\n    }\n  }\n\n private:\n  std::unordered_map<T, T> map_;\n  T default_value_;\n};\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MapId\").Device(DEVICE_CPU).TypeConstraint<int32>(\"T\"),\n    MapIdOp<int32>);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MapId\").Device(DEVICE_CPU).TypeConstraint<int64>(\"T\"),\n    MapIdOp<int64>);\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/merge_flow_dataset_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"absl/strings/str_cat.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/data/kernels/df_resource_kernel.h\"\n#include \"tensorflow/core/framework/cancellation.h\"\n#include \"tensorflow/core/framework/dataset.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing Item = ::tensorflow::monolith_tf::Item;\nusing QueueResource = ::tensorflow::monolith_tf::QueueResource;\nusing VariantType = ::tensorflow::monolith_tf::VariantType;\n\nclass MergeFlowDatasetOp : public DatasetOpKernel {\n public:\n  static constexpr const char *const kDatasetType = \"merge_dataset\";\n  static constexpr const char *const kDataFlow = \"data_flow\";\n  static constexpr const char *const kMaxQueueSize = \"max_queue_size\";\n  static constexpr const char *const kVariantType = \"variant_type\";\n\n  explicit MergeFlowDatasetOp(OpKernelConstruction *ctx);\n\n protected:\n  void MakeDataset(OpKernelContext *ctx, DatasetBase **output) override;\n\n private:\n  class Dataset;\n  std::vector<std::string> data_flows_;\n  int max_queue_size_;\n  VariantType variant_type_;\n};\n\nclass MergeFlowDatasetOp::Dataset : public DatasetBase {\n public:\n  Dataset(OpKernelContext *ctx, const std::vector<const DatasetBase *> &inputs,\n          const std::vector<std::string> &data_flows, int max_queue_size,\n          const VariantType &variant_type)\n      : DatasetBase(DatasetContext(ctx)),\n        inputs_(inputs),\n        data_flows_(data_flows),\n        max_queue_size_(max_queue_size),\n        variant_type_(variant_type) {\n    for (const auto input : inputs_) {\n      input->Ref();\n    }\n  }\n\n  ~Dataset() override {\n    for (const auto input : inputs_) {\n      input->Unref();\n    }\n  }\n\n  std::unique_ptr<IteratorBase> MakeIteratorInternal(\n      const string &prefix) const override {\n    return absl::make_unique<Iterator>(\n        Iterator::Params{this, strings::StrCat(prefix, \"::\", kDatasetType)});\n  }\n\n  const DataTypeVector &output_dtypes() const override {\n    return inputs_[0]->output_dtypes();\n  }\n\n  const std::vector<PartialTensorShape> &output_shapes() const override {\n    return inputs_[0]->output_shapes();\n  }\n\n  string DebugString() const override {\n    return \"This is the customized Dataset: DataFlowDataset\";\n  }\n\n  Status InputDatasets(\n      std::vector<const DatasetBase *> *inputs) const override {\n    for (const auto input : inputs_) {\n      inputs->push_back(input);\n    }\n    return Status::OK();\n  }\n\n  Status CheckExternalState() const override {\n    for (const auto input : inputs_) {\n      Status s = input->CheckExternalState();\n      if (!s.ok()) {\n        return s;\n      }\n    }\n\n    return Status::OK();\n  }\n\n  void SetContainer(const std::string &container) { container_ = container; }\n\n  std::string GetContainer() const { return container_; }\n\n protected:\n  Status AsGraphDefInternal(SerializationContext *ctx,\n                            DatasetGraphDefBuilder *b,\n                            Node **output) const override {\n    std::vector<Node *> input_graph_nodes;\n    input_graph_nodes.reserve(inputs_.size());\n    for (const auto &input : inputs_) {\n      Node *input_node;\n      TF_RETURN_IF_ERROR(b->AddInputDataset(ctx, input, &input_node));\n      input_graph_nodes.emplace_back(input_node);\n    }\n\n    AttrValue data_flows_node;\n    b->BuildAttrValue(data_flows_, &data_flows_node);\n    AttrValue max_queue_size_node;\n    b->BuildAttrValue(max_queue_size_, &max_queue_size_node);\n\n    AttrValue variant_type_node;\n    if (variant_type_ == VariantType::PBInstance) {\n      b->BuildAttrValue(\"instance\", &variant_type_node);\n    } else {\n      b->BuildAttrValue(\"example\", &variant_type_node);\n    }\n\n    TF_RETURN_IF_ERROR(\n        b->AddDataset(this,                                        // dataset\n                      {}, {std::make_pair(0, input_graph_nodes)},  // inputs\n                      {{kDataFlow, data_flows_node},\n                       {kMaxQueueSize, max_queue_size_node},\n                       {kVariantType, variant_type_node}},  // attrs\n                      output));                             // Node**\n\n    return Status::OK();\n  }\n\n private:\n  class Iterator : public DatasetIterator<Dataset> {\n   public:\n    explicit Iterator(const Params &params)\n        : DatasetIterator<Dataset>(params),\n          mu_(std::make_shared<mutex>()),\n          output_mu_(std::make_shared<mutex>()) {}\n\n    ~Iterator() override {\n      CancelThreads();\n      if (deregister_fn_) deregister_fn_();\n\n      for (const std::string &name : dataset()->data_flows_) {\n        auto iter = df_to_queue_.find(name);\n        if (iter != df_to_queue_.end()) {\n          if (iter->second != nullptr) {\n            delete iter->second;\n          }\n          df_to_queue_.erase(iter);\n        }\n      }\n    }\n\n    void CancelThreads() TF_LOCKS_EXCLUDED(mu_) {\n      cancellation_manager_->StartCancel();\n      mutex_lock l(*mu_);\n      cancelled_ = true;\n    }\n\n    Status Initialize(IteratorContext *ctx) override {\n      mutex_lock l(*mu_);\n      cancellation_manager_ = absl::make_unique<CancellationManager>();\n      IteratorContext::Params params(ctx);\n      params.cancellation_manager = cancellation_manager_.get();\n      TF_RETURN_IF_ERROR(\n          ::tensorflow::monolith_tf::RegisterCancellationCallback(\n              ctx->cancellation_manager(), [this]() { CancelThreads(); },\n              &deregister_fn_));\n\n      Status s = Status::OK();\n      input_impls_.reserve(dataset()->inputs_.size());\n      for (const auto input : dataset()->inputs_) {\n        std::unique_ptr<IteratorBase> input_impl;\n        s.Update(input->MakeIterator(IteratorContext(params), this, prefix(),\n                                     &input_impl));\n        input_impls_.push_back(input_impl.release());\n      }\n\n      for (size_t i = 0; i < dataset()->data_flows_.size(); ++i) {\n        std::string data_flows_name = dataset()->data_flows_[i];\n        QueueResource *queue = new QueueResource(dataset()->max_queue_size_);\n        df_to_queue_.emplace(data_flows_name, queue);\n        prefetch_thread_finished_.push_back(false);\n      }\n\n      return s;\n    }\n\n    Status GetNextInternal(IteratorContext *ctx,\n                           std::vector<Tensor> *out_tensors,\n                           bool *end_of_sequence) override {\n      out_tensors->reserve(1);\n      {\n        mutex_lock l(*mu_);\n        TF_RETURN_IF_ERROR(EnsureThreadStarted(ctx));\n      }\n\n      {\n        mutex_lock output_l(*output_mu_);\n        do {\n          for (size_t i = 0; i < dataset()->data_flows_.size(); ++i) {\n            std::string name = dataset()->data_flows_[cur_];\n            const QueueResource *queue = df_to_queue_[name];\n            cur_ = (cur_ + 1) % dataset()->data_flows_.size();\n            if (queue->Empty()) {\n              continue;\n            }\n\n            Item item = queue->Pop();\n            if (item.end_of_sequence) {\n              out_tensors->clear();\n              *end_of_sequence = true;\n            } else {\n              for (const auto &tensor : item.out_tensors) {\n                out_tensors->push_back(tensor);\n                Instance *inst =\n                    out_tensors->at(0).scalar<Variant>()().get<Instance>();\n                inst->mutable_line_id()->set_data_source_name(\n                    absl::StrCat(\"data_source\", inst->data_source_key()));\n              }\n              *end_of_sequence = item.end_of_sequence;\n            }\n\n            return Status::OK();\n          }\n\n          bool finished = true;\n          for (bool f : prefetch_thread_finished_) {\n            finished = finished && f;\n          }\n\n          if (cancelled_ || finished) {\n            out_tensors->clear();\n            *end_of_sequence = true;\n            break;\n          }\n        } while (true);\n      }\n\n      return Status::OK();\n    }\n\n   protected:\n    std::shared_ptr<model::Node> CreateNode(\n        IteratorContext *ctx, model::Node::Args args) const override {\n      return model::MakeUnknownRatioNode(std::move(args));\n    }\n\n    Status SaveInternal(SerializationContext *ctx,\n                        IteratorStateWriter *writer) override {\n      return Status::OK();\n    }\n\n    Status RestoreInternal(IteratorContext *ctx,\n                           IteratorStateReader *reader) override {\n      return Status::OK();\n    }\n\n   private:\n    const std::shared_ptr<mutex> mu_;\n    const std::shared_ptr<mutex> output_mu_;\n    std::function<void()> deregister_fn_;\n    std::unique_ptr<CancellationManager> cancellation_manager_;\n    bool cancelled_ TF_GUARDED_BY(*mu_) = false;\n    bool prefetch_thread_started_ TF_GUARDED_BY(*mu_) = false;\n    std::vector<bool> prefetch_thread_finished_ TF_GUARDED_BY(*mu_);\n\n    size_t cur_ = 0;\n    std::vector<IteratorBase *> input_impls_;\n    std::vector<Thread *> prefetch_threads_;\n    std::unordered_map<std::string, QueueResource *> df_to_queue_;\n\n    Status EnsureThreadStarted(IteratorContext *ctx)\n        TF_EXCLUSIVE_LOCKS_REQUIRED(*mu_) {\n      if (!prefetch_thread_started_) {\n        prefetch_thread_started_ = true;\n        for (size_t i = 0; i < dataset()->data_flows_.size(); ++i) {\n          std::string name = dataset()->data_flows_[i];\n          std::shared_ptr<IteratorContext> new_ctx =\n              std::make_shared<IteratorContext>(*ctx);\n          std::unique_ptr<Thread> prefetch_thread_ = ctx->StartThread(\n              name,\n              [new_ctx, i, name, this]() { PrefetchThread(new_ctx, i, name); });\n          prefetch_threads_.push_back(prefetch_thread_.release());\n        }\n      }\n      return Status::OK();\n    }\n\n    void PrefetchThread(const std::shared_ptr<IteratorContext> &ctx, size_t i,\n                        std::string name) {\n      while (true) {\n        {\n          mutex_lock l(*mu_);\n          if (cancelled_) {\n            prefetch_thread_finished_[i] = true;\n            break;\n          }\n        }\n\n        if (!prefetch_thread_finished_[i]) {\n          Item item;\n          input_impls_[i]->GetNext(ctx.get(), &item.out_tensors,\n                                   &item.end_of_sequence);\n          if (item.end_of_sequence) {\n            prefetch_thread_finished_[i] = true;\n            break;\n          }\n\n          bool pushed = false;\n          do {\n            if (cancelled_ || prefetch_thread_finished_[i]) {\n              break;\n            }\n            pushed = df_to_queue_[name]->TryPush(item);\n          } while (!pushed);\n        }\n      }\n    }\n  };\n\n  const std::vector<const DatasetBase *> inputs_;\n  std::vector<std::string> data_flows_;\n  int max_queue_size_;\n  VariantType variant_type_;\n  std::string container_;\n};\n\nMergeFlowDatasetOp::MergeFlowDatasetOp(OpKernelConstruction *ctx)\n    : DatasetOpKernel(ctx) {\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kDataFlow, &data_flows_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kMaxQueueSize, &max_queue_size_));\n\n  std::string variant_type;\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kVariantType, &variant_type));\n  if (variant_type == \"instance\") {\n    variant_type_ = VariantType::PBInstance;\n  } else if (variant_type == \"example\") {\n    variant_type_ = VariantType::PBExample;\n  } else {\n    LOG(ERROR) << \"invalid variant_type: \" << variant_type;\n    ctx->SetStatus(Status(tensorflow::error::Code::INVALID_ARGUMENT,\n                          \"invalid variant_type\"));\n  }\n}\n\nvoid MergeFlowDatasetOp::MakeDataset(OpKernelContext *ctx,\n                                     DatasetBase **output) {\n  OpInputList iplist;\n  OP_REQUIRES_OK(ctx, ctx->input_list(\"inputs\", &iplist));\n  std::vector<const DatasetBase *> inputs;\n  for (const auto &ds : iplist) {\n    DatasetBase *input;\n    OP_REQUIRES_OK(ctx, GetDatasetFromVariantTensor(ds, &input));\n    inputs.push_back(input);\n  }\n\n  *output =\n      new Dataset(ctx, inputs, data_flows_, max_queue_size_, variant_type_);\n\n  std::string container;\n  // OP_REQUIRES_OK(ctx, GetNodeAttr(def(), \"container\", &container));\n  static_cast<Dataset *>(*output)->SetContainer(\"\");\n}\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"MergeFlowDataset\").Device(DEVICE_CPU),\n                        MergeFlowDatasetOp);\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/multi_label_gen_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_split.h\"\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing LineId = ::idl::matrix::proto::LineId;\nusing Action = google::protobuf::RepeatedField<int>;\nusing Label = google::protobuf::RepeatedField<float>;\n\nclass MultiLabelGenOp : public OpKernel {\n public:\n  explicit MultiLabelGenOp(OpKernelConstruction *context) : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"task_num\", &task_num_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"head_field\", &head_field_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"pos_actions\", &pos_actions_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"neg_actions\", &neg_actions_));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"use_origin_label\", &use_origin_label_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"pos_label\", &pos_label_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"neg_label\", &neg_label_));\n\n    std::string action_priority;\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"action_priority\", &action_priority));\n    std::vector<absl::string_view> action_priority_items =\n        absl::StrSplit(action_priority, \",\");\n    for (size_t i = 0; i < action_priority_items.size(); ++i) {\n      int32 action;\n      absl::SimpleAtoi(action_priority_items[i], &action);\n      action_priority_.emplace(action, static_cast<int32>(i));\n    }\n\n    std::string head_to_index;\n    OP_REQUIRES_OK(context, context->GetAttr(\"head_to_index\", &head_to_index));\n    for (absl::string_view split : absl::StrSplit(head_to_index, \",\")) {\n      std::pair<absl::string_view, absl::string_view> head_and_index =\n          absl::StrSplit(split, \":\");\n      int index;\n      absl::SimpleAtoi(head_and_index.second, &index);\n      CHECK_LT(index, task_num_);\n      head_to_index_.emplace(head_and_index.first, index);\n    }\n\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n    if (variant_type_ != \"instance\" && variant_type_ != \"example\") {\n      LOG(FATAL) << \"Invalid 'variant_type', please choose on from \"\n                    \"['instance', 'example']!\";\n    }\n\n    const ::google::protobuf::Descriptor *descriptor =\n        ::idl::matrix::proto::LineId::GetDescriptor();\n    field = descriptor->FindFieldByName(head_field_);\n    CHECK_EQ(field->is_repeated(), false);\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n\n    bool is_instance = variant_type_ == \"instance\";\n    if (is_instance) {\n      Instance instance;\n      instance.CopyFrom(*input_tensor.scalar<Variant>()().get<Instance>());\n      output_tensor->scalar<Variant>()() = std::move(instance);\n    } else {\n      Example example;\n      example.CopyFrom(*input_tensor.scalar<Variant>()().get<Example>());\n      output_tensor->scalar<Variant>()() = std::move(example);\n    }\n\n    LineId *line_id = GetLineId(output_tensor, is_instance);\n    auto label = GetLabel(output_tensor, is_instance);\n    float label_value = internal::INVALID_LABEL;\n    if (use_origin_label_) {\n      if (!label->empty()) {\n        label_value = label->Get(0);\n      } else {\n        LOG_EVERY_N_SEC(ERROR, 60)\n            << \"Invalid data: label is empty, please investigate and retry!\";\n      }\n    } else {\n      int64_t action;\n      if (FindMostPriorAction(line_id->actions(), &action)) {\n        if (std::find(pos_actions_.begin(), pos_actions_.end(), action) !=\n            pos_actions_.end()) {\n          label_value = pos_label_;\n        } else if (std::find(neg_actions_.begin(), neg_actions_.end(),\n                             action) != neg_actions_.end()) {\n          label_value = neg_label_;\n        }\n      }\n    }\n\n    label->Clear();\n    label->Resize(task_num_, internal::INVALID_LABEL);\n\n    std::string head_flag = GetHeadFlag(*line_id);\n    if (head_to_index_.count(head_flag)) {\n      int idx = head_to_index_[head_flag];\n      label->Set(idx, label_value);\n    }\n  }\n\n private:\n  static LineId *GetLineId(Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_line_id();\n    } else {\n      return output_tensor->scalar<Variant>()()\n          .get<Example>()\n          ->mutable_line_id();\n    }\n  }\n\n  static ::google::protobuf::RepeatedField<float> *GetLabel(\n      Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_label();\n    } else {\n      return output_tensor->scalar<Variant>()().get<Example>()->mutable_label();\n    }\n  }\n\n  bool FindMostPriorAction(const Action &actions, int64_t *action) {\n    if (actions.size() != 0) {\n      if (action_priority_.empty() || actions.size() == 1) {\n        *action = actions[0];\n      } else {\n        int64_t priority = std::numeric_limits<int64_t>::max();\n        for (auto &act : actions) {\n          auto iter = action_priority_.find(act);\n          if (iter != action_priority_.end() && iter->second < priority) {\n            *action = iter->first;\n            priority = iter->second;\n          }\n        }\n\n        if (priority == std::numeric_limits<int64_t>::max())\n          *action = actions[0];\n      }\n      return true;\n    }\n\n    return false;\n  }\n\n  std::string GetHeadFlag(const LineId &line_id) {\n    std::stringstream ss;\n    if (head_field_ == \"chnid\") {\n      ss << line_id.chnid();\n    } else if (head_field_ == \"cid\") {\n      ss << line_id.cid();\n    } else {\n      switch (field->cpp_type()) {\n        case ::google::protobuf::FieldDescriptor::CPPTYPE_INT32:\n          ss << reflection->GetInt32(line_id, field);\n        case ::google::protobuf::FieldDescriptor::CPPTYPE_UINT32:\n          ss << reflection->GetUInt32(line_id, field);\n        case ::google::protobuf::FieldDescriptor::CPPTYPE_INT64:\n          ss << reflection->GetInt64(line_id, field);\n        case ::google::protobuf::FieldDescriptor::CPPTYPE_UINT64:\n          ss << reflection->GetUInt64(line_id, field);\n        case ::google::protobuf::FieldDescriptor::CPPTYPE_STRING:\n          ss << reflection->GetString(line_id, field);\n        default:\n          ss << \"\";\n      }\n    }\n\n    return ss.str();\n  }\n\n  int task_num_;\n  std::string head_field_;\n  std::map<std::string, int> head_to_index_;\n  std::unordered_map<int32, int32> action_priority_;\n  std::vector<int> pos_actions_;\n  std::vector<int> neg_actions_;\n  bool use_origin_label_;\n  float pos_label_;\n  float neg_label_;\n  std::string variant_type_;\n\n  const ::google::protobuf::FieldDescriptor *field;\n  const ::google::protobuf::Reflection *reflection =\n      ::idl::matrix::proto::LineId::GetReflection();\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"MultiLabelGen\").Device(DEVICE_CPU),\n                        MultiLabelGenOp)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/negative_gen_dataset_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/negative_gen_dataset_kernel.h\"\n\n#include <deque>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/data/kernels/item_pool_kernels.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_def_builder.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/partial_tensor_shape.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/stats_aggregator.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/lib/gtl/cleanup.h\"\n#include \"tensorflow/core/lib/io/buffered_inputstream.h\"\n#include \"tensorflow/core/lib/io/inputbuffer.h\"\n#include \"tensorflow/core/lib/io/zlib_compression_options.h\"\n#include \"tensorflow/core/lib/random/random.h\"\n#include \"tensorflow/core/lib/strings/str_util.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing LineId = ::idl::matrix::proto::LineId;\nusing Action = google::protobuf::RepeatedField<int>;\nusing Label = google::protobuf::RepeatedField<float>;\nusing Example = ::monolith::io::proto::Example;\nusing ::tensorflow::monolith_tf::FeatureNameMapper;\nusing ::tensorflow::monolith_tf::FeatureNameMapperTfBridge;\nusing EFeature = ::monolith::io::proto::NamedFeature;\nusing ItemPoolResource = ::tensorflow::monolith_tf::ItemPoolResource;\nusing ItemFeatures = ::tensorflow::monolith_tf::internal::ItemFeatures;\n\nstatic const int32 INVALID_NEGATIVE_ACTION = -99999;\nconstexpr char kInputImplEmpty[] = \"input_impl_empty\";\n\nstatic constexpr const char *const kDatasetType = \"negtive_gen_dataset\";\nstatic constexpr const char *const kNegNum = \"neg_num\";\nstatic constexpr const char *const kPerChannel = \"per_channel\";\nstatic constexpr const char *const kChannelFeature = \"channel_feature\";\nstatic constexpr const char *const kItemFeature = \"item_features\";\nstatic constexpr const char *const kLabelIndex = \"label_index\";\nstatic constexpr const char *const kPositiveLabel = \"positive_label\";\nstatic constexpr const char *const kNegativeLabel = \"negative_label\";\nstatic constexpr const char *const kNegativeAction = \"negative_action\";\nstatic constexpr const char *const kActionPriority = \"action_priority\";\nstatic constexpr const char *const kPositiveActions = \"positive_actions\";\nstatic constexpr const char *const kCacheOnlyPos = \"cache_only_pos\";\nstatic constexpr const char *const kCacheNegativeActions =\n    \"cache_negative_actions\";\nstatic constexpr const char *const kIndexFeature = \"index_feature\";\nstatic constexpr const char *const kThrowOrigin = \"throw_origin\";\nstatic constexpr const char *const kThrowOriginNeg = \"throw_origin_neg\";\nstatic constexpr const char *const kRealNegInstanceWeight =\n    \"real_neg_instance_weight\";\nstatic constexpr const char *const kSampledNegInstanceWeight =\n    \"sampled_neg_instance_weight\";\nstatic constexpr const char *const kUnbiasSampledNeg = \"unbias_sampled_neg\";\nstatic constexpr const char *const kOriginNegInPoolProba =\n    \"origin_neg_in_pool_proba\";\nstatic constexpr const char *const kNegSampleDeclayFactor =\n    \"neg_sample_declay_factor\";\nstatic constexpr const char *const kHardEasyRatio = \"easy_hard_ratio\";\nstatic constexpr const char *const kVariantType = \"variant_type\";\n\nclass InnerIterator {\n public:\n  InnerIterator(IteratorBase *input_impl, ItemPoolResource *resource,\n                int32 neg_num, bool per_channel,\n                const std::string &channel_feature,\n                const std::vector<std::string> &item_features,\n                int32 label_index, int32 positive_label, int32 negative_label,\n                int32 negative_action, const std::string &action_priority,\n                const std::vector<int32> &positive_actions,\n                const std::string &index_feature, bool throw_origin,\n                bool throw_origin_neg, bool cache_only_pos,\n                const std::vector<int32> &cache_negative_actions,\n                float real_neg_instance_weight,\n                float sampled_neg_instance_weight, bool unbias_sampled_neg,\n                float origin_neg_in_pool_proba, float neg_sample_declay_factor,\n                float easy_hard_ratio, const std::string &variant_type)\n      : resource_(resource),\n        index_(0),\n        need_new_ins_(true),\n        input_real_negative_instance_num_(0),\n        input_instance_num_(0),\n        output_instance_num_(0),\n        generate_instance_num_(0),\n        hard_sample_num_(0),\n        easy_sample_num_(0),\n        neg_num_(neg_num),\n        per_channel_(per_channel),\n        channel_feature_(channel_feature),\n        item_features_(item_features.begin(), item_features.end()),\n        label_index_(label_index),\n        positive_label_(positive_label),\n        negative_label_(negative_label),\n        negative_action_(negative_action),\n        positive_actions_(positive_actions.begin(), positive_actions.end()),\n        index_feature_(index_feature),\n        throw_origin_(throw_origin),\n        throw_origin_neg_(throw_origin_neg),\n        cache_only_pos_(cache_only_pos),\n        cache_negative_actions_(cache_negative_actions.begin(),\n                                cache_negative_actions.end()),\n        real_neg_instance_weight_(real_neg_instance_weight),\n        sampled_neg_instance_weight_(sampled_neg_instance_weight),\n        unbias_sampled_neg_(unbias_sampled_neg),\n        origin_neg_in_pool_proba_(origin_neg_in_pool_proba),\n        neg_sample_declay_factor_(neg_sample_declay_factor),\n        easy_hard_ratio_(easy_hard_ratio) {\n    input_impl_ = input_impl;\n    tensors_ = new std::vector<Tensor>();\n    tensors_->reserve(1);\n    std::vector<absl::string_view> action_priority_items =\n        absl::StrSplit(action_priority, \",\");\n    for (size_t i = 0; i < action_priority_items.size(); ++i) {\n      int32 action;\n      if (action_priority_items[i].empty()) {\n        continue;\n      }\n      CHECK(absl::SimpleAtoi(action_priority_items[i], &action));\n      action_priority_.insert({action, static_cast<int32>(i)});\n    }\n\n    if (variant_type == \"instance\") {\n      variant_type_ = VariantType::PBInstance;\n      if (index_feature_.empty()) {\n        has_index_feature_ = false;\n        index_slot_ = 0;\n      } else {\n        has_index_feature_ = true;\n        CHECK(absl::SimpleAtoi(index_feature_, &index_slot_));\n      }\n\n      if (channel_feature_.empty()) {\n        channel_slot_ = 3;\n      } else {\n        CHECK(absl::SimpleAtoi(channel_feature_, &channel_slot_));\n      }\n\n      for (const auto &fname : item_features_) {\n        int32 slot;\n        CHECK(absl::SimpleAtoi(fname, &slot));\n        item_slots_.insert(slot);\n      }\n    } else {\n      variant_type_ = VariantType::PBExample;\n      index_slot_ = 0;\n      has_index_feature_ = !index_feature_.empty();\n      channel_slot_ = 3;\n    }\n  }\n\n  ~InnerIterator() { delete tensors_; }\n\n  Status GetNext(IteratorContext *ctx, std::vector<Tensor> *out_tensors,\n                 bool *end_of_sequence) {\n    if (end_of_sequence_) {\n      *end_of_sequence = end_of_sequence_;\n      out_tensors->clear();\n    }\n\n    while (!end_of_sequence_) {\n      Status s = MaybeGetNextRealInstance(ctx);\n      if (!s.ok()) {\n        return s;\n      }\n\n      if (end_of_sequence_) {\n        *end_of_sequence = end_of_sequence_;\n        out_tensors->clear();\n        break;\n      }\n\n      bool is_positive = IsPositive();\n      if (index_ == 0 && Cacheable(is_positive)) {\n        SaveToCache(is_positive);\n      }\n      if (index_ == 0 && !is_positive) {\n        input_real_negative_instance_num_++;\n      }\n\n      if (is_positive && index_ < neg_num_) {\n        Tensor tensor;\n        if (BuildNegativeTensor(ctx, &tensor)) {\n          *end_of_sequence = end_of_sequence_;\n          out_tensors->push_back(std::move(tensor));\n          index_++;\n          generate_instance_num_++;\n          output_instance_num_++;\n          break;\n        }\n      }\n\n      need_new_ins_ = true;\n      if (Emitable(is_positive)) {\n        *end_of_sequence = end_of_sequence_;\n        if (is_positive) {\n          SetInstanceWeight(&tensors_->back(), 1.0);\n        } else {\n          float instance_weight = real_neg_instance_weight_ > 0.00001\n                                      ? real_neg_instance_weight_\n                                      : 1.0;\n          SetInstanceWeight(&tensors_->back(), instance_weight);\n        }\n        out_tensors->push_back(tensors_->back());\n        output_instance_num_++;\n        break;\n      }\n    }\n\n    LOG_EVERY_N_SEC(INFO, 180) << \"input_instance_num: \" << input_instance_num_;\n    LOG_EVERY_N_SEC(INFO, 180) << \"input_real_negative_instance_num: \"\n                               << input_real_negative_instance_num_;\n    LOG_EVERY_N_SEC(INFO, 180)\n        << \"output_instance_num: \" << output_instance_num_;\n    LOG_EVERY_N_SEC(INFO, 180)\n        << \"generate_instance_num: \" << generate_instance_num_;\n    LOG_EVERY_N_SEC(INFO, 180) << \"hard_sample_num: \" << hard_sample_num_;\n    LOG_EVERY_N_SEC(INFO, 180) << \"easy_sample_num: \" << easy_sample_num_;\n    return Status::OK();\n  }\n\n private:\n  Status MaybeGetNextRealInstance(IteratorContext *ctx) {\n    if (need_new_ins_ && !end_of_sequence_) {\n      tensors_->clear();\n      TF_RETURN_IF_ERROR(\n          input_impl_->GetNext(ctx, tensors_, &end_of_sequence_));\n      if (end_of_sequence_) {\n        input_impl_ = nullptr;\n        return Status::OK();\n      }\n      need_new_ins_ = false;\n      gcids_ = GetGidAndChannelId();\n      ++input_instance_num_;\n      index_ = 0;  // Got one new instance, reset the neg count\n    }\n    return Status::OK();\n  }\n\n  template <typename T>\n  inline T *GetCurrent() {\n    Variant *variant = &tensors_->back().scalar<Variant>()();\n    return variant->get<T>();\n  }\n\n  inline const LineId *GetLineId() {\n    if (variant_type_ == VariantType::PBInstance) {\n      Instance *instance = GetCurrent<Instance>();\n      return &instance->line_id();\n    } else if (variant_type_ == VariantType::PBExample) {\n      Example *example = GetCurrent<Example>();\n      return &example->line_id();\n    } else {\n      return nullptr;\n    }\n  }\n\n  inline const Label *GetLabel() {\n    if (variant_type_ == VariantType::PBInstance) {\n      Instance *instance = GetCurrent<Instance>();\n      return &instance->label();\n    } else if (variant_type_ == VariantType::PBExample) {\n      Example *example = GetCurrent<Example>();\n      return &example->label();\n    } else {\n      return nullptr;\n    }\n  }\n\n  bool IsPositive() {\n    bool is_pos = false;\n    const LineId *line_id = GetLineId();\n    const Label *label = GetLabel();\n\n    if (!positive_actions_.empty() && line_id != nullptr) {\n      if (!line_id->actions().empty()) {\n        int64_t action;\n        FindMostPriorAction(line_id->actions(), &action);\n        auto iter = positive_actions_.find(action);\n        is_pos = iter != positive_actions_.end();\n      }\n    } else if (label != nullptr) {\n      if (label_index_ < label->size()) {\n        is_pos = label->at(label_index_) == positive_label_;\n      } else {\n        LOG_EVERY_N_SEC(ERROR, 60) << absl::StrFormat(\n            \"label_index_ should be less than label_size, while got %d vs %d\",\n            label_index_, label->size());\n      }\n    }\n\n    return is_pos;\n  }\n\n  inline bool Cacheable(bool is_positive) {\n    if (!cache_only_pos_ || is_positive) {\n      return true;\n    } else if (cache_negative_actions_.empty()) {\n      return false;\n    } else {\n      const LineId *line_id = GetLineId();\n      for (auto &action : line_id->actions()) {\n        if (cache_negative_actions_.count(action)) {\n          return true;\n        }\n      }\n      return false;\n    }\n  }\n\n  inline bool Emitable(bool is_positive) {\n    return (!throw_origin_ && (!throw_origin_neg_ || is_positive));\n  }\n\n  std::pair<uint64_t, uint64_t> GetGidAndChannelId() {\n    uint64_t gid = 0, cid = 3;\n    if (variant_type_ == VariantType::PBInstance) {\n      const Instance *instance = GetCurrent<Instance>();\n      gid = instance->line_id().item_id();\n      if (per_channel_ || has_index_feature_) {\n        for (const auto fid : instance->fid()) {\n          int32 slot = slot_id_v1(fid);\n          if (per_channel_ && channel_slot_ == slot) {\n            cid = fid;\n          }\n          if (has_index_feature_ && slot == index_slot_) {\n            gid = fid;\n          }\n        }\n      }\n    } else if (variant_type_ == VariantType::PBExample) {\n      const Example *example = GetCurrent<Example>();\n      gid = example->line_id().item_id();\n      if (per_channel_ || has_index_feature_) {\n        for (const auto &named_feature : example->named_feature()) {\n          const std::string &feature_name = named_feature.name();\n          auto &feature_value = named_feature.feature();\n          if (per_channel_ && channel_feature_ == feature_name) {\n            if (feature_value.type_case() ==\n                    ::monolith::io::proto::Feature::kFidV1List &&\n                feature_value.fid_v1_list().value_size() > 0) {\n              cid = feature_value.fid_v1_list().value(0);\n              LOG_EVERY_N_SEC(INFO, 180) << \"Use Fidv1.\";\n            } else if (feature_value.type_case() ==\n                           ::monolith::io::proto::Feature::kFidV2List &&\n                       feature_value.fid_v2_list().value_size() > 0) {\n              cid = feature_value.fid_v2_list().value(0);\n              LOG_EVERY_N_SEC(INFO, 180) << \"Use Fidv2.\";\n            } else {\n              LOG_EVERY_N_SEC(INFO, 180) << \"Use Default cid.\";\n            }\n          }\n          if (has_index_feature_ && index_feature_ == feature_name) {\n            if (feature_value.type_case() ==\n                    ::monolith::io::proto::Feature::kFidV1List &&\n                feature_value.fid_v1_list().value_size() > 0) {\n              gid = feature_value.fid_v1_list().value(0);\n            } else if (feature_value.type_case() ==\n                           ::monolith::io::proto::Feature::kFidV2List &&\n                       feature_value.fid_v2_list().value_size() > 0) {\n              gid = feature_value.fid_v2_list().value(0);\n            }\n          }\n        }\n      }\n    }\n\n    return std::pair<uint64_t, uint64_t>(gid, cid);\n  }\n\n  void SetInstanceWeight(Tensor *tensor, float instance_weight) {\n    if (variant_type_ == VariantType::PBInstance) {\n      auto *instance = tensor->scalar<Variant>()().get<Instance>();\n      instance->set_instance_weight(instance_weight);\n    } else {\n      auto *example = tensor->scalar<Variant>()().get<Example>();\n      example->set_instance_weight(instance_weight);\n    }\n  }\n\n  void SaveToCache(bool is_positive) {\n    std::shared_ptr<ItemFeatures> item_features =\n        std::make_shared<ItemFeatures>();\n    uint64_t item_id = gcids_.first;\n    uint64_t channel_id = gcids_.second;\n\n    if (channel_id == 0) {\n      return;\n    }\n\n    if (!is_positive && origin_neg_in_pool_proba_ >= 0 &&\n        origin_neg_in_pool_proba_ < 1) {\n      float proba = (std::rand() % 100) / 100.0;\n      if (proba > origin_neg_in_pool_proba_) {\n        return;\n      }\n    }\n\n    if (variant_type_ == VariantType::PBExample) {\n      Example *example = GetCurrent<Example>();\n      if (is_positive) {\n        named_feature_list_.Clear();\n      }\n      for (auto &named_feature : example->named_feature()) {\n        const std::string &feature_name = named_feature.name();\n        if (item_features_.count(feature_name) != 0) {\n          item_features->example_features[feature_name] = named_feature;\n        } else if (is_positive) {\n          named_feature_list_.Add(named_feature);\n        }\n      }\n    } else {\n      Instance *instance = GetCurrent<Instance>();\n      if (is_positive) {\n        fid_list_.Clear();\n      }\n      for (auto fid : instance->fid()) {\n        int32 slot = slot_id_v1(fid);\n        if (item_slots_.count(slot) != 0) {  // only cache group slots\n          item_features->fids.emplace_back(fid);\n        } else if (is_positive) {\n          fid_list_.Add(fid);\n        }\n      }\n    }\n\n    item_features->item_id = item_id;\n    resource_->Add(channel_id, item_id, item_features);\n  }\n\n  template <typename T>\n  void SetLabelAndLineId(T *neg, uint64_t item_id) {\n    if (label_index_ < neg->label_size()) {\n      neg->set_label(label_index_, negative_label_);\n    } else {\n      LOG_EVERY_N_SEC(ERROR, 60) << absl::StrFormat(\n          \"label_index_ should be less than label_size, while got %d vs %d\",\n          label_index_, neg->label_size());\n    }\n\n    neg->mutable_line_id()->set_item_id(item_id);\n    if (negative_action_ != INVALID_NEGATIVE_ACTION) {\n      neg->mutable_line_id()->clear_actions();\n      neg->mutable_line_id()->add_actions(negative_action_);\n    }\n  }\n\n  bool BuildNegativeTensor(IteratorContext *ctx, Tensor *res) {\n    // hard_easy neg when per_channel enabled\n    uint64_t channel_id = gcids_.second;\n    if (per_channel_ && NeedEasyNeg(easy_hard_ratio_)) {\n      resource_->SampleChannelID(&channel_id);\n      easy_sample_num_++;\n    } else {\n      hard_sample_num_++;\n    }\n\n    if (channel_id == 0) {\n      return false;\n    }\n    double freq_factor, time_factor;\n    std::shared_ptr<const ItemFeatures> cached_item =\n        resource_->Sample(channel_id, &freq_factor, &time_factor);\n    if (!cached_item) {\n      return false;\n    }\n    uint64_t item_id = cached_item->item_id;\n    Tensor tensor(ctx->allocator({}), DT_VARIANT, TensorShape({}));\n\n    float instance_weight;\n    if (sampled_neg_instance_weight_ > 0.00001) {\n      instance_weight = sampled_neg_instance_weight_;\n    } else if (unbias_sampled_neg_) {\n      instance_weight =\n          1.0 + neg_num_ * std::pow(time_factor, neg_sample_declay_factor_) *\n                    freq_factor;\n    } else {\n      instance_weight = 1.0;\n    }\n\n    if (variant_type_ == VariantType::PBExample) {\n      Example *example = GetCurrent<Example>();\n      Example new_example;\n      new_example.mutable_line_id()->CopyFrom(example->line_id());\n      for (const auto &label : example->label()) {\n        new_example.add_label(label);\n      }\n\n      const auto &cached_example_features = cached_item->example_features;\n      auto *mutable_named_feature = new_example.mutable_named_feature();\n      for (const auto &nf : named_feature_list_) {\n        mutable_named_feature->Add()->CopyFrom(nf);\n      }\n      for (const auto &nf : cached_example_features) {\n        mutable_named_feature->Add()->CopyFrom(nf.second);\n      }\n      SetLabelAndLineId(&new_example, item_id);\n      new_example.set_instance_weight(instance_weight);\n      tensor.scalar<Variant>()() = std::move(new_example);\n    } else {\n      Instance *instance = GetCurrent<Instance>();\n      Instance new_instance;\n      new_instance.CopyFrom(*instance);\n\n      const auto &cached_fid_list = cached_item->fids;\n      google::protobuf::RepeatedField<::google::protobuf::uint64> fid_list =\n          fid_list_;  // copy\n      for (auto fid : cached_fid_list) {\n        fid_list.Add(fid);\n      }\n      new_instance.mutable_fid()->Swap(&fid_list);\n      SetLabelAndLineId(&new_instance, item_id);\n      new_instance.set_instance_weight(instance_weight);\n      tensor.scalar<Variant>()() = std::move(new_instance);\n    }\n\n    *res = std::move(tensor);\n    return true;\n  }\n\n  bool FindMostPriorAction(const Action &actions, int64_t *action) {\n    if (actions.size() != 0) {\n      if (action_priority_.empty() || actions.size() == 1) {\n        *action = actions[0];\n      } else {\n        int64_t priority = std::numeric_limits<int64_t>::max();\n        for (auto &act : actions) {\n          auto iter = action_priority_.find(act);\n          if (iter != action_priority_.end() && iter->second < priority) {\n            *action = iter->first;\n            priority = iter->second;\n          }\n        }\n\n        if (priority == std::numeric_limits<int64_t>::max())\n          *action = actions[0];\n      }\n      return true;\n    }\n\n    return false;\n  }\n\n  bool NeedEasyNeg(float easy_hard_ratio) {\n    return static_cast<float>(std::rand()) / RAND_MAX < easy_hard_ratio;\n  }\n\n  ItemPoolResource *resource_ = nullptr;\n  bool end_of_sequence_ = false;\n  std::vector<Tensor> *tensors_ = nullptr;\n  IteratorBase *input_impl_ = nullptr;\n  int index_ = 0;\n  bool need_new_ins_ = true;\n  // stats variables\n  int64 input_real_negative_instance_num_ = 0;\n  int64 input_instance_num_ = 0;\n  int64 output_instance_num_ = 0;\n  int64 generate_instance_num_ = 0;\n  // hard & easy stats\n  int64 hard_sample_num_ = 0;\n  int64 easy_sample_num_ = 0;\n\n  int32 neg_num_;\n  bool per_channel_;\n  std::string channel_feature_;\n  int32 channel_slot_;\n  std::unordered_set<std::string> item_features_;\n  std::unordered_set<int32> item_slots_;\n  int32 label_index_;\n  int32 positive_label_;\n  int32 negative_label_;\n  int32 negative_action_;\n  std::unordered_set<int32> positive_actions_;\n  std::unordered_map<int32, int32> action_priority_;\n  std::string index_feature_;\n  int32 index_slot_;\n  bool has_index_feature_;\n  bool throw_origin_;\n  bool throw_origin_neg_;\n  bool cache_only_pos_;\n  std::unordered_set<int> cache_negative_actions_;\n  float real_neg_instance_weight_;\n  float sampled_neg_instance_weight_;\n  bool unbias_sampled_neg_;\n  float origin_neg_in_pool_proba_;\n  float neg_sample_declay_factor_;\n  float easy_hard_ratio_;\n  VariantType variant_type_;\n\n  std::pair<uint64_t, uint64_t> gcids_;\n  google::protobuf::RepeatedField<::google::protobuf::uint64> fid_list_;\n  google::protobuf::RepeatedField<::monolith::io::proto::NamedFeature>\n      named_feature_list_;\n};\n\nclass InstanceNegativeGenDatasetOp::Dataset : public DatasetBase {\n public:\n  Dataset(OpKernelContext *ctx, const DatasetBase *input, int32 neg_num,\n          bool per_channel, const std::string &channel_feature,\n          const std::vector<std::string> &item_features, int32 label_index,\n          int32 positive_label, int32 negative_label, int32 negative_action,\n          std::string action_priority,\n          const std::vector<int32> &positive_actions,\n          const std::string &index_feature, bool throw_origin,\n          bool throw_origin_neg, bool cache_only_pos,\n          const std::vector<int32> &cache_negative_actions,\n          float real_neg_instance_weight, float sampled_neg_instance_weight,\n          bool unbias_sampled_neg, float origin_neg_in_pool_proba,\n          float neg_sample_declay_factor, float easy_hard_ratio,\n          const std::string &variant_type, FeatureNameMapper *mapper)\n      : DatasetBase(DatasetContext(ctx)),\n        input_(input),\n        neg_num_(neg_num),\n        per_channel_(per_channel),\n        channel_feature_(channel_feature),\n        item_features_(item_features),\n        label_index_(label_index),\n        positive_label_(positive_label),\n        negative_label_(negative_label),\n        negative_action_(negative_action),\n        action_priority_(action_priority),\n        positive_actions_(positive_actions),\n        index_feature_(index_feature),\n        throw_origin_(throw_origin),\n        throw_origin_neg_(throw_origin_neg),\n        cache_only_pos_(cache_only_pos),\n        cache_negative_actions_(cache_negative_actions),\n        real_neg_instance_weight_(real_neg_instance_weight),\n        sampled_neg_instance_weight_(sampled_neg_instance_weight),\n        unbias_sampled_neg_(unbias_sampled_neg),\n        origin_neg_in_pool_proba_(origin_neg_in_pool_proba),\n        neg_sample_declay_factor_(neg_sample_declay_factor),\n        easy_hard_ratio_(easy_hard_ratio),\n        variant_type_(variant_type),\n        mapper_(mapper) {\n    input_->Ref();\n\n    const Tensor *pool_tensor_;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pool\", &pool_tensor_));\n    handle_ = pool_tensor_->scalar<ResourceHandle>()();\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, handle_, &resource_));\n\n    if (variant_type_ == \"example\") {\n      std::vector<std::string> valid_feature_names = item_features_;\n      if (!channel_feature_.empty()) {\n        valid_feature_names.push_back(channel_feature_);\n      }\n      if (!index_feature_.empty()) {\n        valid_feature_names.push_back(index_feature_);\n      }\n      mapper_->RegisterValidNames(valid_feature_names);\n    }\n  }\n\n  ~Dataset() override {\n    input_->Unref();\n    core::ScopedUnref unref(resource_);\n  }\n\n  std::unique_ptr<IteratorBase> MakeIteratorInternal(\n      const string &prefix) const override {\n    return absl::make_unique<Iterator>(\n        Iterator::Params{this, strings::StrCat(prefix, \"::\", kDatasetType)});\n  }\n\n  const DataTypeVector &output_dtypes() const override {\n    return input_->output_dtypes();\n  }\n\n  const std::vector<PartialTensorShape> &output_shapes() const override {\n    return input_->output_shapes();\n  }\n\n  string DebugString() const override {\n    return \"This is the customized Dataset: NegativeGenV2\";\n  }\n\n  Status InputDatasets(\n      std::vector<const DatasetBase *> *inputs) const override {\n    inputs->push_back(input_);\n    return Status::OK();\n  }\n\n  Status CheckExternalState() const override {\n    return input_->CheckExternalState();\n  }\n\n protected:\n  Status AsGraphDefInternal(SerializationContext *ctx,\n                            DatasetGraphDefBuilder *b,\n                            Node **output) const override {\n    Node *input_graph_node;\n    TF_RETURN_IF_ERROR(b->AddInputDataset(ctx, input_, &input_graph_node));\n\n    Tensor handle(DT_RESOURCE, TensorShape({}));\n    handle.scalar<ResourceHandle>()() = handle_;\n    Node *pool_node;\n    TF_RETURN_IF_ERROR(b->AddTensor(handle, &pool_node));\n\n    AttrValue neg_num_node;\n    b->BuildAttrValue(neg_num_, &neg_num_node);\n\n    AttrValue per_channel_node;\n    b->BuildAttrValue(per_channel_, &per_channel_node);\n\n    AttrValue channel_feature_node;\n    b->BuildAttrValue(channel_feature_, &channel_feature_node);\n\n    AttrValue item_features_node;\n    b->BuildAttrValue(item_features_, &item_features_node);\n\n    AttrValue label_index_node;\n    b->BuildAttrValue(label_index_, &label_index_node);\n\n    AttrValue positive_label_node;\n    b->BuildAttrValue(positive_label_, &positive_label_node);\n\n    AttrValue negative_label_node;\n    b->BuildAttrValue(negative_label_, &negative_label_node);\n\n    AttrValue negative_action_node;\n    b->BuildAttrValue(negative_action_, &negative_action_node);\n\n    AttrValue action_priority_node;\n    b->BuildAttrValue(action_priority_, &action_priority_node);\n\n    AttrValue positive_actions_node;\n    b->BuildAttrValue(positive_actions_, &positive_actions_node);\n\n    AttrValue index_feature_node;\n    b->BuildAttrValue(index_feature_, &index_feature_node);\n\n    AttrValue throw_origin_node;\n    b->BuildAttrValue(throw_origin_, &throw_origin_node);\n\n    AttrValue throw_origin_neg_node;\n    b->BuildAttrValue(throw_origin_neg_, &throw_origin_neg_node);\n\n    AttrValue cache_only_pos_node;\n    b->BuildAttrValue(cache_only_pos_, &cache_only_pos_node);\n\n    AttrValue cache_negative_actions_node;\n    b->BuildAttrValue(cache_negative_actions_, &cache_negative_actions_node);\n\n    AttrValue real_neg_instance_weight_node;\n    b->BuildAttrValue(real_neg_instance_weight_,\n                      &real_neg_instance_weight_node);\n\n    AttrValue sampled_neg_instance_weight_node;\n    b->BuildAttrValue(sampled_neg_instance_weight_,\n                      &sampled_neg_instance_weight_node);\n\n    AttrValue unbias_sampled_neg_node;\n    b->BuildAttrValue(unbias_sampled_neg_, &unbias_sampled_neg_node);\n\n    AttrValue origin_neg_in_pool_proba_node;\n    b->BuildAttrValue(origin_neg_in_pool_proba_,\n                      &origin_neg_in_pool_proba_node);\n\n    AttrValue neg_sample_declay_factor_node;\n    b->BuildAttrValue(neg_sample_declay_factor_,\n                      &neg_sample_declay_factor_node);\n\n    AttrValue easy_hard_ratio_node;\n    b->BuildAttrValue(easy_hard_ratio_, &easy_hard_ratio_node);\n\n    AttrValue variant_type_node;\n    b->BuildAttrValue(variant_type_, &variant_type_node);\n\n    TF_RETURN_IF_ERROR(b->AddDataset(\n        this,                           // dataset\n        {input_graph_node, pool_node},  // inputs\n        {{kNegNum, neg_num_node},\n         {kPerChannel, per_channel_node},\n         {kChannelFeature, channel_feature_node},\n         {kItemFeature, item_features_node},\n         {kLabelIndex, label_index_node},\n         {kPositiveLabel, positive_label_node},\n         {kNegativeLabel, negative_label_node},\n         {kNegativeAction, negative_action_node},\n         {kActionPriority, action_priority_node},\n         {kPositiveActions, positive_actions_node},\n         {kIndexFeature, index_feature_node},\n         {kThrowOrigin, throw_origin_node},\n         {kThrowOriginNeg, throw_origin_neg_node},\n         {kCacheOnlyPos, cache_only_pos_node},\n         {kCacheNegativeActions, cache_negative_actions_node},\n         {kRealNegInstanceWeight, real_neg_instance_weight_node},\n         {kSampledNegInstanceWeight, sampled_neg_instance_weight_node},\n         {kUnbiasSampledNeg, unbias_sampled_neg_node},\n         {kOriginNegInPoolProba, origin_neg_in_pool_proba_node},\n         {kNegSampleDeclayFactor, neg_sample_declay_factor_node},\n         {kHardEasyRatio, easy_hard_ratio_node},\n         {kVariantType, variant_type_node}},\n        output));  // Node**\n\n    return Status::OK();\n  }\n\n private:\n  class Iterator : public DatasetIterator<Dataset> {\n   public:\n    explicit Iterator(const Params &params)\n        : DatasetIterator<Dataset>(params) {}\n\n    ~Iterator() override {\n      mutex_lock l(mu_);\n      if (input_impl_ != nullptr) {\n        input_impl_.reset();\n      }\n    }\n\n    Status Initialize(IteratorContext *ctx) override {\n      Status s =\n          dataset()->input_->MakeIterator(ctx, this, prefix(), &input_impl_);\n      iter_ = std::make_unique<InnerIterator>(\n          input_impl_.get(), dataset()->resource_, dataset()->neg_num_,\n          dataset()->per_channel_, dataset()->channel_feature_,\n          dataset()->item_features_, dataset()->label_index_,\n          dataset()->positive_label_, dataset()->negative_label_,\n          dataset()->negative_action_, dataset()->action_priority_,\n          dataset()->positive_actions_, dataset()->index_feature_,\n          dataset()->throw_origin_, dataset()->throw_origin_neg_,\n          dataset()->cache_only_pos_, dataset()->cache_negative_actions_,\n          dataset()->real_neg_instance_weight_,\n          dataset()->sampled_neg_instance_weight_,\n          dataset()->unbias_sampled_neg_, dataset()->origin_neg_in_pool_proba_,\n          dataset()->neg_sample_declay_factor_, dataset()->easy_hard_ratio_,\n          dataset()->variant_type_);\n      return s;\n    }\n\n    Status GetNextInternal(IteratorContext *ctx,\n                           std::vector<Tensor> *out_tensors,\n                           bool *end_of_sequence) override {\n      mutex_lock l(mu_);\n      out_tensors->reserve(1);\n      TF_RETURN_IF_ERROR(iter_->GetNext(ctx, out_tensors, end_of_sequence));\n      if (*end_of_sequence) {\n        input_impl_.reset();\n      }\n      return Status::OK();\n    }\n\n   protected:\n    std::shared_ptr<model::Node> CreateNode(\n        IteratorContext *ctx, model::Node::Args args) const override {\n      return model::MakeUnknownRatioNode(std::move(args));\n    }\n\n    Status SaveInternal(SerializationContext *ctx,\n                        IteratorStateWriter *writer) override {\n      mutex_lock l(mu_);\n      if (!input_impl_) {\n        TF_RETURN_IF_ERROR(writer->WriteScalar(full_name(kInputImplEmpty), \"\"));\n      } else {\n        TF_RETURN_IF_ERROR(SaveInput(ctx, writer, input_impl_));\n      }\n      return Status::OK();\n    }\n\n    Status RestoreInternal(IteratorContext *ctx,\n                           IteratorStateReader *reader) override {\n      mutex_lock l(mu_);\n      if (!reader->Contains(full_name(kInputImplEmpty))) {\n        TF_RETURN_IF_ERROR(RestoreInput(ctx, reader, input_impl_));\n      } else {\n        input_impl_.reset();\n      }\n      return Status::OK();\n    }\n\n   private:\n    mutex mu_;\n    std::unique_ptr<IteratorBase> input_impl_ TF_GUARDED_BY(mu_);\n    std::unique_ptr<InnerIterator> iter_;\n  };\n\n  const DatasetBase *const input_;\n  int32 neg_num_;\n  bool per_channel_;\n  std::string channel_feature_;\n  std::vector<std::string> item_features_;\n  int32 label_index_;\n  int32 positive_label_;\n  int32 negative_label_;\n  int32 negative_action_;\n  std::string action_priority_;\n  std::vector<int32> positive_actions_;\n  std::string index_feature_;\n  bool throw_origin_;\n  bool throw_origin_neg_;\n  bool cache_only_pos_;\n  std::vector<int32> cache_negative_actions_;\n  float real_neg_instance_weight_;\n  float sampled_neg_instance_weight_;\n  bool unbias_sampled_neg_;\n  float origin_neg_in_pool_proba_;\n  float neg_sample_declay_factor_;\n  float easy_hard_ratio_;\n  std::string variant_type_;\n\n  ResourceHandle handle_;\n  ItemPoolResource *resource_;\n  FeatureNameMapper *mapper_ = nullptr;\n};\n\nInstanceNegativeGenDatasetOp::InstanceNegativeGenDatasetOp(\n    OpKernelConstruction *ctx)\n    : UnaryDatasetOpKernel(ctx) {\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kNegNum, &neg_num_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kPerChannel, &per_channel_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kChannelFeature, &channel_feature_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kItemFeature, &item_features_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kLabelIndex, &label_index_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kPositiveLabel, &positive_label_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kNegativeLabel, &negative_label_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kNegativeAction, &negative_action_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kPositiveActions, &positive_actions_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kActionPriority, &action_priority_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kIndexFeature, &index_feature_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kThrowOrigin, &throw_origin_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kThrowOriginNeg, &throw_origin_neg_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kCacheOnlyPos, &cache_only_pos_));\n  OP_REQUIRES_OK(ctx,\n                 ctx->GetAttr(kCacheNegativeActions, &cache_negative_actions_));\n  OP_REQUIRES_OK(\n      ctx, ctx->GetAttr(kRealNegInstanceWeight, &real_neg_instance_weight_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kSampledNegInstanceWeight,\n                                   &sampled_neg_instance_weight_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kUnbiasSampledNeg, &unbias_sampled_neg_));\n  OP_REQUIRES_OK(\n      ctx, ctx->GetAttr(kOriginNegInPoolProba, &origin_neg_in_pool_proba_));\n  OP_REQUIRES_OK(\n      ctx, ctx->GetAttr(kNegSampleDeclayFactor, &neg_sample_declay_factor_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kHardEasyRatio, &easy_hard_ratio_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kVariantType, &variant_type_));\n\n  auto creator = [this](FeatureNameMapperTfBridge **out_mapper) {\n    TF_RETURN_IF_ERROR(FeatureNameMapperTfBridge::New(out_mapper));\n    return Status::OK();\n  };\n  ResourceMgr *resource_mgr = ctx->resource_manager();\n  OP_REQUIRES_OK(ctx, resource_mgr->LookupOrCreate<FeatureNameMapperTfBridge>(\n                          resource_mgr->default_container(),\n                          FeatureNameMapperTfBridge::kName, &mapper_, creator));\n}\n\nvoid InstanceNegativeGenDatasetOp::MakeDataset(OpKernelContext *ctx,\n                                               DatasetBase *input,\n                                               DatasetBase **output) {\n  *output = new Dataset(\n      ctx, input, neg_num_, per_channel_, channel_feature_, item_features_,\n      label_index_, positive_label_, negative_label_, negative_action_,\n      action_priority_, positive_actions_, index_feature_, throw_origin_,\n      throw_origin_neg_, cache_only_pos_, cache_negative_actions_,\n      real_neg_instance_weight_, sampled_neg_instance_weight_,\n      unbias_sampled_neg_, origin_neg_in_pool_proba_, neg_sample_declay_factor_,\n      easy_hard_ratio_, variant_type_, mapper_->GetFeatureNameMapper());\n}\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"InstanceNegativeGenDataset\").Device(DEVICE_CPU),\n                        InstanceNegativeGenDatasetOp);\n}  // namespace\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/negative_gen_dataset_kernel.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_KERNELS_NEGATIVE_GEN_DATASET_KERNEL_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_KERNELS_NEGATIVE_GEN_DATASET_KERNEL_H_\n\n#include \"monolith/native_training/data/kernels/feature_name_mapper_tf_bridge.h\"\n#include \"tensorflow/core/framework/dataset.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\n\nenum class VariantType { PBInstance, PBExample };\n\nclass InstanceNegativeGenDatasetOp : public UnaryDatasetOpKernel {\n public:\n  explicit InstanceNegativeGenDatasetOp(OpKernelConstruction* ctx);\n\n  ~InstanceNegativeGenDatasetOp() override { mapper_->Unref(); }\n\n protected:\n  void MakeDataset(OpKernelContext* ctx, DatasetBase* input,\n                   DatasetBase** output) override;\n\n private:\n  class Dataset;\n  int32 neg_num_;\n  bool per_channel_;\n  std::string channel_feature_;\n  std::vector<std::string> item_features_;\n  int32 label_index_;\n  int32 positive_label_;\n  int32 negative_label_;\n  int32 negative_action_;\n  std::string action_priority_;\n  std::vector<int32> positive_actions_;\n  std::string index_feature_;\n  bool throw_origin_;\n  bool throw_origin_neg_;\n  bool cache_only_pos_;\n  std::vector<int32> cache_negative_actions_;\n  float real_neg_instance_weight_ = 1.0;\n  float sampled_neg_instance_weight_ = -1;\n  bool unbias_sampled_neg_;\n  float origin_neg_in_pool_proba_;\n  float neg_sample_declay_factor_;\n  float easy_hard_ratio_;\n  std::string variant_type_;\n  tensorflow::monolith_tf::FeatureNameMapperTfBridge* mapper_ = nullptr;\n};\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_KERNELS_NEGATIVE_GEN_DATASET_KERNEL_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/parquet_dataset_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/internal/parquet_example_reader.h\"\n#include \"parquet/api/reader.h\"\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/dataset.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/profiler/lib/traceme.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\n\nusing monolith::io::proto::Example;\nusing monolith::io::proto::NamedFeature;\nusing monolith::io::proto::NamedFeatureList;\n\nclass ParquetDatasetOp : public DatasetOpKernel {\n public:\n  static const char* const kDatasetType;\n  static const char* const kFileName;\n  static const char* const kOutputPbType;\n  static const char* const kBatchSize;\n  static const char* const kDropRemainder;\n  static const char* const kSelectColumns;\n  static const char* const kSelectColumnsType;\n\n  explicit ParquetDatasetOp(OpKernelConstruction* ctx) : DatasetOpKernel(ctx) {\n    // select_columns\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(kBatchSize, &batch_size_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(kDropRemainder, &drop_remainder_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(kSelectColumns, &select_columns_));\n    OP_REQUIRES_OK(ctx,\n                   ctx->GetAttr(kSelectColumnsType, &select_columns_type_));\n  }\n\n  void MakeDataset(OpKernelContext* ctx, DatasetBase** output) override {\n    tstring file_name;\n    OP_REQUIRES_OK(ctx,\n                   ParseScalarArgument<tstring>(ctx, kFileName, &file_name));\n    tstring output_pb_type;\n    OP_REQUIRES_OK(\n        ctx, ParseScalarArgument<tstring>(ctx, kOutputPbType, &output_pb_type));\n\n    *output =\n        new Dataset(ctx, file_name, output_pb_type, batch_size_,\n                    drop_remainder_, select_columns_, select_columns_type_);\n\n    // config log\n    nlohmann::json j;\n    j[kFileName] = file_name;\n    j[kOutputPbType] = output_pb_type;\n    j[kBatchSize] = batch_size_;\n    j[kSelectColumns] = select_columns_.size();\n    j[kSelectColumnsType] = select_columns_type_.size();\n    LOG(INFO) << j.dump();\n  }\n\n private:\n  class Dataset : public DatasetBase {\n   public:\n    explicit Dataset(OpKernelContext* ctx, tstring file_name,\n                     tstring output_pb_type, int32_t batch_size,\n                     bool drop_remainder, std::vector<tstring> select_columns,\n                     std::vector<tstring> select_columns_type)\n        : DatasetBase(DatasetContext(ctx)),\n          file_name_(std::move(file_name)),\n          output_pb_type_(std::move(output_pb_type)),\n          batch_size_(batch_size),\n          drop_remainder_(drop_remainder),\n          select_columns_(std::move(select_columns)),\n          select_columns_type_(std::move(select_columns_type)) {}\n\n    std::unique_ptr<IteratorBase> MakeIteratorInternal(\n        const string& prefix) const override {\n      return absl::make_unique<Iterator>(\n          Iterator::Params{this, strings::StrCat(prefix, \"::\", kDatasetType)});\n    }\n\n    const DataTypeVector& output_dtypes() const override {\n      static DataTypeVector* dtypes = nullptr;\n      if (!dtypes) {\n        if (output_pb_type_ == \"example\" || output_pb_type_ == \"examplebatch\") {\n          dtypes = new DataTypeVector({DT_VARIANT});\n        } else {\n          dtypes = new DataTypeVector({DT_STRING});\n        }\n      }\n      return *dtypes;\n    }\n\n    const std::vector<PartialTensorShape>& output_shapes() const override {\n      static auto* shapes =\n          new std::vector<PartialTensorShape>{TensorShape({})};\n      return *shapes;\n    }\n\n    string DebugString() const override { return \"ParquetDatasetOp::Dataset\"; }\n\n    Status CheckExternalState() const override { return Status::OK(); }\n\n   protected:\n    Status AsGraphDefInternal(SerializationContext* ctx,\n                              DatasetGraphDefBuilder* b,\n                              Node** output) const override {\n      Node* file_name = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(file_name_, &file_name));\n      Node* output_pb_type = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(output_pb_type_, &output_pb_type));\n      AttrValue batch_size;\n      b->BuildAttrValue(batch_size_, &batch_size);\n      AttrValue drop_remainder;\n      b->BuildAttrValue(drop_remainder_, &drop_remainder);\n      AttrValue select_columns;\n      b->BuildAttrValue(select_columns_, &select_columns);\n      AttrValue select_columns_type;\n      b->BuildAttrValue(select_columns_type_, &select_columns_type);\n\n      TF_RETURN_IF_ERROR(\n          b->AddDataset(this, {file_name, output_pb_type},\n                        {{kBatchSize, batch_size},\n                         {kDropRemainder, drop_remainder},\n                         {kSelectColumns, select_columns},\n                         {kSelectColumnsType, select_columns_type}},\n                        output));\n      return Status::OK();\n    }\n\n   private:\n    class Iterator : public DatasetIterator<Dataset> {\n     public:\n      explicit Iterator(const Params& params)\n          : DatasetIterator<Dataset>(params) {}\n\n      Status GetNextInternal(IteratorContext* ctx,\n                             std::vector<Tensor>* out_tensors,\n                             bool* end_of_sequence) override {\n        mutex_lock l(mu_);\n        out_tensors->clear();\n        out_tensors->reserve(1);\n        if (!parquet_reader_) {\n          parquet_reader_.reset(\n              new tensorflow::data::ParquetExampleReader(ctx->env()));\n          std::vector<string> select_col_str(dataset()->select_columns_.begin(),\n                                             dataset()->select_columns_.end());\n          std::vector<string> select_col_type_str(\n              dataset()->select_columns_type_.begin(),\n              dataset()->select_columns_type_.end());\n\n          TF_RETURN_IF_ERROR(parquet_reader_->Init(\n              dataset()->file_name_, select_col_str, select_col_type_str));\n        }\n\n        if (dataset()->output_pb_type_ == \"example\") {\n          Example example;\n          TF_RETURN_IF_ERROR(GetNextExample(example, end_of_sequence));\n          Tensor record_tensor(ctx->allocator({}), DT_VARIANT, {});\n          record_tensor.scalar<Variant>()() = std::move(example);\n          out_tensors->emplace_back(std::move(record_tensor));\n          if (*end_of_sequence) {\n            LOG(INFO) << \"end_of_sequence of \" << dataset()->file_name_;\n          } else {\n            counter_++;\n            LOG_EVERY_N_SEC(INFO, 60) << \"consume \" << counter_ << \" examples from \" << dataset()->file_name_;\n          }\n        } else if (dataset()->output_pb_type_ == \"examplebatch\") {\n          ExampleBatch example_batch;\n          TF_RETURN_IF_ERROR(\n              GetNextExampleBatch(example_batch, end_of_sequence));\n          if (!(*end_of_sequence)) {\n            if (dataset()->drop_remainder_ &&\n                example_batch.batch_size() < dataset()->batch_size_) {\n              LOG(INFO) << \"last example batch size=\"\n                        << example_batch.batch_size() << \" dropped\";\n              *end_of_sequence = true;\n            } else {\n              Tensor record_tensor(ctx->allocator({}), DT_VARIANT, {});\n              record_tensor.scalar<Variant>()() = std::move(example_batch);\n              out_tensors->emplace_back(std::move(record_tensor));\n              counter_++;\n              if (counter_ % 100 == 0) {\n                LOG(INFO) << \"consume \" << counter_ << \"example_batch from \"\n                          << dataset()->file_name_;\n              }\n            }\n          }\n        } else if (dataset()->output_pb_type_ == \"plaintext\") {\n          // only for debug use, generate examplebatch pb string\n          ExampleBatch example_batch;\n          TF_RETURN_IF_ERROR(\n              GetNextExampleBatch(example_batch, end_of_sequence));\n          Tensor record_tensor(ctx->allocator({}), DT_STRING, {});\n          std::string out;\n          example_batch.SerializeToString(&out);\n          record_tensor.scalar<tstring>()() = out;\n          out_tensors->emplace_back(std::move(record_tensor));\n          if (*end_of_sequence) {\n            LOG(INFO) << \"end_of_sequence of \" << dataset()->file_name_;\n          }\n        } else {\n          return errors::InvalidArgument(\n              \"output_pb_type is \", dataset()->output_pb_type_,\n              \",should be example or examplebatch or plaintext\");\n        }\n\n        return Status::OK();\n      }\n\n      Status GetNextExample(Example& example, bool* end_of_sequence) {\n        if (parquet_reader_->IsEOF()) {\n          *end_of_sequence = true;\n        } else {\n          *end_of_sequence = false;\n          example.Clear();\n          parquet_reader_->GetNextExample(example);\n        }\n\n        return Status::OK();\n      }\n\n      Status GetNextExampleBatch(ExampleBatch& example_batch,\n                                 bool* end_of_sequence) {\n        profiler::TraceMe activity(\n            []() { return \"ParquetDatasetOp::GetNextExampleBatch\"; });\n        if (parquet_reader_->IsEOF()) {\n          *end_of_sequence = true;\n        } else {\n          *end_of_sequence = false;\n          example_batch.Clear();\n          parquet_reader_->GetNextExampleBatch(example_batch,\n                                               dataset()->batch_size_);\n        }\n        return Status::OK();\n      }\n\n      NamedFeatureList* AddNamedFeatureList(ExampleBatch& example_batch,\n                                            const std::string& name,\n                                            int32_t id) {\n        NamedFeatureList* named_feature_list =\n            example_batch.add_named_feature_list();\n        named_feature_list->set_id(id);\n        named_feature_list->set_name(name);\n        return named_feature_list;\n      }\n\n     protected:\n      Status SaveInternal(SerializationContext* ctx,\n                          IteratorStateWriter* writer) override {\n        mutex_lock l(mu_);\n        // do nothing\n        LOG(INFO) << \"Save function is not supported yet.\";\n        return Status::OK();\n      }\n\n      Status RestoreInternal(IteratorContext* ctx,\n                             IteratorStateReader* reader) override {\n        mutex_lock l(mu_);\n        // do nothing\n        LOG(INFO) << \"Restore function is not supported yet.\";\n        return Status::OK();\n      }\n\n     private:\n      mutex mu_;\n      int64_t counter_ = 0;\n      std::unique_ptr<tensorflow::data::ParquetExampleReader> parquet_reader_;\n    };\n\n    // original inputs/attrs\n    tstring file_name_;\n    tstring output_pb_type_;\n    int32_t batch_size_;\n    bool drop_remainder_;\n    std::vector<tstring> select_columns_;\n    std::vector<tstring> select_columns_type_;\n  };\n\n  Dataset* output_ = nullptr;\n  int32_t batch_size_;\n  bool drop_remainder_;\n  std::vector<tstring> select_columns_;\n  std::vector<tstring> select_columns_type_;\n};\n\nconst char* const ParquetDatasetOp::kDatasetType = \"ParquetDataset\";\nconst char* const ParquetDatasetOp::kFileName = \"file_name\";\nconst char* const ParquetDatasetOp::kOutputPbType = \"output_pb_type\";\nconst char* const ParquetDatasetOp::kBatchSize = \"batch_size\";\nconst char* const ParquetDatasetOp::kSelectColumns = \"select_columns\";\nconst char* const ParquetDatasetOp::kSelectColumnsType = \"select_columns_type\";\nconst char* const ParquetDatasetOp::kDropRemainder = \"drop_remainder\";\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"ParquetDataset\").Device(DEVICE_CPU),\n                        ParquetDatasetOp);\n}\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/parse_example_lib.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/parse_example_lib.h\"\n\n#include <algorithm>\n#include <tuple>\n#include <limits>\n\n#include \"absl/strings/match.h\"\n\n#include \"tensorflow/core/platform/threadpool.h\"\n#include \"tensorflow/core/profiler/lib/traceme.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing Instance = ::parser::proto::Instance;\nusing LineId = ::idl::matrix::proto::LineId;\nusing EFeature = ::monolith::io::proto::Feature;\nusing Example = ::monolith::io::proto::Example;\nusing ExampleBatch = ::monolith::io::proto::ExampleBatch;\nusing FeatureListType = ::monolith::io::proto::FeatureListType;\nusing FieldDescriptor = ::google::protobuf::FieldDescriptor;\nusing FeatureConfigs = ::monolith::io::proto::FeatureConfigs;\n\nBaseParser::BaseParser(const std::vector<std::string> &names,\n                       const std::vector<int> &shapes,\n                       const std::vector<DataType> &dtypes,\n                       const std::vector<std::string> extra_names,\n                       DataType input_dtype)\n    : input_dtype_(input_dtype), extra_names_(extra_names) {\n  for (size_t i = 0; i < names.size(); ++i) {\n    name2info_.emplace(names[i], std::make_tuple(i, shapes[i], dtypes[i]));\n    idx2info_.emplace(i, std::make_tuple(names[i], shapes[i], dtypes[i]));\n    if (shapes[i] == -1 && dtypes[i] == DataType::DT_INT64) {\n      ragged_names_.insert(names[i]);\n      idx2info_.emplace(i + names.size(),\n                        std::make_tuple(names[i], shapes[i], dtypes[i]));\n    }\n  }\n}\n\nvoid BaseParser::AllocateFeatures(OpKernelContext *ctx,\n                                  std::vector<Tensor *> *out_tensors,\n                                  OpOutputList *out_list, int batch_size) {\n  profiler::TraceMe activity([]() { return \"AllocateFeatures\"; });\n  std::string name;\n  int shape;\n  DataType dtype;\n\n  for (size_t i = 0; i < name2info_.size(); ++i) {\n    std::tie(name, shape, dtype) = idx2info_[i];\n    if (shape == -1) {\n      OP_REQUIRES(\n          ctx, dtype == DataType::DT_INT64,\n          errors::InvalidArgument(\"If shape is -1, then dtype must be int64\"));\n      OP_REQUIRES_OK(\n          ctx, out_list->allocate(i, {batch_size + 1}, &out_tensors->at(i)));\n    } else {\n      OP_REQUIRES_OK(\n          ctx, out_list->allocate(i, {batch_size, shape}, &out_tensors->at(i)));\n    }\n    std::memset(out_tensors->at(i)->data(), 0,\n                out_tensors->at(i)->TotalBytes());\n  }\n}\n\nvoid BaseParser::AllocateRaggedValues(OpKernelContext *ctx,\n                                      std::vector<Tensor *> *out_tensors,\n                                      OpOutputList *out_list, int batch_size) {\n  profiler::TraceMe activity([]() { return \"AllocateRaggedValues\"; });\n  int idx, shape;\n  DataType dtype;\n  for (const std::string &name : ragged_names_) {\n    std::tie(idx, shape, dtype) = name2info_[name];\n    Tensor *tensor = out_tensors->at(idx);\n    shape = static_cast<int>(tensor->flat<int64>()(batch_size));\n    idx += name2info_.size();\n    OP_REQUIRES_OK(ctx,\n                   out_list->allocate(idx, {shape}, &out_tensors->at(idx)));\n\n    if (shape > 0) {\n      std::memset(out_tensors->at(idx)->data(), 0,\n                  out_tensors->at(idx)->TotalBytes());\n    }\n  }\n}\n\n// TODO: This function can be optimized further if needed:\n//\n// 1. Instead flat tensor inside, flat it outside (Reduce 2/3 running time)\n// 2. Using switch instead of if\nvoid BaseParser::FillFeature(OpKernelContext *ctx, const EFeature &feature,\n                             Tensor *tensor, const std::string &name,\n                             const int shape, const int offset) {\n  if (feature.has_fid_v1_list()) {\n    auto flat = tensor->flat<int64>();\n    flat(offset + 1) = flat(offset) + feature.fid_v1_list().value_size();\n  } else if (feature.has_fid_v2_list()) {\n    auto flat = tensor->flat<int64>();\n    flat(offset + 1) = flat(offset) + feature.fid_v2_list().value_size();\n  } else {\n    if (shape == -1) {\n      auto flat = tensor->flat<int64>();\n      flat(offset + 1) = flat(offset);\n    }\n  }\n\n  if (feature.has_float_list()) {\n    if (shape == 1) {\n      if (feature.float_list().value_size() > 0) {\n        tensor->flat<float>()(offset) = feature.float_list().value(0);\n      } else {\n        tensor->flat<float>()(offset) = std::numeric_limits<float>::min();\n        LOG_EVERY_N(INFO, 10000) << \"float feature \" << name << \" has missing data!\";\n      }\n    } else {\n      auto matrix = tensor->matrix<float>();\n      for (int j = 0; j < std::min(shape, feature.float_list().value_size());\n           ++j) {\n        matrix(offset, j) = feature.float_list().value(j);\n      }\n    }\n  } else if (feature.has_double_list()) {\n    if (shape == 1) {\n      if (feature.double_list().value_size() > 0) {\n        tensor->flat<float>()(offset) = feature.double_list().value(0);\n      } else {\n        tensor->flat<float>()(offset) = std::numeric_limits<float>::min();\n        LOG_EVERY_N(INFO, 10000) << \"double feature \" << name << \" has missing data!\";\n      }\n    } else {\n      auto matrix = tensor->matrix<float>();\n      for (int j = 0; j < std::min(shape, feature.double_list().value_size());\n           ++j) {\n        matrix(offset, j) = feature.double_list().value(j);\n      }\n    }\n  } else if (feature.has_int64_list()) {\n    if (shape == 1) {\n      CHECK_GT(feature.int64_list().value_size(), 0);\n      tensor->flat<int64>()(offset) = feature.int64_list().value(0);\n    } else {\n      auto matrix = tensor->matrix<int64>();\n      for (int j = 0; j < std::min(shape, feature.int64_list().value_size());\n           ++j) {\n        matrix(offset, j) = feature.int64_list().value(j);\n      }\n    }\n  } else if (feature.has_bytes_list()) {\n    OP_REQUIRES(ctx, shape == 1,\n                errors::InvalidArgument(\"shape must be 1 for bytes list!\"));\n    CHECK_GT(feature.bytes_list().value_size(), 0);\n    tensor->flat<tstring>()(offset) = feature.bytes_list().value(0);\n  } else {\n    if (feature.has_fid_v2_lists() || feature.has_float_lists() ||\n        feature.has_double_lists() || feature.has_int64_lists() ||\n        feature.has_bytes_lists()) {\n      LOG(ERROR) << \"list of list is not support yet!\";\n    }\n  }\n}\n\nvoid BaseParser::FillFromLineId(OpKernelContext *ctx, const LineId &line_id,\n                                std::vector<Tensor *> *out_tensors,\n                                const int offset) {\n  int idx, shape;\n  DataType dtype;\n  for (const std::string &name : extra_names_) {\n    std::tie(idx, shape, dtype) = name2info_[name];\n    Tensor *tensor = out_tensors->at(idx);\n    if (name == \"req_time\") {\n      tensor->flat<int64>()(offset) = line_id.req_time();\n    }\n    if (name == \"user_id\") {\n      tensor->flat<tstring>()(offset) = line_id.user_id();\n    } else if (name == \"uid\") {\n      tensor->flat<int64>()(offset) = line_id.uid();\n    } else if (name == \"actions\") {\n      if (shape > line_id.actions_size()) {\n        LOG_EVERY_N(ERROR, 100)\n            << absl::StrFormat(\"Expected actions' shape=%d while got %d\", shape,\n                               line_id.actions_size());\n      }\n      if (shape == 1) {\n        if (line_id.actions_size()) {\n          tensor->flat<int64>()(offset) = line_id.actions(0);\n        }\n      } else {\n        auto matrix = tensor->matrix<int64>();\n        for (int i = 0; i < std::min(shape, line_id.actions_size()); ++i) {\n          matrix(offset, i) = line_id.actions(i);\n        }\n      }\n    } else if (name == \"sample_rate\") {\n      tensor->flat<float>()(offset) = line_id.sample_rate();\n    } else if (name == \"chnid\") {\n      tensor->flat<float>()(offset) = line_id.chnid();\n    } else {\n      const auto *field = descriptor->FindFieldByName(name);\n      OP_REQUIRES_OK(ctx, FillFromLineIdByreflection(line_id, field, tensor,\n                                                     shape, offset));\n    }\n  }\n}\n\nStatus BaseParser::FillFromLineIdByreflection(const LineId &line_id,\n                                              const FieldDescriptor *field,\n                                              Tensor *tensor, int shape,\n                                              int offset) {\n  if (field->is_repeated()) {\n    const int field_size = reflection->FieldSize(line_id, field);\n    switch (field->cpp_type()) {\n      case FieldDescriptor::CPPTYPE_INT32: {\n        if (shape == 1) {\n          tensor->flat<int64>()(offset) =\n              reflection->GetRepeatedInt32(line_id, field, 0);\n        } else {\n          auto matrix = tensor->matrix<int64>();\n          for (int i = 0; i < std::min(shape, field_size); ++i) {\n            matrix(offset, i) = reflection->GetRepeatedInt32(line_id, field, i);\n          }\n        }\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_UINT32: {\n        if (shape == 1) {\n          tensor->flat<int64>()(offset) =\n              reflection->GetRepeatedUInt32(line_id, field, 0);\n        } else {\n          auto matrix = tensor->matrix<int64>();\n          for (int i = 0; i < std::min(shape, field_size); ++i) {\n            matrix(offset, i) =\n                reflection->GetRepeatedUInt32(line_id, field, i);\n          }\n        }\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_INT64: {\n        if (shape == 1) {\n          tensor->flat<int64>()(offset) =\n              reflection->GetRepeatedInt64(line_id, field, 0);\n        } else {\n          auto matrix = tensor->matrix<int64>();\n          for (int i = 0; i < std::min(shape, field_size); ++i) {\n            matrix(offset, i) = reflection->GetRepeatedInt64(line_id, field, i);\n          }\n        }\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_UINT64: {\n        if (shape == 1) {\n          tensor->flat<int64>()(offset) =\n              reflection->GetRepeatedUInt64(line_id, field, 0);\n        } else {\n          auto matrix = tensor->matrix<int64>();\n          for (int i = 0; i < std::min(shape, field_size); ++i) {\n            matrix(offset, i) =\n                reflection->GetRepeatedUInt64(line_id, field, i);\n          }\n        }\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_FLOAT: {\n        if (shape == 1) {\n          tensor->flat<float>()(offset) =\n              reflection->GetRepeatedFloat(line_id, field, 0);\n        } else {\n          auto matrix = tensor->matrix<float>();\n          for (int i = 0; i < std::min(shape, field_size); ++i) {\n            matrix(offset, i) = reflection->GetRepeatedFloat(line_id, field, i);\n          }\n        }\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_DOUBLE: {\n        if (shape == 1) {\n          tensor->flat<float>()(offset) =\n              reflection->GetRepeatedDouble(line_id, field, 0);\n        } else {\n          auto matrix = tensor->matrix<float>();\n          for (int i = 0; i < std::min(shape, field_size); ++i) {\n            matrix(offset, i) =\n                reflection->GetRepeatedDouble(line_id, field, i);\n          }\n        }\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_STRING: {\n        if (shape == 1) {\n          tensor->flat<tstring>()(offset) =\n              reflection->GetRepeatedString(line_id, field, 0);\n        } else {\n          auto matrix = tensor->matrix<tstring>();\n          for (int i = 0; i < std::min(shape, field_size); ++i) {\n            matrix(offset, i) =\n                reflection->GetRepeatedString(line_id, field, i);\n          }\n        }\n        break;\n      }\n      default:\n        return errors::InvalidArgument(field->name(),\n                                       \" Data type not match, only \"\n                                       \"string/int32/int64/float32 \"\n                                       \"supported.\");\n    }\n  } else {\n    switch (field->cpp_type()) {\n      case FieldDescriptor::CPPTYPE_INT32: {\n        auto flat = tensor->flat<int64>();\n        flat(offset) = reflection->GetInt32(line_id, field);\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_UINT32: {\n        auto flat = tensor->flat<int64>();\n        flat(offset) = reflection->GetUInt32(line_id, field);\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_INT64: {\n        auto flat = tensor->flat<int64>();\n        flat(offset) = reflection->GetInt64(line_id, field);\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_UINT64: {\n        auto flat = tensor->flat<int64>();\n        flat(offset) = reflection->GetUInt64(line_id, field);\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_FLOAT: {\n        auto flat = tensor->flat<float>();\n        flat(offset) = reflection->GetFloat(line_id, field);\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_DOUBLE: {\n        auto flat = tensor->flat<float>();\n        flat(offset) = reflection->GetDouble(line_id, field);\n        break;\n      }\n      case FieldDescriptor::CPPTYPE_STRING: {\n        auto flat = tensor->flat<tstring>();\n        flat(offset) = reflection->GetString(line_id, field);\n        break;\n      }\n      default:\n        return errors::InvalidArgument(field->name(),\n                                       \" Data type not match, only \"\n                                       \"int32/int64/float32/string \"\n                                       \"supported.\");\n    }\n  }\n\n  return Status::OK();\n}\n\nExampleParser::ExampleParser(const std::vector<std::string> &names,\n                             const std::vector<int> &shapes,\n                             const std::vector<DataType> &dtypes,\n                             const std::vector<std::string> extra_names,\n                             DataType input_dtype, FeatureNameMapper *mapper)\n    : BaseParser(names, shapes, dtypes, extra_names, input_dtype),\n      mapper_(mapper) {\n  std::unordered_set<std::string> extra_name_set(extra_names.begin(),\n                                                 extra_names.end());\n  std::vector<std::string> sparse_feature_names;\n  for (size_t i = 0; i < names.size(); ++i) {\n    if (!extra_name_set.count(names[i]) && shapes[i] == -1) {\n      sparse_feature_names.push_back(names[i]);\n    }\n  }\n\n  CHECK(mapper_->RegisterValidNames(sparse_feature_names));\n}\n\nvoid ExampleParser::Parse(OpKernelContext *ctx,\n                          const std::vector<const Example *> &examples,\n                          OpOutputList *out_list) {\n  int batch_size = examples.size();\n  std::vector<Tensor *> out_tensors;\n  out_tensors.resize(idx2info_.size());\n\n  int idx, shape;\n  DataType dtype;\n\n  // 1) allocate output tensors for ragged splits and other non-ragged\n  AllocateFeatures(ctx, &out_tensors, out_list, batch_size);\n\n  // 2) fill all tensors expect ragged values\n  int offset = 0;\n  {\n    profiler::TraceMe activity(\n        []() { return \"FillAllTensorsExceptRaggedValues\"; });\n    for (const Example *example : examples) {\n      std::unordered_set<std::string> appeared;\n      appeared.reserve(example->named_feature_size());\n      bool has_fill_label = false, has_fill_instance_weight = false;\n      for (const auto &named_feature : example->named_feature()) {\n        // FeatureNameMapper\n        const std::string &name = named_feature.name();\n        auto it = name2info_.find(name);\n        if (it == name2info_.end()) continue;\n        std::tie(idx, shape, dtype) = it->second;\n        FillFeature(ctx, named_feature.feature(), out_tensors[idx], name, shape,\n                    offset);\n        appeared.insert(name);\n        if (name == \"label\") {\n          has_fill_label = true;\n        } else if (name == \"instance_weight\") {\n          has_fill_instance_weight = true;\n        }\n      }\n\n      for (const auto &ragged : ragged_names_) {\n        if (appeared.find(ragged) == appeared.end()) {\n          std::tie(idx, shape, dtype) = name2info_[ragged];\n          auto flat = out_tensors[idx]->flat<int64>();\n          flat(offset + 1) += flat(offset);\n        }\n      }\n\n      // for label\n      if (!has_fill_label) {\n        auto it = name2info_.find(\"label\");\n        if (it != name2info_.end()) {\n          std::tie(idx, shape, dtype) = it->second;\n          Tensor *tensor = out_tensors[idx];\n          if (example->label_size() > 0) {\n            if (shape == 1) {\n              tensor->flat<float>()(offset) = example->label(0);\n            } else {\n              auto matrix = tensor->matrix<float>();\n              for (int j = 0; j < shape; ++j) {\n                if (j < example->label_size()) {\n                  matrix(offset, j) = example->label(j);\n                } else {\n                  matrix(offset, j) = internal::INVALID_LABEL;\n                }\n              }\n            }\n          } else {\n            if (shape == 1) {\n              tensor->flat<float>()(offset) = internal::INVALID_LABEL;\n            } else {\n              auto matrix = tensor->matrix<float>();\n              for (int j = 0; j < shape; ++j) {\n                matrix(offset, j) = internal::INVALID_LABEL;\n              }\n            }\n          }\n        }\n      }\n\n      // for instance_weight\n      if (!has_fill_instance_weight) {\n        auto it = name2info_.find(\"instance_weight\");\n        if (it != name2info_.end()) {\n          std::tie(idx, shape, dtype) = it->second;\n          Tensor *tensor = out_tensors[idx];\n          float instance_weight = example->instance_weight();\n          tensor->flat<float>()(offset) =\n              instance_weight > 0 ? instance_weight : 1.0;\n        }\n      }\n\n      // for extra fields in line_id\n      if (!extra_names_.empty()) {\n        const LineId &line_id = example->line_id();\n        FillFromLineId(ctx, line_id, &out_tensors, offset);\n      }\n\n      offset++;\n    }\n  }\n\n  // 3) allocate output tensors for ragged values\n  AllocateRaggedValues(ctx, &out_tensors, out_list, batch_size);\n\n  // 4) fill ragged values\n  if (ragged_names_.size()) {\n    profiler::TraceMe activity([]() { return \"FillRaggedValues\"; });\n    offset = 0;\n    for (const Example *example : examples) {\n      for (const auto &named_feature : example->named_feature()) {\n        const auto &name = named_feature.name();\n        auto it = ragged_names_.find(name);\n        if (it != ragged_names_.end()) {\n          std::tie(idx, shape, dtype) = name2info_[name];\n          auto splits = out_tensors[idx]->flat<int64>();\n          auto values = out_tensors[idx + name2info_.size()]->flat<int64>();\n          int start = static_cast<int>(splits(offset));\n          const auto &feature = named_feature.feature();\n          if (feature.has_fid_v1_list()) {\n            for (int i = 0; i < feature.fid_v1_list().value_size(); ++i) {\n              values(start + i) =\n                  convert_fid_v1_to_v2(feature.fid_v1_list().value(i));\n            }\n          } else if (feature.has_fid_v2_list()) {\n            for (int i = 0; i < feature.fid_v2_list().value_size(); ++i) {\n              values(start + i) = feature.fid_v2_list().value(i);\n            }\n          }\n        }\n      }\n\n      offset++;\n    }\n  }\n}\n\nExampleBatchParser::ExampleBatchParser(\n    const std::vector<std::string> &names, const std::vector<int> &shapes,\n    const std::vector<DataType> &dtypes,\n    const std::vector<std::string> extra_names, DataType input_dtype)\n    : BaseParser(names, shapes, dtypes, extra_names, input_dtype) {}\n\nvoid ExampleBatchParser::Parse(OpKernelContext *ctx,\n                               const ExampleBatch &example_batch,\n                               OpOutputList *out_list) {\n  int batch_size = example_batch.batch_size();\n  std::vector<Tensor *> out_tensors;\n  out_tensors.resize(idx2info_.size());\n\n  std::string name;\n  int idx, shape;\n  DataType dtype;\n\n  // 1) allocate output tensors for ragged splits and other non-ragged\n  AllocateFeatures(ctx, &out_tensors, out_list, batch_size);\n\n  // 2) fill all tensors expect ragged values\n  for (const auto &named_feature_list : example_batch.named_feature_list()) {\n    name = named_feature_list.name();\n    if (name == \"__LINE_ID__\") {\n      // for extra fields in line_id\n      if (extra_names_.size() > 0) {\n        int offset = 0;\n        for (const auto &feature : named_feature_list.feature()) {\n          LineId line_id;\n          CHECK_GT(feature.bytes_list().value_size(), 0);\n          const auto serialized = feature.bytes_list().value(0);\n          OP_REQUIRES(\n              ctx, line_id.ParseFromArray(serialized.data(), serialized.size()),\n              errors::FailedPrecondition(\"Failed to parse the LineId.\"));\n          FillFromLineId(ctx, line_id, &out_tensors, offset);\n          offset++;\n        }\n      }\n    } else if (name == \"__LABEL__\") {\n      // for label\n      auto it = name2info_.find(\"label\");\n      if (it != name2info_.end()) {\n        std::tie(idx, shape, dtype) = it->second;\n        Tensor *tensor = out_tensors[idx];\n\n        int offset = 0;\n        for (const auto &feature : named_feature_list.feature()) {\n          if (shape == 1) {\n            CHECK_GT(feature.float_list().value_size(), 0);\n            tensor->flat<float>()(offset) = feature.float_list().value(0);\n          } else {\n            auto matrix = tensor->matrix<float>();\n            for (int j = 0;\n                 j < std::min(shape, feature.float_list().value_size()); ++j) {\n              matrix(offset, j) = feature.float_list().value(j);\n            }\n          }\n          offset++;\n        }\n      }\n    } else if (name == \"instance_weight\") {\n      auto it = name2info_.find(\"instance_weight\");\n      if (it != name2info_.end()) {\n        std::tie(idx, shape, dtype) = it->second;\n        Tensor *tensor = out_tensors[idx];\n        int offset = 0;\n        for (const auto &feature : named_feature_list.feature()) {\n          CHECK_GT(feature.float_list().value_size(), 0);\n          float instance_weight = feature.float_list().value(0);\n          tensor->flat<float>()(offset) =\n              instance_weight > 0 ? instance_weight : 1.0;\n          offset++;\n        }\n      }\n    } else {\n      auto it = name2info_.find(name);\n      if (it == name2info_.end()) continue;\n      std::tie(idx, shape, dtype) = name2info_[name];\n      Tensor *tensor = out_tensors[idx];\n\n      if (named_feature_list.type() == FeatureListType::SHARED) {\n        CHECK_GT(named_feature_list.feature_size(), 0);\n        const auto &feature = named_feature_list.feature(0);\n        for (int offset = 0; offset < batch_size; ++offset) {\n          FillFeature(ctx, feature, tensor, name, shape, offset);\n        }\n      } else {\n        int offset = 0;\n        for (const auto &feature : named_feature_list.feature()) {\n          FillFeature(ctx, feature, tensor, name, shape, offset);\n          offset++;\n        }\n      }\n    }\n  }\n\n  // 3) allocate output tensors for ragged values\n  AllocateRaggedValues(ctx, &out_tensors, out_list, batch_size);\n\n  // 4) fill ragged values\n  if (ragged_names_.size()) {\n    for (const auto &named_feature_list : example_batch.named_feature_list()) {\n      name = named_feature_list.name();\n      auto it = ragged_names_.find(name);\n      if (it != ragged_names_.end()) {\n        std::tie(idx, shape, dtype) = name2info_[name];\n        auto splits = out_tensors[idx]->flat<int64>();\n        auto values = out_tensors[idx + name2info_.size()]->flat<int64>();\n\n        if (named_feature_list.type() == FeatureListType::SHARED) {\n          const auto &feature = named_feature_list.feature(0);\n          for (int offset = 0; offset < batch_size; ++offset) {\n            int start = static_cast<int>(splits(offset));\n            if (feature.has_fid_v1_list()) {\n              for (int i = 0; i < feature.fid_v1_list().value_size(); ++i) {\n                values(start + i) =\n                    convert_fid_v1_to_v2(feature.fid_v1_list().value(i));\n              }\n            } else if (feature.has_fid_v2_list()) {\n              for (int i = 0; i < feature.fid_v2_list().value_size(); ++i) {\n                values(start + i) = feature.fid_v2_list().value(i);\n              }\n            }\n          }\n        } else {\n          int offset = 0;\n          for (const auto &feature : named_feature_list.feature()) {\n            int start = static_cast<int>(splits(offset));\n            if (feature.has_fid_v1_list()) {\n              for (int i = 0; i < feature.fid_v1_list().value_size(); ++i) {\n                values(start + i) =\n                    convert_fid_v1_to_v2(feature.fid_v1_list().value(i));\n              }\n            } else if (feature.has_fid_v2_list()) {\n              for (int i = 0; i < feature.fid_v2_list().value_size(); ++i) {\n                values(start + i) = feature.fid_v2_list().value(i);\n              }\n            }\n            offset++;\n          }\n        }\n      }\n    }\n  }\n}\n\nExampleBatchListParser::ExampleBatchListParser(\n    const std::vector<std::string> &names, const std::vector<int> &shapes,\n    const std::vector<DataType> &dtypes,\n    const std::vector<std::string> &extra_names, DataType input_dtype)\n    : BaseParser(names, shapes, dtypes, extra_names, input_dtype) {}\n\nvoid ExampleBatchListParser::Parse(\n    OpKernelContext *ctx, const ExampleBatch &example_batch,\n    const std::vector<internal::TaskConfig> &label_config_,\n    float positive_label, float negative_label, OpOutputList *out_list) {\n  int batch_size = example_batch.batch_size();\n  std::vector<Tensor *> out_tensors;\n  out_tensors.resize(idx2info_.size());\n\n  std::string name;\n  int idx, shape;\n  DataType dtype;\n\n  // 1) allocate output tensors for ragged splits and other non-ragged\n  AllocateFeatures(ctx, &out_tensors, out_list, batch_size);\n\n  // 2) fill all tensors expect ragged values\n  for (const auto &named_feature_list : example_batch.named_feature_list()) {\n    name = named_feature_list.name();\n    if (name == \"__LINE_ID__\") {\n      auto it = name2info_.find(\"label\");\n      if (it != name2info_.end()) {\n        std::tie(idx, shape, dtype) = it->second;\n      }\n\n      // for extra fields in line_id\n      if (extra_names_.size() > 0) {\n        int offset = 0;\n        for (const auto &feature : named_feature_list.feature()) {\n          LineId line_id;\n          CHECK_GT(feature.bytes_list().value_size(), 0);\n          const auto serialized = feature.bytes_list().value(0);\n          OP_REQUIRES(\n              ctx, line_id.ParseFromArray(serialized.data(), serialized.size()),\n              errors::FailedPrecondition(\"Failed to parse the LineId.\"));\n          FillFromLineId(ctx, line_id, &out_tensors, offset);\n          if (it != name2info_.end()) {\n            FillLabelFromLineId(ctx, line_id, label_config_, positive_label,\n                                negative_label, out_tensors[idx], offset);\n          }\n          offset++;\n        }\n      }\n    } else if (name == \"instance_weight\") {\n      auto it = name2info_.find(\"instance_weight\");\n      if (it != name2info_.end()) {\n        std::tie(idx, shape, dtype) = it->second;\n        Tensor *tensor = out_tensors[idx];\n        int offset = 0;\n        for (const auto &feature : named_feature_list.feature()) {\n          CHECK_GT(feature.float_list().value_size(), 0);\n          float instance_weight = feature.float_list().value(0);\n          tensor->flat<float>()(offset++) =\n              instance_weight > 0 ? instance_weight : 1.0;\n        }\n      }\n    } else {\n      auto it = name2info_.find(name);\n      if (it == name2info_.end()) continue;\n      std::tie(idx, shape, dtype) = it->second;\n      Tensor *tensor = out_tensors[idx];\n\n      if (named_feature_list.type() == FeatureListType::SHARED) {\n        const auto &feature = named_feature_list.feature(0);\n        for (int offset = 0; offset < batch_size; ++offset) {\n          FillFeature(ctx, feature, tensor, name, shape, offset);\n        }\n      } else {\n        int offset = 0;\n        for (const auto &feature : named_feature_list.feature()) {\n          FillFeature(ctx, feature, tensor, name, shape, offset);\n          offset++;\n        }\n      }\n    }\n  }\n\n  // 3) allocate output tensors for ragged values\n  AllocateRaggedValues(ctx, &out_tensors, out_list, batch_size);\n\n  // 4) fill ragged values\n  for (const auto &named_feature_list : example_batch.named_feature_list()) {\n    name = named_feature_list.name();\n    auto it = ragged_names_.find(name);\n    if (it != ragged_names_.end()) {\n      int slot = named_feature_list.id();\n      std::tie(idx, shape, dtype) = name2info_[name];\n      auto splits = out_tensors[idx]->flat<int64>();\n      auto values = out_tensors[idx + name2info_.size()]->flat<int64>();\n\n      if (named_feature_list.type() == FeatureListType::SHARED) {\n        const auto &feature = named_feature_list.feature(0);\n        for (int offset = 0; offset < batch_size; ++offset) {\n          int start = static_cast<int>(splits(offset));\n          if (feature.has_fid_v1_list()) {\n            for (int i = 0; i < feature.fid_v1_list().value_size(); ++i) {\n              values(start + i) =\n                  GetFidV2(slot, feature.fid_v1_list().value(i));\n            }\n          } else if (feature.has_fid_v2_list()) {\n            for (int i = 0; i < feature.fid_v2_list().value_size(); ++i) {\n              values(start + i) =\n                  GetFidV2(slot, feature.fid_v2_list().value(i));\n            }\n          }\n        }\n      } else {\n        int offset = 0;\n        for (const auto &feature : named_feature_list.feature()) {\n          int start = static_cast<int>(splits(offset));\n          if (feature.has_fid_v1_list()) {\n            for (int i = 0; i < feature.fid_v1_list().value_size(); ++i) {\n              values(start + i) =\n                  GetFidV2(slot, feature.fid_v1_list().value(i));\n            }\n          } else if (feature.has_fid_v2_list()) {\n            for (int i = 0; i < feature.fid_v2_list().value_size(); ++i) {\n              values(start + i) =\n                  GetFidV2(slot, feature.fid_v2_list().value(i));\n            }\n          }\n          offset++;\n        }\n      }\n    }\n  }\n}\n\nvoid ExampleBatchListParser::FillLabelFromLineId(\n    OpKernelContext *ctx, const ::idl::matrix::proto::LineId &line_id,\n    const std::vector<internal::TaskConfig> &label_config_,\n    float positive_label, float negative_label, Tensor *out_tensor,\n    const int offset) {\n  std::set<int32_t> actions(line_id.actions().begin(), line_id.actions().end());\n\n  int label_idx = 0;\n  auto matrix = out_tensor->matrix<float>();\n  for (const auto &task_conf : label_config_) {\n    if (internal::HasIntersection(task_conf.pos_actions, actions)) {\n      matrix(offset, label_idx) = positive_label;\n    } else {\n      if (task_conf.neg_actions.empty()) {\n        matrix(offset, label_idx) = negative_label;\n      } else {\n        if (internal::HasIntersection(task_conf.neg_actions, actions)) {\n          matrix(offset, label_idx) = negative_label;\n        } else {\n          matrix(offset, label_idx) = internal::INVALID_LABEL;\n        }\n      }\n    }\n\n    label_idx++;\n  }\n}\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/parse_example_lib.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_KERNELS_PARSE_EXAMPLE_LIB_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_KERNELS_PARSE_EXAMPLE_LIB_H_\n\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"google/protobuf/descriptor.h\"\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/platform/env.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass BaseParser {\n public:\n  explicit BaseParser(const std::vector<std::string> &names,\n                      const std::vector<int> &shapes,\n                      const std::vector<DataType> &dtypes,\n                      const std::vector<std::string> extra_names,\n                      DataType input_dtype);\n\n protected:\n  void AllocateFeatures(OpKernelContext *ctx,\n                        std::vector<Tensor *> *out_tensors,\n                        OpOutputList *out_list, int batch_size);\n\n  void AllocateRaggedValues(OpKernelContext *ctx,\n                            std::vector<Tensor *> *out_tensors,\n                            OpOutputList *out_list, int batch_size);\n\n  void FillFeature(OpKernelContext *ctx,\n                   const ::monolith::io::proto::Feature &feature,\n                   Tensor *tensor, const std::string &name, int shape,\n                   int offset);\n\n  void FillFromLineId(OpKernelContext *ctx,\n                      const ::idl::matrix::proto::LineId &line_id,\n                      std::vector<Tensor *> *out_tensors, const int offset);\n\n  Status FillFromLineIdByreflection(\n      const ::idl::matrix::proto::LineId &line_id,\n      const ::google::protobuf::FieldDescriptor *field, Tensor *tensor,\n      int shape, int offset);\n\n  std::unordered_map<std::string, std::tuple<int, int, DataType>> name2info_;\n  std::unordered_map<int, std::tuple<std::string, int, DataType>> idx2info_;\n  std::unordered_set<std::string> ragged_names_;\n  std::vector<std::string> extra_names_;\n  DataType input_dtype_;\n\n  const ::google::protobuf::Descriptor *descriptor =\n      ::idl::matrix::proto::LineId::GetDescriptor();\n  const ::google::protobuf::Reflection *reflection =\n      ::idl::matrix::proto::LineId::GetReflection();\n};\n\nclass ExampleParser : public BaseParser {\n public:\n  explicit ExampleParser(const std::vector<std::string> &names,\n                         const std::vector<int> &shapes,\n                         const std::vector<DataType> &dtypes,\n                         const std::vector<std::string> extra_names,\n                         DataType input_dtype, FeatureNameMapper *mapper);\n\n  void Parse(\n      OpKernelContext *ctx,\n      const std::vector<const ::monolith::io::proto::Example *> &examples,\n      OpOutputList *out_list);\n\n private:\n  FeatureNameMapper *mapper_ = nullptr;\n};\n\nclass ExampleBatchParser : public BaseParser {\n public:\n  explicit ExampleBatchParser(const std::vector<std::string> &names,\n                              const std::vector<int> &shapes,\n                              const std::vector<DataType> &dtypes,\n                              const std::vector<std::string> extra_names,\n                              DataType input_dtype);\n\n  void Parse(OpKernelContext *ctx,\n             const ::monolith::io::proto::ExampleBatch &example_batch,\n             OpOutputList *out_list);\n};\n\nclass ExampleBatchListParser : public BaseParser {\n public:\n  explicit ExampleBatchListParser(const std::vector<std::string> &names,\n                                  const std::vector<int> &shapes,\n                                  const std::vector<DataType> &dtypes,\n                                  const std::vector<std::string> &extra_names,\n                                  DataType input_dtype);\n\n  void Parse(OpKernelContext *ctx,\n             const ::monolith::io::proto::ExampleBatch &example_batchs,\n             const std::vector<internal::TaskConfig> &label_config_,\n             float positive_label, float negative_label,\n             OpOutputList *out_list);\n\n private:\n  uint64 mask_ = (1 << 48) - 1;\n\n  void FillLabelFromLineId(\n      OpKernelContext *ctx, const ::idl::matrix::proto::LineId &line_id,\n      const std::vector<internal::TaskConfig> &label_config_,\n      float positive_label, float negative_label, Tensor *out_tensor,\n      const int offset);\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif MONOLITH_NATIVE_TRAINING_DATA_KERNELS_PARSE_EXAMPLE_LIB_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/parse_input_data_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <algorithm>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_cat.h\"\n#include \"absl/strings/str_split.h\"\n#include \"google/protobuf/descriptor.h\"\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n#include \"monolith/native_training/data/kernels/feature_name_mapper_tf_bridge.h\"\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/kernels/parse_example_lib.h\"\n#include \"monolith/native_training/data/training_instance/cc/data_reader.h\"\n#include \"monolith/native_training/data/training_instance/cc/parse_instance_lib.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/platform/env.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n#include \"tensorflow/core/profiler/lib/traceme.h\"\n\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/string_view.h\"\n#include \"monolith/native_training/runtime/common/metrics.h\"\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nusing Instance = ::parser::proto::Instance;\nusing LineId = ::idl::matrix::proto::LineId;\nusing EFeature = ::monolith::io::proto::Feature;\nusing Example = ::monolith::io::proto::Example;\nusing ExampleBatch = ::monolith::io::proto::ExampleBatch;\nusing FieldDescriptor = ::google::protobuf::FieldDescriptor;\nusing ExampleParser = ::tensorflow::monolith_tf::ExampleParser;\nusing ExampleBatchParser = ::tensorflow::monolith_tf::ExampleBatchParser;\nusing ExampleBatchListParser =\n    ::tensorflow::monolith_tf::ExampleBatchListParser;\nusing NamedFeatureList = ::monolith::io::proto::NamedFeatureList;\nusing FeatureConfigs = ::monolith::io::proto::FeatureConfigs;\n\nclass DataCounter {\n public:\n  explicit DataCounter(std::string op, bool emit_mini_batch,\n                       int64_t emit_every_n_batch = 2000)\n      : op_(std::move(op)),\n        emit_mini_batch_(emit_mini_batch),\n        mini_batch_num_(0),\n        emit_every_n_batch_(emit_every_n_batch),\n        last_batch_size_(0) {\n    CHECK_GT(emit_every_n_batch_, 0);\n  }\n\n  ~DataCounter() {\n    LOG(INFO) << absl::StrFormat(\n        \"Finally metrics_emit(counter) [data_consume_num] op=%s, \"\n        \"batch_size=%d, total_mini_batch_num=%llu\",\n        op_, last_batch_size_, mini_batch_num_);\n    int64_t remainder = mini_batch_num_ % emit_every_n_batch_;\n    if (remainder) {\n      monolith::GetMetrics()->emit_counter(\"data_consume_num\",\n                                           last_batch_size_ * remainder,\n                                           absl::StrFormat(\"op=%s\", op_));\n      if (emit_mini_batch_) {\n        monolith::GetMetrics()->emit_counter(\"mini_batch_num\", remainder,\n                                             absl::StrFormat(\"op=%s\", op_));\n      }\n    }\n  }\n\n  void EmitDataConsumeNumCounter(int batch_size) {\n    mini_batch_num_ += 1;\n    last_batch_size_ = batch_size;\n    LOG_EVERY_N_SEC(INFO, 300) << absl::StrFormat(\n        \"metrics_emit(counter) [data_consume_num] op=%s, \"\n        \"batch_size=%d, total_mini_batch_num=%llu\",\n        op_, batch_size, mini_batch_num_);\n    if (mini_batch_num_ % emit_every_n_batch_ == 0) {\n      monolith::GetMetrics()->emit_counter(\"data_consume_num\",\n                                           batch_size * emit_every_n_batch_,\n                                           absl::StrFormat(\"op=%s\", op_));\n      if (emit_mini_batch_) {\n        monolith::GetMetrics()->emit_counter(\"mini_batch_num\",\n                                             emit_every_n_batch_,\n                                             absl::StrFormat(\"op=%s\", op_));\n      }\n    }\n  }\n\n private:\n  std::string op_;\n  bool emit_mini_batch_;\n  int64_t mini_batch_num_;\n  int64_t emit_every_n_batch_;\n  int64_t last_batch_size_;\n};\n\nStatus GetParserConfig(OpKernelConstruction *ctx, InstanceParserConfig *c,\n                       std::vector<int> *index) {\n  TF_RETURN_IF_ERROR(ctx, ctx->GetAttr(\"fidv1_features\", &(c->fidv1_features)));\n  TF_RETURN_IF_ERROR(ctx, ctx->GetAttr(\"fidv2_features\", &(c->fidv2_features)));\n\n  std::vector<std::string> names;\n  std::vector<int> shapes;\n  std::vector<DataType> dtypes;\n  std::vector<std::string> extra_names;\n  std::unordered_set<std::string> misc({\"label\", \"instance_weight\"});\n  TF_RETURN_IF_ERROR(ctx, ctx->GetAttr(\"names\", &names));\n  TF_RETURN_IF_ERROR(ctx, ctx->GetAttr(\"shapes\", &shapes));\n  TF_RETURN_IF_ERROR(ctx, ctx->GetAttr(\"dtypes\", &dtypes));\n  TF_RETURN_IF_ERROR(ctx, ctx->GetAttr(\"extra_names\", &extra_names));\n\n  int ragged_size = c->fidv1_features.size() + c->fidv2_features.size();\n  if (names.size() != shapes.size() ||\n      shapes.size() + ragged_size != dtypes.size()) {\n    return errors::InvalidArgument(\n        \"Num of names, shapes and dtypes do not match\");\n  }\n\n  for (size_t i = 0; i < names.size(); ++i) {\n    if (i < ragged_size) {\n      continue;  // skip fidv1/fidv2\n    }\n    std::string name = names[i];\n    int dim = shapes[i];\n    DataType dtype = dtypes[i];\n    auto eit = std::find(extra_names.begin(), extra_names.end(), name);\n    if (eit != extra_names.end() || misc.find(name) != misc.end()) {  // extra\n      switch (dtype) {\n        case DataType::DT_INT64:\n          c->misc_int64_features.push_back(name);\n          c->misc_int64_dims.push_back(dim);\n          break;\n        case DataType::DT_FLOAT:\n          c->misc_float_features.push_back(name);\n          c->misc_float_dims.push_back(dim);\n          break;\n        case DataType::DT_STRING:\n          c->misc_string_features.push_back(name);\n          c->misc_string_dims.push_back(dim);\n          break;\n        default:\n          return errors::InvalidArgument(\"Unsupported data type!\");\n      }\n    } else {  // dense\n      switch (dtype) {\n        case DataType::DT_INT64:\n          c->int64_features.push_back(name);\n          c->int64_feature_dims.push_back(dim);\n          break;\n        case DataType::DT_FLOAT:\n          c->float_features.push_back(name);\n          c->float_feature_dims.push_back(dim);\n          break;\n        case DataType::DT_STRING:\n          c->string_features.push_back(name);\n          c->string_feature_dims.push_back(dim);\n          break;\n        default:\n          return errors::InvalidArgument(\"Unsupported data type!\");\n      }\n    }\n  }\n\n  std::vector<std::string> new_names;\n  new_names.reserve(names.size());\n  new_names.insert(new_names.end(), names.begin(), names.begin() + ragged_size);\n  new_names.insert(new_names.end(), c->float_features.begin(),\n                   c->float_features.end());\n  new_names.insert(new_names.end(), c->int64_features.begin(),\n                   c->int64_features.end());\n  new_names.insert(new_names.end(), c->string_features.begin(),\n                   c->string_features.end());\n  new_names.insert(new_names.end(), c->misc_float_features.begin(),\n                   c->misc_float_features.end());\n  new_names.insert(new_names.end(), c->misc_int64_features.begin(),\n                   c->misc_int64_features.end());\n  new_names.insert(new_names.end(), c->misc_string_features.begin(),\n                   c->misc_string_features.end());\n\n  index->reserve(dtypes.size());\n  std::unordered_map<std::string, int> name_to_idx;\n  for (size_t i = 0; i < names.size(); ++i) {\n    name_to_idx.emplace(names[i], i);\n  }\n\n  for (size_t i = 0; i < new_names.size(); ++i) {\n    int idx = name_to_idx[new_names[i]];\n    if (i < ragged_size) {\n      (*index)[i] = idx;\n      (*index)[i + ragged_size] = idx + names.size();\n    } else {\n      (*index)[i + ragged_size] = idx;\n    }\n  }\n\n  return Status::OK();\n}\n\nclass ParseStringInstancesOp : public OpKernel {\n public:\n  explicit ParseStringInstancesOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    InstanceParserConfig config;\n    OP_REQUIRES_OK(ctx, GetParserConfig(ctx, &config, &index_));\n    config.collapse_batch_dim = false;\n\n    parser_ = std::make_unique<InstanceParser>(config);\n    OP_REQUIRES_OK(ctx, parser_->Init());\n    counter_ = std::make_unique<DataCounter>(\"ParseStringInstancesOp\", true);\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n    const auto &serialized_flat = pb_input->flat<tstring>();\n    const int batch_size = serialized_flat.size();\n    std::vector<Instance> instances(batch_size);  // has alocated memory\n\n    {\n      profiler::TraceMe activity([]() { return \"Deserialize\"; });\n      auto deserialize_fn = [&](int64 begin, int64 end) {\n        for (int64 i = begin; i < end; ++i) {\n          const auto &serialized = serialized_flat(i);\n          OP_REQUIRES(\n              ctx,\n              instances[i].ParseFromArray(serialized.data(), serialized.size()),\n              errors::FailedPrecondition(\"Failed to parse the Instance.\"));\n        }\n      };\n      auto workers = ctx->device()->tensorflow_cpu_worker_threads()->workers;\n      workers->ParallelFor(batch_size,\n                           tensorflow::thread::ThreadPool::SchedulingParams(\n                               tensorflow::thread::ThreadPool::\n                                   SchedulingStrategy::kFixedBlockSize,\n                               absl::nullopt, 1),\n                           deserialize_fn);\n    }\n\n    InstanceParser::Output output;\n    {\n      profiler::TraceMe activity([]() { return \"Parse\"; });\n      OP_REQUIRES_OK(ctx, parser_->Parse(ctx, instances, &output));\n    }\n\n    OpOutputList out_list;\n    {\n      profiler::TraceMe activity([]() { return \"PrepareOutput\"; });\n      OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &out_list));\n\n      OP_REQUIRES(\n          ctx, output.tensors.size() == out_list.size(),\n          errors::FailedPrecondition(\"output tensor size doesn't match\"));\n      for (size_t i = 0; i < output.tensors.size(); ++i) {\n        out_list.set(index_[i], output.tensors[i]);\n      }\n    }\n\n    counter_->EmitDataConsumeNumCounter(batch_size);\n  }\n\n protected:\n  const std::vector<int> &GetIndex() const { return index_; }\n  InstanceParser *GetParse() const { return parser_.get(); }\n  DataCounter *GetCounter() const { return counter_.get(); }\n\n private:\n  std::vector<int> index_;\n  std::unique_ptr<InstanceParser> parser_;\n  std::unique_ptr<DataCounter> counter_;\n};\n\nclass ParseStringInstancesV2Op : public ParseStringInstancesOp {\n public:\n  explicit ParseStringInstancesV2Op(OpKernelConstruction *ctx)\n      : ParseStringInstancesOp(ctx) {}\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n    const auto &serialized_flat = pb_input->flat<tstring>();\n    int batch_size = serialized_flat.size();\n    std::vector<Instance> instances(batch_size);  // has alocated memory\n\n    for (int i = 0; i < batch_size; ++i) {\n      const auto &serialized = serialized_flat(i);\n      OP_REQUIRES(\n          ctx,\n          instances[i].ParseFromArray(serialized.data(), serialized.size()),\n          errors::FailedPrecondition(\"Failed to parse the Instance.\"));\n    }\n\n    InstanceParser::Output output;\n    OP_REQUIRES_OK(ctx, ParseStringInstancesOp::GetParse()->Parse(\n                            ctx, instances, &output));\n\n    OpOutputList out_list;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &out_list));\n\n    OP_REQUIRES(ctx, output.tensors.size() == out_list.size(),\n                errors::FailedPrecondition(\"output tensor size doesn't match\"));\n    for (size_t i = 0; i < output.tensors.size(); ++i) {\n      out_list.set(ParseStringInstancesOp::GetIndex()[i], output.tensors[i]);\n    }\n\n    Tensor *instance_tensor;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(\"sparse_features\",\n                                             TensorShape({\n                                                 batch_size,\n                                             }),\n                                             &instance_tensor));\n    for (size_t i = 0; i < batch_size; ++i) {\n      instance_tensor->flat<Variant>()(i) = std::move(instances[i]);\n    }\n    ParseStringInstancesOp::GetCounter()->EmitDataConsumeNumCounter(batch_size);\n  }\n};\n\nclass ParseVariantInstancesOp : public OpKernel {\n public:\n  explicit ParseVariantInstancesOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    InstanceParserConfig config;\n    OP_REQUIRES_OK(ctx, GetParserConfig(ctx, &config, &index_));\n    config.collapse_batch_dim = false;\n\n    parser_ = std::make_unique<InstanceParser>(config);\n    OP_REQUIRES_OK(ctx, parser_->Init());\n    counter_ = std::make_unique<DataCounter>(\"ParseVariantInstancesOp\", true);\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n\n    TTypes<Variant>::ConstVec pb_variant_tensor = pb_input->vec<Variant>();\n    const int batch_size = pb_variant_tensor.dimension(0);\n    std::vector<Instance> instances;  // not allocated memory\n    instances.reserve(batch_size);\n    for (int i = 0; i < batch_size; ++i) {\n      instances.push_back(*pb_variant_tensor(i).get<Instance>());\n    }\n\n    InstanceParser::Output output;\n    OP_REQUIRES_OK(ctx, parser_->Parse(ctx, instances, &output));\n\n    OpOutputList out_list;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &out_list));\n    for (size_t i = 0; i < output.tensors.size(); ++i) {\n      out_list.set(index_[i], output.tensors[i]);\n    }\n\n    counter_->EmitDataConsumeNumCounter(batch_size);\n  }\n\n private:\n  std::vector<int> index_;\n  std::unique_ptr<InstanceParser> parser_;\n  std::unique_ptr<DataCounter> counter_;\n};\n\nclass ParseVariantInstancesV2Op : public ParseVariantInstancesOp {\n public:\n  explicit ParseVariantInstancesV2Op(OpKernelConstruction *ctx)\n      : ParseVariantInstancesOp(ctx) {}\n\n  void Compute(OpKernelContext *ctx) override {\n    ParseVariantInstancesOp::Compute(ctx);\n    OP_REQUIRES_OK(ctx, ctx->set_output(\"sparse_features\", ctx->input(0)));\n  }\n};\n\nclass ParseStringExamplesOp : public OpKernel {\n public:\n  explicit ParseStringExamplesOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    std::vector<std::string> names;\n    std::vector<int> shapes;\n    std::vector<DataType> dtypes;\n    std::vector<std::string> extra_names;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"names\", &names));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"shapes\", &shapes));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"dtypes\", &dtypes));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"extra_names\", &extra_names));\n\n    auto creator = [this](FeatureNameMapperTfBridge **out_mapper) {\n      TF_RETURN_IF_ERROR(FeatureNameMapperTfBridge::New(out_mapper));\n      return Status::OK();\n    };\n    ResourceMgr *resource_mgr = ctx->resource_manager();\n    OP_REQUIRES_OK(ctx,\n                   resource_mgr->LookupOrCreate<FeatureNameMapperTfBridge>(\n                       resource_mgr->default_container(),\n                       FeatureNameMapperTfBridge::kName, &mapper_, creator));\n    parser_ = std::make_unique<ExampleParser>(names, shapes, dtypes,\n                                              extra_names, DataType::DT_STRING,\n                                              mapper_->GetFeatureNameMapper());\n    counter_ = std::make_unique<DataCounter>(\"ParseStringExamplesOp\", true);\n  }\n\n  ~ParseStringExamplesOp() override { mapper_->Unref(); }\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n    const auto &serialized_flat = pb_input->flat<tstring>();\n    int batch_size = serialized_flat.size();\n    std::vector<Example> examples(batch_size);\n    for (size_t i = 0; i < batch_size; ++i) {\n      const auto &serialized = serialized_flat(i);\n      OP_REQUIRES(\n          ctx, examples[i].ParseFromArray(serialized.data(), serialized.size()),\n          errors::FailedPrecondition(\"Failed to parse the Example.\"));\n      ExtendExample(&examples[i]);\n    }\n\n    OpOutputList out_list;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &out_list));\n    std::vector<const Example *> example_ptrs;\n    example_ptrs.reserve(examples.size());\n    for (const auto &example : examples) {\n      example_ptrs.push_back(&example);\n    }\n\n    parser_->Parse(ctx, example_ptrs, &out_list);\n    counter_->EmitDataConsumeNumCounter(batch_size);\n  }\n\n private:\n  FeatureNameMapperTfBridge *mapper_ = nullptr;\n\n protected:\n  ExampleParser *GetParse() const { return parser_.get(); }\n  DataCounter *GetCounter() const { return counter_.get(); }\n  FeatureNameMapper *GetFeatureNameMapper() const {\n    return mapper_->GetFeatureNameMapper();\n  }\n\n private:\n  std::unique_ptr<ExampleParser> parser_;\n  std::unique_ptr<DataCounter> counter_;\n};\n\nclass ParseStringExamplesV2Op : public ParseStringExamplesOp {\n public:\n  explicit ParseStringExamplesV2Op(OpKernelConstruction *ctx)\n      : ParseStringExamplesOp(ctx) {}\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n    const auto &serialized_flat = pb_input->flat<tstring>();\n    int batch_size = serialized_flat.size();\n\n    Tensor *example_tensor;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(\"sparse_features\",\n                                             TensorShape({\n                                                 batch_size,\n                                             }),\n                                             &example_tensor));\n    google::protobuf::Arena arena;\n    std::vector<const Example *> example_ptrs;\n    example_ptrs.reserve(batch_size);\n\n    for (size_t i = 0; i < batch_size; ++i) {\n      const auto &serialized = serialized_flat(i);\n      auto *example_ptr =\n          google::protobuf::Arena::CreateMessage<Example>(&arena);\n      example_tensor->flat<Variant>()(i) = std::move(*example_ptr);\n      auto example = example_tensor->flat<Variant>()(i).get<Example>();\n      OP_REQUIRES(ctx,\n                  example->ParseFromArray(serialized.data(), serialized.size()),\n                  errors::FailedPrecondition(\"Failed to parse the Example.\"));\n      ExtendExample(example);\n      example_ptrs.push_back(example);\n    }\n\n    OpOutputList out_list;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &out_list));\n    ParseStringExamplesOp::GetParse()->Parse(ctx, example_ptrs, &out_list);\n    ParseStringExamplesOp::GetCounter()->EmitDataConsumeNumCounter(batch_size);\n  }\n};\n\nclass ParseVariantExamplesOp : public OpKernel {\n public:\n  explicit ParseVariantExamplesOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    std::vector<std::string> names;\n    std::vector<int> shapes;\n    std::vector<DataType> dtypes;\n    std::vector<std::string> extra_names;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"names\", &names));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"shapes\", &shapes));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"dtypes\", &dtypes));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"extra_names\", &extra_names));\n\n    auto creator = [this](FeatureNameMapperTfBridge **out_mapper) {\n      TF_RETURN_IF_ERROR(FeatureNameMapperTfBridge::New(out_mapper));\n      return Status::OK();\n    };\n    ResourceMgr *resource_mgr = ctx->resource_manager();\n    OP_REQUIRES_OK(ctx,\n                   resource_mgr->LookupOrCreate<FeatureNameMapperTfBridge>(\n                       resource_mgr->default_container(),\n                       FeatureNameMapperTfBridge::kName, &mapper_, creator));\n    parser_ = std::make_unique<ExampleParser>(names, shapes, dtypes,\n                                              extra_names, DataType::DT_VARIANT,\n                                              mapper_->GetFeatureNameMapper());\n    counter_ = std::make_unique<DataCounter>(\"ParseVariantExamplesOp\", true);\n  }\n\n  ~ParseVariantExamplesOp() override { mapper_->Unref(); }\n\n  void Compute(OpKernelContext *ctx) override {\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n    const auto &pb_variant_tensor = pb_input->vec<Variant>();\n    int batch_size = pb_variant_tensor.dimension(0);\n    std::vector<const Example *> examples;\n    examples.reserve(batch_size);\n\n    for (int i = 0; i < batch_size; ++i) {\n      const auto *example = pb_variant_tensor(i).get<Example>();\n      CHECK_NOTNULL(example);\n      examples.push_back(example);\n    }\n    OpOutputList out_list;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &out_list));\n    {\n      profiler::TraceMe activity([]() { return \"Parse\"; });\n      parser_->Parse(ctx, examples, &out_list);\n    }\n    {\n      profiler::TraceMe activity([]() { return \"EmitDataConsumeNumCounter\"; });\n      counter_->EmitDataConsumeNumCounter(batch_size);\n    }\n  }\n\n private:\n  FeatureNameMapperTfBridge *mapper_ = nullptr;\n  std::unique_ptr<ExampleParser> parser_;\n  std::unique_ptr<DataCounter> counter_;\n};\n\nclass ParseVariantExamplesV2Op : public ParseVariantExamplesOp {\n public:\n  explicit ParseVariantExamplesV2Op(OpKernelConstruction *ctx)\n      : ParseVariantExamplesOp(ctx) {}\n\n  void Compute(OpKernelContext *ctx) override {\n    ParseVariantExamplesOp::Compute(ctx);\n    OP_REQUIRES_OK(ctx, ctx->set_output(\"sparse_features\", ctx->input(0)));\n  }\n};\n\nclass ParseStringExampleBatchOp : public OpKernel {\n public:\n  explicit ParseStringExampleBatchOp(OpKernelConstruction *ctx)\n      : OpKernel(ctx) {\n    std::vector<std::string> names;\n    std::vector<int> shapes;\n    std::vector<DataType> dtypes;\n    std::vector<std::string> extra_names;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"names\", &names));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"shapes\", &shapes));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"dtypes\", &dtypes));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"extra_names\", &extra_names));\n\n    parser_ = std::make_unique<ExampleBatchParser>(\n        names, shapes, dtypes, extra_names, DataType::DT_STRING);\n    counter_ =\n        std::make_unique<DataCounter>(\"ParseStringExampleBatchOp\", false);\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n    const auto &serialized = pb_input->flat<tstring>()(0);\n    google::protobuf::Arena arena;\n    auto *example_batch =\n        google::protobuf::Arena::CreateMessage<ExampleBatch>(&arena);\n    OP_REQUIRES(\n        ctx,\n        example_batch->ParseFromArray(serialized.data(), serialized.size()),\n        errors::FailedPrecondition(\"Failed to parse the Instance.\"));\n\n    OpOutputList out_list;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &out_list));\n    parser_->Parse(ctx, *example_batch, &out_list);\n\n    counter_->EmitDataConsumeNumCounter(example_batch->batch_size());\n  }\n\n protected:\n  ExampleBatchParser *GetParse() const { return parser_.get(); }\n  DataCounter *GetCounter() const { return counter_.get(); }\n\n private:\n  std::unique_ptr<ExampleBatchParser> parser_;\n  std::unique_ptr<DataCounter> counter_;\n};\n\nclass ParseStringExampleBatchV2Op : public ParseStringExampleBatchOp {\n public:\n  explicit ParseStringExampleBatchV2Op(OpKernelConstruction *ctx)\n      : ParseStringExampleBatchOp(ctx) {}\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n    const auto &serialized = pb_input->flat<tstring>()(0);\n    google::protobuf::Arena arena;\n    auto *example_batch_ptr =\n        google::protobuf::Arena::CreateMessage<ExampleBatch>(&arena);\n    Tensor *example_batch_tensor;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(\"sparse_features\",\n                                             TensorShape({\n                                                 1,\n                                             }),\n                                             &example_batch_tensor));\n    example_batch_tensor->scalar<Variant>()() = std::move(*example_batch_ptr);\n    auto example_batch =\n        example_batch_tensor->scalar<Variant>()().get<ExampleBatch>();\n    OP_REQUIRES(\n        ctx,\n        example_batch->ParseFromArray(serialized.data(), serialized.size()),\n        errors::FailedPrecondition(\"Failed to parse the Instance.\"));\n\n    OpOutputList out_list;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &out_list));\n    ParseStringExampleBatchOp::GetParse()->Parse(ctx, *example_batch,\n                                                 &out_list);\n\n    ParseStringExampleBatchOp::GetCounter()->EmitDataConsumeNumCounter(\n        example_batch->batch_size());\n  }\n};\n\nclass ParseVariantExampleBatchOp : public OpKernel {\n public:\n  explicit ParseVariantExampleBatchOp(OpKernelConstruction *ctx)\n      : OpKernel(ctx) {\n    std::vector<std::string> names;\n    std::vector<int> shapes;\n    std::vector<DataType> dtypes;\n    std::vector<std::string> extra_names;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"names\", &names));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"shapes\", &shapes));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"dtypes\", &dtypes));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"extra_names\", &extra_names));\n\n    parser_ = std::make_unique<ExampleBatchParser>(\n        names, shapes, dtypes, extra_names, DataType::DT_VARIANT);\n    counter_ =\n        std::make_unique<DataCounter>(\"ParseVariantExampleBatchOp\", false);\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n    const auto &variant = pb_input->flat<Variant>()(0);\n    const ExampleBatch *example_batch = variant.get<ExampleBatch>();\n\n    OpOutputList out_list;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &out_list));\n    parser_->Parse(ctx, *example_batch, &out_list);\n\n    counter_->EmitDataConsumeNumCounter(example_batch->batch_size());\n  }\n\n private:\n  std::unique_ptr<ExampleBatchParser> parser_;\n  std::unique_ptr<DataCounter> counter_;\n};\n\nclass ParseVariantExampleBatchV2Op : public ParseVariantExampleBatchOp {\n public:\n  explicit ParseVariantExampleBatchV2Op(OpKernelConstruction *ctx)\n      : ParseVariantExampleBatchOp(ctx) {}\n\n  void Compute(OpKernelContext *ctx) override {\n    ParseVariantExampleBatchOp::Compute(ctx);\n    OP_REQUIRES_OK(ctx, ctx->set_output(\"sparse_features\", ctx->input(0)));\n  }\n};\n\nclass ParseVariantExampleBatchListOp : public OpKernel {\n public:\n  explicit ParseVariantExampleBatchListOp(OpKernelConstruction *ctx)\n      : OpKernel(ctx) {\n    std::string label_config;\n    std::vector<std::string> names;\n    std::vector<int> shapes;\n    std::vector<DataType> dtypes;\n    std::vector<std::string> extra_names;\n\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"label_config\", &label_config));\n    internal::ParseTaskConfig(label_config, &label_config_);\n\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"names\", &names));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"shapes\", &shapes));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"dtypes\", &dtypes));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"extra_names\", &extra_names));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"positive_label\", &positive_label_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"negative_label\", &negative_label_));\n\n    parser_ = std::make_unique<ExampleBatchListParser>(\n        names, shapes, dtypes, extra_names, DataType::DT_VARIANT);\n    counter_ =\n        std::make_unique<DataCounter>(\"ParseVariantExampleBatchListOp\", false);\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    OpInputList inputs;\n    OP_REQUIRES_OK(ctx, ctx->input_list(\"inputs\", &inputs));\n    ExampleBatch example_batch;\n    int batch_size = 0;\n    for (auto iter = inputs.begin(); iter != inputs.end(); ++iter) {\n      const ExampleBatch *sub_eb =\n          iter->scalar<Variant>()().get<ExampleBatch>();\n      batch_size += sub_eb->batch_size();\n      example_batch.MergeFrom(*sub_eb);\n    }\n\n    OpOutputList out_list;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &out_list));\n    parser_->Parse(ctx, example_batch, label_config_, positive_label_,\n                   negative_label_, &out_list);\n\n    counter_->EmitDataConsumeNumCounter(batch_size);\n  }\n\n private:\n  std::unique_ptr<ExampleBatchListParser> parser_;\n  std::unique_ptr<DataCounter> counter_;\n  std::vector<internal::TaskConfig> label_config_;\n  float positive_label_ = 1.0f, negative_label_ = 0.0f;\n};\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseInstances\").Device(DEVICE_CPU).TypeConstraint<tstring>(\"T\"),\n    ParseStringInstancesOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseInstances\").Device(DEVICE_CPU).TypeConstraint<Variant>(\"T\"),\n    ParseVariantInstancesOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseInstancesV2\").Device(DEVICE_CPU).TypeConstraint<tstring>(\"T\"),\n    ParseStringInstancesV2Op);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseInstancesV2\").Device(DEVICE_CPU).TypeConstraint<Variant>(\"T\"),\n    ParseVariantInstancesV2Op);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseExamples\").Device(DEVICE_CPU).TypeConstraint<tstring>(\"T\"),\n    ParseStringExamplesOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseExamples\").Device(DEVICE_CPU).TypeConstraint<Variant>(\"T\"),\n    ParseVariantExamplesOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseExamplesV2\").Device(DEVICE_CPU).TypeConstraint<tstring>(\"T\"),\n    ParseStringExamplesV2Op);\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseExamplesV2\").Device(DEVICE_CPU).TypeConstraint<Variant>(\"T\"),\n    ParseVariantExamplesV2Op);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseExampleBatch\").Device(DEVICE_CPU).TypeConstraint<tstring>(\"T\"),\n    ParseStringExampleBatchOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseExampleBatch\").Device(DEVICE_CPU).TypeConstraint<Variant>(\"T\"),\n    ParseVariantExampleBatchOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseExampleBatchV2\").Device(DEVICE_CPU).TypeConstraint<tstring>(\"T\"),\n    ParseStringExampleBatchV2Op);\nREGISTER_KERNEL_BUILDER(\n    Name(\"ParseExampleBatchV2\").Device(DEVICE_CPU).TypeConstraint<Variant>(\"T\"),\n    ParseVariantExampleBatchV2Op);\n\nREGISTER_KERNEL_BUILDER(Name(\"ParseExampleBatchList\").Device(DEVICE_CPU),\n                        ParseVariantExampleBatchListOp);\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/parse_sparse_feature.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/parse_sparse_feature.h\"\n#include <algorithm>\n#include <chrono>\n#include <tuple>\n\n#include \"monolith/native_training/data/kernels/parse_sparse_feature.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/runtime/common/metrics.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nShardingSparseFidsOp::ShardingSparseFidsOp(OpKernelConstruction *ctx,\n                                           int version /* = 1*/)\n    : OpKernel(ctx), version_(version) {\n  std::string feature_cfgs_str;\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(\"ps_num\", &ps_num_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(\"feature_cfgs\", &feature_cfgs_str));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(\"unique\", &unique_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(\"parallel_flag\", &parallel_flag_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(\"single_thread_feature_watermark\", &single_thread_feature_watermark_));\n  std::string input_type;\n  LOG(INFO) << \"[ShardingSparseFidsOp] version: \" << version;\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(\"input_type\", &input_type));\n  if (input_type == \"example\") {\n    input_type_ = 0;\n  } else if (input_type == \"examplebatch\") {\n    input_type_ = 1;\n  } else if (input_type == \"instance\") {\n    input_type_ = 2;\n  } else {\n    OP_REQUIRES(ctx, false,\n                errors::FailedPrecondition(\n                    \"input_type only support example/examplebatch.\"));\n  }\n  ::monolith::io::proto::FeatureConfigs feature_cfgs;\n  OP_REQUIRES(\n      ctx, feature_cfgs.ParseFromString(feature_cfgs_str),\n      errors::FailedPrecondition(\"Failed to parse the FeatureConfigs.\"));\n\n  enable_parallel_ =\n      (parallel_flag_ > 0);  // parallel_flag_ == 0 default not parallel\n\n  auto creator = [this](FeatureNameMapperTfBridge **out_mapper) {\n    TF_RETURN_IF_ERROR(FeatureNameMapperTfBridge::New(out_mapper));\n    return Status::OK();\n  };\n  ResourceMgr *resource_mgr = ctx->resource_manager();\n  OP_REQUIRES_OK(ctx,\n                 resource_mgr->LookupOrCreate<FeatureNameMapperTfBridge>(\n                     resource_mgr->default_container(),\n                     FeatureNameMapperTfBridge::kName, &mapper_, creator));\n  std::vector<std::string> feature_names;\n  feature_names.reserve(feature_cfgs.feature_configs_size());\n  for (const auto &pair : feature_cfgs.feature_configs()) {\n    feature_names.push_back(pair.first);\n  }\n  mapper_raw_ptr_ = mapper_->GetFeatureNameMapper();\n  CHECK(mapper_raw_ptr_->RegisterValidNames(feature_names));\n\n  feature_index_conf_.reserve(feature_cfgs.feature_configs_size() * 2);\n  static std::vector<std::string> slot_id_feature_prefix({\"fc_slot_\", \"slot_\"});\n  for (auto &iter : feature_cfgs.feature_configs()) {\n    auto &feature_cfg = feature_conf_[iter.first];\n    feature_cfg.table_name = iter.second.table();\n    feature_cfg.feature_name = iter.first;\n    feature_cfg.version = version_;\n\n    int dims_sum = 0;\n    for (size_t slice_idx = 0; slice_idx < iter.second.slice_dims_size();\n         slice_idx++) {\n      dims_sum += iter.second.slice_dims(slice_idx);\n    }\n    feature_cfg.dims_sum = dims_sum;\n\n    auto &table_cfg = table_conf_[iter.second.table()];\n    table_cfg.table_name = iter.second.table();\n\n    for (auto &feature_prfix : slot_id_feature_prefix) {\n      if (absl::StartsWith(iter.first, feature_prfix)) {\n        std::string sub_str(iter.first.substr(feature_prfix.size()));\n        try {\n          int slot_id = std::stoi(sub_str);\n          slot_id_to_feature_name_[slot_id] = iter.first;\n        } catch (std::exception const &ex) {\n          LOG(ERROR) << \"slot_id_to_feature_name_ err:\" << ex.what() << \":\"\n                     << iter.first << \",\" << sub_str;\n          continue;\n        }\n      }\n    }\n  }\n  for (auto &iter : table_conf_) {\n    table_cfg_list_.push_back(&iter.second);\n  }\n  std::sort(\n      table_cfg_list_.begin(), table_cfg_list_.end(),\n      [](TableInfo *a, TableInfo *b) { return a->table_name < b->table_name; });\n  for (uint i = 0; i < table_cfg_list_.size(); ++i) {\n    auto &conf = *(table_cfg_list_[i]);\n    conf.table_index = i;\n  }\n\n  for (auto &iter : feature_conf_) {\n    feature_cfg_list_.push_back(&iter.second);\n  }\n  std::sort(feature_cfg_list_.begin(), feature_cfg_list_.end(),\n            [](FeatureInfo *a, FeatureInfo *b) {\n              return a->feature_name < b->feature_name;\n            });\n  for (uint i = 0; i < feature_cfg_list_.size(); ++i) {\n    auto &conf = *(feature_cfg_list_[i]);\n    conf.feature_index = i;\n    auto &table_cfg = table_conf_[conf.table_name];\n    conf.table_index = table_cfg.table_index;\n    conf.feature_in_table_index = table_cfg.feature_count++;\n    table_cfg.feature_index_list.push_back(i);\n  }\n\n  for (uint i = 0; i < feature_cfg_list_.size(); ++i) {\n    auto &conf = *(feature_cfg_list_[i]);\n    auto &table_cfg = table_conf_[conf.table_name];\n    conf.table_feature_count = table_cfg.feature_count;\n  }\n\n  if (version_ == 2) {\n    int output_index = 0;\n    for (uint i = 0; i < table_cfg_list_.size(); ++i) {\n      auto &table_cfg = *(table_cfg_list_[i]);\n      for (auto feature_index : table_cfg.feature_index_list) {\n        auto &feature_cfg = *(feature_cfg_list_[feature_index]);\n        feature_cfg.output_pre_index = output_index;\n      }\n      output_index +=\n          std::max(table_cfg.feature_index_list.size(), 1UL) * ps_num_;\n    }\n  } else {\n    for (auto feature_cfg_ptr : feature_cfg_list_) {\n      feature_cfg_ptr->output_pre_index =\n          feature_cfg_ptr->table_index * ps_num_;\n    }\n  }\n\n  if (mapper_raw_ptr_->IsAvailable()) {\n    int32_t max_sorted_id = -1;\n    LOG_FIRST_N(INFO, 1) << mapper_raw_ptr_->DebugString();\n\n    absl::flat_hash_map<int32_t, FeatureInfo *> feature_index_conf_tmp;\n    feature_index_conf_tmp.reserve(feature_conf_.size() * 2);\n    for (auto &iter : feature_conf_) {\n      int32_t id = -1;\n      int32_t sorted_id = -1;\n      bool found = mapper_raw_ptr_->GetIdByName(iter.first, &id, &sorted_id);\n      if (found && !feature_index_conf_tmp.contains(sorted_id)) {\n        feature_index_conf_tmp[sorted_id] = &iter.second;\n        max_sorted_id = std::max(max_sorted_id, sorted_id);\n      } else {\n        feature_index_conf_tmp.clear();\n        LOG(ERROR) << \"mapper_raw_ptr_ not find:\" << iter.first;\n        break;\n      }\n    }\n    if (feature_index_conf_tmp.size() > 0) {\n      feature_index_conf_.resize(max_sorted_id + 1, nullptr);\n      for (auto &iter : feature_index_conf_tmp) {\n        feature_index_conf_[iter.first] = iter.second;\n      }\n    }\n  } else {\n    LOG(WARNING) << \"mapper_raw_ptr_ not Available()\";\n  }\n}\n\nvoid ShardingSparseFidsOp::FillFidList(\n    uint64_t value, std::vector<std::vector<uint64_t>> &shard_vec,\n    MultiShardUniqHashTable &shard_uniq_hashtable,\n    tensorflow::TTypes<uint64_t>::Flat fid_offset_flat,\n    int feature_output_index, int *offset) {\n  if (unique_) {\n    FillFidList(value, shard_uniq_hashtable, fid_offset_flat, feature_output_index, offset);\n  } else {\n    FillFidList(value, shard_vec, fid_offset_flat, feature_output_index, offset);\n  }\n}\n\nvoid ShardingSparseFidsOp::FillFidList(\n    uint64_t value, std::vector<std::vector<uint64_t>> &shard_vec,\n    tensorflow::TTypes<uint64_t>::Flat fid_offset_flat,\n    int feature_output_index, int *offset) {\n  auto mod = value % ps_num_;\n  int output_offset =\n      feature_cfg_list_[feature_output_index]->GetFidOutputIndex(mod);\n  int feature_offset = shard_vec[mod].size();\n  if (version_ == 3 || version_ == 4 || version_ == 5) {\n    feature_offset *= feature_cfg_list_[feature_output_index]->dims_sum;\n  }\n  fid_offset_flat(*offset) = ((uint64_t(output_offset) << 32) | feature_offset);\n  shard_vec[mod].push_back(value);\n  ++(*offset);\n}\n\nvoid ShardingSparseFidsOp::FillFidList(\n    uint64_t value, MultiShardUniqHashTable &shard_uniq_hashtable,\n    tensorflow::TTypes<uint64_t>::Flat fid_offset_flat,\n    int feature_output_index, int *offset) {\n  auto mod = value % ps_num_;\n  int output_offset =\n      feature_cfg_list_[feature_output_index]->GetFidOutputIndex(mod);\n\n  auto feature_offset = shard_uniq_hashtable.uniq_fid(value, mod);\n\n  // auto fid_find_iter = shard_vec[mod].find(value);\n  // int feature_offset1 = -1;\n  // if (fid_find_iter == shard_vec[mod].end()) {\n  //   feature_offset1 = shard_vec[mod].size();\n  //   shard_vec[mod].emplace(value, feature_offset1);\n  // } else {\n  //   feature_offset1 = fid_find_iter->second;\n  // }\n  // CHECK_EQ(feature_offset1, feature_offset);\n\n  if (version_ == 3 || version_ == 4 || version_ == 5) {\n    feature_offset *= feature_cfg_list_[feature_output_index]->dims_sum;\n  }\n  fid_offset_flat(*offset) = (uint64_t(output_offset) << 32) | feature_offset;\n  ++(*offset);\n}\n\nvoid ShardingSparseFidsOp::CopyFidList(\n    const std::vector<uint64_t> &shard_ptr, int offset,\n    TensorSliceAccessor<int64_t> *cur_tensor) {\n  void *data_ptr = cur_tensor->ptr;\n  std::memcpy(reinterpret_cast<char *>(data_ptr) + int64_size_ * offset,\n              shard_ptr.data(), shard_ptr.size() * int64_size_);\n}\n\nvoid ShardingSparseFidsOp::CopyFidList(\n    const absl::flat_hash_map<uint64_t, int> &shard_ptr, int offset,\n    TensorSliceAccessor<int64_t> *cur_tensor) {\n  // auto cur_tensor_flat = cur_tensor->template flat<int64_t>();\n  for (auto &fid : shard_ptr) {\n    (*cur_tensor)(fid.second + offset) = fid.first;\n  }\n}\n\nStatus ShardingSparseFidsOp::CreateOffsetTensor(\n    OpKernelContext *ctx,\n    const std::vector<std::vector<int>> &all_feature_counter,\n    int all_feature_counter_size, Tensor **nfl_offset_tensor,\n    Tensor **feature_offset_tensor, Tensor **fid_offset_tensor,\n    OpOutputList *fid_list_row_splits_out_list,\n    OpOutputList *fid_list_row_splits_size_out_list,\n    std::vector<Tensor> &tmp_tensor_list,\n    std::vector<ShardingSparseFidsOp::TensorSliceAccessor<int64_t>>\n        &fid_list_row_splits_flat_list,\n    std::vector<int> *nfl_fid_offset,\n    const std::unordered_set<int> *shared_feature) {\n  // feature_size\n  TF_RETURN_IF_ERROR(\n      ctx, ctx->allocate_output(\"nfl_offset\", TensorShape({\n                                                  feature_conf_.size() + 1,\n                                              }),\n                                nfl_offset_tensor));\n  auto nfl_offset_flat = (*nfl_offset_tensor)->flat<uint32_t>();\n\n  // 最大是 feature_size * batch_size, 但是shared_feature只算一个\n  TF_RETURN_IF_ERROR(ctx, ctx->allocate_output(\"feature_offset\",\n                                               TensorShape({\n                                                   all_feature_counter_size + 1,\n                                               }),\n                                               feature_offset_tensor));\n  auto feature_offset_flat = (*feature_offset_tensor)->flat<int32_t>();\n\n  if (version_ == 5) {\n    Tensor *nfl_size_tensor;\n    TF_RETURN_IF_ERROR(ctx, ctx->allocate_output(\"nfl_size\", TensorShape({\n                                                                 1,\n                                                             }),\n                                                 &nfl_size_tensor));\n    nfl_size_tensor->flat<int32>()(0) = feature_conf_.size() + 1;\n\n    Tensor *feature_size_tensor;\n    TF_RETURN_IF_ERROR(ctx, ctx->allocate_output(\"feature_size\", TensorShape({\n                                                                     1,\n                                                                 }),\n                                                 &feature_size_tensor));\n    feature_size_tensor->flat<int32>()(0) = all_feature_counter_size + 1;\n  }\n\n  int all_fid_size = 0;\n  int feature_offset_index = 0;\n  for (uint i = 0; i < all_feature_counter.size(); ++i) {\n    auto &feature_counter = all_feature_counter[i];\n    nfl_offset_flat(i) = feature_offset_index;\n    if (shared_feature->size() > 0 && shared_feature->count(i) > 0) {\n      nfl_offset_flat(i) |= SHARED_FLAG;\n    }\n    (*nfl_fid_offset)[i] = all_fid_size;\n\n    for (uint j = 0; j < feature_counter.size(); ++j) {\n      feature_offset_flat(feature_offset_index) = all_fid_size;\n      all_fid_size += abs(feature_counter[j]);\n      ++feature_offset_index;\n    }\n  }\n  CHECK_EQ(feature_offset_index, all_feature_counter_size);\n  feature_offset_flat(feature_offset_index) = all_fid_size;\n  nfl_offset_flat(all_feature_counter.size()) = feature_offset_index + 1;\n  // fid_size\n  TF_RETURN_IF_ERROR(ctx,\n                     ctx->allocate_output(\"fid_offset\", TensorShape({\n                                                            all_fid_size,\n                                                        }),\n                                          fid_offset_tensor));\n\n  if (version_ == 5) {\n    Tensor *fid_size_tensor;\n    TF_RETURN_IF_ERROR(ctx, ctx->allocate_output(\"fid_size\", TensorShape({\n                                                                 1,\n                                                             }),\n                                                 &fid_size_tensor));\n    fid_size_tensor->flat<int32>()(0) = all_fid_size;\n  }\n\n  if (version_ == 4) {\n    Tensor *fid_list_row_lengths_tensor;\n    int all_feature_count = 0;\n    for (uint i = 0; i < table_cfg_list_.size(); ++i) {\n      all_feature_count += table_cfg_list_[i]->feature_count + 1;\n    }\n    all_feature_count *= ps_num_;\n    TF_RETURN_IF_ERROR(\n        ctx,\n        ctx->allocate_output(\"fid_list_row_splits\", TensorShape({\n                                                        all_feature_count,\n                                                    }),\n                             &fid_list_row_lengths_tensor));\n    auto cur_tensor_flat = fid_list_row_lengths_tensor->flat<int64_t>();\n    cur_tensor_flat.setZero();\n    fid_list_row_splits_flat_list.resize(table_cfg_list_.size() * ps_num_);\n    int pre_feature_count = 0;\n    for (uint ps_num_i = 0; ps_num_i < ps_num_; ++ps_num_i) {\n      for (uint table_index = 0; table_index < table_cfg_list_.size();\n           ++table_index) {\n        /*\n          LOG(ERROR) << \"xxxx all_feature_count:\" << all_feature_count\n                    << \",pre_feature_count:\" << pre_feature_count\n                    << \",feature_count:\"\n                    << table_cfg_list_[table_index]->feature_count;\n          auto cur_tensor = fid_list_row_lengths_tensor->Slice(\n          pre_feature_count,\n          pre_feature_count + table_cfg_list_[table_index]->feature_count);\n        */\n        int cur_count = table_cfg_list_[table_index]->feature_count + 1;\n        fid_list_row_splits_flat_list[table_index * ps_num_ + ps_num_i] =\n            TensorSliceAccessor<int64_t>(\n                {static_cast<int64_t *>(fid_list_row_lengths_tensor->data()) +\n                     pre_feature_count,\n                 cur_count});\n        pre_feature_count += cur_count;\n      }\n    }\n  } else {\n    tmp_tensor_list.resize(table_cfg_list_.size() * ps_num_);\n    int fid_list_row_splits_flat_list_index = -1;\n    for (uint i = 0; i < table_cfg_list_.size(); ++i) {\n      for (uint j = 0; j < ps_num_; ++j) {\n        Tensor *cur_tensor;\n        ++fid_list_row_splits_flat_list_index;\n        if (version_ == 2 || version_ == 3) {\n          TF_RETURN_IF_ERROR(ctx,\n                             fid_list_row_splits_out_list->allocate(\n                                 fid_list_row_splits_flat_list_index,\n                                 tensorflow::TensorShape{\n                                     table_cfg_list_[i]->feature_count + 1},\n                                 &cur_tensor));\n        } else if (version_ == 5) {\n          TF_RETURN_IF_ERROR(ctx,\n                             fid_list_row_splits_out_list->allocate(\n                                 fid_list_row_splits_flat_list_index,\n                                 tensorflow::TensorShape{\n                                     table_cfg_list_[i]->feature_count + 1},\n                                 &cur_tensor));\n          Tensor *size_tensor;\n          TF_RETURN_IF_ERROR(ctx, fid_list_row_splits_size_out_list->allocate(\n                                      fid_list_row_splits_flat_list_index,\n                                      tensorflow::TensorShape{\n                                          1,\n                                      },\n                                      &size_tensor));\n          size_tensor->flat<int32>()(0) = table_cfg_list_[i]->feature_count + 1;\n        } else {\n          cur_tensor = &(tmp_tensor_list[i * ps_num_ + j]);\n          TF_RETURN_IF_ERROR(\n              ctx,\n              ctx->allocate_temp(DT_INT64,\n                                 tensorflow::TensorShape{\n                                     table_cfg_list_[i]->feature_count + 1},\n                                 cur_tensor));\n        }\n        auto cur_tensor_flat = cur_tensor->flat<int64_t>();\n        cur_tensor_flat.setZero();\n        fid_list_row_splits_flat_list.emplace_back(TensorSliceAccessor<int64_t>(\n            {static_cast<int64_t *>(cur_tensor->data()),\n             cur_tensor->NumElements()}));\n      }\n    }\n  }\n\n  return Status::OK();\n}\n\nvoid ShardingSparseFidsOp::ParallelRun(\n    OpKernelContext *ctx, int task_count,\n    const std::function<void(int64, int64)> &fn) {\n  if (enable_parallel_ && task_count > 1) {\n    auto workers = ctx->device()->tensorflow_cpu_worker_threads()->workers;\n    workers->ParallelFor(\n        task_count, thread::ThreadPool::SchedulingParams(\n                        thread::ThreadPool::SchedulingStrategy::kFixedBlockSize,\n                        absl::nullopt, 1),\n        fn);\n  } else {\n    for (int i = 0; i < task_count; ++i) {\n      fn(i, i + 1);\n    }\n  }\n}\n\nvoid ShardingSparseFidsOp::InitInstanceWrapper(\n    ShardingSparseFidsOp::InstanceWrapper *instance_wrapper) {\n  auto &instances = instance_wrapper->instances;\n  if (slot_id_to_feature_name_.size() == 0) {\n    return;\n  }\n  instance_wrapper->fid_v1.reserve(slot_id_to_feature_name_.size());\n  for (auto &slot_name_iter : slot_id_to_feature_name_) {\n    auto &part = instance_wrapper->fid_v1[slot_name_iter.second];\n    part.resize(instances.size());\n    for (int i = 0; i < part.size(); ++i) {\n      part[i].reserve(instances[i]->fid_size());\n    }\n  }\n  for (int i = 0; i < instances.size(); ++i) {\n    for (auto fid : instances[i]->fid()) {\n      int slot_id = slot_id_v1(fid);\n      auto find_iter = slot_id_to_feature_name_.find(slot_id);\n      if (find_iter == slot_id_to_feature_name_.end()) {\n        continue;\n      }\n      instance_wrapper->fid_v1[find_iter->second][i].push_back(fid);\n    }\n  }\n}\n\nvoid ShardingSparseFidsOp::Compute(OpKernelContext *ctx) {\n  auto shard_sparse_op_latency_start = std::chrono::system_clock::now();\n  const Tensor *pb_input;\n  OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n  OpOutputList out_list;\n  if (version_ != 4) {\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"fid_list\", &out_list));\n  }\n  OpOutputList fid_list_row_splits_out_list;\n  OpOutputList fid_list_row_splits_size_out_list;\n  if (version_ == 2 || version_ == 3) {\n    OP_REQUIRES_OK(\n        ctx,\n        ctx->output_list(\"fid_list_row_splits\", &fid_list_row_splits_out_list));\n  }\n  if (version_ == 5) {\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"fid_list_row_splits\",\n                                         &fid_list_row_splits_out_list));\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"fid_list_row_splits_size\",\n                                         &fid_list_row_splits_size_out_list));\n  }\n  int batch_size = 0;\n  Status st;\n  if (input_type_ == 0) {\n    const auto &pb_variant_tensor = pb_input->vec<Variant>();\n    batch_size = pb_variant_tensor.dimension(0);\n    std::vector<const Example *> examples;\n    examples.reserve(batch_size);\n    for (int i = 0; i < batch_size; ++i) {\n      const auto *example = pb_variant_tensor(i).get<Example>();\n      CHECK_NOTNULL(example);\n      examples.push_back(example);\n    }\n    st = FeatureParallelParse(ctx, examples, &out_list,\n                              &fid_list_row_splits_out_list,\n                              &fid_list_row_splits_size_out_list);\n\n  } else if (input_type_ == 1) {\n    const auto &example_batch =\n        *(pb_input->scalar<Variant>()().get<ExampleBatch>());\n    batch_size = example_batch.batch_size();\n    st = FeatureParallelParse(ctx, example_batch, &out_list,\n                              &fid_list_row_splits_out_list,\n                              &fid_list_row_splits_size_out_list);\n  } else if (input_type_ == 2) {\n    const auto &pb_variant_tensor = pb_input->vec<Variant>();\n    batch_size = pb_variant_tensor.dimension(0);\n    InstanceWrapper instance_wapper;\n    instance_wapper.instances.reserve(batch_size);\n    for (int i = 0; i < batch_size; ++i) {\n      const auto *instance = pb_variant_tensor(i).get<Instance>();\n      CHECK_NOTNULL(instance);\n      instance_wapper.instances.push_back(instance);\n    }\n    InitInstanceWrapper(&instance_wapper);\n    st = FeatureParallelParse(ctx, instance_wapper, &out_list,\n                              &fid_list_row_splits_out_list,\n                              &fid_list_row_splits_size_out_list);\n  }\n  OP_REQUIRES_OK(ctx, st);\n\n  Tensor *batch_size_tensor;\n  if (version_ == 5) {\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(\"batch_size\",\n                                             TensorShape({1, }),\n                                             &batch_size_tensor));\n    batch_size_tensor->flat<int32>()(0) = batch_size;\n  } else {\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(\"batch_size\", TensorShape({}),\n                                             &batch_size_tensor));\n    batch_size_tensor->scalar<int32>()() = batch_size;\n  }\n\n  auto shard_sparse_op_latency_end = std::chrono::system_clock::now();\n  std::chrono::duration<double> shard_sparse_op_latency_diff =\n      std::chrono::duration_cast<std::chrono::microseconds>(\n          shard_sparse_op_latency_end - shard_sparse_op_latency_start);\n  monolith::GetMetrics()->emit_timer(\"sharding_sparse_fids_op_latency\",\n                                     shard_sparse_op_latency_diff.count());\n}\n\nREGISTER_KERNEL_BUILDER(Name(\"ShardingSparseFids\").Device(DEVICE_CPU),\n                        ShardingSparseFidsOp);\n\nclass ShardingSparseFidsOpV2 : public ShardingSparseFidsOp {\n public:\n  explicit ShardingSparseFidsOpV2(OpKernelConstruction *ctx)\n      : ShardingSparseFidsOp(ctx, 2) {}\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"ShardingSparseFidsV2\").Device(DEVICE_CPU),\n                        ShardingSparseFidsOpV2);\n\nclass ShardingSparseFidsOpV3 : public ShardingSparseFidsOp {\n public:\n  explicit ShardingSparseFidsOpV3(OpKernelConstruction *ctx)\n      : ShardingSparseFidsOp(ctx, 3) {}\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"ShardingSparseFidsV3\").Device(DEVICE_CPU),\n                        ShardingSparseFidsOpV3);\n\nclass ShardingSparseFidsOpV4 : public ShardingSparseFidsOp {\n public:\n  explicit ShardingSparseFidsOpV4(OpKernelConstruction *ctx)\n      : ShardingSparseFidsOp(ctx, 4) {}\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"ShardingSparseFidsV4\").Device(DEVICE_CPU),\n                        ShardingSparseFidsOpV4);\n\nclass ShardingSparseFidsOpV5 : public ShardingSparseFidsOp {\n public:\n  explicit ShardingSparseFidsOpV5(OpKernelConstruction *ctx)\n      : ShardingSparseFidsOp(ctx, 5) {}\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"ShardingSparseFidsV5\").Device(DEVICE_CPU),\n                        ShardingSparseFidsOpV5);\n\n// 该函数包含6个输出\n// fid_list:             shape(table_count*ps_num, 若干fid),\n//                       将不同feature样本的全部fid聚合，并按照ps_num分shard，按照feature->table的映射聚合填充\n// fid_list_row_splits   shape(table_count*ps_num, table内feature个数+1),\n//                       与fid_list组成ragged_tensor，主要作用是将相同table内不同feature区分开\n// fid_offset            shape(特征数*(1 if shard feature else\n// batch_size)*fid数),\n//                       样本的一维平铺，最后不是存储的fid，而是在fid_list中的偏移，用于在fid_list寻址\n//                       高32位为fid_list 第一维度与feature在table内index 的组合\n//                       低32位为fid在当前feature fid_list的第几位\n// feature_offset        shape(特征数*(1 if shard feature else batch_size)),\n//                       对fid_offset一维平铺的拆解，标识每个样本的分界点\n// nfl_offset            shape(特征数),\n//                       对fid_offset/feature_offset一维平铺的拆解，标识每个特征的分界点\n// batch_size\n\nREGISTER_OP(\"ShardingSparseFids\")\n    .Input(\"pb_input: variant\")\n    .Output(\"fid_list: N * int64\")\n    .Output(\"fid_offset: uint64\")\n    .Output(\"feature_offset: int32\")\n    .Output(\"nfl_offset: uint32\")\n    .Output(\"batch_size: int32\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"N: int\")\n    .Attr(\"unique: bool\")\n    .Attr(\"input_type: string\")\n    .Attr(\"parallel_flag: int\")\n    .Attr(\"single_thread_feature_watermark: int = 320000\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      // fid_list\n      int N = 0;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"N\", &N));\n      for (int i = 0; i < N; ++i) {\n        ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));  // fid_list\n      }\n      ctx->set_output(N, ctx->Vector(ctx->UnknownDim()));      // fid_offset\n      ctx->set_output(N + 1, ctx->Vector(ctx->UnknownDim()));  // feature_offset\n      ctx->set_output(N + 2, ctx->Vector(ctx->UnknownDim()));  // nfl_offset\n      ctx->set_output(N + 3, ctx->Scalar());                   // batch_size\n\n      return Status::OK();\n    });\n\nREGISTER_OP(\"ShardingSparseFidsV2\")\n    .Input(\"pb_input: variant\")\n    .Output(\"fid_list: N * int64\")\n    .Output(\"fid_list_row_splits: N * int64\")\n    .Output(\"fid_offset: uint64\")\n    .Output(\"feature_offset: int32\")\n    .Output(\"nfl_offset: uint32\")\n    .Output(\"batch_size: int32\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"N: int\")\n    .Attr(\"unique: bool\")\n    .Attr(\"input_type: string\")\n    .Attr(\"parallel_flag: int\")\n    .Attr(\"single_thread_feature_watermark: int = 320000\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      // fid_list\n      int N = 0;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"N\", &N));\n      for (int i = 0; i < N; ++i) {\n        ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));  // fid_list\n        ctx->set_output(N + i,\n                        ctx->Vector(ctx->UnknownDim()));  // fid_list_row_splits\n      }\n      N *= 2;\n      ctx->set_output(N, ctx->Vector(ctx->UnknownDim()));      // fid_offset\n      ctx->set_output(N + 1, ctx->Vector(ctx->UnknownDim()));  // feature_offset\n      ctx->set_output(N + 2, ctx->Vector(ctx->UnknownDim()));  // nfl_offset\n      ctx->set_output(N + 3, ctx->Scalar());                   // batch_size\n\n      return Status::OK();\n    });\n\n// 与v2的区别在于fid_offset包含偏移都乘以了feature的dim\n\nREGISTER_OP(\"ShardingSparseFidsV3\")\n    .Input(\"pb_input: variant\")\n    .Output(\"fid_list: N * int64\")\n    .Output(\"fid_list_row_splits: N * int64\")\n    .Output(\"fid_offset: uint64\")\n    .Output(\"feature_offset: int32\")\n    .Output(\"nfl_offset: uint32\")\n    .Output(\"batch_size: int32\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"N: int\")\n    .Attr(\"unique: bool\")\n    .Attr(\"input_type: string\")\n    .Attr(\"parallel_flag: int\")\n    .Attr(\"single_thread_feature_watermark: int = 320000\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      // fid_list\n      int N = 0;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"N\", &N));\n      for (int i = 0; i < N; ++i) {\n        ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));  // fid_list\n        ctx->set_output(N + i,\n                        ctx->Vector(ctx->UnknownDim()));  // fid_list_row_splits\n      }\n      N *= 2;\n      ctx->set_output(N, ctx->Vector(ctx->UnknownDim()));      // fid_offset\n      ctx->set_output(N + 1, ctx->Vector(ctx->UnknownDim()));  // feature_offset\n      ctx->set_output(N + 2, ctx->Vector(ctx->UnknownDim()));  // nfl_offset\n      ctx->set_output(N + 3, ctx->Scalar());                   // batch_size\n      int ps_num = 0;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"ps_num\", &ps_num));\n      std::string feature_cfgs_str;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"feature_cfgs\", &feature_cfgs_str));\n      ::monolith::io::proto::FeatureConfigs feature_cfgs;\n      CHECK(feature_cfgs.ParseFromString(feature_cfgs_str));\n      std::unordered_set<std::string> table_name;\n      for (auto &iter : feature_cfgs.feature_configs()) {\n        table_name.insert(iter.second.table());\n      }\n\n      return Status::OK();\n    });\n\n// 为适配gpu emb，将多个tenor合并优化性能，并设配输入输出结构\n// fid_list_row_splits [ps_shard, table, feature]\n// fid_list_table_row_length [ps_shard, table]\n//  [ps_shard]\n// fid_list_emb_row_lenth [ps_shard, table] 对应emb在ps_shard的切分\n\nREGISTER_OP(\"ShardingSparseFidsV4\")\n    .Input(\"pb_input: variant\")\n    .Output(\"fid_list: int64\")\n    .Output(\"fid_list_row_splits: int64\")\n    .Output(\"fid_list_table_row_length: int32\")\n    .Output(\"fid_list_shard_row_lenth: int32\")\n    .Output(\"fid_list_emb_row_lenth: int32\")\n    .Output(\"fid_offset: uint64\")\n    .Output(\"feature_offset: int32\")\n    .Output(\"nfl_offset: uint32\")\n    .Output(\"batch_size: int32\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"unique: bool\")\n    .Attr(\"input_type: string\")\n    .Attr(\"parallel_flag: int\")\n    .Attr(\"single_thread_feature_watermark: int = 320000\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      int ps_num = 0;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"ps_num\", &ps_num));\n      std::string feature_cfgs_str;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"feature_cfgs\", &feature_cfgs_str));\n      ::monolith::io::proto::FeatureConfigs feature_cfgs;\n      CHECK(feature_cfgs.ParseFromString(feature_cfgs_str));\n      std::unordered_set<std::string> table_name;\n      for (auto &iter : feature_cfgs.feature_configs()) {\n        table_name.insert(iter.second.table());\n      }\n      // fid_list\n      int N = 0;\n      ctx->set_output(0, ctx->Vector(ctx->UnknownDim()));  // fid_list\n      ctx->set_output(1,\n                      ctx->Vector(ctx->UnknownDim()));  // fid_list_row_splits\n      ctx->set_output(\n          2,\n          ctx->Vector(ps_num *\n                      table_name.size()));      // fid_list_table_row_length\n      ctx->set_output(3, ctx->Vector(ps_num));  // fid_list_shard_row_lenth\n      ctx->set_output(\n          4,\n          ctx->Vector(ps_num * table_name.size()));  // fid_list_emb_row_lenth\n\n      N = 5;\n      ctx->set_output(N, ctx->Vector(ctx->UnknownDim()));      // fid_offset\n      ctx->set_output(N + 1, ctx->Vector(ctx->UnknownDim()));  // feature_offset\n      ctx->set_output(N + 2, ctx->Vector(ctx->UnknownDim()));  // nfl_offset\n      ctx->set_output(N + 3, ctx->Scalar());                   // batch_size\n\n      return Status::OK();\n    });\n\nREGISTER_OP(\"ShardingSparseFidsV5\")\n    .Input(\"pb_input: variant\")\n    .Output(\"fid_list: N * int64\")\n    .Output(\"fid_list_row_splits: N * int64\")\n    .Output(\"fid_list_row_splits_size: N * int32\")\n    .Output(\"fid_offset: uint64\")\n    .Output(\"feature_offset: int32\")\n    .Output(\"nfl_offset: uint32\")\n    .Output(\"batch_size: int32\")\n    .Output(\"nfl_size: int32\")\n    .Output(\"feature_size: int32\")\n    .Output(\"fid_size: int32\")\n    .Output(\"emb_size: int32\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"N: int\")\n    .Attr(\"unique: bool\")\n    .Attr(\"input_type: string\")\n    .Attr(\"parallel_flag: int\")\n    .Attr(\"single_thread_feature_watermark: int = 320000\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      // fid_list\n      int N = 0;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"N\", &N));\n      for (int i = 0; i < N; ++i) {\n        ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));  // fid_list\n        ctx->set_output(N + i,\n                        ctx->Vector(ctx->UnknownDim()));  // fid_list_row_splits\n        ctx->set_output(\n            N * 2 + i,\n            ctx->Vector(ctx->UnknownDim()));  // fid_list_row_splits_size\n      }\n      N *= 3;\n      ctx->set_output(N, ctx->Vector(ctx->UnknownDim()));      // fid_offset\n      ctx->set_output(N + 1, ctx->Vector(ctx->UnknownDim()));  // feature_offset\n      ctx->set_output(N + 2, ctx->Vector(ctx->UnknownDim()));  // nfl_offset\n      ctx->set_output(N + 3, ctx->Vector(ctx->UnknownDim()));  // batch_size\n      ctx->set_output(N + 4, ctx->Vector(ctx->UnknownDim()));  // nfl_size\n      ctx->set_output(N + 5, ctx->Vector(ctx->UnknownDim()));  // feature_size\n      ctx->set_output(N + 6, ctx->Vector(ctx->UnknownDim()));  // fid_size\n      ctx->set_output(N + 7, ctx->Vector(ctx->UnknownDim()));  // emb_size\n      // ctx->set_output(N + 3, ctx->Scalar());            // batch_size\n      int ps_num = 0;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"ps_num\", &ps_num));\n      std::string feature_cfgs_str;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"feature_cfgs\", &feature_cfgs_str));\n      ::monolith::io::proto::FeatureConfigs feature_cfgs;\n      CHECK(feature_cfgs.ParseFromString(feature_cfgs_str));\n      std::unordered_set<std::string> table_name;\n      for (auto &iter : feature_cfgs.feature_configs()) {\n        table_name.insert(iter.second.table());\n      }\n\n      return Status::OK();\n    });\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/parse_sparse_feature.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_KERNELS_PARSE_SPARSE_FEATURE_LIB_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_KERNELS_PARSE_SPARSE_FEATURE_LIB_H_\n\n#include <numeric>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"google/protobuf/descriptor.h\"\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n\n#include \"monolith/native_training/data/kernels/feature_name_mapper_tf_bridge.h\"\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/kernels/internal/uniq_hashtable.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"monolith/native_training/runtime/common/metrics.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/platform/env.h\"\n\n#include \"absl/strings/match.h\"\n\n#include \"tensorflow/core/platform/threadpool.h\"\n#include \"tensorflow/core/profiler/lib/traceme.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass ShardingSparseFidsOp : public OpKernel {\n public:\n  using FeatureListType = ::monolith::io::proto::FeatureListType;\n  using Instance = ::parser::proto::Instance;\n  using Example = ::monolith::io::proto::Example;\n  using ExampleBatch = ::monolith::io::proto::ExampleBatch;\n  using FeatureConfigs = ::monolith::io::proto::FeatureConfigs;\n\n  explicit ShardingSparseFidsOp(OpKernelConstruction *ctx, int version = 1);\n  ~ShardingSparseFidsOp() override { mapper_->Unref(); }\n  void Compute(OpKernelContext *ctx) override;\n\n private:\n  struct InstanceWrapper {\n    struct FeaturePtr {\n      FeaturePtr(const std::vector<uint64_t> *fid_v1_,\n                 const idl::matrix::proto::Feature *fid_v2_)\n          : fid_v1(fid_v1_), fid_v2(fid_v2_) {}\n      const std::vector<uint64_t> *fid_v1 = nullptr;\n      const idl::matrix::proto::Feature *fid_v2 = nullptr;\n    };\n    std::vector<const ::parser::proto::Instance *> instances;\n    absl::flat_hash_map<std::string, std::vector<std::vector<uint64_t>>> fid_v1;\n  };\n  void InitInstanceWrapper(InstanceWrapper *instance_wrapper);\n\n  template <typename TInput>\n  Status FeatureParallelParse(OpKernelContext *ctx, const TInput &input,\n                              OpOutputList *fid_list_out_list,\n                              OpOutputList *fid_list_row_splits_out_list,\n                              OpOutputList *fid_list_row_splits_size_out_list);\n\n  int GetBatchSize(const InstanceWrapper &instance_wrapper) {\n    return instance_wrapper.instances.size();\n  }\n  int GetBatchSize(const ::monolith::io::proto::ExampleBatch &example_batch) {\n    return example_batch.batch_size();\n  }\n  template <typename TInput>\n  int GetBatchSize(const std::vector<TInput> &inputs) {\n    return inputs.size();\n  }\n  void ParallelRun(OpKernelContext *ctx, int task_count,\n                   const std::function<void(int64, int64)> &fn);\n\n  template <typename TData>\n  struct TensorSliceAccessor {\n    TData *ptr = nullptr;\n    int64_t size = 0;\n    TData &operator()(int64_t index) {\n      // CHECK(index >= 0 && index < size);\n      return *(ptr + index);\n    }\n  };\n  void FillFidList(uint64_t value,\n                   std::vector<std::vector<uint64_t>> &shard_vec,\n                   MultiShardUniqHashTable &shard_uniq_hashtable,\n                   tensorflow::TTypes<uint64_t>::Flat fid_offset_flat,\n                   int feature_output_index, int *offset);\n  void FillFidList(uint64_t value,\n                   std::vector<std::vector<uint64_t>> &shard_vec,\n                   tensorflow::TTypes<uint64_t>::Flat fid_offset_flat,\n                   int feature_output_index, int *offset);\n  void FillFidList(uint64_t value,\n                   MultiShardUniqHashTable &shard_uniq_hashtable,\n                   tensorflow::TTypes<uint64_t>::Flat fid_offset_flat,\n                   int feature_output_index, int *offset);\n  void CopyFidList(const std::vector<uint64_t> &shard_ptr, int offset,\n                   TensorSliceAccessor<int64_t> *to);\n  void CopyFidList(const absl::flat_hash_map<uint64_t, int> &shard_ptr,\n                   int offset, TensorSliceAccessor<int64_t> *to);\n\n  Status CreateOffsetTensor(\n      OpKernelContext *ctx,\n      const std::vector<std::vector<int>> &all_feature_counter,\n      int all_feature_counter_size, Tensor **nfl_offset_tensor,\n      Tensor **feature_offset_tensor, Tensor **fid_offset_tensor,\n      OpOutputList *fid_list_row_splits_out_list,\n      OpOutputList *fid_list_row_splits_size_out_list,\n      std::vector<Tensor> &tmp_tensor_list,\n      std::vector<TensorSliceAccessor<int64_t>> &fid_list_row_splits_flat_list,\n      std::vector<int> *nfl_fid_offset,\n      const std::unordered_set<int> *shared_feature);\n\n  struct FeatureInfo {\n    std::string feature_name;\n    std::string table_name;\n    int feature_index = -1;\n    int table_index = -1;\n    int feature_in_table_index = -1;\n\n    int table_feature_count;\n    int output_pre_index;\n    int dims_sum;\n\n    int version = 1;\n    int GetFidOutputIndex(int ps_i) {\n      if (version == 2) {\n        return output_pre_index + ps_i * table_feature_count +\n               feature_in_table_index;\n      } else {\n        return output_pre_index + ps_i;\n      }\n    }\n    int GetPsShard(int fid_offset) {\n      if (version == 2) {\n        return (fid_offset - output_pre_index - feature_in_table_index) /\n               table_feature_count;  // no use, only version==1 or version==3\n                                     // will call this func\n      } else {\n        return fid_offset - output_pre_index;\n      }\n    }\n  };\n\n  absl::flat_hash_map<std::string, FeatureInfo> feature_conf_;\n  std::vector<FeatureInfo *> feature_index_conf_;\n  std::vector<FeatureInfo *> feature_cfg_list_;\n  struct TableInfo {\n    std::string table_name;\n    int table_index = -1;\n    int feature_count = 0;\n    std::vector<int> feature_index_list;\n  };\n  absl::flat_hash_map<std::string, TableInfo> table_conf_;\n  std::vector<TableInfo *> table_cfg_list_;\n  absl::flat_hash_map<int, std::string>\n      slot_id_to_feature_name_;  // instance fid_v1 slot 特征 映射\n\n  int ps_num_ = 0;\n  int single_thread_feature_watermark_ = 80000 * 4;\n  int single_thread_assign_watermark_ = 100000 * 4;\n  int int64_size_ = sizeof(int64_t);\n\n  static constexpr uint32_t SHARED_FLAG = (1L << 31);\n\n  bool unique_ = false;\n  int parallel_flag_ = 0;\n  int input_type_ = 0;\n  bool enable_parallel_ = true;\n  int version_ = 1;\n\n  FeatureNameMapperTfBridge *mapper_ = nullptr;\n  FeatureNameMapper *mapper_raw_ptr_ = nullptr;\n\n  template <typename TContext>\n  void SplitTask(const std::vector<TContext> &context_list, int limit,\n                 std::vector<std::vector<int>> *out);\n\n#define DFeatureParallelMakeUpTask1Context(INPUT_TYPE)                      \\\n  template <typename TFeatureParallelTask1Context>                          \\\n  void FeatureParallelMakeUpTask1Context(                                   \\\n      const INPUT_TYPE &input, int batch_size,                              \\\n      std::vector<TFeatureParallelTask1Context> *task_context_list,         \\\n      absl::flat_hash_map<int, TFeatureParallelTask1Context *>              \\\n          *feature_shard_count_map,                                         \\\n      absl::flat_hash_map<int, std::vector<TFeatureParallelTask1Context *>> \\\n          *table_feature_map,                                               \\\n      std::vector<std::vector<int>> *all_feature_counter,                   \\\n      std::unordered_set<int> *shared_feature, int *all_feature_counter_size)\n  DFeatureParallelMakeUpTask1Context(::monolith::io::proto::ExampleBatch);\n  DFeatureParallelMakeUpTask1Context(\n      std::vector<const ::monolith::io::proto::Example *>);\n  DFeatureParallelMakeUpTask1Context(InstanceWrapper);\n#undef DFeatureParallelMakeUpTask1Context\n\n#define DFeatureParallelDoTask1(INPUT_TYPE)                                \\\n  template <typename TFeatureParallelTask1Context>                         \\\n  void FeatureParallelDoTask1(                                             \\\n      const INPUT_TYPE &input, TFeatureParallelTask1Context *task_context, \\\n      std::vector<int> &nfl_fid_offset,                                    \\\n      tensorflow::TTypes<uint64_t>::Flat fid_offset_flat,                  \\\n      tensorflow::TTypes<int32_t>::Flat feature_offset_flat)\n  DFeatureParallelDoTask1(::monolith::io::proto::ExampleBatch);\n  DFeatureParallelDoTask1(std::vector<const ::monolith::io::proto::Example *>);\n  DFeatureParallelDoTask1(InstanceWrapper);\n#undef DFeatureParallelDoTask1\n};\n\ntemplate <typename TContext>\nvoid ShardingSparseFidsOp::SplitTask(const std::vector<TContext> &context_list,\n                                     int limit,\n                                     std::vector<std::vector<int>> *out) {\n  std::vector<int> full_index;\n  int pre_count = 0;\n  for (unsigned int i = 0; i < context_list.size(); ++i) {\n    const auto &context = context_list[i];\n    if (context.size == 0) {\n      continue;\n    } else if (context.size >= limit) {\n      full_index.push_back(i);\n    } else {\n      if (pre_count == 0) {\n        out->emplace_back(std::vector<int>());\n      }\n      if (pre_count + context.size <= limit) {\n        out->back().push_back(i);\n        pre_count += context.size;\n      } else {\n        out->emplace_back(std::vector<int>());\n        out->back().push_back(i);\n        pre_count = context.size;\n      }\n    }\n  }\n  for (auto index : full_index) {\n    out->emplace_back(std::vector<int>({index}));\n  }\n}\n\ntemplate <typename TFeatureParallelTask1Context>\nvoid ShardingSparseFidsOp::FeatureParallelMakeUpTask1Context(\n    const ::monolith::io::proto::ExampleBatch &example_batch, int batch_size,\n    std::vector<TFeatureParallelTask1Context> *task_context_list,\n    absl::flat_hash_map<int, TFeatureParallelTask1Context *>\n        *feature_shard_count_map,\n    absl::flat_hash_map<int, std::vector<TFeatureParallelTask1Context *>>\n        *table_feature_map,\n    std::vector<std::vector<int>> *all_feature_counter,\n    std::unordered_set<int> *shared_feature, int *all_feature_counter_size) {\n  std::vector<int> example_batch_feature_index(feature_conf_.size(), -1);\n  for (int n_i = 0; n_i < example_batch.named_feature_list_size(); ++n_i) {\n    const auto &named_feature_list = example_batch.named_feature_list(n_i);\n    auto &name = named_feature_list.name();\n    auto find_iter = feature_conf_.find(name);\n    if (find_iter == feature_conf_.end()) {\n      continue;\n    }\n    example_batch_feature_index[find_iter->second.feature_index] = n_i;\n  }\n  for (uint i = 0; i < example_batch_feature_index.size(); ++i) {\n    int n_i = example_batch_feature_index[i];\n    auto &feature_counter = (*all_feature_counter)[i];\n    if (n_i < 0) {\n      feature_counter.push_back(0);\n      shared_feature->insert(i);\n      *all_feature_counter_size += 1;\n      continue;\n    }\n    TFeatureParallelTask1Context *task_context = nullptr;\n    auto table_index = feature_cfg_list_[i]->table_index;\n    auto feature_shard_count_map_iter = feature_shard_count_map->find(i);\n    if (feature_shard_count_map_iter == feature_shard_count_map->end()) {\n      task_context_list->emplace_back(TFeatureParallelTask1Context());\n      task_context = &(task_context_list->back());\n      task_context->example_batch_feature_index = n_i;\n      task_context->feature_output_index = i;\n      (*feature_shard_count_map)[i] = task_context;\n      (*table_feature_map)[table_index].push_back(task_context);\n    } else {\n      task_context = feature_shard_count_map_iter->second;\n    }\n\n    const auto &named_feature_list = example_batch.named_feature_list(n_i);\n    int fid_size = 0;\n    if (named_feature_list.type() == FeatureListType::SHARED) {\n      const auto &feature = named_feature_list.feature(0);\n      int tmp_counter = 0;\n      if (feature.has_fid_v1_list()) {\n        tmp_counter = feature.fid_v1_list().value_size();\n      } else if (feature.has_fid_v2_list()) {\n        tmp_counter = feature.fid_v2_list().value_size();\n      }\n      fid_size += tmp_counter;\n      feature_counter.push_back(tmp_counter);\n      shared_feature->insert(i);\n      *all_feature_counter_size += 1;\n    } else {\n      feature_counter.reserve(batch_size);\n      *all_feature_counter_size += batch_size;\n      for (const auto &feature : named_feature_list.feature()) {\n        int tmp_counter = 0;\n        if (feature.has_fid_v1_list()) {\n          tmp_counter = feature.fid_v1_list().value_size();\n        } else if (feature.has_fid_v2_list()) {\n          tmp_counter = feature.fid_v2_list().value_size();\n        }\n        fid_size += tmp_counter;\n        feature_counter.push_back(tmp_counter);\n      }\n    }\n    if (fid_size > 0) {\n      task_context->size += fid_size;\n    }\n  }\n}\n\ntemplate <typename TFeatureParallelTask1Context>\nvoid ShardingSparseFidsOp::FeatureParallelMakeUpTask1Context(\n    const std::vector<const ::monolith::io::proto::Example *> &examples,\n    int batch_size,\n    std::vector<TFeatureParallelTask1Context> *task_context_list,\n    absl::flat_hash_map<int, TFeatureParallelTask1Context *>\n        *feature_shard_count_map,\n    absl::flat_hash_map<int, std::vector<TFeatureParallelTask1Context *>>\n        *table_feature_map,\n    std::vector<std::vector<int>> *all_feature_counter,\n    std::unordered_set<int> *shared_feature, int *all_feature_counter_size) {\n  task_context_list->resize(feature_conf_.size());\n  for (auto &feature_counter : *all_feature_counter) {\n    feature_counter.resize(batch_size, 0);\n  }\n  *all_feature_counter_size = batch_size * feature_conf_.size();\n\n  for (uint i = 0; i < task_context_list->size(); ++i) {\n    auto *task_context = &((*task_context_list)[i]);\n    task_context->named_feature_ptr_list.reserve(examples.size());\n    task_context->feature_sample_index.reserve(examples.size());\n    task_context->feature_output_index = i;\n    (*feature_shard_count_map)[feature_cfg_list_[i]->feature_index] =\n        task_context;\n    (*table_feature_map)[feature_cfg_list_[i]->table_index].push_back(\n        task_context);\n  }\n  for (uint ex_i = 0; ex_i < examples.size(); ++ex_i) {\n    const ::monolith::io::proto::Example *example = examples[ex_i];\n    CHECK_NOTNULL(example);\n    for (const auto &named_feature : example->named_feature()) {\n      int fid_size = 0;\n      const auto &feature = named_feature.feature();\n      fid_size = feature.fid_v2_list().value_size();\n      if (fid_size == 0) {\n        fid_size = feature.fid_v1_list().value_size();\n      }\n      if (fid_size <= 0) continue;\n\n      int feature_index = -1;\n      auto sorted_id = named_feature.sorted_id();\n      if (sorted_id > 0) {  // 优先利用id查找，比string查找更快\n        if (feature_index_conf_.size()) {\n          if (sorted_id >= feature_index_conf_.size()) {\n            continue;\n          }\n          auto feature_ptr = feature_index_conf_.at(sorted_id);\n          if (feature_ptr == nullptr) {\n            continue;\n          }\n          feature_index = feature_ptr->feature_index;\n          // CHECK_EQ(named_feature.name(), feature_ptr->feature_name);\n        } else {\n          LOG_EVERY_N_SEC(ERROR, 10) << \"FeatureNameMapper error\";\n        }\n      } else {\n        const auto &name = named_feature.name();\n        auto find_iter = feature_conf_.find(name);\n        if (find_iter == feature_conf_.end()) {\n          continue;\n        }\n        feature_index = find_iter->second.feature_index;\n      }\n\n      CHECK(feature_index >= 0 && feature_index < task_context_list->size());\n      auto &task_context = (*task_context_list)[feature_index];\n      auto &feature_counter = (*all_feature_counter)[feature_index];\n      feature_counter[ex_i] = fid_size;\n      task_context.size += fid_size;\n      task_context.named_feature_ptr_list.push_back(&named_feature);\n      task_context.feature_sample_index.push_back(feature_index * batch_size +\n                                                  ex_i);\n    }\n  }\n}\n\ntemplate <typename TFeatureParallelTask1Context>\nvoid ShardingSparseFidsOp::FeatureParallelMakeUpTask1Context(\n    const ShardingSparseFidsOp::InstanceWrapper &instance_wrapper,\n    int batch_size,\n    std::vector<TFeatureParallelTask1Context> *task_context_list,\n    absl::flat_hash_map<int, TFeatureParallelTask1Context *>\n        *feature_shard_count_map,\n    absl::flat_hash_map<int, std::vector<TFeatureParallelTask1Context *>>\n        *table_feature_map,\n    std::vector<std::vector<int>> *all_feature_counter,\n    std::unordered_set<int> *shared_feature, int *all_feature_counter_size) {\n  task_context_list->resize(feature_conf_.size());\n  for (auto &feature_counter : *all_feature_counter) {\n    feature_counter.resize(batch_size, 0);\n  }\n  *all_feature_counter_size = batch_size * feature_conf_.size();\n\n  std::vector<std::vector<std::pair<const InstanceWrapper::FeaturePtr, int>>>\n      feature_named_feature_ptr_list(feature_conf_.size());\n\n  for (auto &elem : feature_named_feature_ptr_list) {\n    elem.reserve(batch_size);\n  }\n\n  // fid v1\n  for (auto &iter : instance_wrapper.fid_v1) {\n    auto find_iter = feature_conf_.find(iter.first);\n    if (find_iter == feature_conf_.end()) {\n      continue;\n    }\n    auto &named_feature_ptr_list =\n        feature_named_feature_ptr_list[find_iter->second.feature_index];\n    for (uint ex_i = 0; ex_i < iter.second.size(); ++ex_i) {\n      named_feature_ptr_list.push_back(std::make_pair(\n          InstanceWrapper::FeaturePtr(&iter.second[ex_i], nullptr), ex_i));\n    }\n  }\n  // fid v2\n  for (uint ex_i = 0; ex_i < instance_wrapper.instances.size(); ++ex_i) {\n    const auto *instance = instance_wrapper.instances[ex_i];\n    CHECK_NOTNULL(instance);\n    for (const auto &named_feature : instance->feature()) {\n      const auto &name = named_feature.name();\n      auto find_iter = feature_conf_.find(name);\n      if (find_iter == feature_conf_.end()) {\n        continue;\n      }\n      auto &named_feature_ptr_list =\n          feature_named_feature_ptr_list[find_iter->second.feature_index];\n      named_feature_ptr_list.push_back(std::make_pair(\n          InstanceWrapper::FeaturePtr({nullptr, &named_feature}), ex_i));\n    }\n  }\n  for (uint i = 0; i < feature_named_feature_ptr_list.size(); ++i) {\n    auto *task_context = &((*task_context_list)[i]);\n    task_context->instance_feature_ptr_list.reserve(batch_size);\n    task_context->feature_sample_index.reserve(batch_size);\n    task_context->feature_output_index = i;\n    (*feature_shard_count_map)[feature_cfg_list_[i]->feature_index] =\n        task_context;\n    (*table_feature_map)[feature_cfg_list_[i]->table_index].push_back(\n        task_context);\n\n    auto &feature_counter = (*all_feature_counter)[i];\n    for (uint j = 0; j < feature_named_feature_ptr_list[i].size(); ++j) {\n      auto &info = feature_named_feature_ptr_list[i][j];\n      auto named_feature = info.first;\n      auto ex_i = info.second;\n\n      int fid_size = 0;\n      if (named_feature.fid_v1) {\n        fid_size = named_feature.fid_v1->size();\n      } else {\n        fid_size += named_feature.fid_v2->fid_size();\n        // this is a sequence feature list.\n        for (const auto &fidlist : named_feature.fid_v2->fid_list()) {\n          fid_size += fidlist.value_size();\n        }\n      }\n      if (fid_size > 0) {\n        feature_counter[ex_i] = fid_size;\n        task_context->size += fid_size;\n        task_context->instance_feature_ptr_list.push_back(named_feature);\n        task_context->feature_sample_index.push_back(i * batch_size + ex_i);\n      }\n    }\n  }\n}\n\ntemplate <typename TFeatureParallelTask1Context>\nvoid ShardingSparseFidsOp::FeatureParallelDoTask1(\n    const ::monolith::io::proto::ExampleBatch &example_batch,\n    TFeatureParallelTask1Context *task_context,\n    std::vector<int> &nfl_fid_offset,\n    tensorflow::TTypes<uint64_t>::Flat fid_offset_flat,\n    tensorflow::TTypes<int32_t>::Flat feature_offset_flat) {\n  auto &shard_vec = task_context->fid_list;\n  auto &uniq_hashtable = task_context->uniq_hashtable;\n  const auto &named_feature_list = example_batch.named_feature_list(\n      task_context->example_batch_feature_index);\n  auto feature_output_index = task_context->feature_output_index;\n  auto offset = nfl_fid_offset[feature_output_index];\n  if (named_feature_list.type() == FeatureListType::SHARED) {\n    const auto &feature = named_feature_list.feature(0);\n    if (feature.has_fid_v1_list()) {\n      for (int i = 0; i < feature.fid_v1_list().value_size(); ++i) {\n        auto value = convert_fid_v1_to_v2(feature.fid_v1_list().value(i));\n        FillFidList(value, shard_vec, uniq_hashtable, fid_offset_flat,\n                    feature_output_index, &offset);\n      }\n    } else if (feature.has_fid_v2_list()) {\n      for (int i = 0; i < feature.fid_v2_list().value_size(); ++i) {\n        auto value = feature.fid_v2_list().value(i);\n        FillFidList(value, shard_vec, uniq_hashtable, fid_offset_flat,\n                    feature_output_index, &offset);\n      }\n    }\n  } else {\n    for (const auto &feature : named_feature_list.feature()) {\n      if (feature.has_fid_v1_list()) {\n        for (int i = 0; i < feature.fid_v1_list().value_size(); ++i) {\n          auto value = convert_fid_v1_to_v2(feature.fid_v1_list().value(i));\n          FillFidList(value, shard_vec, uniq_hashtable, fid_offset_flat,\n                      feature_output_index, &offset);\n        }\n      } else if (feature.has_fid_v2_list()) {\n        for (int i = 0; i < feature.fid_v2_list().value_size(); ++i) {\n          auto value = feature.fid_v2_list().value(i);\n          FillFidList(value, shard_vec, uniq_hashtable, fid_offset_flat,\n                      feature_output_index, &offset);\n        }\n      }\n    }\n  }\n  task_context->feature_offset = offset - nfl_fid_offset[feature_output_index];\n}\n\ntemplate <typename TFeatureParallelTask1Context>\nvoid ShardingSparseFidsOp::FeatureParallelDoTask1(\n    const std::vector<const ::monolith::io::proto::Example *> &examples,\n    TFeatureParallelTask1Context *task_context,\n    std::vector<int> &nfl_fid_offset,\n    tensorflow::TTypes<uint64_t>::Flat fid_offset_flat,\n    tensorflow::TTypes<int32_t>::Flat feature_offset_flat) {\n  auto &shard_vec = task_context->fid_list;\n  auto &uniq_hashtable = task_context->uniq_hashtable;\n  auto feature_output_index = task_context->feature_output_index;\n  int offset = 0;\n  for (uint sub_task_index = 0;\n       sub_task_index < task_context->named_feature_ptr_list.size();\n       ++sub_task_index) {\n    const auto named_feature_ptr =\n        task_context->named_feature_ptr_list[sub_task_index];\n    offset =\n        feature_offset_flat(task_context->feature_sample_index[sub_task_index]);\n    const auto &feature = named_feature_ptr->feature();\n    if (feature.has_fid_v1_list()) {\n      for (int i = 0; i < feature.fid_v1_list().value_size(); ++i) {\n        auto value = convert_fid_v1_to_v2(feature.fid_v1_list().value(i));\n        FillFidList(value, shard_vec, uniq_hashtable, fid_offset_flat,\n                    feature_output_index, &offset);\n      }\n    } else if (feature.has_fid_v2_list()) {\n      for (int i = 0; i < feature.fid_v2_list().value_size(); ++i) {\n        auto value = feature.fid_v2_list().value(i);\n        FillFidList(value, shard_vec, uniq_hashtable, fid_offset_flat,\n                    feature_output_index, &offset);\n      }\n    }\n  }\n  task_context->feature_offset = offset - nfl_fid_offset[feature_output_index];\n}\n\ntemplate <typename TFeatureParallelTask1Context>\nvoid ShardingSparseFidsOp::FeatureParallelDoTask1(\n    const ShardingSparseFidsOp::InstanceWrapper &instance_wrapper,\n    TFeatureParallelTask1Context *task_context,\n    std::vector<int> &nfl_fid_offset,\n    tensorflow::TTypes<uint64_t>::Flat fid_offset_flat,\n    tensorflow::TTypes<int32_t>::Flat feature_offset_flat) {\n  auto &shard_vec = task_context->fid_list;\n  auto &uniq_hashtable = task_context->uniq_hashtable;\n  auto feature_output_index = task_context->feature_output_index;\n  int offset = 0;\n  for (uint sub_task_index = 0;\n       sub_task_index < task_context->instance_feature_ptr_list.size();\n       ++sub_task_index) {\n    const auto &named_feature_ptr =\n        task_context->instance_feature_ptr_list[sub_task_index];\n    offset =\n        feature_offset_flat(task_context->feature_sample_index[sub_task_index]);\n    if (named_feature_ptr.fid_v1) {\n      for (auto value : *named_feature_ptr.fid_v1) {\n        value = convert_fid_v1_to_v2(value);\n        FillFidList(value, shard_vec, uniq_hashtable, fid_offset_flat,\n                    feature_output_index, &offset);\n      }\n    } else {\n      for (const auto &value : named_feature_ptr.fid_v2->fid()) {\n        FillFidList(value, shard_vec, uniq_hashtable, fid_offset_flat,\n                    feature_output_index, &offset);\n      }\n      // this is a sequence feature list.\n      for (const auto &fid_list : named_feature_ptr.fid_v2->fid_list()) {\n        for (const auto &value : fid_list.value()) {\n          FillFidList(value, shard_vec, uniq_hashtable, fid_offset_flat,\n                      feature_output_index, &offset);\n        }\n      }\n    }\n  }\n  task_context->feature_offset = offset - nfl_fid_offset[feature_output_index];\n}\n\ntemplate <typename TInput>\nStatus ShardingSparseFidsOp::FeatureParallelParse(\n    OpKernelContext *ctx, const TInput &input, OpOutputList *fid_list_out_list,\n    OpOutputList *fid_list_row_splits_out_list,\n    OpOutputList *fid_list_row_splits_size_out_list) {\n  int batch_size = GetBatchSize(input);\n  struct FeatureParallelTask1Context {\n    std::vector<std::vector<uint64_t>> fid_list;\n    MultiShardUniqHashTable uniq_hashtable;\n    int example_batch_feature_index = -1;  // use for example_batch\n    std::vector<const ::monolith::io::proto::NamedFeature *>\n        named_feature_ptr_list;  // use for example\n    std::vector<InstanceWrapper::FeaturePtr>\n        instance_feature_ptr_list;  // for instance\n    int feature_output_index = -1;\n    std::vector<int> feature_sample_index;  // use for example\n    int size = 0;\n    std::vector<int> table_offset;  // (ps_num_, 0);\n    int feature_offset = 0;\n  };\n  std::vector<FeatureParallelTask1Context> task_context_list;\n  absl::flat_hash_map<int, FeatureParallelTask1Context *>\n      feature_shard_count_map;\n  absl::flat_hash_map<int, std::vector<FeatureParallelTask1Context *>>\n      table_feature_map;\n\n  Tensor *fid_offset_tensor, *feature_offset_tensor;\n  std::vector<Tensor> tmp_tensor_list;\n  std::vector<TensorSliceAccessor<int64_t>> fid_list_row_splits_flat_list;\n  std::vector<int> nfl_fid_offset(feature_conf_.size());\n\n  {\n    profiler::TraceMe activity([]() { return \"ShardingSparseFidsOp::Alloc\"; });\n    feature_shard_count_map.reserve(feature_conf_.size());\n    task_context_list.reserve(feature_conf_.size());\n    table_feature_map.reserve(table_cfg_list_.size());\n\n    std::vector<std::vector<int>> all_feature_counter(feature_conf_.size());\n    int all_feature_counter_size = 0;\n    std::unordered_set<int> shared_feature;\n    FeatureParallelMakeUpTask1Context(\n        input, batch_size, &task_context_list, &feature_shard_count_map,\n        &table_feature_map, &all_feature_counter, &shared_feature,\n        &all_feature_counter_size);\n    for (auto &task_context : task_context_list) {\n      task_context.fid_list.resize(ps_num_);\n      task_context.uniq_hashtable.resize(ps_num_);\n      task_context.table_offset.resize(ps_num_, 0);\n      if (task_context.size > 0) {\n        int reserve_size = task_context.size * 6 / 5 / ps_num_;\n        if (unique_) {\n          task_context.uniq_hashtable.reserve(reserve_size);\n        } else {\n          for (auto &fid_list_part : task_context.fid_list) {\n            fid_list_part.reserve(reserve_size);\n          }\n        }\n      }\n    }\n    Tensor *nfl_offset_tensor;\n    TF_RETURN_IF_ERROR(\n        ctx,\n        CreateOffsetTensor(ctx, all_feature_counter, all_feature_counter_size,\n                           &nfl_offset_tensor, &feature_offset_tensor,\n                           &fid_offset_tensor, fid_list_row_splits_out_list,\n                           fid_list_row_splits_size_out_list, tmp_tensor_list,\n                           fid_list_row_splits_flat_list, &nfl_fid_offset,\n                           &shared_feature));\n  }\n  auto fid_offset_flat = fid_offset_tensor->flat<uint64_t>();\n  auto feature_offset_flat = feature_offset_tensor->flat<int32_t>();\n\n  std::vector<std::vector<int>> task_split;\n  SplitTask<FeatureParallelTask1Context>(\n      task_context_list, single_thread_feature_watermark_, &task_split);\n  {\n    profiler::TraceMe activity([]() { return \"ShardingSparseFidsOp::AddVec\"; });\n    activity.AppendMetadata([&task_split, &task_context_list] {\n      return profiler::TraceMeEncode({{\"task_num\", task_context_list.size()},\n                                      {\"split_num\", task_split.size()}});\n    });\n    std::vector<size_t> capacities(task_split.size(), 0);\n    auto task_func = [this, &task_context_list, &input, &task_split,\n                      &nfl_fid_offset, &fid_offset_flat, &feature_offset_flat,\n                      &capacities](const int64 begin, const int64 end) {\n      UniqHashTable uniq_hashtable;\n      for (int64 task_index = begin; task_index < end; ++task_index) {\n        auto &task_index_list = task_split[task_index];\n        for (auto index : task_index_list) {\n          auto &task_context = task_context_list[index];\n          if (unique_) {\n            uniq_hashtable.Reset();\n            task_context.uniq_hashtable.init(&uniq_hashtable);\n          }\n          FeatureParallelDoTask1(input, &task_context, nfl_fid_offset,\n                                 fid_offset_flat, feature_offset_flat);\n        }\n        capacities[task_index] = uniq_hashtable.Capacity();\n      }\n    };\n\n    ParallelRun(ctx, task_split.size(), task_func);\n\n    double avg_capacity = 0;\n    if (capacities.size() > 0) {\n      avg_capacity = static_cast<double>(std::accumulate(capacities.begin(),\n                                                         capacities.end(), 0)) /\n                     capacities.size();\n    }\n    activity.AppendMetadata([&avg_capacity] {\n      return profiler::TraceMeEncode({{\"hashtable_size\", avg_capacity}});\n    });\n    monolith::GetMetrics()->emit_timer(\"sharding_sparse_fids_op_hashtable_capacity\",\n                                        avg_capacity);\n  }\n\n  struct TaskContext2 {\n    // fill fid_list\n    TaskContext2(const TensorSliceAccessor<int64_t> &accessor_,\n                 std::vector<uint64_t> *shard_fid_list_, std::vector<uint64_t> *shard_ptr_,\n                 int size_, int offset_)\n        : accessor(accessor_),\n          shard_fid_list(shard_fid_list_),\n          shard_ptr(shard_ptr_),\n          size(size_),\n          offset(offset_) {}\n\n    // rewrite fid_offset\n    explicit TaskContext2(FeatureParallelTask1Context *task_context_)\n        : task1_context(task_context_), size(task_context_->feature_offset) {}\n    TensorSliceAccessor<int64_t> accessor;\n    std::vector<uint64_t> *shard_fid_list = nullptr;\n    std::vector<uint64_t> *shard_ptr = nullptr;\n    int size = -1;\n    int offset = -1;\n    FeatureParallelTask1Context *task1_context = nullptr;\n  };\n  std::vector<TaskContext2> task2_context_list;\n\n  Tensor *fid_list_table_row_length_tensor = nullptr;\n  Tensor *fid_list_shard_row_lenth_tensor = nullptr;\n  Tensor *fid_list_emb_row_lenth_tensor = nullptr;\n  std::vector<TensorSliceAccessor<int64_t>> fid_list_tensor_vec(\n      table_cfg_list_.size() * ps_num_);\n  if (version_ == 4) {\n    int size_record_total = 0;\n    std::vector<int> size_record(table_cfg_list_.size() * ps_num_, 0);\n    for (uint table_index = 0; table_index < table_cfg_list_.size();\n         ++table_index) {\n      // auto &table_name = table_names_[table_index];\n      auto table_feature_map_find_iter = table_feature_map.find(table_index);\n      std::vector<FeatureParallelTask1Context *> *feature_vec_ptr = nullptr;\n      if (table_feature_map_find_iter != table_feature_map.end()) {\n        feature_vec_ptr = &(table_feature_map_find_iter->second);\n      }\n      for (int ps_num_i = 0; ps_num_i < ps_num_; ++ps_num_i) {\n        int &size = size_record[table_index * ps_num_ + ps_num_i];\n        if (feature_vec_ptr != nullptr) {\n          for (auto task_context_ptr : *feature_vec_ptr) {\n            if (unique_) {\n              size += task_context_ptr->uniq_hashtable.fid_num(ps_num_i);\n            } else {\n              size += task_context_ptr->fid_list[ps_num_i].size();\n            }\n          }\n        }\n        size_record_total += size;\n      }\n    }\n    Tensor *fid_list_tensor;\n    TF_RETURN_IF_ERROR(ctx,\n                       ctx->allocate_output(\"fid_list\", TensorShape({\n                                                            size_record_total,\n                                                        }),\n                                            &fid_list_tensor));\n    fid_list_tensor->flat<int64_t>().setZero();\n\n    int pre_count = 0;\n    for (uint ps_num_i = 0; ps_num_i < ps_num_; ++ps_num_i) {\n      for (uint table_index = 0; table_index < table_cfg_list_.size();\n           ++table_index) {\n        int size = size_record[table_index * ps_num_ + ps_num_i];\n        fid_list_tensor_vec[table_index * ps_num_ + ps_num_i] =\n            TensorSliceAccessor<int64_t>(\n                {static_cast<int64_t *>(fid_list_tensor->data()) + pre_count,\n                 size});\n        // fid_list_tensor->Slice(pre_count, pre_count + size);\n        pre_count += size;\n      }\n    }\n\n    TF_RETURN_IF_ERROR(\n        ctx,\n        ctx->allocate_output(\"fid_list_table_row_length\",\n                             tensorflow::TensorShape({\n                                 ps_num_ * table_cfg_list_.size(),\n                             }),\n                             &fid_list_table_row_length_tensor));\n    fid_list_table_row_length_tensor->flat<int32_t>().setZero();\n\n    TF_RETURN_IF_ERROR(ctx,\n                       ctx->allocate_output(\"fid_list_shard_row_lenth\",\n                                            tensorflow::TensorShape({\n                                                ps_num_,\n                                            }),\n                                            &fid_list_shard_row_lenth_tensor));\n    fid_list_shard_row_lenth_tensor->flat<int32_t>().setZero();\n\n    TF_RETURN_IF_ERROR(\n        ctx,\n        ctx->allocate_output(\"fid_list_emb_row_lenth\",\n                             tensorflow::TensorShape({\n                                 ps_num_ * table_cfg_list_.size(),\n                             }),\n                             &fid_list_emb_row_lenth_tensor));\n    fid_list_emb_row_lenth_tensor->flat<int32_t>().setZero();\n  }\n  int index = -1;\n  Tensor *emb_size_tensor;\n  if (version_ == 5) {\n    TF_RETURN_IF_ERROR(ctx, ctx->allocate_output(\"emb_size\",\n                                                 TensorShape({\n                                                   table_cfg_list_.size() * ps_num_,\n                                                 }),\n                                                 &emb_size_tensor));\n  }\n  for (uint table_index = 0; table_index < table_cfg_list_.size();\n       ++table_index) {\n    // auto &table_name = table_names_[table_index];\n    auto table_feature_map_find_iter = table_feature_map.find(table_index);\n    std::vector<FeatureParallelTask1Context *> *feature_vec_ptr = nullptr;\n    if (table_feature_map_find_iter != table_feature_map.end()) {\n      feature_vec_ptr = &(table_feature_map_find_iter->second);\n    }\n    for (int ps_num_i = 0; ps_num_i < ps_num_; ++ps_num_i) {\n      auto &cur_tensor_flat =\n          fid_list_row_splits_flat_list[table_index * ps_num_ + ps_num_i];\n      int size = 0;\n      int pre_offset = 0;\n      if (feature_vec_ptr != nullptr) {\n        for (auto task_context_ptr : *feature_vec_ptr) {\n          task_context_ptr->table_offset[ps_num_i] = pre_offset;\n          int cur_fid_size = 0;\n          if (unique_) {\n            cur_fid_size = task_context_ptr->uniq_hashtable.fid_num(ps_num_i);\n          } else {\n            cur_fid_size = task_context_ptr->fid_list[ps_num_i].size();\n          }\n          size += cur_fid_size;\n          // std::cerr << \"cur_fid_size: \" << cur_fid_size << \" size: \" << size\n          // << std::endl << std::flush;\n          if (version_ == 3 || version_ == 4 || version_ == 5) {\n            int emb_size =\n                cur_fid_size *\n                feature_cfg_list_[task_context_ptr->feature_output_index]\n                    ->dims_sum;\n            if (version_ == 4) {\n              auto fid_list_emb_row_lenth_flat =\n                  fid_list_emb_row_lenth_tensor->flat<int32_t>();\n              fid_list_emb_row_lenth_flat(ps_num_i * table_cfg_list_.size() +\n                                          table_index) += emb_size;\n            }\n            pre_offset += emb_size;\n          } else {\n            pre_offset = size;\n          }\n          auto cur_tensor_flat_index =\n              feature_cfg_list_[task_context_ptr->feature_output_index]\n                  ->feature_in_table_index +\n              1;\n          cur_tensor_flat(cur_tensor_flat_index) = cur_fid_size;\n        }\n      }\n      if (version_ == 5) {\n        emb_size_tensor->flat<int32>()(table_index * ps_num_ + ps_num_i) = pre_offset;\n      }\n      TensorSliceAccessor<int64_t> cur_accessor;\n      for (uint z = 2; z <= table_cfg_list_[table_index]->feature_count; ++z) {\n        cur_tensor_flat(z) += cur_tensor_flat(z - 1);\n      }\n      if (version_ != 4) {\n        Tensor *cur_tensor;\n        TF_RETURN_IF_ERROR(\n            ctx,\n            fid_list_out_list->allocate(++index, tensorflow::TensorShape{size},\n                                        &cur_tensor));\n        if (size == 0) {\n          std::memset(cur_tensor->data(), 0, cur_tensor->TotalBytes());\n          continue;\n        }\n        cur_accessor = TensorSliceAccessor<int64_t>(\n            {static_cast<int64_t *>(cur_tensor->data()),\n             cur_tensor->NumElements()});\n      } else {\n        cur_accessor = fid_list_tensor_vec[++index];\n\n        fid_list_table_row_length_tensor->flat<int32_t>()(\n            ps_num_i * table_cfg_list_.size() + table_index) += size;\n        fid_list_shard_row_lenth_tensor->flat<int32_t>()(ps_num_i) += size;\n      }\n      int offset = 0;\n      for (auto task_context_ptr : *feature_vec_ptr) {\n        std::vector<uint64_t> *shard_fid_list = nullptr;\n        std::vector<uint64_t> *shard_ptr = nullptr;\n        int tmp_size = 0;\n        if (unique_) {\n          shard_fid_list =\n              &(task_context_ptr->uniq_hashtable.fid_list(ps_num_i));\n          tmp_size = shard_fid_list->size();\n        } else {\n          shard_ptr = &(task_context_ptr->fid_list[ps_num_i]);\n          tmp_size = shard_ptr->size();\n        }\n        DCHECK_LE(offset + tmp_size, size);\n        TaskContext2 tmp_task_context(cur_accessor, shard_fid_list, shard_ptr,\n                                      tmp_size, offset);\n        task2_context_list.emplace_back(tmp_task_context);\n        offset += tmp_size;\n      }\n    }\n    if (version_ != 2) {\n      if (feature_vec_ptr) {\n        for (auto task_context_ptr : *feature_vec_ptr) {\n          TaskContext2 tmp_task_context(task_context_ptr);\n          task2_context_list.emplace_back(tmp_task_context);\n        }\n      }\n    }\n  }\n\n  std::vector<std::vector<int>> task2_split;\n  SplitTask<TaskContext2>(task2_context_list, single_thread_assign_watermark_,\n                          &task2_split);\n\n  {\n    profiler::TraceMe activity([]() { return \"ShardingSparseFidsOp::Copy\"; });\n    auto tensor_assign_func = [this, ctx, &task2_context_list, &task2_split,\n                               &nfl_fid_offset, &fid_offset_flat](\n        const int64 begin, const int64 end) {\n      for (int64 task_index = begin; task_index < end; ++task_index) {\n        auto &task_index_list = task2_split[task_index];\n        for (auto index : task_index_list) {\n          auto &task_context = task2_context_list[index];\n          if (task_context.task1_context) {\n            auto &task1_context = *task_context.task1_context;\n            auto offset = nfl_fid_offset[task1_context.feature_output_index];\n            /*\n            auto table_index =\n                feature_cfg_list_[task1_context.feature_output_index]\n                    ->table_index *\n                ps_num_;\n            for (int i = 0; i < task1_context.feature_offset; ++i, ++offset)\n            { auto cur = fid_offset_flat(offset); cur +=\n            task1_context.table_offset.at(static_cast<int>(cur >> 32) -\n                                                   table_index);\n              fid_offset_flat(offset) = cur;\n            }*/\n            auto feature_cfg =\n                feature_cfg_list_[task1_context.feature_output_index];\n            for (int i = 0; i < task1_context.feature_offset; ++i, ++offset) {\n              auto cur = fid_offset_flat(offset);\n              cur += task1_context.table_offset.at(\n                  feature_cfg->GetPsShard(static_cast<int>(cur >> 32)));\n              fid_offset_flat(offset) = cur;\n            }\n          } else if (unique_) {\n            CopyFidList(*task_context.shard_fid_list, task_context.offset,\n                        &task_context.accessor);\n          } else {\n            CopyFidList(*task_context.shard_ptr, task_context.offset,\n                        &task_context.accessor);\n          }\n        }\n      }\n    };\n    ParallelRun(ctx, task2_split.size(), tensor_assign_func);\n  }\n  return Status::OK();\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif MONOLITH_NATIVE_TRAINING_DATA_KERNELS_PARSE_SPARSE_FEATURE_LIB_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/pb_dataset_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <utility>\n\n#include \"absl/strings/str_format.h\"\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/dataset.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_def_builder.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/partial_tensor_shape.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/lib/io/buffered_inputstream.h\"\n#include \"tensorflow/core/lib/io/inputbuffer.h\"\n#include \"tensorflow/core/lib/io/zlib_compression_options.h\"\n\n#include \"monolith/native_training/data/kernels/feature_name_mapper_tf_bridge.h\"\n#include \"monolith/native_training/data/training_instance/cc/data_reader.h\"\n#include \"monolith/native_training/runtime/common/metrics.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\nnamespace {\nusing Example = ::monolith::io::proto::Example;\nusing ExampleBatch = ::monolith::io::proto::ExampleBatch;\nusing Instance = ::parser::proto::Instance;\nusing ::tensorflow::monolith_tf::BaseStreamReader;\nusing ::tensorflow::monolith_tf::DataFormatOptions;\nusing ::tensorflow::monolith_tf::ExampleBatchIterator;\nusing ::tensorflow::monolith_tf::ExampleToInstance;\nusing ::tensorflow::monolith_tf::FeatureNameMapper;\nusing ::tensorflow::monolith_tf::FeatureNameMapperTfBridge;\nusing ::tensorflow::monolith_tf::FeaturePruningType;\nusing ::tensorflow::monolith_tf::FileStreamReader;\nusing ::tensorflow::monolith_tf::InputCompressType;\nusing ::tensorflow::monolith_tf::InstanceToExample;\nusing ::tensorflow::monolith_tf::PBIterator;\nusing ::tensorflow::monolith_tf::PBIteratorWithDataFormatTrans;\nusing ::tensorflow::monolith_tf::PBIteratorWithDataFormatTransBaseOutput;\nusing ::tensorflow::monolith_tf::StdinStreamReader;\n\nstruct DsOptions : DataFormatOptions {\n  bool use_snappy = false;\n  int32 compression_type = InputCompressType::UNKNOW;\n  int64 buffer_size = 64 * 1024 * 1024;\n};\n\n}  // namespace\n\n// This is the instance dataset op and used in the estimator as input fn.\nclass PBDatasetOp : public DatasetOpKernel {\n public:\n  static constexpr const char *const kDatasetType = \"PbDataset\";\n  static constexpr const char *const kFileName = \"file_name\";\n  static constexpr const char *const kBufferSize = \"buffer_size\";\n  static constexpr const char *const kUseSnappy = \"use_snappy\";\n  static constexpr const char *const kLagrangexHeader = \"lagrangex_header\";\n  static constexpr const char *const kHasSortId = \"has_sort_id\";\n  static constexpr const char *const kKafkaDump = \"kafka_dump\";\n  static constexpr const char *const kKafkaDumpPrefix = \"kafka_dump_prefix\";\n  static constexpr const char *const kInputPbType = \"input_pb_type\";\n  static constexpr const char *const kOutputPbType = \"output_pb_type\";\n  static constexpr const char *const kOutType = \"out_type\";\n  static constexpr const char *const kFeaturePruningType =\n      \"feature_pruning_type\";\n  static constexpr const char *const kFeatureNameList = \"feature_name_list\";\n  static constexpr const char *const kFeatureIdList = \"feature_id_list\";\n  static constexpr const char *const kCompressionType = \"compression_type\";\n\n  explicit PBDatasetOp(OpKernelConstruction *ctx) : DatasetOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(kOutType, &out_type_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(kCompressionType, &compression_type_));\n\n    auto creator = [this](FeatureNameMapperTfBridge **out_mapper) {\n      TF_RETURN_IF_ERROR(FeatureNameMapperTfBridge::New(out_mapper));\n      return Status::OK();\n    };\n    ResourceMgr *resource_mgr = ctx->resource_manager();\n    OP_REQUIRES_OK(ctx,\n                   resource_mgr->LookupOrCreate<FeatureNameMapperTfBridge>(\n                       resource_mgr->default_container(),\n                       FeatureNameMapperTfBridge::kName, &mapper_, creator));\n  }\n\n  ~PBDatasetOp() override { mapper_->Unref(); };\n\n private:\n  void MakeDataset(OpKernelContext *ctx, DatasetBase **output) override {\n    tstring file_name;\n    DsOptions options;\n    OP_REQUIRES_OK(ctx,\n                   ParseScalarArgument<tstring>(ctx, kFileName, &file_name));\n    OP_REQUIRES_OK(\n        ctx, ParseScalarArgument<bool>(ctx, kUseSnappy, &options.use_snappy));\n    options.compression_type = compression_type_;\n    OP_REQUIRES_OK(\n        ctx, ParseScalarArgument<bool>(ctx, kHasSortId, &options.has_sort_id));\n    OP_REQUIRES_OK(\n        ctx, ParseScalarArgument<bool>(ctx, kKafkaDump, &options.kafka_dump));\n    OP_REQUIRES_OK(ctx, ParseScalarArgument<bool>(ctx, kKafkaDumpPrefix,\n                                                  &options.kafka_dump_prefix));\n    OP_REQUIRES_OK(ctx, ParseScalarArgument<int64>(ctx, kBufferSize,\n                                                   &options.buffer_size));\n    OP_REQUIRES_OK(ctx, ParseScalarArgument<bool>(ctx, kLagrangexHeader,\n                                                  &options.lagrangex_header));\n    tstring input_pb_type;\n    OP_REQUIRES_OK(\n        ctx, ParseScalarArgument<tstring>(ctx, kInputPbType, &input_pb_type));\n    tstring output_pb_type;\n    OP_REQUIRES_OK(\n        ctx, ParseScalarArgument<tstring>(ctx, kOutputPbType, &output_pb_type));\n\n    int feature_pruning_type = 0;\n    OP_REQUIRES_OK(ctx, ParseScalarArgument<int>(ctx, kFeaturePruningType,\n                                                 &feature_pruning_type));\n\n    std::vector<tstring> feature_name_list;\n    OP_REQUIRES_OK(ctx, ParseVectorArgument<tstring>(ctx, kFeatureNameList,\n                                                     &feature_name_list));\n    std::vector<int32_t> feature_id_list;\n    OP_REQUIRES_OK(ctx, ParseVectorArgument<int32_t>(ctx, kFeatureIdList,\n                                                     &feature_id_list));\n    if (feature_name_list.size() != feature_id_list.size()) {\n      LOG(FATAL) << absl::StrFormat(\n          \"feature_name_list/feature_id_list size should match, while got %ld \"\n          \"vs %ld\",\n          feature_name_list.size(), feature_id_list.size());\n    }\n    std::unordered_set<std::string> feature_name_set(feature_name_list.begin(),\n                                                     feature_name_list.end());\n    std::unordered_set<int32_t> feature_id_set(feature_id_list.begin(),\n                                               feature_id_list.end());\n    if (feature_name_list.size() != feature_name_set.size()) {\n      LOG(FATAL)\n          << \"feature name list has duplicates, please investigate and retry !\";\n    }\n    if (feature_id_set.size() > feature_name_set.size()) {\n      LOG(FATAL) << \"feature_name -> feature_id should be  non-injective and \"\n                    \"surjective, that is feature_id_set.size() should be <= \"\n                    \"feature_name_set.size(), please investigate and retry !\";\n    }\n    output_ =\n        new Dataset(ctx, file_name, options, input_pb_type, output_pb_type,\n                    out_type_, feature_pruning_type, feature_name_list,\n                    feature_id_list, mapper_->GetFeatureNameMapper());\n    *output = output_;\n\n    nlohmann::json j;\n    j[kFileName] = file_name;\n    j[kUseSnappy] = options.use_snappy;\n    j[kCompressionType] = options.compression_type;\n    j[kHasSortId] = options.has_sort_id;\n    j[kKafkaDump] = options.kafka_dump;\n    j[kKafkaDumpPrefix] = options.kafka_dump_prefix;\n    j[kBufferSize] = options.buffer_size;\n    j[kLagrangexHeader] = options.lagrangex_header;\n    j[kInputPbType] = input_pb_type;\n    j[kOutputPbType] = output_pb_type;\n    j[kFeaturePruningType] = feature_pruning_type;\n\n    LOG(INFO) << j.dump();\n  }\n\n  class Dataset : public DatasetBase {\n   public:\n    explicit Dataset(OpKernelContext *ctx, tstring file_name,\n                     const DsOptions &options, std::string input_pb_type,\n                     std::string output_pb_type, DataType out_type,\n                     int feature_pruning_type,\n                     std::vector<tstring> feature_name_list,\n                     std::vector<int32_t> feature_id_list,\n                     FeatureNameMapper *mapper)\n        : DatasetBase(DatasetContext(ctx)),\n          file_name_(std::move(file_name)),\n          options_(options),\n          input_pb_type_(std::move(input_pb_type)),\n          output_pb_type_(std::move(output_pb_type)),\n          out_type_(out_type),\n          feature_pruning_type_(feature_pruning_type),\n          feature_name_list_(std::move(feature_name_list)),\n          feature_id_list_(std::move(feature_id_list)),\n          mapper_(mapper) {\n      absl::flat_hash_map<std::string, int32_t> name_to_id;\n      absl::flat_hash_map<int32_t, std::vector<std::string>> id_to_name;\n      for (size_t i = 0; i < feature_name_list_.size(); ++i) {\n        name_to_id.insert({feature_name_list_[i], feature_id_list_[i]});\n        id_to_name[feature_id_list_[i]].push_back(feature_name_list_[i]);\n      }\n      CHECK(mapper_->SetMapping(name_to_id, id_to_name));\n      if (input_pb_type == \"examplebatch\" && output_pb_type == \"example\") {\n        //        mapper_->TurnOn();\n      }\n      //      LOG_FIRST_N(INFO, 1) << \"NameToId: \" << mapper_->DebugString();\n    }\n\n    std::unique_ptr<IteratorBase> MakeIteratorInternal(\n        const string &prefix) const override {\n      return absl::make_unique<Iterator>(\n          Iterator::Params{this, strings::StrCat(prefix, \"::\", kDatasetType)},\n          mapper_, input_pb_type_, output_pb_type_);\n    }\n\n    const DataTypeVector &output_dtypes() const override {\n      static auto *dtypes = new DataTypeVector({out_type_});\n      return *dtypes;\n    }\n\n    const std::vector<PartialTensorShape> &output_shapes() const override {\n      static auto *shapes =\n          new std::vector<PartialTensorShape>{TensorShape({})};\n      return *shapes;\n    }\n\n    string DebugString() const override {\n      return (\"This is the customized Instance Dataset: \" + file_name_);\n    }\n\n    Status CheckExternalState() const override { return Status::OK(); }\n\n   private:\n    Status AsGraphDefInternal(SerializationContext *ctx,\n                              DatasetGraphDefBuilder *b,\n                              Node **output) const override {\n      Node *filename = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(file_name_, &filename));\n      Node *use_snappy = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(options_.use_snappy, &use_snappy));\n      Node *has_sort_id = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(options_.has_sort_id, &has_sort_id));\n      Node *kafka_dump = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(options_.kafka_dump, &kafka_dump));\n      Node *kafka_dump_prefix = nullptr;\n      TF_RETURN_IF_ERROR(\n          b->AddScalar(options_.kafka_dump_prefix, &kafka_dump_prefix));\n      Node *buffer_size = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(options_.buffer_size, &buffer_size));\n      Node *lagrangex_header = nullptr;\n      TF_RETURN_IF_ERROR(\n          b->AddScalar(options_.lagrangex_header, &lagrangex_header));\n      Node *input_pb_type = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(input_pb_type_, &input_pb_type));\n      Node *output_pb_type = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(output_pb_type_, &output_pb_type));\n      Node *feature_pruning_type = nullptr;\n      TF_RETURN_IF_ERROR(\n          b->AddScalar(feature_pruning_type_, &feature_pruning_type));\n      Node *feature_name_list = nullptr;\n      TF_RETURN_IF_ERROR(b->AddVector(feature_name_list_, &feature_name_list));\n      Node *feature_id_list = nullptr;\n      TF_RETURN_IF_ERROR(b->AddVector(feature_id_list_, &feature_id_list));\n      AttrValue out_type;\n      b->BuildAttrValue(out_type_, &out_type);\n      AttrValue compression_type;\n      b->BuildAttrValue(options_.compression_type, &compression_type);\n\n      TF_RETURN_IF_ERROR(b->AddDataset(\n          this,\n          {filename, use_snappy, has_sort_id, kafka_dump, kafka_dump_prefix,\n           buffer_size, lagrangex_header, input_pb_type, output_pb_type,\n           feature_pruning_type, feature_name_list, feature_id_list},\n          {{kOutType, out_type}, {kCompressionType, compression_type}},\n          output));\n      return Status::OK();\n    }\n\n    class Iterator : public DatasetIterator<Dataset> {\n     public:\n      explicit Iterator(const Params &params, FeatureNameMapper *mapper,\n                        const tstring &input_pb_type,\n                        const tstring &output_pb_type)\n          : DatasetIterator<Dataset>(params), mapper_(mapper) {\n        mutex_lock l(mu_);\n        offset_ = 0;\n        input_pb_type_ =\n            ::tensorflow::monolith_tf::data_format::StringToDataFormat(\n                input_pb_type);\n        output_pb_type_ =\n            ::tensorflow::monolith_tf::data_format::StringToDataFormat(\n                output_pb_type);\n        if (input_pb_type_ == ::tensorflow::monolith_tf::data_format::UNKNOW ||\n            output_pb_type_ == ::tensorflow::monolith_tf::data_format::UNKNOW) {\n          LOG(FATAL) << \"dataformat error:\" << input_pb_type << \" or \"\n                     << output_pb_type;\n        }\n      }\n\n      class CurPBIteratorHandler {\n       public:\n        struct CurOutput : public PBIteratorWithDataFormatTransBaseOutput {\n          std::vector<Tensor> *out_tensors;\n          size_t size = 0;\n        };\n\n        template <class TResult>\n        Status HandleReaderNextStauts(const Status &s, const TResult &result) {\n          return Status::OK();\n        }\n\n        template <class TResult>\n        Status HandleResult(TResult &&result, CurOutput *output) {\n          output->size = result.ByteSize();\n          output->out_tensors->back().scalar<Variant>()() = std::move(result);\n          return Status::OK();\n        }\n        Status HandleResult(tstring &&serialized, CurOutput *output) {\n          output->out_tensors->back().scalar<tstring>()() =\n              std::move(serialized);\n          return Status::OK();\n        }\n      };\n\n      Status GetNextInternal(IteratorContext *ctx,\n                             std::vector<Tensor> *out_tensors,\n                             bool *end_of_sequence) override {\n        out_tensors->reserve(1);\n        mutex_lock l(mu_);\n        if (!reader_) {\n          TF_RETURN_IF_ERROR(SetupStreamsLocked(ctx->env()));\n        }\n        out_tensors->emplace_back(ctx->allocator({}), dataset()->out_type_,\n                                  TensorShape({}));\n        PBIteratorWithDataFormatTrans<CurPBIteratorHandler> cur_iter(\n            input_pb_type_, output_pb_type_);\n        CurPBIteratorHandler::CurOutput output;\n        output.out_tensors = out_tensors;\n\n        cur_iter.GetNext(reader_.get(), &output, &offset_);\n        Status s = output.reader_status;\n\n        if (s.ok()) {\n          static monitoring::CounterCell *bytes_counter =\n              metrics::GetTFDataBytesReadCounter(kDatasetType);\n          bytes_counter->IncrementBy(output.size);\n          *end_of_sequence = false;\n          num_random_samples_++;\n          offset_ = reader_->GetOffset();\n          if (num_random_samples_ % metric_emit_step_ == 0) {\n            LOG_EVERY_N_SEC(INFO, 300) << absl::StrFormat(\n                \"metrics_emit(counter) [instance_num] emit=%llu, \"\n                \"total_instance_num=%lld\",\n                metric_emit_step_, num_random_samples_);\n            monolith::GetMetrics()->emit_counter(\"instance_num\",\n                                                 metric_emit_step_);\n          }\n          return Status::OK();\n        }\n\n        out_tensors->pop_back();\n        ResetStreamsLocked();\n        if (errors::IsOutOfRange(s)) {\n          *end_of_sequence = true;\n          int64 unsubmit_instance_num = num_random_samples_ % metric_emit_step_;\n          if (unsubmit_instance_num > 0) {\n            LOG(INFO) << absl::StrFormat(\n                \"metrics_emit(counter) [instance_num] emit=%lld, \"\n                \"total_instance_num=%lld, end_of_sequence\",\n                unsubmit_instance_num, num_random_samples_);\n            monolith::GetMetrics()->emit_counter(\"instance_num\",\n                                                 unsubmit_instance_num);\n          }\n          return Status::OK();\n        }\n        return s;\n      }\n\n     private:\n      std::shared_ptr<model::Node> CreateNode(\n          IteratorContext *ctx, model::Node::Args args) const override {\n        return model::MakeSourceNode(std::move(args));\n      }\n\n      Status SaveInternal(SerializationContext *ctx,\n                          IteratorStateWriter *writer) override {\n        mutex_lock l(mu_);\n        LOG(INFO) << \"Save function is not supported yet.\";\n        TF_RETURN_IF_ERROR(writer->WriteScalar(full_name(\"num_random_samples\"),\n                                               num_random_samples_));\n        TF_RETURN_IF_ERROR(writer->WriteScalar(full_name(\"offset\"), offset_));\n        return Status::OK();\n      }\n\n      Status RestoreInternal(IteratorContext *ctx,\n                             IteratorStateReader *reader) override {\n        mutex_lock l(mu_);\n        LOG(INFO) << \"Restore function is not supported yet.\";\n        TF_RETURN_IF_ERROR(reader->ReadScalar(full_name(\"num_random_samples\"),\n                                              &num_random_samples_));\n        int64 offset;\n        TF_RETURN_IF_ERROR(reader->ReadScalar(full_name(\"offset\"), &offset));\n        if (dataset()->file_name_.empty()) {\n          offset_ = 0;\n        } else {\n          offset_ = offset;\n        }\n        return Status::OK();\n      }\n\n      // Sets up reader streams to read from filename\n      Status SetupStreamsLocked(Env *env) TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) {\n        std::unique_ptr<BaseStreamReader> stream_reader;\n        if (dataset()->file_name_.empty()) {\n          stream_reader = std::make_unique<StdinStreamReader>(\n              dataset()->options_, dataset()->options_.buffer_size);\n        } else {\n          std::unique_ptr<RandomAccessFile> f;\n          TF_RETURN_IF_ERROR(\n              env->NewRandomAccessFile(dataset()->file_name_, &f));\n          auto compression_type = FileStreamReader::GetCompressType(\n              dataset()->options_.use_snappy,\n              dataset()->options_.compression_type);\n          stream_reader = std::make_unique<FileStreamReader>(\n              dataset()->options_, std::move(f), compression_type,\n              dataset()->options_.buffer_size);\n        }\n        if (dataset()->input_pb_type_ == \"instance\" ||\n            dataset()->input_pb_type_ == \"example\") {\n          reader_ = absl::make_unique<PBIterator>(\n              std::move(stream_reader), static_cast<FeaturePruningType>(\n                                            dataset()->feature_pruning_type_));\n        } else {\n          reader_ = absl::make_unique<ExampleBatchIterator>(\n              std::move(stream_reader),\n              static_cast<FeaturePruningType>(dataset()->feature_pruning_type_),\n              mapper_);\n        }\n\n        return Status::OK();\n      }\n\n      // Resets all reader streams.\n      void ResetStreamsLocked() TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) {\n        reader_.reset();\n      }\n\n      ::tensorflow::monolith_tf::data_format::DataFormat input_pb_type_;\n      ::tensorflow::monolith_tf::data_format::DataFormat output_pb_type_;\n      mutex mu_;\n      std::unique_ptr<PBIterator> reader_ TF_GUARDED_BY(mu_);\n      int64 num_random_samples_ TF_GUARDED_BY(mu_) = 0;\n      uint64 offset_ TF_GUARDED_BY(mu_) = 0;\n      uint64 metric_emit_step_ TF_GUARDED_BY(mu_) = 10000;\n      FeatureNameMapper *mapper_ = nullptr;\n    };\n\n    tstring file_name_, input_pb_type_, output_pb_type_;\n    DsOptions options_;\n    DataType out_type_;\n    std::vector<tstring> feature_name_list_;\n    std::vector<int32_t> feature_id_list_;\n    int feature_pruning_type_ = FeaturePruningType::PRUNING_RAW_FEATURE;\n    FeatureNameMapper *mapper_ = nullptr;\n  };\n\n  Dataset *output_ = nullptr;\n  DataType out_type_;\n  int32 compression_type_;\n  FeatureNameMapperTfBridge *mapper_ = nullptr;\n};\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"PBDataset\").Device(DEVICE_CPU), PBDatasetOp);\n}  // namespace\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/ragged_feature_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <string>\n#include <vector>\n#include <unordered_map>\n\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing Example = ::monolith::io::proto::Example;\nusing ExampleBatch = ::monolith::io::proto::ExampleBatch;\nusing NamedFeature = ::monolith::io::proto::NamedFeature;\n\nclass SwitchSlotOp : public OpKernel {\n public:\n  using OpKernel::OpKernel;\n  using ConstFlatSplits = typename TTypes<int64>::ConstFlat;\n\n  explicit SwitchSlotOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"slot\", &slot_));\n\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"fid_version\", &fid_version_));\n  }\n\n  void Compute(OpKernelContext *context) override {\n    // Read the `rt_nested_splits` input & convert to Eigen tensors.\n    OpInputList rt_nested_splits_in;\n    OP_REQUIRES_OK(\n        context, context->input_list(\"rt_nested_splits\", &rt_nested_splits_in));\n    const int rt_nested_splits_len = rt_nested_splits_in.size();\n\n    OpOutputList rt_nested_splits_out;\n    OP_REQUIRES_OK(context, context->output_list(\"nested_splits_out\",\n                                                 &rt_nested_splits_out));\n\n    for (int i = 0; i < rt_nested_splits_len; ++i) {\n      Tensor *out_splits;\n      OP_REQUIRES_OK(context,\n                     rt_nested_splits_out.allocate(\n                         i, rt_nested_splits_in[i].shape(), &out_splits));\n      std::memcpy(out_splits->data(), rt_nested_splits_in[i].data(),\n                  sizeof(int64) * rt_nested_splits_in[i].NumElements());\n    }\n\n    const Tensor &rt_dense_values_in = context->input(rt_nested_splits_len);\n    Tensor *dense_values_out;\n    OP_REQUIRES_OK(context, context->allocate_output(\"dense_values_out\",\n                                                     rt_dense_values_in.shape(),\n                                                     &dense_values_out));\n    auto dense_values_int_ = rt_dense_values_in.flat<int64>();\n    auto dense_values_out_ = dense_values_out->flat<int64>();\n\n    for (int i = 0; i < dense_values_int_.size(); ++i) {\n      if (fid_version_ == 1) {\n        dense_values_out_(i) = convert_v1(dense_values_int_(i));\n      } else {\n        dense_values_out_(i) = convert_v2(dense_values_int_(i));\n      }\n    }\n  }\n\n private:\n  inline int64 convert_v1(int64 fid) {\n    static int64 mask = (static_cast<int64>(1) << 55) - 1;\n    return (static_cast<int64>(slot_) << 54) | (fid & mask);\n  }\n\n  inline int64 convert_v2(int64 fid) {\n    static int64 mask = (static_cast<int64>(1) << 49) - 1;\n    return (static_cast<int64>(slot_) << 48) | (fid & mask);\n  }\n\n  int slot_, fid_version_;\n};\n\n\nenum class VariantType { PBExampleBatch, PBExample };\n\n\nclass SwitchSlotBatchOp : public OpKernel {\n public:\n  using OpKernel::OpKernel;\n  explicit SwitchSlotBatchOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    std::vector<std::string> features;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"features\", &features));\n    std::vector<int> slots;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"slots\", &slots));\n    std::vector<bool> inplaces;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"inplaces\", &inplaces));\n    OP_REQUIRES(ctx, features.size() == slots.size(),\n      errors::FailedPrecondition(\"the length of features and slots are not equal\")\n    );\n    OP_REQUIRES(ctx, features.size() == inplaces.size(),\n      errors::FailedPrecondition(\"the length of features and inplaces are not equal\")\n    );\n\n    for (int i = 0; i < features.size(); ++i) {\n      shared_meta_.emplace(std::piecewise_construct,\n                           std::forward_as_tuple(features[i]),\n                           std::forward_as_tuple(inplaces[i], slots[i]));\n    }\n\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"suffix\", &suffix_));\n    std::string variant_type;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"variant_type\", &variant_type));\n    if (variant_type == \"example\") {\n      variant_type_ = VariantType::PBExample;\n    } else if (variant_type == \"example_batch\") {\n      variant_type_ = VariantType::PBExampleBatch;\n    } else {\n      OP_REQUIRES_OK(ctx, errors::FailedPrecondition(\n        \"variant_type error, variant_type must be example or example_batch!\"));\n    }\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    const Tensor *pb_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"pb_input\", &pb_input));\n    Tensor *pb_output;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(\"pb_output\",\n                                              pb_input->shape(),\n                                              &pb_output));\n    google::protobuf::Arena arena;\n    if (variant_type_ == VariantType::PBExampleBatch) {\n      switch_example_batch(pb_input, pb_output, &arena);\n    } else {\n      switch_example(pb_input, pb_output, &arena);\n    }\n  }\n\n private:\n  void switch_example(const Tensor *pb_input, Tensor *pb_output, google::protobuf::Arena *arena) {\n    auto variant_flat = pb_input->flat<Variant>();\n    auto out_variant_flat = pb_output->flat<Variant>();\n    for (int i=0; i < variant_flat.size(); ++i) {\n      const Example *example = variant_flat(i).get<Example>();\n      Example *new_example = switch_slot(*example, arena);\n      out_variant_flat(i) = *new_example;\n    }\n  }\n\n  void switch_example_batch(const Tensor *pb_input, Tensor *pb_output, google::protobuf::Arena *arena) {\n    const Variant &variant = pb_input->scalar<Variant>()();\n    auto out_variant_scalar = pb_output->scalar<Variant>();\n    const ExampleBatch *eb = variant.get<ExampleBatch>();\n    ExampleBatch *new_eb = switch_slot(*eb, arena);\n    out_variant_scalar() = *new_eb;\n  }\n\n  Example *switch_slot(const Example &example, google::protobuf::Arena *arena) {\n    auto *base = google::protobuf::Arena::CreateMessage<Example>(arena);\n    base->CopyFrom(example);\n    for (int i = 0; i < example.named_feature_size(); ++i) {\n      const auto &name = base->named_feature(i).name();\n      auto it = shared_meta_.find(name);\n      if (it != shared_meta_.end()) {\n        bool inplace = it->second.first;\n        uint64_t shared_slot = it->second.second;\n        NamedFeature *named_feature = base->mutable_named_feature(i);\n\n        if (inplace) {\n          auto *feature = named_feature->mutable_feature();\n          if (feature->has_fid_v1_list()) {\n            const auto &size = feature->mutable_fid_v1_list()->value_size();\n            auto *data = feature->mutable_fid_v1_list()->mutable_value()->mutable_data();\n            for (int j = 0; j < size; ++j) {\n              data[j] = switch_slot_v1(data[j], shared_slot);\n            }\n          }\n\n          if (feature->has_fid_v2_list()) {\n            const auto &size = feature->mutable_fid_v2_list()->value_size();\n            auto *data = feature->mutable_fid_v2_list()->mutable_value()->mutable_data();\n            for (int j = 0; j < size; ++j) {\n              data[j] = switch_slot_v2(data[j], shared_slot);\n            }\n          }\n        } else {\n          auto &feature = named_feature->feature();\n          auto *additive_nf = base->add_named_feature();\n          additive_nf->set_id(named_feature->id());\n          additive_nf->set_name(name + \"_\" + suffix_);\n          additive_nf->set_sorted_id(named_feature->sorted_id());\n          if (feature.has_fid_v1_list()) {\n            auto *fid_v1_list = additive_nf->mutable_feature()->mutable_fid_v1_list();\n            for (const auto &value : feature.fid_v1_list().value()) {\n              fid_v1_list->add_value(switch_slot_v1(value, shared_slot));\n            }\n          }\n          if (feature.has_fid_v2_list()) {\n            auto *fid_v2_list = additive_nf->mutable_feature()->mutable_fid_v2_list();\n            for (const auto &value : feature.fid_v2_list().value()) {\n              fid_v2_list->add_value(switch_slot_v2(value, shared_slot));\n            }\n          }\n        }\n      }\n    }\n    return base;\n  }\n\n  ExampleBatch *switch_slot(const ExampleBatch &example_batch, google::protobuf::Arena *arena) {\n    auto *base = google::protobuf::Arena::CreateMessage<ExampleBatch>(arena);\n    base->CopyFrom(example_batch);\n\n    for (int i = 0; i < example_batch.named_feature_list_size(); ++i) {\n      auto *named_feature_list = base->mutable_named_feature_list(i);\n      const auto &name = named_feature_list->name();\n      auto it = shared_meta_.find(name);\n      if (it != shared_meta_.end()) {\n        bool inplace = it->second.first;\n        uint64_t shared_slot = it->second.second;\n        if (inplace) {\n          for (int j = 0; j < named_feature_list->feature_size(); ++j) {\n            auto *feature = named_feature_list->mutable_feature(j);\n            if (feature->has_fid_v1_list()) {\n              auto *values = named_feature_list->mutable_feature(j)->mutable_fid_v1_list()->mutable_value();\n              auto *data = values->mutable_data();\n              for (int k = 0; k < values->size(); ++k) {\n                data[k] = switch_slot_v1(data[k], shared_slot);\n              }\n            }\n            if (feature->has_fid_v2_list()) {\n              auto *values = feature->mutable_fid_v2_list()->mutable_value();\n              auto *data = values->mutable_data();\n              for (int k = 0; k < values->size(); ++k) {\n                data[k] = switch_slot_v2(data[k], shared_slot);\n              }\n            }\n          }\n        } else {\n          auto *additive_nfl = base->add_named_feature_list();\n          additive_nfl->set_id(named_feature_list->id());\n          additive_nfl->set_name(name + \"_\" + suffix_);\n          additive_nfl->set_type(named_feature_list->type());\n          for (int j = 0; j < named_feature_list->feature_size(); ++j) {\n            auto *feature = additive_nfl->add_feature();\n            if (named_feature_list->feature(j).has_fid_v1_list()) {\n              auto *fid_v1_list = feature->mutable_fid_v1_list();\n              for (const auto &value : named_feature_list->feature(j).fid_v1_list().value()) {\n                fid_v1_list->add_value(switch_slot_v1(value, shared_slot));\n              }\n            }\n\n            if (named_feature_list->feature(j).has_fid_v2_list()) {\n              auto *fid_v2_list = feature->mutable_fid_v2_list();\n              for (const auto &value : named_feature_list->feature(j).fid_v2_list().value()) {\n                fid_v2_list->add_value(switch_slot_v2(value, shared_slot));\n              }\n            }\n          }\n        }\n      }\n    }\n\n    return base;\n  }\n\n  VariantType variant_type_;\n  std::string suffix_;\n  std::unordered_map<std::string, std::pair<bool, uint64_t>> shared_meta_;\n};\n\nclass FeatureCombineOp : public OpKernel {\n public:\n  using OpKernel::OpKernel;\n  using ConstFlatSplits = typename TTypes<int64>::ConstFlat;\n\n  explicit FeatureCombineOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"slot\", &slot_));\n\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"fid_version\", &fid_version_));\n  }\n\n  void Compute(OpKernelContext *context) override {\n    // Read the `rt_nested_splits` input & convert to Eigen tensors.\n    OpInputList rt_nested_splits_src1_in;\n    OP_REQUIRES_OK(context, context->input_list(\"rt_nested_splits_src1\",\n                                                &rt_nested_splits_src1_in));\n    int input_cnt = rt_nested_splits_src1_in.size();\n    const Tensor &rt_dense_values_src1_in = context->input(input_cnt);\n    input_cnt++;\n    OpInputList rt_nested_splits_src2_in;\n    OP_REQUIRES_OK(context, context->input_list(\"rt_nested_splits_src2\",\n                                                &rt_nested_splits_src2_in));\n    input_cnt += rt_nested_splits_src2_in.size();\n    const Tensor &rt_dense_values_src2_in = context->input(input_cnt);\n\n    DCHECK_EQ(rt_nested_splits_src1_in.size(), rt_nested_splits_src2_in.size());\n\n    OpOutputList nested_splits_sink;\n    OP_REQUIRES_OK(context, context->output_list(\"nested_splits_sink\",\n                                                 &nested_splits_sink));\n\n    int src_idx = 0;\n    if (rt_nested_splits_src1_in.size() == 2) {\n      auto batch_splits_src1 = rt_nested_splits_src1_in[src_idx].flat<int64>();\n      auto batch_splits_src2 = rt_nested_splits_src2_in[src_idx].flat<int64>();\n      DCHECK_EQ(batch_splits_src1.size(), batch_splits_src2.size());\n\n      for (int i = 0; i < batch_splits_src1.size(); ++i) {\n        DCHECK_EQ(batch_splits_src1(i), batch_splits_src2(i));\n      }\n      src_idx++;\n    }\n\n    auto ins_splits_src1 = rt_nested_splits_src1_in[src_idx].flat<int64>();\n    auto rt_dense_values_src1 = rt_dense_values_src1_in.flat<int64>();\n    auto ins_splits_src2 = rt_nested_splits_src2_in[src_idx].flat<int64>();\n    auto rt_dense_values_src2 = rt_dense_values_src2_in.flat<int64>();\n\n    int batch_size = ins_splits_src1.size() - 1;\n\n    Tensor *ins_splits_sink;\n    OP_REQUIRES_OK(context,\n                   nested_splits_sink.allocate(\n                       src_idx, rt_nested_splits_src2_in[src_idx].shape(),\n                       &ins_splits_sink));\n    auto ins_splits = ins_splits_sink->flat<int64>();\n    ins_splits(0) = 0;\n    for (int i = 0; i < batch_size; ++i) {\n      int src1_start = ins_splits_src1(i);\n      int src1_end = ins_splits_src1(i + 1);\n      int src2_start = ins_splits_src2(i);\n      int src2_end = ins_splits_src2(i + 1);\n      ins_splits(i + 1) =\n          ins_splits(i) + (src1_end - src1_start) * (src2_end - src2_start);\n    }\n\n    Tensor *dense_values_sink;\n    OP_REQUIRES_OK(context, context->allocate_output(\"dense_values_sink\",\n                                                     {ins_splits(batch_size)},\n                                                     &dense_values_sink));\n    auto dense_values = dense_values_sink->flat<int64>();\n\n    int idx = 0;\n    for (int i = 0; i < batch_size; ++i) {\n      int src1_start = ins_splits_src1(i);\n      int src1_end = ins_splits_src1(i + 1);\n      int src2_start = ins_splits_src2(i);\n      int src2_end = ins_splits_src2(i + 1);\n\n      for (int j = src1_start; j < src1_end; ++j) {\n        int64 fid1 = rt_dense_values_src1(j);\n        for (int k = src2_start; k < src2_end; ++k) {\n          int64 fid2 = rt_dense_values_src2(k);\n          if (fid_version_ == 1) {\n            dense_values(idx++) = convert_v1(combine(fid1, fid2));\n          } else {\n            dense_values(idx++) = convert_v2(combine(fid1, fid2));\n          }\n        }\n      }\n    }\n  }\n\n private:\n  inline int64 convert_v1(int64 fid) {\n    static int64 mask = (static_cast<int64>(1) << 55) - 1;\n    return (static_cast<int64>(slot_) << 54) | (fid & mask);\n  }\n\n  inline int64 convert_v2(int64 fid) {\n    static int64 mask = (static_cast<int64>(1) << 49) - 1;\n    return (static_cast<int64>(slot_) << 48) | (fid & mask);\n  }\n\n  int64 combine(int64 fid1, int64 fid2) {\n    auto mu = absl::int128(fid1) * absl::int128(fid2);\n\n    uint64 hi = static_cast<uint64>((mu >> 64).operator long());  // NOLINT\n    uint64 lo = static_cast<uint64>(mu.operator long());          // NOLINT\n\n    return static_cast<int64>(hi ^ lo);\n  }\n\n  int slot_, fid_version_;\n};\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"FeatureCombine\").Device(DEVICE_CPU),\n                        FeatureCombineOp);\n\nREGISTER_KERNEL_BUILDER(Name(\"SwitchSlot\").Device(DEVICE_CPU), SwitchSlotOp);\nREGISTER_KERNEL_BUILDER(Name(\"SwitchSlotBatch\").Device(DEVICE_CPU), SwitchSlotBatchOp);\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/scatter_label_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <string>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing LineId = ::idl::matrix::proto::LineId;\n\nclass ScatterLabelOp : public OpKernel {\n public:\n  explicit ScatterLabelOp(OpKernelConstruction *context) : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"config\", &config_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n\n    if (variant_type_ != \"instance\" && variant_type_ != \"example\") {\n      LOG(FATAL) << \"Invalid 'variant_type', please choose on from \"\n                    \"['instance', 'example']!\";\n    }\n\n    std::vector<absl::string_view> splits = absl::StrSplit(config_, \",\");\n    CHECK_GT(splits.size(), 0);\n    int max_label_index_ = 0;\n    for (absl::string_view split : splits) {\n      std::vector<absl::string_view> chnid_and_index =\n          absl::StrSplit(split, \":\");\n      CHECK_EQ(chnid_and_index.size(), 2);\n      int64_t chnid = 0;\n      int index = 0;\n      CHECK(absl::SimpleAtoi(chnid_and_index[0], &chnid));\n      CHECK(absl::SimpleAtoi(chnid_and_index[1], &index));\n      chnid_to_label_index_[chnid] = index;\n      if (max_label_index_ < index) {\n        max_label_index_ = index;\n      }\n    }\n    multi_task_num_ = max_label_index_ + 1;\n\n    nlohmann::json j;\n    j[\"chnid_to_label_index\"] = chnid_to_label_index_;\n    j[\"multi_task_num\"] = multi_task_num_;\n    LOG(INFO) << j.dump();\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n\n    bool is_instance = variant_type_ == \"instance\";\n    if (is_instance) {\n      Instance instance;\n      instance.CopyFrom(*input_tensor.scalar<Variant>()().get<Instance>());\n      output_tensor->scalar<Variant>()() = std::move(instance);\n    } else {\n      Example example;\n      example.CopyFrom(*input_tensor.scalar<Variant>()().get<Example>());\n      output_tensor->scalar<Variant>()() = std::move(example);\n    }\n\n    LineId *line_id = GetLineId(output_tensor, is_instance);\n    auto label = GetLabel(output_tensor, is_instance);\n    float label_value = internal::INVALID_LABEL;\n    if (!label->empty()) {\n      label_value = label->Get(0);\n    } else {\n      LOG_EVERY_N_SEC(ERROR, 60)\n          << \"Invalid data: label is empty, please investigate and retry!\";\n    }\n    label->Clear();\n    label->Resize(multi_task_num_, internal::INVALID_LABEL);\n\n    int64_t chnid = line_id->chnid();\n    if (chnid_to_label_index_.count(chnid)) {\n      int idx = chnid_to_label_index_[chnid];\n      label->Set(idx, label_value);\n    }\n  }\n\n private:\n  static LineId *GetLineId(Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_line_id();\n    } else {\n      return output_tensor->scalar<Variant>()()\n          .get<Example>()\n          ->mutable_line_id();\n    }\n  }\n\n  static ::google::protobuf::RepeatedField<float> *GetLabel(\n      Tensor *output_tensor, bool is_instance) {\n    if (is_instance) {\n      return output_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_label();\n    } else {\n      return output_tensor->scalar<Variant>()().get<Example>()->mutable_label();\n    }\n  }\n\n  std::string config_;\n  std::string variant_type_;\n  int multi_task_num_;\n  std::map<int64_t, int> chnid_to_label_index_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"ScatterLabel\").Device(DEVICE_CPU), ScatterLabelOp)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/split_flow_dataset_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <bitset>\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"monolith/native_training/data/kernels/df_resource_kernel.h\"\n#include \"monolith/native_training/data/kernels/internal/datasource_utils.h\"\n#include \"tensorflow/core/framework/dataset.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing Item = ::tensorflow::monolith_tf::Item;\nusing QueueResource = ::tensorflow::monolith_tf::QueueResource;\nusing VariantType = ::tensorflow::monolith_tf::VariantType;\n\nstatic mutex input_mu_;\n\nclass SplitFlowDatasetOp : public UnaryDatasetOpKernel {\n public:\n  static constexpr const char *const kDatasetType = \"dataflow_dataset\";\n  static constexpr const char *const kDataFlow = \"data_flow\";\n  static constexpr const char *const kIndex = \"index\";\n  static constexpr const char *const kMaxQueueSize = \"max_queue_size\";\n  static constexpr const char *const kVariantType = \"variant_type\";\n\n  explicit SplitFlowDatasetOp(OpKernelConstruction *ctx);\n\n protected:\n  void MakeDataset(OpKernelContext *ctx, DatasetBase *input,\n                   DatasetBase **output) override;\n\n private:\n  class Dataset;\n  std::vector<std::string> data_flows_;\n  int index_;\n  int max_queue_size_;\n  VariantType variant_type_;\n};\n\nclass SplitFlowDatasetOp::Dataset : public DatasetBase {\n public:\n  Dataset(OpKernelContext *ctx, const DatasetBase *input,\n          const std::vector<std::string> &data_flows, int index,\n          int max_queue_size, const VariantType &variant_type)\n      : DatasetBase(DatasetContext(ctx)),\n        input_(input),\n        data_flows_(data_flows),\n        index_(index),\n        max_queue_size_(max_queue_size),\n        variant_type_(variant_type) {\n    input_->Ref();\n  }\n\n  ~Dataset() override { input_->Unref(); }\n\n  std::unique_ptr<IteratorBase> MakeIteratorInternal(\n      const string &prefix) const override {\n    return absl::make_unique<Iterator>(\n        Iterator::Params{this, strings::StrCat(prefix, \"::\", kDatasetType)});\n  }\n\n  const DataTypeVector &output_dtypes() const override {\n    return input_->output_dtypes();\n  }\n\n  const std::vector<PartialTensorShape> &output_shapes() const override {\n    return input_->output_shapes();\n  }\n\n  string DebugString() const override {\n    return \"This is the customized Dataset: SplitFlowDataset\";\n  }\n\n  Status InputDatasets(\n      std::vector<const DatasetBase *> *inputs) const override {\n    inputs->push_back(input_);\n    return Status::OK();\n  }\n\n  Status CheckExternalState() const override {\n    return input_->CheckExternalState();\n  }\n\n  void SetContainer(const std::string container) { container_ = container; }\n\n  std::string GetContainer() const { return container_; }\n\n protected:\n  Status AsGraphDefInternal(SerializationContext *ctx,\n                            DatasetGraphDefBuilder *b,\n                            Node **output) const override {\n    Node *input_graph_node;\n    TF_RETURN_IF_ERROR(b->AddInputDataset(ctx, input_, &input_graph_node));\n\n    AttrValue data_flows_node;\n    b->BuildAttrValue(data_flows_, &data_flows_node);\n    AttrValue index_node;\n    b->BuildAttrValue(index_, &index_node);\n    AttrValue max_queue_size_node;\n    b->BuildAttrValue(max_queue_size_, &max_queue_size_node);\n\n    AttrValue variant_type_node;\n    if (variant_type_ == VariantType::PBInstance) {\n      b->BuildAttrValue(\"instance\", &variant_type_node);\n    } else {\n      b->BuildAttrValue(\"example\", &variant_type_node);\n    }\n\n    TF_RETURN_IF_ERROR(\n        b->AddDataset(this,                // dataset\n                      {input_graph_node},  // inputs\n                      {{kDataFlow, data_flows_node},\n                       {kIndex, index_node},\n                       {kMaxQueueSize, max_queue_size_node},\n                       {kVariantType, variant_type_node}},  // attrs\n                      output));                             // Node**\n\n    return Status::OK();\n  }\n\n private:\n  class Iterator : public DatasetIterator<Dataset> {\n   public:\n    explicit Iterator(const Params &params)\n        : DatasetIterator<Dataset>(params),\n          mu_(std::make_shared<mutex>()),\n          output_mu_(std::make_shared<mutex>()) {}\n\n    ~Iterator() override {\n      CancelThreads();\n      if (deregister_fn_) deregister_fn_();\n    }\n\n    void CancelThreads() TF_LOCKS_EXCLUDED(mu_) {\n      cancellation_manager_->StartCancel();\n      mutex_lock l(*mu_);\n      cancelled_ = true;\n    }\n\n    Status Initialize(IteratorContext *ctx) override {\n      mutex_lock l(*mu_);\n      name_ = dataset()->data_flows_[dataset()->index_];\n      cancellation_manager_ = absl::make_unique<CancellationManager>();\n      TF_RETURN_IF_ERROR(\n          ::tensorflow::monolith_tf::RegisterCancellationCallback(\n              ctx->cancellation_manager(), [this]() { CancelThreads(); },\n              &deregister_fn_));\n\n      IteratorContext::Params params(ctx);\n      params.cancellation_manager = cancellation_manager_.get();\n      Status s = dataset()->input_->MakeIterator(IteratorContext(params), this,\n                                                 prefix(), &input_impl_);\n\n      std::function<Status(QueueResource **)> creator =\n          [this](QueueResource **queue) -> Status {\n        *queue = new QueueResource(dataset()->max_queue_size_);\n        return Status::OK();\n      };\n\n      {\n        mutex_lock input_l(input_mu_);\n        for (size_t i = 0; i < dataset()->data_flows_.size(); ++i) {\n          // 1) get data_flow_name and hash it into uint32\n          std::string data_flows_name = dataset()->data_flows_[i];\n          uint32 df_code = static_cast<uint32>(::tensorflow::monolith_tf::internal::java_hash_code(data_flows_name));\n          df_code = df_code << 8;\n\n          // 2) get resource\n          QueueResource *resource = nullptr;\n          s.Update(ctx->resource_mgr()->LookupOrCreate(\n              dataset()->GetContainer(), data_flows_name, &resource, creator));\n          df_to_queue_.emplace(df_code, resource);\n          if (i == dataset()->index_) {\n            data_flow_ = df_code;\n            queue_ = resource;\n          }\n        }\n      }\n\n      return s;\n    }\n\n    Status GetNextInternal(IteratorContext *ctx,\n                           std::vector<Tensor> *out_tensors,\n                           bool *end_of_sequence) override {\n      // std::thread::id this_id = std::this_thread::get_id();\n      {\n        mutex_lock l(*mu_);\n        if (dataset()->index_ == 0) {\n          TF_RETURN_IF_ERROR(EnsureThreadStarted(ctx));\n        }\n      }\n\n      {\n        mutex_lock output_l(*output_mu_);\n        out_tensors->reserve(1);\n\n        Item item;\n        bool poped = false;\n        while (!poped) {\n          // the queue is empty and the fetch threas is cancelled or finished\n          if (cancelled_ || prefetch_thread_finished_) {\n            out_tensors->clear();\n            *end_of_sequence = true;\n            return Status::OK();\n          }\n          poped = queue_->TryPop(item, 100);\n        }\n\n        if (!poped || item.end_of_sequence) {\n          out_tensors->clear();\n          *end_of_sequence = true;\n        } else {\n          for (const auto &tensor : item.out_tensors) {\n            out_tensors->push_back(tensor);\n          }\n          *end_of_sequence = item.end_of_sequence;\n        }\n      }\n\n      return Status::OK();\n    }\n\n   protected:\n    std::shared_ptr<model::Node> CreateNode(\n        IteratorContext *ctx, model::Node::Args args) const override {\n      return model::MakeUnknownRatioNode(std::move(args));\n    }\n\n    Status SaveInternal(SerializationContext *ctx,\n                        IteratorStateWriter *writer) override {\n      return Status::OK();\n    }\n\n    Status RestoreInternal(IteratorContext *ctx,\n                           IteratorStateReader *reader) override {\n      return Status::OK();\n    }\n\n   private:\n    const std::shared_ptr<mutex> mu_;\n    const std::shared_ptr<mutex> output_mu_;\n    std::function<void()> deregister_fn_;\n    std::unique_ptr<CancellationManager> cancellation_manager_;\n    bool cancelled_ TF_GUARDED_BY(*mu_) = false;\n    bool prefetch_thread_started_ TF_GUARDED_BY(*mu_) = false;\n    bool prefetch_thread_finished_ TF_GUARDED_BY(*mu_) = false;\n\n    uint32 data_flow_;\n    std::string name_;\n    QueueResource *queue_;\n    std::unique_ptr<IteratorBase> input_impl_;\n    std::unique_ptr<Thread> prefetch_thread_;\n    std::unordered_map<uint32, QueueResource *> df_to_queue_;\n\n    Status EnsureThreadStarted(IteratorContext *ctx)\n        TF_EXCLUSIVE_LOCKS_REQUIRED(*mu_) {\n      if (!prefetch_thread_started_) {\n        prefetch_thread_started_ = true;\n        std::string name = dataset()->data_flows_[dataset()->index_];\n        std::shared_ptr<IteratorContext> new_ctx =\n            std::make_shared<IteratorContext>(*ctx);\n        prefetch_thread_ = ctx->StartThread(\n            name, [new_ctx, name, this]() { PrefetchThread(new_ctx, name); });\n      }\n\n      return Status::OK();\n    }\n\n    void PrefetchThread(const std::shared_ptr<IteratorContext> &ctx,\n                        std::string name) {\n      while (true) {\n        {\n          mutex_lock l(*mu_);\n          if (cancelled_) {\n            prefetch_thread_finished_ = true;\n            break;\n          }\n        }\n\n        if (!prefetch_thread_finished_) {\n          Item item;\n          input_impl_->GetNext(ctx.get(), &item.out_tensors,\n                               &item.end_of_sequence);\n\n          if (item.end_of_sequence) {\n            mutex_lock l(*mu_);\n            item.end_of_sequence = true;\n            if (!cancelled_ && !prefetch_thread_finished_) {\n              for (auto kv : df_to_queue_) {\n                kv.second->Push(item);\n              }\n            }\n            break;\n          } else {\n            uint32 code;\n            if (dataset()->variant_type_ == VariantType::PBInstance) {\n              code = item.out_tensors[0]\n                         .scalar<Variant>()()\n                         .get<Instance>()\n                         ->data_source_key();\n            } else {\n              code = item.out_tensors[0]\n                         .scalar<Variant>()()\n                         .get<Example>()\n                         ->data_source_key();\n            }\n\n            bool pushed = false;\n            do {\n              if (cancelled_ || prefetch_thread_finished_) {\n                break;\n              }\n              pushed = df_to_queue_[code]->TryPush(item);\n            } while (!pushed);\n          }\n        } else {\n          break;\n        }\n      }\n    }\n  };\n\n  const DatasetBase *const input_;\n  std::vector<std::string> data_flows_;\n  int index_;\n  int max_queue_size_;\n  VariantType variant_type_;\n  std::string container_;\n};\n\nSplitFlowDatasetOp::SplitFlowDatasetOp(OpKernelConstruction *ctx)\n    : UnaryDatasetOpKernel(ctx) {\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kDataFlow, &data_flows_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kIndex, &index_));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kMaxQueueSize, &max_queue_size_));\n\n  std::string variant_type;\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kVariantType, &variant_type));\n  if (variant_type == \"instance\") {\n    variant_type_ = VariantType::PBInstance;\n  } else if (variant_type == \"example\") {\n    variant_type_ = VariantType::PBExample;\n  } else {\n    LOG(ERROR) << \"invalid variant_type: \" << variant_type;\n    ctx->SetStatus(Status(tensorflow::error::Code::INVALID_ARGUMENT,\n                          \"invalid variant_type\"));\n  }\n}\n\nvoid SplitFlowDatasetOp::MakeDataset(OpKernelContext *ctx, DatasetBase *input,\n                                     DatasetBase **output) {\n  *output = new Dataset(ctx, input, data_flows_, index_, max_queue_size_,\n                        variant_type_);\n\n  std::string container;\n  // OP_REQUIRES_OK(ctx, GetNodeAttr(def(), \"container\", &container));\n  static_cast<Dataset *>(*output)->SetContainer(\"\");\n}\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"SplitFlowDataset\").Device(DEVICE_CPU),\n                        SplitFlowDatasetOp);\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/string_to_variant.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n#include \"monolith/native_training/data/kernels/internal/datasource_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/data_reader.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/lib/core/coding.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nusing Example = ::monolith::io::proto::Example;\nusing ExampleBatch = ::monolith::io::proto::ExampleBatch;\nusing Instance = ::parser::proto::Instance;\n\nclass ReadHelper {\n public:\n  explicit ReadHelper(DataFormatOptions options, bool has_header)\n      : options_(options), has_header_(has_header) {}\n\n  Status GetData(absl::string_view in, uint8_t* pb_type,\n                 uint32_t* data_source_key, absl::string_view* out) {\n    if (has_header_) {\n      ZeroCopyStringViewStreamReader r(options_, in);\n      TF_RETURN_IF_ERROR(r.ReadPBBytes(pb_type, data_source_key, out));\n      return Status::OK();\n    }\n    *pb_type = 0;\n    *data_source_key = 0;\n    *out = in;\n    return Status::OK();\n  }\n\n private:\n  DataFormatOptions options_;\n  bool has_header_;\n};\n\nclass StringToVariantOp : public OpKernel {\n public:\n  using OpKernel::OpKernel;\n  using ConstFlatSplits = typename TTypes<int64>::ConstFlat;\n\n  explicit StringToVariantOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"input_type\", &variant_type_));\n\n    std::unordered_set<std::string> variant_type_set_ = {\n        \"instance\", \"example\", \"examplebatch\", \"example_batch\"};\n    OP_REQUIRES(\n        ctx, variant_type_set_.count(variant_type_) != 0,\n        errors::InvalidArgument(\"variant_type can only be instance, example \"\n                                \"and examplebatch/example_batch\"));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"has_header\", &has_header_));\n    if (has_header_) {\n      OP_REQUIRES_OK(ctx, ctx->GetAttr(\"has_sort_id\", &options_.has_sort_id));\n      OP_REQUIRES_OK(\n          ctx, ctx->GetAttr(\"lagrangex_header\", &options_.lagrangex_header));\n      OP_REQUIRES_OK(\n          ctx, ctx->GetAttr(\"kafka_dump_prefix\", &options_.kafka_dump_prefix));\n      OP_REQUIRES_OK(ctx, ctx->GetAttr(\"kafka_dump\", &options_.kafka_dump));\n    }\n\n    std::vector<int64> chnid_list;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"chnids\", &chnid_list));\n    std::vector<std::string> datasource_list;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"datasources\", &datasource_list));\n    CHECK_EQ(chnid_list.size(), datasource_list.size());\n\n    if (!chnid_list.empty()) {\n      int i = 0;\n      for (const std::string& sv : datasource_list) {\n        uint32 code = internal::java_hash_code(sv);\n        code = code << 8;\n        chnid_to_code_.emplace(chnid_list.at(i), code);\n        LOG(INFO) << \"chnid: \" << chnid_list.at(i) << \", code: \" << code;\n        i++;\n      }\n    }\n\n    std::string default_datasource;\n    OP_REQUIRES_OK(ctx,\n                   ctx->GetAttr(\"default_datasource\", &default_datasource));\n    uint32 default_code = internal::java_hash_code(default_datasource);\n    default_code_ = (default_code << 8);\n    LOG(INFO) << \"default_code: \" << default_code_;\n  }\n\n  void Compute(OpKernelContext* context) override {\n    // Grab the input tensor\n    const Tensor& input_tensor = context->input(0);\n    auto input = input_tensor.flat<tstring>();\n\n    // Create an output tensor\n    Tensor* output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n    auto output_flat = output_tensor->flat<Variant>();\n\n    uint8_t pb_type;\n    uint32_t data_source_key;\n    ReadHelper reader(options_, has_header_);\n    for (size_t i = 0; i < input.size(); ++i) {\n      const tstring& buf = input(i);\n\n      absl::string_view res;\n      OP_REQUIRES_OK(context,\n                     reader.GetData(buf, &pb_type, &data_source_key, &res));\n      if (variant_type_ == \"instance\") {\n        Instance pb;\n        if (res.size() > 0) {\n          CHECK(pb.ParseFromArray(res.data(), res.size()));\n          UpdateDatasourceKey(pb.line_id().chnid(), &data_source_key);\n          pb.set_data_source_key(data_source_key);\n        }\n        output_flat(i) = std::move(pb);\n      } else if (variant_type_ == \"example\") {\n        Example pb;\n        if (res.size() > 0) {\n          CHECK(pb.ParseFromArray(res.data(), res.size()));\n          UpdateDatasourceKey(pb.line_id().chnid(), &data_source_key);\n          pb.set_data_source_key(data_source_key);\n        }\n        output_flat(i) = std::move(pb);\n      } else {\n        ExampleBatch pb;\n        if (res.size() > 0) {\n          CHECK(pb.ParseFromArray(res.data(), res.size()));\n          pb.set_data_source_key(data_source_key);\n        }\n        output_flat(i) = std::move(pb);\n      }\n    }\n  }\n\n private:\n  std::string variant_type_;\n  bool has_header_ = false;\n  DataFormatOptions options_;\n  std::unordered_map<int64, uint32> chnid_to_code_;\n  uint32 default_code_;\n\n  void UpdateDatasourceKey(const int64& chnid, uint32_t* data_source_key) {\n    if (has_header_ && options_.lagrangex_header) {\n      return;\n    } else if (!chnid_to_code_.empty()) {\n      if (chnid_to_code_.count(chnid) != 0) {\n        *data_source_key = chnid_to_code_[chnid];\n      } else {\n        *data_source_key = default_code_;\n      }\n    } else {\n      *data_source_key = default_code_;\n    }\n  }\n};\n\nclass StringToVariantWithTransform : public OpKernel {\n public:\n  explicit StringToVariantWithTransform(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    std::string variant_type;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"input_type\", &variant_type));\n    input_type_ = data_format::StringToDataFormat(variant_type);\n    LOG(INFO) << \"input_type_:\" << variant_type << \",\" << input_type_;\n    std::string output_type;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"output_type\", &output_type));\n    output_type_ = data_format::StringToDataFormat(output_type);\n    LOG(INFO) << \"output_type_:\" << output_type << \",\" << output_type_;\n\n    OP_REQUIRES(\n        ctx,\n        (input_type_ == data_format::INSTANCE ||\n         input_type_ == data_format::EXAMPLE ||\n         input_type_ == data_format::EXAMPLEBATCH) &&\n            (output_type_ == data_format::INSTANCE ||\n             output_type_ == data_format::EXAMPLE ||\n             output_type_ == data_format::EXAMPLEBATCH),\n        errors::InvalidArgument(\"variant_type can only be instance, example \"\n                                \"and examplebatch/example_batch\"));\n    OP_REQUIRES(ctx,\n                !(input_type_ != data_format::EXAMPLEBATCH &&\n                  output_type_ == data_format::EXAMPLEBATCH),\n                errors::InvalidArgument(\n                    \"not support output examplebatch input not examplebatch\"));\n\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"has_header\", &has_header_));\n    if (has_header_) {\n      OP_REQUIRES_OK(ctx, ctx->GetAttr(\"has_sort_id\", &options_.has_sort_id));\n      OP_REQUIRES_OK(\n          ctx, ctx->GetAttr(\"lagrangex_header\", &options_.lagrangex_header));\n      OP_REQUIRES_OK(\n          ctx, ctx->GetAttr(\"kafka_dump_prefix\", &options_.kafka_dump_prefix));\n      OP_REQUIRES_OK(ctx, ctx->GetAttr(\"kafka_dump\", &options_.kafka_dump));\n    }\n\n    std::vector<int64> chnid_list;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"chnids\", &chnid_list));\n    std::vector<std::string> datasource_list;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"datasources\", &datasource_list));\n    CHECK_EQ(chnid_list.size(), datasource_list.size());\n\n    if (!chnid_list.empty()) {\n      int i = 0;\n      for (const std::string& sv : datasource_list) {\n        uint32 code = internal::java_hash_code(sv);\n        code = code << 8;\n        chnid_to_code_.emplace(chnid_list.at(i), code);\n        LOG(INFO) << \"chnid: \" << chnid_list.at(i) << \", code: \" << code;\n        i++;\n      }\n    }\n\n    std::string default_datasource;\n    OP_REQUIRES_OK(ctx,\n                   ctx->GetAttr(\"default_datasource\", &default_datasource));\n    uint32 default_code = internal::java_hash_code(default_datasource);\n    default_code_ = (default_code << 8);\n    LOG(INFO) << \"default_code: \" << default_code_;\n  }\n\n  void Compute(OpKernelContext* context) override {\n    // Grab the input tensor\n    const Tensor& input_tensor = context->input(0);\n    auto input = input_tensor.flat<tstring>();\n\n    uint8_t pb_type;\n    uint32_t data_source_key;\n    ReadHelper reader(options_, has_header_);\n    Status s;\n\n    std::unique_ptr<google::protobuf::Arena> arena =\n        std::make_unique<google::protobuf::Arena>();\n\n    if (input_type_ == data_format::EXAMPLEBATCH &&\n        output_type_ != data_format::EXAMPLEBATCH) {\n      std::vector<ExampleBatch*> eb_list;\n      eb_list.reserve(input.size());\n      int total_size = 0;\n      for (size_t i = 0; i < input.size(); ++i) {\n        const tstring& buf = input(i);\n        absl::string_view res;\n        OP_REQUIRES_OK(context,\n                       reader.GetData(buf, &pb_type, &data_source_key, &res));\n        auto pb =\n            google::protobuf::Arena::CreateMessage<ExampleBatch>(arena.get());\n        if (res.size() > 0) {\n          CHECK(pb->ParseFromArray(res.data(), res.size()));\n          pb->set_data_source_key(data_source_key);\n        }\n        eb_list.push_back(pb);\n        total_size += pb->batch_size();\n      }\n      // Create an output tensor\n      Tensor* output_tensor = nullptr;\n      OP_REQUIRES_OK(context,\n                     context->allocate_output(0, {total_size}, &output_tensor));\n      auto output_flat = output_tensor->flat<Variant>();\n\n      LOG_EVERY_N_SEC(INFO, 60)\n          << \"trans pb size:\" << input.size() << \"->\" << total_size;\n      total_size = -1;\n\n      for (auto pb : eb_list) {\n        for (int index = 0; index < pb->batch_size(); ++index) {\n          if (input_type_ == data_format::INSTANCE) {\n            Instance inst;\n            s = ExampleBatchToInstance(pb, index, &inst);\n            output_flat(++total_size) = std::move(inst);\n          } else {\n            Example ep;\n            s = ExampleBatchToExample(pb, index, &ep,\n                                      FeaturePruningType::PRUNING_RAW_FEATURE,\n                                      &fake_mapper_);\n            output_flat(++total_size) = std::move(ep);\n          }\n          if (s != Status::OK()) {\n            LOG(WARNING) << \"Trans error:\" << s;\n          }\n        }\n      }\n    } else {\n      // Create an output tensor\n      Tensor* output_tensor = nullptr;\n      OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                       &output_tensor));\n      auto output_flat = output_tensor->flat<Variant>();\n\n      for (size_t i = 0; i < input.size(); ++i) {\n        const tstring& buf = input(i);\n\n        absl::string_view res;\n        OP_REQUIRES_OK(context,\n                       reader.GetData(buf, &pb_type, &data_source_key, &res));\n        // LOG(ERROR) << \"xxx \" << buf.size() << \",\" << res.size();\n        if (input_type_ == data_format::INSTANCE) {\n          Instance pb;\n          if (res.size() > 0) {\n            CHECK(pb.ParseFromArray(res.data(), res.size()));\n            UpdateDatasourceKey(pb.line_id().chnid(), &data_source_key);\n            pb.set_data_source_key(data_source_key);\n          }\n          if (output_type_ == data_format::INSTANCE) {\n            output_flat(i) = std::move(pb);\n          } else {\n            Example eb_pb;\n            s = InstanceToExample(&pb, &eb_pb);\n            if (s != Status::OK()) {\n              LOG(WARNING) << \"Trans error:\" << s;\n            }\n            output_flat(i) = std::move(eb_pb);\n          }\n        } else if (input_type_ == data_format::EXAMPLE) {\n          Example pb;\n          if (res.size() > 0) {\n            CHECK(pb.ParseFromArray(res.data(), res.size()));\n            UpdateDatasourceKey(pb.line_id().chnid(), &data_source_key);\n            pb.set_data_source_key(data_source_key);\n          }\n          if (output_type_ == data_format::EXAMPLE) {\n            output_flat(i) = std::move(pb);\n          } else {\n            Instance inst;\n            s = ExampleToInstance(&pb, &inst);\n            if (s != Status::OK()) {\n              LOG(WARNING) << \"Trans error:\" << s;\n            }\n            output_flat(i) = std::move(inst);\n          }\n        } else {\n          auto pb =\n              google::protobuf::Arena::CreateMessage<ExampleBatch>(arena.get());\n          if (res.size() > 0) {\n            CHECK(pb->ParseFromArray(res.data(), res.size()));\n            pb->set_data_source_key(data_source_key);\n          }\n          output_flat(i) = std::move(*pb);\n        }\n      }\n    }\n  }\n\n private:\n  data_format::DataFormat input_type_, output_type_;\n  bool has_header_ = false;\n  DataFormatOptions options_;\n  std::unordered_map<int64, uint32> chnid_to_code_;\n  uint32 default_code_;\n  FeatureNameMapper fake_mapper_;\n\n  void UpdateDatasourceKey(const int64& chnid, uint32_t* data_source_key) {\n    if (has_header_ && options_.lagrangex_header) {\n      return;\n    } else if (!chnid_to_code_.empty()) {\n      if (chnid_to_code_.count(chnid) != 0) {\n        *data_source_key = chnid_to_code_[chnid];\n      } else {\n        *data_source_key = default_code_;\n      }\n    } else {\n      *data_source_key = default_code_;\n    }\n  }\n};\n\nclass VariantToZerosOp : public OpKernel {\n public:\n  explicit VariantToZerosOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* context) override {\n    // Grab the input tensor\n    const Tensor& input_tensor = context->input(0);\n\n    // Create an output tensor\n    Tensor* output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n    auto output_flat = output_tensor->flat<int64>();\n    output_flat.setZero();\n  }\n};\n\nclass HasVariantOp : public OpKernel {\n public:\n  explicit HasVariantOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"variant_type\", &variant_type_));\n\n    std::unordered_set<std::string> variant_type_set_ = {\n        \"instance\", \"example\", \"examplebatch\", \"example_batch\"};\n    OP_REQUIRES(\n        ctx, variant_type_set_.count(variant_type_) != 0,\n        errors::InvalidArgument(\"variant_type can only be instance, example \"\n                                \"and examplebatch/example_batch\"));\n  }\n\n  void Compute(OpKernelContext* context) override {\n    // Grab the input tensor\n    const Tensor& input_tensor = context->input(0);\n    // Create an output tensor\n    Tensor* output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n    auto output_scalar = output_tensor->scalar<bool>();\n\n    int byte_size = 0;\n    if (variant_type_ == \"instance\") {\n      const auto* instance = input_tensor.scalar<Variant>()().get<Instance>();\n      byte_size = instance->ByteSize();\n    } else if (variant_type_ == \"example\") {\n      const auto* example = input_tensor.scalar<Variant>()().get<Example>();\n      byte_size = example->ByteSize();\n    } else {\n      const auto* example_batch =\n          input_tensor.scalar<Variant>()().get<ExampleBatch>();\n      byte_size = example_batch->ByteSize();\n    }\n\n    output_scalar() = byte_size > 0;\n  }\n\n private:\n  std::string variant_type_;\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"StringToVariant\").Device(DEVICE_CPU),\n                        StringToVariantOp);\nREGISTER_KERNEL_BUILDER(Name(\"StringToVariantWithTransform\").Device(DEVICE_CPU),\n                        StringToVariantWithTransform);\n\nREGISTER_KERNEL_BUILDER(Name(\"VariantToZeros\").Device(DEVICE_CPU),\n                        VariantToZerosOp);\nREGISTER_KERNEL_BUILDER(Name(\"HasVariant\").Device(DEVICE_CPU), HasVariantOp);\n}  // namespace\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/tf_example_to_example_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstdio>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/data/data_op_config.pb.h\"\n#include \"monolith/native_training/data/training_instance/cc/fid.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/example/example.pb.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing ::monolith::io::proto::Example;\nusing ::monolith::io::proto::Feature;\nusing ::monolith::io::proto::NamedFeature;\nusing ::monolith::native_training::data::config::TFRecordFeatureDescription;\n\nclass TFExampleToExampleOp : public OpKernel {\n public:\n  explicit TFExampleToExampleOp(OpKernelConstruction* context)\n      : OpKernel(context) {\n    std::string serialized;\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"feature_description\", &serialized));\n    OP_REQUIRES(context, feature_description_.ParseFromString(serialized),\n                errors::InvalidArgument(\"Corrupted data!\"));\n    LOG(INFO) << feature_description_.DebugString();\n    const auto& s = feature_description_.sparse_features();\n    const auto& d = feature_description_.dense_features();\n    absl::flat_hash_set<int32_t> slot_ids, duplicates;\n    for (const auto& kv : s) {\n      sparse_features_.insert(kv.first);\n      auto ret = slot_ids.insert(kv.second);\n      if (!ret.second) {\n        duplicates.insert(kv.second);\n      }\n    }\n    dense_features_.insert(d.begin(), d.end());\n    std::set<string> intersection;\n    std::set_intersection(sparse_features_.begin(), sparse_features_.end(),\n                          dense_features_.begin(), dense_features_.end(),\n                          std::inserter(intersection, intersection.begin()));\n    OP_REQUIRES(context, intersection.empty(),\n                errors::InvalidArgument(absl::StrFormat(\n                    \"%s occur in sparse_features and dense_features \"\n                    \"simultaneously, please investigate and retry!\",\n                    absl::StrJoin(intersection, \",\"))));\n    const auto& label = feature_description_.label();\n    const auto& instance_weight = feature_description_.instance_weight();\n    if (!label.empty()) {\n      OP_REQUIRES(context, !sparse_features_.contains(label),\n                  errors::InvalidArgument(absl::StrFormat(\n                      \"label: {%s} should NOT occur in sparse_features, \"\n                      \"please investigate and retry!\",\n                      label)));\n      OP_REQUIRES(context, !dense_features_.contains(label),\n                  errors::InvalidArgument(absl::StrFormat(\n                      \"label: {%s} should NOT occur in dense_features, \"\n                      \"please investigate and retry!\",\n                      label)));\n    }\n    if (!instance_weight.empty()) {\n      OP_REQUIRES(\n          context, !sparse_features_.contains(instance_weight),\n          errors::InvalidArgument(absl::StrFormat(\n              \"instance_weight: {%s} should NOT occur in sparse_features, \"\n              \"please investigate and retry!\",\n              instance_weight)));\n      OP_REQUIRES(\n          context, !dense_features_.contains(instance_weight),\n          errors::InvalidArgument(absl::StrFormat(\n              \"instance_weight: {%s} should NOT occur in dense_features, \"\n              \"please investigate and retry!\",\n              instance_weight)));\n    }\n\n    OP_REQUIRES(context, duplicates.empty(),\n                errors::InvalidArgument(\n                    absl::StrFormat(\"{%s} have multiple sparse feature name \"\n                                    \"mapping, please investigate and retry!\",\n                                    absl::StrJoin(duplicates, \",\"))));\n  }\n\n  void Compute(OpKernelContext* context) override {\n    const Tensor& input_tensor = context->input(0);\n    const auto& serialized = input_tensor.scalar<tstring>()();\n    google::protobuf::Arena arena;\n    auto* tf_example =\n        google::protobuf::Arena::CreateMessage<tensorflow::Example>(&arena);\n    OP_REQUIRES(context, tf_example->ParseFromString(serialized),\n                errors::DataLoss(\"Corrupted data!\"));\n    Tensor* output_tensor = nullptr;\n    OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),\n                                                     &output_tensor));\n\n    auto* example = google::protobuf::Arena::CreateMessage<Example>(&arena);\n    const auto& feature_map = tf_example->features().feature();\n    const auto& label_name = feature_description_.label();\n    if (!label_name.empty() && !feature_map.contains(label_name)) {\n      LOG(ERROR) << \"label_name: \" << label_name\n                 << \" doest not exist in tf.example.features.feature()!\";\n    }\n\n    example->set_instance_weight(1.f);\n    const auto& m = feature_description_.sparse_features();\n    for (const auto& kv : feature_map) {\n      const std::string& name = kv.first;\n      const tensorflow::Feature& f = kv.second;\n\n      // label\n      if (name == feature_description_.label()) {\n        example->mutable_label()->CopyFrom(f.float_list().value());\n        continue;\n      }\n\n      // instance_weight\n      if (name == feature_description_.instance_weight()) {\n        if (!f.has_float_list()) {\n          LOG(ERROR) << absl::StrFormat(\n              \"instance_weight: %s does not have float list!\", name);\n        } else if (f.float_list().value_size() != 1) {\n          LOG(ERROR) << absl::StrFormat(\n              \"instance_weight: %s value_size should be 1\", name);\n        } else {\n          example->set_instance_weight(f.float_list().value(0));\n        }\n        continue;\n      }\n\n      // sparse & dense\n      if (!sparse_features_.contains(name) && !dense_features_.contains(name)) {\n        continue;\n      }\n\n      NamedFeature* named_feature = example->add_named_feature();\n      named_feature->set_name(name);\n      // TODO(zhangbiao.david): set_sorted_id()?\n      // named_feature->set_sorted_id();\n\n      Feature* feature = named_feature->mutable_feature();\n      if (sparse_features_.contains(name)) {\n        int32_t slot_id = m.at(name);\n        named_feature->set_id(slot_id);\n\n        std::vector<FIDV2> fids;\n        if (f.has_int64_list()) {\n          fids.reserve(f.int64_list().value_size());\n          for (int64_t value : f.int64_list().value()) {\n            fids.push_back(FIDV2(slot_id, value));\n          }\n        } else if (f.has_float_list()) {\n          fids.reserve(f.float_list().value_size());\n          for (float value : f.float_list().value()) {\n            int64_t hash_value = CalcHashValue(value);\n            fids.push_back(FIDV2(slot_id, hash_value));\n          }\n        } else {\n          LOG(ERROR) << \"Only supports int64/float32 sparse features!\";\n        }\n\n        for (FIDV2 fid : fids) {\n          feature->mutable_fid_v2_list()->mutable_value()->Add(fid);\n        }\n      } else if (dense_features_.contains(name)) {\n        if (f.has_int64_list()) {\n          feature->mutable_int64_list()->mutable_value()->CopyFrom(\n              f.int64_list().value());\n        } else if (f.has_float_list()) {\n          feature->mutable_float_list()->mutable_value()->CopyFrom(\n              f.float_list().value());\n        } else if (f.has_bytes_list()) {\n          feature->mutable_bytes_list()->mutable_value()->CopyFrom(\n              f.bytes_list().value());\n        }\n      }\n    }\n\n    output_tensor->scalar<Variant>()() = std::move(*example);\n  }\n\n private:\n  int64_t CalcHashValue(float value) const {\n    return static_cast<int64_t>(std::log2(std::abs(value) + 1));\n  }\n\n  TFRecordFeatureDescription feature_description_;\n  absl::flat_hash_set<std::string> sparse_features_;\n  absl::flat_hash_set<std::string> dense_features_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithTFExampleToExample\").Device(DEVICE_CPU),\n                        TFExampleToExampleOp)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/transform_dataset_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/kernels/transform_dataset_kernel.h\"\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_def_builder.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/partial_tensor_shape.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/stats_aggregator.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/lib/gtl/cleanup.h\"\n#include \"tensorflow/core/lib/io/buffered_inputstream.h\"\n#include \"tensorflow/core/lib/io/inputbuffer.h\"\n#include \"tensorflow/core/lib/io/zlib_compression_options.h\"\n#include \"tensorflow/core/lib/random/random.h\"\n#include \"tensorflow/core/lib/strings/str_util.h\"\n\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/data/transform/cc/transforms.h\"\n#include \"monolith/native_training/runtime/common/linalg_utils.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing EFeature = ::monolith::io::proto::Feature;\nusing LineId = ::idl::matrix::proto::LineId;\nusing Action = google::protobuf::RepeatedField<int>;\nusing ::monolith::common::IsAlmostEqual;\nusing monolith::native_training::data::TransformConfig;\nusing tensorflow::monolith_tf::NewTransformFromConfig;\nusing tensorflow::monolith_tf::TransformInterface;\n\n// See documentation in ../../ops/dataset_ops.cc for a high-level\n// description of the following op.\n\n/* static */ constexpr const char *const TransformDatasetOp::kDatasetType;\n/* static */ constexpr const char *const TransformDatasetOp::kInputDataset;\n/* static */ constexpr const char *const TransformDatasetOp::kConfig;\n/* static */ constexpr const char *const TransformDatasetOp::kVariantType;\n\nclass TransformDatasetOp::Dataset : public DatasetBase {\n public:\n  Dataset(OpKernelContext *ctx, const DatasetBase *input,\n          std::string config_serialized, std::string variant_type)\n      : DatasetBase(DatasetContext(ctx)),\n        input_(input),\n        config_serialized_(std::move(config_serialized)),\n        variant_type_(std::move(variant_type)) {\n    input_->Ref();\n    OP_REQUIRES(ctx, config_.ParseFromString(config_serialized_),\n                errors::InvalidArgument(\"Unable to parse config. Make sure it \"\n                                        \"is serialized version of \"\n                                        \"TransformConfig.\"));\n    transform_ = NewTransformFromConfig(config_);\n  }\n\n  ~Dataset() override { input_->Unref(); }\n\n  std::unique_ptr<IteratorBase> MakeIteratorInternal(\n      const string &prefix) const override {\n    return absl::make_unique<Iterator>(\n        Iterator::Params{this, strings::StrCat(prefix, \"::\", kDatasetType)});\n  }\n\n  const DataTypeVector &output_dtypes() const override {\n    return input_->output_dtypes();\n  }\n\n  const std::vector<PartialTensorShape> &output_shapes() const override {\n    return input_->output_shapes();\n  }\n\n  string DebugString() const override {\n    return \"This is the customized Dataset: Mixup\";\n  }\n\n  Status InputDatasets(\n      std::vector<const DatasetBase *> *inputs) const override {\n    inputs->push_back(input_);\n    return Status::OK();\n  }\n\n  Status CheckExternalState() const override {\n    return input_->CheckExternalState();\n  }\n\n protected:\n  Status AsGraphDefInternal(SerializationContext *ctx,\n                            DatasetGraphDefBuilder *b,\n                            Node **output) const override {\n    Node *input_graph_node;\n    TF_RETURN_IF_ERROR(b->AddInputDataset(ctx, input_, &input_graph_node));\n\n    AttrValue config_node;\n    b->BuildAttrValue(config_serialized_, &config_node);\n    AttrValue variant_type_node;\n    b->BuildAttrValue(variant_type_, &variant_type_node);\n\n    TF_RETURN_IF_ERROR(b->AddDataset(\n        this, {input_graph_node},\n        {{kConfig, config_node}, {kVariantType, variant_type_node}}, output));\n\n    return Status::OK();\n  }\n\n private:\n  class Iterator : public DatasetIterator<Dataset> {\n   public:\n    explicit Iterator(const Params &params)\n        : DatasetIterator<Dataset>(params) {}\n\n    Status Initialize(IteratorContext *ctx) override {\n      return dataset()->input_->MakeIterator(ctx, this, prefix(), &input_impl_);\n    }\n\n    Status GetNextInternal(IteratorContext *ctx,\n                           std::vector<Tensor> *out_tensors,\n                           bool *end_of_sequence) override {\n      out_tensors->clear();\n      out_tensors->reserve(1);\n      tensorflow::mutex_lock l(mu_);\n\n      Status status;\n      if (dataset()->variant_type_ == \"instance\") {\n        status = NextInternalImpl<Instance>(ctx, out_tensors, end_of_sequence);\n      } else {\n        status = NextInternalImpl<Example>(ctx, out_tensors, end_of_sequence);\n      }\n\n      return status;\n    }\n\n    template <typename T>\n    Status NextInternalImpl(IteratorContext *ctx,\n                            std::vector<Tensor> *out_tensors,\n                            bool *end_of_sequence) {\n      while (!*end_of_sequence) {\n        std::vector<Tensor> batch_variant;\n        TF_RETURN_IF_ERROR(\n            input_impl_->GetNext(ctx, &batch_variant, end_of_sequence));\n\n        if (!*end_of_sequence) {\n          T *instance_or_example = GetCurrent<T>(&batch_variant.back());\n          std::shared_ptr<T> instance_or_example_ptr;\n          instance_or_example_ptr.reset(instance_or_example, [](...) {});\n          std::vector<std::shared_ptr<T>> instance_or_example_list;\n          dataset()->transform_->Transform(instance_or_example_ptr,\n                                           &instance_or_example_list);\n          if (!instance_or_example_list.empty()) {\n            CHECK_EQ(instance_or_example_list.size(), 1);\n            out_tensors->push_back(batch_variant.back());\n            return Status::OK();\n          }\n        }\n      }\n\n      return Status::OK();\n    }\n\n   protected:\n    std::shared_ptr<model::Node> CreateNode(\n        IteratorContext *ctx, model::Node::Args args) const override {\n      return model::MakeUnknownRatioNode(std::move(args));\n    }\n\n    Status SaveInternal(SerializationContext *ctx,\n                        IteratorStateWriter *writer) override {\n      return Status::OK();\n    }\n\n    Status RestoreInternal(IteratorContext *ctx,\n                           IteratorStateReader *reader) override {\n      return Status::OK();\n    }\n\n   private:\n    template <typename T>\n    inline T *GetCurrent(Tensor *t) {\n      Variant *variant = &t->scalar<Variant>()();\n      return variant->get<T>();\n    }\n\n    tensorflow::mutex mu_;\n    std::unique_ptr<IteratorBase> input_impl_ TF_GUARDED_BY(mu_);\n  };\n\n  const DatasetBase *const input_;\n  std::string config_serialized_;\n  TransformConfig config_;\n  std::string variant_type_;\n  std::unique_ptr<TransformInterface> transform_;\n};\n\nTransformDatasetOp::TransformDatasetOp(OpKernelConstruction *ctx)\n    : UnaryDatasetOpKernel(ctx) {\n  std::string config_serialized;\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kConfig, &config_serialized));\n  OP_REQUIRES(ctx, config_.ParseFromString(config_serialized),\n              errors::InvalidArgument(\"Unable to parse config. Make sure it \"\n                                      \"is serialized version of \"\n                                      \"TransformConfig.\"));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(kVariantType, &variant_type_));\n  LOG(INFO) << \"variant_type: \" << variant_type_ << \", config: \\n\"\n            << config_.DebugString();\n}\n\nvoid TransformDatasetOp::MakeDataset(OpKernelContext *ctx, DatasetBase *input,\n                                     DatasetBase **output) {\n  *output = new Dataset(ctx, input, config_.SerializeAsString(), variant_type_);\n}\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"TransformDataset\").Device(DEVICE_CPU),\n                        TransformDatasetOp)\n}  // namespace\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/kernels/transform_dataset_kernel.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_TRANSFORM_DATASET_KERNEL_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_TRANSFORM_DATASET_KERNEL_H_\n\n#include \"tensorflow/core/framework/dataset.h\"\n#include \"monolith/native_training/data/transform/transform_config.pb.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\n\nclass TransformDatasetOp : public UnaryDatasetOpKernel {\n public:\n  static constexpr const char* const kDatasetType = \"transform\";\n  static constexpr const char* const kInputDataset = \"input_dataset\";\n  static constexpr const char* const kConfig = \"config\";\n  static constexpr const char* const kVariantType = \"variant_type\";\n\n  explicit TransformDatasetOp(OpKernelConstruction* ctx);\n\n protected:\n  void MakeDataset(OpKernelContext* ctx, DatasetBase* input,\n                   DatasetBase** output) override;\n\n private:\n  class Dataset;\n\n  std::string variant_type_;\n  monolith::native_training::data::TransformConfig config_;\n};\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_KERNELS_TRANSFORM_DATASET_KERNEL_H_\n"
  },
  {
    "path": "monolith/native_training/data/kernels/variant_filter_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <climits>\n#include <cstdio>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/data/kernels/feature_name_mapper_tf_bridge.h\"\n#include \"monolith/native_training/data/kernels/internal/relational_utils.h\"\n#include \"monolith/native_training/data/kernels/internal/value_filter_by_feature.h\"\n#include \"monolith/native_training/data/kernels/internal/value_filter_by_line_id.h\"\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/variant.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing Example = ::monolith::io::proto::Example;\nusing LineId = ::idl::matrix::proto::LineId;\nusing tensorflow::monolith_tf::internal::LineIdValueFilter;\nusing tensorflow::monolith_tf::internal::FeatureValueFilter;\n\nclass SetFilterOp : public OpKernel {\n public:\n  explicit SetFilterOp(OpKernelConstruction *context) : OpKernel(context) {\n    std::vector<int64> filter_fids;\n    OP_REQUIRES_OK(context, context->GetAttr(\"filter_fids\", &filter_fids));\n    filter_fids_.insert(filter_fids.begin(), filter_fids.end());\n\n    std::vector<int64> has_fids;\n    OP_REQUIRES_OK(context, context->GetAttr(\"has_fids\", &has_fids));\n    has_fids_.insert(has_fids.begin(), has_fids.end());\n\n    std::vector<int64> select_fids;\n    OP_REQUIRES_OK(context, context->GetAttr(\"select_fids\", &select_fids));\n    select_fids_.insert(select_fids.begin(), select_fids.end());\n\n    std::vector<int64> has_actions;\n    OP_REQUIRES_OK(context, context->GetAttr(\"has_actions\", &has_actions));\n    has_actions_.insert(has_actions.begin(), has_actions.end());\n\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n\n    OP_REQUIRES_OK(context, context->GetAttr(\"req_time_min\", &req_time_min_));\n\n    std::vector<int32_t> select_slots;\n    OP_REQUIRES_OK(context, context->GetAttr(\"select_slots\", &select_slots));\n    for (int32_t slot : select_slots) {\n      CHECK_GE(slot, 0);\n    }\n    select_slots_.insert(select_slots.begin(), select_slots.end());\n\n    auto creator = [this](FeatureNameMapperTfBridge **out_mapper) {\n      TF_RETURN_IF_ERROR(FeatureNameMapperTfBridge::New(out_mapper));\n      return Status::OK();\n    };\n    ResourceMgr *resource_mgr = context->resource_manager();\n    OP_REQUIRES_OK(context,\n                   resource_mgr->LookupOrCreate<FeatureNameMapperTfBridge>(\n                       resource_mgr->default_container(),\n                       FeatureNameMapperTfBridge::kName, &mapper_, creator));\n    if (variant_type_ == \"example\") {\n      std::vector<std::pair<int, int>> valid_ids;\n      for (uint32_t slot : select_slots_) {\n        valid_ids.emplace_back(slot, slot);\n      }\n      for (uint64_t fid : filter_fids_) {\n        valid_ids.emplace_back(slot_id_v1(fid), slot_id_v2(fid));\n      }\n      for (uint64_t fid : has_fids_) {\n        valid_ids.emplace_back(slot_id_v1(fid), slot_id_v2(fid));\n      }\n      for (uint64_t fid : select_fids_) {\n        valid_ids.emplace_back(slot_id_v1(fid), slot_id_v2(fid));\n      }\n\n      OP_REQUIRES_OK(context, mapper_->RegisterValidIds(valid_ids));\n    }\n  }\n\n  ~SetFilterOp() override { mapper_->Unref(); }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(\n        context,\n        context->allocate_output(0, input_tensor.shape(), &output_tensor));\n    auto output = output_tensor->scalar<bool>();\n    output() = IsInstanceOfInterest(input_tensor);\n  }\n\n private:\n  bool IsInstanceOfInterest(const Tensor &input_tensor) {\n    auto input = input_tensor.scalar<Variant>();\n    if (variant_type_ == \"instance\") {\n      const auto *instance = input().get<Instance>();\n      return monolith_tf::IsInstanceOfInterest(\n          *instance, filter_fids_, has_fids_, select_fids_, has_actions_,\n          req_time_min_, select_slots_);\n    } else {\n      const auto *example = input().get<Example>();\n      return monolith_tf::IsInstanceOfInterest(\n          *example, filter_fids_, has_fids_, select_fids_, has_actions_,\n          req_time_min_, select_slots_);\n    }\n  }\n\n  std::set<uint64_t> filter_fids_;\n  std::set<uint64_t> has_fids_;\n  std::set<uint64_t> select_fids_;\n  std::set<int32_t> has_actions_;\n  std::string variant_type_ = \"instance\";\n  int req_time_min_;\n  std::set<uint32_t> select_slots_;\n  FeatureNameMapperTfBridge *mapper_;\n};\n\nclass FeatureValueFilterOp : public OpKernel {\n public:\n  explicit FeatureValueFilterOp(OpKernelConstruction *context)\n      : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"field_name\", &field_name_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"op\", &op_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"float_operand\", &float_operand_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"int_operand\", &int_operand_));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"string_operand\", &string_operand_));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"operand_filepath\", &operand_filepath_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"keep_empty\", &keep_empty_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"field_type\", &field_type_));\n    OP_REQUIRES(context,\n                field_type_ == \"int64\" || field_type_ == \"float\" ||\n                    field_type_ == \"double\" || field_type_ == \"bytes\",\n                errors::Unknown(\n                    \"field_type unknown! need to be int64/float/double/bytes\"));\n    feature_value_filter_ = std::make_unique<FeatureValueFilter>(\n        field_name_, field_type_, op_, float_operand_, int_operand_,\n        string_operand_, operand_filepath_, keep_empty_);\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    const Variant &variant = input_tensor.scalar<Variant>()();\n    OP_REQUIRES(context, variant.TypeId() == TypeIndex::Make<Example>(),\n                errors::InvalidArgument(\"input must be Example proto\"));\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(\n        context,\n        context->allocate_output(0, input_tensor.shape(), &output_tensor));\n    auto output = output_tensor->scalar<bool>();\n    // only support Example input\n    output() = feature_value_filter_->IsInstanceOfInterest(\n        context->env(), *(input_tensor.scalar<Variant>()().get<Example>()));\n  }\n\n private:\n  std::string field_name_;\n  std::string op_;  // gt, ge, eq, lt, le, neq, between\n  bool keep_empty_ = false;\n  std::string operand_filepath_;\n\n  std::vector<float> float_operand_;\n  std::vector<int64> int_operand_;\n  std::vector<std::string> string_operand_;\n\n  std::unique_ptr<FeatureValueFilter> feature_value_filter_;\n  std::string field_type_;\n};\n\nclass ValueFilterOp : public OpKernel {\n public:\n  explicit ValueFilterOp(OpKernelConstruction *context) : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"field_name\", &field_name_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"op\", &op_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"float_operand\", &float_operand_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"int_operand\", &int_operand_));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"string_operand\", &string_operand_));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"operand_filepath\", &operand_filepath_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"keep_empty\", &keep_empty_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n    line_id_value_filter_ = std::make_unique<LineIdValueFilter>(\n        field_name_, op_, float_operand_, int_operand_, string_operand_,\n        operand_filepath_, keep_empty_);\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(\n        context,\n        context->allocate_output(0, input_tensor.shape(), &output_tensor));\n    auto output = output_tensor->scalar<bool>();\n    const LineId &line_id = GetLineId(input_tensor);\n    output() =\n        line_id_value_filter_->IsInstanceOfInterest(context->env(), line_id);\n  }\n\n private:\n  const LineId &GetLineId(const Tensor &input_tensor) {\n    if (variant_type_ == \"instance\") {\n      return input_tensor.scalar<Variant>()().get<Instance>()->line_id();\n    } else {\n      return input_tensor.scalar<Variant>()().get<Example>()->line_id();\n    }\n  }\n\n  std::string field_name_;\n  std::string op_;  // gt, ge, eq, lt, le, neq, between\n  bool keep_empty_ = false;\n  std::string operand_filepath_;\n\n  std::vector<float> float_operand_;\n  std::vector<int64> int_operand_;\n  std::vector<std::string> string_operand_;\n\n  std::unique_ptr<LineIdValueFilter> line_id_value_filter_;\n\n  std::string variant_type_;\n};\n\nclass SpecialStrategyOp : public OpKernel {\n public:\n  explicit SpecialStrategyOp(OpKernelConstruction *context)\n      : OpKernel(context) {\n    std::vector<int> special_strategy;\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"special_strategies\", &special_strategy));\n    std::vector<float> sample_rate;\n    OP_REQUIRES_OK(context, context->GetAttr(\"sample_rates\", &sample_rate));\n    std::vector<float> label;\n    OP_REQUIRES_OK(context, context->GetAttr(\"labels\", &label));\n\n    OP_REQUIRES(\n        context, special_strategy.size() == sample_rate.size(),\n        errors::InvalidArgument(\n            \"length of sample_rates must identity with special_strategies\"));\n    OP_REQUIRES(\n        context, special_strategy.size() == label.size() || label.size() == 0,\n        errors::InvalidArgument(\n            \"length of labels must identity with special_strategies or zero\"));\n\n    for (size_t i = 0; i < special_strategy.size(); ++i) {\n      strategy_to_rate_.emplace(special_strategy[i], sample_rate[i]);\n    }\n\n    if (label.size() > 0) {\n      for (size_t i = 0; i < special_strategy.size(); ++i) {\n        strategy_to_label_.emplace(special_strategy[i], label[i]);\n      }\n    }\n\n    OP_REQUIRES_OK(context, context->GetAttr(\"strategy_list\", &strategy_list_));\n    OP_REQUIRES(context, strategy_list_.size() > 0,\n                errors::InvalidArgument(\"strategy_list cannot be empty\"));\n\n    OP_REQUIRES_OK(\n        context,\n        context->GetAttr(\"keep_empty_strategy\", &keep_empty_strategy_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n  }\n\n  void Compute(OpKernelContext *context) override {\n    Tensor *input_tensor = const_cast<Tensor *>(&(context->input(0)));\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(\n        context,\n        context->allocate_output(0, input_tensor->shape(), &output_tensor));\n    auto output = output_tensor->scalar<bool>();\n    output() = DoCompute(input_tensor);\n  }\n\n private:\n  const LineId &GetLineId(Tensor *input_tensor) {\n    if (variant_type_ == \"instance\") {\n      return input_tensor->scalar<Variant>()().get<Instance>()->line_id();\n    } else {\n      return input_tensor->scalar<Variant>()().get<Example>()->line_id();\n    }\n  }\n\n  float *GetLabel(Tensor *input_tensor, int index = 0) {\n    if (variant_type_ == \"instance\") {\n      return input_tensor->scalar<Variant>()()\n          .get<Instance>()\n          ->mutable_label()\n          ->Mutable(index);\n    } else {\n      return input_tensor->scalar<Variant>()()\n          .get<Example>()\n          ->mutable_label()\n          ->Mutable(index);\n    }\n  }\n\n  bool DoCompute(Tensor *input_tensor) {\n    const LineId &line_id = GetLineId(input_tensor);\n    const auto &strategies = line_id.special_strategies();\n\n    if (strategies.size() > 1) {\n      LOG(INFO) << \"Size of special_strategies is bigger than one, pls. check!\";\n    }\n\n    if (strategies.size() == 0) {\n      // for unknow samples, drop\n      if (keep_empty_strategy_) {\n        // for special_strategies_neg_ins_keep_normal\n        return true;\n      } else {\n        return false;\n      }\n    } else {\n      for (auto &special_strategy : strategy_list_) {\n        auto found =\n            std::find(strategies.begin(), strategies.end(), special_strategy);\n        if (found != strategies.end()) {\n          auto rit = strategy_to_rate_.find(special_strategy);\n          if (rit != strategy_to_rate_.end()) {\n            float rate = rit->second;\n            bool flag = false;\n            if (rate == 1.0) {\n              flag = true;\n            } else {\n              if (random_neg_sample_(generator_) <= rate) {\n                flag = true;\n              }\n            }\n\n            if (strategy_to_label_.size() > 0 && flag) {\n              auto lit = strategy_to_label_.find(special_strategy);\n              if (lit != strategy_to_label_.end()) {\n                float new_label = lit->second;\n                float *old_label = GetLabel(input_tensor);\n                *old_label = new_label;\n              }\n            }\n\n            return flag;\n          } else {\n            return true;\n          }\n        }\n      }\n    }\n  }\n\n  std::default_random_engine generator_;\n  std::uniform_real_distribution<float> random_neg_sample_;\n\n  bool keep_empty_strategy_ = true;\n  std::unordered_map<int, float> strategy_to_rate_;\n  std::unordered_map<int, float> strategy_to_label_;\n  std::vector<int> strategy_list_;\n  std::string variant_type_;\n};\n\nclass NegativeSampleOp : public OpKernel {\n public:\n  explicit NegativeSampleOp(OpKernelConstruction *context) : OpKernel(context) {\n    std::vector<int> priorities;\n    std::vector<int> actions;\n    std::vector<float> per_action_drop_rate;\n    OP_REQUIRES_OK(context, context->GetAttr(\"drop_rate\", &drop_rate_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"label_index\", &label_index_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"threshold\", &threshold_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"priorities\", &priorities));\n    OP_REQUIRES_OK(context, context->GetAttr(\"actions\", &actions));\n    OP_REQUIRES_OK(context, context->GetAttr(\"per_action_drop_rate\",\n                                             &per_action_drop_rate));\n    OP_REQUIRES_OK(context, context->GetAttr(\"variant_type\", &variant_type_));\n\n    OP_REQUIRES(context, actions.size() == per_action_drop_rate.size(),\n                errors::Unknown(\"internal error\"));\n\n    for (size_t i = 0; i < actions.size(); i++) {\n      action_drop_rate_map_.emplace(actions[i], per_action_drop_rate[i]);\n    }\n    for (size_t i = 0; i < priorities.size(); i++) {\n      action_priorities_map_.emplace(priorities[i], i);\n    }\n    if (actions.size() > 0) {\n      enable_drop_by_action_ = true;\n    }\n  }\n\n  void Compute(OpKernelContext *context) override {\n    const Tensor &input_tensor = context->input(0);\n    Tensor *output_tensor = nullptr;\n    OP_REQUIRES_OK(\n        context,\n        context->allocate_output(0, input_tensor.shape(), &output_tensor));\n    auto output = output_tensor->scalar<bool>();\n    float label = GetLabel(input_tensor);\n\n    if (label < threshold_) {\n      float sample_drop_rate = drop_rate_;\n      if (enable_drop_by_action_) {\n        sample_drop_rate = GetNegDropRate(input_tensor);\n      }\n      thread_local std::mt19937 gen((std::random_device())());\n      float random = gen() % 1000 / 1000.0;\n      output() = random < sample_drop_rate ? false : true;\n    } else {\n      output() = true;\n    }\n  }\n\n private:\n  float drop_rate_ = 0.0;\n  int label_index_ = 0;\n  float threshold_ = 0.0;\n  bool enable_drop_by_action_ = false;\n  std::unordered_map<int, int> action_priorities_map_;\n  std::unordered_map<int, float> action_drop_rate_map_;\n  std::string variant_type_ = \"instance\";\n\n  float GetLabel(const Tensor &input_tensor) {\n    auto input = input_tensor.scalar<Variant>();\n    if (variant_type_ == \"instance\") {\n      const Instance *instance = input().get<Instance>();\n      return instance->label(label_index_);\n    } else {\n      const Example *example = input().get<Example>();\n      return example->label(label_index_);\n    }\n\n    return 0;\n  }\n\n  const LineId *GetLineId(const Tensor &input_tensor) {\n    auto input = input_tensor.scalar<Variant>();\n    if (variant_type_ == \"instance\") {\n      const Instance *instance = input().get<Instance>();\n      return &instance->line_id();\n    } else {\n      const Example *example = input().get<Example>();\n      return &example->line_id();\n    }\n  }\n\n  int FindMostPriorAction(const Tensor &input_tensor) {\n    const LineId *line_id = GetLineId(input_tensor);\n    CHECK(line_id != nullptr);\n    int most_prior = INT_MAX;\n    int record_action = -1;\n    for (int action : line_id->actions()) {\n      auto it = action_priorities_map_.find(action);\n      if (it != action_priorities_map_.end() && it->second < most_prior) {\n        most_prior = it->second;\n        record_action = action;\n      }\n    }\n    return record_action;\n  }\n\n  float GetNegDropRate(const Tensor &input_tensor) {\n    int prior_action = FindMostPriorAction(input_tensor);\n    if (prior_action > 0) {\n      auto it = action_drop_rate_map_.find(prior_action);\n      if (it != action_drop_rate_map_.end()) {\n        return it->second;\n      }\n    }\n    return drop_rate_;\n  }\n};\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"SetFilter\").Device(DEVICE_CPU), SetFilterOp);\n\nREGISTER_KERNEL_BUILDER(Name(\"FeatureValueFilter\").Device(DEVICE_CPU),\n                        FeatureValueFilterOp);\n\nREGISTER_KERNEL_BUILDER(Name(\"ValueFilter\").Device(DEVICE_CPU), ValueFilterOp);\n\nREGISTER_KERNEL_BUILDER(Name(\"SpecialStrategy\").Device(DEVICE_CPU),\n                        SpecialStrategyOp);\n\nREGISTER_KERNEL_BUILDER(Name(\"NegativeSample\").Device(DEVICE_CPU),\n                        NegativeSampleOp);\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/multi_flow_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport os\nimport getpass\nimport random\nimport numpy as np\nimport tensorflow as tf\nfrom struct import pack, unpack\nfrom datetime import datetime, timedelta\n\nfrom idl.matrix.proto.proto_parser_pb2 import Instance\nfrom monolith.native_training.data.parsers import parse_instances\nfrom monolith.native_training.data.datasets import PBDataset, PbType\n\nuids = [674432, 9754221, 7665435, 98797865, 778754432]\nitem_ids = [8767554565, 574220985, 65548979, 5358521231]\nactions = [1, 2]\ndevice_types = ['pc', 'mobile', 'cloud']\nslots = [1, 200, 5, 7, 9]\nNUM_INSTANCE = 4096\nMODEL_DIR = os.path.join(os.environ[\"TEST_TMPDIR\"], 'model_dir', 'multi_flow')\n\n\nclass MultiFlowTest(tf.test.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    mask = (1 << 54) - 1\n    start = int(datetime.now().timestamp())\n    stop = int((datetime.now() + timedelta(days=1)).timestamp())\n    if not tf.io.gfile.exists(MODEL_DIR):\n      tf.io.gfile.makedirs(MODEL_DIR)\n    ofile = os.path.join(MODEL_DIR, 'data.pb')\n    print(ofile, flush=True)\n    if not tf.io.gfile.exists(ofile):\n      with tf.io.gfile.GFile(ofile, 'wb') as ostream:\n        for _ in range(NUM_INSTANCE):\n          inst = Instance()\n          for slot in slots:\n            h = random.randrange(start, stop)\n            fid = (slot << 54) | (h & mask)\n            inst.fid.append(fid)\n\n          line_id = inst.line_id\n          line_id.uid = random.choice(uids)\n          line_id.item_id = random.choice(item_ids)\n          line_id.req_time = random.randrange(start, stop)\n          line_id.device_type = random.choice(device_types)\n          line_id.actions.append(random.choice(actions))\n\n          lgx_header = cls.mk_kgx_header(dataflow=line_id.device_type)\n          data = inst.SerializeToString()\n\n          ostream.write(file_content=lgx_header)\n          ostream.write(file_content=pack(f'<Q', len(data)))\n          ostream.write(file_content=data)\n\n  @classmethod\n  def tearDownClass(cls):\n    if not tf.io.gfile.exists(MODEL_DIR):\n      tf.io.gfile.rmtree(MODEL_DIR)\n\n  @classmethod\n  def mk_kgx_header(cls, dataflow: str):\n    # calc java hash code\n    seed, h = 31, 0\n    for c in dataflow:\n      h = np.int32(seed * h) + ord(c)\n\n    dfhc = int(np.uint32(h)).to_bytes(4, 'little')\n    return pack('4Bi', 0, dfhc[0], dfhc[1], dfhc[2], 0)\n\n  def test_data_flow(self):\n    ofile = os.path.join(MODEL_DIR, 'data.pb')\n    dataset = PBDataset(file_name=ofile,\n                        lagrangex_header=True,\n                        input_pb_type=PbType.INSTANCE,\n                        output_pb_type=PbType.INSTANCE)\n    pc = dataset.split_flow(data_flow=device_types,\n                            index=0,\n                            variant_type='instance')\n    mobile = dataset.split_flow(data_flow=device_types,\n                                index=1,\n                                variant_type='instance')\n    cloud = dataset.split_flow(data_flow=device_types,\n                               index=2,\n                               variant_type='instance')\n\n    dataset = pc.merge_flow(dataset_to_merge=[mobile, cloud],\n                            variant_type='instance')\n\n    def map_fn(tensor: tf.Tensor):\n      features = parse_instances(tensor,\n                                 fidv1_features=slots,\n                                 extra_features=[\n                                     'uid', 'item_id', 'req_time',\n                                     'device_type', 'actions'\n                                 ],\n                                 extra_feature_shapes=[1, 1, 1, 1, 1])\n      return features\n\n    dataset = dataset.batch(batch_size=512, drop_remainder=True).map(\n        map_fn, num_parallel_calls=tf.data.AUTOTUNE)\n\n    cnt = 0\n    for feat in dataset:\n      cnt += 1\n    self.assertEqual(cnt, 8)\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/negative_gen_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nimport tensorflow as tf\nfrom random import randint, choice, random\n\nfrom absl import logging\nimport numpy as np\nfrom struct import unpack, pack\n\nfrom monolith.native_training.data.datasets import PBDataset, InstanceReweightDataset, NegativeGenDataset, PbType\nfrom monolith.native_training.data.parsers import parse_instances, parse_examples, parse_example_batch\nfrom monolith.native_training.data.feature_utils import filter_by_fids, filter_by_value, negative_sample, \\\n  switch_slot, feature_combine, special_strategy\nfrom idl.matrix.proto.example_pb2 import Example\nfrom idl.matrix.proto.proto_parser_pb2 import Instance\nfrom idl.matrix.proto.line_id_pb2 import LineId\n\nfeatures = {\n    'f_spm_1': 301,\n    'f_spm_3': 303,\n    'f_spm_2': 302,\n    'f_spm_4': 304,\n    'f_user_id': 1,\n    'f_user_ctx_network': 61,\n    'f_user_id-f_page': 504,\n    'f_scm': 306,\n    'f_goods_id': 200,\n    'f_goods_sale_number_1000': 225,\n    'f_goods_praise_cnt': 229,\n    'f_spm': 300,\n    'f_page': 305,\n    'f_is_dup': 310,\n    'f_user_ctx_platform': 52,\n    'f_goods_title_terms': 209,\n    'f_goods_tags_terms': 211,\n    'f_user_test09_array_int32': 554,\n    'f_user_test15_array_float': 540,\n    'f_user_test14_array_bool': 543,\n    'f_user_test12_array_uint64': 551,\n    'f_user_test10_array_int64': 549\n}\n\nvariant_type = 'instance'\nchannel_feature = 'f_spm_1'\nchannel_slot = 301\nindex_feature = 'f_goods_id'\nindex_slot = 200\nuser_id = 'f_user_id'\nif variant_type == 'example':\n  item_features = [name for name, slot in features.items() if 'goods' in name]\nelse:\n  item_features = [slot for name, slot in features.items() if 'goods' in name]\npos_acts, neg_acts = [1, 2], [3, 4]\nnum_sample, start_num, neg_num = 1000, 10, 5\ncache_only_pos, throw_origin, throw_origin_neg = True, False, False\nper_channel = True\n\n\ndef parser(tensor: tf.Tensor):\n  if variant_type == 'instance':\n    feature_dict = parse_instances(\n        tensor,\n        fidv1_features=list(features.values()),\n        dense_features=['label'],\n        dense_feature_shapes=[1],\n        dense_feature_types=[tf.float32],\n        extra_features=['uid', 'req_time', 'item_id'],\n        extra_feature_shapes=[1, 1, 1])\n  else:\n    feature_dict = parse_examples(\n        tensor,\n        sparse_features=list(features.keys()),\n        dense_features=['label'],\n        dense_feature_shapes=[1],\n        dense_feature_types=[tf.float32],\n        extra_features=['uid', 'req_time', 'item_id', 'actions'],\n        extra_feature_shapes=[1, 1, 1, 1])\n  return feature_dict\n\n\nclass NegativeGenTest(tf.test.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    offset = 48 if variant_type == 'example' else 54\n    cids = [(channel_slot << offset) + randint(1, 1 << 32) for _ in range(10)]\n    gids = [(index_slot << offset) + randint(1, 1 << 32) for _ in range(100)]\n    cls.cid_status, cls.gid_status = {cid: {\n        'p': 0,\n        'n': 0\n    } for cid in cids}, {gid: 0 for gid in gids}\n\n    with open(f'{variant_type}.pb', 'wb') as ostream:\n      for _ in range(num_sample):\n        uid, gid, cid = None, None, None\n        if variant_type == 'example':\n          sample = Example()\n          for name, slot in features.items():\n            named_feature = sample.named_feature.add()\n            named_feature.id = slot\n            named_feature.name = name\n            if name == channel_feature:\n              cid = choice(cids)\n              fid = cid\n            elif name == index_feature:\n              gid = choice(gids)\n              cls.gid_status[gid] += 1\n              fid = gid\n            elif name == user_id:\n              uid = (slot << offset) + randint(1, 1 << 32)\n              fid = uid\n            else:\n              fid = (slot << offset) + randint(1, 1 << 32)\n            named_feature.feature.fid_v2_list.value.append(fid)\n        else:\n          sample = Instance()\n          for name, slot in features.items():\n            if name == channel_feature:\n              cid = choice(cids)\n              fid = cid\n            elif name == index_feature:\n              gid = choice(gids)\n              cls.gid_status[gid] += 1\n              fid = gid\n            elif name == user_id:\n              uid = (slot << offset) + randint(1, 1 << 32)\n              fid = uid\n            else:\n              fid = (slot << offset) + randint(1, 1 << 32)\n            sample.fid.append(fid)\n\n        line_id = LineId(uid=uid, item_id=gid, req_time=int(time.time()))\n        label = []\n        if random() > 0.5:\n          line_id.actions.append(choice(pos_acts))\n          label.append(1)\n          cls.cid_status[cid]['p'] += 1\n        else:\n          line_id.actions.append(choice(neg_acts))\n          label.append(-1)\n          cls.cid_status[cid]['n'] += 1\n\n        if variant_type == 'example':\n          label_nf = sample.named_feature.add()\n          label_nf.name = '__LABEL__'\n          label_nf.feature.float_list.value.extend(label)\n          lid = sample.named_feature.add()\n          lid.name = '__LINE_ID__'\n          lid.feature.bytes_list.value.append(line_id.SerializeToString())\n        else:\n          sample.label.extend(label)\n          sample.line_id.CopyFrom(line_id)\n\n        es = sample.SerializeToString()\n        ostream.write(pack('<QQ', 0, len(es)))\n        ostream.write(es)\n\n    print(cls.cid_status, cls.gid_status)\n\n  @classmethod\n  def tearDownClass(cls):\n    if tf.io.gfile.exists(f'{variant_type}.pb'):\n      tf.io.gfile.remove(f'{variant_type}.pb')\n\n  def test_dataset_target(self):\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        pb_type = PbType.EXAMPLE if variant_type == 'example' else PbType.INSTANCE\n        dataset = PBDataset(file_name=f'{variant_type}.pb',\n                            lagrangex_header=True,\n                            input_pb_type=pb_type,\n                            output_pb_type=pb_type)\n        dataset = dataset.negative_gen(\n            neg_num=neg_num,\n            per_channel=per_channel,\n            start_num=start_num,\n            max_item_num=1000,\n            cache_only_pos=cache_only_pos,\n            channel_feature=channel_feature\n            if variant_type == 'example' else channel_slot,\n            item_features=item_features,\n            throw_origin=throw_origin,\n            throw_origin_neg=throw_origin_neg,\n            variant_type=variant_type)\n        dataset = dataset.batch(8, drop_remainder=False).map(parser)\n        it = tf.compat.v1.data.make_initializable_iterator(dataset)\n        element = it.get_next()\n        sess.run(it.initializer)\n        count, pos_cnt, neg_cnt = 0, 0, 0\n        real_cids = {cid: {'p': 0, 'n': 0} for cid in self.cid_status}\n        channel_feature_name = channel_feature if variant_type == 'example' else f'slot_{channel_slot}'\n        while True:\n          try:\n            element_out = sess.run(element)\n            # print(element_out, flush=True)\n            pos = element_out['label'] > 0\n            neg = element_out['label'] < 0\n            pos_cnt += np.sum(pos)\n            neg_cnt += np.sum(neg)\n            count += element_out['label'].shape[0]\n\n            for cid in self.cid_status:\n              select_channel = element_out[channel_feature_name] == cid\n              np.sum(np.logical_and(select_channel, pos))\n              np.sum(np.logical_and(select_channel, neg))\n\n          except tf.errors.OutOfRangeError:\n            break\n        self.assertEqual(count, pos_cnt + neg_cnt)\n\n        expect_pos, expect_neg = 0, 0\n        for pn_dict in self.cid_status.values():\n          expect_pos += pn_dict['p']\n          expect_neg += pn_dict['n']\n\n        self.assertEqual(expect_pos + expect_neg, num_sample)\n        if not throw_origin:\n          self.assertEqual(pos_cnt, expect_pos)\n        else:\n          self.assertEqual(pos_cnt, 0)\n\n        if not throw_origin and not throw_origin_neg:\n          if per_channel:\n            pass\n          else:\n            min_gen = (expect_pos - start_num) * neg_num\n            max_gen = expect_pos * neg_num\n            real_gen = count - num_sample\n            self.assertTrue(min_gen <= real_gen <= max_gen)\n\n        print(count, pos_cnt, neg_cnt, expect_pos, expect_neg, flush=True)\n        logging.info(\"The number of batch is: {}\".format(count))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/ops/feature_utils_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\n\nREGISTER_OP(\"ExtractFid\")\n    .Input(\"input: int64\")\n    .Attr(\"slot: int\")\n    .Output(\"output: int64\");\n\nREGISTER_OP(\"FeatureHash\")\n    .Input(\"input: variant\")\n    .Attr(\"names: list(string)\")\n    .Output(\"output: variant\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"SetFilter\")\n    .Input(\"input: variant\")\n    .Attr(\"filter_fids: list(int)\")\n    .Attr(\"has_fids: list(int)\")\n    .Attr(\"select_fids: list(int)\")\n    .Attr(\"has_actions: list(int)\")\n    .Attr(\"req_time_min: int\")\n    .Attr(\"select_slots: list(int)\")\n    .Attr(\"variant_type: string\")\n    .Output(\"output: bool\");\n\nREGISTER_OP(\"FeatureValueFilter\")\n    .Input(\"input: variant\")\n    .Attr(\"field_name: string\")\n    .Attr(\"op: string\")\n    .Attr(\"float_operand: list(float)\")\n    .Attr(\"int_operand: list(int)\")\n    .Attr(\"string_operand: list(string)\")\n    .Attr(\"operand_filepath: string\")\n    .Attr(\"field_type: string\")\n    .Attr(\"keep_empty: bool = false\")\n    .Output(\"output: bool\");\n\nREGISTER_OP(\"ValueFilter\")\n    .Input(\"input: variant\")\n    .Attr(\"field_name: string\")\n    .Attr(\"op: string\")\n    .Attr(\"float_operand: list(float)\")\n    .Attr(\"int_operand: list(int)\")\n    .Attr(\"string_operand: list(string)\")\n    .Attr(\"operand_filepath: string\")\n    .Attr(\"keep_empty: bool = false\")\n    .Attr(\"variant_type: string\")\n    .Output(\"output: bool\");\n\nREGISTER_OP(\"AddAction\")\n    .Input(\"input: variant\")\n    .Attr(\"field_name: string\")\n    .Attr(\"op: string\")\n    .Attr(\"float_operand: list(float)\")\n    .Attr(\"int_operand: list(int)\")\n    .Attr(\"string_operand: list(string)\")\n    .Attr(\"variant_type: string\")\n    .Attr(\"actions: list(int)\")\n    .Output(\"output: variant\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"AddLabel\")\n    .Input(\"input: variant\")\n    .Attr(\"config: string\")\n    .Attr(\"negative_value: float\")\n    .Attr(\"sample_rate: float\")\n    .Attr(\"variant_type: string\")\n    .Output(\"output: variant\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"MonolithTFExampleToExample\")\n    .Input(\"input: string\")\n    .Attr(\"feature_description: string\")\n    .Output(\"output: variant\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"ScatterLabel\")\n    .Input(\"input: variant\")\n    .Attr(\"config: string\")\n    .Attr(\"variant_type: string\")\n    .Output(\"output: variant\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"FilterByLabel\")\n    .Input(\"input: variant\")\n    .Attr(\"label_threshold: list(float)\")\n    .Attr(\"filter_equal: bool\")\n    .Attr(\"variant_type: string\")\n    .Output(\"valid: bool\");\n\nREGISTER_OP(\"SpecialStrategy\")\n    .Input(\"input: variant\")\n    .Attr(\"special_strategies: list(int)\")\n    .Attr(\"sample_rates: list(float)\")\n    .Attr(\"labels: list(float)\")\n    .Attr(\"strategy_list: list(int)\")\n    .Attr(\"keep_empty_strategy: bool = true\")\n    .Attr(\"variant_type: string\")\n    .Output(\"output: bool\");\n\nREGISTER_OP(\"NegativeSample\")\n    .Input(\"input: variant\")\n    .Attr(\"drop_rate: float\")\n    .Attr(\"label_index: int = 0\")\n    .Attr(\"threshold: float = 0.0\")\n    .Attr(\"variant_type: string\")\n    .Attr(\"priorities: list(int)\")\n    .Attr(\"actions: list(int)\")\n    .Attr(\"per_action_drop_rate: list(float)\")\n    .Output(\"output: bool\");\n\nREGISTER_OP(\"LabelUpperBound\")\n    .Input(\"input: variant\")\n    .Attr(\"label_upper_bounds: list(float)\")\n    .Attr(\"variant_type: string\")\n    .Output(\"output: variant\");\n\nREGISTER_OP(\"LabelNormalization\")\n    .Input(\"input: variant\")\n    .Attr(\"norm_methods: list(string)\")\n    .Attr(\"norm_values: list(float)\")\n    .Attr(\"variant_type: string\")\n    .Output(\"output: variant\");\n\nREGISTER_OP(\"UseFieldAsLabel\")\n    .Input(\"input: variant\")\n    .Attr(\"field_name: string\")\n    .Attr(\"overwrite_invalid_value: bool\")\n    .Attr(\"label_threshold: float\")\n    .Attr(\"variant_type: string\")\n    .Output(\"output: variant\");\n\nREGISTER_OP(\"SwitchSlot\")\n    .Input(\"rt_nested_splits: RAGGED_RANK * int64\")\n    .Input(\"rt_dense_values: int64\")\n    .Output(\"nested_splits_out: RAGGED_RANK * int64\")\n    .Output(\"dense_values_out: int64\")\n    .Attr(\"slot: int >=1\")\n    .Attr(\"fid_version: int\")\n    .Attr(\"RAGGED_RANK: int >= 1\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      int rank;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"RAGGED_RANK\", &rank));\n      for (int i = 0; i < rank; ++i) {\n        ctx->set_output(i, ctx->input(i));\n      }\n      ctx->set_output(rank, ctx->input(rank));\n\n      return Status::OK();\n    });\n\nREGISTER_OP(\"SwitchSlotBatch\")\n    .Input(\"pb_input: variant\")\n    .Output(\"pb_output: variant\")\n    .Attr(\"features: list(string)\")\n    .Attr(\"slots: list(int)\")\n    .Attr(\"inplaces: list(bool)\")\n    .Attr(\"suffix: string\")\n    .Attr(\"variant_type: string\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"FeatureCombine\")\n    .Input(\"rt_nested_splits_src1: RAGGED_RANK * int64\")\n    .Input(\"rt_dense_values_src1: int64\")\n    .Input(\"rt_nested_splits_src2: RAGGED_RANK * int64\")\n    .Input(\"rt_dense_values_src2: int64\")\n    .Output(\"nested_splits_sink: RAGGED_RANK * int64\")\n    .Output(\"dense_values_sink: int64\")\n    .Attr(\"slot: int >=1\")\n    .Attr(\"fid_version: int\")\n    .Attr(\"RAGGED_RANK: int >= 1\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      int rank;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"RAGGED_RANK\", &rank));\n      for (int i = 0; i < rank; ++i) {\n        ctx->set_output(i, ctx->input(i));\n      }\n      ctx->set_output(rank, ctx->input(rank));\n\n      return Status::OK();\n    });\n\nREGISTER_OP(\"ItemPoolCreate\")\n    .Output(\"pool: resource\")\n    .Attr(\"start_num: int\")\n    .Attr(\"max_item_num_per_channel: int\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    // .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_OP(\"ItemPoolRandomFill\")\n    .Input(\"ipool: resource\")\n    .Output(\"opool: resource\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_OP(\"ItemPoolCheck\")\n    .Input(\"ipool: resource\")\n    .Input(\"global_step: int64\")\n    .Output(\"opool: resource\")\n    .Attr(\"model_path: string\")\n    .Attr(\"nshards: int\")\n    .Attr(\"buffer_size: int\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_OP(\"ItemPoolSave\")\n    .Input(\"ipool: resource\")\n    .Input(\"global_step: int64\")\n    .Output(\"opool: resource\")\n    .Attr(\"model_path: string\")\n    .Attr(\"nshards: int\")\n    .Attr(\"random_sleep_ms: int=0\")\n    // .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_OP(\"ItemPoolRestore\")\n    .Input(\"ipool: resource\")\n    .Input(\"global_step: int64\")\n    .Output(\"opool: resource\")\n    .Attr(\"model_path: string\")\n    .Attr(\"buffer_size: int\")\n    .Attr(\"nshards: int\")\n    .Attr(\"random_sleep_ms: int=0\")\n    // .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_OP(\"FillMultiRankOutput\")\n    .Input(\"input: variant\")\n    .Attr(\"variant_type: string\")\n    .Attr(\"enable_draw_as_rank: bool = false\")\n    .Attr(\"enable_chnid_as_rank: bool = false\")\n    .Attr(\"enable_lineid_rank_as_rank: bool = false\")\n    .Attr(\"rank_num: int = 18\")\n    .Output(\"output: variant\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_OP(\"UseF100MultiHead\")\n    .Input(\"input: variant\")\n    .Attr(\"variant_type: string\")\n    .Output(\"output: variant\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_OP(\"MapId\")\n    .Input(\"input: T\")\n    .Attr(\"from_value: list(int)\")\n    .Attr(\"to_value: list(int)\")\n    .Attr(\"default_value: int\")\n    .Output(\"output: T\")\n    .Attr(\"T: {int32, int64}\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"MultiLabelGen\")\n    .Input(\"input: variant\")\n    .Attr(\"task_num: int\")\n    .Attr(\"head_to_index: string\")\n    .Attr(\"head_field: string\")\n    .Attr(\"action_priority: string\")\n    .Attr(\"pos_actions: list(int)\")\n    .Attr(\"neg_actions: list(int)\")\n    .Attr(\"use_origin_label: bool\")\n    .Attr(\"pos_label: float\")\n    .Attr(\"neg_label: float\")\n    .Attr(\"variant_type: string\")\n    .Output(\"output: variant\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"StringToVariant\")\n    .Input(\"input: string\")\n    .Attr(\"input_type: string\")\n    .Attr(\"has_header: bool\")\n    .Attr(\"has_sort_id: bool\")\n    .Attr(\"lagrangex_header: bool\")\n    .Attr(\"kafka_dump_prefix: bool\")\n    .Attr(\"kafka_dump: bool\")\n    .Attr(\"chnids: list(int)\")\n    .Attr(\"datasources: list(string)\")\n    .Attr(\"default_datasource: string\")\n    .Output(\"output: variant\")\n    .SetDoNotOptimize()\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"StringToVariantWithTransform\")\n    .Input(\"input: string\")\n    .Attr(\"input_type: string\")\n    .Attr(\"output_type: string\")\n    .Attr(\"has_header: bool\")\n    .Attr(\"has_sort_id: bool\")\n    .Attr(\"lagrangex_header: bool\")\n    .Attr(\"kafka_dump_prefix: bool\")\n    .Attr(\"kafka_dump: bool\")\n    .Attr(\"chnids: list(int)\")\n    .Attr(\"datasources: list(string)\")\n    .Attr(\"default_datasource: string\")\n    .Output(\"output: variant\")\n    .SetDoNotOptimize()\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->MakeShape({ctx->UnknownDim()}));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"VariantToZeros\")\n    .Input(\"input: variant\")\n    .Output(\"output: int64\")\n    .SetDoNotOptimize()\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"HasVariant\")\n    .Input(\"input: variant\")\n    .Output(\"output: bool\")\n    .Attr(\"variant_type: string\")\n    .SetShapeFn([](shape_inference::InferenceContext *c) {\n      c->set_output(0, c->Scalar());\n      return Status::OK();\n    });\n\nREGISTER_OP(\"KafkaGroupReadableInit\")\n    .Input(\"topics: string\")\n    .Input(\"metadata: string\")\n    .Output(\"resource: resource\")\n    .Attr(\"input_pb_type: string = ''\")\n    .Attr(\"output_pb_type: string = ''\")\n    .Attr(\"has_sort_id: bool = false\")\n    .Attr(\"lagrangex_header: bool = false\")\n    .Attr(\"kafka_dump_prefix: bool = false\")\n    .Attr(\"kafka_dump: bool = false\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetShapeFn([](shape_inference::InferenceContext *c) {\n      c->set_output(0, c->Scalar());\n      return Status::OK();\n    });\n\nREGISTER_OP(\"KafkaGroupReadableNext\")\n    .Input(\"input: resource\")\n    .Input(\"index: int64\")\n    .Input(\"message_poll_timeout: int64\")\n    .Input(\"stream_timeout: int64\")\n    .Output(\"message: string\")\n    .Output(\"key: string\")\n    .Output(\"continue_fetch: int64\")\n    .SetShapeFn([](shape_inference::InferenceContext *c) {\n      c->set_output(0, c->MakeShape({c->UnknownDim()}));\n      c->set_output(1, c->MakeShape({c->UnknownDim()}));\n      c->set_output(2, c->Scalar());\n      return Status::OK();\n    });\n\nREGISTER_OP(\"KafkaGroupReadableNextV2\")\n    .Input(\"input: resource\")\n    .Input(\"index: int64\")\n    .Input(\"message_poll_timeout: int64\")\n    .Input(\"stream_timeout: int64\")\n    .Output(\"message: variant\")\n    .Output(\"continue_fetch: int64\")\n    .SetShapeFn([](shape_inference::InferenceContext *c) {\n      c->set_output(0, c->MakeShape({c->UnknownDim()}));\n      c->set_output(1, c->Scalar());\n      return Status::OK();\n    });\n\nREGISTER_OP(\"MonolithGenFidMask\")\n    .Input(\"splits: T\")\n    .Input(\"values: int64\")\n    .Output(\"mask: float32\")\n    .Attr(\"fid: int\")\n    .Attr(\"T: {int32, int64}\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      shape_inference::ShapeHandle shape = ctx->input(0);\n      if (ctx->Rank(shape) == 1) {\n        shape_inference::DimensionHandle new_dim;\n        TF_RETURN_IF_ERROR(ctx->Subtract(ctx->Dim(shape, 0), 1, &new_dim));\n        shape_inference::ShapeHandle out_shape;\n        TF_RETURN_IF_ERROR(ctx->ReplaceDim(shape, 0, new_dim, &out_shape));\n        ctx->set_output(0, out_shape);\n      } else {\n        ctx->set_output(0, ctx->MakeShape({ctx->UnknownDim()}));\n      }\n\n      return Status::OK();\n    });\n\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/ops/parse_input_data_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nStatus ShapeFn(shape_inference::InferenceContext *ctx) {\n  int batch_size = ctx->Value(ctx->Dim(ctx->input(0), 0));\n  std::vector<int> shapes;\n  std::vector<DataType> dtypes;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"shapes\", &shapes));\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"dtypes\", &dtypes));\n\n  if (batch_size > 0) {  // know batch_size\n    for (size_t i = 0; i < dtypes.size(); ++i) {\n      if (i >= shapes.size()) {\n        ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));\n      } else {\n        DataType dtype = dtypes[i];\n        int shape = shapes[i];\n        if (shape == -1) {\n          if (dtype != DataType::DT_INT64) {\n            return errors::InvalidArgument(\n                \"If shape is -1, then dtype must be int64\");\n          }\n          ctx->set_output(i, ctx->Vector(batch_size + 1));\n        } else {\n          ctx->set_output(i, ctx->Matrix(batch_size, shape));\n        }\n      }\n    }\n  } else {  // batch_size unknown\n    for (size_t i = 0; i < dtypes.size(); ++i) {\n      if (i >= shapes.size()) {\n        ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));\n      } else {\n        int shape = shapes[i];\n        if (shape > 0) {\n          ctx->set_output(i, ctx->Matrix(ctx->UnknownDim(), shape));\n        } else {\n          ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));\n        }\n      }\n    }\n  }\n\n  return Status::OK();\n}\n\nREGISTER_OP(\"ParseInstances\")\n    .Input(\"pb_input: T\")\n    .Output(\"tensors: dtypes\")\n    .Attr(\"fidv1_features: list(int)\")\n    .Attr(\"fidv2_features: list(string)\")\n    .Attr(\"names: list(string)\")\n    .Attr(\"shapes: list(int)\")\n    .Attr(\"dtypes: list(type)\")\n    .Attr(\"extra_names: list(string)\")\n    .Attr(\"T: {variant, string}\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn(ShapeFn);\n\nREGISTER_OP(\"ParseInstancesV2\")\n    .Input(\"pb_input: T\")\n    .Output(\"tensors: dtypes\")\n    .Output(\"sparse_features: variant\")\n    .Attr(\"fidv1_features: list(int)\")\n    .Attr(\"fidv2_features: list(string)\")\n    .Attr(\"names: list(string)\")\n    .Attr(\"shapes: list(int)\")\n    .Attr(\"dtypes: list(type)\")\n    .Attr(\"extra_names: list(string)\")\n    .Attr(\"T: {variant, string}\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      auto status = ShapeFn(ctx);\n      std::vector<DataType> dtypes;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"dtypes\", &dtypes));\n      ctx->set_output(dtypes.size(), ctx->input(0));\n      return status;\n    });\n\nREGISTER_OP(\"ParseExamples\")\n    .Input(\"pb_input: T\")\n    .Output(\"tensors: dtypes\")\n    .Attr(\"names: list(string)\")\n    .Attr(\"shapes: list(int)\")\n    .Attr(\"dtypes: list(type)\")\n    .Attr(\"extra_names: list(string)\")\n    .Attr(\"T: {variant, string}\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn(ShapeFn);\n\nREGISTER_OP(\"ParseExamplesV2\")\n    .Input(\"pb_input: T\")\n    .Output(\"tensors: dtypes\")\n    .Output(\"sparse_features: variant\")\n    .Attr(\"names: list(string)\")\n    .Attr(\"shapes: list(int)\")\n    .Attr(\"dtypes: list(type)\")\n    .Attr(\"extra_names: list(string)\")\n    .Attr(\"T: {variant, string}\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      auto status = ShapeFn(ctx);\n      std::vector<DataType> dtypes;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"dtypes\", &dtypes));\n      ctx->set_output(dtypes.size(), ctx->input(0));\n      return status;\n    });\n\nREGISTER_OP(\"ParseExampleBatch\")\n    .Input(\"pb_input: T\")\n    .Output(\"tensors: dtypes\")\n    .Attr(\"names: list(string)\")\n    .Attr(\"shapes: list(int)\")\n    .Attr(\"dtypes: list(type)\")\n    .Attr(\"extra_names: list(string)\")\n    .Attr(\"T: {variant, string}\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      std::vector<int> shapes;\n      std::vector<DataType> dtypes;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"shapes\", &shapes));\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"dtypes\", &dtypes));\n\n      for (size_t i = 0; i < dtypes.size(); ++i) {\n        if (i >= shapes.size()) {\n          ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));\n        } else {\n          int shape = shapes[i];\n          if (shape > 0) {\n            ctx->set_output(i, ctx->Matrix(ctx->UnknownDim(), shape));\n          } else {\n            ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));\n          }\n        }\n      }\n      return Status::OK();\n    });\n\nREGISTER_OP(\"ParseExampleBatchV2\")\n    .Input(\"pb_input: T\")\n    .Output(\"tensors: dtypes\")\n    .Output(\"sparse_features: variant\")\n    .Attr(\"names: list(string)\")\n    .Attr(\"shapes: list(int)\")\n    .Attr(\"dtypes: list(type)\")\n    .Attr(\"extra_names: list(string)\")\n    .Attr(\"T: {variant, string}\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      // same as ParseExampleBatch\n      std::vector<int> shapes;\n      std::vector<DataType> dtypes;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"shapes\", &shapes));\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"dtypes\", &dtypes));\n\n      for (size_t i = 0; i < dtypes.size(); ++i) {\n        if (i >= shapes.size()) {\n          ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));\n        } else {\n          int shape = shapes[i];\n          if (shape > 0) {\n            ctx->set_output(i, ctx->Matrix(ctx->UnknownDim(), shape));\n          } else {\n            ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));\n          }\n        }\n      }\n\n      // add sparse_features\n      ctx->set_output(dtypes.size(), ctx->Scalar());\n      return Status::OK();\n    });\n\nREGISTER_OP(\"ParseExampleBatchList\")\n    .Input(\"inputs: N * variant\")\n    .Output(\"tensors: dtypes\")\n    .Attr(\"label_config: string\")\n    .Attr(\"names: list(string)\")\n    .Attr(\"shapes: list(int)\")\n    .Attr(\"dtypes: list(type)\")\n    .Attr(\"extra_names: list(string)\")\n    .Attr(\"positive_label: float\")\n    .Attr(\"negative_label: float\")\n    .Attr(\"N: int\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      std::vector<int> shapes;\n      std::vector<DataType> dtypes;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"shapes\", &shapes));\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"dtypes\", &dtypes));\n\n      for (size_t i = 0; i < dtypes.size(); ++i) {\n        if (i >= shapes.size()) {\n          ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));\n        } else {\n          int shape = shapes[i];\n          if (shape > 0) {\n            ctx->set_output(i, ctx->Matrix(ctx->UnknownDim(), shape));\n          } else {\n            ctx->set_output(i, ctx->Vector(ctx->UnknownDim()));\n          }\n        }\n      }\n      return Status::OK();\n    });\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/ops/pb_dataset_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_def_builder.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\n\nREGISTER_OP(\"PBDataset\")\n    .Input(\"file_name: string\")\n    .Input(\"use_snappy: bool\")\n    .Input(\"has_sort_id: bool\")\n    .Input(\"kafka_dump: bool\")\n    .Input(\"kafka_dump_prefix: bool\")\n    .Input(\"buffer_size: int64\")\n    .Input(\"lagrangex_header: bool\")\n    .Input(\"input_pb_type: string\")\n    .Input(\"output_pb_type: string\")\n    .Input(\"feature_pruning_type: int32\")\n    .Input(\"feature_name_list: string\")\n    .Input(\"feature_id_list: int32\")\n    .Attr(\"out_type: {variant, string}\")\n    .Attr(\"compression_type: int = 0\")\n    .Output(\"handle: variant\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle unused;\n      TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 1, &unused));\n      return shape_inference::ScalarShape(c);\n    });\n\nREGISTER_OP(\"ParquetDataset\")\n    // basic\n    .Input(\"file_name: string\")\n    .Input(\"output_pb_type: string\")  // example or example_batch\n    .Attr(\"batch_size: int\")  // valid when output_pb_type is 'example_batch'\n    // select cols\n    .Attr(\"select_columns: list(string)\")\n    .Attr(\"select_columns_type: list(string)\")\n    .Attr(\"drop_remainder: bool\")\n    // output\n    .Output(\"handle: variant\")\n    .SetDoNotOptimize()\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle unused;\n      TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 1, &unused));\n      return shape_inference::ScalarShape(c);\n    });\n\nREGISTER_OP(\"InstanceReweightDataset\")\n    .Input(\"input: variant\")\n    .Attr(\"method: int\")\n    .Attr(\"actions: list(int)\")\n    .Attr(\"weights: list(int)\")\n    .Attr(\"labels: list(int)\")\n    .Attr(\"priorities: list(int)\")\n    .Attr(\"variant_type: string\")\n    .Output(\"handle: variant\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle unused;\n      TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 0, &unused));\n      return shape_inference::ScalarShape(c);\n    });\n\nREGISTER_OP(\"InstanceNegativeGenDataset\")\n    .Input(\"input: variant\")\n    .Input(\"pool: resource\")\n    .Attr(\"neg_num: int >= 1\")\n    .Attr(\"per_channel: bool\")\n    .Attr(\"channel_feature: string\")\n    .Attr(\"item_features: list(string)\")\n    .Attr(\"label_index: int >= 0\")\n    .Attr(\"positive_label: int\")\n    .Attr(\"negative_label: int\")\n    .Attr(\"negative_action: int\")\n    .Attr(\"action_priority: string\")\n    .Attr(\"positive_actions: list(int)\")\n    .Attr(\"index_feature: string\")\n    .Attr(\"throw_origin: bool\")\n    .Attr(\"throw_origin_neg: bool\")\n    .Attr(\"cache_only_pos: bool\")\n    .Attr(\"cache_negative_actions: list(int)\")\n    .Attr(\"real_neg_instance_weight: float\")\n    .Attr(\"sampled_neg_instance_weight: float\")\n    .Attr(\"unbias_sampled_neg: bool\")\n    .Attr(\"origin_neg_in_pool_proba: float\")\n    .Attr(\"neg_sample_declay_factor: float\")\n    .Attr(\"easy_hard_ratio: float\")\n    .Attr(\"variant_type: string\")\n    .Output(\"handle: variant\")\n    .SetDoNotOptimize()\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle unused;\n      TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 0, &unused));\n      return shape_inference::ScalarShape(c);\n    });\n\nREGISTER_OP(\"SplitFlowDataset\")\n    .Input(\"input: variant\")\n    .Attr(\"data_flow: list(string)\")\n    .Attr(\"index: int\")\n    .Attr(\"max_queue_size: int\")\n    .Attr(\"variant_type: string\")\n    .Output(\"handle: variant\")\n    .SetDoNotOptimize()\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle unused;\n      TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 0, &unused));\n      return shape_inference::ScalarShape(c);\n    });\n\nREGISTER_OP(\"MergeFlowDataset\")\n    .Input(\"inputs:  N * variant\")\n    .Attr(\"data_flow: list(string)\")\n    .Attr(\"max_queue_size: int\")\n    .Attr(\"variant_type: string\")\n    .Attr(\"N: int >= 1\")\n    .Output(\"handle: variant\")\n    .SetDoNotOptimize()\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle unused;\n      TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 0, &unused));\n      return shape_inference::ScalarShape(c);\n    });\n\nREGISTER_OP(\"DynamicMatchingFilesDataset\")\n    .Input(\"patterns: string\")\n    .Output(\"handle: variant\")\n    .SetDoNotOptimize()\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      return shape_inference::ScalarShape(c);\n    });\n\nREGISTER_OP(\"MonolithCacheOneDataset\")\n    .Input(\"input: variant\")\n    .Output(\"handle: variant\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle unused;\n      TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 0, &unused));\n      return shape_inference::ScalarShape(c);\n    });\n\nREGISTER_OP(\"TransformDataset\")\n    .Input(\"input: variant\")\n    .Attr(\"config: string\")\n    .Attr(\"variant_type: string\")\n    .Output(\"handle: variant\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle unused;\n      TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 0, &unused));\n      return shape_inference::ScalarShape(c);\n    });\n\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/parse_sparse_feature_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom collections import defaultdict\nfrom copy import deepcopy\nimport os\nimport time\nfrom typing import Callable, Dict, Iterable, List, Set, Tuple\n\nimport tensorflow as tf\n\nfrom absl import logging\nimport numpy as np\nfrom struct import unpack\nimport random\n\nfrom monolith.native_training.data.parsers import parse_instances, parse_examples, parse_example_batch, \\\n    sharding_sparse_fids, get_default_parser_ctx, ParserCtx\nfrom monolith.native_training.model_export.data_gen_utils import gen_fids_v1, gen_fids_v2, fill_line_id, \\\n  gen_instance, FeatureMeta\nfrom idl.matrix.proto.example_pb2 import Example, ExampleBatch, FeatureConfigs, FeatureConfig, FeatureListType\nfrom idl.matrix.proto.line_id_pb2 import LineId\n\nfeatures = {\n    'f_spm_1': 301,\n    'f_spm_3': 303,\n    'f_spm_2': 302,\n    'f_spm_4': 304,\n    'f_user_id': 1,\n    'f_user_ctx_network': 61,\n    'f_user_id-f_page': 504,\n    'f_scm': 306,\n    'f_goods_id': 200,\n    'f_goods_sale_number_1000': 225,\n    'f_goods_praise_cnt': 229,\n    'f_spm': 300,\n    'f_page': 305,\n    'f_is_dup': 310,\n    'f_user_ctx_platform': 52,\n    'f_goods_title_terms': 209,\n    'f_goods_tags_terms': 211,\n    'f_user_test09_array_int32': 554,\n    'f_user_test15_array_float': 540,\n    'f_user_test14_array_bool': 543,\n    'f_user_test12_array_uint64': 551,\n    'f_user_test10_array_int64': 549\n}\n\n\nclass DataOpsV2Test(tf.test.TestCase):\n\n  def __init__(self, *args, **kwargs):\n    super(DataOpsV2Test, self).__init__(*args, **kwargs)\n    self.mask = (1 << 48) - 1\n    self.version = 3\n\n  def fid_v1_to_v2(self, fid_v1):\n    slot_id = (fid_v1 >> 54)\n    fid_v2 = ((slot_id << 48) | (self.mask & fid_v1))\n    return slot_id, fid_v2\n\n  def fill_row_split(self, ps_num, t_cfg, fid_list, row_split):\n    for ps_i in range(ps_num):\n      lenth = 0\n      for feature_name in t_cfg[\"feature_list\"]:\n        key = feature_name + \":\" + str(ps_i)\n        if key in fid_list:\n          lenth += len(fid_list[key])\n      row_split[t_cfg[\"table_name\"] + \":\" + str(ps_i)].append(lenth)\n\n  def get_pre_output_offset(self, shard, f_cfg):\n    if self.version == 2:\n      return f_cfg[\"pre_output_index\"] + shard * f_cfg[\n          \"table_feature_count\"] + f_cfg[\"feature_in_table_index\"]\n    else:\n      return f_cfg[\"pre_output_index\"] + shard\n\n  def get_feature_cfg(self, raw_feature_cfgs, ps_num):\n    feature_cfg = defaultdict(dict)\n    table_cfg = defaultdict(dict)\n    for feature_name, cfg in raw_feature_cfgs.feature_configs.items():\n      feature_cfg[feature_name] = {\n          \"feature_name\": feature_name,\n          \"feature_index\": -1,\n          \"table_name\": cfg.table,\n          \"table_index\": -1,\n          \"feature_in_table_index\": -1,\n          \"table_feature_count\": 0,\n          \"pre_output_index\": 0,\n          \"dims_sum\": sum(cfg.slice_dims),\n      }\n      if cfg.table not in table_cfg:\n        table_cfg[cfg.table] = {\n            \"table_name\": cfg.table,\n            \"feature_list\": [],\n            \"table_index\": -1,\n            \"feature_count\": 0,\n        }\n\n    table_name_sort = sorted(table_cfg.keys())\n    for idx, name in enumerate(table_name_sort):\n      table_cfg[name][\"table_index\"] = idx\n\n    feature_name_sort = sorted(feature_cfg.keys())\n    for idx, name in enumerate(feature_name_sort):\n      f_cfg = feature_cfg[name]\n      t_cfg = table_cfg[f_cfg[\"table_name\"]]\n\n      f_cfg[\"feature_index\"] = idx\n      f_cfg[\"table_index\"] = t_cfg[\"table_index\"]\n      f_cfg[\"feature_in_table_index\"] = len(t_cfg[\"feature_list\"])\n\n      t_cfg[\"feature_list\"].append(name)\n\n    pre_index = 0\n    for idx, name in enumerate(table_name_sort):\n      t_cfg = table_cfg[name]\n      t_cfg[\"feature_count\"] = len(t_cfg[\"feature_list\"])\n      for feature_name in t_cfg[\"feature_list\"]:\n        f_cfg = feature_cfg[feature_name]\n        f_cfg[\n            \"pre_output_index\"] = pre_index if self.version == 2 else idx * ps_num\n        f_cfg[\"table_feature_count\"] = t_cfg[\"feature_count\"]\n      pre_index += max(t_cfg[\"feature_count\"], 1) * ps_num\n\n    logging.info(f\"show feature_cfg: {feature_cfg}\")\n    logging.info(f\"show table_cfg: {table_cfg}\")\n    return feature_cfg, table_cfg, feature_name_sort, table_name_sort\n\n  def handle_feature(self, fid_v1_list, fid_v2_list, f_cfg, t_cfg, ps_num,\n                     fid_offset_list, fid_offset_list2, fid_map_t,\n                     fid_map_unique_map, fid_map_unique_t):\n    value_list = []\n    if len(fid_v1_list) != 0:\n      for fid in fid_v1_list:\n        slot_id, fid_v2 = self.fid_v1_to_v2(fid)\n        value_list.append(fid_v2)\n    elif len(fid_v2_list) != 0:\n      value_list = fid_v2_list\n    for value in value_list:\n      shard = value % ps_num\n      key = f_cfg[\"feature_name\"] + \":\" + str(shard)\n      fid_offset_list.append((len(fid_map_t[key]), shard))\n      fid_map_t[key].append(value)\n      if value not in fid_map_unique_map[key]:\n        fid_map_unique_map[key][value] = len(fid_map_unique_map[key])\n        fid_map_unique_t[key].append(value)\n      fid_offset_list2.append((fid_map_unique_map[key][value], shard))\n\n  def get_offset_result(self,\n                        feature_name_sort,\n                        table_name_sort,\n                        ps_num,\n                        feature_cfg,\n                        table_cfg,\n                        fid_offset_map,\n                        fid_offset_map_unique,\n                        fid_map_t_in,\n                        fid_map_unique_t_in,\n                        sparse_feature_shared=set()):\n\n    fid_map_t = defaultdict(list)\n    fid_map_table_pre_offset = defaultdict(lambda: 0)\n    fid_map_feature_pre_offset = defaultdict(lambda: 0)\n    fid_map_unique_t = defaultdict(list)\n    fid_map_unique_table_pre_offset = defaultdict(lambda: 0)\n    fid_map_unique_feature_pre_offset = defaultdict(lambda: 0)\n    for table_name in table_name_sort:\n      t_cfg = table_cfg[table_name]\n      for ps_i in range(ps_num):\n        to_key = table_name + \":\" + str(ps_i)\n        table_pre_offset = fid_map_table_pre_offset[to_key]\n        unique_table_pre_offset = fid_map_unique_table_pre_offset[to_key]\n        for feature_name in t_cfg[\"feature_list\"]:\n          key = feature_name + \":\" + str(ps_i)\n          f_cfg = feature_cfg[feature_name]\n          dims_sum = f_cfg[\"dims_sum\"]\n          fid_map_t[to_key].extend(fid_map_t_in[key])\n          fid_map_feature_pre_offset[key] = table_pre_offset\n          table_pre_offset += dims_sum * len(fid_map_t_in[key])\n\n          fid_map_unique_t[to_key].extend(fid_map_unique_t_in[key])\n          fid_map_unique_feature_pre_offset[key] = unique_table_pre_offset\n          unique_table_pre_offset += dims_sum * len(fid_map_unique_t_in[key])\n\n    feature_offset_t = []\n    nfl_offset_t = []\n    fid_offset_list = []\n    fid_offset_list_unique = []\n\n    def rewrie_fid_offset(feature_name, pre_offset_dict, f_cfg, fid_list,\n                          fid_offset_list_):\n      for mix_offset in fid_list:\n        fid_offset = mix_offset[0]\n        shard = mix_offset[1]\n        feat_offset = self.get_pre_output_offset(shard, f_cfg)\n        if self.version == 3 or self.version == 4:\n          fid_offset *= f_cfg['dims_sum']\n          fid_offset += pre_offset_dict[feature_name + \":\" + str(shard)]\n        fid_offset_list_.append(feat_offset << 32 | fid_offset)\n        #logging.info(f\"xxxxx {pre_offset}\")\n\n    for sparse_key in feature_name_sort:\n      f_cfg = feature_cfg[sparse_key]\n      if sparse_key in sparse_feature_shared:\n        nfl_offset = len(feature_offset_t) | 1 << 31\n        #pass\n      else:\n        nfl_offset = len(feature_offset_t)\n      nfl_offset_t.append(nfl_offset)\n      if sparse_key not in fid_offset_map:\n        continue\n      for fid_list in fid_offset_map[sparse_key]:\n        feature_offset_t.append(len(fid_offset_list))\n        rewrie_fid_offset(sparse_key, fid_map_feature_pre_offset, f_cfg,\n                          fid_list, fid_offset_list)\n      for fid_list in fid_offset_map_unique[sparse_key]:\n        rewrie_fid_offset(sparse_key, fid_map_unique_feature_pre_offset, f_cfg,\n                          fid_list, fid_offset_list_unique)\n    feature_offset_t.append(len(fid_offset_list))\n    nfl_offset_t.append(len(feature_offset_t))\n\n    print('==' * 10 + \"fid_map_t\" + '==' * 10)\n    # print(fid_map_t)\n    print('==' * 10 + \"fid_map_unique_t\" + '==' * 10)\n    # print(fid_map_unique_t)\n    print('==' * 10 + \"fid_offset_map\" + '==' * 10)\n    # print(fid_offset_map)\n    return nfl_offset_t, feature_offset_t, fid_offset_list, fid_offset_list_unique, fid_map_t, fid_map_unique_t\n\n  def diff_test(self, input_type, parse_func, input_str_list, feature_name_sort,\n                table_name_sort, ps_num, feature_cfg, table_cfg, feature_cfgs,\n                sparse_features, dense_features, extra_features, nfl_offset_t,\n                feature_offset_t, fid_offset_list, fid_offset_list_unique,\n                fid_map_t, fid_map_row_split_t, fid_map_unique_t,\n                fid_map_row_split_unique_t):\n\n    if self.version == 4:\n      fid_list_table_row_length_t = [0] * (ps_num * len(table_name_sort))\n      fid_list_shard_row_lenth_t = [0] * ps_num\n      fid_list_emb_row_lenth_t = [0] * (ps_num * len(table_name_sort))\n\n      fid_list_table_row_length_unique_t = [0] * (ps_num * len(table_name_sort))\n      fid_list_shard_row_lenth_unique_t = [0] * ps_num\n      fid_list_emb_row_lenth_unique_t = [0] * (ps_num * len(table_name_sort))\n\n      fid_map_t2, fid_map_row_split_t2, fid_map_unique_t2, fid_map_row_split_unique_t2 = [], [], [], []\n\n      table_count = len(table_name_sort)\n      for ps_i in range(ps_num):\n        for table_i in range(table_count):\n          table_name = table_name_sort[table_i]\n\n          #t_cfg = table_cfg[table_name]\n          to_key = table_name + \":\" + str(ps_i)\n          fid_map_t2.extend(fid_map_t[to_key])\n          fid_map_row_split_t2.extend(fid_map_row_split_t[to_key])\n          fid_list_table_row_length_t[ps_i * table_count + table_i] = len(\n              fid_map_t[to_key])\n          fid_list_shard_row_lenth_t[ps_i] += len(fid_map_t[to_key])\n\n          fid_map_unique_t2.extend(fid_map_unique_t[to_key])\n          fid_map_row_split_unique_t2.extend(fid_map_row_split_unique_t[to_key])\n          fid_list_table_row_length_unique_t[ps_i * table_count +\n                                             table_i] = len(\n                                                 fid_map_unique_t[to_key])\n          fid_list_shard_row_lenth_unique_t[ps_i] += len(\n              fid_map_unique_t[to_key])\n\n          emb_dim_sum, emb_dim_sum_unique = 0, 0\n          t_cfg = table_cfg[table_name]\n          assert len(t_cfg[\"feature_list\"]) + 1 == len(\n              fid_map_row_split_t[to_key])\n          for feature_idx in range(len(t_cfg[\"feature_list\"])):\n            feature_name = t_cfg[\"feature_list\"][feature_idx]\n            f_cfg = feature_cfg[feature_name]\n            dims_sum = f_cfg[\"dims_sum\"]\n            emb_dim_sum += dims_sum * (\n                fid_map_row_split_t[to_key][feature_idx + 1] -\n                fid_map_row_split_t[to_key][feature_idx])\n            emb_dim_sum_unique += dims_sum * (\n                fid_map_row_split_unique_t[to_key][feature_idx + 1] -\n                fid_map_row_split_unique_t[to_key][feature_idx])\n\n          fid_list_emb_row_lenth_t[ps_i * table_count + table_i] = \\\n              emb_dim_sum\n          fid_list_emb_row_lenth_unique_t[ps_i * table_count + table_i] = \\\n              emb_dim_sum_unique\n\n      fid_map_t, fid_map_row_split_t, fid_map_unique_t, fid_map_row_split_unique_t = \\\n        fid_map_t2, fid_map_row_split_t2, fid_map_unique_t2, fid_map_row_split_unique_t2\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      input_placeholder = tf.compat.v1.placeholder(dtype=tf.string,\n                                                   shape=(None,))\n      parsed_results_base, parsed_results = parse_func(input_placeholder)\n      example_batch_varint = parsed_results.pop(\n          ParserCtx.sharding_sparse_fids_sparse_features_key)\n\n      parallel_flag_list = [0, 1]\n      fid_map_list = []\n      fid_map_unique_list = []\n      for parallel_flag in parallel_flag_list:\n        fid_map, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size, \\\n        fid_map_row_split, fid_map_row_split_size, fid_list_emb_row_lenth, \\\n        fid_list_table_row_length, fid_list_shard_row_lenth = sharding_sparse_fids(\n            example_batch_varint,\n            ps_num,\n            feature_cfgs,\n            False,\n            input_type,\n            parallel_flag,\n            version=self.version)\n        fid_map_list.append([\n            fid_map, fid_offset, feature_offset, nfl_offset, fid_map_row_split\n        ])\n        fid_map_unique, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size, \\\n        fid_map_unique_row_split, fid_map_unique_row_split_size, fid_list_emb_row_lenth_unique, \\\n        fid_list_table_row_length_unique, fid_list_shard_row_lenth_unique = sharding_sparse_fids(\n            example_batch_varint,\n            ps_num,\n            feature_cfgs,\n            True,\n            input_type,\n            parallel_flag,\n            version=self.version)\n        fid_map_unique_list.append([\n            fid_map_unique, fid_offset, feature_offset, nfl_offset,\n            fid_map_unique_row_split\n        ])\n\n      with self.session(config=config) as sess:\n\n        parsed_results_base1, parsed_results1 = sess.run(\n            fetches=[parsed_results_base, parsed_results],\n            feed_dict={input_placeholder: input_str_list})\n\n        def diff(k, a, b, sort=False):\n\n          if not isinstance(a[0], list) and sort:\n            a.sort()\n            b.sort()\n          #print(\"diff:a {} {}\".format(k, a), flush=True)\n          #print(\"diff:b {} {}\".format(k, b), flush=True)\n          assert (len(a) == len(b))\n          if (len(a) == 0):\n            return\n          for i in range(len(a)):\n            if isinstance(a[i], list):\n              assert isinstance(b[i], list)\n              diff(k + \"/\" + str(i), a[i], b[i], sort)\n            else:\n              assert (a[i] == b[i]), f\"{i}: {a[i]} / {b[i]}\"\n\n        #print('==' * 10 + \"parsed_results_base1\" + '==' * 10, flush=True)\n        #print('==' * 10 + \"parsed_results1\" + '==' * 10, flush=True)\n        # .numpy()\n        for k, v in parsed_results_base1.items():\n          if k in sparse_features:\n            continue\n          if k in dense_features + extra_features:\n            if k not in parsed_results1:\n              print(\"no find {} in parse_example_batch_v2\".format(k))\n              assert (False)\n            diff(k, v.tolist(), parsed_results1[k].tolist())  #.numpy()\n          else:\n            print(\"no need {}\".format(k), flush=True)\n            assert (False)\n        for k, v in parsed_results1.items():\n          if k not in dense_features + extra_features:\n            print(\"no need {}\".format(k), flush=True)\n            assert (False)\n\n        for fid_map_index in range(len(parallel_flag_list)):\n          fid_map = fid_map_list[fid_map_index]\n          fid_map_unique = fid_map_unique_list[fid_map_index]\n          fid_map1_list, fid_map_unique1_list = sess.run(\n              fetches=[fid_map, fid_map_unique],\n              feed_dict={input_placeholder: input_str_list})\n\n          #print('==' * 10 + \"fid_map1\" + '==' * 10, flush=True)\n          #print(fid_map1, flush=True)\n          #print('==' * 10 + \"fid_map_unique1\" + '==' * 10, flush=True)\n          #print(fid_map_unique1, flush=True)\n          \n          #print('==' * 10 + \"fid_offset2\" + '==' * 10, flush=True)\n          #print(list(fid_offset2), flush=True)\n          #print('==' * 10 + \"fid_offset_list_unique\" + '==' * 10, flush=True)\n          #print(list(fid_offset_list_unique), flush=True)\n          fid_map1, fid_offset1, feature_offset1, nfl_offset1, fid_map_row_split1 = fid_map1_list\n          fid_map2, fid_offset2, feature_offset2, nfl_offset2, fid_map_unique_row_split1 = fid_map_unique1_list\n          print('==' * 10 + \"diff fidoffset \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n\n          diff(\"nfl_offset\", nfl_offset1, nfl_offset_t)\n          diff(\"nfl_offset2\", nfl_offset2, nfl_offset_t)\n          diff(\"feature_offset\", feature_offset1, feature_offset_t)\n          diff(\"feature_offset2\", feature_offset2, feature_offset_t)\n          diff(\"fid_offset2\", fid_offset2, fid_offset_list_unique)\n          diff(\"fid_offset\", fid_offset1, fid_offset_list)\n\n          if isinstance(fid_map_t, Dict):\n            assert (len(fid_map_t) == len(fid_map1))\n            assert (len(fid_map_unique_t) == len(fid_map2))\n\n          def fid_diff(a, b):\n            if isinstance(a, Dict):\n              for k, v in a.items():\n                assert (k in b)\n                diff(k, v.tolist(), b[k])  #.numpy()\n              for k, v in b.items():\n                assert (k in a)\n            else:\n              diff(\"main\", a.tolist(), b)\n\n          print('==' * 10 + \"diff fid_map1 \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          #print(f\"xxxx fid_map1 {fid_map1} \", flush=True)\n          #print(f\"xxxx fid_map_t {fid_map_t} \", flush=True)\n          fid_diff(fid_map1, fid_map_t)\n          #print(f\"xxxx fid_map_row_split1 {fid_map_row_split1}\", flush=True)\n          #print(f\"xxxx fid_map_row_split_t {fid_map_row_split_t}\", flush=True)\n          fid_diff(fid_map_row_split1, fid_map_row_split_t)\n          print('==' * 10 + \"diff fid_map_unique1 \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          #print(f\"xxxx fid_map2 {fid_map2} \", flush=True)\n          #print(f\"xxxx fid_map_unique_t {fid_map_unique_t} \", flush=True)\n          #print(f\"xxxx fid_map_unique_row_split {fid_map_unique_row_split} \",\n          #      flush=True)\n          fid_diff(fid_map2, fid_map_unique_t)\n          fid_diff(fid_map_unique_row_split1, fid_map_row_split_unique_t)\n\n        if self.version == 4:\n          fid_list_table_row_length_1, fid_list_shard_row_lenth_1, \\\n             fid_list_table_row_length_unique_1, fid_list_shard_row_lenth_unique_1, \\\n              fid_list_emb_row_lenth_1, fid_list_emb_row_lenth_unique_1 = \\\n            sess.run(fetches=[fid_list_table_row_length, fid_list_shard_row_lenth, \\\n                fid_list_table_row_length_unique, fid_list_shard_row_lenth_unique, \\\n                  fid_list_emb_row_lenth, fid_list_emb_row_lenth_unique], \\\n              feed_dict={input_placeholder: input_str_list})\n            \n          diff(\"\", fid_list_table_row_length_1.tolist(),\n               fid_list_table_row_length_t)\n          diff(\"\", fid_list_shard_row_lenth_1.tolist(),\n               fid_list_shard_row_lenth_t)\n          diff(\"\", fid_list_emb_row_lenth_1.tolist(), fid_list_emb_row_lenth_t)\n          diff(\"\", fid_list_table_row_length_unique_1.tolist(),\n               fid_list_table_row_length_unique_t)\n          diff(\"\", fid_list_shard_row_lenth_unique_1.tolist(),\n               fid_list_shard_row_lenth_unique_t)\n          diff(\"\", fid_list_emb_row_lenth_unique_1.tolist(),\n               fid_list_emb_row_lenth_unique_t)\n\n  def testExampleBatchSharding(self):\n    file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n\n    sparse_features = list(features.keys())\n    with open(file_name, 'rb') as stream:\n      stream.read(8)  # strip lagrangex_header\n      size = unpack(\"<Q\", stream.read(8))[0]\n      eb_str = stream.read(size)\n      example_batch = ExampleBatch()\n      example_batch.ParseFromString(eb_str)\n      #add shared\n      named_feature_list = example_batch.named_feature_list.add()\n      named_feature_list.type = FeatureListType.SHARED\n      named_feature_list.id = 990\n      named_feature_list.name = \"test_shared1\"\n      feature = named_feature_list.feature.add()\n      feature.fid_v2_list.value.extend(gen_fids_v2(990, 10))\n      named_feature_list = example_batch.named_feature_list.add()\n      named_feature_list.type = FeatureListType.SHARED\n      named_feature_list.id = 991\n      named_feature_list.name = \"test_shared2\"\n      feature = named_feature_list.feature.add()\n      feature.fid_v1_list.value.extend(gen_fids_v1(991, 10))\n      named_feature_list = example_batch.named_feature_list.add()\n      named_feature_list.type = FeatureListType.SHARED\n      named_feature_list.id = 991\n      named_feature_list.name = \"test_shared3\"\n      feature = named_feature_list.feature.add()\n\n      eb_str = example_batch.SerializeToString()\n\n    sparse_features += [\"test_shared1\", \"test_shared2\", \"test_shared3\"]\n    dense_features = ['label']\n    dense_feature_shapes = [2]\n    dense_feature_types = [tf.float32]\n    extra_features = ['uid', 'req_time', 'item_id']\n    extra_feature_shapes = [1, 1, 1]\n\n    print('==' * 10 + \"sparse_features\" + '==' * 10)\n    #print(sparse_features)\n\n    feature_cfgs = FeatureConfigs()\n    index = 0\n    ps_num = 3\n    for sparse_key in sparse_features:\n      cfg = FeatureConfig()\n      cfg.table = 'table_{}'.format(index % 3)\n      cfg.slice_dims.extend(random.sample(range(1, 6), 3))\n      #print(sparse_key, cfg.table)\n      feature_cfgs.feature_configs[sparse_key].CopyFrom(cfg)\n      index += 1\n\n    feature_cfg, table_cfg, feature_name_sort, table_name_sort = self.get_feature_cfg(\n        feature_cfgs, ps_num)\n\n    #f_goods_title_terms\n    fid_map_t = defaultdict(list)\n    fid_map_unique_t = defaultdict(list)\n    fid_map_unique_map = defaultdict(dict)\n\n    fid_offset_map = defaultdict(list)\n    fid_offset_map_unique = defaultdict(list)\n\n    fid_map_row_split_t = defaultdict(list)\n    fid_map_row_split_unique_t = defaultdict(list)\n\n    sparse_feature_shared = set()\n    example_batch_feature_map = {}\n    for named_feature_list in example_batch.named_feature_list:\n      if named_feature_list.name not in feature_cfgs.feature_configs or \\\n              named_feature_list.name not in feature_name_sort:\n        continue\n      example_batch_feature_map[named_feature_list.name] = named_feature_list\n\n    for sparse_key in feature_name_sort:\n      f_cfg = feature_cfg[sparse_key]\n      table_name = f_cfg[\"table_name\"]\n      t_cfg = table_cfg[table_name]\n      self.fill_row_split(ps_num, t_cfg, fid_map_t, fid_map_row_split_t)\n      self.fill_row_split(ps_num, t_cfg, fid_map_unique_t,\n                          fid_map_row_split_unique_t)\n      if sparse_key not in example_batch_feature_map:\n        continue\n      named_feature_list = example_batch_feature_map[sparse_key]\n      table_index = table_cfg[table_name][\"table_index\"]\n      if named_feature_list.type == FeatureListType.SHARED:\n        sparse_feature_shared.add(named_feature_list.name)\n        feature = named_feature_list.feature[0]\n        fid_offset_list = []\n        fid_offset_list2 = []\n        self.handle_feature(feature.fid_v1_list.value,\n                            feature.fid_v2_list.value, f_cfg, t_cfg, ps_num,\n                            fid_offset_list, fid_offset_list2, fid_map_t,\n                            fid_map_unique_map, fid_map_unique_t)\n        fid_offset_map[named_feature_list.name].append(fid_offset_list)\n        fid_offset_map_unique[named_feature_list.name].append(fid_offset_list2)\n      else:\n        for feature in named_feature_list.feature:\n          fid_offset_list = []\n          fid_offset_list2 = []\n          self.handle_feature(feature.fid_v1_list.value,\n                              feature.fid_v2_list.value, f_cfg, t_cfg, ps_num,\n                              fid_offset_list, fid_offset_list2, fid_map_t,\n                              fid_map_unique_map, fid_map_unique_t)\n          fid_offset_map[named_feature_list.name].append(fid_offset_list)\n          fid_offset_map_unique[named_feature_list.name].append(\n              fid_offset_list2)\n    for table_name in table_name_sort:\n      t_cfg = table_cfg[table_name]\n      self.fill_row_split(ps_num, t_cfg, fid_map_t, fid_map_row_split_t)\n      self.fill_row_split(ps_num, t_cfg, fid_map_unique_t,\n                          fid_map_row_split_unique_t)\n\n    nfl_offset_t, feature_offset_t, fid_offset_list, fid_offset_list_unique, \\\n      fid_map_t, fid_map_unique_t = self.get_offset_result(feature_name_sort,\n                        table_name_sort,\n                        ps_num,\n                        feature_cfg,\n                        table_cfg,\n                        fid_offset_map,\n                        fid_offset_map_unique,\n                        fid_map_t,\n                        fid_map_unique_t,\n                        sparse_feature_shared\n    )\n\n    #print('==' * 10 + \"example_batch\" + '==' * 10)\n    #print(example_batch)\n\n    def parse_func(input_placeholder):\n      get_default_parser_ctx().enable_fused_layout = False\n      parsed_results_base = parse_example_batch(\n          input_placeholder,\n          sparse_features=sparse_features,\n          dense_features=dense_features,\n          dense_feature_shapes=dense_feature_shapes,\n          dense_feature_types=dense_feature_types,\n          extra_features=extra_features,\n          extra_feature_shapes=extra_feature_shapes)\n      get_default_parser_ctx().enable_fused_layout = True\n      parsed_results = parse_example_batch(\n          input_placeholder,\n          sparse_features=[],\n          dense_features=dense_features,\n          dense_feature_shapes=dense_feature_shapes,\n          dense_feature_types=dense_feature_types,\n          extra_features=extra_features,\n          extra_feature_shapes=extra_feature_shapes)\n      return parsed_results_base, parsed_results\n\n    self.diff_test(\"examplebatch\", parse_func, [eb_str], feature_name_sort,\n                   table_name_sort, ps_num, feature_cfg, table_cfg,\n                   feature_cfgs, sparse_features, dense_features,\n                   extra_features, nfl_offset_t, feature_offset_t,\n                   fid_offset_list, fid_offset_list_unique, fid_map_t,\n                   fid_map_row_split_t, fid_map_unique_t,\n                   fid_map_row_split_unique_t)\n    #assert (False)\n\n  def testExampleSharding(self):\n    sparse_features_set = set()\n    dense_features = ['label']\n    dense_feature_shapes = [2]\n    dense_feature_types = [tf.float32]\n    extra_features = ['uid', 'req_time', 'item_id', 'actions']\n    extra_feature_shapes = [1, 1, 1, 1]\n    example_str_list = []\n    example_list = []\n    file_name = \"monolith/native_training/data/training_instance/example.pb\"\n    feature_fid_map = defaultdict(list)\n    with open(file_name, 'rb') as stream:\n      while (True):\n        if len(example_str_list) > 10:\n          break\n        try:\n          stream.read(8)  # strip has_sort_id\n          stream.read(8)  # strip kafka_dump\n          size = unpack(\"<Q\", stream.read(8))[0]\n          example_str = stream.read(size)\n\n          example = Example()\n          example.ParseFromString(example_str)\n\n          for feature_index in range(1, 3):\n            named_feature = example.named_feature.add()\n            named_feature.name = 'fc_slot_9999{}'.format(feature_index)\n            fid_list = gen_fids_v2(9999 + feature_index, 10)\n            named_feature.feature.fid_v2_list.value.extend(fid_list)\n            named_feature.feature.fid_v2_list.value.extend(fid_list)\n\n          for named_feature in example.named_feature:\n            feature_fids = feature_fid_map[named_feature.name]\n            value_list = []\n            if len(named_feature.feature.fid_v1_list.value) != 0:\n              for fid in named_feature.feature.fid_v1_list.value:\n                slot_id, fid_v2 = self.fid_v1_to_v2(fid)\n                value_list.append(fid_v2)\n            elif len(named_feature.feature.fid_v2_list.value) != 0:\n              value_list = named_feature.feature.fid_v2_list.value\n            '''\n            for k, v in feature_fid_map.items():\n              if k != named_feature.name:\n                for fid in value_list:\n                  if fid in v:\n                    print(\n                        f\"xxxxx hit fid {fid} {named_feature.name} in other feat {k}\",\n                        flush=False)\n            feature_fids.extend(value_list)\n            '''\n\n          #print('xxxxx example_str_list,', example, flush=True)\n          for named_feature in example.named_feature:\n            if \"fc_slot_\" in named_feature.name:\n              sparse_features_set.add(named_feature.name)\n          example_list.append(example)\n          example_str_list.append(example.SerializeToString())\n        except:\n          break\n\n    #for k, v in feature_fid_map.items():\n    #  print(f\"xxxxx show fid  {k} {v}\", flush=False)\n\n    sparse_features = sorted(sparse_features_set)\n    #print(sparse_features, flush=True)\n\n    feature_cfgs = FeatureConfigs()\n    index = 0\n    ps_num = 3\n    table_name_index_map = {}\n    for sparse_key in sparse_features:\n      cfg = FeatureConfig()\n      cfg.table = 'table_{}'.format(index % 3)\n      cfg.slice_dims.extend(random.sample(range(1, 6), 3))\n      table_name_index_map[cfg.table] = -1\n      feature_cfgs.feature_configs[sparse_key].CopyFrom(cfg)\n      index += 1\n\n    feature_cfg, table_cfg, feature_name_sort, table_name_sort = self.get_feature_cfg(\n        feature_cfgs, ps_num)\n\n    fid_map_t = defaultdict(list)\n    fid_map_unique_t = defaultdict(list)\n    fid_map_unique_map = defaultdict(dict)\n\n    fid_offset_map = defaultdict(list)\n    fid_offset_map_unique = defaultdict(list)\n\n    fid_map_row_split_t = defaultdict(list)\n    fid_map_row_split_unique_t = defaultdict(list)\n\n    for sparse_key in feature_name_sort:\n      f_cfg = feature_cfg[sparse_key]\n      table_name = f_cfg[\"table_name\"]\n      t_cfg = table_cfg[table_name]\n      self.fill_row_split(ps_num, t_cfg, fid_map_t, fid_map_row_split_t)\n      self.fill_row_split(ps_num, t_cfg, fid_map_unique_t,\n                          fid_map_row_split_unique_t)\n      for example in example_list:\n        find_named_feature = None\n        for named_feature in example.named_feature:\n          if named_feature.name == sparse_key:\n            find_named_feature = named_feature\n            break\n        fid_offset_list = []\n        fid_offset_list2 = []\n        if find_named_feature:\n          table_index = table_name_index_map[table_name]\n          self.handle_feature(find_named_feature.feature.fid_v1_list.value,\n                              find_named_feature.feature.fid_v2_list.value,\n                              f_cfg, t_cfg, ps_num, fid_offset_list,\n                              fid_offset_list2, fid_map_t, fid_map_unique_map,\n                              fid_map_unique_t)\n        fid_offset_map[sparse_key].append(fid_offset_list)\n        fid_offset_map_unique[sparse_key].append(fid_offset_list2)\n    for table_name in table_name_sort:\n      t_cfg = table_cfg[table_name]\n      self.fill_row_split(ps_num, t_cfg, fid_map_t, fid_map_row_split_t)\n      self.fill_row_split(ps_num, t_cfg, fid_map_unique_t,\n                          fid_map_row_split_unique_t)\n\n    nfl_offset_t, feature_offset_t, fid_offset_list, fid_offset_list_unique, \\\n      fid_map_t, fid_map_unique_t = self.get_offset_result(feature_name_sort,\n                                                            table_name_sort,\n                                                            ps_num,\n                                                            feature_cfg,\n                                                            table_cfg,\n                                                            fid_offset_map,\n                                                            fid_offset_map_unique,\n                                                            fid_map_t,\n                                                            fid_map_unique_t\n                                                         )\n\n    def parse_func(input_placeholder):\n      get_default_parser_ctx().enable_fused_layout = False\n      parsed_results_base = parse_examples(\n          input_placeholder,\n          sparse_features=sparse_features,\n          dense_features=dense_features,\n          dense_feature_shapes=dense_feature_shapes,\n          dense_feature_types=dense_feature_types,\n          extra_features=extra_features,\n          extra_feature_shapes=extra_feature_shapes)\n      get_default_parser_ctx().enable_fused_layout = True\n      parsed_results = parse_examples(input_placeholder,\n                                      sparse_features=[],\n                                      dense_features=dense_features,\n                                      dense_feature_shapes=dense_feature_shapes,\n                                      dense_feature_types=dense_feature_types,\n                                      extra_features=extra_features,\n                                      extra_feature_shapes=extra_feature_shapes)\n      return parsed_results_base, parsed_results\n\n    #example_tensor = tf.convert_to_tensor(example_str_list)\n    self.diff_test(\"example\", parse_func, example_str_list, feature_name_sort,\n                   table_name_sort, ps_num, feature_cfg, table_cfg,\n                   feature_cfgs, sparse_features, dense_features,\n                   extra_features, nfl_offset_t, feature_offset_t,\n                   fid_offset_list, fid_offset_list_unique, fid_map_t,\n                   fid_map_row_split_t, fid_map_unique_t,\n                   fid_map_row_split_unique_t)\n    #assert (False)\n\n  def testInstanceSharding(self):\n    fidv1_features = [1, 200, 3, 5, 9, 203, 205]\n    fidv2_features = [\"fc_v2_1\", \"fc_v2_2\", \"fc_v2_3\"]\n    dense_features = ['label']\n    dense_feature_shapes = [2]\n    dense_feature_types = [tf.float32]\n    extra_features = ['uid', 'req_time', 'item_id', 'actions']\n    extra_feature_shapes = [1, 1, 1, 2]\n\n    instance_str_list = []\n    instance_list = []\n    while (len(instance_str_list) < 128):\n      instance = gen_instance(\n          fidv1_features=fidv1_features,\n          fidv2_features=[],\n          dense_features=[FeatureMeta('label', shape=2, dtype=tf.float32)],\n          extra_features=[\n              FeatureMeta('actions', shape=2),\n              FeatureMeta('uid'),\n              FeatureMeta('req_time', dtype=tf.int32),\n              FeatureMeta('item_id'),\n          ])\n      #print(\"aaaaaa:\", instance)\n      instance_list.append(instance)\n      instance_str_list.append(instance.SerializeToString())\n\n      instance2 = deepcopy(instance)\n      for slot, feature_name in enumerate(fidv2_features):\n        feature = instance2.feature.add()\n        feature.name = feature_name\n        feature.fid.extend(gen_fids_v2(1000 + slot, 10))\n      instance_list.append(instance2)\n      instance_str_list.append(instance2.SerializeToString())\n\n      instance3 = deepcopy(instance2)\n      del instance3.fid[:]\n      instance_list.append(instance3)\n      instance_str_list.append(instance3.SerializeToString())\n\n    def gen_slot_feature_name(slot_id):\n      return f\"slot_{slot_id}\"\n\n    sparse_features = sorted(\n        fidv2_features +\n        [gen_slot_feature_name(slot_id) for slot_id in fidv1_features])\n    print(sparse_features, flush=True)\n\n    feature_cfgs = FeatureConfigs()\n    index = 0\n    ps_num = 3\n    table_name_index_map = {}\n    for sparse_key in sparse_features:\n      cfg = FeatureConfig()\n      cfg.table = 'table_{}'.format(index % 3)\n      cfg.slice_dims.extend(random.sample(range(1, 6), 3))\n      table_name_index_map[cfg.table] = -1\n      feature_cfgs.feature_configs[sparse_key].CopyFrom(cfg)\n      index += 1\n\n    feature_cfg, table_cfg, feature_name_sort, table_name_sort = self.get_feature_cfg(\n        feature_cfgs, ps_num)\n\n    fid_map_t = defaultdict(list)\n    fid_map_unique_t = defaultdict(list)\n    fid_map_unique_map = defaultdict(dict)\n\n    intance_tmp_dict = defaultdict(list)\n    for instance in instance_list:\n      fid_v2_list = defaultdict(list)\n      for fid in instance.fid:\n        slot_id, fid_v2 = self.fid_v1_to_v2(fid)\n        sparse_key = gen_slot_feature_name(slot_id)\n        if sparse_key not in feature_name_sort:\n          continue\n        fid_v2_list[sparse_key].append(fid_v2)\n\n      for feature in instance.feature:\n        sparse_key = feature.name\n        if sparse_key not in feature_name_sort:\n          continue\n        fid_v2_list[sparse_key] = feature.fid\n\n      for sparse_key in feature_name_sort:\n        if sparse_key not in fid_v2_list:\n          fid_v2_list[sparse_key] = []\n\n        fid_list = fid_v2_list[sparse_key]\n        intance_tmp_dict[sparse_key].append(fid_list)\n\n    fid_offset_map = defaultdict(list)\n    fid_offset_map_unique = defaultdict(list)\n    fid_map_row_split_t = defaultdict(list)\n    fid_map_row_split_unique_t = defaultdict(list)\n\n    for sparse_key in feature_name_sort:\n      f_cfg = feature_cfg[sparse_key]\n      table_name = f_cfg[\"table_name\"]\n      t_cfg = table_cfg[table_name]\n      self.fill_row_split(ps_num, t_cfg, fid_map_t, fid_map_row_split_t)\n      self.fill_row_split(ps_num, t_cfg, fid_map_unique_t,\n                          fid_map_row_split_unique_t)\n      for fid_list in intance_tmp_dict[sparse_key]:\n        fid_offset_list = []\n        fid_offset_list2 = []\n        table_index = table_name_index_map[table_name]\n        self.handle_feature([], fid_list, f_cfg, t_cfg, ps_num, fid_offset_list,\n                            fid_offset_list2, fid_map_t, fid_map_unique_map,\n                            fid_map_unique_t)\n        #print(\"bbbbb {} {} {}\".format(sparse_key, fid_offset_list,\n        #                              fid_offset_list2))\n        fid_offset_map[sparse_key].append(fid_offset_list)\n        fid_offset_map_unique[sparse_key].append(fid_offset_list2)\n    for table_name in table_name_sort:\n      t_cfg = table_cfg[table_name]\n      self.fill_row_split(ps_num, t_cfg, fid_map_t, fid_map_row_split_t)\n      self.fill_row_split(ps_num, t_cfg, fid_map_unique_t,\n                          fid_map_row_split_unique_t)\n\n    nfl_offset_t, feature_offset_t, fid_offset_list, fid_offset_list_unique, \\\n      fid_map_t, fid_map_unique_t = self.get_offset_result(feature_name_sort,\n                                                            table_name_sort,\n                                                            ps_num,\n                                                            feature_cfg,\n                                                            table_cfg,\n                                                            fid_offset_map,\n                                                            fid_offset_map_unique,\n                                                            fid_map_t,\n                                                            fid_map_unique_t\n                                                          )\n\n    def parse_func(input_placeholder):\n      get_default_parser_ctx().enable_fused_layout = False\n      parsed_results_base = parse_instances(\n          input_placeholder,\n          fidv1_features=fidv1_features,\n          fidv2_features=fidv2_features,\n          dense_features=dense_features,\n          dense_feature_shapes=dense_feature_shapes,\n          dense_feature_types=dense_feature_types,\n          extra_features=extra_features,\n          extra_feature_shapes=extra_feature_shapes)\n      get_default_parser_ctx().enable_fused_layout = True\n      parsed_results = parse_instances(\n          input_placeholder,\n          fidv1_features=fidv1_features,\n          fidv2_features=fidv2_features,\n          dense_features=dense_features,\n          dense_feature_shapes=dense_feature_shapes,\n          dense_feature_types=dense_feature_types,\n          extra_features=extra_features,\n          extra_feature_shapes=extra_feature_shapes)\n      return parsed_results_base, parsed_results\n\n    self.diff_test(\"instance\", parse_func, instance_str_list, feature_name_sort,\n                   table_name_sort, ps_num, feature_cfg, table_cfg,\n                   feature_cfgs, sparse_features, dense_features,\n                   extra_features, nfl_offset_t, feature_offset_t,\n                   fid_offset_list, fid_offset_list_unique, fid_map_t,\n                   fid_map_row_split_t, fid_map_unique_t,\n                   fid_map_row_split_unique_t)\n    #assert (False)\n\n\nclass DataOpsV2TestFitPreV2(DataOpsV2Test):\n\n  def __init__(self, *args, **kwargs):\n    super(DataOpsV2TestFitPreV2, self).__init__(*args, **kwargs)\n    self.version = 2\n\n\nclass DataOpsV2Testv4(DataOpsV2Test):\n\n  def __init__(self, *args, **kwargs):\n    super(DataOpsV2Testv4, self).__init__(*args, **kwargs)\n    self.version = 4\n\n\nclass DataOpsV2TestFitPre(tf.test.TestCase):  #DataOpsV2Test\n\n  def __init__(self, *args, **kwargs):\n    super(DataOpsV2TestFitPre, self).__init__(*args, **kwargs)\n\n  def testExampleBatchSharding(self):\n    file_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\n\n    sparse_features = list(features.keys())\n    with open(file_name, 'rb') as stream:\n      stream.read(8)  # strip lagrangex_header\n      size = unpack(\"<Q\", stream.read(8))[0]\n      eb_str = stream.read(size)\n      example_batch = ExampleBatch()\n      example_batch.ParseFromString(eb_str)\n      #add shared\n      named_feature_list = example_batch.named_feature_list.add()\n      named_feature_list.type = FeatureListType.SHARED\n      named_feature_list.id = 990\n      named_feature_list.name = \"test_shared1\"\n      feature = named_feature_list.feature.add()\n      feature.fid_v2_list.value.extend(gen_fids_v2(990, 10))\n      named_feature_list = example_batch.named_feature_list.add()\n      named_feature_list.type = FeatureListType.SHARED\n      named_feature_list.id = 991\n      named_feature_list.name = \"test_shared2\"\n      feature = named_feature_list.feature.add()\n      feature.fid_v1_list.value.extend(gen_fids_v1(991, 10))\n      named_feature_list = example_batch.named_feature_list.add()\n      named_feature_list.type = FeatureListType.SHARED\n      named_feature_list.id = 991\n      named_feature_list.name = \"test_shared3\"\n      feature = named_feature_list.feature.add()\n\n      eb_str = example_batch.SerializeToString()\n\n    sparse_features += [\"test_shared1\", \"test_shared2\", \"test_shared3\"]\n    dense_features = ['label']\n    dense_feature_shapes = [2]\n    dense_feature_types = [tf.float32]\n    extra_features = ['uid', 'req_time', 'item_id']\n    extra_feature_shapes = [1, 1, 1]\n\n    print('==' * 10 + \"sparse_features\" + '==' * 10)\n    print(sparse_features)\n\n    feature_cfgs = FeatureConfigs()\n    index = 0\n    ps_num = 3\n    table_name_index_map = {}\n    for sparse_key in sparse_features:\n      cfg = FeatureConfig()\n      cfg.table = 'table_{}'.format(index % 3)\n      table_name_index_map[cfg.table] = -1\n      feature_cfgs.feature_configs[sparse_key].CopyFrom(cfg)\n      index += 1\n\n    sparse_features.sort()\n    table_name_list = list(table_name_index_map.keys())\n    table_name_list.sort()\n    for index, table_name in enumerate(table_name_list):\n      table_name_index_map[table_name] = index\n\n    #f_goods_title_terms\n    fid_map_t = defaultdict(list)\n    fid_map_unique_t = defaultdict(list)\n    fid_map_unique_map = defaultdict(dict)\n    mask = (1 << 48) - 1\n\n    def handle_feature(feature, table_name, table_index, fid_offset_list,\n                       fid_offset_list2):\n      value_list = []\n      if len(feature.fid_v1_list.value) != 0:\n        for fid in feature.fid_v1_list.value:\n          slot_id = (fid >> 54)\n          fid_v2 = ((slot_id << 48) | (mask & fid))\n          value_list.append(fid_v2)\n      elif len(feature.fid_v2_list.value) != 0:\n        value_list = feature.fid_v2_list.value\n      for value in value_list:\n        shard = value % ps_num\n        key = table_name + \":\" + str(shard)\n        fid_offset = (table_index * ps_num + shard) << 32\n        fid_offset_list.append(fid_offset | len(fid_map_t[key]))\n        fid_map_t[key].append(value)\n        if value not in fid_map_unique_map[key]:\n          fid_map_unique_map[key][value] = len(fid_map_unique_map[key])\n          fid_map_unique_t[key].append(value)\n        fid_offset_list2.append(fid_offset | fid_map_unique_map[key][value])\n\n    fid_offset_map = defaultdict(list)\n    fid_offset_map_unique = defaultdict(list)\n    sparse_feature_shared = set()\n    example_batch_feature_map = {}\n    for named_feature_list in example_batch.named_feature_list:\n      if named_feature_list.name not in feature_cfgs.feature_configs or \\\n              named_feature_list.name not in sparse_features:\n        continue\n      example_batch_feature_map[named_feature_list.name] = named_feature_list\n\n    for sparse_key in sparse_features:\n      if sparse_key not in example_batch_feature_map:\n        continue\n      named_feature_list = example_batch_feature_map[sparse_key]\n      table_name = feature_cfgs.feature_configs[named_feature_list.name].table\n      table_index = table_name_index_map[table_name]\n      if named_feature_list.type == FeatureListType.SHARED:\n        sparse_feature_shared.add(named_feature_list.name)\n        feature = named_feature_list.feature[0]\n        fid_offset_list = []\n        fid_offset_list2 = []\n        handle_feature(feature, table_name, table_index, fid_offset_list,\n                       fid_offset_list2)\n        fid_offset_map[named_feature_list.name].append(fid_offset_list)\n        fid_offset_map_unique[named_feature_list.name].append(fid_offset_list2)\n      else:\n        for feature in named_feature_list.feature:\n          fid_offset_list = []\n          fid_offset_list2 = []\n          handle_feature(feature, table_name, table_index, fid_offset_list,\n                         fid_offset_list2)\n          fid_offset_map[named_feature_list.name].append(fid_offset_list)\n          fid_offset_map_unique[named_feature_list.name].append(\n              fid_offset_list2)\n\n    feature_offset_t = []\n    nfl_offset_t = []\n    fid_offset_list = []\n    fid_offset_list_unique = []\n    for sparse_key in sparse_features:\n      if sparse_key in sparse_feature_shared:\n        nfl_offset = len(feature_offset_t) | 1 << 31\n        #pass\n      else:\n        nfl_offset = len(feature_offset_t)\n      nfl_offset_t.append(nfl_offset)\n      if sparse_key not in fid_offset_map:\n        continue\n      for fid_list in fid_offset_map[sparse_key]:\n        feature_offset_t.append(len(fid_offset_list))\n        fid_offset_list.extend(fid_list)\n      for fid_list in fid_offset_map_unique[sparse_key]:\n        fid_offset_list_unique.extend(fid_list)\n    feature_offset_t.append(len(fid_offset_list))\n    nfl_offset_t.append(len(feature_offset_t))\n\n    print('==' * 10 + \"fid_map_t\" + '==' * 10)\n    #print(fid_map_t)\n    print('==' * 10 + \"fid_map_unique_t\" + '==' * 10)\n    #print(fid_map_unique_t)\n    print('==' * 10 + \"fid_offset_map\" + '==' * 10)\n    #print(fid_offset_map)\n    #print('==' * 10 + \"example_batch\" + '==' * 10)\n    #print(example_batch)\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      examples_placeholder = tf.compat.v1.placeholder(dtype=tf.string,\n                                                      shape=(None,))\n      get_default_parser_ctx().enable_fused_layout = False\n      parsed_results_base = parse_example_batch(\n          examples_placeholder,\n          sparse_features=sparse_features,\n          dense_features=dense_features,\n          dense_feature_shapes=dense_feature_shapes,\n          dense_feature_types=dense_feature_types,\n          extra_features=extra_features,\n          extra_feature_shapes=extra_feature_shapes)\n      get_default_parser_ctx().enable_fused_layout = True\n      parsed_results = parse_example_batch(\n          examples_placeholder,\n          sparse_features=[],\n          dense_features=dense_features,\n          dense_feature_shapes=dense_feature_shapes,\n          dense_feature_types=dense_feature_types,\n          extra_features=extra_features,\n          extra_feature_shapes=extra_feature_shapes)\n      example_batch_varint = parsed_results.pop(\n          ParserCtx.sharding_sparse_fids_sparse_features_key)\n\n      parallel_flag_list = [0, 1]\n      fid_map_list = []\n      fid_map_unique_list = []\n      for parallel_flag in parallel_flag_list:\n        fid_map, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size, \\\n        fid_list_row_splits, fid_list_row_splits_size, fid_list_emb_row_lenth, \\\n        fid_list_table_row_length, fid_list_shard_row_lenth = sharding_sparse_fids(\n            example_batch_varint,\n            ps_num,\n            feature_cfgs,\n            False,\n            \"examplebatch\",\n            parallel_flag,\n            version=1)\n        fid_map_list.append([fid_map, fid_offset, feature_offset, nfl_offset])\n        fid_map_unique, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size, \\\n        fid_list_row_splits, fid_list_row_splits_size, fid_list_emb_row_lenth, \\\n        fid_list_table_row_length, fid_list_shard_row_lenth = sharding_sparse_fids(\n            example_batch_varint,\n            ps_num,\n            feature_cfgs,\n            True,\n            \"examplebatch\",\n            parallel_flag,\n            version=1)\n        fid_map_unique_list.append(\n            [fid_map_unique, fid_offset, feature_offset, nfl_offset])\n\n      with self.session(config=config) as sess:\n        parsed_results_base1, parsed_results1 = sess.run(\n            fetches=[parsed_results_base, parsed_results],\n            feed_dict={examples_placeholder: [eb_str]})\n\n        def diff(k, a, b, sort=False):\n\n          if not isinstance(a[0], list) and sort:\n            a.sort()\n            b.sort()\n          #print(\"diff:a {} {}\".format(k, a), flush=True)\n          #print(\"diff:b {} {}\".format(k, b), flush=True)\n          assert (len(a) == len(b))\n          if (len(a) == 0):\n            return\n          for i in range(len(a)):\n            if isinstance(a[i], list):\n              assert isinstance(b[i], list)\n              diff(k + \"/\" + str(i), a[i], b[i], sort)\n            else:\n              assert (a[i] == b[i]), f\"{i}: {a[i]} / {b[i]}\"\n\n        #print('==' * 10 + \"parsed_results_base1\" + '==' * 10, flush=True)\n        #print('==' * 10 + \"parsed_results1\" + '==' * 10, flush=True)\n        # .numpy()\n        for k, v in parsed_results_base1.items():\n          if k in sparse_features:\n            continue\n          if k in dense_features + extra_features:\n            if k not in parsed_results1:\n              print(\"no find {} in parse_example_batch_v2\".format(k))\n              assert (False)\n            diff(k, v.tolist(), parsed_results1[k].tolist())  #.numpy()\n          else:\n            print(\"no need {}\".format(k), flush=True)\n            assert (False)\n        for k, v in parsed_results1.items():\n          if k not in dense_features + extra_features:\n            print(\"no need {}\".format(k), flush=True)\n            assert (False)\n\n        for fid_map_index in range(len(parallel_flag_list)):\n          fid_map = fid_map_list[fid_map_index]\n          fid_map_unique = fid_map_unique_list[fid_map_index]\n          fid_map1_list, fid_map_unique1_list = sess.run(\n              fetches=[fid_map, fid_map_unique],\n              feed_dict={examples_placeholder: [eb_str]})\n\n          #print('==' * 10 + \"fid_map1\" + '==' * 10, flush=True)\n          #print(fid_map1, flush=True)\n          #print('==' * 10 + \"fid_map_unique1\" + '==' * 10, flush=True)\n          #print(fid_map_unique1, flush=True)\n          fid_map1, fid_offset1, feature_offset1, nfl_offset1 = fid_map1_list\n          fid_map2, fid_offset2, feature_offset2, nfl_offset2 = fid_map_unique1_list\n          print('==' * 10 + \"diff fidoffset \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          diff(\"nfl_offset\", nfl_offset1, nfl_offset_t)\n          diff(\"nfl_offset2\", nfl_offset2, nfl_offset_t)\n          diff(\"feature_offset\", feature_offset1, feature_offset_t)\n          diff(\"feature_offset2\", feature_offset2, feature_offset_t)\n          diff(\"fid_offset\", fid_offset1, fid_offset_list)\n          diff(\"fid_offset2\", fid_offset2, fid_offset_list_unique)\n\n          assert (len(fid_map_t) == len(fid_map1))\n          assert (len(fid_map_unique_t) == len(fid_map2))\n\n          def fid_diff(a, b):\n            for k, v in a.items():\n              assert (k in b)\n              diff(k, v.tolist(), b[k], True)  #.numpy()\n            for k, v in b.items():\n              assert (k in a)\n\n          print('==' * 10 + \"diff fid_map1 \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          fid_diff(fid_map1, fid_map_t)\n          print('==' * 10 + \"diff fid_map_unique1 \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          fid_diff(fid_map2, fid_map_unique_t)\n        #assert (False)\n\n  def testExampleSharding(self):\n    sparse_features_set = set()\n    dense_features = ['label']\n    dense_feature_shapes = [2]\n    dense_feature_types = [tf.float32]\n    extra_features = ['uid', 'req_time', 'item_id', 'actions']\n    extra_feature_shapes = [1, 1, 1, 1]\n    example_str_list = []\n    example_list = []\n    file_name = \"monolith/native_training/data/training_instance/example.pb\"\n    with open(file_name, 'rb') as stream:\n      while (True):\n        if len(example_str_list) > 10:\n          break\n        try:\n          stream.read(8)  # strip has_sort_id\n          stream.read(8)  # strip kafka_dump\n          size = unpack(\"<Q\", stream.read(8))[0]\n          example_str = stream.read(size)\n\n          example = Example()\n          example.ParseFromString(example_str)\n          for feature_index in range(1, 3):\n            named_feature = example.named_feature.add()\n            named_feature.name = 'fc_slot_9999{}'.format(feature_index)\n            fid_list = gen_fids_v2(feature_index, 10)\n            named_feature.feature.fid_v2_list.value.extend(fid_list)\n            named_feature.feature.fid_v2_list.value.extend(fid_list)\n          for named_feature in example.named_feature:\n            if \"fc_slot_\" in named_feature.name:\n              sparse_features_set.add(named_feature.name)\n          example_list.append(example)\n          example_str_list.append(example.SerializeToString())\n        except:\n          break\n\n    sparse_features = sorted(sparse_features_set)\n    print(sparse_features, flush=True)\n\n    feature_cfgs = FeatureConfigs()\n    index = 0\n    ps_num = 3\n    table_name_index_map = {}\n    for sparse_key in sparse_features:\n      cfg = FeatureConfig()\n      cfg.table = 'table_{}'.format(index % 3)\n      table_name_index_map[cfg.table] = -1\n      feature_cfgs.feature_configs[sparse_key].CopyFrom(cfg)\n      index += 1\n\n    sparse_features.sort()\n    table_name_list = list(table_name_index_map.keys())\n    table_name_list.sort()\n    for index, table_name in enumerate(table_name_list):\n      table_name_index_map[table_name] = index\n\n    fid_map_t = defaultdict(list)\n    fid_map_unique_t = defaultdict(list)\n    fid_map_unique_map = defaultdict(dict)\n    mask = (1 << 48) - 1\n\n    def handle_feature(feature, table_name, table_index, fid_offset_list,\n                       fid_offset_list2):\n      value_list = []\n      if len(feature.fid_v1_list.value) != 0:\n        for fid in feature.fid_v1_list.value:\n          slot_id = (fid >> 54)\n          fid_v2 = ((slot_id << 48) | (mask & fid))\n          value_list.append(fid_v2)\n      elif len(feature.fid_v2_list.value) != 0:\n        value_list = feature.fid_v2_list.value\n      for value in value_list:\n        shard = value % ps_num\n        key = table_name + \":\" + str(shard)\n        fid_offset = (table_index * ps_num + shard) << 32\n        fid_offset_list.append(fid_offset | len(fid_map_t[key]))\n        fid_map_t[key].append(value)\n        if value not in fid_map_unique_map[key]:\n          fid_map_unique_map[key][value] = len(fid_map_unique_map[key])\n          fid_map_unique_t[key].append(value)\n        fid_offset_list2.append(fid_offset | fid_map_unique_map[key][value])\n\n    fid_offset_map = defaultdict(list)\n    fid_offset_map_unique = defaultdict(list)\n    for sparse_key in sparse_features:\n      for example in example_list:\n        find_named_feature = None\n        for named_feature in example.named_feature:\n          if named_feature.name == sparse_key:\n            find_named_feature = named_feature\n            break\n        fid_offset_list = []\n        fid_offset_list2 = []\n        if find_named_feature:\n          table_name = feature_cfgs.feature_configs[sparse_key].table\n          table_index = table_name_index_map[table_name]\n          handle_feature(find_named_feature.feature, table_name, table_index,\n                         fid_offset_list, fid_offset_list2)\n        fid_offset_map[sparse_key].append(fid_offset_list)\n        fid_offset_map_unique[sparse_key].append(fid_offset_list2)\n\n    feature_offset_t = []\n    nfl_offset_t = []\n    fid_offset_list = []\n    fid_offset_list_unique = []\n    for sparse_key in sparse_features:\n      nfl_offset = len(feature_offset_t)\n      nfl_offset_t.append(nfl_offset)\n      if sparse_key not in fid_offset_map:\n        continue\n      for fid_list in fid_offset_map[sparse_key]:\n        feature_offset_t.append(len(fid_offset_list))\n        fid_offset_list.extend(fid_list)\n      for fid_list in fid_offset_map_unique[sparse_key]:\n        fid_offset_list_unique.extend(fid_list)\n    feature_offset_t.append(len(fid_offset_list))\n    nfl_offset_t.append(len(feature_offset_t))\n\n    print('==' * 10 + \"fid_map_t\" + '==' * 10, flush=True)\n    #print(fid_map_t, flush=True)\n    print('==' * 10 + \"fid_map_unique_t\" + '==' * 10, flush=True)\n    #print(fid_map_unique_t, flush=True)\n    print('==' * 10 + \"fid_offset_map\" + '==' * 10)\n    #print(fid_offset_map)\n\n    #example_tensor = tf.convert_to_tensor(example_str_list)\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      examples_placeholder = tf.compat.v1.placeholder(dtype=tf.string,\n                                                      shape=(None))\n\n      get_default_parser_ctx().enable_fused_layout = False\n      parsed_results_base = parse_examples(\n          examples_placeholder,\n          sparse_features=sparse_features,\n          dense_features=dense_features,\n          dense_feature_shapes=dense_feature_shapes,\n          dense_feature_types=dense_feature_types,\n          extra_features=extra_features,\n          extra_feature_shapes=extra_feature_shapes)\n      get_default_parser_ctx().enable_fused_layout = True\n      parsed_results = parse_examples(examples_placeholder,\n                                      sparse_features=[],\n                                      dense_features=dense_features,\n                                      dense_feature_shapes=dense_feature_shapes,\n                                      dense_feature_types=dense_feature_types,\n                                      extra_features=extra_features,\n                                      extra_feature_shapes=extra_feature_shapes)\n      examples_varint = parsed_results.pop(\n          ParserCtx.sharding_sparse_fids_sparse_features_key)\n      parallel_flag_list = [0, 1]\n      fid_map_list = []\n      fid_map_unique_list = []\n      for parallel_flag in parallel_flag_list:\n        fid_map, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size, \\\n        fid_list_row_splits, fid_list_row_splits_size, fid_list_emb_row_lenth, \\\n        fid_list_table_row_length, fid_list_shard_row_lenth = sharding_sparse_fids(\n            examples_varint,\n            ps_num,\n            feature_cfgs,\n            False,\n            \"example\",\n            parallel_flag,\n            version=1)\n        fid_map_list.append([fid_map, fid_offset, feature_offset, nfl_offset])\n        fid_map_unique, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size, \\\n        fid_list_row_splits, fid_list_row_splits_size, fid_list_emb_row_lenth, \\\n        fid_list_table_row_length, fid_list_shard_row_lenth = sharding_sparse_fids(\n            examples_varint,\n            ps_num,\n            feature_cfgs,\n            True,\n            \"example\",\n            parallel_flag,\n            version=1)\n        fid_map_unique_list.append(\n            [fid_map_unique, fid_offset, feature_offset, nfl_offset])\n\n      with self.session(config=config) as sess:\n        parsed_results_base1, parsed_results1 = sess.run(\n            fetches=[parsed_results_base, parsed_results],\n            feed_dict={examples_placeholder: example_str_list})\n\n        def diff(k, a, b, sort=False):\n          if not isinstance(a[0], list) and sort:\n            a.sort()\n            b.sort()\n\n          def print_func():\n            print(\"diff:a {} {}\".format(k, a), flush=True)\n            print(\"diff:b {} {}\".format(k, b), flush=True)\n            return \"{}, {}\".format(len(a), len(b))\n\n          assert (len(a) == len(b)), print_func()\n          if (len(a) == 0):\n            return\n          for i in range(len(a)):\n            if isinstance(a[i], list):\n              assert isinstance(b[i], list), print_func()\n              diff(k + \"/\" + str(i), a[i], b[i], sort)\n            else:\n              assert (a[i] == b[i]), print_func()\n\n        #print('==' * 10 + \"parsed_results_base1\" + '==' * 10, flush=True)\n        #print(parsed_results_base1, flush=True)\n        #print('==' * 10 + \"parsed_results1\" + '==' * 10, flush=True)\n        #print(parsed_results1, flush=True)\n        # .numpy()\n        for k, v in parsed_results_base1.items():\n          if k in sparse_features:\n            continue\n          if k in dense_features + extra_features:\n            if k not in parsed_results1:\n              print(\"no find {} in parse_example_batch_v2\".format(k),\n                    flush=True)\n              assert (False)\n            diff(k, v.tolist(), parsed_results1[k].tolist())  #.numpy()\n          else:\n            print(\"no need {}\".format(k), flush=True)\n            assert (False)\n        for k, v in parsed_results1.items():\n          if k not in dense_features + extra_features:\n            print(\"no need {}\".format(k), flush=True)\n            assert (False)\n\n        for fid_map_index in range(len(parallel_flag_list)):\n          fid_map = fid_map_list[fid_map_index]\n          fid_map_unique = fid_map_unique_list[fid_map_index]\n          fid_map1_list, fid_map_unique1_list = sess.run(\n              fetches=[fid_map, fid_map_unique],\n              feed_dict={examples_placeholder: example_str_list})\n\n          #print('==' * 10 + \"fid_map1\" + '==' * 10, flush=True)\n          #print(fid_map1, flush=True)\n          #print('==' * 10 + \"fid_map_unique1\" + '==' * 10, flush=True)\n          #print(fid_map_unique1, flush=True)\n          fid_map1, fid_offset1, feature_offset1, nfl_offset1 = fid_map1_list\n          fid_map2, fid_offset2, feature_offset2, nfl_offset2 = fid_map_unique1_list\n          print('==' * 10 + \"diff fidoffset \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          diff(\"nfl_offset\", list(nfl_offset1), nfl_offset_t)\n          diff(\"nfl_offset2\", list(nfl_offset2), nfl_offset_t)\n          diff(\"feature_offset\", list(feature_offset1), feature_offset_t)\n          diff(\"feature_offset2\", list(feature_offset2), feature_offset_t)\n          diff(\"fid_offset\", list(fid_offset1), fid_offset_list)\n          diff(\"fid_offset2\", list(fid_offset2), fid_offset_list_unique)\n\n          print('==' * 10 + \"fid_map1\" + '==' * 10, flush=True)\n          print('==' * 10 + \"fid_map_unique1\" + '==' * 10, flush=True)\n\n          assert (len(fid_map_t) == len(fid_map1))\n          assert (len(fid_map_unique_t) == len(fid_map2))\n\n          def fid_diff(a, b):\n            for k, v in a.items():\n              assert (k in b)\n              diff(k, v.tolist(), b[k], True)  #.numpy()\n            for k, v in b.items():\n              assert (k in a)\n\n          print('==' * 10 + \"diff fid_map1 \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          fid_diff(fid_map1, fid_map_t)\n          print('==' * 10 + \"diff fid_map_unique1 \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          fid_diff(fid_map2, fid_map_unique_t)\n        #assert (False)\n\n  def testInstanceSharding(self):\n    fidv1_features = [1, 200, 3, 5, 9, 203, 205]\n    fidv2_features = [\"fc_v2_1\", \"fc_v2_2\", \"fc_v2_3\"]\n    dense_features = ['label']\n    dense_feature_shapes = [2]\n    dense_feature_types = [tf.float32]\n    extra_features = ['uid', 'req_time', 'item_id', 'actions']\n    extra_feature_shapes = [1, 1, 1, 2]\n\n    instance_str_list = []\n    instance_list = []\n    while (len(instance_str_list) < 128):\n      instance = gen_instance(\n          fidv1_features=fidv1_features,\n          fidv2_features=[],\n          dense_features=[FeatureMeta('label', shape=2, dtype=tf.float32)],\n          extra_features=[\n              FeatureMeta('actions', shape=2),\n              FeatureMeta('uid'),\n              FeatureMeta('req_time', dtype=tf.int32),\n              FeatureMeta('item_id'),\n          ])\n      instance_list.append(instance)\n      instance_str_list.append(instance.SerializeToString())\n\n      instance2 = deepcopy(instance)\n      for slot, feature_name in enumerate(fidv2_features):\n        feature = instance2.feature.add()\n        feature.name = feature_name\n        feature.fid.extend(gen_fids_v2(1000 + slot, 10))\n      instance_list.append(instance2)\n      instance_str_list.append(instance2.SerializeToString())\n\n      instance3 = deepcopy(instance2)\n      del instance3.fid[:]\n      instance_list.append(instance3)\n      instance_str_list.append(instance3.SerializeToString())\n\n    def gen_slot_feature_name(slot_id):\n      return f\"slot_{slot_id}\"\n\n    sparse_features = sorted(\n        fidv2_features +\n        [gen_slot_feature_name(slot_id) for slot_id in fidv1_features])\n    print(sparse_features, flush=True)\n\n    feature_cfgs = FeatureConfigs()\n    index = 0\n    ps_num = 3\n    table_name_index_map = {}\n    for sparse_key in sparse_features:\n      cfg = FeatureConfig()\n      cfg.table = 'table_{}'.format(index % 3)\n      table_name_index_map[cfg.table] = -1\n      feature_cfgs.feature_configs[sparse_key].CopyFrom(cfg)\n      index += 1\n\n    sparse_features.sort()\n    table_name_list = list(table_name_index_map.keys())\n    table_name_list.sort()\n    for index, table_name in enumerate(table_name_list):\n      table_name_index_map[table_name] = index\n\n    fid_map_t = defaultdict(list)\n    fid_map_unique_t = defaultdict(list)\n    fid_map_unique_map = defaultdict(dict)\n    mask = (1 << 48) - 1\n\n    def handle_feature(value_list, table_name, table_index, fid_offset_list,\n                       fid_offset_list2):\n      for value in value_list:\n        shard = value % ps_num\n        key = table_name + \":\" + str(shard)\n        fid_offset = (table_index * ps_num + shard) << 32\n        fid_offset_list.append(fid_offset | len(fid_map_t[key]))\n        fid_map_t[key].append(value)\n        if value not in fid_map_unique_map[key]:\n          fid_map_unique_map[key][value] = len(fid_map_unique_map[key])\n          fid_map_unique_t[key].append(value)\n        fid_offset_list2.append(fid_offset | fid_map_unique_map[key][value])\n\n    def slot_id_v1(fid):\n      return fid >> 54\n\n    intance_tmp_dict = defaultdict(list)\n    for instance in instance_list:\n      fid_v2_list = defaultdict(list)\n      for fid in instance.fid:\n        slot_id = slot_id_v1(fid)\n        sparse_key = gen_slot_feature_name(slot_id)\n        if sparse_key not in sparse_features:\n          continue\n        fid_v2 = ((slot_id << 48) | (mask & fid))\n        fid_v2_list[sparse_key].append(fid_v2)\n\n      for feature in instance.feature:\n        sparse_key = feature.name\n        if sparse_key not in sparse_features:\n          continue\n        fid_v2_list[sparse_key] = feature.fid\n\n      for sparse_key in sparse_features:\n        if sparse_key not in fid_v2_list:\n          fid_v2_list[sparse_key] = []\n\n        fid_list = fid_v2_list[sparse_key]\n        intance_tmp_dict[sparse_key].append(fid_list)\n\n    fid_offset_map = defaultdict(list)\n    fid_offset_map_unique = defaultdict(list)\n    for sparse_key in sparse_features:\n      for fid_list in intance_tmp_dict[sparse_key]:\n        fid_offset_list = []\n        fid_offset_list2 = []\n        table_name = feature_cfgs.feature_configs[sparse_key].table\n        table_index = table_name_index_map[table_name]\n        handle_feature(fid_list, table_name, table_index, fid_offset_list,\n                       fid_offset_list2)\n        fid_offset_map[sparse_key].append(fid_offset_list)\n        fid_offset_map_unique[sparse_key].append(fid_offset_list2)\n\n    feature_offset_t = []\n    nfl_offset_t = []\n    fid_offset_list = []\n    fid_offset_list_unique = []\n    for sparse_key in sparse_features:\n      nfl_offset = len(feature_offset_t)\n      nfl_offset_t.append(nfl_offset)\n      if sparse_key not in fid_offset_map:\n        continue\n      for fid_list in fid_offset_map[sparse_key]:\n        feature_offset_t.append(len(fid_offset_list))\n        fid_offset_list.extend(fid_list)\n      for fid_list in fid_offset_map_unique[sparse_key]:\n        fid_offset_list_unique.extend(fid_list)\n    feature_offset_t.append(len(fid_offset_list))\n    nfl_offset_t.append(len(feature_offset_t))\n\n    print('==' * 10 + \"fid_map_t\" + '==' * 10, flush=True)\n    #print(fid_map_t, flush=True)\n    print('==' * 10 + \"fid_map_unique_t\" + '==' * 10, flush=True)\n    #print(fid_map_unique_t, flush=True)\n    print('==' * 10 + \"fid_offset_map\" + '==' * 10)\n    print(\"xxxxx:\", len(feature_offset_t), len(instance_list))\n    #print(fid_offset_map)\n\n    #example_tensor = tf.convert_to_tensor(example_str_list)\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      examples_placeholder = tf.compat.v1.placeholder(dtype=tf.string,\n                                                      shape=(None))\n\n      get_default_parser_ctx().enable_fused_layout = False\n      parsed_results_base = parse_instances(\n          examples_placeholder,\n          fidv1_features=fidv1_features,\n          fidv2_features=fidv2_features,\n          dense_features=dense_features,\n          dense_feature_shapes=dense_feature_shapes,\n          dense_feature_types=dense_feature_types,\n          extra_features=extra_features,\n          extra_feature_shapes=extra_feature_shapes)\n      get_default_parser_ctx().enable_fused_layout = True\n      parsed_results = parse_instances(\n          examples_placeholder,\n          fidv1_features=fidv1_features,\n          fidv2_features=fidv2_features,\n          dense_features=dense_features,\n          dense_feature_shapes=dense_feature_shapes,\n          dense_feature_types=dense_feature_types,\n          extra_features=extra_features,\n          extra_feature_shapes=extra_feature_shapes)\n      examples_varint = parsed_results.pop(\n          ParserCtx.sharding_sparse_fids_sparse_features_key)\n      parallel_flag_list = [0, 1]\n      fid_map_list = []\n      fid_map_unique_list = []\n      for parallel_flag in parallel_flag_list:\n        fid_map, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size, \\\n        fid_list_row_splits, fid_list_row_splits_size, fid_list_emb_row_lenth, \\\n        fid_list_table_row_length, fid_list_shard_row_lenth = sharding_sparse_fids(\n            examples_varint,\n            ps_num,\n            feature_cfgs,\n            False,\n            \"instance\",\n            parallel_flag,\n            version=1)\n        fid_map_list.append([fid_map, fid_offset, feature_offset, nfl_offset])\n        fid_map_unique, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size, \\\n        fid_list_row_splits, fid_list_row_splits_size, fid_list_emb_row_lenth, \\\n        fid_list_table_row_length, fid_list_shard_row_lenth = sharding_sparse_fids(\n            examples_varint,\n            ps_num,\n            feature_cfgs,\n            True,\n            \"instance\",\n            parallel_flag,\n            version=1)\n        fid_map_unique_list.append(\n            [fid_map_unique, fid_offset, feature_offset, nfl_offset])\n\n      with self.session(config=config) as sess:\n        parsed_results_base1, parsed_results1 = sess.run(\n            fetches=[parsed_results_base, parsed_results],\n            feed_dict={examples_placeholder: instance_str_list})\n\n        def diff(k, a, b, sort=False):\n          if not isinstance(a[0], list) and sort:\n            a.sort()\n            b.sort()\n\n          def print_func():\n            print(\"diff:a {} {}\".format(k, a), flush=True)\n            print(\"diff:b {} {}\".format(k, b), flush=True)\n            return \"{}, {}\".format(len(a), len(b))\n\n          assert (len(a) == len(b)), print_func()\n          if (len(a) == 0):\n            return\n          for i in range(len(a)):\n            if isinstance(a[i], list):\n              assert isinstance(b[i], list), print_func()\n              diff(k + \"/\" + str(i), a[i], b[i], sort)\n            else:\n              assert (a[i] == b[i]), print_func()\n\n        print('==' * 10 + \"parsed_results_base1\" + '==' * 10, flush=True)\n        print(parsed_results_base1, flush=True)\n        print('==' * 10 + \"parsed_results1\" + '==' * 10, flush=True)\n        print(parsed_results1, flush=True)\n        # .numpy()\n        for k, v in parsed_results_base1.items():\n          if k in sparse_features:\n            continue\n          if k in dense_features + extra_features:\n            if k not in parsed_results1:\n              print(\"no find {} in parse_example_batch_v2\".format(k),\n                    flush=True)\n              assert (False)\n            diff(k, v.tolist(), parsed_results1[k].tolist())  #.numpy()\n          else:\n            print(\"no need {}\".format(k), flush=True)\n            assert (False)\n        for k, v in parsed_results1.items():\n          if k not in dense_features + extra_features:\n            print(\"no need {}\".format(k), flush=True)\n            assert (False)\n\n        for fid_map_index in range(len(parallel_flag_list)):\n          fid_map = fid_map_list[fid_map_index]\n          fid_map_unique = fid_map_unique_list[fid_map_index]\n          fid_map1_list, fid_map_unique1_list = sess.run(\n              fetches=[fid_map, fid_map_unique],\n              feed_dict={examples_placeholder: instance_str_list})\n\n          #print('==' * 10 + \"fid_map1\" + '==' * 10, flush=True)\n          #print('==' * 10 + \"fid_map_unique1\" + '==' * 10, flush=True)\n          fid_map1, fid_offset1, feature_offset1, nfl_offset1 = fid_map1_list\n          fid_map2, fid_offset2, feature_offset2, nfl_offset2 = fid_map_unique1_list\n          print('==' * 10 + \"diff fidoffset \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          diff(\"nfl_offset\", list(nfl_offset1), nfl_offset_t)\n          diff(\"nfl_offset2\", list(nfl_offset2), nfl_offset_t)\n          diff(\"feature_offset\", list(feature_offset1), feature_offset_t)\n          diff(\"feature_offset2\", list(feature_offset2), feature_offset_t)\n          diff(\"fid_offset\", list(fid_offset1), fid_offset_list)\n          diff(\"fid_offset2\", list(fid_offset2), fid_offset_list_unique)\n\n          print('==' * 10 + \"fid_map1\" + '==' * 10, flush=True)\n          #print(fid_map1, flush=True)\n          print('==' * 10 + \"fid_map_unique1\" + '==' * 10, flush=True)\n          #print(fid_map_unique1, flush=True)\n\n          assert (len(fid_map_t) == len(fid_map1))\n          assert (len(fid_map_unique_t) == len(fid_map2))\n\n          def fid_diff(a, b):\n            for k, v in a.items():\n              assert (k in b)\n              diff(k, v.tolist(), b[k], True)  #.numpy()\n            for k, v in b.items():\n              assert (k in a)\n\n          print('==' * 10 + \"diff fid_map1 \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          fid_diff(fid_map1, fid_map_t)\n          print('==' * 10 + \"diff fid_map_unique1 \" +\n                str(parallel_flag_list[fid_map_index]) + '==' * 10,\n                flush=True)\n          fid_diff(fid_map2, fid_map_unique_t)\n        #assert (False)\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/parsers.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging, flags\nimport os\nimport struct\nfrom copy import deepcopy\nfrom typing import Dict, List, Iterable, Callable\nfrom collections import deque\nfrom dataclasses import dataclass\nimport traceback\n\nimport tensorflow as tf\n\nfrom idl.matrix.proto.line_id_pb2 import LineId\nfrom idl.matrix.proto.example_pb2 import FeatureConfigs\n\nfrom monolith.utils import get_libops_path\nfrom monolith.native_training import logging_ops\nfrom monolith.native_training import native_task_context\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.data.feature_list import get_feature_name_and_slot, FeatureList\nfrom monolith.native_training.data.data_op_config_pb2 import LabelConf, TaskLabelConf\nfrom monolith.native_training.data.feature_list import add_feature, is_example_batch\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\nfrom monolith.native_training.utils import add_to_collections\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.framework import common_shapes\n\nFLAGS = flags.FLAGS\n\nparse_instance_ops = gen_monolith_ops\n\n_line_id_descriptor = LineId.DESCRIPTOR\n\n_default_parser_ctx = None\n\nFLAGS = flags.FLAGS\n\n@dataclass\nclass ShardingSparseFidsOpParams:\n  num_ps: int\n  use_native_multi_hash_table: bool\n  unique: Callable\n  transfer_float16: bool\n  sub_table_name_to_config: Dict\n  feature_configs: FeatureConfigs\n  enable_gpu_emb: bool\n  use_gpu: bool\n\n\nclass ParserCtx(object):\n  enable_resource_constrained_roughsort = False\n  sharding_sparse_fids_features_prefix = \"__sharding_sparse_fids__\"\n  sharding_sparse_fids_sparse_features_key = \"__sharding_sparse_fids__sparse_features\"\n\n  def __init__(self, enable_fused_layout: bool = False):\n    self._old_parser_ctx = None\n    self.parser_type = None\n    self.enable_fused_layout = enable_fused_layout\n    self.sharding_sparse_fids_op_params: ShardingSparseFidsOpParams = None\n    self._ctx_kv = {}\n\n  def __enter__(self):\n    global _default_parser_ctx\n    self._old_parser_ctx = _default_parser_ctx\n    _default_parser_ctx = self\n    return self\n  def __exit__(self, exc_type, exc_val, exc_tb):\n    global _default_parser_ctx\n    _default_parser_ctx = self._old_parser_ctx\n    self._old_parser_ctx = None\n\n  @classmethod\n  def sharding_sparse_fids_features_insert_to_features(cls, inputs, features):\n    if isinstance(inputs, Dict):\n      for k, v in inputs.items():\n        if isinstance(v, Dict):\n          for sub_k, sub_v in v.items():\n            features[cls.sharding_sparse_fids_features_prefix + k + \"/\" +\n                     sub_k] = sub_v\n        elif isinstance(v, List):\n          raise ValueError(\"not support\")\n        else:\n          features[cls.sharding_sparse_fids_features_prefix + k] = v\n    else:\n      raise ValueError(\"not support\")\n\n  @classmethod\n  def sharding_sparse_fids_features_parse_from_features(cls, features):\n    outputs = {}\n    pop_key_list = []\n    for k, v in features.items():\n      if k.startswith(cls.sharding_sparse_fids_features_prefix):\n        name = k[len(cls.sharding_sparse_fids_features_prefix):]\n        level_ouput = outputs\n        level_names = name.split(\"/\")\n        for level_name in level_names[:-1]:\n          if level_name not in level_ouput:\n            level_ouput[level_name] = {}\n          level_ouput = level_ouput[level_name]\n        level_ouput[level_names[-1]] = v\n        pop_key_list.append(k)\n    for k in pop_key_list:\n      features.pop(k)\n    return outputs\n\n  def set(self, key, value):\n    self._ctx_kv[key] = value\n\n  def get(self, key, default_value=None):\n    return self._ctx_kv.get(key, default_value)\n\n\n\ndef get_default_parser_ctx() -> ParserCtx:\n  global _default_parser_ctx\n  if _default_parser_ctx is None:\n    _default_parser_ctx = ParserCtx(False)\n  return _default_parser_ctx\n\n\nclass ProtoType:\n  TYPE_BOOL: int = 8\n  TYPE_BYTES: int = 12\n  TYPE_DOUBLE: int = 1\n  TYPE_ENUM: int = 14\n  TYPE_FIXED32: int = 7\n  TYPE_FIXED64: int = 6\n  TYPE_FLOAT: int = 2\n  TYPE_GROUP: int = 10\n  TYPE_INT32: int = 5\n  TYPE_INT64: int = 3\n  TYPE_MESSAGE: int = 11\n  TYPE_SFIXED32: int = 15\n  TYPE_SFIXED64: int = 16\n  TYPE_SINT32: int = 17\n  TYPE_SINT64: int = 18\n  TYPE_STRING: int = 9\n  TYPE_UINT32: int = 13\n  TYPE_UINT64: int = 4\n\n  UNKNOWN = {TYPE_BOOL, TYPE_ENUM, TYPE_GROUP, TYPE_MESSAGE}\n  STRING = {TYPE_BYTES, TYPE_STRING}\n  FLOAT = {TYPE_FLOAT, TYPE_DOUBLE}\n  INT = {\n      TYPE_INT32, TYPE_INT64, TYPE_SINT32, TYPE_SINT64, TYPE_UINT32,\n      TYPE_UINT64, TYPE_FIXED32, TYPE_FIXED64, TYPE_SFIXED32, TYPE_SFIXED64\n  }\n\n  @classmethod\n  def get_tf_type(cls, proto_type: int):\n    if proto_type in cls.INT:\n      return tf.int64\n    elif proto_type in cls.FLOAT:\n      return tf.float32\n    elif proto_type in cls.STRING:\n      return tf.string\n    else:\n      raise Exception('proto_type {} is not support'.format(proto_type))\n\n\ndef _add_dense_features(names: List[str], shapes: List[int],\n                        types: List[tf.compat.v1.dtypes.DType],\n                        dense_features: List[str],\n                        dense_feature_shapes: List[int],\n                        dense_feature_types: List[tf.compat.v1.dtypes.DType]):\n  assert dense_features is not None\n  assert dense_feature_shapes is not None\n  assert len(dense_features) == len(dense_feature_shapes)\n  assert all([s > 0 for s in dense_feature_shapes])\n\n  if dense_feature_types is None:\n    dense_feature_types = [tf.float32] * len(dense_features)\n  else:\n    assert len(dense_features) == len(dense_feature_types)\n\n  names.extend(dense_features)\n  shapes.extend(dense_feature_shapes)\n  types.extend(dense_feature_types)\n\n\ndef _add_extra_features(names: List[str], shapes: List[int],\n                        types: List[tf.compat.v1.dtypes.DType],\n                        extra_features: List[str],\n                        extra_feature_shapes: List[int]):\n  assert extra_features is not None\n  assert extra_feature_shapes is not None\n  assert len(extra_features) == len(extra_feature_shapes)\n  assert all([s > 0 for s in extra_feature_shapes])\n\n  extra_dtypes = []\n  for name in extra_features:\n    try:\n      extra_dtypes.append(\n          ProtoType.get_tf_type(_line_id_descriptor.fields_by_name[name].type))\n    except:\n      raise Exception(f\"{name} is not in line id, pls check!\")\n\n  names.extend(extra_features)\n  shapes.extend(extra_feature_shapes)\n  types.extend(extra_dtypes)\n\n\ndef _assemble(sparse_features,\n              names,\n              shapes,\n              types,\n              out_list,\n              batch_size: int = None):\n  assert len(out_list) == len(types)\n  features = {}\n  for i, name in enumerate(names):\n    if name in sparse_features:\n      value = out_list[i + len(names)]\n      if batch_size:\n        batch_size = batch_size[0] if isinstance(batch_size,\n                                                 (list, tuple)) else batch_size\n        split = tf.reshape(out_list[i], shape=(batch_size + 1,))\n      else:\n        split = out_list[i]\n      features[name] = tf.RaggedTensor.from_row_splits(value,\n                                                       split,\n                                                       validate=False)\n    else:\n      features[name] = out_list[i]\n\n  return features\n\n\ndef parse_instances(tensor: tf.Tensor,\n                    fidv1_features: List[int] = None,\n                    fidv2_features: List[str] = None,\n                    dense_features: List[str] = None,\n                    dense_feature_shapes: List[int] = None,\n                    dense_feature_types: List[tf.compat.v1.dtypes.DType] = None,\n                    extra_features: List[str] = None,\n                    extra_feature_shapes: List[int] = None):\n  \"\"\"从Tensor中解析instance\n  \n  Example格式中, 所有特征均存于feature中, 没有平铺的特征. Sparse特征由于长度不定, 输出RaggedTensor, 其它特征输出Tensor\n  \n  Args:\n    tensor (:obj:`tf.Tensor`): 输入样本\n    fidv1_features (:obj:`List[int]`): 在Instance中, fidv1_features是平铺的, 所以用slot指定, 可以是部分slot\n    fidv2_features (:obj:`List[str]`): 在Instance中, fidv2_features存放于feature中, 可以用名字指定, 可以是部分特征名\n    dense_features (:obj:`List[str]`): 稠密特征(或Label)名称, 可以有多个, 也可以有不同类型\n    dense_feature_shapes (:obj:`List[int]`): 稠密特征名称的shape\n    dense_feature_types (:obj:`List[dtype]`): 稠密特征名称的数据类型, 默认为`tf.float32`\n    extra_features (:obj:`List[str]`): 主要指LineId中的字段, 可以有多个, Monolith会自动从LineId中提取数据类型\n    extra_feature_shapes (:obj:`List[int]`): extra特征名称的shape\n  \n  Returns:\n    Dict[str, Tensor] 解析出特征名到特征的字典\n  \n  \"\"\"\n\n  if ParserCtx.enable_resource_constrained_roughsort:\n    if extra_features is None:\n      extra_features = [\"item_id\"]\n      extra_feature_shapes = [1]\n    elif \"item_id\" not in extra_features:\n      extra_features.append(\"item_id\")\n      extra_feature_shapes.append(1)\n\n  if dense_features:\n    assert dense_feature_shapes is not None\n    assert len(dense_feature_shapes) == len(dense_features)\n    if dense_feature_types:\n      assert len(dense_feature_types) == len(dense_features)\n    else:\n      dense_feature_types = [tf.float32] * len(dense_features)\n\n  get_default_parser_ctx().parser_type = 'instance'\n  add_to_collections('fidv1_features', fidv1_features)\n  add_to_collections('fidv2_features', fidv2_features)\n  add_to_collections('dense_features', dense_features)\n  add_to_collections('dense_feature_shapes', dense_feature_shapes)\n  add_to_collections('dense_feature_types', dense_feature_types)\n  add_to_collections('extra_features', extra_features)\n  add_to_collections('extra_feature_shapes', extra_feature_shapes)\n  add_to_collections('variant_type', 'instance')\n  get_default_parser_ctx().set('fidv1_features', fidv1_features)\n  get_default_parser_ctx().set('fidv2_features', fidv2_features)\n\n  names, shapes, types = [], [], []\n\n  if not get_default_parser_ctx().enable_fused_layout:\n    sparse_features = []\n    if fidv1_features is not None:\n      names.extend(\n          [get_feature_name_and_slot(slot)[0] for slot in fidv1_features])\n      if all(isinstance(feature_name, str) for feature_name in fidv1_features):\n        try:\n          feature_list = FeatureList.parse()\n          fidv1_features = [\n              feature_list.get(feature_name).slot\n              for feature_name in fidv1_features\n          ]\n        except:\n          raise RuntimeError(\"fidv1_features error\")\n      shapes.extend([-1] * len(fidv1_features))\n      types.extend([tf.int64] * len(fidv1_features))\n\n    if fidv2_features is not None:\n      names.extend(fidv2_features)\n      shapes.extend([-1] * len(fidv2_features))\n      types.extend([tf.int64] * len(fidv2_features))\n\n    sparse_features.extend(names)\n\n  if dense_features is not None:\n    _add_dense_features(names, shapes, types, dense_features,\n                        dense_feature_shapes, dense_feature_types)\n\n  if extra_features is not None:\n    _add_extra_features(names, shapes, types, extra_features,\n                        extra_feature_shapes)\n  if get_default_parser_ctx().enable_fused_layout:\n    if len(names) == 0:\n      names.append(\"__FAKE_FEATURE__\")\n      shapes.append(1)\n      types.append(tf.float32)\n    out_list, instances = parse_instance_ops.parse_instances_v2(\n        tensor, [], [], names, shapes, types, extra_features or [])\n    features = _assemble([], names, shapes, types, out_list)\n    parser_ctx = get_default_parser_ctx()\n    if parser_ctx.sharding_sparse_fids_op_params is not None and (parser_ctx.sharding_sparse_fids_op_params.use_gpu or FLAGS.dataset_use_dataservice):\n      sharding_sparse_fids_with_context(instances, features, parser_ctx)\n    else:\n      features[ParserCtx.sharding_sparse_fids_sparse_features_key] = instances\n    if \"__FAKE_FEATURE__\" in features:\n      del features[\"__FAKE_FEATURE__\"]\n    return features\n  else:\n    types.extend([tf.int64] * len(sparse_features))\n    assert len(names) == len(set(names)), \"deplicate names, pls check!\"\n    out_list = parse_instance_ops.parse_instances(tensor, fidv1_features or [],\n                                                  fidv2_features or [], names,\n                                                  shapes, types,\n                                                  extra_features or [])\n    return _assemble(sparse_features, names, shapes, types, out_list)\n\n\n@monolith_export\ndef parse_examples(tensor: tf.Tensor,\n                   sparse_features: List[str],\n                   dense_features: List[str] = None,\n                   dense_feature_shapes: List[int] = None,\n                   dense_feature_types: List[tf.compat.v1.dtypes.DType] = None,\n                   extra_features: List[str] = None,\n                   extra_feature_shapes: List[int] = None):\n  \"\"\"从Tensor中解析example\n  \n  Example格式中, 所有特征均存于feature中, 没有平铺特征. Sparse特征由于长度不定, 输出RaggedTensor, 其它特征输出Tensor\n  \n  Args:\n    tensor (:obj:`tf.Tensor`): 输入样本\n    sparse_features (:obj:`List[str]`): 稀疏特征名称, 可以有多个\n    dense_features (:obj:`List[str]`): 稠密特征(或Label)名称, 可以有多个, 也可以有不同类型\n    dense_feature_shapes (:obj:`List[int]`): 稠密特征名称的shape\n    dense_feature_types (:obj:`List[dtype]`): 稠密特征名称的数据类型, 默认为`tf.float32`\n    extra_features (:obj:`List[str]`): 主要指LineId中的字段, 可以有多个, Monolith会自动从LineId中提取数据类型\n    extra_feature_shapes (:obj:`List[int]`): extra特征名称的shape\n  \n  Returns:\n    Dict[str, Tensor] 解析出特征名到特征的字典\n  \n  \"\"\"\n\n  if dense_features:\n    assert dense_feature_shapes is not None\n    assert len(dense_feature_shapes) == len(dense_features)\n    if dense_feature_types:\n      assert len(dense_feature_types) == len(dense_features)\n    else:\n      dense_feature_types = [tf.float32] * len(dense_features)\n\n  get_default_parser_ctx().parser_type = 'example'\n  add_to_collections('sparse_features', sparse_features)\n  add_to_collections('dense_features', dense_features)\n  add_to_collections('dense_feature_shapes', dense_feature_shapes)\n  add_to_collections('dense_feature_types', dense_feature_types)\n  add_to_collections('extra_features', extra_features)\n  add_to_collections('extra_feature_shapes', extra_feature_shapes)\n  add_to_collections('variant_type', 'example')\n  get_default_parser_ctx().set('sparse_features', sparse_features)\n  if is_example_batch():\n    add_feature(sparse_features)\n    if dense_features:\n      if 'label' in dense_features:\n        add_feature('__LABEL__')\n      add_feature([feat for feat in dense_features if feat != 'label'])\n    if extra_features:\n      add_feature('__LINE_ID__')\n\n  names, shapes, types = [], [], []\n\n  if not get_default_parser_ctx().enable_fused_layout:\n    assert sparse_features is not None\n    names.extend(sparse_features)\n    shapes.extend([-1] * len(sparse_features))\n    types.extend([tf.int64] * len(sparse_features))\n\n  if dense_features is not None:\n    _add_dense_features(names, shapes, types, dense_features,\n                        dense_feature_shapes, dense_feature_types)\n\n  if extra_features is not None:\n    _add_extra_features(names, shapes, types, extra_features,\n                        extra_feature_shapes)\n\n  assert len(names) == len(set(names)), \"deplicate names, pls check!\"\n  if get_default_parser_ctx().enable_fused_layout:\n    if len(names) == 0:\n      names.append(\"__FAKE_FEATURE__\")\n      shapes.append(1)\n      types.append(tf.float32)\n    out_list, examples = parse_instance_ops.parse_examples_v2(\n        tensor, names, shapes, types, extra_features or [])\n    features = _assemble([], names, shapes, types, out_list)\n    parser_ctx = get_default_parser_ctx()\n    if parser_ctx.sharding_sparse_fids_op_params is not None and (parser_ctx.sharding_sparse_fids_op_params.use_gpu or FLAGS.dataset_use_dataservice):\n      sharding_sparse_fids_with_context(examples, features, parser_ctx)\n    else:\n      features[ParserCtx.sharding_sparse_fids_sparse_features_key] = examples\n    if \"__FAKE_FEATURE__\" in features:\n      del features[\"__FAKE_FEATURE__\"]\n    return features\n  else:\n    types.extend([tf.int64] * len(sparse_features))\n    out_list = parse_instance_ops.parse_examples(tensor, names, shapes, types,\n                                                 extra_features or [])\n    return _assemble(sparse_features, names, shapes, types, out_list)\n\n\n@monolith_export\ndef parse_example_batch(\n    tensor: tf.Tensor,\n    sparse_features: List[str],\n    dense_features: List[str] = None,\n    dense_feature_shapes: List[int] = None,\n    dense_feature_types: List[tf.compat.v1.dtypes.DType] = None,\n    extra_features: List[str] = None,\n    extra_feature_shapes: List[int] = None):\n  \"\"\"从Tensor中解析example_batch\n  \n  Example_batch格式中, 所有特征均存于feature中, 没有平铺特征. Sparse特征由于长度不定, 输出RaggedTensor, 其它特征输出Tensor\n  \n  Args:\n    tensor (:obj:`tf.Tensor`): 输入样本\n    sparse_features (:obj:`List[str]`): 稀疏特征名称, 可以有多个\n    dense_features (:obj:`List[str]`): 稠密特征(或Label)名称, 可以有多个, 也可以有不同类型\n    dense_feature_shapes (:obj:`List[int]`): 稠密特征名称的shape\n    dense_feature_types (:obj:`List[dtype]`): 稠密特征名称的数据类型, 默认为`tf.float32`\n    extra_features (:obj:`List[str]`): 主要指LineId中的字段, 可以有多个, Monolith会自动从LineId中提取数据类型\n    extra_feature_shapes (:obj:`List[int]`): extra特征名称的shape\n  \n  Returns:\n    Dict[str, Tensor] 解析出特征名到特征的字典\n  \n  \"\"\"\n\n  if dense_features:\n    assert dense_feature_shapes is not None\n    assert len(dense_feature_shapes) == len(dense_features)\n    if dense_feature_types:\n      assert len(dense_feature_types) == len(dense_features)\n    else:\n      dense_feature_types = [tf.float32] * len(dense_features)\n\n  get_default_parser_ctx().parser_type = 'examplebatch'\n  add_to_collections('sparse_features', sparse_features)\n  add_to_collections('dense_features', dense_features)\n  add_to_collections('dense_feature_shapes', dense_feature_shapes)\n  add_to_collections('dense_feature_types', dense_feature_types)\n  add_to_collections('extra_features', extra_features)\n  add_to_collections('extra_feature_shapes', extra_feature_shapes)\n  add_to_collections('variant_type', 'example_batch')\n  get_default_parser_ctx().set('sparse_features', sparse_features)\n  if is_example_batch():\n    add_feature(sparse_features)\n    if dense_features:\n      if 'label' in dense_features:\n        add_feature('__LABEL__')\n      add_feature([feat for feat in dense_features if feat != 'label'])\n    if extra_features:\n      add_feature('__LINE_ID__')\n\n  names, shapes, types = [], [], []\n\n  if not get_default_parser_ctx().enable_fused_layout:\n    assert sparse_features is not None\n    names.extend(sparse_features)\n    shapes.extend([-1] * len(sparse_features))\n    types.extend([tf.int64] * len(sparse_features))\n\n  if dense_features is not None:\n    _add_dense_features(names, shapes, types, dense_features,\n                        dense_feature_shapes, dense_feature_types)\n\n  if extra_features is not None:\n    _add_extra_features(names, shapes, types, extra_features,\n                        extra_feature_shapes)\n  batch_size = get_default_parser_ctx().get('batch_size')\n  assert len(names) == len(set(names)), \"deplicate names, pls check!\"\n  if get_default_parser_ctx().enable_fused_layout:\n    if len(names) == 0:\n      names.append(\"__FAKE_FEATURE__\")\n      shapes.append(1)\n      types.append(tf.float32)\n    out_list, example_batch = parse_instance_ops.parse_example_batch_v2(\n        tensor, names, shapes, types, extra_features or [])\n    features = _assemble([], names, shapes, types, out_list)\n    parser_ctx = get_default_parser_ctx()\n    if parser_ctx.sharding_sparse_fids_op_params is not None and (parser_ctx.sharding_sparse_fids_op_params.use_gpu or FLAGS.dataset_use_dataservice):\n      sharding_sparse_fids_with_context(example_batch, features, parser_ctx)\n    else:\n      features[\n          ParserCtx.sharding_sparse_fids_sparse_features_key] = example_batch\n    if \"__FAKE_FEATURE__\" in features:\n      del features[\"__FAKE_FEATURE__\"]\n    return features\n  else:\n    types.extend([tf.int64] * len(sparse_features))\n    out_list = parse_instance_ops.parse_example_batch(tensor, names, shapes,\n                                                      types, extra_features or\n                                                      [])\n    return _assemble(sparse_features,\n                     names,\n                     shapes,\n                     types,\n                     out_list,\n                     batch_size=batch_size)\n\n\n@monolith_export\ndef sharding_sparse_fids(tensor: tf.Tensor,\n                         ps_num: int,\n                         feature_cfgs: FeatureConfigs,\n                         unique: bool,\n                         input_type: str,\n                         parallel_flag: int = 0,\n                         fid_list_ret_list: bool = False,\n                         version: int = 5):\n  assert input_type in [\"example\", \"examplebatch\", \"example_batch\", \"instance\"]\n  input_type = 'examplebatch' if input_type == 'example_batch' else input_type\n  table_name_list = []\n  for cfg in feature_cfgs.feature_configs.values():\n    if cfg.table not in table_name_list:\n      table_name_list.append(cfg.table)\n  table_name_list.sort()\n  ps_num = 1 if ps_num == 0 else ps_num\n  logging.info(\n      f\"num of multi_type_hashtable is {ps_num} {len(table_name_list)}: [{table_name_list}]\"\n  )\n  table_count = len(table_name_list) * ps_num\n  fid_list_emb_row_lenth = None\n  fid_list_table_row_length = None\n  fid_list_shard_row_lenth = None\n  fid_list_row_splits_size = None\n  nfl_size = None\n  feature_size = None\n  fid_size = None\n  emb_size = None\n  (tensor,), start_ts = logging_ops.tensors_timestamp([tensor])\n  if version == 5:\n    fid_list, fid_list_row_splits, fid_list_row_splits_size, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size = parse_instance_ops.sharding_sparse_fids_v5(\n        pb_input=tensor,\n        ps_num=ps_num,\n        feature_cfgs=feature_cfgs.SerializeToString(),\n        N=table_count,\n        unique=unique,\n        input_type=input_type,\n        parallel_flag=parallel_flag)\n  elif version == 4:\n    fid_list, fid_list_row_splits, fid_list_table_row_length, fid_list_shard_row_lenth, fid_list_emb_row_lenth, fid_offset, feature_offset, nfl_offset, batch_size = parse_instance_ops.sharding_sparse_fids_v4(\n        pb_input=tensor,\n        ps_num=ps_num,\n        feature_cfgs=feature_cfgs.SerializeToString(),\n        unique=unique,\n        input_type=input_type,\n        parallel_flag=parallel_flag)\n  elif version == 3:\n    fid_list, fid_list_row_splits, fid_offset, feature_offset, nfl_offset, batch_size = parse_instance_ops.sharding_sparse_fids_v3(\n        pb_input=tensor,\n        ps_num=ps_num,\n        feature_cfgs=feature_cfgs.SerializeToString(),\n        N=table_count,\n        unique=unique,\n        input_type=input_type,\n        parallel_flag=parallel_flag,\n        single_thread_feature_watermark=4*80000)\n    fid_list_row_splits_size = [None] * table_count\n  elif version == 2:\n    fid_list, fid_list_row_splits, fid_offset, feature_offset, nfl_offset, batch_size = parse_instance_ops.sharding_sparse_fids_v2(\n        pb_input=tensor,\n        ps_num=ps_num,\n        feature_cfgs=feature_cfgs.SerializeToString(),\n        N=table_count,\n        unique=unique,\n        input_type=input_type,\n        parallel_flag=parallel_flag)\n    fid_list_row_splits_size = [None] * table_count\n  else:\n    fid_list, fid_offset, feature_offset, nfl_offset, batch_size = parse_instance_ops.sharding_sparse_fids(\n        pb_input=tensor,\n        ps_num=ps_num,\n        feature_cfgs=feature_cfgs.SerializeToString(),\n        N=table_count,\n        unique=unique,\n        input_type=input_type,\n        parallel_flag=parallel_flag)\n    fid_list_row_splits = [None] * table_count\n    fid_list_row_splits_size = [None] * table_count\n  (fid_offset,), end_ts = logging_ops.tensors_timestamp([fid_offset])\n  def emit_sharding_sparse_timer_ops(interval):\n    return [\n        logging_ops.emit_timer(\n            \"sharding_sparse_fids\",\n            tf.cast(interval, tf.float32),\n            tags={\n                \"model_name\": native_task_context.get().model_name\n            })\n    ]\n  with tf.control_dependencies(emit_sharding_sparse_timer_ops(end_ts - start_ts)):\n    tf.no_op()\n  if version != 4:\n    assert len(fid_list) == table_count\n    assert len(fid_list_row_splits) == table_count\n  if version == 5:\n    assert len(fid_list_row_splits_size) == table_count\n  if fid_list_ret_list or version == 4:\n    return fid_list, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size, fid_list_row_splits, fid_list_row_splits_size, fid_list_emb_row_lenth, fid_list_table_row_length, fid_list_shard_row_lenth\n  ret = {}\n  ret_row_split = {}\n  ret_row_split_size = {}\n  index = 0\n  for table_idx in range(len(table_name_list)):\n    table_name = table_name_list[table_idx]\n    for ps_index in range(ps_num):\n      ret[table_name + \":\" + str(ps_index)] = fid_list[index]\n      ret_row_split[table_name + \":\" +\n                    str(ps_index)] = fid_list_row_splits[index]\n      ret_row_split_size[table_name + \":\" +\n                         str(ps_index)] = fid_list_row_splits_size[index]\n      index += 1\n  return ret, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, feature_size, fid_size, emb_size, ret_row_split, ret_row_split_size, fid_list_emb_row_lenth, fid_list_table_row_length, fid_list_shard_row_lenth\n\n\ndef sharding_sparse_fids_with_context(sparse_features: tf.Tensor,\n                                      features,\n                                      parser_ctx: ParserCtx = None):\n  if parser_ctx is None:\n    parser_ctx = get_default_parser_ctx()\n  shards, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, \\\n  feature_size, fid_size, emb_size, shards_row_split, shards_row_split_size, \\\n  fid_list_emb_row_lenth, fid_list_table_row_length, fid_list_shard_row_lenth = sharding_sparse_fids(\n      sparse_features,\n      ps_num=parser_ctx.sharding_sparse_fids_op_params.num_ps,\n      feature_cfgs=parser_ctx.sharding_sparse_fids_op_params.feature_configs,\n      unique=parser_ctx.sharding_sparse_fids_op_params.unique(),\n      input_type=parser_ctx.parser_type,\n      fid_list_ret_list=parser_ctx.sharding_sparse_fids_op_params.enable_gpu_emb,\n      parallel_flag=1 if parser_ctx.sharding_sparse_fids_op_params.use_gpu else 0,\n      version=4 if parser_ctx.sharding_sparse_fids_op_params.enable_gpu_emb else 5)\n\n  if parser_ctx.sharding_sparse_fids_op_params.enable_gpu_emb:\n    '''\n    table_count = len(shards) // parser_ctx.sharding_sparse_fids_op_params.num_ps\n\n    def tensor_list_to_ragged_tensor(tensor_list):\n      if len(tensor_list) == 1:\n        shard_ragged_tensor = tf.RaggedTensor.from_row_starts(\n            tensor_list[0], tf.constant([0], dtype=tf.int32), validate=False)\n      else:\n        shard_ragged_tensor = tf.ragged.stack(tensor_list)\n      return shard_ragged_tensor\n\n    shards_new_order_list = []\n    for ps_i in range(parser_ctx.sharding_sparse_fids_op_params.num_ps):\n      for table_i in range(table_count):\n        shards_new_order_list.append(shards[ps_i + table_i * parser_ctx.sharding_sparse_fids_op_params.num_ps])\n    shard_ragged_tensor = tensor_list_to_ragged_tensor(shards_new_order_list)\n    shards_value = shard_ragged_tensor.values\n    shards_table_row_lengths = shard_ragged_tensor.row_lengths()\n    shards_row_lengths = tf.reduce_sum(tf.reshape(shards_table_row_lengths,\n                                                  [parser_ctx.sharding_sparse_fids_op_params.num_ps, table_count]),\n                                       axis=-1)\n    '''\n    shards_value = shards\n    shards_row_lengths = fid_list_shard_row_lenth\n    shards_table_row_lengths = fid_list_table_row_length\n\n    parser_ctx.sharding_sparse_fids_features_insert_to_features(\n        {\n            \"shards_value\": shards_value,\n            \"shards_row_lengths\": shards_row_lengths,\n            \"shards_table_row_lengths\": shards_table_row_lengths,\n            \"fid_offset\": fid_offset,\n            \"feature_offset\": feature_offset,\n            \"nfl_offset\": nfl_offset,\n            \"batch_size\": batch_size,\n            \"fid_list_emb_row_lenth\": fid_list_emb_row_lenth,\n        }, features)\n  else:\n    features_dict = {\n        \"shards\": shards,\n        \"fid_offset\": fid_offset,\n        \"feature_offset\": feature_offset,\n        \"nfl_offset\": nfl_offset,\n        \"batch_size\": batch_size,\n        \"nfl_size\": nfl_size,\n        \"feature_size\": feature_size,\n        \"fid_size\": fid_size,\n        \"emb_size\": emb_size,\n    }\n    if parser_ctx.sharding_sparse_fids_op_params.use_native_multi_hash_table:\n      features_dict.update({\"shards_row_split\": shards_row_split})\n      features_dict.update({\"shards_row_split_size\": shards_row_split_size})\n    parser_ctx.sharding_sparse_fids_features_insert_to_features(\n        features_dict, features)\n\n\ndef parse_example_batch_list(\n    tensor: List[tf.Tensor],\n    label_config: str = None,\n    positive_label: float = 1.0,\n    negative_label: float = 0.0,\n    names: List[str] = None,\n    shapes: List[int] = None,\n    dtypes: List[tf.dtypes.DType] = None,\n    extra_features: List[str] = None) -> Dict[str, tf.Tensor]:\n  names, shapes, dtypes = list(names), list(shapes), list(dtypes)\n  get_default_parser_ctx().parser_type = 'examplebatch'\n  label_conf = LabelConf()\n  if label_config is not None and len(label_config) > 0:\n    tasks = label_config.split(';')\n    names.append('label')\n    shapes.append(len(tasks))\n    dtypes.append(tf.float32)\n\n    for task in tasks:\n      task_conf = label_conf.conf.add()\n      pos_actions, neg_actions = task.split(':')\n      pos_actions_list = [\n          int(pos) for pos in pos_actions.split(',') if len(pos) > 0\n      ]\n      neg_actions_list = [\n          int(neg) for neg in neg_actions.split(',') if len(neg) > 0\n      ]\n      task_conf.pos_actions.extend(pos_actions_list)\n      task_conf.neg_actions.extend(neg_actions_list)\n\n  sparse_features = []\n  for i, name in enumerate(names):\n    if shapes[i] == -1:\n      sparse_features.append(name)\n      dtypes.append(tf.int64)\n\n  assert len(names) == len(set(names)), \"deplicate names, pls check!\"\n  out_list = parse_instance_ops.parse_example_batch_list(\n      tensor,\n      label_config=label_conf.SerializeToString(),\n      names=names,\n      shapes=shapes,\n      dtypes=dtypes,\n      extra_names=extra_features,\n      positive_label=positive_label,\n      negative_label=negative_label)\n  return _assemble(sparse_features, names, shapes, dtypes, out_list)\n"
  },
  {
    "path": "monolith/native_training/data/test_data/BUILD",
    "content": "package(default_visibility = [\n    \"//monolith/integration_test:__subpackages__\",\n    \"//monolith/native_training:__subpackages__\",\n])\n\nfilegroup(\n    name = \"test_feature_lists\",\n    srcs = [\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/data/test_data/mhy.conf",
    "content": "feed_name=area; shared=true; feature_id=368235\nfeed_name=att_traced; shared=true; feature_id=368245\nfeed_name=bhv_scm; shared=true; feature_id=368239\nfeed_name=bhv_spm; shared=true; feature_id=368240\nfeed_name=bhv_spm_1; shared=true; feature_id=368241\nfeed_name=bhv_spm_2; shared=true; feature_id=368242\nfeed_name=bhv_spm_3; shared=true; feature_id=368243\nfeed_name=bhv_spm_4; shared=true; feature_id=368244\nfeed_name=bhv_time_hour; shared=true; feature_id=368237\nfeed_name=bhv_time_monthday; shared=true; feature_id=368246\nfeed_name=bhv_time_weekday; shared=true; feature_id=368238\nfeed_name=city; shared=true; feature_id=368233\nfeed_name=client_version; shared=true; feature_id=368227\nfeed_name=country; shared=true; feature_id=368231\nfeed_name=device_model; shared=true; feature_id=368229\nfeed_name=district; shared=true; feature_id=368234\nfeed_name=doc_author_fans_10; feature_id=368214\nfeed_name=doc_author_id; feature_id=368198\nfeed_name=doc_author_level; feature_id=368213\nfeed_name=doc_author_name; feature_id=368212\nfeed_name=doc_cate1; feature_id=368195\nfeed_name=doc_cate2; feature_id=368196\nfeed_name=doc_cate3; feature_id=368197\nfeed_name=doc_collect_cnt_10; feature_id=368201\nfeed_name=doc_collection; feature_id=368215\nfeed_name=doc_comment_cnt_10; feature_id=368203\nfeed_name=doc_content_length_2; feature_id=368219\nfeed_name=doc_create_time; feature_id=368220\nfeed_name=doc_detail_pic_num; feature_id=368209\nfeed_name=doc_expire_time; feature_id=368222\nfeed_name=doc_id; feature_id=368192\nfeed_name=doc_id_post_click_180d; shared=true; feature_id=368733\nfeed_name=doc_id_post_click_1d; shared=true; feature_id=368730\nfeed_name=doc_id_post_click_1h; shared=true; feature_id=368728\nfeed_name=doc_id_post_click_30d; shared=true; feature_id=368732\nfeed_name=doc_id_post_click_6h; shared=true; feature_id=368729\nfeed_name=doc_id_post_click_7d; shared=true; feature_id=368731\nfeed_name=doc_id_post_favorite_180d; shared=true; feature_id=368739\nfeed_name=doc_id_post_favorite_1d; shared=true; feature_id=368736\nfeed_name=doc_id_post_favorite_1h; shared=true; feature_id=368734\nfeed_name=doc_id_post_favorite_30d; shared=true; feature_id=368738\nfeed_name=doc_id_post_favorite_6h; shared=true; feature_id=368735\nfeed_name=doc_id_post_favorite_7d; shared=true; feature_id=368737\nfeed_name=doc_id_post_praise_180d; shared=true; feature_id=368751\nfeed_name=doc_id_post_praise_1d; shared=true; feature_id=368748\nfeed_name=doc_id_post_praise_1h; shared=true; feature_id=368746\nfeed_name=doc_id_post_praise_30d; shared=true; feature_id=368750\nfeed_name=doc_id_post_praise_6h; shared=true; feature_id=368747\nfeed_name=doc_id_post_praise_7d; shared=true; feature_id=368749\nfeed_name=doc_id_post_share_180d; shared=true; feature_id=368745\nfeed_name=doc_id_post_share_1d; shared=true; feature_id=368742\nfeed_name=doc_id_post_share_1h; shared=true; feature_id=368740\nfeed_name=doc_id_post_share_30d; shared=true; feature_id=368744\nfeed_name=doc_id_post_share_6h; shared=true; feature_id=368741\nfeed_name=doc_id_post_share_7d; shared=true; feature_id=368743\nfeed_name=doc_keyword; feature_id=368211\nfeed_name=doc_location_tag; feature_id=368217\nfeed_name=doc_pic_url; feature_id=368206\nfeed_name=doc_praise_cnt_10; feature_id=368202\nfeed_name=doc_pub_time; feature_id=368221\nfeed_name=doc_rating; feature_id=368205\nfeed_name=doc_related_goods_ids; feature_id=368210\nfeed_name=doc_share_cnt_10; feature_id=368200\nfeed_name=doc_source_id; feature_id=368204\nfeed_name=doc_tags; feature_id=368199\nfeed_name=doc_title_length; feature_id=368218\nfeed_name=doc_title_terms; feature_id=368193\nfeed_name=doc_topic_tag; feature_id=368216\nfeed_name=doc_type; feature_id=368194\nfeed_name=doc_video_duration_10; feature_id=368208\nfeed_name=doc_video_url; feature_id=368207\nfeed_name=fake_context_id; shared=true; feature_id=368230\nfeed_name=goods_exposure_cnt_lt; feature_id=449545\nfeed_name=goods_is_prepublic; feature_id=431592\nfeed_name=goods_op_rec_status; feature_id=368247\nfeed_name=goods_quality_score; feature_id=431591\nfeed_name=goods_rec_scene_id; feature_id=408247\nfeed_name=network; shared=true; feature_id=368228\nfeed_name=os; shared=true; feature_id=368225\nfeed_name=os_version; shared=true; feature_id=368226\nfeed_name=page; shared=true; feature_id=368223\nfeed_name=platform; shared=true; feature_id=368224\nfeed_name=province; shared=true; feature_id=368232\nfeed_name=time; shared=true; feature_id=368236\nfeed_name=user_age; shared=true; feature_id=368181\nfeed_name=user_area; shared=true; feature_id=368189\nfeed_name=user_city; shared=true; feature_id=368187\nfeed_name=user_country; shared=true; feature_id=368185\nfeed_name=user_device_id; shared=true; feature_id=368183\nfeed_name=user_district; shared=true; feature_id=368188\nfeed_name=user_gender; shared=true; feature_id=368182\nfeed_name=user_id; shared=true; feature_id=368180\nfeed_name=user_is_prepublic; shared=true; feature_id=431587\nfeed_name=user_lt_doc_author_id_cart_cp; shared=true; feature_id=368361\nfeed_name=user_lt_doc_author_id_click_cp; shared=true; feature_id=368332\nfeed_name=user_lt_doc_author_id_conversion_cp; shared=true; feature_id=368385\nfeed_name=user_lt_doc_author_id_favorite_cp; shared=true; feature_id=368333\nfeed_name=user_lt_doc_author_id_praise_cp; shared=true; feature_id=368334\nfeed_name=user_lt_doc_author_id_query_cp; shared=true; feature_id=368335\nfeed_name=user_lt_doc_cate1_cart_cp; shared=true; feature_id=368352\nfeed_name=user_lt_doc_cate1_click_cp; shared=true; feature_id=368320\nfeed_name=user_lt_doc_cate1_conversion_cp; shared=true; feature_id=368376\nfeed_name=user_lt_doc_cate1_favorite_cp; shared=true; feature_id=368321\nfeed_name=user_lt_doc_cate1_praise_cp; shared=true; feature_id=368322\nfeed_name=user_lt_doc_cate1_query_cp; shared=true; feature_id=368323\nfeed_name=user_lt_doc_cate2_cart_cp; shared=true; feature_id=368355\nfeed_name=user_lt_doc_cate2_click_cp; shared=true; feature_id=368324\nfeed_name=user_lt_doc_cate2_conversion_cp; shared=true; feature_id=368379\nfeed_name=user_lt_doc_cate2_favorite_cp; shared=true; feature_id=368325\nfeed_name=user_lt_doc_cate2_praise_cp; shared=true; feature_id=368326\nfeed_name=user_lt_doc_cate2_query_cp; shared=true; feature_id=368327\nfeed_name=user_lt_doc_cate3_cart_cp; shared=true; feature_id=368358\nfeed_name=user_lt_doc_cate3_click_cp; shared=true; feature_id=368328\nfeed_name=user_lt_doc_cate3_conversion_cp; shared=true; feature_id=368382\nfeed_name=user_lt_doc_cate3_favorite_cp; shared=true; feature_id=368329\nfeed_name=user_lt_doc_cate3_praise_cp; shared=true; feature_id=368330\nfeed_name=user_lt_doc_cate3_query_cp; shared=true; feature_id=368331\nfeed_name=user_lt_doc_id_cart_cp; shared=true; feature_id=368346\nfeed_name=user_lt_doc_id_click_cp; shared=true; feature_id=368312\nfeed_name=user_lt_doc_id_conversion_cp; shared=true; feature_id=368370\nfeed_name=user_lt_doc_id_favorite_cp; shared=true; feature_id=368313\nfeed_name=user_lt_doc_id_praise_cp; shared=true; feature_id=368314\nfeed_name=user_lt_doc_id_query_cp; shared=true; feature_id=368315\nfeed_name=user_lt_doc_keyword_cart_cp; shared=true; feature_id=368367\nfeed_name=user_lt_doc_keyword_click_cp; shared=true; feature_id=368340\nfeed_name=user_lt_doc_keyword_conversion_cp; shared=true; feature_id=368391\nfeed_name=user_lt_doc_keyword_favorite_cp; shared=true; feature_id=368341\nfeed_name=user_lt_doc_keyword_praise_cp; shared=true; feature_id=368342\nfeed_name=user_lt_doc_keyword_query_cp; shared=true; feature_id=368343\nfeed_name=user_lt_doc_tags_cart_cp; shared=true; feature_id=368364\nfeed_name=user_lt_doc_tags_click_cp; shared=true; feature_id=368336\nfeed_name=user_lt_doc_tags_conversion_cp; shared=true; feature_id=368388\nfeed_name=user_lt_doc_tags_favorite_cp; shared=true; feature_id=368337\nfeed_name=user_lt_doc_tags_praise_cp; shared=true; feature_id=368338\nfeed_name=user_lt_doc_tags_query_cp; shared=true; feature_id=368339\nfeed_name=user_lt_doc_title_terms_cart_cp; shared=true; feature_id=368349\nfeed_name=user_lt_doc_title_terms_click_cp; shared=true; feature_id=368316\nfeed_name=user_lt_doc_title_terms_conversion_cp; shared=true; feature_id=368373\nfeed_name=user_lt_doc_title_terms_favorite_cp; shared=true; feature_id=368317\nfeed_name=user_lt_doc_title_terms_praise_cp; shared=true; feature_id=368318\nfeed_name=user_lt_doc_title_terms_query_cp; shared=true; feature_id=368319\nfeed_name=user_membership_level; shared=true; feature_id=368184\nfeed_name=user_province; shared=true; feature_id=368186\nfeed_name=user_quality_score; shared=true; feature_id=431588\nfeed_name=user_recent_click_doc_cate1_180d; shared=true; feature_id=368607\nfeed_name=user_recent_click_doc_cate1_1d; shared=true; feature_id=368604\nfeed_name=user_recent_click_doc_cate1_1h; shared=true; feature_id=368602\nfeed_name=user_recent_click_doc_cate1_30d; shared=true; feature_id=368606\nfeed_name=user_recent_click_doc_cate1_6h; shared=true; feature_id=368603\nfeed_name=user_recent_click_doc_cate1_7d; shared=true; feature_id=368605\nfeed_name=user_recent_click_doc_cate2_180d; shared=true; feature_id=368619\nfeed_name=user_recent_click_doc_cate2_1d; shared=true; feature_id=368616\nfeed_name=user_recent_click_doc_cate2_1h; shared=true; feature_id=368614\nfeed_name=user_recent_click_doc_cate2_30d; shared=true; feature_id=368618\nfeed_name=user_recent_click_doc_cate2_6h; shared=true; feature_id=368615\nfeed_name=user_recent_click_doc_cate2_7d; shared=true; feature_id=368617\nfeed_name=user_recent_click_doc_cate3_180d; shared=true; feature_id=368613\nfeed_name=user_recent_click_doc_cate3_1d; shared=true; feature_id=368610\nfeed_name=user_recent_click_doc_cate3_1h; shared=true; feature_id=368608\nfeed_name=user_recent_click_doc_cate3_30d; shared=true; feature_id=368612\nfeed_name=user_recent_click_doc_cate3_6h; shared=true; feature_id=368609\nfeed_name=user_recent_click_doc_cate3_7d; shared=true; feature_id=368611\nfeed_name=user_recent_click_doc_id_180d; shared=true; feature_id=368625\nfeed_name=user_recent_click_doc_id_1d; shared=true; feature_id=368622\nfeed_name=user_recent_click_doc_id_1h; shared=true; feature_id=368620\nfeed_name=user_recent_click_doc_id_30d; shared=true; feature_id=368624\nfeed_name=user_recent_click_doc_id_6h; shared=true; feature_id=368621\nfeed_name=user_recent_click_doc_id_7d; shared=true; feature_id=368623\nfeed_name=user_recent_click_doc_keyword_180d; shared=true; feature_id=368601\nfeed_name=user_recent_click_doc_keyword_1d; shared=true; feature_id=368598\nfeed_name=user_recent_click_doc_keyword_1h; shared=true; feature_id=368596\nfeed_name=user_recent_click_doc_keyword_30d; shared=true; feature_id=368600\nfeed_name=user_recent_click_doc_keyword_6h; shared=true; feature_id=368597\nfeed_name=user_recent_click_doc_keyword_7d; shared=true; feature_id=368599\nfeed_name=user_recent_click_doc_tags_180d; shared=true; feature_id=368631\nfeed_name=user_recent_click_doc_tags_1d; shared=true; feature_id=368628\nfeed_name=user_recent_click_doc_tags_1h; shared=true; feature_id=368626\nfeed_name=user_recent_click_doc_tags_30d; shared=true; feature_id=368630\nfeed_name=user_recent_click_doc_tags_6h; shared=true; feature_id=368627\nfeed_name=user_recent_click_doc_tags_7d; shared=true; feature_id=368629\nfeed_name=user_recent_click_doc_topic_tag_180d; shared=true; feature_id=368595\nfeed_name=user_recent_click_doc_topic_tag_1d; shared=true; feature_id=368592\nfeed_name=user_recent_click_doc_topic_tag_1h; shared=true; feature_id=368590\nfeed_name=user_recent_click_doc_topic_tag_30d; shared=true; feature_id=368594\nfeed_name=user_recent_click_doc_topic_tag_6h; shared=true; feature_id=368591\nfeed_name=user_recent_click_doc_topic_tag_7d; shared=true; feature_id=368593\nfeed_name=user_recent_click_doc_type_180d; shared=true; feature_id=368589\nfeed_name=user_recent_click_doc_type_1d; shared=true; feature_id=368586\nfeed_name=user_recent_click_doc_type_1h; shared=true; feature_id=368584\nfeed_name=user_recent_click_doc_type_30d; shared=true; feature_id=368588\nfeed_name=user_recent_click_doc_type_6h; shared=true; feature_id=368585\nfeed_name=user_recent_click_doc_type_7d; shared=true; feature_id=368587\nfeed_name=user_recent_exposure_doc_cate1_180d; shared=true; feature_id=368703\nfeed_name=user_recent_exposure_doc_cate1_1d; shared=true; feature_id=368700\nfeed_name=user_recent_exposure_doc_cate1_1h; shared=true; feature_id=368698\nfeed_name=user_recent_exposure_doc_cate1_30d; shared=true; feature_id=368702\nfeed_name=user_recent_exposure_doc_cate1_6h; shared=true; feature_id=368699\nfeed_name=user_recent_exposure_doc_cate1_7d; shared=true; feature_id=368701\nfeed_name=user_recent_exposure_doc_cate2_180d; shared=true; feature_id=368715\nfeed_name=user_recent_exposure_doc_cate2_1d; shared=true; feature_id=368712\nfeed_name=user_recent_exposure_doc_cate2_1h; shared=true; feature_id=368710\nfeed_name=user_recent_exposure_doc_cate2_30d; shared=true; feature_id=368714\nfeed_name=user_recent_exposure_doc_cate2_6h; shared=true; feature_id=368711\nfeed_name=user_recent_exposure_doc_cate2_7d; shared=true; feature_id=368713\nfeed_name=user_recent_exposure_doc_cate3_180d; shared=true; feature_id=368709\nfeed_name=user_recent_exposure_doc_cate3_1d; shared=true; feature_id=368706\nfeed_name=user_recent_exposure_doc_cate3_1h; shared=true; feature_id=368704\nfeed_name=user_recent_exposure_doc_cate3_30d; shared=true; feature_id=368708\nfeed_name=user_recent_exposure_doc_cate3_6h; shared=true; feature_id=368705\nfeed_name=user_recent_exposure_doc_cate3_7d; shared=true; feature_id=368707\nfeed_name=user_recent_exposure_doc_id_180d; shared=true; feature_id=368721\nfeed_name=user_recent_exposure_doc_id_1d; shared=true; feature_id=368718\nfeed_name=user_recent_exposure_doc_id_1h; shared=true; feature_id=368716\nfeed_name=user_recent_exposure_doc_id_30d; shared=true; feature_id=368720\nfeed_name=user_recent_exposure_doc_id_6h; shared=true; feature_id=368717\nfeed_name=user_recent_exposure_doc_id_7d; shared=true; feature_id=368719\nfeed_name=user_recent_exposure_doc_keyword_180d; shared=true; feature_id=368697\nfeed_name=user_recent_exposure_doc_keyword_1d; shared=true; feature_id=368694\nfeed_name=user_recent_exposure_doc_keyword_1h; shared=true; feature_id=368692\nfeed_name=user_recent_exposure_doc_keyword_30d; shared=true; feature_id=368696\nfeed_name=user_recent_exposure_doc_keyword_6h; shared=true; feature_id=368693\nfeed_name=user_recent_exposure_doc_keyword_7d; shared=true; feature_id=368695\nfeed_name=user_recent_exposure_doc_tags_180d; shared=true; feature_id=368727\nfeed_name=user_recent_exposure_doc_tags_1d; shared=true; feature_id=368724\nfeed_name=user_recent_exposure_doc_tags_1h; shared=true; feature_id=368722\nfeed_name=user_recent_exposure_doc_tags_30d; shared=true; feature_id=368726\nfeed_name=user_recent_exposure_doc_tags_6h; shared=true; feature_id=368723\nfeed_name=user_recent_exposure_doc_tags_7d; shared=true; feature_id=368725\nfeed_name=user_recent_exposure_doc_topic_tag_180d; shared=true; feature_id=368691\nfeed_name=user_recent_exposure_doc_topic_tag_1d; shared=true; feature_id=368688\nfeed_name=user_recent_exposure_doc_topic_tag_1h; shared=true; feature_id=368686\nfeed_name=user_recent_exposure_doc_topic_tag_30d; shared=true; feature_id=368690\nfeed_name=user_recent_exposure_doc_topic_tag_6h; shared=true; feature_id=368687\nfeed_name=user_recent_exposure_doc_topic_tag_7d; shared=true; feature_id=368689\nfeed_name=user_recent_exposure_doc_type_180d; shared=true; feature_id=368685\nfeed_name=user_recent_exposure_doc_type_1d; shared=true; feature_id=368682\nfeed_name=user_recent_exposure_doc_type_1h; shared=true; feature_id=368680\nfeed_name=user_recent_exposure_doc_type_30d; shared=true; feature_id=368684\nfeed_name=user_recent_exposure_doc_type_6h; shared=true; feature_id=368681\nfeed_name=user_recent_exposure_doc_type_7d; shared=true; feature_id=368683\nfeed_name=user_recent_favorite_doc_cate1_180d; shared=true; feature_id=368559\nfeed_name=user_recent_favorite_doc_cate1_1d; shared=true; feature_id=368556\nfeed_name=user_recent_favorite_doc_cate1_1h; shared=true; feature_id=368554\nfeed_name=user_recent_favorite_doc_cate1_30d; shared=true; feature_id=368558\nfeed_name=user_recent_favorite_doc_cate1_6h; shared=true; feature_id=368555\nfeed_name=user_recent_favorite_doc_cate1_7d; shared=true; feature_id=368557\nfeed_name=user_recent_favorite_doc_cate2_180d; shared=true; feature_id=368571\nfeed_name=user_recent_favorite_doc_cate2_1d; shared=true; feature_id=368568\nfeed_name=user_recent_favorite_doc_cate2_1h; shared=true; feature_id=368566\nfeed_name=user_recent_favorite_doc_cate2_30d; shared=true; feature_id=368570\nfeed_name=user_recent_favorite_doc_cate2_6h; shared=true; feature_id=368567\nfeed_name=user_recent_favorite_doc_cate2_7d; shared=true; feature_id=368569\nfeed_name=user_recent_favorite_doc_cate3_180d; shared=true; feature_id=368565\nfeed_name=user_recent_favorite_doc_cate3_1d; shared=true; feature_id=368562\nfeed_name=user_recent_favorite_doc_cate3_1h; shared=true; feature_id=368560\nfeed_name=user_recent_favorite_doc_cate3_30d; shared=true; feature_id=368564\nfeed_name=user_recent_favorite_doc_cate3_6h; shared=true; feature_id=368561\nfeed_name=user_recent_favorite_doc_cate3_7d; shared=true; feature_id=368563\nfeed_name=user_recent_favorite_doc_id_180d; shared=true; feature_id=368577\nfeed_name=user_recent_favorite_doc_id_1d; shared=true; feature_id=368574\nfeed_name=user_recent_favorite_doc_id_1h; shared=true; feature_id=368572\nfeed_name=user_recent_favorite_doc_id_30d; shared=true; feature_id=368576\nfeed_name=user_recent_favorite_doc_id_6h; shared=true; feature_id=368573\nfeed_name=user_recent_favorite_doc_id_7d; shared=true; feature_id=368575\nfeed_name=user_recent_favorite_doc_keyword_180d; shared=true; feature_id=368553\nfeed_name=user_recent_favorite_doc_keyword_1d; shared=true; feature_id=368550\nfeed_name=user_recent_favorite_doc_keyword_1h; shared=true; feature_id=368548\nfeed_name=user_recent_favorite_doc_keyword_30d; shared=true; feature_id=368552\nfeed_name=user_recent_favorite_doc_keyword_6h; shared=true; feature_id=368549\nfeed_name=user_recent_favorite_doc_keyword_7d; shared=true; feature_id=368551\nfeed_name=user_recent_favorite_doc_tags_180d; shared=true; feature_id=368583\nfeed_name=user_recent_favorite_doc_tags_1d; shared=true; feature_id=368580\nfeed_name=user_recent_favorite_doc_tags_1h; shared=true; feature_id=368578\nfeed_name=user_recent_favorite_doc_tags_30d; shared=true; feature_id=368582\nfeed_name=user_recent_favorite_doc_tags_6h; shared=true; feature_id=368579\nfeed_name=user_recent_favorite_doc_tags_7d; shared=true; feature_id=368581\nfeed_name=user_recent_favorite_doc_topic_tag_180d; shared=true; feature_id=368547\nfeed_name=user_recent_favorite_doc_topic_tag_1d; shared=true; feature_id=368544\nfeed_name=user_recent_favorite_doc_topic_tag_1h; shared=true; feature_id=368542\nfeed_name=user_recent_favorite_doc_topic_tag_30d; shared=true; feature_id=368546\nfeed_name=user_recent_favorite_doc_topic_tag_6h; shared=true; feature_id=368543\nfeed_name=user_recent_favorite_doc_topic_tag_7d; shared=true; feature_id=368545\nfeed_name=user_recent_favorite_doc_type_180d; shared=true; feature_id=368541\nfeed_name=user_recent_favorite_doc_type_1d; shared=true; feature_id=368538\nfeed_name=user_recent_favorite_doc_type_1h; shared=true; feature_id=368536\nfeed_name=user_recent_favorite_doc_type_30d; shared=true; feature_id=368540\nfeed_name=user_recent_favorite_doc_type_6h; shared=true; feature_id=368537\nfeed_name=user_recent_favorite_doc_type_7d; shared=true; feature_id=368539\nfeed_name=user_recent_praise_doc_cate1_180d; shared=true; feature_id=368655\nfeed_name=user_recent_praise_doc_cate1_1d; shared=true; feature_id=368652\nfeed_name=user_recent_praise_doc_cate1_1h; shared=true; feature_id=368650\nfeed_name=user_recent_praise_doc_cate1_30d; shared=true; feature_id=368654\nfeed_name=user_recent_praise_doc_cate1_6h; shared=true; feature_id=368651\nfeed_name=user_recent_praise_doc_cate1_7d; shared=true; feature_id=368653\nfeed_name=user_recent_praise_doc_cate2_180d; shared=true; feature_id=368667\nfeed_name=user_recent_praise_doc_cate2_1d; shared=true; feature_id=368664\nfeed_name=user_recent_praise_doc_cate2_1h; shared=true; feature_id=368662\nfeed_name=user_recent_praise_doc_cate2_30d; shared=true; feature_id=368666\nfeed_name=user_recent_praise_doc_cate2_6h; shared=true; feature_id=368663\nfeed_name=user_recent_praise_doc_cate2_7d; shared=true; feature_id=368665\nfeed_name=user_recent_praise_doc_cate3_180d; shared=true; feature_id=368661\nfeed_name=user_recent_praise_doc_cate3_1d; shared=true; feature_id=368658\nfeed_name=user_recent_praise_doc_cate3_1h; shared=true; feature_id=368656\nfeed_name=user_recent_praise_doc_cate3_30d; shared=true; feature_id=368660\nfeed_name=user_recent_praise_doc_cate3_6h; shared=true; feature_id=368657\nfeed_name=user_recent_praise_doc_cate3_7d; shared=true; feature_id=368659\nfeed_name=user_recent_praise_doc_id_180d; shared=true; feature_id=368673\nfeed_name=user_recent_praise_doc_id_1d; shared=true; feature_id=368670\nfeed_name=user_recent_praise_doc_id_1h; shared=true; feature_id=368668\nfeed_name=user_recent_praise_doc_id_30d; shared=true; feature_id=368672\nfeed_name=user_recent_praise_doc_id_6h; shared=true; feature_id=368669\nfeed_name=user_recent_praise_doc_id_7d; shared=true; feature_id=368671\nfeed_name=user_recent_praise_doc_keyword_180d; shared=true; feature_id=368649\nfeed_name=user_recent_praise_doc_keyword_1d; shared=true; feature_id=368646\nfeed_name=user_recent_praise_doc_keyword_1h; shared=true; feature_id=368644\nfeed_name=user_recent_praise_doc_keyword_30d; shared=true; feature_id=368648\nfeed_name=user_recent_praise_doc_keyword_6h; shared=true; feature_id=368645\nfeed_name=user_recent_praise_doc_keyword_7d; shared=true; feature_id=368647\nfeed_name=user_recent_praise_doc_tags_180d; shared=true; feature_id=368679\nfeed_name=user_recent_praise_doc_tags_1d; shared=true; feature_id=368676\nfeed_name=user_recent_praise_doc_tags_1h; shared=true; feature_id=368674\nfeed_name=user_recent_praise_doc_tags_30d; shared=true; feature_id=368678\nfeed_name=user_recent_praise_doc_tags_6h; shared=true; feature_id=368675\nfeed_name=user_recent_praise_doc_tags_7d; shared=true; feature_id=368677\nfeed_name=user_recent_praise_doc_topic_tag_180d; shared=true; feature_id=368643\nfeed_name=user_recent_praise_doc_topic_tag_1d; shared=true; feature_id=368640\nfeed_name=user_recent_praise_doc_topic_tag_1h; shared=true; feature_id=368638\nfeed_name=user_recent_praise_doc_topic_tag_30d; shared=true; feature_id=368642\nfeed_name=user_recent_praise_doc_topic_tag_6h; shared=true; feature_id=368639\nfeed_name=user_recent_praise_doc_topic_tag_7d; shared=true; feature_id=368641\nfeed_name=user_recent_praise_doc_type_180d; shared=true; feature_id=368637\nfeed_name=user_recent_praise_doc_type_1d; shared=true; feature_id=368634\nfeed_name=user_recent_praise_doc_type_1h; shared=true; feature_id=368632\nfeed_name=user_recent_praise_doc_type_30d; shared=true; feature_id=368636\nfeed_name=user_recent_praise_doc_type_6h; shared=true; feature_id=368633\nfeed_name=user_recent_praise_doc_type_7d; shared=true; feature_id=368635\nfeed_name=user_recent_share_doc_cate1_180d; shared=true; feature_id=368511\nfeed_name=user_recent_share_doc_cate1_1d; shared=true; feature_id=368508\nfeed_name=user_recent_share_doc_cate1_1h; shared=true; feature_id=368506\nfeed_name=user_recent_share_doc_cate1_30d; shared=true; feature_id=368510\nfeed_name=user_recent_share_doc_cate1_6h; shared=true; feature_id=368507\nfeed_name=user_recent_share_doc_cate1_7d; shared=true; feature_id=368509\nfeed_name=user_recent_share_doc_cate2_180d; shared=true; feature_id=368523\nfeed_name=user_recent_share_doc_cate2_1d; shared=true; feature_id=368520\nfeed_name=user_recent_share_doc_cate2_1h; shared=true; feature_id=368518\nfeed_name=user_recent_share_doc_cate2_30d; shared=true; feature_id=368522\nfeed_name=user_recent_share_doc_cate2_6h; shared=true; feature_id=368519\nfeed_name=user_recent_share_doc_cate2_7d; shared=true; feature_id=368521\nfeed_name=user_recent_share_doc_cate3_180d; shared=true; feature_id=368517\nfeed_name=user_recent_share_doc_cate3_1d; shared=true; feature_id=368514\nfeed_name=user_recent_share_doc_cate3_1h; shared=true; feature_id=368512\nfeed_name=user_recent_share_doc_cate3_30d; shared=true; feature_id=368516\nfeed_name=user_recent_share_doc_cate3_6h; shared=true; feature_id=368513\nfeed_name=user_recent_share_doc_cate3_7d; shared=true; feature_id=368515\nfeed_name=user_recent_share_doc_id_180d; shared=true; feature_id=368529\nfeed_name=user_recent_share_doc_id_1d; shared=true; feature_id=368526\nfeed_name=user_recent_share_doc_id_1h; shared=true; feature_id=368524\nfeed_name=user_recent_share_doc_id_30d; shared=true; feature_id=368528\nfeed_name=user_recent_share_doc_id_6h; shared=true; feature_id=368525\nfeed_name=user_recent_share_doc_id_7d; shared=true; feature_id=368527\nfeed_name=user_recent_share_doc_keyword_180d; shared=true; feature_id=368505\nfeed_name=user_recent_share_doc_keyword_1d; shared=true; feature_id=368502\nfeed_name=user_recent_share_doc_keyword_1h; shared=true; feature_id=368500\nfeed_name=user_recent_share_doc_keyword_30d; shared=true; feature_id=368504\nfeed_name=user_recent_share_doc_keyword_6h; shared=true; feature_id=368501\nfeed_name=user_recent_share_doc_keyword_7d; shared=true; feature_id=368503\nfeed_name=user_recent_share_doc_tags_180d; shared=true; feature_id=368535\nfeed_name=user_recent_share_doc_tags_1d; shared=true; feature_id=368532\nfeed_name=user_recent_share_doc_tags_1h; shared=true; feature_id=368530\nfeed_name=user_recent_share_doc_tags_30d; shared=true; feature_id=368534\nfeed_name=user_recent_share_doc_tags_6h; shared=true; feature_id=368531\nfeed_name=user_recent_share_doc_tags_7d; shared=true; feature_id=368533\nfeed_name=user_recent_share_doc_topic_tag_180d; shared=true; feature_id=368499\nfeed_name=user_recent_share_doc_topic_tag_1d; shared=true; feature_id=368496\nfeed_name=user_recent_share_doc_topic_tag_1h; shared=true; feature_id=368494\nfeed_name=user_recent_share_doc_topic_tag_30d; shared=true; feature_id=368498\nfeed_name=user_recent_share_doc_topic_tag_6h; shared=true; feature_id=368495\nfeed_name=user_recent_share_doc_topic_tag_7d; shared=true; feature_id=368497\nfeed_name=user_recent_share_doc_type_180d; shared=true; feature_id=368493\nfeed_name=user_recent_share_doc_type_1d; shared=true; feature_id=368490\nfeed_name=user_recent_share_doc_type_1h; shared=true; feature_id=368488\nfeed_name=user_recent_share_doc_type_30d; shared=true; feature_id=368492\nfeed_name=user_recent_share_doc_type_6h; shared=true; feature_id=368489\nfeed_name=user_recent_share_doc_type_7d; shared=true; feature_id=368491\nfeed_name=user_register_time; shared=true; feature_id=368191\nfeed_name=user_st_1d_doc_author_id_cart_cp; shared=true; feature_id=368359\nfeed_name=user_st_1d_doc_author_id_cart_recent; shared=true; feature_id=368466\nfeed_name=user_st_1d_doc_author_id_click_cp; shared=true; feature_id=368268\nfeed_name=user_st_1d_doc_author_id_click_recent; shared=true; feature_id=368412\nfeed_name=user_st_1d_doc_author_id_conversion_cp; shared=true; feature_id=368383\nfeed_name=user_st_1d_doc_author_id_conversion_recent; shared=true; feature_id=368482\nfeed_name=user_st_1d_doc_author_id_favorite_cp; shared=true; feature_id=368269\nfeed_name=user_st_1d_doc_author_id_favorite_recent; shared=true; feature_id=368413\nfeed_name=user_st_1d_doc_author_id_praise_cp; shared=true; feature_id=368270\nfeed_name=user_st_1d_doc_author_id_praise_recent; shared=true; feature_id=368414\nfeed_name=user_st_1d_doc_author_id_query_cp; shared=true; feature_id=368271\nfeed_name=user_st_1d_doc_author_id_query_recent; shared=true; feature_id=368415\nfeed_name=user_st_1d_doc_cate1_cart_cp; shared=true; feature_id=368350\nfeed_name=user_st_1d_doc_cate1_cart_recent; shared=true; feature_id=368460\nfeed_name=user_st_1d_doc_cate1_click_cp; shared=true; feature_id=368256\nfeed_name=user_st_1d_doc_cate1_click_recent; shared=true; feature_id=368400\nfeed_name=user_st_1d_doc_cate1_conversion_cp; shared=true; feature_id=368374\nfeed_name=user_st_1d_doc_cate1_conversion_recent; shared=true; feature_id=368476\nfeed_name=user_st_1d_doc_cate1_favorite_cp; shared=true; feature_id=368257\nfeed_name=user_st_1d_doc_cate1_favorite_recent; shared=true; feature_id=368401\nfeed_name=user_st_1d_doc_cate1_praise_cp; shared=true; feature_id=368258\nfeed_name=user_st_1d_doc_cate1_praise_recent; shared=true; feature_id=368402\nfeed_name=user_st_1d_doc_cate1_query_cp; shared=true; feature_id=368259\nfeed_name=user_st_1d_doc_cate1_query_recent; shared=true; feature_id=368403\nfeed_name=user_st_1d_doc_cate2_cart_cp; shared=true; feature_id=368353\nfeed_name=user_st_1d_doc_cate2_cart_recent; shared=true; feature_id=368462\nfeed_name=user_st_1d_doc_cate2_click_cp; shared=true; feature_id=368260\nfeed_name=user_st_1d_doc_cate2_click_recent; shared=true; feature_id=368404\nfeed_name=user_st_1d_doc_cate2_conversion_cp; shared=true; feature_id=368377\nfeed_name=user_st_1d_doc_cate2_conversion_recent; shared=true; feature_id=368478\nfeed_name=user_st_1d_doc_cate2_favorite_cp; shared=true; feature_id=368261\nfeed_name=user_st_1d_doc_cate2_favorite_recent; shared=true; feature_id=368405\nfeed_name=user_st_1d_doc_cate2_praise_cp; shared=true; feature_id=368262\nfeed_name=user_st_1d_doc_cate2_praise_recent; shared=true; feature_id=368406\nfeed_name=user_st_1d_doc_cate2_query_cp; shared=true; feature_id=368263\nfeed_name=user_st_1d_doc_cate2_query_recent; shared=true; feature_id=368407\nfeed_name=user_st_1d_doc_cate3_cart_cp; shared=true; feature_id=368356\nfeed_name=user_st_1d_doc_cate3_cart_recent; shared=true; feature_id=368464\nfeed_name=user_st_1d_doc_cate3_click_cp; shared=true; feature_id=368264\nfeed_name=user_st_1d_doc_cate3_click_recent; shared=true; feature_id=368408\nfeed_name=user_st_1d_doc_cate3_conversion_cp; shared=true; feature_id=368380\nfeed_name=user_st_1d_doc_cate3_conversion_recent; shared=true; feature_id=368480\nfeed_name=user_st_1d_doc_cate3_favorite_cp; shared=true; feature_id=368265\nfeed_name=user_st_1d_doc_cate3_favorite_recent; shared=true; feature_id=368409\nfeed_name=user_st_1d_doc_cate3_praise_cp; shared=true; feature_id=368266\nfeed_name=user_st_1d_doc_cate3_praise_recent; shared=true; feature_id=368410\nfeed_name=user_st_1d_doc_cate3_query_cp; shared=true; feature_id=368267\nfeed_name=user_st_1d_doc_cate3_query_recent; shared=true; feature_id=368411\nfeed_name=user_st_1d_doc_id_cart_cp; shared=true; feature_id=368344\nfeed_name=user_st_1d_doc_id_cart_recent; shared=true; feature_id=368456\nfeed_name=user_st_1d_doc_id_click_cp; shared=true; feature_id=368248\nfeed_name=user_st_1d_doc_id_click_recent; shared=true; feature_id=368392\nfeed_name=user_st_1d_doc_id_conversion_cp; shared=true; feature_id=368368\nfeed_name=user_st_1d_doc_id_conversion_recent; shared=true; feature_id=368472\nfeed_name=user_st_1d_doc_id_favorite_cp; shared=true; feature_id=368249\nfeed_name=user_st_1d_doc_id_favorite_recent; shared=true; feature_id=368393\nfeed_name=user_st_1d_doc_id_praise_cp; shared=true; feature_id=368250\nfeed_name=user_st_1d_doc_id_praise_recent; shared=true; feature_id=368394\nfeed_name=user_st_1d_doc_id_query_cp; shared=true; feature_id=368251\nfeed_name=user_st_1d_doc_id_query_recent; shared=true; feature_id=368395\nfeed_name=user_st_1d_doc_keyword_cart_cp; shared=true; feature_id=368365\nfeed_name=user_st_1d_doc_keyword_cart_recent; shared=true; feature_id=368470\nfeed_name=user_st_1d_doc_keyword_click_cp; shared=true; feature_id=368276\nfeed_name=user_st_1d_doc_keyword_click_recent; shared=true; feature_id=368420\nfeed_name=user_st_1d_doc_keyword_conversion_cp; shared=true; feature_id=368389\nfeed_name=user_st_1d_doc_keyword_conversion_recent; shared=true; feature_id=368486\nfeed_name=user_st_1d_doc_keyword_favorite_cp; shared=true; feature_id=368277\nfeed_name=user_st_1d_doc_keyword_favorite_recent; shared=true; feature_id=368421\nfeed_name=user_st_1d_doc_keyword_praise_cp; shared=true; feature_id=368278\nfeed_name=user_st_1d_doc_keyword_praise_recent; shared=true; feature_id=368422\nfeed_name=user_st_1d_doc_keyword_query_cp; shared=true; feature_id=368279\nfeed_name=user_st_1d_doc_keyword_query_recent; shared=true; feature_id=368423\nfeed_name=user_st_1d_doc_tags_cart_cp; shared=true; feature_id=368362\nfeed_name=user_st_1d_doc_tags_cart_recent; shared=true; feature_id=368468\nfeed_name=user_st_1d_doc_tags_click_cp; shared=true; feature_id=368272\nfeed_name=user_st_1d_doc_tags_click_recent; shared=true; feature_id=368416\nfeed_name=user_st_1d_doc_tags_conversion_cp; shared=true; feature_id=368386\nfeed_name=user_st_1d_doc_tags_conversion_recent; shared=true; feature_id=368484\nfeed_name=user_st_1d_doc_tags_favorite_cp; shared=true; feature_id=368273\nfeed_name=user_st_1d_doc_tags_favorite_recent; shared=true; feature_id=368417\nfeed_name=user_st_1d_doc_tags_praise_cp; shared=true; feature_id=368274\nfeed_name=user_st_1d_doc_tags_praise_recent; shared=true; feature_id=368418\nfeed_name=user_st_1d_doc_tags_query_cp; shared=true; feature_id=368275\nfeed_name=user_st_1d_doc_tags_query_recent; shared=true; feature_id=368419\nfeed_name=user_st_1d_doc_title_terms_cart_cp; shared=true; feature_id=368347\nfeed_name=user_st_1d_doc_title_terms_cart_recent; shared=true; feature_id=368458\nfeed_name=user_st_1d_doc_title_terms_click_cp; shared=true; feature_id=368252\nfeed_name=user_st_1d_doc_title_terms_click_recent; shared=true; feature_id=368396\nfeed_name=user_st_1d_doc_title_terms_conversion_cp; shared=true; feature_id=368371\nfeed_name=user_st_1d_doc_title_terms_conversion_recent; shared=true; feature_id=368474\nfeed_name=user_st_1d_doc_title_terms_favorite_cp; shared=true; feature_id=368253\nfeed_name=user_st_1d_doc_title_terms_favorite_recent; shared=true; feature_id=368397\nfeed_name=user_st_1d_doc_title_terms_praise_cp; shared=true; feature_id=368254\nfeed_name=user_st_1d_doc_title_terms_praise_recent; shared=true; feature_id=368398\nfeed_name=user_st_1d_doc_title_terms_query_cp; shared=true; feature_id=368255\nfeed_name=user_st_1d_doc_title_terms_query_recent; shared=true; feature_id=368399\nfeed_name=user_st_7d_doc_author_id_cart_cp; shared=true; feature_id=368360\nfeed_name=user_st_7d_doc_author_id_cart_recent; shared=true; feature_id=368467\nfeed_name=user_st_7d_doc_author_id_click_cp; shared=true; feature_id=368300\nfeed_name=user_st_7d_doc_author_id_click_recent; shared=true; feature_id=368444\nfeed_name=user_st_7d_doc_author_id_conversion_cp; shared=true; feature_id=368384\nfeed_name=user_st_7d_doc_author_id_conversion_recent; shared=true; feature_id=368483\nfeed_name=user_st_7d_doc_author_id_favorite_cp; shared=true; feature_id=368301\nfeed_name=user_st_7d_doc_author_id_favorite_recent; shared=true; feature_id=368445\nfeed_name=user_st_7d_doc_author_id_praise_cp; shared=true; feature_id=368302\nfeed_name=user_st_7d_doc_author_id_praise_recent; shared=true; feature_id=368446\nfeed_name=user_st_7d_doc_author_id_query_cp; shared=true; feature_id=368303\nfeed_name=user_st_7d_doc_author_id_query_recent; shared=true; feature_id=368447\nfeed_name=user_st_7d_doc_cate1_cart_cp; shared=true; feature_id=368351\nfeed_name=user_st_7d_doc_cate1_cart_recent; shared=true; feature_id=368461\nfeed_name=user_st_7d_doc_cate1_click_cp; shared=true; feature_id=368288\nfeed_name=user_st_7d_doc_cate1_click_recent; shared=true; feature_id=368432\nfeed_name=user_st_7d_doc_cate1_conversion_cp; shared=true; feature_id=368375\nfeed_name=user_st_7d_doc_cate1_conversion_recent; shared=true; feature_id=368477\nfeed_name=user_st_7d_doc_cate1_favorite_cp; shared=true; feature_id=368289\nfeed_name=user_st_7d_doc_cate1_favorite_recent; shared=true; feature_id=368433\nfeed_name=user_st_7d_doc_cate1_praise_cp; shared=true; feature_id=368290\nfeed_name=user_st_7d_doc_cate1_praise_recent; shared=true; feature_id=368434\nfeed_name=user_st_7d_doc_cate1_query_cp; shared=true; feature_id=368291\nfeed_name=user_st_7d_doc_cate1_query_recent; shared=true; feature_id=368435\nfeed_name=user_st_7d_doc_cate2_cart_cp; shared=true; feature_id=368354\nfeed_name=user_st_7d_doc_cate2_cart_recent; shared=true; feature_id=368463\nfeed_name=user_st_7d_doc_cate2_click_cp; shared=true; feature_id=368292\nfeed_name=user_st_7d_doc_cate2_click_recent; shared=true; feature_id=368436\nfeed_name=user_st_7d_doc_cate2_conversion_cp; shared=true; feature_id=368378\nfeed_name=user_st_7d_doc_cate2_conversion_recent; shared=true; feature_id=368479\nfeed_name=user_st_7d_doc_cate2_favorite_cp; shared=true; feature_id=368293\nfeed_name=user_st_7d_doc_cate2_favorite_recent; shared=true; feature_id=368437\nfeed_name=user_st_7d_doc_cate2_praise_cp; shared=true; feature_id=368294\nfeed_name=user_st_7d_doc_cate2_praise_recent; shared=true; feature_id=368438\nfeed_name=user_st_7d_doc_cate2_query_cp; shared=true; feature_id=368295\nfeed_name=user_st_7d_doc_cate2_query_recent; shared=true; feature_id=368439\nfeed_name=user_st_7d_doc_cate3_cart_cp; shared=true; feature_id=368357\nfeed_name=user_st_7d_doc_cate3_cart_recent; shared=true; feature_id=368465\nfeed_name=user_st_7d_doc_cate3_click_cp; shared=true; feature_id=368296\nfeed_name=user_st_7d_doc_cate3_click_recent; shared=true; feature_id=368440\nfeed_name=user_st_7d_doc_cate3_conversion_cp; shared=true; feature_id=368381\nfeed_name=user_st_7d_doc_cate3_conversion_recent; shared=true; feature_id=368481\nfeed_name=user_st_7d_doc_cate3_favorite_cp; shared=true; feature_id=368297\nfeed_name=user_st_7d_doc_cate3_favorite_recent; shared=true; feature_id=368441\nfeed_name=user_st_7d_doc_cate3_praise_cp; shared=true; feature_id=368298\nfeed_name=user_st_7d_doc_cate3_praise_recent; shared=true; feature_id=368442\nfeed_name=user_st_7d_doc_cate3_query_cp; shared=true; feature_id=368299\nfeed_name=user_st_7d_doc_cate3_query_recent; shared=true; feature_id=368443\nfeed_name=user_st_7d_doc_id_cart_cp; shared=true; feature_id=368345\nfeed_name=user_st_7d_doc_id_cart_recent; shared=true; feature_id=368457\nfeed_name=user_st_7d_doc_id_click_cp; shared=true; feature_id=368280\nfeed_name=user_st_7d_doc_id_click_recent; shared=true; feature_id=368424\nfeed_name=user_st_7d_doc_id_conversion_cp; shared=true; feature_id=368369\nfeed_name=user_st_7d_doc_id_conversion_recent; shared=true; feature_id=368473\nfeed_name=user_st_7d_doc_id_favorite_cp; shared=true; feature_id=368281\nfeed_name=user_st_7d_doc_id_favorite_recent; shared=true; feature_id=368425\nfeed_name=user_st_7d_doc_id_praise_cp; shared=true; feature_id=368282\nfeed_name=user_st_7d_doc_id_praise_recent; shared=true; feature_id=368426\nfeed_name=user_st_7d_doc_id_query_cp; shared=true; feature_id=368283\nfeed_name=user_st_7d_doc_id_query_recent; shared=true; feature_id=368427\nfeed_name=user_st_7d_doc_keyword_cart_cp; shared=true; feature_id=368366\nfeed_name=user_st_7d_doc_keyword_cart_recent; shared=true; feature_id=368471\nfeed_name=user_st_7d_doc_keyword_click_cp; shared=true; feature_id=368308\nfeed_name=user_st_7d_doc_keyword_click_recent; shared=true; feature_id=368452\nfeed_name=user_st_7d_doc_keyword_conversion_cp; shared=true; feature_id=368390\nfeed_name=user_st_7d_doc_keyword_conversion_recent; shared=true; feature_id=368487\nfeed_name=user_st_7d_doc_keyword_favorite_cp; shared=true; feature_id=368309\nfeed_name=user_st_7d_doc_keyword_favorite_recent; shared=true; feature_id=368453\nfeed_name=user_st_7d_doc_keyword_praise_cp; shared=true; feature_id=368310\nfeed_name=user_st_7d_doc_keyword_praise_recent; shared=true; feature_id=368454\nfeed_name=user_st_7d_doc_keyword_query_cp; shared=true; feature_id=368311\nfeed_name=user_st_7d_doc_keyword_query_recent; shared=true; feature_id=368455\nfeed_name=user_st_7d_doc_tags_cart_cp; shared=true; feature_id=368363\nfeed_name=user_st_7d_doc_tags_cart_recent; shared=true; feature_id=368469\nfeed_name=user_st_7d_doc_tags_click_cp; shared=true; feature_id=368304\nfeed_name=user_st_7d_doc_tags_click_recent; shared=true; feature_id=368448\nfeed_name=user_st_7d_doc_tags_conversion_cp; shared=true; feature_id=368387\nfeed_name=user_st_7d_doc_tags_conversion_recent; shared=true; feature_id=368485\nfeed_name=user_st_7d_doc_tags_favorite_cp; shared=true; feature_id=368305\nfeed_name=user_st_7d_doc_tags_favorite_recent; shared=true; feature_id=368449\nfeed_name=user_st_7d_doc_tags_praise_cp; shared=true; feature_id=368306\nfeed_name=user_st_7d_doc_tags_praise_recent; shared=true; feature_id=368450\nfeed_name=user_st_7d_doc_tags_query_cp; shared=true; feature_id=368307\nfeed_name=user_st_7d_doc_tags_query_recent; shared=true; feature_id=368451\nfeed_name=user_st_7d_doc_title_terms_cart_cp; shared=true; feature_id=368348\nfeed_name=user_st_7d_doc_title_terms_cart_recent; shared=true; feature_id=368459\nfeed_name=user_st_7d_doc_title_terms_click_cp; shared=true; feature_id=368284\nfeed_name=user_st_7d_doc_title_terms_click_recent; shared=true; feature_id=368428\nfeed_name=user_st_7d_doc_title_terms_conversion_cp; shared=true; feature_id=368372\nfeed_name=user_st_7d_doc_title_terms_conversion_recent; shared=true; feature_id=368475\nfeed_name=user_st_7d_doc_title_terms_favorite_cp; shared=true; feature_id=368285\nfeed_name=user_st_7d_doc_title_terms_favorite_recent; shared=true; feature_id=368429\nfeed_name=user_st_7d_doc_title_terms_praise_cp; shared=true; feature_id=368286\nfeed_name=user_st_7d_doc_title_terms_praise_recent; shared=true; feature_id=368430\nfeed_name=user_st_7d_doc_title_terms_query_cp; shared=true; feature_id=368287\nfeed_name=user_st_7d_doc_title_terms_query_recent; shared=true; feature_id=368431\nfeed_name=user_tags_list; shared=true; feature_id=368190\n\ncolumn_name : area, att_traced, bhv_scm, bhv_spm, bhv_spm_1, bhv_spm_2, bhv_spm_3, bhv_spm_4, bhv_time_hour, bhv_time_monthday, bhv_time_weekday, city, client_version, country, device_model, district, doc_author_fans_10, doc_author_id, doc_author_level, doc_author_name, doc_cate1, doc_cate2, doc_cate3, doc_collect_cnt_10, doc_collection, doc_comment_cnt_10, doc_content_length_2, doc_create_time, doc_detail_pic_num, doc_expire_time, doc_id, doc_id_post_click_180d, doc_id_post_click_1d, doc_id_post_click_1h, doc_id_post_click_30d, doc_id_post_click_6h, doc_id_post_click_7d, doc_id_post_favorite_180d, doc_id_post_favorite_1d, doc_id_post_favorite_1h, doc_id_post_favorite_30d, doc_id_post_favorite_6h, doc_id_post_favorite_7d, doc_id_post_praise_180d, doc_id_post_praise_1d, doc_id_post_praise_1h, doc_id_post_praise_30d, doc_id_post_praise_6h, doc_id_post_praise_7d, doc_id_post_share_180d, doc_id_post_share_1d, doc_id_post_share_1h, doc_id_post_share_30d, doc_id_post_share_6h, doc_id_post_share_7d, doc_keyword, doc_location_tag, doc_pic_url, doc_praise_cnt_10, doc_pub_time, doc_rating, doc_related_goods_ids, doc_share_cnt_10, doc_source_id, doc_tags, doc_title_length, doc_title_terms, doc_topic_tag, doc_type, doc_video_duration_10, doc_video_url, fake_context_id, goods_exposure_cnt_lt, goods_is_prepublic, goods_op_rec_status, goods_quality_score, goods_rec_scene_id, network, os, os_version, page, platform, province, time, user_age, user_area, user_city, user_country, user_device_id, user_district, user_gender, user_id, user_is_prepublic, user_lt_doc_author_id_cart_cp, user_lt_doc_author_id_click_cp, user_lt_doc_author_id_conversion_cp, user_lt_doc_author_id_favorite_cp, user_lt_doc_author_id_praise_cp, user_lt_doc_author_id_query_cp, user_lt_doc_cate1_cart_cp, user_lt_doc_cate1_click_cp, user_lt_doc_cate1_conversion_cp, user_lt_doc_cate1_favorite_cp, user_lt_doc_cate1_praise_cp, user_lt_doc_cate1_query_cp, user_lt_doc_cate2_cart_cp, user_lt_doc_cate2_click_cp, user_lt_doc_cate2_conversion_cp, user_lt_doc_cate2_favorite_cp, user_lt_doc_cate2_praise_cp, user_lt_doc_cate2_query_cp, user_lt_doc_cate3_cart_cp, user_lt_doc_cate3_click_cp, user_lt_doc_cate3_conversion_cp, user_lt_doc_cate3_favorite_cp, user_lt_doc_cate3_praise_cp, user_lt_doc_cate3_query_cp, user_lt_doc_id_cart_cp, user_lt_doc_id_click_cp, user_lt_doc_id_conversion_cp, user_lt_doc_id_favorite_cp, user_lt_doc_id_praise_cp, user_lt_doc_id_query_cp, user_lt_doc_keyword_cart_cp, user_lt_doc_keyword_click_cp, user_lt_doc_keyword_conversion_cp, user_lt_doc_keyword_favorite_cp, user_lt_doc_keyword_praise_cp, user_lt_doc_keyword_query_cp, user_lt_doc_tags_cart_cp, user_lt_doc_tags_click_cp, user_lt_doc_tags_conversion_cp, user_lt_doc_tags_favorite_cp, user_lt_doc_tags_praise_cp, user_lt_doc_tags_query_cp, user_lt_doc_title_terms_cart_cp, user_lt_doc_title_terms_click_cp, user_lt_doc_title_terms_conversion_cp, user_lt_doc_title_terms_favorite_cp, user_lt_doc_title_terms_praise_cp, user_lt_doc_title_terms_query_cp, user_membership_level, user_province, user_quality_score, user_recent_click_doc_cate1_180d, user_recent_click_doc_cate1_1d, user_recent_click_doc_cate1_1h, user_recent_click_doc_cate1_30d, user_recent_click_doc_cate1_6h, user_recent_click_doc_cate1_7d, user_recent_click_doc_cate2_180d, user_recent_click_doc_cate2_1d, user_recent_click_doc_cate2_1h, user_recent_click_doc_cate2_30d, user_recent_click_doc_cate2_6h, user_recent_click_doc_cate2_7d, user_recent_click_doc_cate3_180d, user_recent_click_doc_cate3_1d, user_recent_click_doc_cate3_1h, user_recent_click_doc_cate3_30d, user_recent_click_doc_cate3_6h, user_recent_click_doc_cate3_7d, user_recent_click_doc_id_180d, user_recent_click_doc_id_1d, user_recent_click_doc_id_1h, user_recent_click_doc_id_30d, user_recent_click_doc_id_6h, user_recent_click_doc_id_7d, user_recent_click_doc_keyword_180d, user_recent_click_doc_keyword_1d, user_recent_click_doc_keyword_1h, user_recent_click_doc_keyword_30d, user_recent_click_doc_keyword_6h, user_recent_click_doc_keyword_7d, user_recent_click_doc_tags_180d, user_recent_click_doc_tags_1d, user_recent_click_doc_tags_1h, user_recent_click_doc_tags_30d, user_recent_click_doc_tags_6h, user_recent_click_doc_tags_7d, user_recent_click_doc_topic_tag_180d, user_recent_click_doc_topic_tag_1d, user_recent_click_doc_topic_tag_1h, user_recent_click_doc_topic_tag_30d, user_recent_click_doc_topic_tag_6h, user_recent_click_doc_topic_tag_7d, user_recent_click_doc_type_180d, user_recent_click_doc_type_1d, user_recent_click_doc_type_1h, user_recent_click_doc_type_30d, user_recent_click_doc_type_6h, user_recent_click_doc_type_7d, user_recent_exposure_doc_cate1_180d, user_recent_exposure_doc_cate1_1d, user_recent_exposure_doc_cate1_1h, user_recent_exposure_doc_cate1_30d, user_recent_exposure_doc_cate1_6h, user_recent_exposure_doc_cate1_7d, user_recent_exposure_doc_cate2_180d, user_recent_exposure_doc_cate2_1d, user_recent_exposure_doc_cate2_1h, user_recent_exposure_doc_cate2_30d, user_recent_exposure_doc_cate2_6h, user_recent_exposure_doc_cate2_7d, user_recent_exposure_doc_cate3_180d, user_recent_exposure_doc_cate3_1d, user_recent_exposure_doc_cate3_1h, user_recent_exposure_doc_cate3_30d, user_recent_exposure_doc_cate3_6h, user_recent_exposure_doc_cate3_7d, user_recent_exposure_doc_id_180d, user_recent_exposure_doc_id_1d, user_recent_exposure_doc_id_1h, user_recent_exposure_doc_id_30d, user_recent_exposure_doc_id_6h, user_recent_exposure_doc_id_7d, user_recent_exposure_doc_keyword_180d, user_recent_exposure_doc_keyword_1d, user_recent_exposure_doc_keyword_1h, user_recent_exposure_doc_keyword_30d, user_recent_exposure_doc_keyword_6h, user_recent_exposure_doc_keyword_7d, user_recent_exposure_doc_tags_180d, user_recent_exposure_doc_tags_1d, user_recent_exposure_doc_tags_1h, user_recent_exposure_doc_tags_30d, user_recent_exposure_doc_tags_6h, user_recent_exposure_doc_tags_7d, user_recent_exposure_doc_topic_tag_180d, user_recent_exposure_doc_topic_tag_1d, user_recent_exposure_doc_topic_tag_1h, user_recent_exposure_doc_topic_tag_30d, user_recent_exposure_doc_topic_tag_6h, user_recent_exposure_doc_topic_tag_7d, user_recent_exposure_doc_type_180d, user_recent_exposure_doc_type_1d, user_recent_exposure_doc_type_1h, user_recent_exposure_doc_type_30d, user_recent_exposure_doc_type_6h, user_recent_exposure_doc_type_7d, user_recent_favorite_doc_cate1_180d, user_recent_favorite_doc_cate1_1d, user_recent_favorite_doc_cate1_1h, user_recent_favorite_doc_cate1_30d, user_recent_favorite_doc_cate1_6h, user_recent_favorite_doc_cate1_7d, user_recent_favorite_doc_cate2_180d, user_recent_favorite_doc_cate2_1d, user_recent_favorite_doc_cate2_1h, user_recent_favorite_doc_cate2_30d, user_recent_favorite_doc_cate2_6h, user_recent_favorite_doc_cate2_7d, user_recent_favorite_doc_cate3_180d, user_recent_favorite_doc_cate3_1d, user_recent_favorite_doc_cate3_1h, user_recent_favorite_doc_cate3_30d, user_recent_favorite_doc_cate3_6h, user_recent_favorite_doc_cate3_7d, user_recent_favorite_doc_id_180d, user_recent_favorite_doc_id_1d, user_recent_favorite_doc_id_1h, user_recent_favorite_doc_id_30d, user_recent_favorite_doc_id_6h, user_recent_favorite_doc_id_7d, user_recent_favorite_doc_keyword_180d, user_recent_favorite_doc_keyword_1d, user_recent_favorite_doc_keyword_1h, user_recent_favorite_doc_keyword_30d, user_recent_favorite_doc_keyword_6h, user_recent_favorite_doc_keyword_7d, user_recent_favorite_doc_tags_180d, user_recent_favorite_doc_tags_1d, user_recent_favorite_doc_tags_1h, user_recent_favorite_doc_tags_30d, user_recent_favorite_doc_tags_6h, user_recent_favorite_doc_tags_7d, user_recent_favorite_doc_topic_tag_180d, user_recent_favorite_doc_topic_tag_1d, user_recent_favorite_doc_topic_tag_1h, user_recent_favorite_doc_topic_tag_30d, user_recent_favorite_doc_topic_tag_6h, user_recent_favorite_doc_topic_tag_7d, user_recent_favorite_doc_type_180d, user_recent_favorite_doc_type_1d, user_recent_favorite_doc_type_1h, user_recent_favorite_doc_type_30d, user_recent_favorite_doc_type_6h, user_recent_favorite_doc_type_7d, user_recent_praise_doc_cate1_180d, user_recent_praise_doc_cate1_1d, user_recent_praise_doc_cate1_1h, user_recent_praise_doc_cate1_30d, user_recent_praise_doc_cate1_6h, user_recent_praise_doc_cate1_7d, user_recent_praise_doc_cate2_180d, user_recent_praise_doc_cate2_1d, user_recent_praise_doc_cate2_1h, user_recent_praise_doc_cate2_30d, user_recent_praise_doc_cate2_6h, user_recent_praise_doc_cate2_7d, user_recent_praise_doc_cate3_180d, user_recent_praise_doc_cate3_1d, user_recent_praise_doc_cate3_1h, user_recent_praise_doc_cate3_30d, user_recent_praise_doc_cate3_6h, user_recent_praise_doc_cate3_7d, user_recent_praise_doc_id_180d, user_recent_praise_doc_id_1d, user_recent_praise_doc_id_1h, user_recent_praise_doc_id_30d, user_recent_praise_doc_id_6h, user_recent_praise_doc_id_7d, user_recent_praise_doc_keyword_180d, user_recent_praise_doc_keyword_1d, user_recent_praise_doc_keyword_1h, user_recent_praise_doc_keyword_30d, user_recent_praise_doc_keyword_6h, user_recent_praise_doc_keyword_7d, user_recent_praise_doc_tags_180d, user_recent_praise_doc_tags_1d, user_recent_praise_doc_tags_1h, user_recent_praise_doc_tags_30d, user_recent_praise_doc_tags_6h, user_recent_praise_doc_tags_7d, user_recent_praise_doc_topic_tag_180d, user_recent_praise_doc_topic_tag_1d, user_recent_praise_doc_topic_tag_1h, user_recent_praise_doc_topic_tag_30d, user_recent_praise_doc_topic_tag_6h, user_recent_praise_doc_topic_tag_7d, user_recent_praise_doc_type_180d, user_recent_praise_doc_type_1d, user_recent_praise_doc_type_1h, user_recent_praise_doc_type_30d, user_recent_praise_doc_type_6h, user_recent_praise_doc_type_7d, user_recent_share_doc_cate1_180d, user_recent_share_doc_cate1_1d, user_recent_share_doc_cate1_1h, user_recent_share_doc_cate1_30d, user_recent_share_doc_cate1_6h, user_recent_share_doc_cate1_7d, user_recent_share_doc_cate2_180d, user_recent_share_doc_cate2_1d, user_recent_share_doc_cate2_1h, user_recent_share_doc_cate2_30d, user_recent_share_doc_cate2_6h, user_recent_share_doc_cate2_7d, user_recent_share_doc_cate3_180d, user_recent_share_doc_cate3_1d, user_recent_share_doc_cate3_1h, user_recent_share_doc_cate3_30d, user_recent_share_doc_cate3_6h, user_recent_share_doc_cate3_7d, user_recent_share_doc_id_180d, user_recent_share_doc_id_1d, user_recent_share_doc_id_1h, user_recent_share_doc_id_30d, user_recent_share_doc_id_6h, user_recent_share_doc_id_7d, user_recent_share_doc_keyword_180d, user_recent_share_doc_keyword_1d, user_recent_share_doc_keyword_1h, user_recent_share_doc_keyword_30d, user_recent_share_doc_keyword_6h, user_recent_share_doc_keyword_7d, user_recent_share_doc_tags_180d, user_recent_share_doc_tags_1d, user_recent_share_doc_tags_1h, user_recent_share_doc_tags_30d, user_recent_share_doc_tags_6h, user_recent_share_doc_tags_7d, user_recent_share_doc_topic_tag_180d, user_recent_share_doc_topic_tag_1d, user_recent_share_doc_topic_tag_1h, user_recent_share_doc_topic_tag_30d, user_recent_share_doc_topic_tag_6h, user_recent_share_doc_topic_tag_7d, user_recent_share_doc_type_180d, user_recent_share_doc_type_1d, user_recent_share_doc_type_1h, user_recent_share_doc_type_30d, user_recent_share_doc_type_6h, user_recent_share_doc_type_7d, user_register_time, user_st_1d_doc_author_id_cart_cp, user_st_1d_doc_author_id_cart_recent, user_st_1d_doc_author_id_click_cp, user_st_1d_doc_author_id_click_recent, user_st_1d_doc_author_id_conversion_cp, user_st_1d_doc_author_id_conversion_recent, user_st_1d_doc_author_id_favorite_cp, user_st_1d_doc_author_id_favorite_recent, user_st_1d_doc_author_id_praise_cp, user_st_1d_doc_author_id_praise_recent, user_st_1d_doc_author_id_query_cp, user_st_1d_doc_author_id_query_recent, user_st_1d_doc_cate1_cart_cp, user_st_1d_doc_cate1_cart_recent, user_st_1d_doc_cate1_click_cp, user_st_1d_doc_cate1_click_recent, user_st_1d_doc_cate1_conversion_cp, user_st_1d_doc_cate1_conversion_recent, user_st_1d_doc_cate1_favorite_cp, user_st_1d_doc_cate1_favorite_recent, user_st_1d_doc_cate1_praise_cp, user_st_1d_doc_cate1_praise_recent, user_st_1d_doc_cate1_query_cp, user_st_1d_doc_cate1_query_recent, user_st_1d_doc_cate2_cart_cp, user_st_1d_doc_cate2_cart_recent, user_st_1d_doc_cate2_click_cp, user_st_1d_doc_cate2_click_recent, user_st_1d_doc_cate2_conversion_cp, user_st_1d_doc_cate2_conversion_recent, user_st_1d_doc_cate2_favorite_cp, user_st_1d_doc_cate2_favorite_recent, user_st_1d_doc_cate2_praise_cp, user_st_1d_doc_cate2_praise_recent, user_st_1d_doc_cate2_query_cp, user_st_1d_doc_cate2_query_recent, user_st_1d_doc_cate3_cart_cp, user_st_1d_doc_cate3_cart_recent, user_st_1d_doc_cate3_click_cp, user_st_1d_doc_cate3_click_recent, user_st_1d_doc_cate3_conversion_cp, user_st_1d_doc_cate3_conversion_recent, user_st_1d_doc_cate3_favorite_cp, user_st_1d_doc_cate3_favorite_recent, user_st_1d_doc_cate3_praise_cp, user_st_1d_doc_cate3_praise_recent, user_st_1d_doc_cate3_query_cp, user_st_1d_doc_cate3_query_recent, user_st_1d_doc_id_cart_cp, user_st_1d_doc_id_cart_recent, user_st_1d_doc_id_click_cp, user_st_1d_doc_id_click_recent, user_st_1d_doc_id_conversion_cp, user_st_1d_doc_id_conversion_recent, user_st_1d_doc_id_favorite_cp, user_st_1d_doc_id_favorite_recent, user_st_1d_doc_id_praise_cp, user_st_1d_doc_id_praise_recent, user_st_1d_doc_id_query_cp, user_st_1d_doc_id_query_recent, user_st_1d_doc_keyword_cart_cp, user_st_1d_doc_keyword_cart_recent, user_st_1d_doc_keyword_click_cp, user_st_1d_doc_keyword_click_recent, user_st_1d_doc_keyword_conversion_cp, user_st_1d_doc_keyword_conversion_recent, user_st_1d_doc_keyword_favorite_cp, user_st_1d_doc_keyword_favorite_recent, user_st_1d_doc_keyword_praise_cp, user_st_1d_doc_keyword_praise_recent, user_st_1d_doc_keyword_query_cp, user_st_1d_doc_keyword_query_recent, user_st_1d_doc_tags_cart_cp, user_st_1d_doc_tags_cart_recent, user_st_1d_doc_tags_click_cp, user_st_1d_doc_tags_click_recent, user_st_1d_doc_tags_conversion_cp, user_st_1d_doc_tags_conversion_recent, user_st_1d_doc_tags_favorite_cp, user_st_1d_doc_tags_favorite_recent, user_st_1d_doc_tags_praise_cp, user_st_1d_doc_tags_praise_recent, user_st_1d_doc_tags_query_cp, user_st_1d_doc_tags_query_recent, user_st_1d_doc_title_terms_cart_cp, user_st_1d_doc_title_terms_cart_recent, user_st_1d_doc_title_terms_click_cp, user_st_1d_doc_title_terms_click_recent, user_st_1d_doc_title_terms_conversion_cp, user_st_1d_doc_title_terms_conversion_recent, user_st_1d_doc_title_terms_favorite_cp, user_st_1d_doc_title_terms_favorite_recent, user_st_1d_doc_title_terms_praise_cp, user_st_1d_doc_title_terms_praise_recent, user_st_1d_doc_title_terms_query_cp, user_st_1d_doc_title_terms_query_recent, user_st_7d_doc_author_id_cart_cp, user_st_7d_doc_author_id_cart_recent, user_st_7d_doc_author_id_click_cp, user_st_7d_doc_author_id_click_recent, user_st_7d_doc_author_id_conversion_cp, user_st_7d_doc_author_id_conversion_recent, user_st_7d_doc_author_id_favorite_cp, user_st_7d_doc_author_id_favorite_recent, user_st_7d_doc_author_id_praise_cp, user_st_7d_doc_author_id_praise_recent, user_st_7d_doc_author_id_query_cp, user_st_7d_doc_author_id_query_recent, user_st_7d_doc_cate1_cart_cp, user_st_7d_doc_cate1_cart_recent, user_st_7d_doc_cate1_click_cp, user_st_7d_doc_cate1_click_recent, user_st_7d_doc_cate1_conversion_cp, user_st_7d_doc_cate1_conversion_recent, user_st_7d_doc_cate1_favorite_cp, user_st_7d_doc_cate1_favorite_recent, user_st_7d_doc_cate1_praise_cp, user_st_7d_doc_cate1_praise_recent, user_st_7d_doc_cate1_query_cp, user_st_7d_doc_cate1_query_recent, user_st_7d_doc_cate2_cart_cp, user_st_7d_doc_cate2_cart_recent, user_st_7d_doc_cate2_click_cp, user_st_7d_doc_cate2_click_recent, user_st_7d_doc_cate2_conversion_cp, user_st_7d_doc_cate2_conversion_recent, user_st_7d_doc_cate2_favorite_cp, user_st_7d_doc_cate2_favorite_recent, user_st_7d_doc_cate2_praise_cp, user_st_7d_doc_cate2_praise_recent, user_st_7d_doc_cate2_query_cp, user_st_7d_doc_cate2_query_recent, user_st_7d_doc_cate3_cart_cp, user_st_7d_doc_cate3_cart_recent, user_st_7d_doc_cate3_click_cp, user_st_7d_doc_cate3_click_recent, user_st_7d_doc_cate3_conversion_cp, user_st_7d_doc_cate3_conversion_recent, user_st_7d_doc_cate3_favorite_cp, user_st_7d_doc_cate3_favorite_recent, user_st_7d_doc_cate3_praise_cp, user_st_7d_doc_cate3_praise_recent, user_st_7d_doc_cate3_query_cp, user_st_7d_doc_cate3_query_recent, user_st_7d_doc_id_cart_cp, user_st_7d_doc_id_cart_recent, user_st_7d_doc_id_click_cp, user_st_7d_doc_id_click_recent, user_st_7d_doc_id_conversion_cp, user_st_7d_doc_id_conversion_recent, user_st_7d_doc_id_favorite_cp, user_st_7d_doc_id_favorite_recent, user_st_7d_doc_id_praise_cp, user_st_7d_doc_id_praise_recent, user_st_7d_doc_id_query_cp, user_st_7d_doc_id_query_recent, user_st_7d_doc_keyword_cart_cp, user_st_7d_doc_keyword_cart_recent, user_st_7d_doc_keyword_click_cp, user_st_7d_doc_keyword_click_recent, user_st_7d_doc_keyword_conversion_cp, user_st_7d_doc_keyword_conversion_recent, user_st_7d_doc_keyword_favorite_cp, user_st_7d_doc_keyword_favorite_recent, user_st_7d_doc_keyword_praise_cp, user_st_7d_doc_keyword_praise_recent, user_st_7d_doc_keyword_query_cp, user_st_7d_doc_keyword_query_recent, user_st_7d_doc_tags_cart_cp, user_st_7d_doc_tags_cart_recent, user_st_7d_doc_tags_click_cp, user_st_7d_doc_tags_click_recent, user_st_7d_doc_tags_conversion_cp, user_st_7d_doc_tags_conversion_recent, user_st_7d_doc_tags_favorite_cp, user_st_7d_doc_tags_favorite_recent, user_st_7d_doc_tags_praise_cp, user_st_7d_doc_tags_praise_recent, user_st_7d_doc_tags_query_cp, user_st_7d_doc_tags_query_recent, user_st_7d_doc_title_terms_cart_cp, user_st_7d_doc_title_terms_cart_recent, user_st_7d_doc_title_terms_click_cp, user_st_7d_doc_title_terms_click_recent, user_st_7d_doc_title_terms_conversion_cp, user_st_7d_doc_title_terms_conversion_recent, user_st_7d_doc_title_terms_favorite_cp, user_st_7d_doc_title_terms_favorite_recent, user_st_7d_doc_title_terms_praise_cp, user_st_7d_doc_title_terms_praise_recent, user_st_7d_doc_title_terms_query_cp, user_st_7d_doc_title_terms_query_recent, user_tags_list\n\nfeature_name=f_area; depend=area; method=DirectString; slot=312; shared=true; feature_id=368807\nfeature_name=f_att_traced; depend=att_traced; method=DirectInt32; slot=322; shared=true; feature_id=368817\nfeature_name=f_bhv_scm; depend=bhv_scm; method=DirectString; slot=316; shared=true; feature_id=368811\nfeature_name=f_bhv_spm; depend=bhv_spm; method=DirectString; slot=317; shared=true; feature_id=368812\nfeature_name=f_bhv_spm_1; depend=bhv_spm_1; method=DirectString; slot=318; shared=true; feature_id=368813\nfeature_name=f_bhv_spm_2; depend=bhv_spm_2; method=DirectString; slot=319; shared=true; feature_id=368814\nfeature_name=f_bhv_spm_3; depend=bhv_spm_3; method=DirectString; slot=320; shared=true; feature_id=368815\nfeature_name=f_bhv_spm_4; depend=bhv_spm_4; method=DirectString; slot=321; shared=true; feature_id=368816\nfeature_name=f_bhv_time_hour; depend=bhv_time_hour; method=DirectString; slot=314; shared=true; feature_id=368809\nfeature_name=f_bhv_time_monthday; depend=bhv_time_monthday; method=DirectString; slot=323; shared=true; feature_id=368818\nfeature_name=f_bhv_time_weekday; depend=bhv_time_weekday; method=DirectString; slot=315; shared=true; feature_id=368810\nfeature_name=f_city; depend=city; method=DirectString; slot=310; shared=true; feature_id=368805\nfeature_name=f_client_version; depend=client_version; method=DirectString; slot=304; shared=true; feature_id=368799\nfeature_name=f_country; depend=country; method=DirectString; slot=308; shared=true; feature_id=368803\nfeature_name=f_device_model; depend=device_model; method=DirectString; slot=306; shared=true; feature_id=368801\nfeature_name=f_district; depend=district; method=DirectString; slot=311; shared=true; feature_id=368806\nfeature_name=f_doc_author_fans_10; depend=doc_author_fans_10; method=DirectString; slot=222; feature_id=368786\nfeature_name=f_doc_author_id; depend=doc_author_id; method=DirectString; slot=206; feature_id=368770\nfeature_name=f_doc_author_level; depend=doc_author_level; method=DirectString; slot=221; feature_id=368785\nfeature_name=f_doc_author_name; depend=doc_author_name; method=DirectString; slot=220; feature_id=368784\nfeature_name=f_doc_cate1; depend=doc_cate1; method=DirectString; slot=203; feature_id=368767\nfeature_name=f_doc_cate2; depend=doc_cate2; method=DirectString; slot=204; feature_id=368768\nfeature_name=f_doc_cate3; depend=doc_cate3; method=DirectString; slot=205; feature_id=368769\nfeature_name=f_doc_collect_cnt_10; depend=doc_collect_cnt_10; method=DirectInt32; slot=209; feature_id=368773\nfeature_name=f_doc_collection; depend=doc_collection; method=DirectString; slot=223; feature_id=368787\nfeature_name=f_doc_comment_cnt_10; depend=doc_comment_cnt_10; method=DirectInt32; slot=211; feature_id=368775\nfeature_name=f_doc_content_length_2; depend=doc_content_length_2; method=DirectInt32; slot=239; feature_id=368791\nfeature_name=f_doc_create_time; depend=doc_create_time; method=DirectInt64; slot=240; feature_id=368792\nfeature_name=f_doc_detail_pic_num; depend=doc_detail_pic_num; method=DirectInt32; slot=217; feature_id=368781\nfeature_name=f_doc_expire_time; depend=doc_expire_time; method=DirectInt64; slot=242; feature_id=368794\nfeature_name=f_doc_id; depend=doc_id; method=DirectString; slot=200; feature_id=368764\nfeature_name=f_doc_keyword; depend=doc_keyword; method=VectorTopString; slot=219; feature_id=368783\nfeature_name=f_doc_location_tag; depend=doc_location_tag; method=DirectString; slot=225; feature_id=368789\nfeature_name=f_doc_pic_url; depend=doc_pic_url; method=VectorTopString; slot=214; feature_id=368778\nfeature_name=f_doc_praise_cnt_10; depend=doc_praise_cnt_10; method=DirectInt32; slot=210; feature_id=368774\nfeature_name=f_doc_pub_time; depend=doc_pub_time; method=DirectInt64; slot=241; feature_id=368793\nfeature_name=f_doc_rating; depend=doc_rating; method=VectorTopString; slot=213; feature_id=368777\nfeature_name=f_doc_related_goods_ids; depend=doc_related_goods_ids; method=VectorTopString; slot=218; feature_id=368782\nfeature_name=f_doc_share_cnt_10; depend=doc_share_cnt_10; method=DirectInt32; slot=208; feature_id=368772\nfeature_name=f_doc_source_id; depend=doc_source_id; method=DirectString; slot=212; feature_id=368776\nfeature_name=f_doc_tags; depend=doc_tags; method=VectorTopString; slot=207; feature_id=368771\nfeature_name=f_doc_title_length; depend=doc_title_length; method=DirectInt32; slot=238; feature_id=368790\nfeature_name=f_doc_title_terms; depend=doc_title_terms; method=VectorTopString; slot=201; feature_id=368765\nfeature_name=f_doc_topic_tag; depend=doc_topic_tag; method=DirectString; slot=224; feature_id=368788\nfeature_name=f_doc_type; depend=doc_type; method=DirectString; slot=202; feature_id=368766\nfeature_name=f_doc_video_duration_10; depend=doc_video_duration_10; method=DirectInt32; slot=216; feature_id=368780\nfeature_name=f_doc_video_url; depend=doc_video_url; method=VectorTopString; slot=215; feature_id=368779\nfeature_name=f_fake_context_id; depend=fake_context_id; method=DirectString; slot=307; shared=true; feature_id=368802\nfeature_name=f_goods_exposure_cnt_lt; depend=goods_exposure_cnt_lt; method=DirectInt64; slot=515; feature_id=449546\nfeature_name=f_goods_is_prepublic; depend=goods_is_prepublic; method=DirectInt32; slot=514; feature_id=431594; feature_version=2\nfeature_name=f_goods_op_rec_status; depend=goods_op_rec_status; method=DirectInt32; slot=512; feature_id=368819\nfeature_name=f_goods_quality_score; depend=goods_quality_score; method=DirectInt32; slot=512; feature_id=431593\nfeature_name=f_goods_rec_scene_id; depend=goods_rec_scene_id; method=DirectString; slot=512; feature_id=408248\nfeature_name=f_network; depend=network; method=DirectString; slot=305; shared=true; feature_id=368800\nfeature_name=f_os; depend=os; method=DirectString; slot=302; shared=true; feature_id=368797\nfeature_name=f_os_version; depend=os_version; method=DirectString; slot=303; shared=true; feature_id=368798\nfeature_name=f_page; depend=page; method=DirectString; slot=300; shared=true; feature_id=368795\nfeature_name=f_platform; depend=platform; method=DirectString; slot=301; shared=true; feature_id=368796\nfeature_name=f_province; depend=province; method=DirectString; slot=309; shared=true; feature_id=368804\nfeature_name=f_time; depend=time; method=DirectInt32; slot=313; shared=true; feature_id=368808\nfeature_name=f_user_age; depend=user_age; method=DirectString; slot=2; shared=true; feature_id=368753\nfeature_name=f_user_area; depend=user_area; method=DirectString; slot=10; shared=true; feature_id=368761\nfeature_name=f_user_city; depend=user_city; method=DirectString; slot=8; shared=true; feature_id=368759\nfeature_name=f_user_country; depend=user_country; method=DirectString; slot=6; shared=true; feature_id=368757\nfeature_name=f_user_device_id; depend=user_device_id; method=DirectString; slot=4; shared=true; feature_id=368755\nfeature_name=f_user_district; depend=user_district; method=DirectString; slot=9; shared=true; feature_id=368760\nfeature_name=f_user_gender; depend=user_gender; method=DirectString; slot=3; shared=true; feature_id=368754\nfeature_name=f_user_id; depend=user_id; method=DirectString; slot=1; shared=true; feature_id=368752\nfeature_name=f_user_is_prepublic; depend=user_is_prepublic; method=DirectInt32; slot=514; shared=true; feature_id=431589; feature_version=2\nfeature_name=f_user_membership_level; depend=user_membership_level; method=DirectString; slot=5; shared=true; feature_id=368756\nfeature_name=f_user_province; depend=user_province; method=DirectString; slot=7; shared=true; feature_id=368758\nfeature_name=f_user_quality_score; depend=user_quality_score; method=DirectInt32; slot=515; shared=true; feature_id=431590\nfeature_name=f_user_register_time; depend=user_register_time; method=DirectInt64; slot=12; shared=true; feature_id=368763\nfeature_name=f_user_tags_list; depend=user_tags_list; method=VectorTopString; slot=11; shared=true; feature_id=368762\nfeature_name=fc_doc_id_post_click_180d_cnt; depend=doc_id_post_click_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4735; shared=true; feature_id=369795\nfeature_name=fc_doc_id_post_click_180d_concat; depend=doc_id_post_click_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4737; shared=true; feature_id=369797\nfeature_name=fc_doc_id_post_click_180d_smooth; depend=doc_id_post_click_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4736; shared=true; feature_id=369796\nfeature_name=fc_doc_id_post_click_1d_cnt; depend=doc_id_post_click_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4726; shared=true; feature_id=369786\nfeature_name=fc_doc_id_post_click_1d_concat; depend=doc_id_post_click_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4728; shared=true; feature_id=369788\nfeature_name=fc_doc_id_post_click_1d_smooth; depend=doc_id_post_click_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4727; shared=true; feature_id=369787\nfeature_name=fc_doc_id_post_click_1h_cnt; depend=doc_id_post_click_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4720; shared=true; feature_id=369780\nfeature_name=fc_doc_id_post_click_1h_concat; depend=doc_id_post_click_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4722; shared=true; feature_id=369782\nfeature_name=fc_doc_id_post_click_1h_smooth; depend=doc_id_post_click_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4721; shared=true; feature_id=369781\nfeature_name=fc_doc_id_post_click_30d_cnt; depend=doc_id_post_click_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4732; shared=true; feature_id=369792\nfeature_name=fc_doc_id_post_click_30d_concat; depend=doc_id_post_click_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4734; shared=true; feature_id=369794\nfeature_name=fc_doc_id_post_click_30d_smooth; depend=doc_id_post_click_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4733; shared=true; feature_id=369793\nfeature_name=fc_doc_id_post_click_6h_cnt; depend=doc_id_post_click_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4723; shared=true; feature_id=369783\nfeature_name=fc_doc_id_post_click_6h_concat; depend=doc_id_post_click_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4725; shared=true; feature_id=369785\nfeature_name=fc_doc_id_post_click_6h_smooth; depend=doc_id_post_click_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4724; shared=true; feature_id=369784\nfeature_name=fc_doc_id_post_click_7d_cnt; depend=doc_id_post_click_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4729; shared=true; feature_id=369789\nfeature_name=fc_doc_id_post_click_7d_concat; depend=doc_id_post_click_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4731; shared=true; feature_id=369791\nfeature_name=fc_doc_id_post_click_7d_smooth; depend=doc_id_post_click_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4730; shared=true; feature_id=369790\nfeature_name=fc_doc_id_post_favorite_180d_cnt; depend=doc_id_post_favorite_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4753; shared=true; feature_id=369813\nfeature_name=fc_doc_id_post_favorite_180d_concat; depend=doc_id_post_favorite_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4755; shared=true; feature_id=369815\nfeature_name=fc_doc_id_post_favorite_180d_smooth; depend=doc_id_post_favorite_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4754; shared=true; feature_id=369814\nfeature_name=fc_doc_id_post_favorite_1d_cnt; depend=doc_id_post_favorite_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4744; shared=true; feature_id=369804\nfeature_name=fc_doc_id_post_favorite_1d_concat; depend=doc_id_post_favorite_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4746; shared=true; feature_id=369806\nfeature_name=fc_doc_id_post_favorite_1d_smooth; depend=doc_id_post_favorite_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4745; shared=true; feature_id=369805\nfeature_name=fc_doc_id_post_favorite_1h_cnt; depend=doc_id_post_favorite_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4738; shared=true; feature_id=369798\nfeature_name=fc_doc_id_post_favorite_1h_concat; depend=doc_id_post_favorite_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4740; shared=true; feature_id=369800\nfeature_name=fc_doc_id_post_favorite_1h_smooth; depend=doc_id_post_favorite_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4739; shared=true; feature_id=369799\nfeature_name=fc_doc_id_post_favorite_30d_cnt; depend=doc_id_post_favorite_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4750; shared=true; feature_id=369810\nfeature_name=fc_doc_id_post_favorite_30d_concat; depend=doc_id_post_favorite_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4752; shared=true; feature_id=369812\nfeature_name=fc_doc_id_post_favorite_30d_smooth; depend=doc_id_post_favorite_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4751; shared=true; feature_id=369811\nfeature_name=fc_doc_id_post_favorite_6h_cnt; depend=doc_id_post_favorite_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4741; shared=true; feature_id=369801\nfeature_name=fc_doc_id_post_favorite_6h_concat; depend=doc_id_post_favorite_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4743; shared=true; feature_id=369803\nfeature_name=fc_doc_id_post_favorite_6h_smooth; depend=doc_id_post_favorite_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4742; shared=true; feature_id=369802\nfeature_name=fc_doc_id_post_favorite_7d_cnt; depend=doc_id_post_favorite_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4747; shared=true; feature_id=369807\nfeature_name=fc_doc_id_post_favorite_7d_concat; depend=doc_id_post_favorite_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4749; shared=true; feature_id=369809\nfeature_name=fc_doc_id_post_favorite_7d_smooth; depend=doc_id_post_favorite_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4748; shared=true; feature_id=369808\nfeature_name=fc_doc_id_post_praise_180d_cnt; depend=doc_id_post_praise_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4789; shared=true; feature_id=369849\nfeature_name=fc_doc_id_post_praise_180d_concat; depend=doc_id_post_praise_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4791; shared=true; feature_id=369851\nfeature_name=fc_doc_id_post_praise_180d_smooth; depend=doc_id_post_praise_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4790; shared=true; feature_id=369850\nfeature_name=fc_doc_id_post_praise_1d_cnt; depend=doc_id_post_praise_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4780; shared=true; feature_id=369840\nfeature_name=fc_doc_id_post_praise_1d_concat; depend=doc_id_post_praise_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4782; shared=true; feature_id=369842\nfeature_name=fc_doc_id_post_praise_1d_smooth; depend=doc_id_post_praise_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4781; shared=true; feature_id=369841\nfeature_name=fc_doc_id_post_praise_1h_cnt; depend=doc_id_post_praise_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4774; shared=true; feature_id=369834\nfeature_name=fc_doc_id_post_praise_1h_concat; depend=doc_id_post_praise_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4776; shared=true; feature_id=369836\nfeature_name=fc_doc_id_post_praise_1h_smooth; depend=doc_id_post_praise_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4775; shared=true; feature_id=369835\nfeature_name=fc_doc_id_post_praise_30d_cnt; depend=doc_id_post_praise_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4786; shared=true; feature_id=369846\nfeature_name=fc_doc_id_post_praise_30d_concat; depend=doc_id_post_praise_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4788; shared=true; feature_id=369848\nfeature_name=fc_doc_id_post_praise_30d_smooth; depend=doc_id_post_praise_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4787; shared=true; feature_id=369847\nfeature_name=fc_doc_id_post_praise_6h_cnt; depend=doc_id_post_praise_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4777; shared=true; feature_id=369837\nfeature_name=fc_doc_id_post_praise_6h_concat; depend=doc_id_post_praise_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4779; shared=true; feature_id=369839\nfeature_name=fc_doc_id_post_praise_6h_smooth; depend=doc_id_post_praise_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4778; shared=true; feature_id=369838\nfeature_name=fc_doc_id_post_praise_7d_cnt; depend=doc_id_post_praise_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4783; shared=true; feature_id=369843\nfeature_name=fc_doc_id_post_praise_7d_concat; depend=doc_id_post_praise_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4785; shared=true; feature_id=369845\nfeature_name=fc_doc_id_post_praise_7d_smooth; depend=doc_id_post_praise_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4784; shared=true; feature_id=369844\nfeature_name=fc_doc_id_post_share_180d_cnt; depend=doc_id_post_share_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4771; shared=true; feature_id=369831\nfeature_name=fc_doc_id_post_share_180d_concat; depend=doc_id_post_share_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4773; shared=true; feature_id=369833\nfeature_name=fc_doc_id_post_share_180d_smooth; depend=doc_id_post_share_180d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4772; shared=true; feature_id=369832\nfeature_name=fc_doc_id_post_share_1d_cnt; depend=doc_id_post_share_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4762; shared=true; feature_id=369822\nfeature_name=fc_doc_id_post_share_1d_concat; depend=doc_id_post_share_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4764; shared=true; feature_id=369824\nfeature_name=fc_doc_id_post_share_1d_smooth; depend=doc_id_post_share_1d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4763; shared=true; feature_id=369823\nfeature_name=fc_doc_id_post_share_1h_cnt; depend=doc_id_post_share_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4756; shared=true; feature_id=369816\nfeature_name=fc_doc_id_post_share_1h_concat; depend=doc_id_post_share_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4758; shared=true; feature_id=369818\nfeature_name=fc_doc_id_post_share_1h_smooth; depend=doc_id_post_share_1h; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4757; shared=true; feature_id=369817\nfeature_name=fc_doc_id_post_share_30d_cnt; depend=doc_id_post_share_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4768; shared=true; feature_id=369828\nfeature_name=fc_doc_id_post_share_30d_concat; depend=doc_id_post_share_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4770; shared=true; feature_id=369830\nfeature_name=fc_doc_id_post_share_30d_smooth; depend=doc_id_post_share_30d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4769; shared=true; feature_id=369829\nfeature_name=fc_doc_id_post_share_6h_cnt; depend=doc_id_post_share_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4759; shared=true; feature_id=369819\nfeature_name=fc_doc_id_post_share_6h_concat; depend=doc_id_post_share_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4761; shared=true; feature_id=369821\nfeature_name=fc_doc_id_post_share_6h_smooth; depend=doc_id_post_share_6h; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4760; shared=true; feature_id=369820\nfeature_name=fc_doc_id_post_share_7d_cnt; depend=doc_id_post_share_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,4,0,1; slot=4765; shared=true; feature_id=369825\nfeature_name=fc_doc_id_post_share_7d_concat; depend=doc_id_post_share_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,6,0,1; slot=4767; shared=true; feature_id=369827\nfeature_name=fc_doc_id_post_share_7d_smooth; depend=doc_id_post_share_7d; method=TobInstanceProfilePairList; feature_version=2; args=1,7,0,1,4,1000,10000; slot=4766; shared=true; feature_id=369826\nfeature_name=fc_user_lt_doc_author_id_cart_cp; depend=user_lt_doc_author_id_cart_cp; method=VectorTopString; feature_version=2; args=50; slot=1361; shared=true; feature_id=368933\nfeature_name=fc_user_lt_doc_author_id_click_cp; depend=user_lt_doc_author_id_click_cp; method=VectorTopString; feature_version=2; args=50; slot=1332; shared=true; feature_id=368904\nfeature_name=fc_user_lt_doc_author_id_conversion_cp; depend=user_lt_doc_author_id_conversion_cp; method=VectorTopString; feature_version=2; args=50; slot=1385; shared=true; feature_id=368957\nfeature_name=fc_user_lt_doc_author_id_favorite_cp; depend=user_lt_doc_author_id_favorite_cp; method=VectorTopString; feature_version=2; args=50; slot=1333; shared=true; feature_id=368905\nfeature_name=fc_user_lt_doc_author_id_praise_cp; depend=user_lt_doc_author_id_praise_cp; method=VectorTopString; feature_version=2; args=50; slot=1334; shared=true; feature_id=368906\nfeature_name=fc_user_lt_doc_author_id_query_cp; depend=user_lt_doc_author_id_query_cp; method=VectorTopString; feature_version=2; args=50; slot=1335; shared=true; feature_id=368907\nfeature_name=fc_user_lt_doc_cate1_cart_cp; depend=user_lt_doc_cate1_cart_cp; method=VectorTopString; feature_version=2; args=50; slot=1352; shared=true; feature_id=368924\nfeature_name=fc_user_lt_doc_cate1_click_cp; depend=user_lt_doc_cate1_click_cp; method=VectorTopString; feature_version=2; args=50; slot=1320; shared=true; feature_id=368892\nfeature_name=fc_user_lt_doc_cate1_conversion_cp; depend=user_lt_doc_cate1_conversion_cp; method=VectorTopString; feature_version=2; args=50; slot=1376; shared=true; feature_id=368948\nfeature_name=fc_user_lt_doc_cate1_favorite_cp; depend=user_lt_doc_cate1_favorite_cp; method=VectorTopString; feature_version=2; args=50; slot=1321; shared=true; feature_id=368893\nfeature_name=fc_user_lt_doc_cate1_praise_cp; depend=user_lt_doc_cate1_praise_cp; method=VectorTopString; feature_version=2; args=50; slot=1322; shared=true; feature_id=368894\nfeature_name=fc_user_lt_doc_cate1_query_cp; depend=user_lt_doc_cate1_query_cp; method=VectorTopString; feature_version=2; args=50; slot=1323; shared=true; feature_id=368895\nfeature_name=fc_user_lt_doc_cate2_cart_cp; depend=user_lt_doc_cate2_cart_cp; method=VectorTopString; feature_version=2; args=50; slot=1355; shared=true; feature_id=368927\nfeature_name=fc_user_lt_doc_cate2_click_cp; depend=user_lt_doc_cate2_click_cp; method=VectorTopString; feature_version=2; args=50; slot=1324; shared=true; feature_id=368896\nfeature_name=fc_user_lt_doc_cate2_conversion_cp; depend=user_lt_doc_cate2_conversion_cp; method=VectorTopString; feature_version=2; args=50; slot=1379; shared=true; feature_id=368951\nfeature_name=fc_user_lt_doc_cate2_favorite_cp; depend=user_lt_doc_cate2_favorite_cp; method=VectorTopString; feature_version=2; args=50; slot=1325; shared=true; feature_id=368897\nfeature_name=fc_user_lt_doc_cate2_praise_cp; depend=user_lt_doc_cate2_praise_cp; method=VectorTopString; feature_version=2; args=50; slot=1326; shared=true; feature_id=368898\nfeature_name=fc_user_lt_doc_cate2_query_cp; depend=user_lt_doc_cate2_query_cp; method=VectorTopString; feature_version=2; args=50; slot=1327; shared=true; feature_id=368899\nfeature_name=fc_user_lt_doc_cate3_cart_cp; depend=user_lt_doc_cate3_cart_cp; method=VectorTopString; feature_version=2; args=50; slot=1358; shared=true; feature_id=368930\nfeature_name=fc_user_lt_doc_cate3_click_cp; depend=user_lt_doc_cate3_click_cp; method=VectorTopString; feature_version=2; args=50; slot=1328; shared=true; feature_id=368900\nfeature_name=fc_user_lt_doc_cate3_conversion_cp; depend=user_lt_doc_cate3_conversion_cp; method=VectorTopString; feature_version=2; args=50; slot=1382; shared=true; feature_id=368954\nfeature_name=fc_user_lt_doc_cate3_favorite_cp; depend=user_lt_doc_cate3_favorite_cp; method=VectorTopString; feature_version=2; args=50; slot=1329; shared=true; feature_id=368901\nfeature_name=fc_user_lt_doc_cate3_praise_cp; depend=user_lt_doc_cate3_praise_cp; method=VectorTopString; feature_version=2; args=50; slot=1330; shared=true; feature_id=368902\nfeature_name=fc_user_lt_doc_cate3_query_cp; depend=user_lt_doc_cate3_query_cp; method=VectorTopString; feature_version=2; args=50; slot=1331; shared=true; feature_id=368903\nfeature_name=fc_user_lt_doc_id_cart_cp; depend=user_lt_doc_id_cart_cp; method=VectorTopString; feature_version=2; args=50; slot=1346; shared=true; feature_id=368918\nfeature_name=fc_user_lt_doc_id_click_cp; depend=user_lt_doc_id_click_cp; method=VectorTopString; feature_version=2; args=50; slot=1312; shared=true; feature_id=368884\nfeature_name=fc_user_lt_doc_id_conversion_cp; depend=user_lt_doc_id_conversion_cp; method=VectorTopString; feature_version=2; args=50; slot=1370; shared=true; feature_id=368942\nfeature_name=fc_user_lt_doc_id_favorite_cp; depend=user_lt_doc_id_favorite_cp; method=VectorTopString; feature_version=2; args=50; slot=1313; shared=true; feature_id=368885\nfeature_name=fc_user_lt_doc_id_praise_cp; depend=user_lt_doc_id_praise_cp; method=VectorTopString; feature_version=2; args=50; slot=1314; shared=true; feature_id=368886\nfeature_name=fc_user_lt_doc_id_query_cp; depend=user_lt_doc_id_query_cp; method=VectorTopString; feature_version=2; args=50; slot=1315; shared=true; feature_id=368887\nfeature_name=fc_user_lt_doc_keyword_cart_cp; depend=user_lt_doc_keyword_cart_cp; method=VectorTopString; feature_version=2; args=50; slot=1367; shared=true; feature_id=368939\nfeature_name=fc_user_lt_doc_keyword_click_cp; depend=user_lt_doc_keyword_click_cp; method=VectorTopString; feature_version=2; args=50; slot=1340; shared=true; feature_id=368912\nfeature_name=fc_user_lt_doc_keyword_conversion_cp; depend=user_lt_doc_keyword_conversion_cp; method=VectorTopString; feature_version=2; args=50; slot=1391; shared=true; feature_id=368963\nfeature_name=fc_user_lt_doc_keyword_favorite_cp; depend=user_lt_doc_keyword_favorite_cp; method=VectorTopString; feature_version=2; args=50; slot=1341; shared=true; feature_id=368913\nfeature_name=fc_user_lt_doc_keyword_praise_cp; depend=user_lt_doc_keyword_praise_cp; method=VectorTopString; feature_version=2; args=50; slot=1342; shared=true; feature_id=368914\nfeature_name=fc_user_lt_doc_keyword_query_cp; depend=user_lt_doc_keyword_query_cp; method=VectorTopString; feature_version=2; args=50; slot=1343; shared=true; feature_id=368915\nfeature_name=fc_user_lt_doc_tags_cart_cp; depend=user_lt_doc_tags_cart_cp; method=VectorTopString; feature_version=2; args=50; slot=1364; shared=true; feature_id=368936\nfeature_name=fc_user_lt_doc_tags_click_cp; depend=user_lt_doc_tags_click_cp; method=VectorTopString; feature_version=2; args=50; slot=1336; shared=true; feature_id=368908\nfeature_name=fc_user_lt_doc_tags_conversion_cp; depend=user_lt_doc_tags_conversion_cp; method=VectorTopString; feature_version=2; args=50; slot=1388; shared=true; feature_id=368960\nfeature_name=fc_user_lt_doc_tags_favorite_cp; depend=user_lt_doc_tags_favorite_cp; method=VectorTopString; feature_version=2; args=50; slot=1337; shared=true; feature_id=368909\nfeature_name=fc_user_lt_doc_tags_praise_cp; depend=user_lt_doc_tags_praise_cp; method=VectorTopString; feature_version=2; args=50; slot=1338; shared=true; feature_id=368910\nfeature_name=fc_user_lt_doc_tags_query_cp; depend=user_lt_doc_tags_query_cp; method=VectorTopString; feature_version=2; args=50; slot=1339; shared=true; feature_id=368911\nfeature_name=fc_user_lt_doc_title_terms_cart_cp; depend=user_lt_doc_title_terms_cart_cp; method=VectorTopString; feature_version=2; args=50; slot=1349; shared=true; feature_id=368921\nfeature_name=fc_user_lt_doc_title_terms_click_cp; depend=user_lt_doc_title_terms_click_cp; method=VectorTopString; feature_version=2; args=50; slot=1316; shared=true; feature_id=368888\nfeature_name=fc_user_lt_doc_title_terms_conversion_cp; depend=user_lt_doc_title_terms_conversion_cp; method=VectorTopString; feature_version=2; args=50; slot=1373; shared=true; feature_id=368945\nfeature_name=fc_user_lt_doc_title_terms_favorite_cp; depend=user_lt_doc_title_terms_favorite_cp; method=VectorTopString; feature_version=2; args=50; slot=1317; shared=true; feature_id=368889\nfeature_name=fc_user_lt_doc_title_terms_praise_cp; depend=user_lt_doc_title_terms_praise_cp; method=VectorTopString; feature_version=2; args=50; slot=1318; shared=true; feature_id=368890\nfeature_name=fc_user_lt_doc_title_terms_query_cp; depend=user_lt_doc_title_terms_query_cp; method=VectorTopString; feature_version=2; args=50; slot=1319; shared=true; feature_id=368891\nfeature_name=fc_user_recent_click_doc_cate1_180d; depend=user_recent_click_doc_cate1_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4357; shared=true; feature_id=369417\nfeature_name=fc_user_recent_click_doc_cate1_180d_has_match; depend=fc_user_recent_click_doc_cate1_180d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4359; feature_id=369419\nfeature_name=fc_user_recent_click_doc_cate1_180d_tob_profile_match; depend=user_recent_click_doc_cate1_180d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4358; feature_id=369418\nfeature_name=fc_user_recent_click_doc_cate1_1d; depend=user_recent_click_doc_cate1_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4348; shared=true; feature_id=369408\nfeature_name=fc_user_recent_click_doc_cate1_1d_has_match; depend=fc_user_recent_click_doc_cate1_1d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4350; feature_id=369410\nfeature_name=fc_user_recent_click_doc_cate1_1d_tob_profile_match; depend=user_recent_click_doc_cate1_1d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4349; feature_id=369409\nfeature_name=fc_user_recent_click_doc_cate1_1h; depend=user_recent_click_doc_cate1_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4342; shared=true; feature_id=369402\nfeature_name=fc_user_recent_click_doc_cate1_1h_has_match; depend=fc_user_recent_click_doc_cate1_1h,f_doc_cate1; method=HasMatch; feature_version=2; slot=4344; feature_id=369404\nfeature_name=fc_user_recent_click_doc_cate1_1h_tob_profile_match; depend=user_recent_click_doc_cate1_1h,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4343; feature_id=369403\nfeature_name=fc_user_recent_click_doc_cate1_30d; depend=user_recent_click_doc_cate1_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4354; shared=true; feature_id=369414\nfeature_name=fc_user_recent_click_doc_cate1_30d_has_match; depend=fc_user_recent_click_doc_cate1_30d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4356; feature_id=369416\nfeature_name=fc_user_recent_click_doc_cate1_30d_tob_profile_match; depend=user_recent_click_doc_cate1_30d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4355; feature_id=369415\nfeature_name=fc_user_recent_click_doc_cate1_6h; depend=user_recent_click_doc_cate1_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4345; shared=true; feature_id=369405\nfeature_name=fc_user_recent_click_doc_cate1_6h_has_match; depend=fc_user_recent_click_doc_cate1_6h,f_doc_cate1; method=HasMatch; feature_version=2; slot=4347; feature_id=369407\nfeature_name=fc_user_recent_click_doc_cate1_6h_tob_profile_match; depend=user_recent_click_doc_cate1_6h,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4346; feature_id=369406\nfeature_name=fc_user_recent_click_doc_cate1_7d; depend=user_recent_click_doc_cate1_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4351; shared=true; feature_id=369411\nfeature_name=fc_user_recent_click_doc_cate1_7d_has_match; depend=fc_user_recent_click_doc_cate1_7d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4353; feature_id=369413\nfeature_name=fc_user_recent_click_doc_cate1_7d_tob_profile_match; depend=user_recent_click_doc_cate1_7d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4352; feature_id=369412\nfeature_name=fc_user_recent_click_doc_cate2_180d; depend=user_recent_click_doc_cate2_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4393; shared=true; feature_id=369453\nfeature_name=fc_user_recent_click_doc_cate2_180d_has_match; depend=fc_user_recent_click_doc_cate2_180d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4395; feature_id=369455\nfeature_name=fc_user_recent_click_doc_cate2_180d_tob_profile_match; depend=user_recent_click_doc_cate2_180d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4394; feature_id=369454\nfeature_name=fc_user_recent_click_doc_cate2_1d; depend=user_recent_click_doc_cate2_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4384; shared=true; feature_id=369444\nfeature_name=fc_user_recent_click_doc_cate2_1d_has_match; depend=fc_user_recent_click_doc_cate2_1d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4386; feature_id=369446\nfeature_name=fc_user_recent_click_doc_cate2_1d_tob_profile_match; depend=user_recent_click_doc_cate2_1d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4385; feature_id=369445\nfeature_name=fc_user_recent_click_doc_cate2_1h; depend=user_recent_click_doc_cate2_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4378; shared=true; feature_id=369438\nfeature_name=fc_user_recent_click_doc_cate2_1h_has_match; depend=fc_user_recent_click_doc_cate2_1h,f_doc_cate2; method=HasMatch; feature_version=2; slot=4380; feature_id=369440\nfeature_name=fc_user_recent_click_doc_cate2_1h_tob_profile_match; depend=user_recent_click_doc_cate2_1h,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4379; feature_id=369439\nfeature_name=fc_user_recent_click_doc_cate2_30d; depend=user_recent_click_doc_cate2_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4390; shared=true; feature_id=369450\nfeature_name=fc_user_recent_click_doc_cate2_30d_has_match; depend=fc_user_recent_click_doc_cate2_30d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4392; feature_id=369452\nfeature_name=fc_user_recent_click_doc_cate2_30d_tob_profile_match; depend=user_recent_click_doc_cate2_30d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4391; feature_id=369451\nfeature_name=fc_user_recent_click_doc_cate2_6h; depend=user_recent_click_doc_cate2_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4381; shared=true; feature_id=369441\nfeature_name=fc_user_recent_click_doc_cate2_6h_has_match; depend=fc_user_recent_click_doc_cate2_6h,f_doc_cate2; method=HasMatch; feature_version=2; slot=4383; feature_id=369443\nfeature_name=fc_user_recent_click_doc_cate2_6h_tob_profile_match; depend=user_recent_click_doc_cate2_6h,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4382; feature_id=369442\nfeature_name=fc_user_recent_click_doc_cate2_7d; depend=user_recent_click_doc_cate2_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4387; shared=true; feature_id=369447\nfeature_name=fc_user_recent_click_doc_cate2_7d_has_match; depend=fc_user_recent_click_doc_cate2_7d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4389; feature_id=369449\nfeature_name=fc_user_recent_click_doc_cate2_7d_tob_profile_match; depend=user_recent_click_doc_cate2_7d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4388; feature_id=369448\nfeature_name=fc_user_recent_click_doc_cate3_180d; depend=user_recent_click_doc_cate3_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4375; shared=true; feature_id=369435\nfeature_name=fc_user_recent_click_doc_cate3_180d_has_match; depend=fc_user_recent_click_doc_cate3_180d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4377; feature_id=369437\nfeature_name=fc_user_recent_click_doc_cate3_180d_tob_profile_match; depend=user_recent_click_doc_cate3_180d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4376; feature_id=369436\nfeature_name=fc_user_recent_click_doc_cate3_1d; depend=user_recent_click_doc_cate3_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4366; shared=true; feature_id=369426\nfeature_name=fc_user_recent_click_doc_cate3_1d_has_match; depend=fc_user_recent_click_doc_cate3_1d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4368; feature_id=369428\nfeature_name=fc_user_recent_click_doc_cate3_1d_tob_profile_match; depend=user_recent_click_doc_cate3_1d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4367; feature_id=369427\nfeature_name=fc_user_recent_click_doc_cate3_1h; depend=user_recent_click_doc_cate3_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4360; shared=true; feature_id=369420\nfeature_name=fc_user_recent_click_doc_cate3_1h_has_match; depend=fc_user_recent_click_doc_cate3_1h,f_doc_cate3; method=HasMatch; feature_version=2; slot=4362; feature_id=369422\nfeature_name=fc_user_recent_click_doc_cate3_1h_tob_profile_match; depend=user_recent_click_doc_cate3_1h,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4361; feature_id=369421\nfeature_name=fc_user_recent_click_doc_cate3_30d; depend=user_recent_click_doc_cate3_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4372; shared=true; feature_id=369432\nfeature_name=fc_user_recent_click_doc_cate3_30d_has_match; depend=fc_user_recent_click_doc_cate3_30d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4374; feature_id=369434\nfeature_name=fc_user_recent_click_doc_cate3_30d_tob_profile_match; depend=user_recent_click_doc_cate3_30d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4373; feature_id=369433\nfeature_name=fc_user_recent_click_doc_cate3_6h; depend=user_recent_click_doc_cate3_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4363; shared=true; feature_id=369423\nfeature_name=fc_user_recent_click_doc_cate3_6h_has_match; depend=fc_user_recent_click_doc_cate3_6h,f_doc_cate3; method=HasMatch; feature_version=2; slot=4365; feature_id=369425\nfeature_name=fc_user_recent_click_doc_cate3_6h_tob_profile_match; depend=user_recent_click_doc_cate3_6h,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4364; feature_id=369424\nfeature_name=fc_user_recent_click_doc_cate3_7d; depend=user_recent_click_doc_cate3_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4369; shared=true; feature_id=369429\nfeature_name=fc_user_recent_click_doc_cate3_7d_has_match; depend=fc_user_recent_click_doc_cate3_7d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4371; feature_id=369431\nfeature_name=fc_user_recent_click_doc_cate3_7d_tob_profile_match; depend=user_recent_click_doc_cate3_7d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4370; feature_id=369430\nfeature_name=fc_user_recent_click_doc_id_180d; depend=user_recent_click_doc_id_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4411; shared=true; feature_id=369471\nfeature_name=fc_user_recent_click_doc_id_180d_has_match; depend=fc_user_recent_click_doc_id_180d,f_doc_id; method=HasMatch; feature_version=2; slot=4413; feature_id=369473\nfeature_name=fc_user_recent_click_doc_id_180d_tob_profile_match; depend=user_recent_click_doc_id_180d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4412; feature_id=369472\nfeature_name=fc_user_recent_click_doc_id_1d; depend=user_recent_click_doc_id_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4402; shared=true; feature_id=369462\nfeature_name=fc_user_recent_click_doc_id_1d_has_match; depend=fc_user_recent_click_doc_id_1d,f_doc_id; method=HasMatch; feature_version=2; slot=4404; feature_id=369464\nfeature_name=fc_user_recent_click_doc_id_1d_tob_profile_match; depend=user_recent_click_doc_id_1d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4403; feature_id=369463\nfeature_name=fc_user_recent_click_doc_id_1h; depend=user_recent_click_doc_id_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4396; shared=true; feature_id=369456\nfeature_name=fc_user_recent_click_doc_id_1h_has_match; depend=fc_user_recent_click_doc_id_1h,f_doc_id; method=HasMatch; feature_version=2; slot=4398; feature_id=369458\nfeature_name=fc_user_recent_click_doc_id_1h_tob_profile_match; depend=user_recent_click_doc_id_1h,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4397; feature_id=369457\nfeature_name=fc_user_recent_click_doc_id_30d; depend=user_recent_click_doc_id_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4408; shared=true; feature_id=369468\nfeature_name=fc_user_recent_click_doc_id_30d_has_match; depend=fc_user_recent_click_doc_id_30d,f_doc_id; method=HasMatch; feature_version=2; slot=4410; feature_id=369470\nfeature_name=fc_user_recent_click_doc_id_30d_tob_profile_match; depend=user_recent_click_doc_id_30d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4409; feature_id=369469\nfeature_name=fc_user_recent_click_doc_id_6h; depend=user_recent_click_doc_id_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4399; shared=true; feature_id=369459\nfeature_name=fc_user_recent_click_doc_id_6h_has_match; depend=fc_user_recent_click_doc_id_6h,f_doc_id; method=HasMatch; feature_version=2; slot=4401; feature_id=369461\nfeature_name=fc_user_recent_click_doc_id_6h_tob_profile_match; depend=user_recent_click_doc_id_6h,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4400; feature_id=369460\nfeature_name=fc_user_recent_click_doc_id_7d; depend=user_recent_click_doc_id_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4405; shared=true; feature_id=369465\nfeature_name=fc_user_recent_click_doc_id_7d_has_match; depend=fc_user_recent_click_doc_id_7d,f_doc_id; method=HasMatch; feature_version=2; slot=4407; feature_id=369467\nfeature_name=fc_user_recent_click_doc_id_7d_tob_profile_match; depend=user_recent_click_doc_id_7d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4406; feature_id=369466\nfeature_name=fc_user_recent_click_doc_keyword_180d; depend=user_recent_click_doc_keyword_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4339; shared=true; feature_id=369399\nfeature_name=fc_user_recent_click_doc_keyword_180d_has_match; depend=fc_user_recent_click_doc_keyword_180d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4341; feature_id=369401\nfeature_name=fc_user_recent_click_doc_keyword_180d_tob_profile_match; depend=user_recent_click_doc_keyword_180d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4340; feature_id=369400\nfeature_name=fc_user_recent_click_doc_keyword_1d; depend=user_recent_click_doc_keyword_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4330; shared=true; feature_id=369390\nfeature_name=fc_user_recent_click_doc_keyword_1d_has_match; depend=fc_user_recent_click_doc_keyword_1d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4332; feature_id=369392\nfeature_name=fc_user_recent_click_doc_keyword_1d_tob_profile_match; depend=user_recent_click_doc_keyword_1d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4331; feature_id=369391\nfeature_name=fc_user_recent_click_doc_keyword_1h; depend=user_recent_click_doc_keyword_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4324; shared=true; feature_id=369384\nfeature_name=fc_user_recent_click_doc_keyword_1h_has_match; depend=fc_user_recent_click_doc_keyword_1h,f_doc_keyword; method=HasMatch; feature_version=2; slot=4326; feature_id=369386\nfeature_name=fc_user_recent_click_doc_keyword_1h_tob_profile_match; depend=user_recent_click_doc_keyword_1h,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4325; feature_id=369385\nfeature_name=fc_user_recent_click_doc_keyword_30d; depend=user_recent_click_doc_keyword_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4336; shared=true; feature_id=369396\nfeature_name=fc_user_recent_click_doc_keyword_30d_has_match; depend=fc_user_recent_click_doc_keyword_30d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4338; feature_id=369398\nfeature_name=fc_user_recent_click_doc_keyword_30d_tob_profile_match; depend=user_recent_click_doc_keyword_30d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4337; feature_id=369397\nfeature_name=fc_user_recent_click_doc_keyword_6h; depend=user_recent_click_doc_keyword_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4327; shared=true; feature_id=369387\nfeature_name=fc_user_recent_click_doc_keyword_6h_has_match; depend=fc_user_recent_click_doc_keyword_6h,f_doc_keyword; method=HasMatch; feature_version=2; slot=4329; feature_id=369389\nfeature_name=fc_user_recent_click_doc_keyword_6h_tob_profile_match; depend=user_recent_click_doc_keyword_6h,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4328; feature_id=369388\nfeature_name=fc_user_recent_click_doc_keyword_7d; depend=user_recent_click_doc_keyword_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4333; shared=true; feature_id=369393\nfeature_name=fc_user_recent_click_doc_keyword_7d_has_match; depend=fc_user_recent_click_doc_keyword_7d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4335; feature_id=369395\nfeature_name=fc_user_recent_click_doc_keyword_7d_tob_profile_match; depend=user_recent_click_doc_keyword_7d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4334; feature_id=369394\nfeature_name=fc_user_recent_click_doc_tags_180d; depend=user_recent_click_doc_tags_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4429; shared=true; feature_id=369489\nfeature_name=fc_user_recent_click_doc_tags_180d_has_match; depend=fc_user_recent_click_doc_tags_180d,f_doc_tags; method=HasMatch; feature_version=2; slot=4431; feature_id=369491\nfeature_name=fc_user_recent_click_doc_tags_180d_tob_profile_match; depend=user_recent_click_doc_tags_180d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4430; feature_id=369490\nfeature_name=fc_user_recent_click_doc_tags_1d; depend=user_recent_click_doc_tags_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4420; shared=true; feature_id=369480\nfeature_name=fc_user_recent_click_doc_tags_1d_has_match; depend=fc_user_recent_click_doc_tags_1d,f_doc_tags; method=HasMatch; feature_version=2; slot=4422; feature_id=369482\nfeature_name=fc_user_recent_click_doc_tags_1d_tob_profile_match; depend=user_recent_click_doc_tags_1d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4421; feature_id=369481\nfeature_name=fc_user_recent_click_doc_tags_1h; depend=user_recent_click_doc_tags_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4414; shared=true; feature_id=369474\nfeature_name=fc_user_recent_click_doc_tags_1h_has_match; depend=fc_user_recent_click_doc_tags_1h,f_doc_tags; method=HasMatch; feature_version=2; slot=4416; feature_id=369476\nfeature_name=fc_user_recent_click_doc_tags_1h_tob_profile_match; depend=user_recent_click_doc_tags_1h,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4415; feature_id=369475\nfeature_name=fc_user_recent_click_doc_tags_30d; depend=user_recent_click_doc_tags_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4426; shared=true; feature_id=369486\nfeature_name=fc_user_recent_click_doc_tags_30d_has_match; depend=fc_user_recent_click_doc_tags_30d,f_doc_tags; method=HasMatch; feature_version=2; slot=4428; feature_id=369488\nfeature_name=fc_user_recent_click_doc_tags_30d_tob_profile_match; depend=user_recent_click_doc_tags_30d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4427; feature_id=369487\nfeature_name=fc_user_recent_click_doc_tags_6h; depend=user_recent_click_doc_tags_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4417; shared=true; feature_id=369477\nfeature_name=fc_user_recent_click_doc_tags_6h_has_match; depend=fc_user_recent_click_doc_tags_6h,f_doc_tags; method=HasMatch; feature_version=2; slot=4419; feature_id=369479\nfeature_name=fc_user_recent_click_doc_tags_6h_tob_profile_match; depend=user_recent_click_doc_tags_6h,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4418; feature_id=369478\nfeature_name=fc_user_recent_click_doc_tags_7d; depend=user_recent_click_doc_tags_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4423; shared=true; feature_id=369483\nfeature_name=fc_user_recent_click_doc_tags_7d_has_match; depend=fc_user_recent_click_doc_tags_7d,f_doc_tags; method=HasMatch; feature_version=2; slot=4425; feature_id=369485\nfeature_name=fc_user_recent_click_doc_tags_7d_tob_profile_match; depend=user_recent_click_doc_tags_7d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4424; feature_id=369484\nfeature_name=fc_user_recent_click_doc_topic_tag_180d; depend=user_recent_click_doc_topic_tag_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4321; shared=true; feature_id=369381\nfeature_name=fc_user_recent_click_doc_topic_tag_180d_has_match; depend=fc_user_recent_click_doc_topic_tag_180d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4323; feature_id=369383\nfeature_name=fc_user_recent_click_doc_topic_tag_180d_tob_profile_match; depend=user_recent_click_doc_topic_tag_180d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4322; feature_id=369382\nfeature_name=fc_user_recent_click_doc_topic_tag_1d; depend=user_recent_click_doc_topic_tag_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4312; shared=true; feature_id=369372\nfeature_name=fc_user_recent_click_doc_topic_tag_1d_has_match; depend=fc_user_recent_click_doc_topic_tag_1d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4314; feature_id=369374\nfeature_name=fc_user_recent_click_doc_topic_tag_1d_tob_profile_match; depend=user_recent_click_doc_topic_tag_1d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4313; feature_id=369373\nfeature_name=fc_user_recent_click_doc_topic_tag_1h; depend=user_recent_click_doc_topic_tag_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4306; shared=true; feature_id=369366\nfeature_name=fc_user_recent_click_doc_topic_tag_1h_has_match; depend=fc_user_recent_click_doc_topic_tag_1h,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4308; feature_id=369368\nfeature_name=fc_user_recent_click_doc_topic_tag_1h_tob_profile_match; depend=user_recent_click_doc_topic_tag_1h,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4307; feature_id=369367\nfeature_name=fc_user_recent_click_doc_topic_tag_30d; depend=user_recent_click_doc_topic_tag_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4318; shared=true; feature_id=369378\nfeature_name=fc_user_recent_click_doc_topic_tag_30d_has_match; depend=fc_user_recent_click_doc_topic_tag_30d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4320; feature_id=369380\nfeature_name=fc_user_recent_click_doc_topic_tag_30d_tob_profile_match; depend=user_recent_click_doc_topic_tag_30d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4319; feature_id=369379\nfeature_name=fc_user_recent_click_doc_topic_tag_6h; depend=user_recent_click_doc_topic_tag_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4309; shared=true; feature_id=369369\nfeature_name=fc_user_recent_click_doc_topic_tag_6h_has_match; depend=fc_user_recent_click_doc_topic_tag_6h,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4311; feature_id=369371\nfeature_name=fc_user_recent_click_doc_topic_tag_6h_tob_profile_match; depend=user_recent_click_doc_topic_tag_6h,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4310; feature_id=369370\nfeature_name=fc_user_recent_click_doc_topic_tag_7d; depend=user_recent_click_doc_topic_tag_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4315; shared=true; feature_id=369375\nfeature_name=fc_user_recent_click_doc_topic_tag_7d_has_match; depend=fc_user_recent_click_doc_topic_tag_7d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4317; feature_id=369377\nfeature_name=fc_user_recent_click_doc_topic_tag_7d_tob_profile_match; depend=user_recent_click_doc_topic_tag_7d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4316; feature_id=369376\nfeature_name=fc_user_recent_click_doc_type_180d; depend=user_recent_click_doc_type_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4303; shared=true; feature_id=369363\nfeature_name=fc_user_recent_click_doc_type_180d_has_match; depend=fc_user_recent_click_doc_type_180d,f_doc_type; method=HasMatch; feature_version=2; slot=4305; feature_id=369365\nfeature_name=fc_user_recent_click_doc_type_180d_tob_profile_match; depend=user_recent_click_doc_type_180d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4304; feature_id=369364\nfeature_name=fc_user_recent_click_doc_type_1d; depend=user_recent_click_doc_type_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4294; shared=true; feature_id=369354\nfeature_name=fc_user_recent_click_doc_type_1d_has_match; depend=fc_user_recent_click_doc_type_1d,f_doc_type; method=HasMatch; feature_version=2; slot=4296; feature_id=369356\nfeature_name=fc_user_recent_click_doc_type_1d_tob_profile_match; depend=user_recent_click_doc_type_1d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4295; feature_id=369355\nfeature_name=fc_user_recent_click_doc_type_1h; depend=user_recent_click_doc_type_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4288; shared=true; feature_id=369348\nfeature_name=fc_user_recent_click_doc_type_1h_has_match; depend=fc_user_recent_click_doc_type_1h,f_doc_type; method=HasMatch; feature_version=2; slot=4290; feature_id=369350\nfeature_name=fc_user_recent_click_doc_type_1h_tob_profile_match; depend=user_recent_click_doc_type_1h,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4289; feature_id=369349\nfeature_name=fc_user_recent_click_doc_type_30d; depend=user_recent_click_doc_type_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4300; shared=true; feature_id=369360\nfeature_name=fc_user_recent_click_doc_type_30d_has_match; depend=fc_user_recent_click_doc_type_30d,f_doc_type; method=HasMatch; feature_version=2; slot=4302; feature_id=369362\nfeature_name=fc_user_recent_click_doc_type_30d_tob_profile_match; depend=user_recent_click_doc_type_30d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4301; feature_id=369361\nfeature_name=fc_user_recent_click_doc_type_6h; depend=user_recent_click_doc_type_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4291; shared=true; feature_id=369351\nfeature_name=fc_user_recent_click_doc_type_6h_has_match; depend=fc_user_recent_click_doc_type_6h,f_doc_type; method=HasMatch; feature_version=2; slot=4293; feature_id=369353\nfeature_name=fc_user_recent_click_doc_type_6h_tob_profile_match; depend=user_recent_click_doc_type_6h,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4292; feature_id=369352\nfeature_name=fc_user_recent_click_doc_type_7d; depend=user_recent_click_doc_type_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4297; shared=true; feature_id=369357\nfeature_name=fc_user_recent_click_doc_type_7d_has_match; depend=fc_user_recent_click_doc_type_7d,f_doc_type; method=HasMatch; feature_version=2; slot=4299; feature_id=369359\nfeature_name=fc_user_recent_click_doc_type_7d_tob_profile_match; depend=user_recent_click_doc_type_7d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4298; feature_id=369358\nfeature_name=fc_user_recent_exposure_doc_cate1_180d; depend=user_recent_exposure_doc_cate1_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4645; shared=true; feature_id=369705\nfeature_name=fc_user_recent_exposure_doc_cate1_180d_has_match; depend=fc_user_recent_exposure_doc_cate1_180d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4647; feature_id=369707\nfeature_name=fc_user_recent_exposure_doc_cate1_180d_tob_profile_match; depend=user_recent_exposure_doc_cate1_180d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4646; feature_id=369706\nfeature_name=fc_user_recent_exposure_doc_cate1_1d; depend=user_recent_exposure_doc_cate1_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4636; shared=true; feature_id=369696\nfeature_name=fc_user_recent_exposure_doc_cate1_1d_has_match; depend=fc_user_recent_exposure_doc_cate1_1d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4638; feature_id=369698\nfeature_name=fc_user_recent_exposure_doc_cate1_1d_tob_profile_match; depend=user_recent_exposure_doc_cate1_1d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4637; feature_id=369697\nfeature_name=fc_user_recent_exposure_doc_cate1_1h; depend=user_recent_exposure_doc_cate1_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4630; shared=true; feature_id=369690\nfeature_name=fc_user_recent_exposure_doc_cate1_1h_has_match; depend=fc_user_recent_exposure_doc_cate1_1h,f_doc_cate1; method=HasMatch; feature_version=2; slot=4632; feature_id=369692\nfeature_name=fc_user_recent_exposure_doc_cate1_1h_tob_profile_match; depend=user_recent_exposure_doc_cate1_1h,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4631; feature_id=369691\nfeature_name=fc_user_recent_exposure_doc_cate1_30d; depend=user_recent_exposure_doc_cate1_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4642; shared=true; feature_id=369702\nfeature_name=fc_user_recent_exposure_doc_cate1_30d_has_match; depend=fc_user_recent_exposure_doc_cate1_30d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4644; feature_id=369704\nfeature_name=fc_user_recent_exposure_doc_cate1_30d_tob_profile_match; depend=user_recent_exposure_doc_cate1_30d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4643; feature_id=369703\nfeature_name=fc_user_recent_exposure_doc_cate1_6h; depend=user_recent_exposure_doc_cate1_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4633; shared=true; feature_id=369693\nfeature_name=fc_user_recent_exposure_doc_cate1_6h_has_match; depend=fc_user_recent_exposure_doc_cate1_6h,f_doc_cate1; method=HasMatch; feature_version=2; slot=4635; feature_id=369695\nfeature_name=fc_user_recent_exposure_doc_cate1_6h_tob_profile_match; depend=user_recent_exposure_doc_cate1_6h,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4634; feature_id=369694\nfeature_name=fc_user_recent_exposure_doc_cate1_7d; depend=user_recent_exposure_doc_cate1_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4639; shared=true; feature_id=369699\nfeature_name=fc_user_recent_exposure_doc_cate1_7d_has_match; depend=fc_user_recent_exposure_doc_cate1_7d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4641; feature_id=369701\nfeature_name=fc_user_recent_exposure_doc_cate1_7d_tob_profile_match; depend=user_recent_exposure_doc_cate1_7d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4640; feature_id=369700\nfeature_name=fc_user_recent_exposure_doc_cate2_180d; depend=user_recent_exposure_doc_cate2_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4681; shared=true; feature_id=369741\nfeature_name=fc_user_recent_exposure_doc_cate2_180d_has_match; depend=fc_user_recent_exposure_doc_cate2_180d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4683; feature_id=369743\nfeature_name=fc_user_recent_exposure_doc_cate2_180d_tob_profile_match; depend=user_recent_exposure_doc_cate2_180d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4682; feature_id=369742\nfeature_name=fc_user_recent_exposure_doc_cate2_1d; depend=user_recent_exposure_doc_cate2_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4672; shared=true; feature_id=369732\nfeature_name=fc_user_recent_exposure_doc_cate2_1d_has_match; depend=fc_user_recent_exposure_doc_cate2_1d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4674; feature_id=369734\nfeature_name=fc_user_recent_exposure_doc_cate2_1d_tob_profile_match; depend=user_recent_exposure_doc_cate2_1d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4673; feature_id=369733\nfeature_name=fc_user_recent_exposure_doc_cate2_1h; depend=user_recent_exposure_doc_cate2_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4666; shared=true; feature_id=369726\nfeature_name=fc_user_recent_exposure_doc_cate2_1h_has_match; depend=fc_user_recent_exposure_doc_cate2_1h,f_doc_cate2; method=HasMatch; feature_version=2; slot=4668; feature_id=369728\nfeature_name=fc_user_recent_exposure_doc_cate2_1h_tob_profile_match; depend=user_recent_exposure_doc_cate2_1h,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4667; feature_id=369727\nfeature_name=fc_user_recent_exposure_doc_cate2_30d; depend=user_recent_exposure_doc_cate2_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4678; shared=true; feature_id=369738\nfeature_name=fc_user_recent_exposure_doc_cate2_30d_has_match; depend=fc_user_recent_exposure_doc_cate2_30d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4680; feature_id=369740\nfeature_name=fc_user_recent_exposure_doc_cate2_30d_tob_profile_match; depend=user_recent_exposure_doc_cate2_30d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4679; feature_id=369739\nfeature_name=fc_user_recent_exposure_doc_cate2_6h; depend=user_recent_exposure_doc_cate2_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4669; shared=true; feature_id=369729\nfeature_name=fc_user_recent_exposure_doc_cate2_6h_has_match; depend=fc_user_recent_exposure_doc_cate2_6h,f_doc_cate2; method=HasMatch; feature_version=2; slot=4671; feature_id=369731\nfeature_name=fc_user_recent_exposure_doc_cate2_6h_tob_profile_match; depend=user_recent_exposure_doc_cate2_6h,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4670; feature_id=369730\nfeature_name=fc_user_recent_exposure_doc_cate2_7d; depend=user_recent_exposure_doc_cate2_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4675; shared=true; feature_id=369735\nfeature_name=fc_user_recent_exposure_doc_cate2_7d_has_match; depend=fc_user_recent_exposure_doc_cate2_7d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4677; feature_id=369737\nfeature_name=fc_user_recent_exposure_doc_cate2_7d_tob_profile_match; depend=user_recent_exposure_doc_cate2_7d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4676; feature_id=369736\nfeature_name=fc_user_recent_exposure_doc_cate3_180d; depend=user_recent_exposure_doc_cate3_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4663; shared=true; feature_id=369723\nfeature_name=fc_user_recent_exposure_doc_cate3_180d_has_match; depend=fc_user_recent_exposure_doc_cate3_180d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4665; feature_id=369725\nfeature_name=fc_user_recent_exposure_doc_cate3_180d_tob_profile_match; depend=user_recent_exposure_doc_cate3_180d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4664; feature_id=369724\nfeature_name=fc_user_recent_exposure_doc_cate3_1d; depend=user_recent_exposure_doc_cate3_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4654; shared=true; feature_id=369714\nfeature_name=fc_user_recent_exposure_doc_cate3_1d_has_match; depend=fc_user_recent_exposure_doc_cate3_1d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4656; feature_id=369716\nfeature_name=fc_user_recent_exposure_doc_cate3_1d_tob_profile_match; depend=user_recent_exposure_doc_cate3_1d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4655; feature_id=369715\nfeature_name=fc_user_recent_exposure_doc_cate3_1h; depend=user_recent_exposure_doc_cate3_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4648; shared=true; feature_id=369708\nfeature_name=fc_user_recent_exposure_doc_cate3_1h_has_match; depend=fc_user_recent_exposure_doc_cate3_1h,f_doc_cate3; method=HasMatch; feature_version=2; slot=4650; feature_id=369710\nfeature_name=fc_user_recent_exposure_doc_cate3_1h_tob_profile_match; depend=user_recent_exposure_doc_cate3_1h,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4649; feature_id=369709\nfeature_name=fc_user_recent_exposure_doc_cate3_30d; depend=user_recent_exposure_doc_cate3_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4660; shared=true; feature_id=369720\nfeature_name=fc_user_recent_exposure_doc_cate3_30d_has_match; depend=fc_user_recent_exposure_doc_cate3_30d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4662; feature_id=369722\nfeature_name=fc_user_recent_exposure_doc_cate3_30d_tob_profile_match; depend=user_recent_exposure_doc_cate3_30d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4661; feature_id=369721\nfeature_name=fc_user_recent_exposure_doc_cate3_6h; depend=user_recent_exposure_doc_cate3_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4651; shared=true; feature_id=369711\nfeature_name=fc_user_recent_exposure_doc_cate3_6h_has_match; depend=fc_user_recent_exposure_doc_cate3_6h,f_doc_cate3; method=HasMatch; feature_version=2; slot=4653; feature_id=369713\nfeature_name=fc_user_recent_exposure_doc_cate3_6h_tob_profile_match; depend=user_recent_exposure_doc_cate3_6h,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4652; feature_id=369712\nfeature_name=fc_user_recent_exposure_doc_cate3_7d; depend=user_recent_exposure_doc_cate3_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4657; shared=true; feature_id=369717\nfeature_name=fc_user_recent_exposure_doc_cate3_7d_has_match; depend=fc_user_recent_exposure_doc_cate3_7d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4659; feature_id=369719\nfeature_name=fc_user_recent_exposure_doc_cate3_7d_tob_profile_match; depend=user_recent_exposure_doc_cate3_7d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4658; feature_id=369718\nfeature_name=fc_user_recent_exposure_doc_id_180d; depend=user_recent_exposure_doc_id_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4699; shared=true; feature_id=369759\nfeature_name=fc_user_recent_exposure_doc_id_180d_has_match; depend=fc_user_recent_exposure_doc_id_180d,f_doc_id; method=HasMatch; feature_version=2; slot=4701; feature_id=369761\nfeature_name=fc_user_recent_exposure_doc_id_180d_tob_profile_match; depend=user_recent_exposure_doc_id_180d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4700; feature_id=369760\nfeature_name=fc_user_recent_exposure_doc_id_1d; depend=user_recent_exposure_doc_id_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4690; shared=true; feature_id=369750\nfeature_name=fc_user_recent_exposure_doc_id_1d_has_match; depend=fc_user_recent_exposure_doc_id_1d,f_doc_id; method=HasMatch; feature_version=2; slot=4692; feature_id=369752\nfeature_name=fc_user_recent_exposure_doc_id_1d_tob_profile_match; depend=user_recent_exposure_doc_id_1d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4691; feature_id=369751\nfeature_name=fc_user_recent_exposure_doc_id_1h; depend=user_recent_exposure_doc_id_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4684; shared=true; feature_id=369744\nfeature_name=fc_user_recent_exposure_doc_id_1h_has_match; depend=fc_user_recent_exposure_doc_id_1h,f_doc_id; method=HasMatch; feature_version=2; slot=4686; feature_id=369746\nfeature_name=fc_user_recent_exposure_doc_id_1h_tob_profile_match; depend=user_recent_exposure_doc_id_1h,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4685; feature_id=369745\nfeature_name=fc_user_recent_exposure_doc_id_30d; depend=user_recent_exposure_doc_id_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4696; shared=true; feature_id=369756\nfeature_name=fc_user_recent_exposure_doc_id_30d_has_match; depend=fc_user_recent_exposure_doc_id_30d,f_doc_id; method=HasMatch; feature_version=2; slot=4698; feature_id=369758\nfeature_name=fc_user_recent_exposure_doc_id_30d_tob_profile_match; depend=user_recent_exposure_doc_id_30d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4697; feature_id=369757\nfeature_name=fc_user_recent_exposure_doc_id_6h; depend=user_recent_exposure_doc_id_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4687; shared=true; feature_id=369747\nfeature_name=fc_user_recent_exposure_doc_id_6h_has_match; depend=fc_user_recent_exposure_doc_id_6h,f_doc_id; method=HasMatch; feature_version=2; slot=4689; feature_id=369749\nfeature_name=fc_user_recent_exposure_doc_id_6h_tob_profile_match; depend=user_recent_exposure_doc_id_6h,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4688; feature_id=369748\nfeature_name=fc_user_recent_exposure_doc_id_7d; depend=user_recent_exposure_doc_id_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4693; shared=true; feature_id=369753\nfeature_name=fc_user_recent_exposure_doc_id_7d_has_match; depend=fc_user_recent_exposure_doc_id_7d,f_doc_id; method=HasMatch; feature_version=2; slot=4695; feature_id=369755\nfeature_name=fc_user_recent_exposure_doc_id_7d_tob_profile_match; depend=user_recent_exposure_doc_id_7d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4694; feature_id=369754\nfeature_name=fc_user_recent_exposure_doc_keyword_180d; depend=user_recent_exposure_doc_keyword_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4627; shared=true; feature_id=369687\nfeature_name=fc_user_recent_exposure_doc_keyword_180d_has_match; depend=fc_user_recent_exposure_doc_keyword_180d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4629; feature_id=369689\nfeature_name=fc_user_recent_exposure_doc_keyword_180d_tob_profile_match; depend=user_recent_exposure_doc_keyword_180d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4628; feature_id=369688\nfeature_name=fc_user_recent_exposure_doc_keyword_1d; depend=user_recent_exposure_doc_keyword_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4618; shared=true; feature_id=369678\nfeature_name=fc_user_recent_exposure_doc_keyword_1d_has_match; depend=fc_user_recent_exposure_doc_keyword_1d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4620; feature_id=369680\nfeature_name=fc_user_recent_exposure_doc_keyword_1d_tob_profile_match; depend=user_recent_exposure_doc_keyword_1d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4619; feature_id=369679\nfeature_name=fc_user_recent_exposure_doc_keyword_1h; depend=user_recent_exposure_doc_keyword_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4612; shared=true; feature_id=369672\nfeature_name=fc_user_recent_exposure_doc_keyword_1h_has_match; depend=fc_user_recent_exposure_doc_keyword_1h,f_doc_keyword; method=HasMatch; feature_version=2; slot=4614; feature_id=369674\nfeature_name=fc_user_recent_exposure_doc_keyword_1h_tob_profile_match; depend=user_recent_exposure_doc_keyword_1h,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4613; feature_id=369673\nfeature_name=fc_user_recent_exposure_doc_keyword_30d; depend=user_recent_exposure_doc_keyword_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4624; shared=true; feature_id=369684\nfeature_name=fc_user_recent_exposure_doc_keyword_30d_has_match; depend=fc_user_recent_exposure_doc_keyword_30d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4626; feature_id=369686\nfeature_name=fc_user_recent_exposure_doc_keyword_30d_tob_profile_match; depend=user_recent_exposure_doc_keyword_30d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4625; feature_id=369685\nfeature_name=fc_user_recent_exposure_doc_keyword_6h; depend=user_recent_exposure_doc_keyword_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4615; shared=true; feature_id=369675\nfeature_name=fc_user_recent_exposure_doc_keyword_6h_has_match; depend=fc_user_recent_exposure_doc_keyword_6h,f_doc_keyword; method=HasMatch; feature_version=2; slot=4617; feature_id=369677\nfeature_name=fc_user_recent_exposure_doc_keyword_6h_tob_profile_match; depend=user_recent_exposure_doc_keyword_6h,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4616; feature_id=369676\nfeature_name=fc_user_recent_exposure_doc_keyword_7d; depend=user_recent_exposure_doc_keyword_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4621; shared=true; feature_id=369681\nfeature_name=fc_user_recent_exposure_doc_keyword_7d_has_match; depend=fc_user_recent_exposure_doc_keyword_7d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4623; feature_id=369683\nfeature_name=fc_user_recent_exposure_doc_keyword_7d_tob_profile_match; depend=user_recent_exposure_doc_keyword_7d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4622; feature_id=369682\nfeature_name=fc_user_recent_exposure_doc_tags_180d; depend=user_recent_exposure_doc_tags_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4717; shared=true; feature_id=369777\nfeature_name=fc_user_recent_exposure_doc_tags_180d_has_match; depend=fc_user_recent_exposure_doc_tags_180d,f_doc_tags; method=HasMatch; feature_version=2; slot=4719; feature_id=369779\nfeature_name=fc_user_recent_exposure_doc_tags_180d_tob_profile_match; depend=user_recent_exposure_doc_tags_180d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4718; feature_id=369778\nfeature_name=fc_user_recent_exposure_doc_tags_1d; depend=user_recent_exposure_doc_tags_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4708; shared=true; feature_id=369768\nfeature_name=fc_user_recent_exposure_doc_tags_1d_has_match; depend=fc_user_recent_exposure_doc_tags_1d,f_doc_tags; method=HasMatch; feature_version=2; slot=4710; feature_id=369770\nfeature_name=fc_user_recent_exposure_doc_tags_1d_tob_profile_match; depend=user_recent_exposure_doc_tags_1d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4709; feature_id=369769\nfeature_name=fc_user_recent_exposure_doc_tags_1h; depend=user_recent_exposure_doc_tags_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4702; shared=true; feature_id=369762\nfeature_name=fc_user_recent_exposure_doc_tags_1h_has_match; depend=fc_user_recent_exposure_doc_tags_1h,f_doc_tags; method=HasMatch; feature_version=2; slot=4704; feature_id=369764\nfeature_name=fc_user_recent_exposure_doc_tags_1h_tob_profile_match; depend=user_recent_exposure_doc_tags_1h,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4703; feature_id=369763\nfeature_name=fc_user_recent_exposure_doc_tags_30d; depend=user_recent_exposure_doc_tags_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4714; shared=true; feature_id=369774\nfeature_name=fc_user_recent_exposure_doc_tags_30d_has_match; depend=fc_user_recent_exposure_doc_tags_30d,f_doc_tags; method=HasMatch; feature_version=2; slot=4716; feature_id=369776\nfeature_name=fc_user_recent_exposure_doc_tags_30d_tob_profile_match; depend=user_recent_exposure_doc_tags_30d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4715; feature_id=369775\nfeature_name=fc_user_recent_exposure_doc_tags_6h; depend=user_recent_exposure_doc_tags_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4705; shared=true; feature_id=369765\nfeature_name=fc_user_recent_exposure_doc_tags_6h_has_match; depend=fc_user_recent_exposure_doc_tags_6h,f_doc_tags; method=HasMatch; feature_version=2; slot=4707; feature_id=369767\nfeature_name=fc_user_recent_exposure_doc_tags_6h_tob_profile_match; depend=user_recent_exposure_doc_tags_6h,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4706; feature_id=369766\nfeature_name=fc_user_recent_exposure_doc_tags_7d; depend=user_recent_exposure_doc_tags_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4711; shared=true; feature_id=369771\nfeature_name=fc_user_recent_exposure_doc_tags_7d_has_match; depend=fc_user_recent_exposure_doc_tags_7d,f_doc_tags; method=HasMatch; feature_version=2; slot=4713; feature_id=369773\nfeature_name=fc_user_recent_exposure_doc_tags_7d_tob_profile_match; depend=user_recent_exposure_doc_tags_7d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4712; feature_id=369772\nfeature_name=fc_user_recent_exposure_doc_topic_tag_180d; depend=user_recent_exposure_doc_topic_tag_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4609; shared=true; feature_id=369669\nfeature_name=fc_user_recent_exposure_doc_topic_tag_180d_has_match; depend=fc_user_recent_exposure_doc_topic_tag_180d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4611; feature_id=369671\nfeature_name=fc_user_recent_exposure_doc_topic_tag_180d_tob_profile_match; depend=user_recent_exposure_doc_topic_tag_180d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4610; feature_id=369670\nfeature_name=fc_user_recent_exposure_doc_topic_tag_1d; depend=user_recent_exposure_doc_topic_tag_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4600; shared=true; feature_id=369660\nfeature_name=fc_user_recent_exposure_doc_topic_tag_1d_has_match; depend=fc_user_recent_exposure_doc_topic_tag_1d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4602; feature_id=369662\nfeature_name=fc_user_recent_exposure_doc_topic_tag_1d_tob_profile_match; depend=user_recent_exposure_doc_topic_tag_1d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4601; feature_id=369661\nfeature_name=fc_user_recent_exposure_doc_topic_tag_1h; depend=user_recent_exposure_doc_topic_tag_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4594; shared=true; feature_id=369654\nfeature_name=fc_user_recent_exposure_doc_topic_tag_1h_has_match; depend=fc_user_recent_exposure_doc_topic_tag_1h,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4596; feature_id=369656\nfeature_name=fc_user_recent_exposure_doc_topic_tag_1h_tob_profile_match; depend=user_recent_exposure_doc_topic_tag_1h,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4595; feature_id=369655\nfeature_name=fc_user_recent_exposure_doc_topic_tag_30d; depend=user_recent_exposure_doc_topic_tag_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4606; shared=true; feature_id=369666\nfeature_name=fc_user_recent_exposure_doc_topic_tag_30d_has_match; depend=fc_user_recent_exposure_doc_topic_tag_30d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4608; feature_id=369668\nfeature_name=fc_user_recent_exposure_doc_topic_tag_30d_tob_profile_match; depend=user_recent_exposure_doc_topic_tag_30d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4607; feature_id=369667\nfeature_name=fc_user_recent_exposure_doc_topic_tag_6h; depend=user_recent_exposure_doc_topic_tag_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4597; shared=true; feature_id=369657\nfeature_name=fc_user_recent_exposure_doc_topic_tag_6h_has_match; depend=fc_user_recent_exposure_doc_topic_tag_6h,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4599; feature_id=369659\nfeature_name=fc_user_recent_exposure_doc_topic_tag_6h_tob_profile_match; depend=user_recent_exposure_doc_topic_tag_6h,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4598; feature_id=369658\nfeature_name=fc_user_recent_exposure_doc_topic_tag_7d; depend=user_recent_exposure_doc_topic_tag_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4603; shared=true; feature_id=369663\nfeature_name=fc_user_recent_exposure_doc_topic_tag_7d_has_match; depend=fc_user_recent_exposure_doc_topic_tag_7d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4605; feature_id=369665\nfeature_name=fc_user_recent_exposure_doc_topic_tag_7d_tob_profile_match; depend=user_recent_exposure_doc_topic_tag_7d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4604; feature_id=369664\nfeature_name=fc_user_recent_exposure_doc_type_180d; depend=user_recent_exposure_doc_type_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4591; shared=true; feature_id=369651\nfeature_name=fc_user_recent_exposure_doc_type_180d_has_match; depend=fc_user_recent_exposure_doc_type_180d,f_doc_type; method=HasMatch; feature_version=2; slot=4593; feature_id=369653\nfeature_name=fc_user_recent_exposure_doc_type_180d_tob_profile_match; depend=user_recent_exposure_doc_type_180d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4592; feature_id=369652\nfeature_name=fc_user_recent_exposure_doc_type_1d; depend=user_recent_exposure_doc_type_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4582; shared=true; feature_id=369642\nfeature_name=fc_user_recent_exposure_doc_type_1d_has_match; depend=fc_user_recent_exposure_doc_type_1d,f_doc_type; method=HasMatch; feature_version=2; slot=4584; feature_id=369644\nfeature_name=fc_user_recent_exposure_doc_type_1d_tob_profile_match; depend=user_recent_exposure_doc_type_1d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4583; feature_id=369643\nfeature_name=fc_user_recent_exposure_doc_type_1h; depend=user_recent_exposure_doc_type_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4576; shared=true; feature_id=369636\nfeature_name=fc_user_recent_exposure_doc_type_1h_has_match; depend=fc_user_recent_exposure_doc_type_1h,f_doc_type; method=HasMatch; feature_version=2; slot=4578; feature_id=369638\nfeature_name=fc_user_recent_exposure_doc_type_1h_tob_profile_match; depend=user_recent_exposure_doc_type_1h,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4577; feature_id=369637\nfeature_name=fc_user_recent_exposure_doc_type_30d; depend=user_recent_exposure_doc_type_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4588; shared=true; feature_id=369648\nfeature_name=fc_user_recent_exposure_doc_type_30d_has_match; depend=fc_user_recent_exposure_doc_type_30d,f_doc_type; method=HasMatch; feature_version=2; slot=4590; feature_id=369650\nfeature_name=fc_user_recent_exposure_doc_type_30d_tob_profile_match; depend=user_recent_exposure_doc_type_30d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4589; feature_id=369649\nfeature_name=fc_user_recent_exposure_doc_type_6h; depend=user_recent_exposure_doc_type_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4579; shared=true; feature_id=369639\nfeature_name=fc_user_recent_exposure_doc_type_6h_has_match; depend=fc_user_recent_exposure_doc_type_6h,f_doc_type; method=HasMatch; feature_version=2; slot=4581; feature_id=369641\nfeature_name=fc_user_recent_exposure_doc_type_6h_tob_profile_match; depend=user_recent_exposure_doc_type_6h,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4580; feature_id=369640\nfeature_name=fc_user_recent_exposure_doc_type_7d; depend=user_recent_exposure_doc_type_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4585; shared=true; feature_id=369645\nfeature_name=fc_user_recent_exposure_doc_type_7d_has_match; depend=fc_user_recent_exposure_doc_type_7d,f_doc_type; method=HasMatch; feature_version=2; slot=4587; feature_id=369647\nfeature_name=fc_user_recent_exposure_doc_type_7d_tob_profile_match; depend=user_recent_exposure_doc_type_7d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4586; feature_id=369646\nfeature_name=fc_user_recent_favorite_doc_cate1_180d; depend=user_recent_favorite_doc_cate1_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4213; shared=true; feature_id=369273\nfeature_name=fc_user_recent_favorite_doc_cate1_180d_has_match; depend=fc_user_recent_favorite_doc_cate1_180d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4215; feature_id=369275\nfeature_name=fc_user_recent_favorite_doc_cate1_180d_tob_profile_match; depend=user_recent_favorite_doc_cate1_180d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4214; feature_id=369274\nfeature_name=fc_user_recent_favorite_doc_cate1_1d; depend=user_recent_favorite_doc_cate1_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4204; shared=true; feature_id=369264\nfeature_name=fc_user_recent_favorite_doc_cate1_1d_has_match; depend=fc_user_recent_favorite_doc_cate1_1d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4206; feature_id=369266\nfeature_name=fc_user_recent_favorite_doc_cate1_1d_tob_profile_match; depend=user_recent_favorite_doc_cate1_1d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4205; feature_id=369265\nfeature_name=fc_user_recent_favorite_doc_cate1_1h; depend=user_recent_favorite_doc_cate1_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4198; shared=true; feature_id=369258\nfeature_name=fc_user_recent_favorite_doc_cate1_1h_has_match; depend=fc_user_recent_favorite_doc_cate1_1h,f_doc_cate1; method=HasMatch; feature_version=2; slot=4200; feature_id=369260\nfeature_name=fc_user_recent_favorite_doc_cate1_1h_tob_profile_match; depend=user_recent_favorite_doc_cate1_1h,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4199; feature_id=369259\nfeature_name=fc_user_recent_favorite_doc_cate1_30d; depend=user_recent_favorite_doc_cate1_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4210; shared=true; feature_id=369270\nfeature_name=fc_user_recent_favorite_doc_cate1_30d_has_match; depend=fc_user_recent_favorite_doc_cate1_30d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4212; feature_id=369272\nfeature_name=fc_user_recent_favorite_doc_cate1_30d_tob_profile_match; depend=user_recent_favorite_doc_cate1_30d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4211; feature_id=369271\nfeature_name=fc_user_recent_favorite_doc_cate1_6h; depend=user_recent_favorite_doc_cate1_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4201; shared=true; feature_id=369261\nfeature_name=fc_user_recent_favorite_doc_cate1_6h_has_match; depend=fc_user_recent_favorite_doc_cate1_6h,f_doc_cate1; method=HasMatch; feature_version=2; slot=4203; feature_id=369263\nfeature_name=fc_user_recent_favorite_doc_cate1_6h_tob_profile_match; depend=user_recent_favorite_doc_cate1_6h,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4202; feature_id=369262\nfeature_name=fc_user_recent_favorite_doc_cate1_7d; depend=user_recent_favorite_doc_cate1_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4207; shared=true; feature_id=369267\nfeature_name=fc_user_recent_favorite_doc_cate1_7d_has_match; depend=fc_user_recent_favorite_doc_cate1_7d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4209; feature_id=369269\nfeature_name=fc_user_recent_favorite_doc_cate1_7d_tob_profile_match; depend=user_recent_favorite_doc_cate1_7d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4208; feature_id=369268\nfeature_name=fc_user_recent_favorite_doc_cate2_180d; depend=user_recent_favorite_doc_cate2_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4249; shared=true; feature_id=369309\nfeature_name=fc_user_recent_favorite_doc_cate2_180d_has_match; depend=fc_user_recent_favorite_doc_cate2_180d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4251; feature_id=369311\nfeature_name=fc_user_recent_favorite_doc_cate2_180d_tob_profile_match; depend=user_recent_favorite_doc_cate2_180d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4250; feature_id=369310\nfeature_name=fc_user_recent_favorite_doc_cate2_1d; depend=user_recent_favorite_doc_cate2_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4240; shared=true; feature_id=369300\nfeature_name=fc_user_recent_favorite_doc_cate2_1d_has_match; depend=fc_user_recent_favorite_doc_cate2_1d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4242; feature_id=369302\nfeature_name=fc_user_recent_favorite_doc_cate2_1d_tob_profile_match; depend=user_recent_favorite_doc_cate2_1d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4241; feature_id=369301\nfeature_name=fc_user_recent_favorite_doc_cate2_1h; depend=user_recent_favorite_doc_cate2_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4234; shared=true; feature_id=369294\nfeature_name=fc_user_recent_favorite_doc_cate2_1h_has_match; depend=fc_user_recent_favorite_doc_cate2_1h,f_doc_cate2; method=HasMatch; feature_version=2; slot=4236; feature_id=369296\nfeature_name=fc_user_recent_favorite_doc_cate2_1h_tob_profile_match; depend=user_recent_favorite_doc_cate2_1h,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4235; feature_id=369295\nfeature_name=fc_user_recent_favorite_doc_cate2_30d; depend=user_recent_favorite_doc_cate2_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4246; shared=true; feature_id=369306\nfeature_name=fc_user_recent_favorite_doc_cate2_30d_has_match; depend=fc_user_recent_favorite_doc_cate2_30d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4248; feature_id=369308\nfeature_name=fc_user_recent_favorite_doc_cate2_30d_tob_profile_match; depend=user_recent_favorite_doc_cate2_30d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4247; feature_id=369307\nfeature_name=fc_user_recent_favorite_doc_cate2_6h; depend=user_recent_favorite_doc_cate2_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4237; shared=true; feature_id=369297\nfeature_name=fc_user_recent_favorite_doc_cate2_6h_has_match; depend=fc_user_recent_favorite_doc_cate2_6h,f_doc_cate2; method=HasMatch; feature_version=2; slot=4239; feature_id=369299\nfeature_name=fc_user_recent_favorite_doc_cate2_6h_tob_profile_match; depend=user_recent_favorite_doc_cate2_6h,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4238; feature_id=369298\nfeature_name=fc_user_recent_favorite_doc_cate2_7d; depend=user_recent_favorite_doc_cate2_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4243; shared=true; feature_id=369303\nfeature_name=fc_user_recent_favorite_doc_cate2_7d_has_match; depend=fc_user_recent_favorite_doc_cate2_7d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4245; feature_id=369305\nfeature_name=fc_user_recent_favorite_doc_cate2_7d_tob_profile_match; depend=user_recent_favorite_doc_cate2_7d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4244; feature_id=369304\nfeature_name=fc_user_recent_favorite_doc_cate3_180d; depend=user_recent_favorite_doc_cate3_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4231; shared=true; feature_id=369291\nfeature_name=fc_user_recent_favorite_doc_cate3_180d_has_match; depend=fc_user_recent_favorite_doc_cate3_180d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4233; feature_id=369293\nfeature_name=fc_user_recent_favorite_doc_cate3_180d_tob_profile_match; depend=user_recent_favorite_doc_cate3_180d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4232; feature_id=369292\nfeature_name=fc_user_recent_favorite_doc_cate3_1d; depend=user_recent_favorite_doc_cate3_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4222; shared=true; feature_id=369282\nfeature_name=fc_user_recent_favorite_doc_cate3_1d_has_match; depend=fc_user_recent_favorite_doc_cate3_1d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4224; feature_id=369284\nfeature_name=fc_user_recent_favorite_doc_cate3_1d_tob_profile_match; depend=user_recent_favorite_doc_cate3_1d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4223; feature_id=369283\nfeature_name=fc_user_recent_favorite_doc_cate3_1h; depend=user_recent_favorite_doc_cate3_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4216; shared=true; feature_id=369276\nfeature_name=fc_user_recent_favorite_doc_cate3_1h_has_match; depend=fc_user_recent_favorite_doc_cate3_1h,f_doc_cate3; method=HasMatch; feature_version=2; slot=4218; feature_id=369278\nfeature_name=fc_user_recent_favorite_doc_cate3_1h_tob_profile_match; depend=user_recent_favorite_doc_cate3_1h,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4217; feature_id=369277\nfeature_name=fc_user_recent_favorite_doc_cate3_30d; depend=user_recent_favorite_doc_cate3_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4228; shared=true; feature_id=369288\nfeature_name=fc_user_recent_favorite_doc_cate3_30d_has_match; depend=fc_user_recent_favorite_doc_cate3_30d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4230; feature_id=369290\nfeature_name=fc_user_recent_favorite_doc_cate3_30d_tob_profile_match; depend=user_recent_favorite_doc_cate3_30d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4229; feature_id=369289\nfeature_name=fc_user_recent_favorite_doc_cate3_6h; depend=user_recent_favorite_doc_cate3_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4219; shared=true; feature_id=369279\nfeature_name=fc_user_recent_favorite_doc_cate3_6h_has_match; depend=fc_user_recent_favorite_doc_cate3_6h,f_doc_cate3; method=HasMatch; feature_version=2; slot=4221; feature_id=369281\nfeature_name=fc_user_recent_favorite_doc_cate3_6h_tob_profile_match; depend=user_recent_favorite_doc_cate3_6h,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4220; feature_id=369280\nfeature_name=fc_user_recent_favorite_doc_cate3_7d; depend=user_recent_favorite_doc_cate3_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4225; shared=true; feature_id=369285\nfeature_name=fc_user_recent_favorite_doc_cate3_7d_has_match; depend=fc_user_recent_favorite_doc_cate3_7d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4227; feature_id=369287\nfeature_name=fc_user_recent_favorite_doc_cate3_7d_tob_profile_match; depend=user_recent_favorite_doc_cate3_7d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4226; feature_id=369286\nfeature_name=fc_user_recent_favorite_doc_id_180d; depend=user_recent_favorite_doc_id_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4267; shared=true; feature_id=369327\nfeature_name=fc_user_recent_favorite_doc_id_180d_has_match; depend=fc_user_recent_favorite_doc_id_180d,f_doc_id; method=HasMatch; feature_version=2; slot=4269; feature_id=369329\nfeature_name=fc_user_recent_favorite_doc_id_180d_tob_profile_match; depend=user_recent_favorite_doc_id_180d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4268; feature_id=369328\nfeature_name=fc_user_recent_favorite_doc_id_1d; depend=user_recent_favorite_doc_id_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4258; shared=true; feature_id=369318\nfeature_name=fc_user_recent_favorite_doc_id_1d_has_match; depend=fc_user_recent_favorite_doc_id_1d,f_doc_id; method=HasMatch; feature_version=2; slot=4260; feature_id=369320\nfeature_name=fc_user_recent_favorite_doc_id_1d_tob_profile_match; depend=user_recent_favorite_doc_id_1d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4259; feature_id=369319\nfeature_name=fc_user_recent_favorite_doc_id_1h; depend=user_recent_favorite_doc_id_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4252; shared=true; feature_id=369312\nfeature_name=fc_user_recent_favorite_doc_id_1h_has_match; depend=fc_user_recent_favorite_doc_id_1h,f_doc_id; method=HasMatch; feature_version=2; slot=4254; feature_id=369314\nfeature_name=fc_user_recent_favorite_doc_id_1h_tob_profile_match; depend=user_recent_favorite_doc_id_1h,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4253; feature_id=369313\nfeature_name=fc_user_recent_favorite_doc_id_30d; depend=user_recent_favorite_doc_id_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4264; shared=true; feature_id=369324\nfeature_name=fc_user_recent_favorite_doc_id_30d_has_match; depend=fc_user_recent_favorite_doc_id_30d,f_doc_id; method=HasMatch; feature_version=2; slot=4266; feature_id=369326\nfeature_name=fc_user_recent_favorite_doc_id_30d_tob_profile_match; depend=user_recent_favorite_doc_id_30d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4265; feature_id=369325\nfeature_name=fc_user_recent_favorite_doc_id_6h; depend=user_recent_favorite_doc_id_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4255; shared=true; feature_id=369315\nfeature_name=fc_user_recent_favorite_doc_id_6h_has_match; depend=fc_user_recent_favorite_doc_id_6h,f_doc_id; method=HasMatch; feature_version=2; slot=4257; feature_id=369317\nfeature_name=fc_user_recent_favorite_doc_id_6h_tob_profile_match; depend=user_recent_favorite_doc_id_6h,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4256; feature_id=369316\nfeature_name=fc_user_recent_favorite_doc_id_7d; depend=user_recent_favorite_doc_id_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4261; shared=true; feature_id=369321\nfeature_name=fc_user_recent_favorite_doc_id_7d_has_match; depend=fc_user_recent_favorite_doc_id_7d,f_doc_id; method=HasMatch; feature_version=2; slot=4263; feature_id=369323\nfeature_name=fc_user_recent_favorite_doc_id_7d_tob_profile_match; depend=user_recent_favorite_doc_id_7d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4262; feature_id=369322\nfeature_name=fc_user_recent_favorite_doc_keyword_180d; depend=user_recent_favorite_doc_keyword_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4195; shared=true; feature_id=369255\nfeature_name=fc_user_recent_favorite_doc_keyword_180d_has_match; depend=fc_user_recent_favorite_doc_keyword_180d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4197; feature_id=369257\nfeature_name=fc_user_recent_favorite_doc_keyword_180d_tob_profile_match; depend=user_recent_favorite_doc_keyword_180d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4196; feature_id=369256\nfeature_name=fc_user_recent_favorite_doc_keyword_1d; depend=user_recent_favorite_doc_keyword_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4186; shared=true; feature_id=369246\nfeature_name=fc_user_recent_favorite_doc_keyword_1d_has_match; depend=fc_user_recent_favorite_doc_keyword_1d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4188; feature_id=369248\nfeature_name=fc_user_recent_favorite_doc_keyword_1d_tob_profile_match; depend=user_recent_favorite_doc_keyword_1d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4187; feature_id=369247\nfeature_name=fc_user_recent_favorite_doc_keyword_1h; depend=user_recent_favorite_doc_keyword_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4180; shared=true; feature_id=369240\nfeature_name=fc_user_recent_favorite_doc_keyword_1h_has_match; depend=fc_user_recent_favorite_doc_keyword_1h,f_doc_keyword; method=HasMatch; feature_version=2; slot=4182; feature_id=369242\nfeature_name=fc_user_recent_favorite_doc_keyword_1h_tob_profile_match; depend=user_recent_favorite_doc_keyword_1h,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4181; feature_id=369241\nfeature_name=fc_user_recent_favorite_doc_keyword_30d; depend=user_recent_favorite_doc_keyword_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4192; shared=true; feature_id=369252\nfeature_name=fc_user_recent_favorite_doc_keyword_30d_has_match; depend=fc_user_recent_favorite_doc_keyword_30d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4194; feature_id=369254\nfeature_name=fc_user_recent_favorite_doc_keyword_30d_tob_profile_match; depend=user_recent_favorite_doc_keyword_30d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4193; feature_id=369253\nfeature_name=fc_user_recent_favorite_doc_keyword_6h; depend=user_recent_favorite_doc_keyword_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4183; shared=true; feature_id=369243\nfeature_name=fc_user_recent_favorite_doc_keyword_6h_has_match; depend=fc_user_recent_favorite_doc_keyword_6h,f_doc_keyword; method=HasMatch; feature_version=2; slot=4185; feature_id=369245\nfeature_name=fc_user_recent_favorite_doc_keyword_6h_tob_profile_match; depend=user_recent_favorite_doc_keyword_6h,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4184; feature_id=369244\nfeature_name=fc_user_recent_favorite_doc_keyword_7d; depend=user_recent_favorite_doc_keyword_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4189; shared=true; feature_id=369249\nfeature_name=fc_user_recent_favorite_doc_keyword_7d_has_match; depend=fc_user_recent_favorite_doc_keyword_7d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4191; feature_id=369251\nfeature_name=fc_user_recent_favorite_doc_keyword_7d_tob_profile_match; depend=user_recent_favorite_doc_keyword_7d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4190; feature_id=369250\nfeature_name=fc_user_recent_favorite_doc_tags_180d; depend=user_recent_favorite_doc_tags_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4285; shared=true; feature_id=369345\nfeature_name=fc_user_recent_favorite_doc_tags_180d_has_match; depend=fc_user_recent_favorite_doc_tags_180d,f_doc_tags; method=HasMatch; feature_version=2; slot=4287; feature_id=369347\nfeature_name=fc_user_recent_favorite_doc_tags_180d_tob_profile_match; depend=user_recent_favorite_doc_tags_180d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4286; feature_id=369346\nfeature_name=fc_user_recent_favorite_doc_tags_1d; depend=user_recent_favorite_doc_tags_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4276; shared=true; feature_id=369336\nfeature_name=fc_user_recent_favorite_doc_tags_1d_has_match; depend=fc_user_recent_favorite_doc_tags_1d,f_doc_tags; method=HasMatch; feature_version=2; slot=4278; feature_id=369338\nfeature_name=fc_user_recent_favorite_doc_tags_1d_tob_profile_match; depend=user_recent_favorite_doc_tags_1d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4277; feature_id=369337\nfeature_name=fc_user_recent_favorite_doc_tags_1h; depend=user_recent_favorite_doc_tags_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4270; shared=true; feature_id=369330\nfeature_name=fc_user_recent_favorite_doc_tags_1h_has_match; depend=fc_user_recent_favorite_doc_tags_1h,f_doc_tags; method=HasMatch; feature_version=2; slot=4272; feature_id=369332\nfeature_name=fc_user_recent_favorite_doc_tags_1h_tob_profile_match; depend=user_recent_favorite_doc_tags_1h,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4271; feature_id=369331\nfeature_name=fc_user_recent_favorite_doc_tags_30d; depend=user_recent_favorite_doc_tags_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4282; shared=true; feature_id=369342\nfeature_name=fc_user_recent_favorite_doc_tags_30d_has_match; depend=fc_user_recent_favorite_doc_tags_30d,f_doc_tags; method=HasMatch; feature_version=2; slot=4284; feature_id=369344\nfeature_name=fc_user_recent_favorite_doc_tags_30d_tob_profile_match; depend=user_recent_favorite_doc_tags_30d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4283; feature_id=369343\nfeature_name=fc_user_recent_favorite_doc_tags_6h; depend=user_recent_favorite_doc_tags_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4273; shared=true; feature_id=369333\nfeature_name=fc_user_recent_favorite_doc_tags_6h_has_match; depend=fc_user_recent_favorite_doc_tags_6h,f_doc_tags; method=HasMatch; feature_version=2; slot=4275; feature_id=369335\nfeature_name=fc_user_recent_favorite_doc_tags_6h_tob_profile_match; depend=user_recent_favorite_doc_tags_6h,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4274; feature_id=369334\nfeature_name=fc_user_recent_favorite_doc_tags_7d; depend=user_recent_favorite_doc_tags_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4279; shared=true; feature_id=369339\nfeature_name=fc_user_recent_favorite_doc_tags_7d_has_match; depend=fc_user_recent_favorite_doc_tags_7d,f_doc_tags; method=HasMatch; feature_version=2; slot=4281; feature_id=369341\nfeature_name=fc_user_recent_favorite_doc_tags_7d_tob_profile_match; depend=user_recent_favorite_doc_tags_7d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4280; feature_id=369340\nfeature_name=fc_user_recent_favorite_doc_topic_tag_180d; depend=user_recent_favorite_doc_topic_tag_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4177; shared=true; feature_id=369237\nfeature_name=fc_user_recent_favorite_doc_topic_tag_180d_has_match; depend=fc_user_recent_favorite_doc_topic_tag_180d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4179; feature_id=369239\nfeature_name=fc_user_recent_favorite_doc_topic_tag_180d_tob_profile_match; depend=user_recent_favorite_doc_topic_tag_180d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4178; feature_id=369238\nfeature_name=fc_user_recent_favorite_doc_topic_tag_1d; depend=user_recent_favorite_doc_topic_tag_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4168; shared=true; feature_id=369228\nfeature_name=fc_user_recent_favorite_doc_topic_tag_1d_has_match; depend=fc_user_recent_favorite_doc_topic_tag_1d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4170; feature_id=369230\nfeature_name=fc_user_recent_favorite_doc_topic_tag_1d_tob_profile_match; depend=user_recent_favorite_doc_topic_tag_1d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4169; feature_id=369229\nfeature_name=fc_user_recent_favorite_doc_topic_tag_1h; depend=user_recent_favorite_doc_topic_tag_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4162; shared=true; feature_id=369222\nfeature_name=fc_user_recent_favorite_doc_topic_tag_1h_has_match; depend=fc_user_recent_favorite_doc_topic_tag_1h,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4164; feature_id=369224\nfeature_name=fc_user_recent_favorite_doc_topic_tag_1h_tob_profile_match; depend=user_recent_favorite_doc_topic_tag_1h,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4163; feature_id=369223\nfeature_name=fc_user_recent_favorite_doc_topic_tag_30d; depend=user_recent_favorite_doc_topic_tag_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4174; shared=true; feature_id=369234\nfeature_name=fc_user_recent_favorite_doc_topic_tag_30d_has_match; depend=fc_user_recent_favorite_doc_topic_tag_30d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4176; feature_id=369236\nfeature_name=fc_user_recent_favorite_doc_topic_tag_30d_tob_profile_match; depend=user_recent_favorite_doc_topic_tag_30d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4175; feature_id=369235\nfeature_name=fc_user_recent_favorite_doc_topic_tag_6h; depend=user_recent_favorite_doc_topic_tag_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4165; shared=true; feature_id=369225\nfeature_name=fc_user_recent_favorite_doc_topic_tag_6h_has_match; depend=fc_user_recent_favorite_doc_topic_tag_6h,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4167; feature_id=369227\nfeature_name=fc_user_recent_favorite_doc_topic_tag_6h_tob_profile_match; depend=user_recent_favorite_doc_topic_tag_6h,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4166; feature_id=369226\nfeature_name=fc_user_recent_favorite_doc_topic_tag_7d; depend=user_recent_favorite_doc_topic_tag_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4171; shared=true; feature_id=369231\nfeature_name=fc_user_recent_favorite_doc_topic_tag_7d_has_match; depend=fc_user_recent_favorite_doc_topic_tag_7d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4173; feature_id=369233\nfeature_name=fc_user_recent_favorite_doc_topic_tag_7d_tob_profile_match; depend=user_recent_favorite_doc_topic_tag_7d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4172; feature_id=369232\nfeature_name=fc_user_recent_favorite_doc_type_180d; depend=user_recent_favorite_doc_type_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4159; shared=true; feature_id=369219\nfeature_name=fc_user_recent_favorite_doc_type_180d_has_match; depend=fc_user_recent_favorite_doc_type_180d,f_doc_type; method=HasMatch; feature_version=2; slot=4161; feature_id=369221\nfeature_name=fc_user_recent_favorite_doc_type_180d_tob_profile_match; depend=user_recent_favorite_doc_type_180d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4160; feature_id=369220\nfeature_name=fc_user_recent_favorite_doc_type_1d; depend=user_recent_favorite_doc_type_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4150; shared=true; feature_id=369210\nfeature_name=fc_user_recent_favorite_doc_type_1d_has_match; depend=fc_user_recent_favorite_doc_type_1d,f_doc_type; method=HasMatch; feature_version=2; slot=4152; feature_id=369212\nfeature_name=fc_user_recent_favorite_doc_type_1d_tob_profile_match; depend=user_recent_favorite_doc_type_1d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4151; feature_id=369211\nfeature_name=fc_user_recent_favorite_doc_type_1h; depend=user_recent_favorite_doc_type_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4144; shared=true; feature_id=369204\nfeature_name=fc_user_recent_favorite_doc_type_1h_has_match; depend=fc_user_recent_favorite_doc_type_1h,f_doc_type; method=HasMatch; feature_version=2; slot=4146; feature_id=369206\nfeature_name=fc_user_recent_favorite_doc_type_1h_tob_profile_match; depend=user_recent_favorite_doc_type_1h,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4145; feature_id=369205\nfeature_name=fc_user_recent_favorite_doc_type_30d; depend=user_recent_favorite_doc_type_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4156; shared=true; feature_id=369216\nfeature_name=fc_user_recent_favorite_doc_type_30d_has_match; depend=fc_user_recent_favorite_doc_type_30d,f_doc_type; method=HasMatch; feature_version=2; slot=4158; feature_id=369218\nfeature_name=fc_user_recent_favorite_doc_type_30d_tob_profile_match; depend=user_recent_favorite_doc_type_30d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4157; feature_id=369217\nfeature_name=fc_user_recent_favorite_doc_type_6h; depend=user_recent_favorite_doc_type_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4147; shared=true; feature_id=369207\nfeature_name=fc_user_recent_favorite_doc_type_6h_has_match; depend=fc_user_recent_favorite_doc_type_6h,f_doc_type; method=HasMatch; feature_version=2; slot=4149; feature_id=369209\nfeature_name=fc_user_recent_favorite_doc_type_6h_tob_profile_match; depend=user_recent_favorite_doc_type_6h,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4148; feature_id=369208\nfeature_name=fc_user_recent_favorite_doc_type_7d; depend=user_recent_favorite_doc_type_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4153; shared=true; feature_id=369213\nfeature_name=fc_user_recent_favorite_doc_type_7d_has_match; depend=fc_user_recent_favorite_doc_type_7d,f_doc_type; method=HasMatch; feature_version=2; slot=4155; feature_id=369215\nfeature_name=fc_user_recent_favorite_doc_type_7d_tob_profile_match; depend=user_recent_favorite_doc_type_7d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4154; feature_id=369214\nfeature_name=fc_user_recent_praise_doc_cate1_180d; depend=user_recent_praise_doc_cate1_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4501; shared=true; feature_id=369561\nfeature_name=fc_user_recent_praise_doc_cate1_180d_has_match; depend=fc_user_recent_praise_doc_cate1_180d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4503; feature_id=369563\nfeature_name=fc_user_recent_praise_doc_cate1_180d_tob_profile_match; depend=user_recent_praise_doc_cate1_180d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4502; feature_id=369562\nfeature_name=fc_user_recent_praise_doc_cate1_1d; depend=user_recent_praise_doc_cate1_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4492; shared=true; feature_id=369552\nfeature_name=fc_user_recent_praise_doc_cate1_1d_has_match; depend=fc_user_recent_praise_doc_cate1_1d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4494; feature_id=369554\nfeature_name=fc_user_recent_praise_doc_cate1_1d_tob_profile_match; depend=user_recent_praise_doc_cate1_1d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4493; feature_id=369553\nfeature_name=fc_user_recent_praise_doc_cate1_1h; depend=user_recent_praise_doc_cate1_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4486; shared=true; feature_id=369546\nfeature_name=fc_user_recent_praise_doc_cate1_1h_has_match; depend=fc_user_recent_praise_doc_cate1_1h,f_doc_cate1; method=HasMatch; feature_version=2; slot=4488; feature_id=369548\nfeature_name=fc_user_recent_praise_doc_cate1_1h_tob_profile_match; depend=user_recent_praise_doc_cate1_1h,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4487; feature_id=369547\nfeature_name=fc_user_recent_praise_doc_cate1_30d; depend=user_recent_praise_doc_cate1_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4498; shared=true; feature_id=369558\nfeature_name=fc_user_recent_praise_doc_cate1_30d_has_match; depend=fc_user_recent_praise_doc_cate1_30d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4500; feature_id=369560\nfeature_name=fc_user_recent_praise_doc_cate1_30d_tob_profile_match; depend=user_recent_praise_doc_cate1_30d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4499; feature_id=369559\nfeature_name=fc_user_recent_praise_doc_cate1_6h; depend=user_recent_praise_doc_cate1_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4489; shared=true; feature_id=369549\nfeature_name=fc_user_recent_praise_doc_cate1_6h_has_match; depend=fc_user_recent_praise_doc_cate1_6h,f_doc_cate1; method=HasMatch; feature_version=2; slot=4491; feature_id=369551\nfeature_name=fc_user_recent_praise_doc_cate1_6h_tob_profile_match; depend=user_recent_praise_doc_cate1_6h,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4490; feature_id=369550\nfeature_name=fc_user_recent_praise_doc_cate1_7d; depend=user_recent_praise_doc_cate1_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4495; shared=true; feature_id=369555\nfeature_name=fc_user_recent_praise_doc_cate1_7d_has_match; depend=fc_user_recent_praise_doc_cate1_7d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4497; feature_id=369557\nfeature_name=fc_user_recent_praise_doc_cate1_7d_tob_profile_match; depend=user_recent_praise_doc_cate1_7d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4496; feature_id=369556\nfeature_name=fc_user_recent_praise_doc_cate2_180d; depend=user_recent_praise_doc_cate2_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4537; shared=true; feature_id=369597\nfeature_name=fc_user_recent_praise_doc_cate2_180d_has_match; depend=fc_user_recent_praise_doc_cate2_180d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4539; feature_id=369599\nfeature_name=fc_user_recent_praise_doc_cate2_180d_tob_profile_match; depend=user_recent_praise_doc_cate2_180d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4538; feature_id=369598\nfeature_name=fc_user_recent_praise_doc_cate2_1d; depend=user_recent_praise_doc_cate2_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4528; shared=true; feature_id=369588\nfeature_name=fc_user_recent_praise_doc_cate2_1d_has_match; depend=fc_user_recent_praise_doc_cate2_1d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4530; feature_id=369590\nfeature_name=fc_user_recent_praise_doc_cate2_1d_tob_profile_match; depend=user_recent_praise_doc_cate2_1d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4529; feature_id=369589\nfeature_name=fc_user_recent_praise_doc_cate2_1h; depend=user_recent_praise_doc_cate2_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4522; shared=true; feature_id=369582\nfeature_name=fc_user_recent_praise_doc_cate2_1h_has_match; depend=fc_user_recent_praise_doc_cate2_1h,f_doc_cate2; method=HasMatch; feature_version=2; slot=4524; feature_id=369584\nfeature_name=fc_user_recent_praise_doc_cate2_1h_tob_profile_match; depend=user_recent_praise_doc_cate2_1h,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4523; feature_id=369583\nfeature_name=fc_user_recent_praise_doc_cate2_30d; depend=user_recent_praise_doc_cate2_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4534; shared=true; feature_id=369594\nfeature_name=fc_user_recent_praise_doc_cate2_30d_has_match; depend=fc_user_recent_praise_doc_cate2_30d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4536; feature_id=369596\nfeature_name=fc_user_recent_praise_doc_cate2_30d_tob_profile_match; depend=user_recent_praise_doc_cate2_30d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4535; feature_id=369595\nfeature_name=fc_user_recent_praise_doc_cate2_6h; depend=user_recent_praise_doc_cate2_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4525; shared=true; feature_id=369585\nfeature_name=fc_user_recent_praise_doc_cate2_6h_has_match; depend=fc_user_recent_praise_doc_cate2_6h,f_doc_cate2; method=HasMatch; feature_version=2; slot=4527; feature_id=369587\nfeature_name=fc_user_recent_praise_doc_cate2_6h_tob_profile_match; depend=user_recent_praise_doc_cate2_6h,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4526; feature_id=369586\nfeature_name=fc_user_recent_praise_doc_cate2_7d; depend=user_recent_praise_doc_cate2_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4531; shared=true; feature_id=369591\nfeature_name=fc_user_recent_praise_doc_cate2_7d_has_match; depend=fc_user_recent_praise_doc_cate2_7d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4533; feature_id=369593\nfeature_name=fc_user_recent_praise_doc_cate2_7d_tob_profile_match; depend=user_recent_praise_doc_cate2_7d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4532; feature_id=369592\nfeature_name=fc_user_recent_praise_doc_cate3_180d; depend=user_recent_praise_doc_cate3_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4519; shared=true; feature_id=369579\nfeature_name=fc_user_recent_praise_doc_cate3_180d_has_match; depend=fc_user_recent_praise_doc_cate3_180d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4521; feature_id=369581\nfeature_name=fc_user_recent_praise_doc_cate3_180d_tob_profile_match; depend=user_recent_praise_doc_cate3_180d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4520; feature_id=369580\nfeature_name=fc_user_recent_praise_doc_cate3_1d; depend=user_recent_praise_doc_cate3_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4510; shared=true; feature_id=369570\nfeature_name=fc_user_recent_praise_doc_cate3_1d_has_match; depend=fc_user_recent_praise_doc_cate3_1d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4512; feature_id=369572\nfeature_name=fc_user_recent_praise_doc_cate3_1d_tob_profile_match; depend=user_recent_praise_doc_cate3_1d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4511; feature_id=369571\nfeature_name=fc_user_recent_praise_doc_cate3_1h; depend=user_recent_praise_doc_cate3_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4504; shared=true; feature_id=369564\nfeature_name=fc_user_recent_praise_doc_cate3_1h_has_match; depend=fc_user_recent_praise_doc_cate3_1h,f_doc_cate3; method=HasMatch; feature_version=2; slot=4506; feature_id=369566\nfeature_name=fc_user_recent_praise_doc_cate3_1h_tob_profile_match; depend=user_recent_praise_doc_cate3_1h,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4505; feature_id=369565\nfeature_name=fc_user_recent_praise_doc_cate3_30d; depend=user_recent_praise_doc_cate3_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4516; shared=true; feature_id=369576\nfeature_name=fc_user_recent_praise_doc_cate3_30d_has_match; depend=fc_user_recent_praise_doc_cate3_30d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4518; feature_id=369578\nfeature_name=fc_user_recent_praise_doc_cate3_30d_tob_profile_match; depend=user_recent_praise_doc_cate3_30d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4517; feature_id=369577\nfeature_name=fc_user_recent_praise_doc_cate3_6h; depend=user_recent_praise_doc_cate3_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4507; shared=true; feature_id=369567\nfeature_name=fc_user_recent_praise_doc_cate3_6h_has_match; depend=fc_user_recent_praise_doc_cate3_6h,f_doc_cate3; method=HasMatch; feature_version=2; slot=4509; feature_id=369569\nfeature_name=fc_user_recent_praise_doc_cate3_6h_tob_profile_match; depend=user_recent_praise_doc_cate3_6h,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4508; feature_id=369568\nfeature_name=fc_user_recent_praise_doc_cate3_7d; depend=user_recent_praise_doc_cate3_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4513; shared=true; feature_id=369573\nfeature_name=fc_user_recent_praise_doc_cate3_7d_has_match; depend=fc_user_recent_praise_doc_cate3_7d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4515; feature_id=369575\nfeature_name=fc_user_recent_praise_doc_cate3_7d_tob_profile_match; depend=user_recent_praise_doc_cate3_7d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4514; feature_id=369574\nfeature_name=fc_user_recent_praise_doc_id_180d; depend=user_recent_praise_doc_id_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4555; shared=true; feature_id=369615\nfeature_name=fc_user_recent_praise_doc_id_180d_has_match; depend=fc_user_recent_praise_doc_id_180d,f_doc_id; method=HasMatch; feature_version=2; slot=4557; feature_id=369617\nfeature_name=fc_user_recent_praise_doc_id_180d_tob_profile_match; depend=user_recent_praise_doc_id_180d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4556; feature_id=369616\nfeature_name=fc_user_recent_praise_doc_id_1d; depend=user_recent_praise_doc_id_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4546; shared=true; feature_id=369606\nfeature_name=fc_user_recent_praise_doc_id_1d_has_match; depend=fc_user_recent_praise_doc_id_1d,f_doc_id; method=HasMatch; feature_version=2; slot=4548; feature_id=369608\nfeature_name=fc_user_recent_praise_doc_id_1d_tob_profile_match; depend=user_recent_praise_doc_id_1d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4547; feature_id=369607\nfeature_name=fc_user_recent_praise_doc_id_1h; depend=user_recent_praise_doc_id_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4540; shared=true; feature_id=369600\nfeature_name=fc_user_recent_praise_doc_id_1h_has_match; depend=fc_user_recent_praise_doc_id_1h,f_doc_id; method=HasMatch; feature_version=2; slot=4542; feature_id=369602\nfeature_name=fc_user_recent_praise_doc_id_1h_tob_profile_match; depend=user_recent_praise_doc_id_1h,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4541; feature_id=369601\nfeature_name=fc_user_recent_praise_doc_id_30d; depend=user_recent_praise_doc_id_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4552; shared=true; feature_id=369612\nfeature_name=fc_user_recent_praise_doc_id_30d_has_match; depend=fc_user_recent_praise_doc_id_30d,f_doc_id; method=HasMatch; feature_version=2; slot=4554; feature_id=369614\nfeature_name=fc_user_recent_praise_doc_id_30d_tob_profile_match; depend=user_recent_praise_doc_id_30d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4553; feature_id=369613\nfeature_name=fc_user_recent_praise_doc_id_6h; depend=user_recent_praise_doc_id_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4543; shared=true; feature_id=369603\nfeature_name=fc_user_recent_praise_doc_id_6h_has_match; depend=fc_user_recent_praise_doc_id_6h,f_doc_id; method=HasMatch; feature_version=2; slot=4545; feature_id=369605\nfeature_name=fc_user_recent_praise_doc_id_6h_tob_profile_match; depend=user_recent_praise_doc_id_6h,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4544; feature_id=369604\nfeature_name=fc_user_recent_praise_doc_id_7d; depend=user_recent_praise_doc_id_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4549; shared=true; feature_id=369609\nfeature_name=fc_user_recent_praise_doc_id_7d_has_match; depend=fc_user_recent_praise_doc_id_7d,f_doc_id; method=HasMatch; feature_version=2; slot=4551; feature_id=369611\nfeature_name=fc_user_recent_praise_doc_id_7d_tob_profile_match; depend=user_recent_praise_doc_id_7d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4550; feature_id=369610\nfeature_name=fc_user_recent_praise_doc_keyword_180d; depend=user_recent_praise_doc_keyword_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4483; shared=true; feature_id=369543\nfeature_name=fc_user_recent_praise_doc_keyword_180d_has_match; depend=fc_user_recent_praise_doc_keyword_180d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4485; feature_id=369545\nfeature_name=fc_user_recent_praise_doc_keyword_180d_tob_profile_match; depend=user_recent_praise_doc_keyword_180d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4484; feature_id=369544\nfeature_name=fc_user_recent_praise_doc_keyword_1d; depend=user_recent_praise_doc_keyword_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4474; shared=true; feature_id=369534\nfeature_name=fc_user_recent_praise_doc_keyword_1d_has_match; depend=fc_user_recent_praise_doc_keyword_1d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4476; feature_id=369536\nfeature_name=fc_user_recent_praise_doc_keyword_1d_tob_profile_match; depend=user_recent_praise_doc_keyword_1d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4475; feature_id=369535\nfeature_name=fc_user_recent_praise_doc_keyword_1h; depend=user_recent_praise_doc_keyword_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4468; shared=true; feature_id=369528\nfeature_name=fc_user_recent_praise_doc_keyword_1h_has_match; depend=fc_user_recent_praise_doc_keyword_1h,f_doc_keyword; method=HasMatch; feature_version=2; slot=4470; feature_id=369530\nfeature_name=fc_user_recent_praise_doc_keyword_1h_tob_profile_match; depend=user_recent_praise_doc_keyword_1h,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4469; feature_id=369529\nfeature_name=fc_user_recent_praise_doc_keyword_30d; depend=user_recent_praise_doc_keyword_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4480; shared=true; feature_id=369540\nfeature_name=fc_user_recent_praise_doc_keyword_30d_has_match; depend=fc_user_recent_praise_doc_keyword_30d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4482; feature_id=369542\nfeature_name=fc_user_recent_praise_doc_keyword_30d_tob_profile_match; depend=user_recent_praise_doc_keyword_30d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4481; feature_id=369541\nfeature_name=fc_user_recent_praise_doc_keyword_6h; depend=user_recent_praise_doc_keyword_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4471; shared=true; feature_id=369531\nfeature_name=fc_user_recent_praise_doc_keyword_6h_has_match; depend=fc_user_recent_praise_doc_keyword_6h,f_doc_keyword; method=HasMatch; feature_version=2; slot=4473; feature_id=369533\nfeature_name=fc_user_recent_praise_doc_keyword_6h_tob_profile_match; depend=user_recent_praise_doc_keyword_6h,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4472; feature_id=369532\nfeature_name=fc_user_recent_praise_doc_keyword_7d; depend=user_recent_praise_doc_keyword_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4477; shared=true; feature_id=369537\nfeature_name=fc_user_recent_praise_doc_keyword_7d_has_match; depend=fc_user_recent_praise_doc_keyword_7d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4479; feature_id=369539\nfeature_name=fc_user_recent_praise_doc_keyword_7d_tob_profile_match; depend=user_recent_praise_doc_keyword_7d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4478; feature_id=369538\nfeature_name=fc_user_recent_praise_doc_tags_180d; depend=user_recent_praise_doc_tags_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4573; shared=true; feature_id=369633\nfeature_name=fc_user_recent_praise_doc_tags_180d_has_match; depend=fc_user_recent_praise_doc_tags_180d,f_doc_tags; method=HasMatch; feature_version=2; slot=4575; feature_id=369635\nfeature_name=fc_user_recent_praise_doc_tags_180d_tob_profile_match; depend=user_recent_praise_doc_tags_180d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4574; feature_id=369634\nfeature_name=fc_user_recent_praise_doc_tags_1d; depend=user_recent_praise_doc_tags_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4564; shared=true; feature_id=369624\nfeature_name=fc_user_recent_praise_doc_tags_1d_has_match; depend=fc_user_recent_praise_doc_tags_1d,f_doc_tags; method=HasMatch; feature_version=2; slot=4566; feature_id=369626\nfeature_name=fc_user_recent_praise_doc_tags_1d_tob_profile_match; depend=user_recent_praise_doc_tags_1d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4565; feature_id=369625\nfeature_name=fc_user_recent_praise_doc_tags_1h; depend=user_recent_praise_doc_tags_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4558; shared=true; feature_id=369618\nfeature_name=fc_user_recent_praise_doc_tags_1h_has_match; depend=fc_user_recent_praise_doc_tags_1h,f_doc_tags; method=HasMatch; feature_version=2; slot=4560; feature_id=369620\nfeature_name=fc_user_recent_praise_doc_tags_1h_tob_profile_match; depend=user_recent_praise_doc_tags_1h,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4559; feature_id=369619\nfeature_name=fc_user_recent_praise_doc_tags_30d; depend=user_recent_praise_doc_tags_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4570; shared=true; feature_id=369630\nfeature_name=fc_user_recent_praise_doc_tags_30d_has_match; depend=fc_user_recent_praise_doc_tags_30d,f_doc_tags; method=HasMatch; feature_version=2; slot=4572; feature_id=369632\nfeature_name=fc_user_recent_praise_doc_tags_30d_tob_profile_match; depend=user_recent_praise_doc_tags_30d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4571; feature_id=369631\nfeature_name=fc_user_recent_praise_doc_tags_6h; depend=user_recent_praise_doc_tags_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4561; shared=true; feature_id=369621\nfeature_name=fc_user_recent_praise_doc_tags_6h_has_match; depend=fc_user_recent_praise_doc_tags_6h,f_doc_tags; method=HasMatch; feature_version=2; slot=4563; feature_id=369623\nfeature_name=fc_user_recent_praise_doc_tags_6h_tob_profile_match; depend=user_recent_praise_doc_tags_6h,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4562; feature_id=369622\nfeature_name=fc_user_recent_praise_doc_tags_7d; depend=user_recent_praise_doc_tags_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4567; shared=true; feature_id=369627\nfeature_name=fc_user_recent_praise_doc_tags_7d_has_match; depend=fc_user_recent_praise_doc_tags_7d,f_doc_tags; method=HasMatch; feature_version=2; slot=4569; feature_id=369629\nfeature_name=fc_user_recent_praise_doc_tags_7d_tob_profile_match; depend=user_recent_praise_doc_tags_7d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4568; feature_id=369628\nfeature_name=fc_user_recent_praise_doc_topic_tag_180d; depend=user_recent_praise_doc_topic_tag_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4465; shared=true; feature_id=369525\nfeature_name=fc_user_recent_praise_doc_topic_tag_180d_has_match; depend=fc_user_recent_praise_doc_topic_tag_180d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4467; feature_id=369527\nfeature_name=fc_user_recent_praise_doc_topic_tag_180d_tob_profile_match; depend=user_recent_praise_doc_topic_tag_180d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4466; feature_id=369526\nfeature_name=fc_user_recent_praise_doc_topic_tag_1d; depend=user_recent_praise_doc_topic_tag_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4456; shared=true; feature_id=369516\nfeature_name=fc_user_recent_praise_doc_topic_tag_1d_has_match; depend=fc_user_recent_praise_doc_topic_tag_1d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4458; feature_id=369518\nfeature_name=fc_user_recent_praise_doc_topic_tag_1d_tob_profile_match; depend=user_recent_praise_doc_topic_tag_1d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4457; feature_id=369517\nfeature_name=fc_user_recent_praise_doc_topic_tag_1h; depend=user_recent_praise_doc_topic_tag_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4450; shared=true; feature_id=369510\nfeature_name=fc_user_recent_praise_doc_topic_tag_1h_has_match; depend=fc_user_recent_praise_doc_topic_tag_1h,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4452; feature_id=369512\nfeature_name=fc_user_recent_praise_doc_topic_tag_1h_tob_profile_match; depend=user_recent_praise_doc_topic_tag_1h,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4451; feature_id=369511\nfeature_name=fc_user_recent_praise_doc_topic_tag_30d; depend=user_recent_praise_doc_topic_tag_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4462; shared=true; feature_id=369522\nfeature_name=fc_user_recent_praise_doc_topic_tag_30d_has_match; depend=fc_user_recent_praise_doc_topic_tag_30d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4464; feature_id=369524\nfeature_name=fc_user_recent_praise_doc_topic_tag_30d_tob_profile_match; depend=user_recent_praise_doc_topic_tag_30d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4463; feature_id=369523\nfeature_name=fc_user_recent_praise_doc_topic_tag_6h; depend=user_recent_praise_doc_topic_tag_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4453; shared=true; feature_id=369513\nfeature_name=fc_user_recent_praise_doc_topic_tag_6h_has_match; depend=fc_user_recent_praise_doc_topic_tag_6h,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4455; feature_id=369515\nfeature_name=fc_user_recent_praise_doc_topic_tag_6h_tob_profile_match; depend=user_recent_praise_doc_topic_tag_6h,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4454; feature_id=369514\nfeature_name=fc_user_recent_praise_doc_topic_tag_7d; depend=user_recent_praise_doc_topic_tag_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4459; shared=true; feature_id=369519\nfeature_name=fc_user_recent_praise_doc_topic_tag_7d_has_match; depend=fc_user_recent_praise_doc_topic_tag_7d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4461; feature_id=369521\nfeature_name=fc_user_recent_praise_doc_topic_tag_7d_tob_profile_match; depend=user_recent_praise_doc_topic_tag_7d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4460; feature_id=369520\nfeature_name=fc_user_recent_praise_doc_type_180d; depend=user_recent_praise_doc_type_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4447; shared=true; feature_id=369507\nfeature_name=fc_user_recent_praise_doc_type_180d_has_match; depend=fc_user_recent_praise_doc_type_180d,f_doc_type; method=HasMatch; feature_version=2; slot=4449; feature_id=369509\nfeature_name=fc_user_recent_praise_doc_type_180d_tob_profile_match; depend=user_recent_praise_doc_type_180d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4448; feature_id=369508\nfeature_name=fc_user_recent_praise_doc_type_1d; depend=user_recent_praise_doc_type_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4438; shared=true; feature_id=369498\nfeature_name=fc_user_recent_praise_doc_type_1d_has_match; depend=fc_user_recent_praise_doc_type_1d,f_doc_type; method=HasMatch; feature_version=2; slot=4440; feature_id=369500\nfeature_name=fc_user_recent_praise_doc_type_1d_tob_profile_match; depend=user_recent_praise_doc_type_1d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4439; feature_id=369499\nfeature_name=fc_user_recent_praise_doc_type_1h; depend=user_recent_praise_doc_type_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4432; shared=true; feature_id=369492\nfeature_name=fc_user_recent_praise_doc_type_1h_has_match; depend=fc_user_recent_praise_doc_type_1h,f_doc_type; method=HasMatch; feature_version=2; slot=4434; feature_id=369494\nfeature_name=fc_user_recent_praise_doc_type_1h_tob_profile_match; depend=user_recent_praise_doc_type_1h,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4433; feature_id=369493\nfeature_name=fc_user_recent_praise_doc_type_30d; depend=user_recent_praise_doc_type_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4444; shared=true; feature_id=369504\nfeature_name=fc_user_recent_praise_doc_type_30d_has_match; depend=fc_user_recent_praise_doc_type_30d,f_doc_type; method=HasMatch; feature_version=2; slot=4446; feature_id=369506\nfeature_name=fc_user_recent_praise_doc_type_30d_tob_profile_match; depend=user_recent_praise_doc_type_30d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4445; feature_id=369505\nfeature_name=fc_user_recent_praise_doc_type_6h; depend=user_recent_praise_doc_type_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4435; shared=true; feature_id=369495\nfeature_name=fc_user_recent_praise_doc_type_6h_has_match; depend=fc_user_recent_praise_doc_type_6h,f_doc_type; method=HasMatch; feature_version=2; slot=4437; feature_id=369497\nfeature_name=fc_user_recent_praise_doc_type_6h_tob_profile_match; depend=user_recent_praise_doc_type_6h,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4436; feature_id=369496\nfeature_name=fc_user_recent_praise_doc_type_7d; depend=user_recent_praise_doc_type_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4441; shared=true; feature_id=369501\nfeature_name=fc_user_recent_praise_doc_type_7d_has_match; depend=fc_user_recent_praise_doc_type_7d,f_doc_type; method=HasMatch; feature_version=2; slot=4443; feature_id=369503\nfeature_name=fc_user_recent_praise_doc_type_7d_tob_profile_match; depend=user_recent_praise_doc_type_7d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4442; feature_id=369502\nfeature_name=fc_user_recent_share_doc_cate1_180d; depend=user_recent_share_doc_cate1_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4069; shared=true; feature_id=369129\nfeature_name=fc_user_recent_share_doc_cate1_180d_has_match; depend=fc_user_recent_share_doc_cate1_180d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4071; feature_id=369131\nfeature_name=fc_user_recent_share_doc_cate1_180d_tob_profile_match; depend=user_recent_share_doc_cate1_180d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4070; feature_id=369130\nfeature_name=fc_user_recent_share_doc_cate1_1d; depend=user_recent_share_doc_cate1_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4060; shared=true; feature_id=369120\nfeature_name=fc_user_recent_share_doc_cate1_1d_has_match; depend=fc_user_recent_share_doc_cate1_1d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4062; feature_id=369122\nfeature_name=fc_user_recent_share_doc_cate1_1d_tob_profile_match; depend=user_recent_share_doc_cate1_1d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4061; feature_id=369121\nfeature_name=fc_user_recent_share_doc_cate1_1h; depend=user_recent_share_doc_cate1_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4054; shared=true; feature_id=369114\nfeature_name=fc_user_recent_share_doc_cate1_1h_has_match; depend=fc_user_recent_share_doc_cate1_1h,f_doc_cate1; method=HasMatch; feature_version=2; slot=4056; feature_id=369116\nfeature_name=fc_user_recent_share_doc_cate1_1h_tob_profile_match; depend=user_recent_share_doc_cate1_1h,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4055; feature_id=369115\nfeature_name=fc_user_recent_share_doc_cate1_30d; depend=user_recent_share_doc_cate1_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4066; shared=true; feature_id=369126\nfeature_name=fc_user_recent_share_doc_cate1_30d_has_match; depend=fc_user_recent_share_doc_cate1_30d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4068; feature_id=369128\nfeature_name=fc_user_recent_share_doc_cate1_30d_tob_profile_match; depend=user_recent_share_doc_cate1_30d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4067; feature_id=369127\nfeature_name=fc_user_recent_share_doc_cate1_6h; depend=user_recent_share_doc_cate1_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4057; shared=true; feature_id=369117\nfeature_name=fc_user_recent_share_doc_cate1_6h_has_match; depend=fc_user_recent_share_doc_cate1_6h,f_doc_cate1; method=HasMatch; feature_version=2; slot=4059; feature_id=369119\nfeature_name=fc_user_recent_share_doc_cate1_6h_tob_profile_match; depend=user_recent_share_doc_cate1_6h,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4058; feature_id=369118\nfeature_name=fc_user_recent_share_doc_cate1_7d; depend=user_recent_share_doc_cate1_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4063; shared=true; feature_id=369123\nfeature_name=fc_user_recent_share_doc_cate1_7d_has_match; depend=fc_user_recent_share_doc_cate1_7d,f_doc_cate1; method=HasMatch; feature_version=2; slot=4065; feature_id=369125\nfeature_name=fc_user_recent_share_doc_cate1_7d_tob_profile_match; depend=user_recent_share_doc_cate1_7d,f_doc_cate1; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4064; feature_id=369124\nfeature_name=fc_user_recent_share_doc_cate2_180d; depend=user_recent_share_doc_cate2_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4105; shared=true; feature_id=369165\nfeature_name=fc_user_recent_share_doc_cate2_180d_has_match; depend=fc_user_recent_share_doc_cate2_180d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4107; feature_id=369167\nfeature_name=fc_user_recent_share_doc_cate2_180d_tob_profile_match; depend=user_recent_share_doc_cate2_180d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4106; feature_id=369166\nfeature_name=fc_user_recent_share_doc_cate2_1d; depend=user_recent_share_doc_cate2_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4096; shared=true; feature_id=369156\nfeature_name=fc_user_recent_share_doc_cate2_1d_has_match; depend=fc_user_recent_share_doc_cate2_1d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4098; feature_id=369158\nfeature_name=fc_user_recent_share_doc_cate2_1d_tob_profile_match; depend=user_recent_share_doc_cate2_1d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4097; feature_id=369157\nfeature_name=fc_user_recent_share_doc_cate2_1h; depend=user_recent_share_doc_cate2_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4090; shared=true; feature_id=369150\nfeature_name=fc_user_recent_share_doc_cate2_1h_has_match; depend=fc_user_recent_share_doc_cate2_1h,f_doc_cate2; method=HasMatch; feature_version=2; slot=4092; feature_id=369152\nfeature_name=fc_user_recent_share_doc_cate2_1h_tob_profile_match; depend=user_recent_share_doc_cate2_1h,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4091; feature_id=369151\nfeature_name=fc_user_recent_share_doc_cate2_30d; depend=user_recent_share_doc_cate2_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4102; shared=true; feature_id=369162\nfeature_name=fc_user_recent_share_doc_cate2_30d_has_match; depend=fc_user_recent_share_doc_cate2_30d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4104; feature_id=369164\nfeature_name=fc_user_recent_share_doc_cate2_30d_tob_profile_match; depend=user_recent_share_doc_cate2_30d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4103; feature_id=369163\nfeature_name=fc_user_recent_share_doc_cate2_6h; depend=user_recent_share_doc_cate2_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4093; shared=true; feature_id=369153\nfeature_name=fc_user_recent_share_doc_cate2_6h_has_match; depend=fc_user_recent_share_doc_cate2_6h,f_doc_cate2; method=HasMatch; feature_version=2; slot=4095; feature_id=369155\nfeature_name=fc_user_recent_share_doc_cate2_6h_tob_profile_match; depend=user_recent_share_doc_cate2_6h,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4094; feature_id=369154\nfeature_name=fc_user_recent_share_doc_cate2_7d; depend=user_recent_share_doc_cate2_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4099; shared=true; feature_id=369159\nfeature_name=fc_user_recent_share_doc_cate2_7d_has_match; depend=fc_user_recent_share_doc_cate2_7d,f_doc_cate2; method=HasMatch; feature_version=2; slot=4101; feature_id=369161\nfeature_name=fc_user_recent_share_doc_cate2_7d_tob_profile_match; depend=user_recent_share_doc_cate2_7d,f_doc_cate2; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4100; feature_id=369160\nfeature_name=fc_user_recent_share_doc_cate3_180d; depend=user_recent_share_doc_cate3_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4087; shared=true; feature_id=369147\nfeature_name=fc_user_recent_share_doc_cate3_180d_has_match; depend=fc_user_recent_share_doc_cate3_180d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4089; feature_id=369149\nfeature_name=fc_user_recent_share_doc_cate3_180d_tob_profile_match; depend=user_recent_share_doc_cate3_180d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4088; feature_id=369148\nfeature_name=fc_user_recent_share_doc_cate3_1d; depend=user_recent_share_doc_cate3_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4078; shared=true; feature_id=369138\nfeature_name=fc_user_recent_share_doc_cate3_1d_has_match; depend=fc_user_recent_share_doc_cate3_1d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4080; feature_id=369140\nfeature_name=fc_user_recent_share_doc_cate3_1d_tob_profile_match; depend=user_recent_share_doc_cate3_1d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4079; feature_id=369139\nfeature_name=fc_user_recent_share_doc_cate3_1h; depend=user_recent_share_doc_cate3_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4072; shared=true; feature_id=369132\nfeature_name=fc_user_recent_share_doc_cate3_1h_has_match; depend=fc_user_recent_share_doc_cate3_1h,f_doc_cate3; method=HasMatch; feature_version=2; slot=4074; feature_id=369134\nfeature_name=fc_user_recent_share_doc_cate3_1h_tob_profile_match; depend=user_recent_share_doc_cate3_1h,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4073; feature_id=369133\nfeature_name=fc_user_recent_share_doc_cate3_30d; depend=user_recent_share_doc_cate3_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4084; shared=true; feature_id=369144\nfeature_name=fc_user_recent_share_doc_cate3_30d_has_match; depend=fc_user_recent_share_doc_cate3_30d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4086; feature_id=369146\nfeature_name=fc_user_recent_share_doc_cate3_30d_tob_profile_match; depend=user_recent_share_doc_cate3_30d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4085; feature_id=369145\nfeature_name=fc_user_recent_share_doc_cate3_6h; depend=user_recent_share_doc_cate3_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4075; shared=true; feature_id=369135\nfeature_name=fc_user_recent_share_doc_cate3_6h_has_match; depend=fc_user_recent_share_doc_cate3_6h,f_doc_cate3; method=HasMatch; feature_version=2; slot=4077; feature_id=369137\nfeature_name=fc_user_recent_share_doc_cate3_6h_tob_profile_match; depend=user_recent_share_doc_cate3_6h,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4076; feature_id=369136\nfeature_name=fc_user_recent_share_doc_cate3_7d; depend=user_recent_share_doc_cate3_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4081; shared=true; feature_id=369141\nfeature_name=fc_user_recent_share_doc_cate3_7d_has_match; depend=fc_user_recent_share_doc_cate3_7d,f_doc_cate3; method=HasMatch; feature_version=2; slot=4083; feature_id=369143\nfeature_name=fc_user_recent_share_doc_cate3_7d_tob_profile_match; depend=user_recent_share_doc_cate3_7d,f_doc_cate3; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4082; feature_id=369142\nfeature_name=fc_user_recent_share_doc_id_180d; depend=user_recent_share_doc_id_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4123; shared=true; feature_id=369183\nfeature_name=fc_user_recent_share_doc_id_180d_has_match; depend=fc_user_recent_share_doc_id_180d,f_doc_id; method=HasMatch; feature_version=2; slot=4125; feature_id=369185\nfeature_name=fc_user_recent_share_doc_id_180d_tob_profile_match; depend=user_recent_share_doc_id_180d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4124; feature_id=369184\nfeature_name=fc_user_recent_share_doc_id_1d; depend=user_recent_share_doc_id_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4114; shared=true; feature_id=369174\nfeature_name=fc_user_recent_share_doc_id_1d_has_match; depend=fc_user_recent_share_doc_id_1d,f_doc_id; method=HasMatch; feature_version=2; slot=4116; feature_id=369176\nfeature_name=fc_user_recent_share_doc_id_1d_tob_profile_match; depend=user_recent_share_doc_id_1d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4115; feature_id=369175\nfeature_name=fc_user_recent_share_doc_id_1h; depend=user_recent_share_doc_id_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4108; shared=true; feature_id=369168\nfeature_name=fc_user_recent_share_doc_id_1h_has_match; depend=fc_user_recent_share_doc_id_1h,f_doc_id; method=HasMatch; feature_version=2; slot=4110; feature_id=369170\nfeature_name=fc_user_recent_share_doc_id_1h_tob_profile_match; depend=user_recent_share_doc_id_1h,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4109; feature_id=369169\nfeature_name=fc_user_recent_share_doc_id_30d; depend=user_recent_share_doc_id_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4120; shared=true; feature_id=369180\nfeature_name=fc_user_recent_share_doc_id_30d_has_match; depend=fc_user_recent_share_doc_id_30d,f_doc_id; method=HasMatch; feature_version=2; slot=4122; feature_id=369182\nfeature_name=fc_user_recent_share_doc_id_30d_tob_profile_match; depend=user_recent_share_doc_id_30d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4121; feature_id=369181\nfeature_name=fc_user_recent_share_doc_id_6h; depend=user_recent_share_doc_id_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4111; shared=true; feature_id=369171\nfeature_name=fc_user_recent_share_doc_id_6h_has_match; depend=fc_user_recent_share_doc_id_6h,f_doc_id; method=HasMatch; feature_version=2; slot=4113; feature_id=369173\nfeature_name=fc_user_recent_share_doc_id_6h_tob_profile_match; depend=user_recent_share_doc_id_6h,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4112; feature_id=369172\nfeature_name=fc_user_recent_share_doc_id_7d; depend=user_recent_share_doc_id_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4117; shared=true; feature_id=369177\nfeature_name=fc_user_recent_share_doc_id_7d_has_match; depend=fc_user_recent_share_doc_id_7d,f_doc_id; method=HasMatch; feature_version=2; slot=4119; feature_id=369179\nfeature_name=fc_user_recent_share_doc_id_7d_tob_profile_match; depend=user_recent_share_doc_id_7d,f_doc_id; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4118; feature_id=369178\nfeature_name=fc_user_recent_share_doc_keyword_180d; depend=user_recent_share_doc_keyword_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4051; shared=true; feature_id=369111\nfeature_name=fc_user_recent_share_doc_keyword_180d_has_match; depend=fc_user_recent_share_doc_keyword_180d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4053; feature_id=369113\nfeature_name=fc_user_recent_share_doc_keyword_180d_tob_profile_match; depend=user_recent_share_doc_keyword_180d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4052; feature_id=369112\nfeature_name=fc_user_recent_share_doc_keyword_1d; depend=user_recent_share_doc_keyword_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4042; shared=true; feature_id=369102\nfeature_name=fc_user_recent_share_doc_keyword_1d_has_match; depend=fc_user_recent_share_doc_keyword_1d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4044; feature_id=369104\nfeature_name=fc_user_recent_share_doc_keyword_1d_tob_profile_match; depend=user_recent_share_doc_keyword_1d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4043; feature_id=369103\nfeature_name=fc_user_recent_share_doc_keyword_1h; depend=user_recent_share_doc_keyword_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4036; shared=true; feature_id=369096\nfeature_name=fc_user_recent_share_doc_keyword_1h_has_match; depend=fc_user_recent_share_doc_keyword_1h,f_doc_keyword; method=HasMatch; feature_version=2; slot=4038; feature_id=369098\nfeature_name=fc_user_recent_share_doc_keyword_1h_tob_profile_match; depend=user_recent_share_doc_keyword_1h,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4037; feature_id=369097\nfeature_name=fc_user_recent_share_doc_keyword_30d; depend=user_recent_share_doc_keyword_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4048; shared=true; feature_id=369108\nfeature_name=fc_user_recent_share_doc_keyword_30d_has_match; depend=fc_user_recent_share_doc_keyword_30d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4050; feature_id=369110\nfeature_name=fc_user_recent_share_doc_keyword_30d_tob_profile_match; depend=user_recent_share_doc_keyword_30d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4049; feature_id=369109\nfeature_name=fc_user_recent_share_doc_keyword_6h; depend=user_recent_share_doc_keyword_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4039; shared=true; feature_id=369099\nfeature_name=fc_user_recent_share_doc_keyword_6h_has_match; depend=fc_user_recent_share_doc_keyword_6h,f_doc_keyword; method=HasMatch; feature_version=2; slot=4041; feature_id=369101\nfeature_name=fc_user_recent_share_doc_keyword_6h_tob_profile_match; depend=user_recent_share_doc_keyword_6h,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4040; feature_id=369100\nfeature_name=fc_user_recent_share_doc_keyword_7d; depend=user_recent_share_doc_keyword_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4045; shared=true; feature_id=369105\nfeature_name=fc_user_recent_share_doc_keyword_7d_has_match; depend=fc_user_recent_share_doc_keyword_7d,f_doc_keyword; method=HasMatch; feature_version=2; slot=4047; feature_id=369107\nfeature_name=fc_user_recent_share_doc_keyword_7d_tob_profile_match; depend=user_recent_share_doc_keyword_7d,f_doc_keyword; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4046; feature_id=369106\nfeature_name=fc_user_recent_share_doc_tags_180d; depend=user_recent_share_doc_tags_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4141; shared=true; feature_id=369201\nfeature_name=fc_user_recent_share_doc_tags_180d_has_match; depend=fc_user_recent_share_doc_tags_180d,f_doc_tags; method=HasMatch; feature_version=2; slot=4143; feature_id=369203\nfeature_name=fc_user_recent_share_doc_tags_180d_tob_profile_match; depend=user_recent_share_doc_tags_180d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4142; feature_id=369202\nfeature_name=fc_user_recent_share_doc_tags_1d; depend=user_recent_share_doc_tags_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4132; shared=true; feature_id=369192\nfeature_name=fc_user_recent_share_doc_tags_1d_has_match; depend=fc_user_recent_share_doc_tags_1d,f_doc_tags; method=HasMatch; feature_version=2; slot=4134; feature_id=369194\nfeature_name=fc_user_recent_share_doc_tags_1d_tob_profile_match; depend=user_recent_share_doc_tags_1d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4133; feature_id=369193\nfeature_name=fc_user_recent_share_doc_tags_1h; depend=user_recent_share_doc_tags_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4126; shared=true; feature_id=369186\nfeature_name=fc_user_recent_share_doc_tags_1h_has_match; depend=fc_user_recent_share_doc_tags_1h,f_doc_tags; method=HasMatch; feature_version=2; slot=4128; feature_id=369188\nfeature_name=fc_user_recent_share_doc_tags_1h_tob_profile_match; depend=user_recent_share_doc_tags_1h,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4127; feature_id=369187\nfeature_name=fc_user_recent_share_doc_tags_30d; depend=user_recent_share_doc_tags_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4138; shared=true; feature_id=369198\nfeature_name=fc_user_recent_share_doc_tags_30d_has_match; depend=fc_user_recent_share_doc_tags_30d,f_doc_tags; method=HasMatch; feature_version=2; slot=4140; feature_id=369200\nfeature_name=fc_user_recent_share_doc_tags_30d_tob_profile_match; depend=user_recent_share_doc_tags_30d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4139; feature_id=369199\nfeature_name=fc_user_recent_share_doc_tags_6h; depend=user_recent_share_doc_tags_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4129; shared=true; feature_id=369189\nfeature_name=fc_user_recent_share_doc_tags_6h_has_match; depend=fc_user_recent_share_doc_tags_6h,f_doc_tags; method=HasMatch; feature_version=2; slot=4131; feature_id=369191\nfeature_name=fc_user_recent_share_doc_tags_6h_tob_profile_match; depend=user_recent_share_doc_tags_6h,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4130; feature_id=369190\nfeature_name=fc_user_recent_share_doc_tags_7d; depend=user_recent_share_doc_tags_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4135; shared=true; feature_id=369195\nfeature_name=fc_user_recent_share_doc_tags_7d_has_match; depend=fc_user_recent_share_doc_tags_7d,f_doc_tags; method=HasMatch; feature_version=2; slot=4137; feature_id=369197\nfeature_name=fc_user_recent_share_doc_tags_7d_tob_profile_match; depend=user_recent_share_doc_tags_7d,f_doc_tags; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4136; feature_id=369196\nfeature_name=fc_user_recent_share_doc_topic_tag_180d; depend=user_recent_share_doc_topic_tag_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4033; shared=true; feature_id=369093\nfeature_name=fc_user_recent_share_doc_topic_tag_180d_has_match; depend=fc_user_recent_share_doc_topic_tag_180d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4035; feature_id=369095\nfeature_name=fc_user_recent_share_doc_topic_tag_180d_tob_profile_match; depend=user_recent_share_doc_topic_tag_180d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4034; feature_id=369094\nfeature_name=fc_user_recent_share_doc_topic_tag_1d; depend=user_recent_share_doc_topic_tag_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4024; shared=true; feature_id=369084\nfeature_name=fc_user_recent_share_doc_topic_tag_1d_has_match; depend=fc_user_recent_share_doc_topic_tag_1d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4026; feature_id=369086\nfeature_name=fc_user_recent_share_doc_topic_tag_1d_tob_profile_match; depend=user_recent_share_doc_topic_tag_1d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4025; feature_id=369085\nfeature_name=fc_user_recent_share_doc_topic_tag_1h; depend=user_recent_share_doc_topic_tag_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4018; shared=true; feature_id=369078\nfeature_name=fc_user_recent_share_doc_topic_tag_1h_has_match; depend=fc_user_recent_share_doc_topic_tag_1h,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4020; feature_id=369080\nfeature_name=fc_user_recent_share_doc_topic_tag_1h_tob_profile_match; depend=user_recent_share_doc_topic_tag_1h,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4019; feature_id=369079\nfeature_name=fc_user_recent_share_doc_topic_tag_30d; depend=user_recent_share_doc_topic_tag_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4030; shared=true; feature_id=369090\nfeature_name=fc_user_recent_share_doc_topic_tag_30d_has_match; depend=fc_user_recent_share_doc_topic_tag_30d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4032; feature_id=369092\nfeature_name=fc_user_recent_share_doc_topic_tag_30d_tob_profile_match; depend=user_recent_share_doc_topic_tag_30d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4031; feature_id=369091\nfeature_name=fc_user_recent_share_doc_topic_tag_6h; depend=user_recent_share_doc_topic_tag_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4021; shared=true; feature_id=369081\nfeature_name=fc_user_recent_share_doc_topic_tag_6h_has_match; depend=fc_user_recent_share_doc_topic_tag_6h,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4023; feature_id=369083\nfeature_name=fc_user_recent_share_doc_topic_tag_6h_tob_profile_match; depend=user_recent_share_doc_topic_tag_6h,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4022; feature_id=369082\nfeature_name=fc_user_recent_share_doc_topic_tag_7d; depend=user_recent_share_doc_topic_tag_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4027; shared=true; feature_id=369087\nfeature_name=fc_user_recent_share_doc_topic_tag_7d_has_match; depend=fc_user_recent_share_doc_topic_tag_7d,f_doc_topic_tag; method=HasMatch; feature_version=2; slot=4029; feature_id=369089\nfeature_name=fc_user_recent_share_doc_topic_tag_7d_tob_profile_match; depend=user_recent_share_doc_topic_tag_7d,f_doc_topic_tag; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4028; feature_id=369088\nfeature_name=fc_user_recent_share_doc_type_180d; depend=user_recent_share_doc_type_180d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4015; shared=true; feature_id=369075\nfeature_name=fc_user_recent_share_doc_type_180d_has_match; depend=fc_user_recent_share_doc_type_180d,f_doc_type; method=HasMatch; feature_version=2; slot=4017; feature_id=369077\nfeature_name=fc_user_recent_share_doc_type_180d_tob_profile_match; depend=user_recent_share_doc_type_180d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4016; feature_id=369076\nfeature_name=fc_user_recent_share_doc_type_1d; depend=user_recent_share_doc_type_1d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4006; shared=true; feature_id=369066\nfeature_name=fc_user_recent_share_doc_type_1d_has_match; depend=fc_user_recent_share_doc_type_1d,f_doc_type; method=HasMatch; feature_version=2; slot=4008; feature_id=369068\nfeature_name=fc_user_recent_share_doc_type_1d_tob_profile_match; depend=user_recent_share_doc_type_1d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4007; feature_id=369067\nfeature_name=fc_user_recent_share_doc_type_1h; depend=user_recent_share_doc_type_1h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4000; shared=true; feature_id=369060\nfeature_name=fc_user_recent_share_doc_type_1h_has_match; depend=fc_user_recent_share_doc_type_1h,f_doc_type; method=HasMatch; feature_version=2; slot=4002; feature_id=369062\nfeature_name=fc_user_recent_share_doc_type_1h_tob_profile_match; depend=user_recent_share_doc_type_1h,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4001; feature_id=369061\nfeature_name=fc_user_recent_share_doc_type_30d; depend=user_recent_share_doc_type_30d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4012; shared=true; feature_id=369072\nfeature_name=fc_user_recent_share_doc_type_30d_has_match; depend=fc_user_recent_share_doc_type_30d,f_doc_type; method=HasMatch; feature_version=2; slot=4014; feature_id=369074\nfeature_name=fc_user_recent_share_doc_type_30d_tob_profile_match; depend=user_recent_share_doc_type_30d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4013; feature_id=369073\nfeature_name=fc_user_recent_share_doc_type_6h; depend=user_recent_share_doc_type_6h; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4003; shared=true; feature_id=369063\nfeature_name=fc_user_recent_share_doc_type_6h_has_match; depend=fc_user_recent_share_doc_type_6h,f_doc_type; method=HasMatch; feature_version=2; slot=4005; feature_id=369065\nfeature_name=fc_user_recent_share_doc_type_6h_tob_profile_match; depend=user_recent_share_doc_type_6h,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4004; feature_id=369064\nfeature_name=fc_user_recent_share_doc_type_7d; depend=user_recent_share_doc_type_7d; method=TobInstanceProfilePairList; feature_version=2; args=10,2; slot=4009; shared=true; feature_id=369069\nfeature_name=fc_user_recent_share_doc_type_7d_has_match; depend=fc_user_recent_share_doc_type_7d,f_doc_type; method=HasMatch; feature_version=2; slot=4011; feature_id=369071\nfeature_name=fc_user_recent_share_doc_type_7d_tob_profile_match; depend=user_recent_share_doc_type_7d,f_doc_type; method=TobInstanceProfileMatch; feature_version=2; args=1,3; slot=4010; feature_id=369070\nfeature_name=fc_user_st_1d_doc_author_id_cart_cp; depend=user_st_1d_doc_author_id_cart_cp; method=VectorTopString; feature_version=2; args=10; slot=1359; shared=true; feature_id=368931\nfeature_name=fc_user_st_1d_doc_author_id_cart_recent; depend=user_st_1d_doc_author_id_cart_recent; method=VectorTopString; feature_version=2; args=30; slot=2098; shared=true; feature_id=369038\nfeature_name=fc_user_st_1d_doc_author_id_click_cp; depend=user_st_1d_doc_author_id_click_cp; method=VectorTopString; feature_version=2; args=10; slot=1220; shared=true; feature_id=368840\nfeature_name=fc_user_st_1d_doc_author_id_click_recent; depend=user_st_1d_doc_author_id_click_recent; method=VectorTopString; feature_version=2; args=30; slot=2020; shared=true; feature_id=368984\nfeature_name=fc_user_st_1d_doc_author_id_conversion_cp; depend=user_st_1d_doc_author_id_conversion_cp; method=VectorTopString; feature_version=2; args=10; slot=1383; shared=true; feature_id=368955\nfeature_name=fc_user_st_1d_doc_author_id_conversion_recent; depend=user_st_1d_doc_author_id_conversion_recent; method=VectorTopString; feature_version=2; args=30; slot=2114; shared=true; feature_id=369054\nfeature_name=fc_user_st_1d_doc_author_id_favorite_cp; depend=user_st_1d_doc_author_id_favorite_cp; method=VectorTopString; feature_version=2; args=10; slot=1221; shared=true; feature_id=368841\nfeature_name=fc_user_st_1d_doc_author_id_favorite_recent; depend=user_st_1d_doc_author_id_favorite_recent; method=VectorTopString; feature_version=2; args=30; slot=2021; shared=true; feature_id=368985\nfeature_name=fc_user_st_1d_doc_author_id_praise_cp; depend=user_st_1d_doc_author_id_praise_cp; method=VectorTopString; feature_version=2; args=10; slot=1222; shared=true; feature_id=368842\nfeature_name=fc_user_st_1d_doc_author_id_praise_recent; depend=user_st_1d_doc_author_id_praise_recent; method=VectorTopString; feature_version=2; args=30; slot=2022; shared=true; feature_id=368986\nfeature_name=fc_user_st_1d_doc_author_id_query_cp; depend=user_st_1d_doc_author_id_query_cp; method=VectorTopString; feature_version=2; args=10; slot=1223; shared=true; feature_id=368843\nfeature_name=fc_user_st_1d_doc_author_id_query_recent; depend=user_st_1d_doc_author_id_query_recent; method=VectorTopString; feature_version=2; args=30; slot=2023; shared=true; feature_id=368987\nfeature_name=fc_user_st_1d_doc_cate1_cart_cp; depend=user_st_1d_doc_cate1_cart_cp; method=VectorTopString; feature_version=2; args=10; slot=1350; shared=true; feature_id=368922\nfeature_name=fc_user_st_1d_doc_cate1_cart_recent; depend=user_st_1d_doc_cate1_cart_recent; method=VectorTopString; feature_version=2; args=30; slot=2092; shared=true; feature_id=369032\nfeature_name=fc_user_st_1d_doc_cate1_click_cp; depend=user_st_1d_doc_cate1_click_cp; method=VectorTopString; feature_version=2; args=10; slot=1208; shared=true; feature_id=368828\nfeature_name=fc_user_st_1d_doc_cate1_click_recent; depend=user_st_1d_doc_cate1_click_recent; method=VectorTopString; feature_version=2; args=30; slot=2008; shared=true; feature_id=368972\nfeature_name=fc_user_st_1d_doc_cate1_conversion_cp; depend=user_st_1d_doc_cate1_conversion_cp; method=VectorTopString; feature_version=2; args=10; slot=1374; shared=true; feature_id=368946\nfeature_name=fc_user_st_1d_doc_cate1_conversion_recent; depend=user_st_1d_doc_cate1_conversion_recent; method=VectorTopString; feature_version=2; args=30; slot=2108; shared=true; feature_id=369048\nfeature_name=fc_user_st_1d_doc_cate1_favorite_cp; depend=user_st_1d_doc_cate1_favorite_cp; method=VectorTopString; feature_version=2; args=10; slot=1209; shared=true; feature_id=368829\nfeature_name=fc_user_st_1d_doc_cate1_favorite_recent; depend=user_st_1d_doc_cate1_favorite_recent; method=VectorTopString; feature_version=2; args=30; slot=2009; shared=true; feature_id=368973\nfeature_name=fc_user_st_1d_doc_cate1_praise_cp; depend=user_st_1d_doc_cate1_praise_cp; method=VectorTopString; feature_version=2; args=10; slot=1210; shared=true; feature_id=368830\nfeature_name=fc_user_st_1d_doc_cate1_praise_recent; depend=user_st_1d_doc_cate1_praise_recent; method=VectorTopString; feature_version=2; args=30; slot=2010; shared=true; feature_id=368974\nfeature_name=fc_user_st_1d_doc_cate1_query_cp; depend=user_st_1d_doc_cate1_query_cp; method=VectorTopString; feature_version=2; args=10; slot=1211; shared=true; feature_id=368831\nfeature_name=fc_user_st_1d_doc_cate1_query_recent; depend=user_st_1d_doc_cate1_query_recent; method=VectorTopString; feature_version=2; args=30; slot=2011; shared=true; feature_id=368975\nfeature_name=fc_user_st_1d_doc_cate2_cart_cp; depend=user_st_1d_doc_cate2_cart_cp; method=VectorTopString; feature_version=2; args=10; slot=1353; shared=true; feature_id=368925\nfeature_name=fc_user_st_1d_doc_cate2_cart_recent; depend=user_st_1d_doc_cate2_cart_recent; method=VectorTopString; feature_version=2; args=30; slot=2094; shared=true; feature_id=369034\nfeature_name=fc_user_st_1d_doc_cate2_click_cp; depend=user_st_1d_doc_cate2_click_cp; method=VectorTopString; feature_version=2; args=10; slot=1212; shared=true; feature_id=368832\nfeature_name=fc_user_st_1d_doc_cate2_click_recent; depend=user_st_1d_doc_cate2_click_recent; method=VectorTopString; feature_version=2; args=30; slot=2012; shared=true; feature_id=368976\nfeature_name=fc_user_st_1d_doc_cate2_conversion_cp; depend=user_st_1d_doc_cate2_conversion_cp; method=VectorTopString; feature_version=2; args=10; slot=1377; shared=true; feature_id=368949\nfeature_name=fc_user_st_1d_doc_cate2_conversion_recent; depend=user_st_1d_doc_cate2_conversion_recent; method=VectorTopString; feature_version=2; args=30; slot=2110; shared=true; feature_id=369050\nfeature_name=fc_user_st_1d_doc_cate2_favorite_cp; depend=user_st_1d_doc_cate2_favorite_cp; method=VectorTopString; feature_version=2; args=10; slot=1213; shared=true; feature_id=368833\nfeature_name=fc_user_st_1d_doc_cate2_favorite_recent; depend=user_st_1d_doc_cate2_favorite_recent; method=VectorTopString; feature_version=2; args=30; slot=2013; shared=true; feature_id=368977\nfeature_name=fc_user_st_1d_doc_cate2_praise_cp; depend=user_st_1d_doc_cate2_praise_cp; method=VectorTopString; feature_version=2; args=10; slot=1214; shared=true; feature_id=368834\nfeature_name=fc_user_st_1d_doc_cate2_praise_recent; depend=user_st_1d_doc_cate2_praise_recent; method=VectorTopString; feature_version=2; args=30; slot=2014; shared=true; feature_id=368978\nfeature_name=fc_user_st_1d_doc_cate2_query_cp; depend=user_st_1d_doc_cate2_query_cp; method=VectorTopString; feature_version=2; args=10; slot=1215; shared=true; feature_id=368835\nfeature_name=fc_user_st_1d_doc_cate2_query_recent; depend=user_st_1d_doc_cate2_query_recent; method=VectorTopString; feature_version=2; args=30; slot=2015; shared=true; feature_id=368979\nfeature_name=fc_user_st_1d_doc_cate3_cart_cp; depend=user_st_1d_doc_cate3_cart_cp; method=VectorTopString; feature_version=2; args=10; slot=1356; shared=true; feature_id=368928\nfeature_name=fc_user_st_1d_doc_cate3_cart_recent; depend=user_st_1d_doc_cate3_cart_recent; method=VectorTopString; feature_version=2; args=30; slot=2096; shared=true; feature_id=369036\nfeature_name=fc_user_st_1d_doc_cate3_click_cp; depend=user_st_1d_doc_cate3_click_cp; method=VectorTopString; feature_version=2; args=10; slot=1216; shared=true; feature_id=368836\nfeature_name=fc_user_st_1d_doc_cate3_click_recent; depend=user_st_1d_doc_cate3_click_recent; method=VectorTopString; feature_version=2; args=30; slot=2016; shared=true; feature_id=368980\nfeature_name=fc_user_st_1d_doc_cate3_conversion_cp; depend=user_st_1d_doc_cate3_conversion_cp; method=VectorTopString; feature_version=2; args=10; slot=1380; shared=true; feature_id=368952\nfeature_name=fc_user_st_1d_doc_cate3_conversion_recent; depend=user_st_1d_doc_cate3_conversion_recent; method=VectorTopString; feature_version=2; args=30; slot=2112; shared=true; feature_id=369052\nfeature_name=fc_user_st_1d_doc_cate3_favorite_cp; depend=user_st_1d_doc_cate3_favorite_cp; method=VectorTopString; feature_version=2; args=10; slot=1217; shared=true; feature_id=368837\nfeature_name=fc_user_st_1d_doc_cate3_favorite_recent; depend=user_st_1d_doc_cate3_favorite_recent; method=VectorTopString; feature_version=2; args=30; slot=2017; shared=true; feature_id=368981\nfeature_name=fc_user_st_1d_doc_cate3_praise_cp; depend=user_st_1d_doc_cate3_praise_cp; method=VectorTopString; feature_version=2; args=10; slot=1218; shared=true; feature_id=368838\nfeature_name=fc_user_st_1d_doc_cate3_praise_recent; depend=user_st_1d_doc_cate3_praise_recent; method=VectorTopString; feature_version=2; args=30; slot=2018; shared=true; feature_id=368982\nfeature_name=fc_user_st_1d_doc_cate3_query_cp; depend=user_st_1d_doc_cate3_query_cp; method=VectorTopString; feature_version=2; args=10; slot=1219; shared=true; feature_id=368839\nfeature_name=fc_user_st_1d_doc_cate3_query_recent; depend=user_st_1d_doc_cate3_query_recent; method=VectorTopString; feature_version=2; args=30; slot=2019; shared=true; feature_id=368983\nfeature_name=fc_user_st_1d_doc_id_cart_cp; depend=user_st_1d_doc_id_cart_cp; method=VectorTopString; feature_version=2; args=10; slot=1344; shared=true; feature_id=368916\nfeature_name=fc_user_st_1d_doc_id_cart_recent; depend=user_st_1d_doc_id_cart_recent; method=VectorTopString; feature_version=2; args=30; slot=2088; shared=true; feature_id=369028\nfeature_name=fc_user_st_1d_doc_id_click_cp; depend=user_st_1d_doc_id_click_cp; method=VectorTopString; feature_version=2; args=10; slot=1200; shared=true; feature_id=368820\nfeature_name=fc_user_st_1d_doc_id_click_recent; depend=user_st_1d_doc_id_click_recent; method=VectorTopString; feature_version=2; args=30; slot=2000; shared=true; feature_id=368964\nfeature_name=fc_user_st_1d_doc_id_conversion_cp; depend=user_st_1d_doc_id_conversion_cp; method=VectorTopString; feature_version=2; args=10; slot=1368; shared=true; feature_id=368940\nfeature_name=fc_user_st_1d_doc_id_conversion_recent; depend=user_st_1d_doc_id_conversion_recent; method=VectorTopString; feature_version=2; args=30; slot=2104; shared=true; feature_id=369044\nfeature_name=fc_user_st_1d_doc_id_favorite_cp; depend=user_st_1d_doc_id_favorite_cp; method=VectorTopString; feature_version=2; args=10; slot=1201; shared=true; feature_id=368821\nfeature_name=fc_user_st_1d_doc_id_favorite_recent; depend=user_st_1d_doc_id_favorite_recent; method=VectorTopString; feature_version=2; args=30; slot=2001; shared=true; feature_id=368965\nfeature_name=fc_user_st_1d_doc_id_praise_cp; depend=user_st_1d_doc_id_praise_cp; method=VectorTopString; feature_version=2; args=10; slot=1202; shared=true; feature_id=368822\nfeature_name=fc_user_st_1d_doc_id_praise_recent; depend=user_st_1d_doc_id_praise_recent; method=VectorTopString; feature_version=2; args=30; slot=2002; shared=true; feature_id=368966\nfeature_name=fc_user_st_1d_doc_id_query_cp; depend=user_st_1d_doc_id_query_cp; method=VectorTopString; feature_version=2; args=10; slot=1203; shared=true; feature_id=368823\nfeature_name=fc_user_st_1d_doc_id_query_recent; depend=user_st_1d_doc_id_query_recent; method=VectorTopString; feature_version=2; args=30; slot=2003; shared=true; feature_id=368967\nfeature_name=fc_user_st_1d_doc_keyword_cart_cp; depend=user_st_1d_doc_keyword_cart_cp; method=VectorTopString; feature_version=2; args=10; slot=1365; shared=true; feature_id=368937\nfeature_name=fc_user_st_1d_doc_keyword_cart_recent; depend=user_st_1d_doc_keyword_cart_recent; method=VectorTopString; feature_version=2; args=30; slot=2102; shared=true; feature_id=369042\nfeature_name=fc_user_st_1d_doc_keyword_click_cp; depend=user_st_1d_doc_keyword_click_cp; method=VectorTopString; feature_version=2; args=10; slot=1228; shared=true; feature_id=368848\nfeature_name=fc_user_st_1d_doc_keyword_click_recent; depend=user_st_1d_doc_keyword_click_recent; method=VectorTopString; feature_version=2; args=30; slot=2028; shared=true; feature_id=368992\nfeature_name=fc_user_st_1d_doc_keyword_conversion_cp; depend=user_st_1d_doc_keyword_conversion_cp; method=VectorTopString; feature_version=2; args=10; slot=1389; shared=true; feature_id=368961\nfeature_name=fc_user_st_1d_doc_keyword_conversion_recent; depend=user_st_1d_doc_keyword_conversion_recent; method=VectorTopString; feature_version=2; args=30; slot=2118; shared=true; feature_id=369058\nfeature_name=fc_user_st_1d_doc_keyword_favorite_cp; depend=user_st_1d_doc_keyword_favorite_cp; method=VectorTopString; feature_version=2; args=10; slot=1229; shared=true; feature_id=368849\nfeature_name=fc_user_st_1d_doc_keyword_favorite_recent; depend=user_st_1d_doc_keyword_favorite_recent; method=VectorTopString; feature_version=2; args=30; slot=2029; shared=true; feature_id=368993\nfeature_name=fc_user_st_1d_doc_keyword_praise_cp; depend=user_st_1d_doc_keyword_praise_cp; method=VectorTopString; feature_version=2; args=10; slot=1230; shared=true; feature_id=368850\nfeature_name=fc_user_st_1d_doc_keyword_praise_recent; depend=user_st_1d_doc_keyword_praise_recent; method=VectorTopString; feature_version=2; args=30; slot=2030; shared=true; feature_id=368994\nfeature_name=fc_user_st_1d_doc_keyword_query_cp; depend=user_st_1d_doc_keyword_query_cp; method=VectorTopString; feature_version=2; args=10; slot=1231; shared=true; feature_id=368851\nfeature_name=fc_user_st_1d_doc_keyword_query_recent; depend=user_st_1d_doc_keyword_query_recent; method=VectorTopString; feature_version=2; args=30; slot=2031; shared=true; feature_id=368995\nfeature_name=fc_user_st_1d_doc_tags_cart_cp; depend=user_st_1d_doc_tags_cart_cp; method=VectorTopString; feature_version=2; args=10; slot=1362; shared=true; feature_id=368934\nfeature_name=fc_user_st_1d_doc_tags_cart_recent; depend=user_st_1d_doc_tags_cart_recent; method=VectorTopString; feature_version=2; args=30; slot=2100; shared=true; feature_id=369040\nfeature_name=fc_user_st_1d_doc_tags_click_cp; depend=user_st_1d_doc_tags_click_cp; method=VectorTopString; feature_version=2; args=10; slot=1224; shared=true; feature_id=368844\nfeature_name=fc_user_st_1d_doc_tags_click_recent; depend=user_st_1d_doc_tags_click_recent; method=VectorTopString; feature_version=2; args=30; slot=2024; shared=true; feature_id=368988\nfeature_name=fc_user_st_1d_doc_tags_conversion_cp; depend=user_st_1d_doc_tags_conversion_cp; method=VectorTopString; feature_version=2; args=10; slot=1386; shared=true; feature_id=368958\nfeature_name=fc_user_st_1d_doc_tags_conversion_recent; depend=user_st_1d_doc_tags_conversion_recent; method=VectorTopString; feature_version=2; args=30; slot=2116; shared=true; feature_id=369056\nfeature_name=fc_user_st_1d_doc_tags_favorite_cp; depend=user_st_1d_doc_tags_favorite_cp; method=VectorTopString; feature_version=2; args=10; slot=1225; shared=true; feature_id=368845\nfeature_name=fc_user_st_1d_doc_tags_favorite_recent; depend=user_st_1d_doc_tags_favorite_recent; method=VectorTopString; feature_version=2; args=30; slot=2025; shared=true; feature_id=368989\nfeature_name=fc_user_st_1d_doc_tags_praise_cp; depend=user_st_1d_doc_tags_praise_cp; method=VectorTopString; feature_version=2; args=10; slot=1226; shared=true; feature_id=368846\nfeature_name=fc_user_st_1d_doc_tags_praise_recent; depend=user_st_1d_doc_tags_praise_recent; method=VectorTopString; feature_version=2; args=30; slot=2026; shared=true; feature_id=368990\nfeature_name=fc_user_st_1d_doc_tags_query_cp; depend=user_st_1d_doc_tags_query_cp; method=VectorTopString; feature_version=2; args=10; slot=1227; shared=true; feature_id=368847\nfeature_name=fc_user_st_1d_doc_tags_query_recent; depend=user_st_1d_doc_tags_query_recent; method=VectorTopString; feature_version=2; args=30; slot=2027; shared=true; feature_id=368991\nfeature_name=fc_user_st_1d_doc_title_terms_cart_cp; depend=user_st_1d_doc_title_terms_cart_cp; method=VectorTopString; feature_version=2; args=10; slot=1347; shared=true; feature_id=368919\nfeature_name=fc_user_st_1d_doc_title_terms_cart_recent; depend=user_st_1d_doc_title_terms_cart_recent; method=VectorTopString; feature_version=2; args=30; slot=2090; shared=true; feature_id=369030\nfeature_name=fc_user_st_1d_doc_title_terms_click_cp; depend=user_st_1d_doc_title_terms_click_cp; method=VectorTopString; feature_version=2; args=10; slot=1204; shared=true; feature_id=368824\nfeature_name=fc_user_st_1d_doc_title_terms_click_recent; depend=user_st_1d_doc_title_terms_click_recent; method=VectorTopString; feature_version=2; args=30; slot=2004; shared=true; feature_id=368968\nfeature_name=fc_user_st_1d_doc_title_terms_conversion_cp; depend=user_st_1d_doc_title_terms_conversion_cp; method=VectorTopString; feature_version=2; args=10; slot=1371; shared=true; feature_id=368943\nfeature_name=fc_user_st_1d_doc_title_terms_conversion_recent; depend=user_st_1d_doc_title_terms_conversion_recent; method=VectorTopString; feature_version=2; args=30; slot=2106; shared=true; feature_id=369046\nfeature_name=fc_user_st_1d_doc_title_terms_favorite_cp; depend=user_st_1d_doc_title_terms_favorite_cp; method=VectorTopString; feature_version=2; args=10; slot=1205; shared=true; feature_id=368825\nfeature_name=fc_user_st_1d_doc_title_terms_favorite_recent; depend=user_st_1d_doc_title_terms_favorite_recent; method=VectorTopString; feature_version=2; args=30; slot=2005; shared=true; feature_id=368969\nfeature_name=fc_user_st_1d_doc_title_terms_praise_cp; depend=user_st_1d_doc_title_terms_praise_cp; method=VectorTopString; feature_version=2; args=10; slot=1206; shared=true; feature_id=368826\nfeature_name=fc_user_st_1d_doc_title_terms_praise_recent; depend=user_st_1d_doc_title_terms_praise_recent; method=VectorTopString; feature_version=2; args=30; slot=2006; shared=true; feature_id=368970\nfeature_name=fc_user_st_1d_doc_title_terms_query_cp; depend=user_st_1d_doc_title_terms_query_cp; method=VectorTopString; feature_version=2; args=10; slot=1207; shared=true; feature_id=368827\nfeature_name=fc_user_st_1d_doc_title_terms_query_recent; depend=user_st_1d_doc_title_terms_query_recent; method=VectorTopString; feature_version=2; args=30; slot=2007; shared=true; feature_id=368971\nfeature_name=fc_user_st_7d_doc_author_id_cart_cp; depend=user_st_7d_doc_author_id_cart_cp; method=VectorTopString; feature_version=2; args=30; slot=1360; shared=true; feature_id=368932\nfeature_name=fc_user_st_7d_doc_author_id_cart_recent; depend=user_st_7d_doc_author_id_cart_recent; method=VectorTopString; feature_version=2; args=50; slot=2099; shared=true; feature_id=369039\nfeature_name=fc_user_st_7d_doc_author_id_click_cp; depend=user_st_7d_doc_author_id_click_cp; method=VectorTopString; feature_version=2; args=30; slot=1276; shared=true; feature_id=368872\nfeature_name=fc_user_st_7d_doc_author_id_click_recent; depend=user_st_7d_doc_author_id_click_recent; method=VectorTopString; feature_version=2; args=50; slot=2076; shared=true; feature_id=369016\nfeature_name=fc_user_st_7d_doc_author_id_conversion_cp; depend=user_st_7d_doc_author_id_conversion_cp; method=VectorTopString; feature_version=2; args=30; slot=1384; shared=true; feature_id=368956\nfeature_name=fc_user_st_7d_doc_author_id_conversion_recent; depend=user_st_7d_doc_author_id_conversion_recent; method=VectorTopString; feature_version=2; args=50; slot=2115; shared=true; feature_id=369055\nfeature_name=fc_user_st_7d_doc_author_id_favorite_cp; depend=user_st_7d_doc_author_id_favorite_cp; method=VectorTopString; feature_version=2; args=30; slot=1277; shared=true; feature_id=368873\nfeature_name=fc_user_st_7d_doc_author_id_favorite_recent; depend=user_st_7d_doc_author_id_favorite_recent; method=VectorTopString; feature_version=2; args=50; slot=2077; shared=true; feature_id=369017\nfeature_name=fc_user_st_7d_doc_author_id_praise_cp; depend=user_st_7d_doc_author_id_praise_cp; method=VectorTopString; feature_version=2; args=30; slot=1278; shared=true; feature_id=368874\nfeature_name=fc_user_st_7d_doc_author_id_praise_recent; depend=user_st_7d_doc_author_id_praise_recent; method=VectorTopString; feature_version=2; args=50; slot=2078; shared=true; feature_id=369018\nfeature_name=fc_user_st_7d_doc_author_id_query_cp; depend=user_st_7d_doc_author_id_query_cp; method=VectorTopString; feature_version=2; args=30; slot=1279; shared=true; feature_id=368875\nfeature_name=fc_user_st_7d_doc_author_id_query_recent; depend=user_st_7d_doc_author_id_query_recent; method=VectorTopString; feature_version=2; args=50; slot=2079; shared=true; feature_id=369019\nfeature_name=fc_user_st_7d_doc_cate1_cart_cp; depend=user_st_7d_doc_cate1_cart_cp; method=VectorTopString; feature_version=2; args=30; slot=1351; shared=true; feature_id=368923\nfeature_name=fc_user_st_7d_doc_cate1_cart_recent; depend=user_st_7d_doc_cate1_cart_recent; method=VectorTopString; feature_version=2; args=50; slot=2093; shared=true; feature_id=369033\nfeature_name=fc_user_st_7d_doc_cate1_click_cp; depend=user_st_7d_doc_cate1_click_cp; method=VectorTopString; feature_version=2; args=30; slot=1264; shared=true; feature_id=368860\nfeature_name=fc_user_st_7d_doc_cate1_click_recent; depend=user_st_7d_doc_cate1_click_recent; method=VectorTopString; feature_version=2; args=50; slot=2064; shared=true; feature_id=369004\nfeature_name=fc_user_st_7d_doc_cate1_conversion_cp; depend=user_st_7d_doc_cate1_conversion_cp; method=VectorTopString; feature_version=2; args=30; slot=1375; shared=true; feature_id=368947\nfeature_name=fc_user_st_7d_doc_cate1_conversion_recent; depend=user_st_7d_doc_cate1_conversion_recent; method=VectorTopString; feature_version=2; args=50; slot=2109; shared=true; feature_id=369049\nfeature_name=fc_user_st_7d_doc_cate1_favorite_cp; depend=user_st_7d_doc_cate1_favorite_cp; method=VectorTopString; feature_version=2; args=30; slot=1265; shared=true; feature_id=368861\nfeature_name=fc_user_st_7d_doc_cate1_favorite_recent; depend=user_st_7d_doc_cate1_favorite_recent; method=VectorTopString; feature_version=2; args=50; slot=2065; shared=true; feature_id=369005\nfeature_name=fc_user_st_7d_doc_cate1_praise_cp; depend=user_st_7d_doc_cate1_praise_cp; method=VectorTopString; feature_version=2; args=30; slot=1266; shared=true; feature_id=368862\nfeature_name=fc_user_st_7d_doc_cate1_praise_recent; depend=user_st_7d_doc_cate1_praise_recent; method=VectorTopString; feature_version=2; args=50; slot=2066; shared=true; feature_id=369006\nfeature_name=fc_user_st_7d_doc_cate1_query_cp; depend=user_st_7d_doc_cate1_query_cp; method=VectorTopString; feature_version=2; args=30; slot=1267; shared=true; feature_id=368863\nfeature_name=fc_user_st_7d_doc_cate1_query_recent; depend=user_st_7d_doc_cate1_query_recent; method=VectorTopString; feature_version=2; args=50; slot=2067; shared=true; feature_id=369007\nfeature_name=fc_user_st_7d_doc_cate2_cart_cp; depend=user_st_7d_doc_cate2_cart_cp; method=VectorTopString; feature_version=2; args=30; slot=1354; shared=true; feature_id=368926\nfeature_name=fc_user_st_7d_doc_cate2_cart_recent; depend=user_st_7d_doc_cate2_cart_recent; method=VectorTopString; feature_version=2; args=50; slot=2095; shared=true; feature_id=369035\nfeature_name=fc_user_st_7d_doc_cate2_click_cp; depend=user_st_7d_doc_cate2_click_cp; method=VectorTopString; feature_version=2; args=30; slot=1268; shared=true; feature_id=368864\nfeature_name=fc_user_st_7d_doc_cate2_click_recent; depend=user_st_7d_doc_cate2_click_recent; method=VectorTopString; feature_version=2; args=50; slot=2068; shared=true; feature_id=369008\nfeature_name=fc_user_st_7d_doc_cate2_conversion_cp; depend=user_st_7d_doc_cate2_conversion_cp; method=VectorTopString; feature_version=2; args=30; slot=1378; shared=true; feature_id=368950\nfeature_name=fc_user_st_7d_doc_cate2_conversion_recent; depend=user_st_7d_doc_cate2_conversion_recent; method=VectorTopString; feature_version=2; args=50; slot=2111; shared=true; feature_id=369051\nfeature_name=fc_user_st_7d_doc_cate2_favorite_cp; depend=user_st_7d_doc_cate2_favorite_cp; method=VectorTopString; feature_version=2; args=30; slot=1269; shared=true; feature_id=368865\nfeature_name=fc_user_st_7d_doc_cate2_favorite_recent; depend=user_st_7d_doc_cate2_favorite_recent; method=VectorTopString; feature_version=2; args=50; slot=2069; shared=true; feature_id=369009\nfeature_name=fc_user_st_7d_doc_cate2_praise_cp; depend=user_st_7d_doc_cate2_praise_cp; method=VectorTopString; feature_version=2; args=30; slot=1270; shared=true; feature_id=368866\nfeature_name=fc_user_st_7d_doc_cate2_praise_recent; depend=user_st_7d_doc_cate2_praise_recent; method=VectorTopString; feature_version=2; args=50; slot=2070; shared=true; feature_id=369010\nfeature_name=fc_user_st_7d_doc_cate2_query_cp; depend=user_st_7d_doc_cate2_query_cp; method=VectorTopString; feature_version=2; args=30; slot=1271; shared=true; feature_id=368867\nfeature_name=fc_user_st_7d_doc_cate2_query_recent; depend=user_st_7d_doc_cate2_query_recent; method=VectorTopString; feature_version=2; args=50; slot=2071; shared=true; feature_id=369011\nfeature_name=fc_user_st_7d_doc_cate3_cart_cp; depend=user_st_7d_doc_cate3_cart_cp; method=VectorTopString; feature_version=2; args=30; slot=1357; shared=true; feature_id=368929\nfeature_name=fc_user_st_7d_doc_cate3_cart_recent; depend=user_st_7d_doc_cate3_cart_recent; method=VectorTopString; feature_version=2; args=50; slot=2097; shared=true; feature_id=369037\nfeature_name=fc_user_st_7d_doc_cate3_click_cp; depend=user_st_7d_doc_cate3_click_cp; method=VectorTopString; feature_version=2; args=30; slot=1272; shared=true; feature_id=368868\nfeature_name=fc_user_st_7d_doc_cate3_click_recent; depend=user_st_7d_doc_cate3_click_recent; method=VectorTopString; feature_version=2; args=50; slot=2072; shared=true; feature_id=369012\nfeature_name=fc_user_st_7d_doc_cate3_conversion_cp; depend=user_st_7d_doc_cate3_conversion_cp; method=VectorTopString; feature_version=2; args=30; slot=1381; shared=true; feature_id=368953\nfeature_name=fc_user_st_7d_doc_cate3_conversion_recent; depend=user_st_7d_doc_cate3_conversion_recent; method=VectorTopString; feature_version=2; args=50; slot=2113; shared=true; feature_id=369053\nfeature_name=fc_user_st_7d_doc_cate3_favorite_cp; depend=user_st_7d_doc_cate3_favorite_cp; method=VectorTopString; feature_version=2; args=30; slot=1273; shared=true; feature_id=368869\nfeature_name=fc_user_st_7d_doc_cate3_favorite_recent; depend=user_st_7d_doc_cate3_favorite_recent; method=VectorTopString; feature_version=2; args=50; slot=2073; shared=true; feature_id=369013\nfeature_name=fc_user_st_7d_doc_cate3_praise_cp; depend=user_st_7d_doc_cate3_praise_cp; method=VectorTopString; feature_version=2; args=30; slot=1274; shared=true; feature_id=368870\nfeature_name=fc_user_st_7d_doc_cate3_praise_recent; depend=user_st_7d_doc_cate3_praise_recent; method=VectorTopString; feature_version=2; args=50; slot=2074; shared=true; feature_id=369014\nfeature_name=fc_user_st_7d_doc_cate3_query_cp; depend=user_st_7d_doc_cate3_query_cp; method=VectorTopString; feature_version=2; args=30; slot=1275; shared=true; feature_id=368871\nfeature_name=fc_user_st_7d_doc_cate3_query_recent; depend=user_st_7d_doc_cate3_query_recent; method=VectorTopString; feature_version=2; args=50; slot=2075; shared=true; feature_id=369015\nfeature_name=fc_user_st_7d_doc_id_cart_cp; depend=user_st_7d_doc_id_cart_cp; method=VectorTopString; feature_version=2; args=30; slot=1345; shared=true; feature_id=368917\nfeature_name=fc_user_st_7d_doc_id_cart_recent; depend=user_st_7d_doc_id_cart_recent; method=VectorTopString; feature_version=2; args=50; slot=2089; shared=true; feature_id=369029\nfeature_name=fc_user_st_7d_doc_id_click_cp; depend=user_st_7d_doc_id_click_cp; method=VectorTopString; feature_version=2; args=30; slot=1256; shared=true; feature_id=368852\nfeature_name=fc_user_st_7d_doc_id_click_recent; depend=user_st_7d_doc_id_click_recent; method=VectorTopString; feature_version=2; args=50; slot=2056; shared=true; feature_id=368996\nfeature_name=fc_user_st_7d_doc_id_conversion_cp; depend=user_st_7d_doc_id_conversion_cp; method=VectorTopString; feature_version=2; args=30; slot=1369; shared=true; feature_id=368941\nfeature_name=fc_user_st_7d_doc_id_conversion_recent; depend=user_st_7d_doc_id_conversion_recent; method=VectorTopString; feature_version=2; args=50; slot=2105; shared=true; feature_id=369045\nfeature_name=fc_user_st_7d_doc_id_favorite_cp; depend=user_st_7d_doc_id_favorite_cp; method=VectorTopString; feature_version=2; args=30; slot=1257; shared=true; feature_id=368853\nfeature_name=fc_user_st_7d_doc_id_favorite_recent; depend=user_st_7d_doc_id_favorite_recent; method=VectorTopString; feature_version=2; args=50; slot=2057; shared=true; feature_id=368997\nfeature_name=fc_user_st_7d_doc_id_praise_cp; depend=user_st_7d_doc_id_praise_cp; method=VectorTopString; feature_version=2; args=30; slot=1258; shared=true; feature_id=368854\nfeature_name=fc_user_st_7d_doc_id_praise_recent; depend=user_st_7d_doc_id_praise_recent; method=VectorTopString; feature_version=2; args=50; slot=2058; shared=true; feature_id=368998\nfeature_name=fc_user_st_7d_doc_id_query_cp; depend=user_st_7d_doc_id_query_cp; method=VectorTopString; feature_version=2; args=30; slot=1259; shared=true; feature_id=368855\nfeature_name=fc_user_st_7d_doc_id_query_recent; depend=user_st_7d_doc_id_query_recent; method=VectorTopString; feature_version=2; args=50; slot=2059; shared=true; feature_id=368999\nfeature_name=fc_user_st_7d_doc_keyword_cart_cp; depend=user_st_7d_doc_keyword_cart_cp; method=VectorTopString; feature_version=2; args=30; slot=1366; shared=true; feature_id=368938\nfeature_name=fc_user_st_7d_doc_keyword_cart_recent; depend=user_st_7d_doc_keyword_cart_recent; method=VectorTopString; feature_version=2; args=50; slot=2103; shared=true; feature_id=369043\nfeature_name=fc_user_st_7d_doc_keyword_click_cp; depend=user_st_7d_doc_keyword_click_cp; method=VectorTopString; feature_version=2; args=30; slot=1284; shared=true; feature_id=368880\nfeature_name=fc_user_st_7d_doc_keyword_click_recent; depend=user_st_7d_doc_keyword_click_recent; method=VectorTopString; feature_version=2; args=50; slot=2084; shared=true; feature_id=369024\nfeature_name=fc_user_st_7d_doc_keyword_conversion_cp; depend=user_st_7d_doc_keyword_conversion_cp; method=VectorTopString; feature_version=2; args=30; slot=1390; shared=true; feature_id=368962\nfeature_name=fc_user_st_7d_doc_keyword_conversion_recent; depend=user_st_7d_doc_keyword_conversion_recent; method=VectorTopString; feature_version=2; args=50; slot=2119; shared=true; feature_id=369059\nfeature_name=fc_user_st_7d_doc_keyword_favorite_cp; depend=user_st_7d_doc_keyword_favorite_cp; method=VectorTopString; feature_version=2; args=30; slot=1285; shared=true; feature_id=368881\nfeature_name=fc_user_st_7d_doc_keyword_favorite_recent; depend=user_st_7d_doc_keyword_favorite_recent; method=VectorTopString; feature_version=2; args=50; slot=2085; shared=true; feature_id=369025\nfeature_name=fc_user_st_7d_doc_keyword_praise_cp; depend=user_st_7d_doc_keyword_praise_cp; method=VectorTopString; feature_version=2; args=30; slot=1286; shared=true; feature_id=368882\nfeature_name=fc_user_st_7d_doc_keyword_praise_recent; depend=user_st_7d_doc_keyword_praise_recent; method=VectorTopString; feature_version=2; args=50; slot=2086; shared=true; feature_id=369026\nfeature_name=fc_user_st_7d_doc_keyword_query_cp; depend=user_st_7d_doc_keyword_query_cp; method=VectorTopString; feature_version=2; args=30; slot=1287; shared=true; feature_id=368883\nfeature_name=fc_user_st_7d_doc_keyword_query_recent; depend=user_st_7d_doc_keyword_query_recent; method=VectorTopString; feature_version=2; args=50; slot=2087; shared=true; feature_id=369027\nfeature_name=fc_user_st_7d_doc_tags_cart_cp; depend=user_st_7d_doc_tags_cart_cp; method=VectorTopString; feature_version=2; args=30; slot=1363; shared=true; feature_id=368935\nfeature_name=fc_user_st_7d_doc_tags_cart_recent; depend=user_st_7d_doc_tags_cart_recent; method=VectorTopString; feature_version=2; args=50; slot=2101; shared=true; feature_id=369041\nfeature_name=fc_user_st_7d_doc_tags_click_cp; depend=user_st_7d_doc_tags_click_cp; method=VectorTopString; feature_version=2; args=30; slot=1280; shared=true; feature_id=368876\nfeature_name=fc_user_st_7d_doc_tags_click_recent; depend=user_st_7d_doc_tags_click_recent; method=VectorTopString; feature_version=2; args=50; slot=2080; shared=true; feature_id=369020\nfeature_name=fc_user_st_7d_doc_tags_conversion_cp; depend=user_st_7d_doc_tags_conversion_cp; method=VectorTopString; feature_version=2; args=30; slot=1387; shared=true; feature_id=368959\nfeature_name=fc_user_st_7d_doc_tags_conversion_recent; depend=user_st_7d_doc_tags_conversion_recent; method=VectorTopString; feature_version=2; args=50; slot=2117; shared=true; feature_id=369057\nfeature_name=fc_user_st_7d_doc_tags_favorite_cp; depend=user_st_7d_doc_tags_favorite_cp; method=VectorTopString; feature_version=2; args=30; slot=1281; shared=true; feature_id=368877\nfeature_name=fc_user_st_7d_doc_tags_favorite_recent; depend=user_st_7d_doc_tags_favorite_recent; method=VectorTopString; feature_version=2; args=50; slot=2081; shared=true; feature_id=369021\nfeature_name=fc_user_st_7d_doc_tags_praise_cp; depend=user_st_7d_doc_tags_praise_cp; method=VectorTopString; feature_version=2; args=30; slot=1282; shared=true; feature_id=368878\nfeature_name=fc_user_st_7d_doc_tags_praise_recent; depend=user_st_7d_doc_tags_praise_recent; method=VectorTopString; feature_version=2; args=50; slot=2082; shared=true; feature_id=369022\nfeature_name=fc_user_st_7d_doc_tags_query_cp; depend=user_st_7d_doc_tags_query_cp; method=VectorTopString; feature_version=2; args=30; slot=1283; shared=true; feature_id=368879\nfeature_name=fc_user_st_7d_doc_tags_query_recent; depend=user_st_7d_doc_tags_query_recent; method=VectorTopString; feature_version=2; args=50; slot=2083; shared=true; feature_id=369023\nfeature_name=fc_user_st_7d_doc_title_terms_cart_cp; depend=user_st_7d_doc_title_terms_cart_cp; method=VectorTopString; feature_version=2; args=30; slot=1348; shared=true; feature_id=368920\nfeature_name=fc_user_st_7d_doc_title_terms_cart_recent; depend=user_st_7d_doc_title_terms_cart_recent; method=VectorTopString; feature_version=2; args=50; slot=2091; shared=true; feature_id=369031\nfeature_name=fc_user_st_7d_doc_title_terms_click_cp; depend=user_st_7d_doc_title_terms_click_cp; method=VectorTopString; feature_version=2; args=30; slot=1260; shared=true; feature_id=368856\nfeature_name=fc_user_st_7d_doc_title_terms_click_recent; depend=user_st_7d_doc_title_terms_click_recent; method=VectorTopString; feature_version=2; args=50; slot=2060; shared=true; feature_id=369000\nfeature_name=fc_user_st_7d_doc_title_terms_conversion_cp; depend=user_st_7d_doc_title_terms_conversion_cp; method=VectorTopString; feature_version=2; args=30; slot=1372; shared=true; feature_id=368944\nfeature_name=fc_user_st_7d_doc_title_terms_conversion_recent; depend=user_st_7d_doc_title_terms_conversion_recent; method=VectorTopString; feature_version=2; args=50; slot=2107; shared=true; feature_id=369047\nfeature_name=fc_user_st_7d_doc_title_terms_favorite_cp; depend=user_st_7d_doc_title_terms_favorite_cp; method=VectorTopString; feature_version=2; args=30; slot=1261; shared=true; feature_id=368857\nfeature_name=fc_user_st_7d_doc_title_terms_favorite_recent; depend=user_st_7d_doc_title_terms_favorite_recent; method=VectorTopString; feature_version=2; args=50; slot=2061; shared=true; feature_id=369001\nfeature_name=fc_user_st_7d_doc_title_terms_praise_cp; depend=user_st_7d_doc_title_terms_praise_cp; method=VectorTopString; feature_version=2; args=30; slot=1262; shared=true; feature_id=368858\nfeature_name=fc_user_st_7d_doc_title_terms_praise_recent; depend=user_st_7d_doc_title_terms_praise_recent; method=VectorTopString; feature_version=2; args=50; slot=2062; shared=true; feature_id=369002\nfeature_name=fc_user_st_7d_doc_title_terms_query_cp; depend=user_st_7d_doc_title_terms_query_cp; method=VectorTopString; feature_version=2; args=30; slot=1263; shared=true; feature_id=368859\nfeature_name=fc_user_st_7d_doc_title_terms_query_recent; depend=user_st_7d_doc_title_terms_query_recent; method=VectorTopString; feature_version=2; args=50; slot=2063; shared=true; feature_id=369003\n"
  },
  {
    "path": "monolith/native_training/data/tf_example_to_example_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport math\n\nfrom absl import app, logging\nimport tensorflow as tf\nimport numpy as np\n\nfrom monolith.native_training.data.feature_utils import tf_example_to_example\nfrom monolith.native_training.data.parsers import parse_examples\n\n# The following functions can be used to convert a value to a type compatible\n# with tf.train.Example.\n\n\ndef _bytes_feature(value):\n  \"\"\"Returns a bytes_list from a string / byte.\"\"\"\n  if isinstance(value, type(tf.constant(0))):\n    value = value.numpy(\n    )  # BytesList won't unpack a string from an EagerTensor.\n  return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))\n\n\ndef _float_feature(value):\n  \"\"\"Returns a float_list from a float / double.\"\"\"\n  return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))\n\n\ndef _int64_feature(value):\n  \"\"\"Returns an int64_list from a bool / enum / int / uint.\"\"\"\n  return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))\n\n\n# @tf.py_function(Tout=tf.string)\ndef serialize_example(feature0, feature1, feature2, feature3, feature4):\n  \"\"\"\n  Creates a tf.train.Example message ready to be written to a file.\n  \"\"\"\n  # Create a dictionary mapping the feature name to the tf.train.Example-compatible\n  # data type.\n  feature = {\n      'feature0': _int64_feature(feature0),\n      'feature1': _int64_feature(feature1),\n      'feature2': _bytes_feature(feature2),\n      'feature3': _float_feature(feature3),\n      'feature4': _float_feature(feature4),\n  }\n\n  # Create a Features message using tf.train.Example.\n\n  example_proto = tf.train.Example(features=tf.train.Features(feature=feature))\n  return example_proto.SerializeToString()\n\n\ndef get_fid_v2(slot: int, signature: int):\n  fid_v2_mask = (1 << 48) - 1\n  return (slot << 48) | (signature & fid_v2_mask)\n\n\ndef calc_hash_value(val: float):\n  return int(math.log2(abs(val) + 1))\n\n\nclass TFExampleToExampleTest(tf.test.TestCase):\n\n  def test_tf_example_to_example(self):\n    tf.compat.v1.disable_v2_behavior()\n    logging.set_verbosity(logging.INFO)\n\n    # The number of observations in the dataset.\n    n_observations = int(1e4)\n\n    # Boolean feature, encoded as False or True.\n    feature0 = np.random.choice([False, True], n_observations)\n\n    # Integer feature, random from 0 to 4.\n    feature1 = np.random.randint(0, 5, n_observations)\n\n    # String feature.\n    strings = np.array([b'cat', b'dog', b'chicken', b'horse', b'goat'])\n    feature2 = strings[feature1]\n\n    # Float feature, from a standard normal distribution.\n    feature3 = np.random.randn(n_observations)\n\n    feature4 = np.random.randn(n_observations)\n\n    filename = '/tmp/test.tfrecord'\n    # Write the `tf.train.Example` observations to the file.\n    with tf.io.TFRecordWriter(filename) as writer:\n      for i in range(n_observations):\n        example = serialize_example(feature0[i], feature1[i], feature2[i],\n                                    feature3[i], feature4[i])\n        writer.write(example)\n\n    filenames = [filename]\n    dataset = tf.data.TFRecordDataset(filenames)\n\n    # logging.info(dataset)\n    # for raw_record in dataset.take(1):\n    #   example = tf.train.Example()\n    #   example.ParseFromString(raw_record.numpy())\n    #   print(example)\n\n    def map_fn(tensor: tf.Tensor):\n      return tf_example_to_example(tensor,\n                                   sparse_features={\n                                       \"feature0\": 1,\n                                       \"feature1\": 2,\n                                       \"feature4\": 3\n                                   },\n                                   dense_features=[\"feature2\"],\n                                   label=\"feature3\",\n                                   instance_weight=None)\n\n    def parse_fn(variant: tf.Tensor):\n      return parse_examples(\n          variant,\n          sparse_features=[\"not_existed1\", \"feature0\", \"feature1\", \"feature4\"],\n          dense_features=[\n              \"label\", \"feature2\", \"feature3\", \"not_existed2\", \"instance_weight\"\n          ],\n          dense_feature_types=[\n              tf.float32, tf.string, tf.float32, tf.float32, tf.float32\n          ],\n          dense_feature_shapes=[1, 1, 1, 1, 1],\n      )\n\n    batch_size = 2\n    dataset = dataset.map(map_fn)\n    dataset = dataset.batch(batch_size)\n    dataset = dataset.map(parse_fn)\n    session_config = tf.compat.v1.ConfigProto(allow_soft_placement=True)\n    session_config.graph_options.rewrite_options.disable_meta_optimizer = True\n    with tf.compat.v1.Session(config=session_config) as sess:\n      it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n      element = it.get_next()\n      element[\"label\"] = tf.reshape(element['label'], shape=(-1,))\n      element[\"feature2\"] = tf.reshape(element['feature2'], shape=(-1,))\n      element[\"feature3\"] = tf.reshape(element['feature3'], shape=(-1,))\n      element[\"not_existed2\"] = tf.reshape(element['not_existed2'], shape=(-1,))\n      element[\"instance_weight\"] = tf.reshape(element['instance_weight'],\n                                              shape=(-1,))\n      for i in range(n_observations // batch_size):\n        features = sess.run(fetches=element)\n        self.assertAllEqual(features['not_existed1'].values.shape, (0,))\n        feature0_fids = [\n            get_fid_v2(1, val)\n            for val in feature0[i * batch_size:(i + 1) * batch_size]\n        ]\n        feature1_fids = [\n            get_fid_v2(2, val)\n            for val in feature1[i * batch_size:(i + 1) * batch_size]\n        ]\n        feature4_fids = [\n            get_fid_v2(3, calc_hash_value(val))\n            for val in feature4[i * batch_size:(i + 1) * batch_size]\n        ]\n        self.assertAllEqual(features['feature0'].values, feature0_fids)\n        self.assertAllEqual(features['feature1'].values, feature1_fids)\n        self.assertAllEqual(features['feature4'].values, feature4_fids)\n        self.assertAllClose(features['label'],\n                            feature3[i * batch_size:(i + 1) * batch_size])\n        self.assertAllClose(features['feature3'], [0, 0])\n        self.assertAllClose(features['not_existed2'], [0, 0])\n        self.assertAllClose(features['instance_weight'], [1.0, 1.0])\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_test\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_cc_binary\", \"tf_cc_test\", \"tf_custom_op_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\n\ncc_library(\n    name = \"fid\",\n    hdrs = [\"cc/fid.h\"],\n    deps = [],\n)\n\ncc_test(\n    name = \"fid_test\",\n    srcs = [\"cc/fid_test.cc\"],\n    deps = [\n        \":fid\",\n        \":reader_util\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"reader_util\",\n    srcs = [\"cc/reader_util.cc\"],\n    hdrs = [\"cc/reader_util.h\"],\n    deps = [\n        \"//third_party/nlohmann:json\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/container:flat_hash_set\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_absl//absl/synchronization\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:ops_util_hdrs\",\n        \"@org_tensorflow//tensorflow/core/platform:logging\",\n    ],\n)\n\ncc_test(\n    name = \"reader_util_test\",\n    srcs = [\"cc/reader_util_test.cc\"],\n    deps = [\n        \":reader_util\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"cached_mem_pool\",\n    srcs = [\"cc/cached_mem_pool.cc\"],\n    hdrs = [\"cc/cached_mem_pool.h\"],\n    deps = [\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_test(\n    name = \"cached_mem_pool_test\",\n    srcs = [\"cc/cached_mem_pool_test.cc\"],\n    deps = [\n        \":cached_mem_pool\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"snappy_inputbuffer\",\n    srcs = [\n        \"cc/snappy_inputbuffer.cc\",\n    ],\n    hdrs = [\n        \"cc/snappy_inputbuffer.h\",\n    ],\n    deps = [\n        \":cached_mem_pool\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n)\n\ncc_library(\n    name=\"zstd_inputbuffer\",\n    srcs=[\n        \"cc/zstd_inputbuffer.cc\",\n    ],\n    hdrs=[\n        \"cc/zstd_inputbuffer.h\",\n    ],\n    deps=[\n        \":cached_mem_pool\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@zstd\",\n    ],\n)\n\ncc_library(\n    name = \"ue_compress\",\n    srcs = [\"cc/ue_compress.cc\"],\n    hdrs = [\"cc/ue_compress.h\"],\n    deps = [\n        \"//idl:compression_qtz8mm\",\n        \"//idl:proto_parser_cc_proto\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_test(\n    name = \"ue_compress_test\",\n    srcs = [\"cc/ue_compress_test.cc\"],\n    deps = [\n        \":ue_compress\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"data_format_options\",\n    hdrs = [\"cc/data_format_options.h\"],\n)\n\ncc_library(\n    name=\"data_reader\",\n    srcs=[\n        \"cc/data_reader.cc\",\n        \"cc/data_reader.h\",\n        \"cc/pb_variant.cc\",\n        \"cc/pb_variant.h\",\n    ],\n    deps=[\n        \":data_format_options\",\n        \":reader_util\",\n        \":snappy_inputbuffer\",\n        \":zstd_inputbuffer\",\n        \":ue_compress\",\n        \"//idl:example_cc_proto\",\n        \"//idl:proto_parser_cc_proto\",\n        \"//monolith/native_training/runtime/ops:traceme\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n)\n\ncc_library(\n    name = \"data_writer\",\n    srcs = [\n        \"cc/data_writer.cc\",\n    ],\n    hdrs = [\n        \"cc/data_writer.h\",\n    ],\n    deps = [\n        \":data_format_options\",\n        \"@com_google_absl//absl/strings\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n)\n\ntf_cc_test(\n    name = \"data_read_write_test\",\n    srcs = [\n        \"cc/data_read_write_test.cc\",\n    ],\n    deps = [\n        \":data_reader\",\n        \":data_writer\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"parse_instance_lib\",\n    srcs = [\"cc/parse_instance_lib.cc\"],\n    hdrs = [\"cc/parse_instance_lib.h\"],\n    deps = [\n        \":data_reader\",\n        \":reader_util\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/container:flat_hash_set\",\n        \"@com_google_absl//absl/types:span\",\n        \"@com_google_glog//:glog\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:ops_util_hdrs\",\n    ],\n)\n\ncc_library(\n    name = \"instance_utils\",\n    srcs = [\"cc/instance_utils.cc\"],\n    hdrs = [\"cc/instance_utils.h\"],\n    deps = [\n        \":reader_util\",\n        \"//idl:example_cc_proto\",\n        \"//idl:proto_parser_cc_proto\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"instance_utils_test\",\n    srcs = [\"cc/instance_utils_test.cc\"],\n    deps = [\n        \":instance_utils\",\n        \"@com_google_absl//absl/time\",\n        \"@com_google_glog//:glog\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ntf_cc_binary(\n    name = \"instance_processor\",\n    srcs = [\n        \"cc/instance_processor.cc\",\n    ],\n    copts = [\"-fexceptions\"],\n    deps = [\n        \":data_reader\",\n        \":instance_utils\",\n        \"//third_party/nlohmann:json\",\n        \"@org_tensorflow//tensorflow/core:tensorflow\",\n    ],\n)\n\ntf_cc_binary(\n    name = \"instance_reader\",\n    srcs = [\n        \"cc/instance_reader.cc\",\n    ],\n    copts = [\"-fexceptions\"],\n    deps = [\n        \":data_reader\",\n        \":fid\",\n        \":instance_utils\",\n        \"//monolith/native_training/data/transform:transforms\",\n        \"//third_party/cli11:cli11\",\n        \"//third_party/nlohmann:json\",\n        \"@org_tensorflow//tensorflow/core:tensorflow\",\n    ],\n)\n\ncc_library(\n    name = \"pb_datasource_lib\",\n    srcs = [\n        \"cc/instance_dataset_kernel.cc\",\n        \"cc/parse_instance_kernel.cc\",\n    ],\n    deps = [\n        \":data_reader\",\n        \":instance_utils\",\n        \":parse_instance_lib\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n    ],\n)\n\ncc_library(\n    name = \"pb_datasource_ops\",\n    srcs = [\n        \"cc/instance_dataset_ops.cc\",\n        \"cc/parse_instance_ops.cc\",\n    ],\n    copts = [\"-DNDEBUG\"],\n    deps = [\n        \":pb_datasource_lib\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n    alwayslink = 1,\n)\n\npy_library(\n    name = \"instance_dataset_ops_py\",\n    srcs = [\n        \"python/instance_dataset_op.py\",\n    ],\n    deps = [\n        \"//monolith:utils\",\n        \"//monolith/native_training:runner_utils\",\n        \"//monolith/native_training/distribute:distributed_dataset\",\n        \"//monolith/native_training/data:datasets_py\",\n        \"//monolith/native_training/hooks:ckpt_hooks\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_test(\n    name = \"parse_instance_ops_py_test\",\n    srcs = [\n        \"python/parse_instance_ops_test.py\",\n    ],\n    main = \"python/parse_instance_ops_test.py\",\n    deps = [\n        \":instance_dataset_ops_py\",\n        \":parse_instance_ops_py\",\n        \"//idl:proto_parser_py_proto\",\n    ],\n)\n\npy_binary(\n    name = \"instance_dataset_op_py_test_stdin\",\n    srcs = [\n        \"python/instance_dataset_op_test_stdin.py\",\n    ],\n    main = \"python/instance_dataset_op_test_stdin.py\",\n    deps = [\n        \":instance_dataset_ops_py\",\n        \":parse_instance_ops_py\",\n        \"//idl:proto_parser_py_proto\",\n    ],\n)\n\n\npy_library(\n    name = \"parser_utils\",\n    srcs = [\n        \"python/parser_utils.py\",\n    ],\n    deps = [\n        \"//monolith/native_training:ragged_utils\",\n    ],\n)\n\npy_library(\n    name = \"parse_instance_ops_py\",\n    srcs = [\n        \"python/parse_instance_ops.py\",\n    ],\n    deps = [\n        \":parser_utils\",\n        \"//idl:proto_parser_py_proto\",\n        \"//monolith:utils\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\nexports_files([\n    \"cc/parse_instance_kernel.cc\",\n    \"cc/parse_instance_ops.cc\",\n])\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/cached_mem_pool.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"cached_mem_pool.h\"\n\n#include \"glog/logging.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nstd::mutex session_mutex;\nCachedMemPool* CachedMemPool::cached_mem_pool = nullptr;\n\nCachedMemPool* CachedMemPool::init(size_t buffer_size) {\n  std::unique_lock<std::mutex> lock(session_mutex);\n  if (cached_mem_pool == nullptr) {\n    cached_mem_pool = new CachedMemPool(buffer_size);\n  }\n  return cached_mem_pool;\n}\n\nstd::unique_ptr<char[]> CachedMemPool::allocate() {\n  std::unique_lock<std::mutex> lock(alloc_mtx_);\n  if (cached_buffers_.empty()) {\n    total_requested_++;\n    return std::make_unique<char[]>(buffer_size_);\n  } else {\n    auto buffer = std::move(cached_buffers_.back());\n    cached_buffers_.pop_back();\n    return buffer;\n  }\n}\n\nvoid CachedMemPool::deallocate(std::unique_ptr<char[]>& buffer) {\n  std::unique_lock<std::mutex> lock(alloc_mtx_);\n  cached_buffers_.emplace_back(std::move(buffer));\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/cached_mem_pool.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_CACHED_MEM_POOL_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_CACHED_MEM_POOL_H_\n\n#include <memory>\n#include <mutex>\n#include <vector>\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass CachedMemPool {\n public:\n  static CachedMemPool* init(size_t buffer_size);\n\n  std::unique_ptr<char[]> allocate();\n  void deallocate(std::unique_ptr<char[]>& buffer);\n\n  // Test Only method.\n  size_t get_buffer_size() {\n    std::unique_lock<std::mutex> lock(alloc_mtx_);\n    return cached_buffers_.size();\n  }\n\n private:\n  explicit CachedMemPool(size_t buffer_size) : buffer_size_(buffer_size) {}\n  ~CachedMemPool() { cached_buffers_.clear(); }\n\n  size_t buffer_size_;\n\n  std::mutex alloc_mtx_;\n  size_t total_requested_ = 0;\n  std::vector<std::unique_ptr<char[]>> cached_buffers_;\n  static CachedMemPool* cached_mem_pool;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_CACHED_MEM_POOL_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/cached_mem_pool_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"cached_mem_pool.h\"\n\n#include <memory>\n#include <thread>\n\n#include \"gtest/gtest.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nTEST(CachedMemPoolTest, Basic) {\n  CachedMemPool* mem_pool = CachedMemPool::init(1024 * 1024);\n  auto buffer = mem_pool->allocate();\n  EXPECT_EQ(mem_pool->get_buffer_size(), 0);\n  mem_pool->deallocate(buffer);\n  EXPECT_EQ(mem_pool->get_buffer_size(), 1);\n}\n\nTEST(CachedMemPoolTest, RecursiveAllocation) {\n  CachedMemPool* mem_pool = CachedMemPool::init(1024 * 1024);\n  std::vector<std::unique_ptr<char[]>> buffers;\n  for (int i = 0; i < 30; i++) {\n    auto buffer = mem_pool->allocate();\n    buffers.emplace_back(std::move(buffer));\n  }\n  EXPECT_EQ(mem_pool->get_buffer_size(), 0);\n  for (auto& buffer : buffers) {\n    mem_pool->deallocate(buffer);\n  }\n  EXPECT_EQ(mem_pool->get_buffer_size(), 30);\n  for (int i = 0; i < 30; i++) {\n    auto buffer = mem_pool->allocate();\n    buffers.emplace_back(std::move(buffer));\n  }\n  EXPECT_EQ(mem_pool->get_buffer_size(), 0);\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/data_format_options.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_DATA_FORMAT_OPTIONS_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_DATA_FORMAT_OPTIONS_H_\n\n#include <iostream>\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nstruct DataFormatOptions {\n  bool lagrangex_header = false;\n  bool kafka_dump_prefix = false;\n  bool has_sort_id = false;\n  bool kafka_dump = false;\n};\n\ninline std::ostream& operator<<(std::ostream& os,\n                                const DataFormatOptions& opts) {\n  return os << \"lagrangex_header: \" << opts.lagrangex_header\n            << \" kafka_dump_prefix: \" << opts.kafka_dump_prefix\n            << \" has_sort_id: \" << opts.has_sort_id\n            << \" kafka_dump: \" << opts.kafka_dump;\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_DATA_FORMAT_OPTIONS_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/data_read_write_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/data/training_instance/cc/data_reader.h\"\n#include \"monolith/native_training/data/training_instance/cc/data_writer.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nclass ReadWriteTest : public ::testing::TestWithParam<DataFormatOptions> {};\n\nStatus ReadBytes(BaseStreamReaderTmpl<absl::string_view>* reader,\n                 absl::string_view* out) {\n  uint8_t pb_type;\n  uint32_t data_source_key;\n  return reader->ReadPBBytes(&pb_type, &data_source_key, out);\n}\n\nTEST_P(ReadWriteTest, Basic) {\n  DataFormatOptions options = GetParam();\n  std::string s;\n  StringStreamWriter writer(options, &s);\n  for (int i = 0; i < 16; ++i) {\n    EXPECT_TRUE(writer.WriteRecord(std::string(i, 'a')).ok());\n  }\n  ZeroCopyStringViewStreamReader reader(options, s);\n  absl::string_view out;\n  for (int i = 0; i < 16; ++i) {\n    auto status = ReadBytes(&reader, &out);\n    EXPECT_TRUE(status.ok()) << status;\n    EXPECT_THAT(out, std::string(i, 'a')) << i;\n  }\n}\n\nstd::vector<DataFormatOptions> GenerateOptions() {\n  std::vector<DataFormatOptions> res;\n  for (int i = 0; i < 16; ++i) {\n    DataFormatOptions options;\n    options.lagrangex_header = i & 1;\n    options.kafka_dump_prefix = i / 2 & 1;\n    options.has_sort_id = i / 4 & 1;\n    options.kafka_dump = i / 8 & 1;\n    res.push_back(options);\n  }\n  return res;\n}\n\nINSTANTIATE_TEST_SUITE_P(ReadWriteTestAll, ReadWriteTest,\n                         testing::ValuesIn(GenerateOptions()));\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/data_reader.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/training_instance/cc/data_reader.h\"\n\n#include <bitset>\n#include <climits>\n\n#include \"monolith/native_training/data/training_instance/cc/snappy_inputbuffer.h\"\n#include \"monolith/native_training/data/training_instance/cc/zstd_inputbuffer.h\"\n#include \"tensorflow/core/lib/core/coding.h\"\n#include \"tensorflow/core/lib/core/errors.h\"\n#include \"tensorflow/core/lib/io/buffered_inputstream.h\"\n#include \"tensorflow/core/lib/io/random_inputstream.h\"\n#include \"tensorflow/core/lib/io/zlib_compression_options.h\"\n#include \"tensorflow/core/lib/io/zlib_inputstream.h\"\n#include \"tensorflow/core/platform/env.h\"\n#include \"tensorflow/core/profiler/lib/traceme.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nusing EFeature = ::monolith::io::proto::Feature;\nusing Example = ::monolith::io::proto::Example;\nusing ExampleBatch = ::monolith::io::proto::ExampleBatch;\nusing FeatureListType = ::monolith::io::proto::FeatureListType;\nusing IFeature = ::idl::matrix::proto::Feature;\nusing Instance = ::parser::proto::Instance;\nusing LineId = ::idl::matrix::proto::LineId;\n\ndata_format::DataFormat data_format::StringToDataFormat(\n    const std::string &type) {\n  static const std::string EXAMPLEBATCH_STR = \"examplebatch\";\n  static const std::string EXAMPLE_STR = \"example\";\n  static const std::string INSTANCE_STR = \"instance\";\n  static const std::string PLAINTEXT_STR = \"plaintext\";\n\n  if (type == PLAINTEXT_STR) {\n    return data_format::PLAINTEXT;\n  } else if (type == EXAMPLEBATCH_STR) {\n    return data_format::EXAMPLEBATCH;\n  } else if (type == EXAMPLE_STR) {\n    return data_format::EXAMPLE;\n  } else if (type == INSTANCE_STR) {\n    return data_format::INSTANCE;\n  } else {\n    LOG(WARNING) << \"unknow input:\" << type;\n    return data_format::UNKNOW;\n  }\n  return data_format::UNKNOW;\n}\n\nstatic const size_t kLengthSize = 8;\nconst int kDEFAULT_SNAPPY_BUFFER_SIZE = 64 * 1024 * 1024;  // 64MB\n\nStatus AddFeature(const std::string &name, const EFeature &efeat,\n                  Instance *instance) {\n  if (name == \"__LINE_ID__\") {\n    const auto &line_id = efeat.bytes_list().value(0);\n    bool ok = instance->mutable_line_id()->ParseFromArray(line_id.data(),\n                                                          line_id.size());\n    if (!ok) {\n      return errors::FailedPrecondition(\"Failed to parse the LineId\");\n    }\n  } else if (name == \"__LABEL__\") {\n    const auto &float_list = efeat.float_list();\n    for (const auto &value : float_list.value()) {\n      instance->add_label(value);\n    }\n  } else if (name == \"instance_weight\") {\n    float instance_weight = 1.0;\n    if (efeat.float_list().value_size() > 0) {\n      instance_weight = efeat.float_list().value(0);\n    }\n    instance->set_instance_weight(instance_weight);\n  } else {\n    switch (efeat.type_case()) {\n      case EFeature::TypeCase::TYPE_NOT_SET:\n        break;\n      case EFeature::TypeCase::kFidV1List:\n        for (const auto &value : efeat.fid_v1_list().value()) {\n          instance->add_fid(value);\n        }\n        break;\n      default:\n        IFeature *ifeat = instance->add_feature();\n        ifeat->set_name(name);\n        switch (efeat.type_case()) {\n          case EFeature::TypeCase::kFidV2List:\n            for (const auto &fid : efeat.fid_v2_list().value()) {\n              ifeat->add_fid(fid);\n            }\n            break;\n          case EFeature::TypeCase::kFloatList:\n            for (const auto &fv : efeat.float_list().value()) {\n              ifeat->add_float_value(fv);\n            }\n            break;\n          case EFeature::TypeCase::kInt64List:\n            for (const auto &iv : efeat.int64_list().value()) {\n              ifeat->add_int64_value(iv);\n            }\n            break;\n          case EFeature::TypeCase::kBytesList:\n            for (const auto &bv : efeat.bytes_list().value()) {\n              ifeat->add_bytes_value(bv);\n            }\n            break;\n          case EFeature::TypeCase::kFidV2Lists:\n            for (const auto &elist : efeat.fid_v2_lists().list()) {\n              auto *ilist = ifeat->add_fid_list();\n              for (const auto &value : elist.value()) {\n                ilist->add_value(value);\n              }\n            }\n            break;\n          case EFeature::TypeCase::kFloatLists:\n            for (const auto &elist : efeat.float_lists().list()) {\n              auto *ilist = ifeat->add_float_list();\n              for (const auto &value : elist.value()) {\n                ilist->add_value(value);\n              }\n            }\n            break;\n          case EFeature::TypeCase::kInt64Lists:\n            for (const auto &elist : efeat.int64_lists().list()) {\n              auto *ilist = ifeat->add_int64_list();\n              for (const auto &value : elist.value()) {\n                ilist->add_value(value);\n              }\n            }\n            break;\n          case EFeature::TypeCase::kBytesLists:\n            for (const auto &elist : efeat.bytes_lists().list()) {\n              auto *ilist = ifeat->add_bytes_list();\n              for (const auto &value : elist.value()) {\n                ilist->add_value(value);\n              }\n            }\n            break;\n          default:\n            break;\n        }\n        break;\n    }\n  }\n\n  return Status::OK();\n}\n\nvoid ExtendExample(Example *pb, FeatureNameMapper *mapper /* = nullptr*/) {\n  bool has_line_id = false, has_label = false, has_instance_weight = false;\n  for (uint i = 0; i < pb->named_feature_size(); ++i) {\n    auto &named_feature = *(pb->mutable_named_feature(i));\n    if (mapper) {\n      int id;\n      int32_t sorted_id = -1;\n      if (mapper->GetIdByName(named_feature.name(), &id, &sorted_id)) {\n        named_feature.set_sorted_id(sorted_id);\n      }\n    }\n    if (named_feature.name() == \"__LINE_ID__\") {\n      has_line_id = true;\n      const auto &line_id = named_feature.feature().bytes_list().value(0);\n      pb->mutable_line_id()->ParseFromArray(line_id.data(), line_id.size());\n    } else if (named_feature.name() == \"__LABEL__\") {\n      has_label = true;\n      const auto &float_list = named_feature.feature().float_list();\n      for (const auto &value : float_list.value()) {\n        pb->add_label(value);\n      }\n    } else if (named_feature.name() == \"instance_weight\") {\n      has_instance_weight = true;\n      float instance_weight = 1.0;\n      if (named_feature.feature().float_list().value_size() > 0) {\n        instance_weight = named_feature.feature().float_list().value(0);\n      }\n      pb->set_instance_weight(instance_weight);\n    }\n\n    if (has_line_id && has_label && has_instance_weight) {\n      break;\n    }\n  }\n}\n\nStatus ExampleToInstance(Example *example, Instance *instance) {\n  for (const auto &named_feature : example->named_feature()) {\n    std::string name = named_feature.name();\n    const EFeature &efeat = named_feature.feature();\n    TF_RETURN_IF_ERROR(AddFeature(name, efeat, instance));\n  }\n\n  // (todo): named_raw_feature is not supported in instance\n  return Status::OK();\n}\n\nStatus InstanceToExample(Instance *instance, Example *example) {\n  int index = 0;\n  if (instance->has_line_id()) {\n    example->mutable_line_id()->CopyFrom(instance->line_id());\n  }\n\n  if (instance->label_size() > 0) {\n    for (const auto &value : instance->label()) {\n      example->add_label(value);\n    }\n  }\n\n  if (instance->has_instance_weight()) {\n    example->set_instance_weight(instance->instance_weight());\n  } else {\n    example->set_instance_weight(1.0);\n  }\n\n  if (instance->value_size() > 0) {\n    auto *named_feature = example->add_named_feature();\n    named_feature->set_name(\"value\");\n    named_feature->set_id(index++);\n    auto *efeat = named_feature->mutable_feature();\n    auto *float_list = efeat->mutable_float_list();\n    for (const auto &value : instance->value()) {\n      float_list->add_value(value);\n    }\n  }\n\n  std::unordered_map<int, ::monolith::io::proto::FidList *> slot_to_efeat_;\n  const auto &fids = instance->fid();\n  for (const auto &fid : fids) {\n    int slot_id = fid >> 54;\n    auto it = slot_to_efeat_.find(slot_id);\n    ::monolith::io::proto::FidList *fid_v1_list = nullptr;\n    if (it != slot_to_efeat_.end()) {\n      fid_v1_list = it->second;\n    } else {\n      auto *named_feature = example->add_named_feature();\n      named_feature->set_name(absl::StrCat(\"fc_slot_\", slot_id));\n      named_feature->set_id(index++);\n      fid_v1_list = named_feature->mutable_feature()->mutable_fid_v1_list();\n      slot_to_efeat_.emplace(slot_id, fid_v1_list);\n    }\n\n    fid_v1_list->add_value(fid);\n  }\n\n  for (const auto &ifeat : instance->feature()) {\n    auto *named_feature = example->add_named_feature();\n    named_feature->set_name(ifeat.name());\n    named_feature->set_id(index++);\n\n    auto *efeat = named_feature->mutable_feature();\n\n    if (ifeat.fid_size() > 0) {\n      auto *list = efeat->mutable_fid_v2_list();\n      for (const auto &value : ifeat.fid()) {\n        list->add_value(value);\n      }\n    } else if (ifeat.float_value_size() > 0) {\n      auto *list = efeat->mutable_float_list();\n      for (const auto &value : ifeat.float_value()) {\n        list->add_value(value);\n      }\n    } else if (ifeat.int64_value_size() > 0) {\n      auto *list = efeat->mutable_int64_list();\n      for (const auto &value : ifeat.int64_value()) {\n        list->add_value(value);\n      }\n    } else if (ifeat.bytes_value_size() > 0) {\n      auto *bytes_list = efeat->mutable_bytes_list();\n      for (const auto &value : ifeat.bytes_value()) {\n        bytes_list->add_value(value);\n      }\n    } else if (ifeat.fid_list_size() > 0) {\n      auto *elists = efeat->mutable_fid_v2_lists();\n      for (const auto &ilist : ifeat.fid_list()) {\n        auto *elist = elists->add_list();\n        for (const auto &value : ilist.value()) {\n          elist->add_value(value);\n        }\n      }\n    } else if (ifeat.float_list_size() > 0) {\n      auto *elists = efeat->mutable_float_lists();\n      for (const auto &ilist : ifeat.float_list()) {\n        auto *list = elists->add_list();\n        for (const auto &value : ilist.value()) {\n          list->add_value(value);\n        }\n      }\n    } else if (ifeat.int64_list_size() > 0) {\n      auto *elists = efeat->mutable_int64_lists();\n      for (const auto &ilist : ifeat.int64_list()) {\n        auto *list = elists->add_list();\n        for (const auto &value : ilist.value()) {\n          list->add_value(value);\n        }\n      }\n    } else if (ifeat.bytes_list_size() > 0) {\n      auto *elists = efeat->mutable_bytes_lists();\n      for (const auto &ilist : ifeat.bytes_list()) {\n        auto *list = elists->add_list();\n        for (const auto &value : ilist.value()) {\n          list->add_value(value);\n        }\n      }\n    } else {\n      LOG(INFO) << absl::StrCat(\"empty \", ifeat.name());\n    }\n  }\n\n  return Status::OK();\n}\n\nStatus ExampleBatchToInstance(ExampleBatch *example_batch, int index,\n                              Instance *instance) {\n  for (const auto &named_feature_list :\n       example_batch->named_feature_list()) {  // NamedFeatureList\n    const std::string &name = named_feature_list.name();\n    const EFeature &efeat = named_feature_list.type() == FeatureListType::SHARED\n                                ? named_feature_list.feature(0)\n                                : named_feature_list.feature(index);\n    TF_RETURN_IF_ERROR(AddFeature(name, efeat, instance));\n  }\n  instance->set_data_source_key(example_batch->data_source_key());\n\n  // (todo): named_raw_feature is not supported in instance\n  return Status::OK();\n}\n\nStatus ExampleBatchToExample(ExampleBatch *example_batch, int index,\n                             Example *example,\n                             FeaturePruningType feature_pruning_type,\n                             FeatureNameMapper *mapper) {\n  profiler::TraceMe activity([]() { return \"ExampleBatchToExample\"; });\n  for (const auto &named_feature : example_batch->named_feature_list()) {\n    if (named_feature.type() != FeatureListType::SHARED) {\n      if (example_batch->batch_size() != named_feature.feature_size()) {\n        std::string err_log = absl::StrFormat(\n            \"ExampleBatch batch_size should be equal to named_feature size, \"\n            \"while got %d vs %d for feature_name %s\",\n            example_batch->batch_size(), named_feature.feature_size(),\n            named_feature.name());\n        LOG(ERROR) << err_log;\n        return errors::OutOfRange(err_log);\n      }\n      if (index >= named_feature.feature_size()) {\n        std::string err_log = absl::StrFormat(\n            \"index should be less than named_feature size, \"\n            \"while got %d vs %d for feautre_name %s\",\n            index, named_feature.feature_size(), named_feature.name());\n        LOG(ERROR) << err_log;\n        return errors::OutOfRange(err_log);\n      }\n    } else {\n      if (named_feature.feature_size() == 0) {\n        std::string err_log = absl::StrFormat(\n            \"named_feature size should be positive while got %d for \"\n            \"feature_name %s\",\n            named_feature.feature_size(), named_feature.name());\n        LOG(ERROR) << err_log;\n        return errors::OutOfRange(err_log);\n      }\n    }\n    const auto &efeat = named_feature.type() == FeatureListType::SHARED\n                            ? named_feature.feature(0)\n                            : named_feature.feature(index);\n    if (named_feature.name() == \"__LINE_ID__\") {\n      const auto &line_id = efeat.bytes_list().value(0);\n      bool ok = example->mutable_line_id()->ParseFromArray(line_id.data(),\n                                                           line_id.size());\n      if (!ok) {\n        return errors::FailedPrecondition(\"Failed to parse the LineId\");\n      }\n    } else if (named_feature.name() == \"__LABEL__\") {\n      const auto &float_list = efeat.float_list();\n      for (const auto &value : float_list.value()) {\n        example->add_label(value);\n      }\n    } else if (named_feature.name() == \"instance_weight\") {\n      float instance_weight = 1.0;\n      if (efeat.float_list().value_size() > 0) {\n        instance_weight = efeat.float_list().value(0);\n      }\n      example->set_instance_weight(instance_weight);\n    } else if (feature_pruning_type != PRUNING_FEATURE) {\n      // FeatureNameMapper\n      if (mapper == nullptr) {\n        return errors::InvalidArgument(\n            \"FeatureNameMapper should be specified, while we got \"\n            \"mapper==nullptr\");\n      }\n      if (mapper->IsAvailable()) {\n        int32_t id = -1;\n        int32_t sorted_id = -1;\n        bool found = mapper->GetIdByName(named_feature.name(), &id, &sorted_id);\n        if (found) {\n          auto *out = example->add_named_feature();\n          out->set_name(named_feature.name());\n          out->set_id(named_feature.id());\n          out->set_sorted_id(sorted_id);\n          out->mutable_feature()->MergeFrom(efeat);\n        }\n      } else {\n        LOG_FIRST_N(INFO, 1) << \"FeatureNameMapper is not available!\";\n        auto *out = example->add_named_feature();\n        out->set_name(named_feature.name());\n        out->set_id(named_feature.id());\n        out->mutable_feature()->MergeFrom(efeat);\n      }\n    }\n  }\n\n  if (feature_pruning_type != PRUNING_RAW_FEATURE) {\n    for (const auto &named_feature : example_batch->named_raw_feature_list()) {\n      const auto &efeat = named_feature.type() == FeatureListType::SHARED\n                              ? named_feature.raw_feature(0)\n                              : named_feature.raw_feature(index);\n      auto *out = example->add_named_raw_feature();\n      out->set_name(named_feature.name());\n      out->set_id(named_feature.id());\n      out->mutable_raw_feature()->MergeFrom(efeat);\n    }\n  }\n\n  example->set_data_source_key(example_batch->data_source_key());\n\n  return Status::OK();\n}\n\nInputStreamReader::InputStreamReader(\n    const DataFormatOptions &options,\n    std::unique_ptr<io::InputStreamInterface> input_stream)\n    : BaseStreamReader(options),\n      input_stream_(std::move(input_stream)),\n      last_read_failed_(false) {}\n\nnamespace {\n\nstd::unique_ptr<io::InputStreamInterface> CreateInputFileStream(\n    RandomAccessFile *f, const InputCompressType compression_type,\n    int64 buffer_size) {\n  buffer_size = buffer_size ? buffer_size : kDEFAULT_SNAPPY_BUFFER_SIZE;\n  if (compression_type == InputCompressType::SNAPPY) {\n    return std::make_unique<io::ByteSnappyInputBuffer>(f, buffer_size,\n                                                       buffer_size);\n  } else if (compression_type == InputCompressType::ZSTD) {\n    auto s = std::make_unique<io::RandomAccessInputStream>(f);\n    return std::make_unique<io::MonolithZstdInputStream>(\n        s.release(), buffer_size, buffer_size);\n  } else if (compression_type == InputCompressType::ZLIB ||\n             compression_type == InputCompressType::GZIP) {\n    const io::ZlibCompressionOptions zlib_options =\n        compression_type == InputCompressType::ZLIB\n            ? io::ZlibCompressionOptions::DEFAULT()\n            : io::ZlibCompressionOptions::GZIP();\n    auto s = std::make_unique<io::RandomAccessInputStream>(f);\n    return std::make_unique<io::ZlibInputStream>(\n        s.release(), static_cast<size_t>(buffer_size),\n        static_cast<size_t>(buffer_size), zlib_options);\n  } else {\n    auto s = std::make_unique<io::RandomAccessInputStream>(f);\n    return std::make_unique<io::BufferedInputStream>(s.release(), buffer_size,\n                                                     true);\n  }\n}\n\n}  // namespace\n\nFileStreamReader::FileStreamReader(const DataFormatOptions &options,\n                                   std::unique_ptr<RandomAccessFile> f,\n                                   const InputCompressType compression_type,\n                                   int64 buffer_size)\n    : InputStreamReader(options, CreateInputFileStream(\n                                     f.get(), compression_type, buffer_size)),\n      f_(std::move(f)) {}\n\nStatus InputStreamReader::ReadNBytes(size_t n, tstring *result) {\n  if (n >= SIZE_MAX - sizeof(uint32)) {\n    return errors::DataLoss(\"record size too large\");\n  }\n\n  TF_RETURN_IF_ERROR(input_stream_->ReadNBytes(n, result));\n  if (result->size() != n) {\n    last_read_failed_ = true;\n    if (result->empty()) {\n      return errors::OutOfRange(\"eof\");\n    } else {\n      return errors::DataLoss(\"truncated record\");\n    }\n  }\n\n  return Status::OK();\n}\n\nuint64 InputStreamReader::GetOffset() { return input_stream_->Tell(); }\n\nStatus InputStreamReader::SetOffset(uint64 *offset) {\n  int64 curr_pos = input_stream_->Tell();\n  int64 desired_pos = static_cast<int64>(*offset);\n  if (curr_pos > desired_pos || curr_pos < 0 /* EOF */ ||\n      (curr_pos == desired_pos && last_read_failed_)) {\n    last_read_failed_ = false;\n    TF_RETURN_IF_ERROR(input_stream_->Reset());\n    TF_RETURN_IF_ERROR(input_stream_->SkipNBytes(desired_pos));\n  } else if (curr_pos < desired_pos) {\n    TF_RETURN_IF_ERROR(input_stream_->SkipNBytes(desired_pos - curr_pos));\n  }\n  DCHECK_EQ(desired_pos, input_stream_->Tell());\n  return Status::OK();\n}\n\n// TODO(leqi.zou): Make input stream async and static.\n// Currently the problem is if we are unable to read N bytes,\n// the code is not cancellable (CTRL + C not working).\nStdinStreamReader::StdinStreamReader(const DataFormatOptions &options,\n                                     int64 buffer_size)\n    : BaseStreamReader(options), buffer_size_(buffer_size) {\n  LOG_FIRST_N(INFO, 1) << \"Init stdin read buffer_size: \" << buffer_size_;\n  input_stream_.reset(&std::cin, [](...) {});\n  buffer_.reset(new char[buffer_size]);\n}\n\nStatus StdinStreamReader::ReadNBytes(size_t n, tstring *result) {\n  if (n > buffer_size_) {\n    // return errors::DataLoss(\n    //     \"Buffer size may be too small! Should be bigger than \", n);\n    // TODO(ltli): 临时支持推荐平台项目，sandbox 测试数据格式错误过不了检查\n    LOG(WARNING) << \"Data header abnormal! read too big size: \" << n\n                 << \". pls check PBDataset params: lagrangex_header, sort_id\";\n    LOG(INFO) << \"stdin meets EOF\";\n    return errors::OutOfRange(\"eof\");\n  }\n  if (!input_stream_->read(buffer_.get(), n)) {\n    if (input_stream_->eof()) {\n      LOG(INFO) << \"stdin meets EOF\";\n      return errors::OutOfRange(\"eof\");\n    } else {\n      return errors::DataLoss(\"streaming load broken\");\n    }\n  }\n  offset_ += n;\n  result->assign(buffer_.get(), n);\n  return Status::OK();\n}\n\nuint64 StdinStreamReader::GetOffset() { return offset_; }\n\nStatus StdinStreamReader::SetOffset(uint64 *offset) {\n  if (offset_ < *offset) {\n    tstring buf;\n    size_t size = *offset - offset_;\n    bool result = static_cast<bool>(input_stream_->read(buf.data(), size));\n    if (input_stream_->eof()) {\n      return errors::OutOfRange(\"eof\");\n    } else if (input_stream_->fail() || !result) {\n      return errors::DataLoss(\"streaming load broken\");\n    }\n    offset_ = *offset;\n    return Status::OK();\n  }\n  if (offset_ == *offset) {\n    return Status::OK();\n  } else {\n    return errors::FailedPrecondition(\n        \"Cannot set the offset of stdin ahead of current position\");\n  }\n}\n\nPBIterator::PBIterator(std::unique_ptr<BaseStreamReader> reader,\n                       FeaturePruningType feature_pruning_type)\n    : feature_pruning_type_(feature_pruning_type),\n      reader_(std::move(reader)),\n      counter_(std::make_unique<FeaturePruningByteCounter>()) {}\n\nStatus PBIterator::next(uint64 *offset, uint32_t *data_source_key,\n                        tstring *serialized) {\n  uint8_t pb_type;\n  reader_->SetOffset(offset);\n  TF_RETURN_IF_ERROR(\n      reader_->ReadPBBytes(&pb_type, data_source_key, serialized));\n  return Status::OK();\n}\n\nStatus PBIterator::next(uint64 *offset, Instance *pb) {\n  tstring buf;\n  uint32_t data_source_key;\n  TF_RETURN_IF_ERROR(next(offset, &data_source_key, &buf));\n  bool ok = pb->ParseFromArray(buf.data(), buf.size());\n  pb->set_data_source_key(data_source_key);\n  if (ok) {\n    return Status::OK();\n  } else {\n    return errors::FailedPrecondition(\"Failed to parse the Instance.\");\n  }\n}\n\nStatus PBIterator::next(uint64 *offset, Example *pb) {\n  tstring buf;\n  uint32_t data_source_key;\n  TF_RETURN_IF_ERROR(next(offset, &data_source_key, &buf));\n  bool ok = pb->ParseFromArray(buf.data(), buf.size());\n  pb->set_data_source_key(data_source_key);\n\n  if (ok) {\n    ExtendExample(pb);\n    counter_->AddByteSize(pb->ByteSizeLong());\n    if (feature_pruning_type_ == PRUNING_FEATURE) {\n      auto *named_features = pb->mutable_named_feature();\n      named_features->erase(named_features->begin(), named_features->end());\n    } else if (feature_pruning_type_ == PRUNING_RAW_FEATURE) {\n      auto *named_raw_feature = pb->mutable_named_raw_feature();\n      named_raw_feature->erase(named_raw_feature->cbegin(),\n                               named_raw_feature->cend());\n    }\n    counter_->AddByteSizePruned(pb->ByteSizeLong());\n    LOG_EVERY_N_SEC(INFO, 180) << counter_->DebugString();\n\n    return Status::OK();\n  } else {\n    return errors::FailedPrecondition(\"Failed to parse the Example.\");\n  }\n}\n\nStatus PBIterator::next(uint64 *offset, ExampleBatch *pb) {\n  tstring buf;\n  uint32_t data_source_key;\n  TF_RETURN_IF_ERROR(next(offset, &data_source_key, &buf));\n  bool ok = pb->ParseFromArray(buf.data(), buf.size());\n  pb->set_data_source_key(data_source_key);\n\n  counter_->AddByteSize(pb->ByteSizeLong());\n  if (feature_pruning_type_ == PRUNING_FEATURE) {\n    auto *named_feature_list = pb->mutable_named_feature_list();\n    auto it = named_feature_list->begin();\n    while (it != named_feature_list->end()) {\n      if (it->name() != \"__LABEL__\" && it->name() != \"__LINE_ID__\" &&\n          it->name() != \"instance_weight\") {\n        // if erase, it will move to the next element\n        named_feature_list->erase(it);\n      } else {\n        ++it;\n      }\n    }\n  } else if (feature_pruning_type_ == PRUNING_RAW_FEATURE) {\n    auto *named_raw_feature_list = pb->mutable_named_raw_feature_list();\n    named_raw_feature_list->erase(named_raw_feature_list->begin(),\n                                  named_raw_feature_list->end());\n  }\n\n  counter_->AddByteSizePruned(pb->ByteSizeLong());\n  LOG_EVERY_N_SEC(INFO, 180) << counter_->DebugString();\n\n  if (ok) {\n    return Status::OK();\n  } else {\n    return errors::FailedPrecondition(\"Failed to parse the ExampleBatch.\");\n  }\n}\n\nuint64 PBIterator::GetOffset() { return reader_->GetOffset(); }\n\nStatus PBIterator::SetOffset(uint64 *offset) {\n  return reader_->SetOffset(offset);\n}\n\nExampleBatchIterator::ExampleBatchIterator(\n    std::unique_ptr<BaseStreamReader> reader,\n    FeaturePruningType feature_pruning_type, FeatureNameMapper *mapper)\n    : PBIterator(std::move(reader), feature_pruning_type), mapper_(mapper) {\n  arena_ = std::make_unique<google::protobuf::Arena>();\n  cur_ = google::protobuf::Arena::CreateMessage<ExampleBatch>(arena_.get());\n}\n\nStatus ExampleBatchIterator::next_internal(uint64 *offset) {\n  if (index_ < batch_size_ - 1) {\n    index_++;\n    return Status::OK();\n  }\n\n  profiler::TraceMe activity([]() { return \"ReadAndDeserialize\"; });\n  uint8_t pb_type;\n  uint32_t data_source_key;\n  tstring buf;\n  reader_->SetOffset(offset);\n  arena_ = std::make_unique<google::protobuf::Arena>();\n  cur_ = google::protobuf::Arena::CreateMessage<ExampleBatch>(arena_.get());\n\n  TF_RETURN_IF_ERROR(reader_->ReadPBBytes(&pb_type, &data_source_key, &buf));\n  bool ok = cur_->ParseFromArray(buf.data(), buf.size());\n  counter_->AddByteSize(cur_->ByteSizeLong());\n  cur_->set_data_source_key(data_source_key);\n\n  if (!ok) {\n    return errors::FailedPrecondition(\"Failed to parse the ExampleBatch.\");\n  } else {\n    index_ = 0;\n    batch_size_ = cur_->batch_size();\n    return Status::OK();\n  }\n}\n\nStatus ExampleBatchIterator::next(uint64 *offset, uint32_t *data_source_key,\n                                  tstring *serialized) {\n  uint8_t pb_type;\n  reader_->SetOffset(offset);\n  TF_RETURN_IF_ERROR(\n      reader_->ReadPBBytes(&pb_type, data_source_key, serialized));\n  return Status::OK();\n}\n\nStatus ExampleBatchIterator::next(uint64 *offset, ExampleBatch *pb) {\n  uint8_t pb_type;\n  uint32_t data_source_key;\n  tstring buf;\n  reader_->SetOffset(offset);\n  TF_RETURN_IF_ERROR(reader_->ReadPBBytes(&pb_type, &data_source_key, &buf));\n  bool ok = pb->ParseFromArray(buf.data(), buf.size());\n  pb->set_data_source_key(data_source_key);\n  counter_->AddByteSize(pb->ByteSizeLong());\n\n  if (feature_pruning_type_ == PRUNING_FEATURE) {\n    auto *named_feature_list = pb->mutable_named_feature_list();\n    auto it = named_feature_list->begin();\n    while (it != named_feature_list->end()) {\n      if (it->name() != \"__LABEL__\" && it->name() != \"__LINE_ID__\" &&\n          it->name() != \"instance_weight\") {\n        // if erase, it will move to the next element\n        named_feature_list->erase(it);\n      } else {\n        ++it;\n      }\n    }\n  } else if (feature_pruning_type_ == PRUNING_RAW_FEATURE) {\n    auto *named_raw_feature_list = pb->mutable_named_raw_feature_list();\n    named_raw_feature_list->erase(named_raw_feature_list->begin(),\n                                  named_raw_feature_list->end());\n  }\n\n  counter_->AddByteSizePruned(pb->ByteSizeLong());\n  LOG_EVERY_N_SEC(INFO, 180) << counter_->DebugString();\n\n  if (!ok) {\n    return errors::FailedPrecondition(\"Failed to parse the ExampleBatch.\");\n  } else {\n    return Status::OK();\n  }\n}\n\nStatus ExampleBatchIterator::next(uint64 *offset, Instance *pb) {\n  TF_RETURN_IF_ERROR(next_internal(offset));\n  return ExampleBatchToInstance(cur_, index_, pb);\n}\n\nStatus ExampleBatchIterator::next(uint64 *offset, Example *pb) {\n  profiler::TraceMe activity([]() { return \"ExampleBatchIteratorNext\"; });\n  TF_RETURN_IF_ERROR(next_internal(offset));\n  Status s =\n      ExampleBatchToExample(cur_, index_, pb, feature_pruning_type_, mapper_);\n  counter_->AddByteSizePruned(pb->ByteSizeLong());\n  LOG_EVERY_N_SEC(INFO, 3600) << counter_->DebugString();\n  return s;\n}\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/data_reader.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_DATA_READER_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_DATA_READER_H_\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"tensorflow/core/framework/variant.h\"\n#include \"tensorflow/core/lib/core/coding.h\"\n#include \"tensorflow/core/lib/core/errors.h\"\n#include \"tensorflow/core/lib/core/stringpiece.h\"\n#include \"tensorflow/core/lib/io/inputstream_interface.h\"\n#include \"tensorflow/core/platform/env.h\"\n#include \"tensorflow/core/platform/file_system.h\"\n#include \"tensorflow/core/platform/macros.h\"\n#include \"tensorflow/core/platform/types.h\"\n\n#include \"monolith/native_training/data/training_instance/cc/data_format_options.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nenum FeaturePruningType {\n  AS_IS = 0,\n  PRUNING_FEATURE = 1,\n  PRUNING_RAW_FEATURE = 2\n};\n\nnamespace data_format {\nenum DataFormat {\n  UNKNOW = 0,\n  PLAINTEXT = 1,\n  INSTANCE = 2,\n  EXAMPLE = 3,\n  EXAMPLEBATCH = 4\n};\n\nDataFormat StringToDataFormat(const std::string &type);\n};  // namespace data_format\n\nvoid ExtendExample(::monolith::io::proto::Example *pb,\n                   FeatureNameMapper *mapper = nullptr);\nStatus ExampleToInstance(::monolith::io::proto::Example *example,\n                         ::parser::proto::Instance *instance);\nStatus InstanceToExample(::parser::proto::Instance *instance,\n                         ::monolith::io::proto::Example *example);\n\nStatus ExampleBatchToInstance(\n    ::monolith::io::proto::ExampleBatch *example_batch, int index,\n    ::parser::proto::Instance *instance);\n\nStatus ExampleBatchToExample(::monolith::io::proto::ExampleBatch *example_batch,\n                             int index, ::monolith::io::proto::Example *example,\n                             FeaturePruningType feature_pruning_type,\n                             FeatureNameMapper *mapper);\n\ntemplate <class T>\nclass BaseStreamReaderTmpl {\n public:\n  explicit BaseStreamReaderTmpl(const DataFormatOptions &options)\n      : options_(options) {}\n  virtual ~BaseStreamReaderTmpl() = default;\n\n  Status ReadPBBytes(uint8_t *pb_type, uint32_t *data_source_key, T *record) {\n    TF_RETURN_IF_ERROR(ReadDataHeader(pb_type, data_source_key));\n    size_t size;\n    TF_RETURN_IF_ERROR(ReadBinarySize(&size));\n    // Don't know whether FALLBACK_RESERVE_VALUE is in use.\n    if (size == 0xfefefefe) {\n      return errors::InvalidArgument(\"DEADBEEF value found\");\n    }\n    TF_RETURN_IF_ERROR(ReadNBytes(size, record));\n    return Status::OK();\n  }\n  virtual uint64 GetOffset() = 0;\n  virtual Status SetOffset(uint64 *offset) = 0;\n\n protected:\n  virtual Status ReadNBytes(size_t n, T *result) = 0;\n\n private:\n  Status ReadDataHeader(uint8_t *pb_type, uint32_t *data_source_key) {\n    size_t size = 0, aggregate_page_sortid_size = 0;\n    if (options_.lagrangex_header) {\n      // *dtype = ins_type == 0 ? PROTO_INSTANCE : EXAMPLE_PB;\n      TF_RETURN_IF_ERROR(ReadBinarySize(&size));\n      uint64_t lgx_header = static_cast<uint64_t>(size);\n      *pb_type = static_cast<uint8_t>(lgx_header & 0xff);\n      uint32_t source = static_cast<uint32_t>(lgx_header);\n      *data_source_key = (source >> 8) << 8;\n    } else {\n      *pb_type = 0;\n      if (options_.kafka_dump_prefix) {\n        TF_RETURN_IF_ERROR(ReadBinarySize(&size));\n        if (size == 0) {\n          TF_RETURN_IF_ERROR(ReadBinarySize(&size));\n        } else {\n          aggregate_page_sortid_size = size;\n        }\n      }\n      if (options_.has_sort_id) {\n        if (aggregate_page_sortid_size == 0) {\n          TF_RETURN_IF_ERROR(ReadBinarySize(&size));\n        } else {\n          size = aggregate_page_sortid_size;\n        }\n        T sort_id;\n        TF_RETURN_IF_ERROR(ReadNBytes(size, &sort_id));\n      }\n      if (options_.kafka_dump) {\n        TF_RETURN_IF_ERROR(ReadBinarySize(&size));\n      }\n    }\n\n    return Status::OK();\n  }\n\n  Status ReadBinarySize(size_t *size) {\n    T result;\n    TF_RETURN_IF_ERROR(ReadNBytes(sizeof(size_t), &result));\n    *size = static_cast<size_t>(core::DecodeFixed64(result.data()));\n    return Status::OK();\n  }\n\n  DataFormatOptions options_;\n};\n\nusing BaseStreamReader = BaseStreamReaderTmpl<tstring>;\n\nclass StdinStreamReader : public BaseStreamReader {\n public:\n  explicit StdinStreamReader(const DataFormatOptions &options,\n                             int64 buffer_size = 64 * 1024 * 1024);\n  ~StdinStreamReader() override = default;\n  uint64 GetOffset() override;\n  Status SetOffset(uint64 *offset) override;\n\n protected:\n  Status ReadNBytes(size_t n, tstring *result) override;\n\n private:\n  std::shared_ptr<std::istream> input_stream_;\n  std::unique_ptr<char[]> buffer_;\n  uint64 offset_;\n  int64 buffer_size_;\n\n  TF_DISALLOW_COPY_AND_ASSIGN(StdinStreamReader);\n};\n\nclass InputStreamReader : public BaseStreamReader {\n public:\n  explicit InputStreamReader(\n      const DataFormatOptions &options,\n      std::unique_ptr<io::InputStreamInterface> input_stream);\n  ~InputStreamReader() override = default;\n  uint64 GetOffset() override;\n  Status SetOffset(uint64 *offset) override;\n\n private:\n  Status ReadNBytes(size_t n, tstring *result) override;\n\n  std::unique_ptr<io::InputStreamInterface> input_stream_;\n  bool last_read_failed_;\n\n  TF_DISALLOW_COPY_AND_ASSIGN(InputStreamReader);\n};\n\nenum InputCompressType {\n  UNKNOW = 0,\n  NO = 1,\n  SNAPPY = 2,\n  ZSTD = 3,\n  ZLIB = 4,\n  GZIP = 5,\n  MAX = 6\n};\n\nclass FileStreamReader : public InputStreamReader {\n public:\n  explicit FileStreamReader(const DataFormatOptions &options,\n                            std::unique_ptr<RandomAccessFile> f,\n                            const InputCompressType compression_type,\n                            int64 buffer_size = 64 * 1024 * 1024);\n  static InputCompressType GetCompressType(const bool use_snappy,\n                                           const int32 compression_type) {\n    if (compression_type < InputCompressType::UNKNOW ||\n        compression_type >= InputCompressType::MAX) {\n      LOG(FATAL) << \"GetCompressType error : compression_type\"\n                 << compression_type;\n    }\n    InputCompressType ret = InputCompressType::NO;\n    if (use_snappy) {\n      if (compression_type != InputCompressType::SNAPPY &&\n          compression_type != InputCompressType::UNKNOW) {\n        LOG(FATAL) << \"GetCompressType error: \" << use_snappy << \",\"\n                   << compression_type;\n      }\n      ret = InputCompressType::SNAPPY;\n    } else {\n      if (compression_type == InputCompressType::UNKNOW) {\n        ret = InputCompressType::NO;\n      } else {\n        ret = static_cast<InputCompressType>(compression_type);\n      }\n    }\n    return ret;\n  }\n\n private:\n  std::unique_ptr<RandomAccessFile> f_;\n};\n\ntemplate <class T>\nclass StringStreamReader : public BaseStreamReaderTmpl<T> {\n public:\n  explicit StringStreamReader(const DataFormatOptions &options, T content)\n      : BaseStreamReaderTmpl<T>(options),\n        content_(std::move(content)),\n        cur_(0) {}\n  Status ReadNBytes(size_t n, T *result) override {\n    if (cur_ + n > content_.size()) {\n      return errors::FailedPrecondition(\"request n error\");\n    }\n    if (n > 0 && cur_ == content_.size()) {\n      return errors::OutOfRange(\"Size exceeds he content size.\");\n    }\n    *result = T(content_.data() + cur_, n);\n    cur_ += n;\n    return Status::OK();\n  }\n\n  uint64 GetOffset() override { return cur_; }\n  Status SetOffset(uint64 *offset) override {\n    cur_ = *offset;\n    return Status::OK();\n  }\n\n private:\n  T content_;\n  int64 cur_;\n};\n\nusing ZeroCopyStringViewStreamReader = StringStreamReader<absl::string_view>;\n\nclass PBIterator {\n public:\n  PBIterator() = default;\n  explicit PBIterator(std::unique_ptr<BaseStreamReader> reader,\n                      FeaturePruningType feature_pruning_type);\n  virtual ~PBIterator() = default;\n\n  virtual Status next(uint64 *offset, uint32_t *data_source_key,\n                      tstring *serialized);\n\n  virtual Status next(uint64 *offset, ::parser::proto::Instance *pb);\n\n  virtual Status next(uint64 *offset, ::monolith::io::proto::Example *pb);\n\n  virtual Status next(uint64 *offset, ::monolith::io::proto::ExampleBatch *pb);\n\n  uint64 GetOffset();\n  Status SetOffset(uint64 *offset);\n\n protected:\n  FeaturePruningType feature_pruning_type_ = PRUNING_RAW_FEATURE;\n  std::unique_ptr<BaseStreamReader> reader_;\n  std::unique_ptr<FeaturePruningByteCounter> counter_;\n\n  TF_DISALLOW_COPY_AND_ASSIGN(PBIterator);\n};\n\nclass ExampleBatchIterator : public PBIterator {\n public:\n  ExampleBatchIterator() = default;\n  explicit ExampleBatchIterator(std::unique_ptr<BaseStreamReader> reader,\n                                FeaturePruningType feature_pruning_type,\n                                FeatureNameMapper *mapper);\n\n  Status next(uint64 *offset, uint32_t *data_source_key, tstring *serialized);\n  Status next(uint64 *offset, ::monolith::io::proto::ExampleBatch *pb);\n  Status next(uint64 *offset, ::parser::proto::Instance *pb) override;\n  Status next(uint64 *offset, ::monolith::io::proto::Example *pb) override;\n\n private:\n  Status next_internal(uint64 *offset);\n  int index_ = 0, batch_size_ = 0;\n  monolith::io::proto::ExampleBatch *cur_;\n  std::unique_ptr<google::protobuf::Arena> arena_;\n  FeatureNameMapper *mapper_;\n  TF_DISALLOW_COPY_AND_ASSIGN(ExampleBatchIterator);\n};\n\n/*\nclass THanler {\n  struct CurOutput : public PBIteratorWithDataFormatTransBaseOutput {\n  };\n\n  template <class TResult>\n  Status HandleReaderNextStauts(const Status &s, const TResult &result) {\n    return errors::Unimplemented(\"not implement\");\n  }\n\n  template <class TResult>\n  Status HandleResult(TResult &&result, CurOutput *output) { return\nerrors::Unimplemented(\"not implement\");\n  }\n\n};\n*/\n\nstruct PBIteratorWithDataFormatTransBaseOutput {\n  Status reader_status;\n};\n\ntemplate <class THanler>\nclass PBIteratorWithDataFormatTrans : public THanler {\n public:\n  PBIteratorWithDataFormatTrans(data_format::DataFormat input_pb_type,\n                                data_format::DataFormat output_pb_type)\n      : input_pb_type_(input_pb_type), output_pb_type_(output_pb_type) {}\n\n  Status GetNext(PBIterator *reader, typename THanler::CurOutput *output,\n                 uint64 *offset) {\n    Status s;\n    if (output_pb_type_ == data_format::PLAINTEXT) {\n      tstring serialized;\n      uint32_t data_source_key;\n      output->reader_status =\n          reader->next(offset, &data_source_key, &serialized);\n      s = THanler::HandleReaderNextStauts(output->reader_status, serialized);\n      if (!s.ok()) return s;\n      s = THanler::HandleResult(std::move(serialized), output);\n    } else if (input_pb_type_ == data_format::EXAMPLE &&\n               output_pb_type_ == data_format::INSTANCE) {\n      ::monolith::io::proto::Example exa_pb;\n      output->reader_status = reader->next(offset, &exa_pb);\n      s = THanler::HandleReaderNextStauts(output->reader_status, exa_pb);\n      if (!s.ok()) return s;\n      ::parser::proto::Instance ins_pb;\n      ExampleToInstance(&exa_pb, &ins_pb);\n      s = THanler::HandleResult(std::move(ins_pb), output);\n    } else if (input_pb_type_ == data_format::INSTANCE &&\n               output_pb_type_ == data_format::EXAMPLE) {\n      ::parser::proto::Instance ins_pb;\n      output->reader_status = reader->next(offset, &ins_pb);\n      s = THanler::HandleReaderNextStauts(output->reader_status, ins_pb);\n      if (!s.ok()) return s;\n      ::monolith::io::proto::Example exa_pb;\n      InstanceToExample(&ins_pb, &exa_pb);\n      s = THanler::HandleResult(std::move(exa_pb), output);\n    } else if (output_pb_type_ == data_format::EXAMPLE) {  // any ->\n                                                           // example\n      ::monolith::io::proto::Example exa_pb;\n      output->reader_status = reader->next(offset, &exa_pb);\n      s = THanler::HandleReaderNextStauts(output->reader_status, exa_pb);\n      if (!s.ok()) return s;\n      s = THanler::HandleResult(std::move(exa_pb), output);\n    } else if (output_pb_type_ == data_format::INSTANCE) {  // any ->\n                                                            // instance\n      ::parser::proto::Instance ins_pb;\n      output->reader_status = reader->next(offset, &ins_pb);\n      s = THanler::HandleReaderNextStauts(output->reader_status, ins_pb);\n      if (!s.ok()) return s;\n      s = THanler::HandleResult(std::move(ins_pb), output);\n    } else {  // any -> example_batch\n      ::monolith::io::proto::ExampleBatch eb_pb;\n      output->reader_status = reader->next(offset, &eb_pb);\n      s = THanler::HandleReaderNextStauts(output->reader_status, eb_pb);\n      if (!s.ok()) return s;\n      s = THanler::HandleResult(std::move(eb_pb), output);\n    }\n    return s;\n  }\n\n  data_format::DataFormat output_pb_type_;\n  data_format::DataFormat input_pb_type_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_DATA_READER_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/data_writer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/training_instance/cc/data_writer.h\"\n\n#include \"absl/strings/str_cat.h\"\n#include \"tensorflow/core/platform/coding.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nStatus BaseStreamWriter::PrepareHeader() {\n  if (options_.lagrangex_header) {\n    TF_RETURN_IF_ERROR(Write(std::string(8, 0)));\n  } else {\n    if (options_.kafka_dump_prefix) {\n      TF_RETURN_IF_ERROR(Write(std::string(16, 0)));\n    }\n    if (options_.has_sort_id) {\n      TF_RETURN_IF_ERROR(Write(std::string(8, 0)));\n    }\n    if (options_.kafka_dump) {\n      TF_RETURN_IF_ERROR(Write(std::string(8, 0)));\n    }\n  }\n  return Status::OK();\n}\n\nBaseStreamWriter::BaseStreamWriter(DataFormatOptions options)\n    : options_(std::move(options)) {}\n\nStatus BaseStreamWriter::WriteRecord(absl::string_view record) {\n  TF_RETURN_IF_ERROR(PrepareHeader());\n  char size_encoded[8];\n  core::EncodeFixed64(size_encoded, record.size());\n  TF_RETURN_IF_ERROR(Write(absl::string_view(size_encoded, 8)));\n  TF_RETURN_IF_ERROR(Write(record));\n  return Status::OK();\n}\n\nStatus StringStreamWriter::Write(absl::string_view s) {\n  absl::StrAppend(out_, s);\n  return Status::OK();\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/data_writer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_DATA_WRITER_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_DATA_WRITER_H_\n\n#include \"absl/strings/string_view.h\"\n#include \"monolith/native_training/data/training_instance/cc/data_format_options.h\"\n#include \"tensorflow/core/platform/errors.h\"\n#include \"tensorflow/core/platform/status.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass BaseStreamWriter {\n public:\n  explicit BaseStreamWriter(DataFormatOptions options);\n\n  Status WriteRecord(absl::string_view record);\n\n protected:\n  virtual Status Write(absl::string_view s) = 0;\n\n private:\n  Status PrepareHeader();\n\n private:\n  DataFormatOptions options_;\n};\n\nclass StringStreamWriter : public BaseStreamWriter {\n public:\n  explicit StringStreamWriter(DataFormatOptions options, std::string* out)\n      : BaseStreamWriter(std::move(options)), out_(out) {}\n\n private:\n  Status Write(absl::string_view s) override;\n  std::string* out_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_DATA_WRITER_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/fid.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_FID_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_FID_H_\n\n#include <iostream>\n#include <sstream>\n\nunion FIDV2;\n\nunion FIDV1 {\n  struct Underlying {\n    uint64_t signature : 54;\n    uint64_t slot : 10;\n    Underlying(uint64_t slot, uint64_t signature)\n        : slot(slot), signature(signature) {}\n  };\n\n  Underlying underlying;\n  uint64_t value;\n\n  FIDV1() : underlying(0, 0) {}\n  FIDV1(uint64_t slot, int64_t signature) : underlying(slot, signature) {\n    if (slot >= 1024) {\n      throw std::invalid_argument(\"slot should be less than 1024, while got \" +\n                                  std::to_string(slot));\n    }\n  }\n  FIDV1(uint64_t fid_v1_value) : value(fid_v1_value) {}\n\n  operator uint64_t() const { return this->value; }\n\n  [[nodiscard]] uint64_t slot() const { return this->underlying.slot; }\n\n  [[nodiscard]] uint64_t signature() const {\n    return this->underlying.signature;\n  }\n\n  [[nodiscard]] std::string DebugString() const {\n    std::stringstream ss;\n    ss << value << \"(v1|slot=\" << underlying.slot\n       << \"|sig=\" << underlying.signature << \")\";\n    return ss.str();\n  }\n\n  [[nodiscard]] FIDV2 ConvertAsV2() const;\n};\n\nunion FIDV2 {\n  struct Underlying {\n    uint64_t signature : 48;\n    uint64_t slot : 15;\n    uint64_t reserved : 1;\n\n    Underlying(uint64_t slot, uint64_t signature)\n        : reserved(0), slot(slot), signature(signature) {}\n  };\n\n  Underlying underlying;\n  uint64_t value;\n\n  FIDV2() : underlying(0, 0) {}\n  FIDV2(uint64_t slot, uint64_t signature) : underlying(slot, signature) {\n    if (slot >= 32768) {\n      throw std::invalid_argument(\"slot should be less than 32768, while got \" +\n                                  std::to_string(slot));\n    }\n  }\n  FIDV2(uint64_t fid_v2_value) : value(fid_v2_value) {\n    if (this->underlying.reserved == 1) {\n      throw std::invalid_argument(\"slot should be less than 32768, while got \" +\n                                  std::to_string(this->slot() + 32768));\n    }\n  }\n\n  operator uint64_t() const { return value; }\n\n  [[nodiscard]] uint64_t slot() const { return this->underlying.slot; }\n\n  [[nodiscard]] uint64_t signature() const {\n    return this->underlying.signature;\n  }\n\n  [[nodiscard]] std::string DebugString() const {\n    std::stringstream ss;\n    ss << value << \"(v2|slot=\" << underlying.slot\n       << \"|sig=\" << underlying.signature << \")\";\n    return ss.str();\n  }\n};\n\nFIDV2 FIDV1::ConvertAsV2() const {\n  return {this->underlying.slot, this->underlying.signature};\n}\n\nnamespace std {\n\ntemplate <>\nstruct hash<FIDV1> {\n  std::size_t operator()(FIDV1 fid) const { return std::hash<uint64_t>()(fid); }\n};\n\ntemplate <>\nstruct hash<FIDV2> {\n  std::size_t operator()(FIDV2 fid) const { return std::hash<uint64_t>()(fid); }\n};\n\n}  // namespace std\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_FID_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/fid_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/data/training_instance/cc/fid.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n\nnamespace {\nusing tensorflow::monolith_tf::GetFidV1;\nusing tensorflow::monolith_tf::GetFidV2;\n\nTEST(FIDTest, FIDV1) {\n  // 8 bytes\n  EXPECT_EQ(sizeof(FIDV1), 8);\n\n  // normal case\n  FIDV1 fid1(1, 100);\n  EXPECT_EQ(fid1.slot(), 1);\n  EXPECT_EQ(fid1.signature(), 100);\n  EXPECT_EQ(fid1, GetFidV1(1, 100));\n\n  // corner case1\n  FIDV1 fid2(1023, 1LL << 54);\n  EXPECT_EQ(fid2.slot(), 1023);\n  EXPECT_EQ(fid2.signature(), 0);\n  EXPECT_EQ(fid2, GetFidV1(1023, 1LL << 54));\n\n  // corner case2\n  EXPECT_THROW(\n      {\n        FIDV1 fid3(1024, 1LL << 54);\n        EXPECT_EQ(fid3.slot(), 0);\n        EXPECT_EQ(fid3.signature(), 0);\n        EXPECT_EQ(fid3, GetFidV1(1024, 1LL << 54));\n      },\n      std::invalid_argument);\n\n  // corner case3\n  EXPECT_THROW(\n      {\n        FIDV1 fid4(1025, 1LL << 54 | 1);\n        EXPECT_EQ(fid4.slot(), 1);\n        EXPECT_EQ(fid4.signature(), 1);\n        EXPECT_EQ(fid4, GetFidV1(1025, 1LL << 54 | 1));\n      },\n      std::invalid_argument);\n}\n\nTEST(FIDTest, FIDV2) {\n  // 8 bytes\n  EXPECT_EQ(sizeof(FIDV2), 8);\n\n  // normal case\n  FIDV2 fid1(1, 100);\n  EXPECT_EQ(fid1.slot(), 1);\n  EXPECT_EQ(fid1.signature(), 100);\n  EXPECT_EQ(fid1, GetFidV2(1, 100));\n\n  // corner case1\n  FIDV2 fid2(1024, 1LL << 54);\n  EXPECT_EQ(fid2.slot(), 1024);\n  EXPECT_EQ(fid2.signature(), 0);\n  EXPECT_EQ(fid2, GetFidV2(1024, 1LL << 54));\n\n  // corner case2\n  FIDV2 fid3(32767, 1LL << 48);\n  EXPECT_EQ(fid3.slot(), 32767);\n  EXPECT_EQ(fid3.signature(), 0);\n  EXPECT_EQ(fid3, GetFidV2(32767, 1LL << 48));\n\n  // corner case3\n  EXPECT_THROW(\n      {\n        FIDV2 fid4(32768, 1LL << 48);\n        EXPECT_EQ(fid4.slot(), 0);\n        EXPECT_EQ(fid4.signature(), 0);\n        // GetFidV2 has a tiny bug\n        EXPECT_EQ(fid4, (GetFidV2(32768, 1LL << 48) << 1) >> 1);\n      },\n      std::invalid_argument);\n\n  // corner case4\n  EXPECT_THROW(\n      {\n        FIDV2 fid5(32769, 1LL << 48 | 1);\n        EXPECT_EQ(fid5.slot(), 1);\n        EXPECT_EQ(fid5.signature(), 1);\n        // GetFidV2 has a tiny bug\n        EXPECT_EQ(fid5, (GetFidV2(32769, 1LL << 48 | 1) << 1) >> 1);\n      },\n      std::invalid_argument);\n}\n\nTEST(FIDTest, FIDV1ConvertV2) {\n  // normal case\n  FIDV1 fid_v1(1, 100);\n  FIDV2 fid_v2 = fid_v1.ConvertAsV2();\n  EXPECT_EQ(fid_v2.slot(), 1);\n  EXPECT_EQ(fid_v2.signature(), 100);\n  EXPECT_EQ(fid_v2, convert_fid_v1_to_v2(fid_v1));\n\n  // corner case1\n  FIDV1 fid_v1_1(1023, 1LL << 54);\n  FIDV2 fid_v2_1 = fid_v1_1.ConvertAsV2();\n  EXPECT_EQ(fid_v2_1.slot(), 1023);\n  EXPECT_EQ(fid_v2_1.signature(), 0);\n  EXPECT_EQ(fid_v2_1, convert_fid_v1_to_v2(fid_v1_1));\n\n  // corner case2\n  EXPECT_THROW(\n      {\n        FIDV1 fid_v1_2(1024, 1LL << 54);\n        FIDV2 fid_v2_2 = fid_v1_2.ConvertAsV2();\n        EXPECT_EQ(fid_v2_2.slot(), 0);\n        EXPECT_EQ(fid_v2_2.signature(), 0);\n        EXPECT_EQ(fid_v2_2, convert_fid_v1_to_v2(fid_v1_2));\n      },\n      std::invalid_argument);\n}\n\n}  // namespace\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/instance_dataset_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/dataset.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/partial_tensor_shape.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/lib/io/buffered_inputstream.h\"\n#include \"tensorflow/core/lib/io/inputbuffer.h\"\n#include \"tensorflow/core/lib/io/random_inputstream.h\"\n#include \"tensorflow/core/lib/io/zlib_compression_options.h\"\n#include \"tensorflow/core/lib/io/zlib_inputstream.h\"\n\n#include \"monolith/native_training/data/training_instance/cc/data_reader.h\"\n\nnamespace tensorflow {\nnamespace data {\nnamespace monolith_tf {\n\nusing ::tensorflow::monolith_tf::BaseStreamReader;\nusing ::tensorflow::monolith_tf::DataFormatOptions;\nusing ::tensorflow::monolith_tf::FileStreamReader;\nusing ::tensorflow::monolith_tf::InputCompressType;\nusing ::tensorflow::monolith_tf::PBIterator;\nusing ::tensorflow::monolith_tf::PRUNING_RAW_FEATURE;\nusing ::tensorflow::monolith_tf::StdinStreamReader;\n\nstruct DsOptions : DataFormatOptions {\n  bool use_snappy = false;\n  int32 compression_type = InputCompressType::UNKNOW;\n};\n\n// This is the instance dataset op and used in the estimator as input fn.\nclass InstanceDatasetOp : public DatasetOpKernel {\n public:\n  static constexpr const char* const kDatasetType = \"PbInstance\";\n  static constexpr const char* const kFileName = \"file_name\";\n  static constexpr const char* const kUseSnappy = \"use_snappy\";\n  static constexpr const char* const kHasSortId = \"has_sort_id\";\n  static constexpr const char* const kKafkaDump = \"kafka_dump\";\n  static constexpr const char* const kKafkaDumpPrefix = \"kafka_dump_prefix\";\n  static constexpr const char* const kCompressionType = \"compression_type\";\n\n  explicit InstanceDatasetOp(OpKernelConstruction* ctx) : DatasetOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(kCompressionType, &compression_type_));\n  }\n  ~InstanceDatasetOp() {}\n\n private:\n  void MakeDataset(OpKernelContext* ctx, DatasetBase** output) override {\n    tstring file_name;\n    DsOptions options;\n    OP_REQUIRES_OK(ctx,\n                   ParseScalarArgument<tstring>(ctx, kFileName, &file_name));\n    OP_REQUIRES_OK(\n        ctx, ParseScalarArgument<bool>(ctx, kUseSnappy, &options.use_snappy));\n    options.compression_type = compression_type_;\n    OP_REQUIRES_OK(\n        ctx, ParseScalarArgument<bool>(ctx, kHasSortId, &options.has_sort_id));\n    OP_REQUIRES_OK(\n        ctx, ParseScalarArgument<bool>(ctx, kKafkaDump, &options.kafka_dump));\n    OP_REQUIRES_OK(ctx, ParseScalarArgument<bool>(ctx, kKafkaDumpPrefix,\n                                                  &options.kafka_dump_prefix));\n    output_ = new Dataset(ctx, file_name, options);\n    *output = output_;\n  }\n\n  class Dataset : public DatasetBase {\n   public:\n    explicit Dataset(OpKernelContext* ctx, const tstring& file_name,\n                     const DsOptions& options)\n        : DatasetBase(DatasetContext(ctx)),\n          file_name_(file_name),\n          options_(options) {}\n\n    std::unique_ptr<IteratorBase> MakeIteratorInternal(\n        const string& prefix) const override {\n      return absl::make_unique<Iterator>(\n          Iterator::Params{this, strings::StrCat(prefix, \"::\", kDatasetType)});\n    }\n\n    const DataTypeVector& output_dtypes() const override {\n      static DataTypeVector* dtypes = new DataTypeVector({DT_STRING});\n      return *dtypes;\n    }\n\n    const std::vector<PartialTensorShape>& output_shapes() const override {\n      static std::vector<PartialTensorShape>* shapes =\n          new std::vector<PartialTensorShape>{TensorShape({})};\n      return *shapes;\n    }\n\n    string DebugString() const override {\n      return (\"This is the customized Instance Dataset: \" + file_name_);\n    }\n\n    Status CheckExternalState() const override { return Status::OK(); }\n\n   private:\n    Status AsGraphDefInternal(SerializationContext* ctx,\n                              DatasetGraphDefBuilder* b,\n                              Node** output) const override {\n      Node* filename = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(file_name_, &filename));\n      Node* use_snappy = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(options_.use_snappy, &use_snappy));\n      Node* has_sort_id = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(options_.has_sort_id, &has_sort_id));\n      Node* kafka_dump = nullptr;\n      TF_RETURN_IF_ERROR(b->AddScalar(options_.kafka_dump, &kafka_dump));\n      Node* kafka_dump_prefix = nullptr;\n      TF_RETURN_IF_ERROR(\n          b->AddScalar(options_.kafka_dump_prefix, &kafka_dump_prefix));\n      AttrValue compression_type;\n      b->BuildAttrValue(options_.compression_type, &compression_type);\n      TF_RETURN_IF_ERROR(b->AddDataset(\n          this,\n          {filename, use_snappy, has_sort_id, kafka_dump, kafka_dump_prefix},\n          {{kCompressionType, compression_type}}, output));\n      return Status::OK();\n    }\n\n    class Iterator : public DatasetIterator<Dataset> {\n     public:\n      explicit Iterator(const Params& params)\n          : DatasetIterator<Dataset>(params) {}\n\n      Status GetNextInternal(IteratorContext* ctx,\n                             std::vector<Tensor>* out_tensors,\n                             bool* end_of_sequence) override {\n        out_tensors->reserve(1);\n        mutex_lock l(mu_);\n        if (!reader_) {\n          TF_RETURN_IF_ERROR(SetupStreamsLocked(ctx->env()));\n        }\n        out_tensors->emplace_back(ctx->allocator({}), DT_STRING,\n                                  TensorShape({}));\n        uint32_t data_source_key;\n        Status s = reader_->next(&offset_, &data_source_key,\n                                 &out_tensors->back().scalar<tstring>()());\n        if (s.ok()) {\n          static monitoring::CounterCell* bytes_counter =\n              metrics::GetTFDataBytesReadCounter(kDatasetType);\n          bytes_counter->IncrementBy(\n              out_tensors->back().scalar<tstring>()().size());\n          *end_of_sequence = false;\n          num_random_samples_++;\n          offset_ = reader_->GetOffset();\n          return Status::OK();\n        }\n        out_tensors->pop_back();\n        ResetStreamsLocked();\n        if (errors::IsOutOfRange(s)) {\n          *end_of_sequence = true;\n          return Status::OK();\n        }\n        return s;\n      }\n\n     private:\n      std::shared_ptr<model::Node> CreateNode(\n          IteratorContext* ctx, model::Node::Args args) const override {\n        return model::MakeSourceNode(std::move(args));\n      }\n\n      Status SaveInternal(SerializationContext* ctx,\n                          IteratorStateWriter* writer) override {\n        mutex_lock l(mu_);\n        LOG(INFO) << \"Save function is not supported yet.\";\n        TF_RETURN_IF_ERROR(writer->WriteScalar(full_name(\"num_random_samples\"),\n                                               num_random_samples_));\n        TF_RETURN_IF_ERROR(writer->WriteScalar(full_name(\"offset_\"), offset_));\n        return Status::OK();\n      }\n\n      Status RestoreInternal(IteratorContext* ctx,\n                             IteratorStateReader* reader) override {\n        mutex_lock l(mu_);\n        LOG(INFO) << \"Restore function is not supported yet.\";\n        TF_RETURN_IF_ERROR(reader->ReadScalar(full_name(\"num_random_samples\"),\n                                              &num_random_samples_));\n        int64 offset;\n        TF_RETURN_IF_ERROR(reader->ReadScalar(full_name(\"offset_\"), &offset));\n        if (dataset()->file_name_.empty()) {\n          offset_ = 0;\n        } else {\n          offset_ = offset;\n        }\n        return Status::OK();\n      }\n\n      // Sets up reader streams to read from filename\n      Status SetupStreamsLocked(Env* env) TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) {\n        std::unique_ptr<BaseStreamReader> stream_reader;\n        if (dataset()->file_name_.empty()) {\n          stream_reader =\n              std::make_unique<StdinStreamReader>(dataset()->options_);\n        } else {\n          std::unique_ptr<RandomAccessFile> f;\n          TF_RETURN_IF_ERROR(\n              env->NewRandomAccessFile(dataset()->file_name_, &f));\n\n          auto compression_type = FileStreamReader::GetCompressType(\n              dataset()->options_.use_snappy,\n              dataset()->options_.compression_type);\n          stream_reader = std::make_unique<FileStreamReader>(\n              dataset()->options_, std::move(f), compression_type);\n        }\n        reader_ = absl::make_unique<PBIterator>(std::move(stream_reader),\n                                                PRUNING_RAW_FEATURE);\n        return Status::OK();\n      }\n\n      // Resets all reader streams.\n      void ResetStreamsLocked() TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) {\n        reader_.reset();\n      }\n\n      mutex mu_;\n      std::unique_ptr<PBIterator> reader_ TF_GUARDED_BY(mu_);\n      int64 num_random_samples_ TF_GUARDED_BY(mu_) = 0;\n      uint64 offset_ TF_GUARDED_BY(mu_) = 0;\n    };\n\n    tstring file_name_;\n    DsOptions options_;\n  };\n  int32 compression_type_;\n  Dataset* output_ = nullptr;\n};\n\nnamespace {\nREGISTER_KERNEL_BUILDER(Name(\"InstanceDataset\").Device(DEVICE_CPU),\n                        InstanceDatasetOp);\n}  // namespace\n\n}  // namespace monolith_tf\n}  // namespace data\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/instance_dataset_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_def_builder.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\n\nREGISTER_OP(\"InstanceDataset\")\n    .Input(\"file_name: string\")\n    .Input(\"use_snappy: bool\")\n    .Input(\"has_sort_id: bool\")\n    .Input(\"kafka_dump: bool\")\n    .Input(\"kafka_dump_prefix: bool\")\n    .Output(\"handle: variant\")\n    .Attr(\"compression_type: int = 0\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle unused;\n      TF_RETURN_IF_ERROR(c->WithRankAtMost(c->input(0), 1, &unused));\n      TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 0, &unused));\n      TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 0, &unused));\n      TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 0, &unused));\n      return shape_inference::ScalarShape(c);\n    });\n\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/instance_processor.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <iostream>\n\n#include \"absl/strings/str_format.h\"\n#include \"absl/time/clock.h\"\n#include \"gflags/gflags.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n#include \"monolith/native_training/data/training_instance/cc/data_reader.h\"\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nDEFINE_bool(kafka_dump, false, \"kafka_dump\");\nDEFINE_bool(kafka_dump_prefix, false, \"kafka_dump_prefix\");\nDEFINE_bool(has_sort_id, true, \"has_sort_id\");\nDEFINE_string(has_fids, \"\",\n              \"The instance of interest should contain at least one of the \"\n              \"given fids, or it will be dropped\");\nDEFINE_string(has_actions, \"\",\n              \"The instance of interest should contain at least one of the \"\n              \"given actions, or it will be dropped\");\nDEFINE_string(\n    filter_fids, \"\",\n    \"The instance will be dropped if it contains any one of the given fids.\");\nDEFINE_string(\n    select_fids, \"\",\n    \"The instance of interest should contain all of the given fids, or it \"\n    \"will be dropped.\");\nDEFINE_int64(\n    req_time_min, 0,\n    \"The instance of interest should satisfy line_id.req_time >= req_time_min\");\nDEFINE_int32(buffer_size, 32, \"The buffer number of instance\");\n\nusing tensorflow::Status;\nusing tensorflow::tstring;\nusing tensorflow::uint64;\nusing tensorflow::monolith_tf::IsInstanceOfInterest;\nusing ::tensorflow::monolith_tf::PBIterator;\nusing ::tensorflow::monolith_tf::DataFormatOptions;\nusing ::tensorflow::monolith_tf::StdinStreamReader;\nusing ::tensorflow::monolith_tf::StrToIntegerSet;\n\nint main(int argc, char* argv[]) {\n  ::gflags::ParseCommandLineFlags(&argc, &argv, true);\n  auto has_fids = StrToIntegerSet<uint64_t>(FLAGS_has_fids);\n  auto filter_fids = StrToIntegerSet<uint64_t>(FLAGS_filter_fids);\n  auto select_fids = StrToIntegerSet<uint64_t>(FLAGS_select_fids);\n  auto has_actions = StrToIntegerSet<int32_t>(FLAGS_has_actions);\n  absl::Time t = absl::FromUnixSeconds(FLAGS_req_time_min);\n\n  nlohmann::json json;\n  json[\"kafka_dump\"] = FLAGS_kafka_dump;\n  json[\"kafka_dump_prefix\"] = FLAGS_kafka_dump_prefix;\n  json[\"has_sort_id\"] = FLAGS_has_sort_id;\n  json[\"has_fids\"] = has_fids;\n  json[\"filter_fids\"] = filter_fids;\n  json[\"select_fids\"] = select_fids;\n  json[\"has_actions\"] = has_actions;\n  json[\"req_time_min\"] = FLAGS_req_time_min;\n  json[\"req_time_min_human_readable\"] = absl::FormatTime(t);\n  std::cerr << absl::StrFormat(\"%s Instance processor config:\\n%s\",\n                               absl::FormatTime(absl::Now()), json.dump(2))\n            << std::endl;\n\n  DataFormatOptions options;\n  options.kafka_dump = FLAGS_kafka_dump;\n  options.kafka_dump_prefix = FLAGS_kafka_dump_prefix;\n  options.has_sort_id = FLAGS_has_sort_id;\n  PBIterator reader(std::make_unique<StdinStreamReader>(options),\n                    tensorflow::monolith_tf::PRUNING_RAW_FEATURE);\n\n  uint64 offset = 0, count = 0, total = 0;\n  tstring sort_id, serialized_instance;\n  std::stringstream ss;\n  uint32_t data_source_key;\n  while (reader.next(&offset, &data_source_key, &serialized_instance) ==\n         Status::OK()) {\n    offset = reader.GetOffset();\n    parser::proto::Instance instance;\n    instance.ParseFromArray(serialized_instance.data(),\n                            serialized_instance.size());\n    ++total;\n    if (IsInstanceOfInterest(instance, filter_fids, has_fids, select_fids,\n                             has_actions, FLAGS_req_time_min, {})) {\n      std::string serialized_instance = instance.SerializeAsString();\n      uint64_t size_of_sort_id = sort_id.length();\n      uint64_t size_of_pb = serialized_instance.length();\n      ss.write(reinterpret_cast<char*>(&size_of_sort_id),\n               sizeof(size_of_sort_id));\n      ss.write(sort_id.data(), sort_id.length());\n      ss.write(reinterpret_cast<char*>(&size_of_pb), sizeof(size_of_pb));\n      ss.write(const_cast<char*>(serialized_instance.data()),\n               serialized_instance.length());\n      ++count;\n\n      if (count % FLAGS_buffer_size == 0) {\n        std::string output = ss.str();\n        ss.str(\"\");\n        std::cout.write(output.data(), output.length());\n        std::cout.flush();\n      }\n    }\n\n    if (total % 1000000 == 0) {\n      std::cerr\n          << absl::StrFormat(\n                 \"%s Instance processor input_num = %ld, output_num = %ld.\",\n                 absl::FormatTime(absl::Now()), total, count)\n          << std::endl;\n    }\n  }\n\n  if (count % FLAGS_buffer_size) {\n    std::string output = ss.str();\n    std::cout.write(output.data(), output.length());\n    std::cout.flush();\n  }\n\n  std::cerr << absl::StrFormat(\n                   \"%s Instance processor input_num = %ld, output_num = %ld. \"\n                   \"Successfully finished!\",\n                   absl::FormatTime(absl::Now()), total, count)\n            << std::endl;\n\n  return 0;\n}\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/instance_reader.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <iostream>\n#include <queue>\n#include \"absl/strings/str_format.h\"\n#include \"absl/time/clock.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n#include \"monolith/native_training/data/training_instance/cc/data_reader.h\"\n#include \"monolith/native_training/data/training_instance/cc/fid.h\"\n#include \"monolith/native_training/data/transform/cc/transforms.h\"\n#include \"monolith/native_training/data/transform/transform_config.pb.h\"\n#include \"tensorflow/core/platform/base64.h\"\n#include \"third_party/cli11/CLI11.hpp\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tf = tensorflow;\nusing idl::matrix::proto::LineId;\nusing monolith::io::proto::Example;\nusing monolith::io::proto::ExampleBatch;\nusing monolith::io::proto::Feature;\nusing monolith::native_training::data::TransformConfig;\nusing parser::proto::Instance;\nusing tf::monolith_tf::FeatureNameMapper;\nusing tf::monolith_tf::FeaturePruningType;\nusing tf::monolith_tf::FileStreamReader;\nusing tf::monolith_tf::InputCompressType;\nusing tf::monolith_tf::PBIterator;\nusing tf::monolith_tf::StdinStreamReader;\nusing tf::monolith_tf::TransformInterface;\n\nstruct Options {\n  int verbose_level = 0;\n  std::string filepath;\n  std::string dtype = \"instance\";\n  std::string compression_type = \"none\";\n  std::string config;\n  bool lagrangex_header = false;\n  bool kafka_dump = false;\n  bool kafka_dump_prefix = false;\n  bool has_sort_id = true;\n  int64_t limit = std::numeric_limits<int64_t>::max();\n  std::string output_format = \"json\";\n  bool silent = false;\n};\n\nvoid AddOptions(CLI::App& app, Options* options) {\n  app.add_option(\"-v,--verbose\", options->verbose_level,\n                 \"Verbose level, default: 0\");\n  app.add_option(\"-i,--input\", options->filepath,\n                 \"Input filepath, read from stdin if empty!\");\n  app.add_option(\"-d,--dtype\", options->dtype,\n                 \"Data type, default: instance, choices = [instance, example, \"\n                 \"example_batch]\")\n      ->check([](std::string choice) {\n        std::unordered_set<std::string> choices = {\"instance\", \"example\",\n                                                   \"example_batch\"};\n        if (!choices.count(choice)) {\n          return absl::StrFormat(\"Invalid dtype: %s\", choice);\n        }\n        return std::string();\n      });\n  app.add_option(\"-c,--compression_type\", options->compression_type,\n                 \"Compression type, default: none, choices = [none, snappy]\")\n      ->check([](std::string choice) {\n        std::unordered_set<std::string> choices = {\"none\", \"snappy\"};\n        if (!choices.count(choice)) {\n          return absl::StrFormat(\"Invalid compression_type: %s\", choice);\n        }\n        return std::string();\n      });\n  app.add_option(\"--lagrangex_header\", options->lagrangex_header,\n                 \"default: false\");\n  app.add_option(\"-k,--kafka_dump\", options->kafka_dump, \"default: false\");\n  app.add_option(\"--kafka_dump_prefix\", options->kafka_dump_prefix,\n                 \"default: false\");\n  app.add_option(\"--has_sort_id\", options->has_sort_id, \"default: true\");\n  app.add_option(\"--config\", options->config,\n                 \"Transform config, plain text, e.g. configs { basic_config { \"\n                 \"filter_by_fid { select_fids: 18428264561369945341 } } }\");\n  app.add_option(\"-l,--limit\", options->limit,\n                 \"Output limit number records, default: inf\");\n  app.add_option(\"-f,--format\", options->output_format,\n                 \"Output format, default: json, choices = [json, pbtxt]\")\n      ->check([](std::string choice) {\n        std::unordered_set<std::string> choices = {\"json\", \"pbtxt\"};\n        if (!choices.count(choice)) {\n          return absl::StrFormat(\"Invalid output format: %s\", choice);\n        }\n        return std::string();\n      });\n  app.add_option(\"--silent\", options->silent,\n                 \"Output nothing but statistics information.\");\n}\n\nclass InputReader {\n public:\n  explicit InputReader(const Options& options, TransformConfig config)\n      : config_(std::move(config)),\n        offset_(0),\n        total_(0),\n        count_(0),\n        end_of_sequence_(false) {\n    tf::monolith_tf::DataFormatOptions ds_options{\n        options.lagrangex_header, options.kafka_dump_prefix,\n        options.has_sort_id, options.kafka_dump};\n\n    tf::Env* env = tf::Env::Default();\n    std::unique_ptr<tf::monolith_tf::BaseStreamReader> stream_reader;\n    if (options.filepath.empty()) {\n      stream_reader = std::make_unique<StdinStreamReader>(ds_options);\n    } else {\n      std::unique_ptr<tf::RandomAccessFile> f;\n      TF_CHECK_OK(env->NewRandomAccessFile(options.filepath, &f));\n      stream_reader = std::make_unique<FileStreamReader>(\n          ds_options, std::move(f),\n          options.compression_type == \"none\" ? InputCompressType::NO\n                                             : InputCompressType::SNAPPY);\n    }\n\n    if (options.dtype == \"instance\" || options.dtype == \"example\") {\n      reader_ = absl::make_unique<tf::monolith_tf::PBIterator>(\n          std::move(stream_reader), FeaturePruningType::AS_IS);\n    } else {\n      mapper_ = std::make_unique<FeatureNameMapper>();\n      reader_ = absl::make_unique<tf::monolith_tf::ExampleBatchIterator>(\n          std::move(stream_reader), FeaturePruningType::AS_IS, mapper_.get());\n    }\n\n    transform_ = tf::monolith_tf::NewTransformFromConfig(config_);\n  }\n\n  template <typename T>\n  bool ReadOne(T* output) {\n    if (IsBufferEmpty<T>()) {\n      tf::tstring serialized_instance;\n      try {\n        uint32_t data_source_key = 0;\n        while (!end_of_sequence_ &&\n               reader_->next(&offset_, &data_source_key, &serialized_instance)\n                   .ok()) {\n          offset_ = reader_->GetOffset();\n          std::shared_ptr<T> sample = std::make_shared<T>();\n          if (!sample->ParseFromArray(serialized_instance.data(),\n                                      serialized_instance.size())) {\n            LOG(ERROR) << \"Unable to parse data. Data might be corrupted\";\n            return false;\n          }\n\n          ++total_;\n          std::vector<std::shared_ptr<T>> outputs;\n          Transform(sample, &outputs);\n          count_ += outputs.size();\n          for (const auto& sample : outputs) {\n            PushIntoBuffer<T>(sample);\n          }\n\n          if (!outputs.empty()) {\n            break;\n          }\n        }\n      } catch (const std::out_of_range& e) {\n        end_of_sequence_ = true;\n        LOG(INFO) << e.what();\n      } catch (const std::exception& e) {\n        end_of_sequence_ = true;\n        LOG(ERROR) << e.what();\n      }\n    }\n\n    if (!IsBufferEmpty<T>()) {\n      std::shared_ptr<T> front;\n      PopFromBuffer(&front);\n      front->Swap(output);\n      return true;\n    }\n\n    return false;\n  }\n\n  template <typename T>\n  bool ReadOneSerialized(std::string* serialized) {\n    T t;\n    bool success = ReadOne(&t);\n    if (success) {\n      *serialized = t.SerializeAsString();\n    }\n    return success;\n  }\n\n private:\n  template <typename T>\n  typename std::enable_if<std::is_same<T, Instance>::value, void>::type\n  Transform(std::shared_ptr<T> input, std::vector<std::shared_ptr<T>>* output) {\n    transform_->Transform(input, output);\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, Example>::value, void>::type\n  Transform(std::shared_ptr<T> input, std::vector<std::shared_ptr<T>>* output) {\n    transform_->Transform(input, output);\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, ExampleBatch>::value, void>::type\n  Transform(std::shared_ptr<T> input, std::vector<std::shared_ptr<T>>* output) {\n    output->push_back(input);\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, Instance>::value, bool>::type\n  IsBufferEmpty() {\n    return instance_buffer_.empty();\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, Example>::value, bool>::type\n  IsBufferEmpty() {\n    return example_buffer_.empty();\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, ExampleBatch>::value, bool>::type\n  IsBufferEmpty() {\n    return example_batch_buffer_.empty();\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, Instance>::value, void>::type\n  PushIntoBuffer(std::shared_ptr<T> t) {\n    instance_buffer_.push(t);\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, Example>::value, void>::type\n  PushIntoBuffer(std::shared_ptr<T> t) {\n    example_buffer_.push(t);\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, ExampleBatch>::value, void>::type\n  PushIntoBuffer(std::shared_ptr<T> t) {\n    example_batch_buffer_.push(t);\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, Instance>::value, void>::type\n  PopFromBuffer(std::shared_ptr<T>* t) {\n    *t = instance_buffer_.front();\n    instance_buffer_.pop();\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, Example>::value, void>::type\n  PopFromBuffer(std::shared_ptr<T>* t) {\n    *t = example_buffer_.front();\n    example_buffer_.pop();\n  }\n\n  template <typename T>\n  typename std::enable_if<std::is_same<T, ExampleBatch>::value, void>::type\n  PopFromBuffer(std::shared_ptr<T>* t) {\n    *t = example_batch_buffer_.front();\n    example_batch_buffer_.pop();\n  }\n\n  TransformConfig config_;\n  std::unique_ptr<TransformInterface> transform_;\n  std::unique_ptr<PBIterator> reader_;\n  std::unique_ptr<FeatureNameMapper> mapper_;\n  std::queue<std::shared_ptr<Instance>> instance_buffer_;\n  std::queue<std::shared_ptr<monolith::io::proto::Example>> example_buffer_;\n  std::queue<std::shared_ptr<monolith::io::proto::ExampleBatch>>\n      example_batch_buffer_;\n  tf::uint64 offset_;\n  uint64_t total_;\n  uint64_t count_;\n  bool end_of_sequence_;\n};\n\ntemplate <typename T>\nvoid ReadAndSerialize(\n    InputReader& reader, T* t, const Options& options,\n    const std::function<std::string(const std::string& serialized)>&\n        callback_fn) {\n  auto json_options = google::protobuf::util::JsonOptions();\n  json_options.add_whitespace = true;\n  json_options.preserve_proto_field_names = true;\n  for (int64_t i = 0; i < options.limit; ++i) {\n    if (reader.ReadOne(t)) {\n      if (!options.silent) {\n        std::string output;\n        if (options.output_format == \"json\") {\n          google::protobuf::util::MessageToJsonString(*t, &output,\n                                                      json_options);\n          output = callback_fn(output);\n        } else {\n          output = t->DebugString();\n        }\n\n        std::cout.write(output.data(), output.length());\n        std::cout.flush();\n      }\n    } else {\n      break;\n    }\n  }\n}\n\nvoid to_json(nlohmann::json& j, const FIDV1& fid) { j = fid.DebugString(); }\n\nvoid to_json(nlohmann::json& j, const FIDV2& fid) { j = fid.DebugString(); }\n\nstd::string JsonCallbackFn(const std::string& serialized) {\n  nlohmann::json json;\n  try {\n    json = nlohmann::json::parse(serialized);\n  } catch (const std::exception& e) {\n    LOG(FATAL) << e.what() << \"\\nserialized:\\n\" << serialized;\n  }\n\n  auto CollectFID = [](const nlohmann::json& j, std::vector<uint64_t>* fids) {\n    CHECK(j.is_array());\n    fids->reserve(j.size());\n    for (absl::string_view fid_str : j) {\n      uint64_t fid = 0;\n      CHECK(absl::SimpleAtoi(fid_str, &fid));\n      fids->emplace_back(fid);\n    }\n    std::sort(fids->begin(), fids->end());\n  };\n\n  // instance fid(v1)\n  if (json.contains(\"fid\") && json[\"fid\"].is_array()) {\n    std::vector<uint64_t> fids;\n    CollectFID(json[\"fid\"], &fids);\n    std::vector<FIDV1> fids_v1(fids.begin(), fids.end());\n    json[\"fid\"] = fids_v1;\n  }\n\n  // instance feature(v2)\n  if (json.contains(\"feature\") && json[\"feature\"].is_array()) {\n    for (nlohmann::json& element : json[\"feature\"]) {\n      if (element.contains(\"fid\") && element[\"fid\"].is_array()) {\n        std::vector<uint64_t> fids;\n        CollectFID(element[\"fid\"], &fids);\n        std::vector<FIDV2> fids_v2(fids.begin(), fids.end());\n        element[\"fid\"] = fids_v2;\n      }\n    }\n  }\n\n  auto ReplaceFeatureFn = [&](nlohmann::json& feature, bool is_line_id) {\n    if (feature.contains(\"fid_v1_list\")) {\n      nlohmann::json& fid_v1_list = feature[\"fid_v1_list\"];\n      if (fid_v1_list.contains(\"value\") && fid_v1_list[\"value\"].is_array()) {\n        std::vector<uint64_t> fids;\n        CollectFID(fid_v1_list[\"value\"], &fids);\n        std::vector<FIDV1> fids_v1(fids.begin(), fids.end());\n        fid_v1_list[\"value\"] = fids_v1;\n      }\n    } else if (feature.contains(\"fid_v2_list\")) {\n      nlohmann::json& fid_v2_list = feature[\"fid_v2_list\"];\n      if (fid_v2_list.contains(\"value\") && fid_v2_list[\"value\"].is_array()) {\n        std::vector<uint64_t> fids;\n        CollectFID(fid_v2_list[\"value\"], &fids);\n        std::vector<FIDV2> fids_v2(fids.begin(), fids.end());\n        fid_v2_list[\"value\"] = fids_v2;\n      }\n    } else if (feature.contains(\"bytes_list\") && is_line_id) {\n      nlohmann::json& bytes_list = feature[\"bytes_list\"];\n      if (bytes_list.contains(\"value\") && bytes_list[\"value\"].is_array()) {\n        CHECK_EQ(bytes_list[\"value\"].size(), 1);\n        LineId line_id;\n        std::string based64_encoded = bytes_list[\"value\"][0];\n        std::string serialized;\n        tf::Base64Decode(based64_encoded, &serialized);\n        CHECK(line_id.ParseFromArray(serialized.data(), serialized.size()))\n            << serialized;\n        auto json_options = google::protobuf::util::JsonOptions();\n        json_options.add_whitespace = true;\n        json_options.preserve_proto_field_names = true;\n        std::string output;\n        google::protobuf::util::MessageToJsonString(line_id, &output,\n                                                    json_options);\n        bytes_list[\"value\"] = nlohmann::json::parse(output);\n      }\n    }\n  };\n\n  // example named_feature\n  if (json.contains(\"named_feature\") && json[\"named_feature\"].is_array()) {\n    for (nlohmann::json& element : json[\"named_feature\"]) {\n      if (element.contains(\"feature\")) {\n        ReplaceFeatureFn(element[\"feature\"], false);\n      }\n    }\n  }\n\n  // ExampleBatch named_feature_list\n  if (json.contains(\"named_feature_list\") &&\n      json[\"named_feature_list\"].is_array()) {\n    for (nlohmann::json& element : json[\"named_feature_list\"]) {\n      if (element.contains(\"feature\") && element[\"feature\"].is_array()) {\n        bool is_line_id =\n            element.contains(\"name\") && element[\"name\"] == \"__LINE_ID__\";\n        for (nlohmann::json& feature : element[\"feature\"]) {\n          ReplaceFeatureFn(feature, is_line_id);\n        }\n      }\n    }\n  }\n\n  return json.dump(2);\n}\n\nint main(int argc, char* argv[]) {\n  CLI::App app(\"instance_reader\");\n  app.set_version_flag(\"--version\", \"0.0.1\");\n\n  Options options;\n  AddOptions(app, &options);\n  CLI11_PARSE(app, argc, argv)\n\n  if (options.dtype == \"example_batch\" && !options.config.empty()) {\n    LOG(FATAL) << \"Transform cannot process ExampleBatch!\";\n  }\n  TransformConfig config;\n  CHECK(google::protobuf::TextFormat::ParseFromString(options.config, &config));\n  std::cerr << config.DebugString();\n\n  InputReader reader(options, config);\n  std::string output;\n\n  if (options.dtype == \"instance\") {\n    Instance instance;\n    ReadAndSerialize(reader, &instance, options, JsonCallbackFn);\n  } else if (options.dtype == \"example\") {\n    Example example;\n    ReadAndSerialize(reader, &example, options, JsonCallbackFn);\n  } else if (options.dtype == \"example_batch\") {\n    ExampleBatch example_batch;\n    ReadAndSerialize(reader, &example_batch, options, JsonCallbackFn);\n  } else {\n    throw std::invalid_argument(\n        absl::StrFormat(\"Invalid dtype=%s\", options.dtype));\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/instance_utils.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/instance_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_INSTANCE_UTILS_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_INSTANCE_UTILS_H_\n\n#include <set>\n#include \"absl/strings/numbers.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_split.h\"\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntemplate <typename T>\nstd::set<T> StrToIntegerSet(const std::string& str) {\n  static_assert(\n      std::is_same<T, uint64_t>::value || std::is_same<T, int32_t>::value,\n      \"Template typename T should be uint64_t or int32_t!\");\n  std::set<T> integers;\n  std::set<std::string> splits = absl::StrSplit(str, \",\");\n  for (const auto& s : splits) {\n    if (!s.empty()) {\n      T fid;\n      if (absl::SimpleAtoi(s, &fid)) {\n        integers.insert(fid);\n      } else {\n        throw std::invalid_argument(\n            absl::StrFormat(\"Invalid integer string: %s\", s));\n      }\n    }\n  }\n\n  return integers;\n}\n\ntemplate <typename T>\ntypename std::enable_if<std::is_same<T, parser::proto::Instance>::value,\n                        void>::type\nCollectFidIntoSet(const T& instance, std::set<uint64_t>* fid_set) {\n  const auto& instance_fids = instance.fid();\n  fid_set->insert(instance_fids.begin(), instance_fids.end());\n}\n\ntemplate <typename T>\ntypename std::enable_if<std::is_same<T, monolith::io::proto::Example>::value,\n                        void>::type\nCollectFidIntoSet(const T& example, std::set<uint64_t>* fid_set) {\n  for (const auto& named_feature : example.named_feature()) {\n    if (named_feature.feature().has_fid_v1_list()) {\n      const auto& fids = named_feature.feature().fid_v1_list().value();\n      fid_set->insert(fids.begin(), fids.end());\n    }\n    if (named_feature.feature().has_fid_v2_list()) {\n      const auto& fids = named_feature.feature().fid_v2_list().value();\n      fid_set->insert(fids.begin(), fids.end());\n    }\n  }\n}\n\ntemplate <typename T>\ntypename std::enable_if<std::is_same<T, parser::proto::Instance>::value,\n                        void>::type\nCollectSlotIntoSet(const T& instance, std::set<uint32_t>* slot_set) {\n  for (uint64_t fid : instance.fid()) {\n    int slot = slot_id_v1(fid);\n    slot_set->insert(slot);\n  }\n\n  for (const auto& f : instance.feature()) {\n    for (uint64_t fid : f.fid()) {\n      int slot = slot_id_v2(fid);\n      slot_set->insert(slot);\n    }\n  }\n}\n\ntemplate <typename T>\ntypename std::enable_if<std::is_same<T, monolith::io::proto::Example>::value,\n                        void>::type\nCollectSlotIntoSet(const T& example, std::set<uint32_t>* slot_set) {\n  for (const auto& named_feature : example.named_feature()) {\n    if (named_feature.feature().has_fid_v1_list()) {\n      const auto& fids = named_feature.feature().fid_v1_list().value();\n      for (uint64_t fid : fids) {\n        int slot = slot_id_v1(fid);\n        slot_set->insert(slot);\n      }\n    }\n\n    if (named_feature.feature().has_fid_v2_list()) {\n      const auto& fids = named_feature.feature().fid_v2_list().value();\n      for (uint64_t fid : fids) {\n        int slot = slot_id_v2(fid);\n        slot_set->insert(slot);\n      }\n    }\n  }\n}\n\ntemplate <typename T>\nbool IsInstanceOfInterest(const T& pb, const std::set<uint64_t>& filter_fids,\n                          const std::set<uint64_t>& has_fids,\n                          const std::set<uint64_t>& select_fids,\n                          const std::set<int32_t>& has_actions,\n                          int64_t req_time_min,\n                          const std::set<uint32_t>& select_slots) {\n  if (pb.line_id().req_time() < req_time_min) {\n    return false;\n  }\n\n  std::set<uint64_t> fid_set;\n  CollectFidIntoSet(pb, &fid_set);\n\n  std::set<uint32_t> slot_set;\n  CollectSlotIntoSet(pb, &slot_set);\n\n  const auto& actions = pb.line_id().actions();\n  std::set<int32_t> instance_actions_set(actions.begin(), actions.end());\n\n  if (!filter_fids.empty()) {\n    std::set<uint64_t> intersection;\n    std::set_intersection(fid_set.begin(), fid_set.end(), filter_fids.begin(),\n                          filter_fids.end(),\n                          std::inserter(intersection, intersection.begin()));\n    // If the instance contains any one of the given `filter_fids`, it will be\n    // dropped.\n    if (!intersection.empty()) {\n      return false;\n    }\n  }\n\n  if (!has_fids.empty()) {\n    std::set<uint64_t> intersection;\n    std::set_intersection(fid_set.begin(), fid_set.end(), has_fids.begin(),\n                          has_fids.end(),\n                          std::inserter(intersection, intersection.begin()));\n    // If the instance does not contain any one of the given `has_fids`, it will\n    // be dropped.\n    if (intersection.empty()) {\n      return false;\n    }\n  }\n\n  if (!select_fids.empty()) {\n    std::set<uint64_t> intersection;\n    std::set_intersection(fid_set.begin(), fid_set.end(), select_fids.begin(),\n                          select_fids.end(),\n                          std::inserter(intersection, intersection.begin()));\n    // If the instance does not contain all of the given `select_fids`, it will\n    // be dropped.\n    if (intersection.size() < select_fids.size()) {\n      return false;\n    }\n  }\n\n  if (!select_slots.empty()) {\n    std::set<uint32_t> intersection;\n    std::set_intersection(slot_set.begin(), slot_set.end(),\n                          select_slots.begin(), select_slots.end(),\n                          std::inserter(intersection, intersection.begin()));\n    // If the instance does not contain all of the given `select_slots`, it will\n    // be dropped.\n    if (intersection.size() < select_slots.size()) {\n      return false;\n    }\n  }\n\n  if (!has_actions.empty()) {\n    std::set<int32_t> intersection;\n    std::set_intersection(instance_actions_set.begin(),\n                          instance_actions_set.end(), has_actions.begin(),\n                          has_actions.end(),\n                          std::inserter(intersection, intersection.begin()));\n    // If the instance does not contain any one of the given `has_actions`, it\n    // will be dropped.\n    if (intersection.empty()) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_INSTANCE_UTILS_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/instance_utils_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n\n#include \"absl/time/clock.h\"\n#include \"glog/logging.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nusing ::testing::Eq;\n\nTEST(StrToIntegerSet, StrToFIDs) {\n  std::set<uint64_t> fids1 = StrToIntegerSet<uint64_t>(\"\");\n  EXPECT_TRUE(fids1.empty());\n\n  std::set<uint64_t> fids2 = StrToIntegerSet<uint64_t>(\"6461985998153810495\");\n  EXPECT_EQ(fids2.size(), 1);\n  EXPECT_EQ(*fids2.begin(), 6461985998153810495ull);\n\n  std::set<uint64_t> fids3 =\n      StrToIntegerSet<uint64_t>(\"6457882839108881377,6436927642569553426,\");\n  EXPECT_EQ(fids3.size(), 2);\n  EXPECT_EQ(*fids3.begin(), 6436927642569553426ull);\n  EXPECT_EQ(*std::next(fids3.begin(), 1), 6457882839108881377ul);\n\n  std::set<uint64_t> fids4 = StrToIntegerSet<uint64_t>(\"6461985998153810495,\");\n  EXPECT_EQ(fids4.size(), 1);\n  EXPECT_EQ(*fids4.begin(), 6461985998153810495ull);\n\n  try {\n    StrToIntegerSet<uint64_t>(\"6461985998153810495,abc\");\n  } catch (const std::invalid_argument& e) {\n    EXPECT_THAT(std::string(e.what()), Eq(\"Invalid integer string: abc\"));\n  } catch (const std::exception& e) {\n    LOG(ERROR) << \"Unexpected exception thrown: \" << e.what() << std::endl;\n  }\n}\n\nTEST(StrToIntegerSet, StrToActions) {\n  std::set<int32_t> actions1 = StrToIntegerSet<int32_t>(\"\");\n  EXPECT_TRUE(actions1.empty());\n\n  std::set<int32_t> actions2 = StrToIntegerSet<int32_t>(\"-1\");\n  EXPECT_EQ(actions2.size(), 1);\n  EXPECT_EQ(*actions2.begin(), -1);\n\n  std::set<int32_t> actions3 = StrToIntegerSet<int32_t>(\"-1,3,\");\n  EXPECT_EQ(actions3.size(), 2);\n  EXPECT_EQ(*actions3.begin(), -1);\n  EXPECT_EQ(*std::next(actions3.begin(), 1), 3);\n\n  std::set<int32_t> actions4 = StrToIntegerSet<int32_t>(\"1,\");\n  EXPECT_EQ(actions4.size(), 1);\n  EXPECT_EQ(*actions4.begin(), 1);\n\n  try {\n    StrToIntegerSet<int32_t>(\"1,abc\");\n  } catch (const std::invalid_argument& e) {\n    EXPECT_THAT(std::string(e.what()), Eq(\"Invalid integer string: abc\"));\n  } catch (const std::exception& e) {\n    LOG(ERROR) << \"Unexpected exception thrown: \" << e.what() << std::endl;\n  }\n}\n\nTEST(IsInstanceOfInterest, Basic) {\n  parser::proto::Instance instance;\n  instance.mutable_fid()->Add(6436927642569553426ull);\n  instance.mutable_fid()->Add(6457882839108881377ull);\n  instance.mutable_fid()->Add(6461985998153810495ull);\n\n  int64_t now = absl::ToUnixSeconds(absl::Now());\n  instance.mutable_line_id()->set_req_time(now);\n  instance.mutable_line_id()->mutable_actions()->Add(-1);\n  instance.mutable_line_id()->mutable_actions()->Add(1);\n\n  std::set<uint64_t> filter_fids = {6436927642569553426ull};\n  std::set<uint64_t> has_fids = {6436927642569553426ull};\n  std::set<uint64_t> select_fids = {6436927642569553426ull,\n                                    6457882839108881377ull};\n\n  // 1626537600 -> 2021-07-18 00:00:00\n  int64_t req_time_min = 1626537600;\n  EXPECT_TRUE(IsInstanceOfInterest(instance, {}, {}, {}, {}, req_time_min, {}));\n  EXPECT_TRUE(IsInstanceOfInterest(instance, {}, {}, {}, {}, now, {}));\n  EXPECT_TRUE(!IsInstanceOfInterest(instance, {}, {}, {}, {}, now + 1, {}));\n  EXPECT_TRUE(!IsInstanceOfInterest(instance, filter_fids, {}, {}, {},\n                                    req_time_min, {}));\n  EXPECT_TRUE(\n      IsInstanceOfInterest(instance, {}, has_fids, {}, {}, req_time_min, {}));\n  EXPECT_TRUE(IsInstanceOfInterest(instance, {}, {}, select_fids, {},\n                                   req_time_min, {}));\n  EXPECT_TRUE(!IsInstanceOfInterest(instance, filter_fids, has_fids,\n                                    select_fids, {}, req_time_min, {}));\n  EXPECT_TRUE(\n      IsInstanceOfInterest(instance, {}, {}, {}, {-1}, req_time_min, {}));\n  EXPECT_TRUE(\n      IsInstanceOfInterest(instance, {}, {}, {}, {1, 5}, req_time_min, {}));\n  EXPECT_TRUE(IsInstanceOfInterest(instance, {}, has_fids, select_fids, {1, 5},\n                                   req_time_min, {}));\n}\n\nTEST(CollectFidIntoSet, Instance) {\n  parser::proto::Instance instance;\n  instance.mutable_fid()->Add(GetFidV1(2, 200));\n  instance.mutable_fid()->Add(GetFidV1(3, 300));\n\n  auto f1 = instance.mutable_feature()->Add();\n  f1->mutable_fid()->Add(GetFidV2(1024, 102400));\n  auto f2 = instance.mutable_feature()->Add();\n  f2->mutable_fid()->Add(GetFidV2(4096, 409600));\n\n  std::set<uint32_t> slots, slots_expected{2, 3, 1024, 4096}, intersection;\n  CollectSlotIntoSet(instance, &slots);\n  std::set_intersection(slots.begin(), slots.end(), slots_expected.begin(),\n                        slots_expected.end(),\n                        std::inserter(intersection, intersection.begin()));\n  EXPECT_EQ(intersection.size(), slots_expected.size());\n\n  std::set<uint32_t> select_slots1 = {2, 3, 1024, 4096},\n                     select_slots2 = {2, 10};\n  EXPECT_TRUE(IsInstanceOfInterest(instance, {}, {}, {}, {}, 0, select_slots1));\n  EXPECT_TRUE(\n      !IsInstanceOfInterest(instance, {}, {}, {}, {}, 0, select_slots2));\n}\n\nTEST(CollectFidIntoSet, Example) {\n  monolith::io::proto::Example example;\n\n  auto f1 = example.mutable_named_feature()->Add();\n  f1->set_name(\"user_id\");\n  f1->mutable_feature()->mutable_fid_v1_list()->mutable_value()->Add(\n      GetFidV1(2, 200));\n  auto f2 = example.mutable_named_feature()->Add();\n  f2->set_name(\"item_id\");\n  f2->mutable_feature()->mutable_fid_v1_list()->mutable_value()->Add(\n      GetFidV1(3, 300));\n  auto f3 = example.mutable_named_feature()->Add();\n  f3->set_name(\"gender\");\n  f3->mutable_feature()->mutable_fid_v2_list()->mutable_value()->Add(\n      GetFidV2(1024, 102400));\n  auto f4 = example.mutable_named_feature()->Add();\n  f4->set_name(\"age\");\n  f4->mutable_feature()->mutable_fid_v2_list()->mutable_value()->Add(\n      GetFidV2(4096, 409600));\n\n  std::set<uint32_t> slots, slots_expected{2, 3, 1024, 4096}, intersection;\n  CollectSlotIntoSet(example, &slots);\n  std::set_intersection(slots.begin(), slots.end(), slots_expected.begin(),\n                        slots_expected.end(),\n                        std::inserter(intersection, intersection.begin()));\n  EXPECT_EQ(intersection.size(), slots_expected.size());\n\n  std::set<uint32_t> select_slots1 = {2, 3, 1024, 4096},\n                     select_slots2 = {2, 10};\n  EXPECT_TRUE(IsInstanceOfInterest(example, {}, {}, {}, {}, 0, select_slots1));\n  EXPECT_TRUE(!IsInstanceOfInterest(example, {}, {}, {}, {}, 0, select_slots2));\n}\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/parse_instance_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <algorithm>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"google/protobuf/descriptor.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n#include \"monolith/native_training/data/training_instance/cc/parse_instance_lib.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/platform/env.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nusing Instance = ::parser::proto::Instance;\n\nStatus GetParserConfig(OpKernelConstruction *ctx, InstanceParserConfig *c) {\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"fidv1_features\", &c->fidv1_features));\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"fidv2_features\", &c->fidv2_features));\n  TF_RETURN_IF_ERROR(\n      ctx->GetAttr(\"float_feature_dims\", &c->float_feature_dims));\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"float_features\", &c->float_features));\n  if (c->float_features.size() != c->float_feature_dims.size()) {\n    return errors::InvalidArgument(\n        \"Num of float features and float feature dims do not match\");\n  }\n\n  TF_RETURN_IF_ERROR(\n      ctx->GetAttr(\"int64_feature_dims\", &c->int64_feature_dims));\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"int64_features\", &c->int64_features));\n  if (c->int64_features.size() != c->int64_feature_dims.size()) {\n    return errors::InvalidArgument(\n        \"Num of int64 features and int64 feature dims do not match\");\n  }\n\n  TF_RETURN_IF_ERROR(\n      ctx->GetAttr(\"string_feature_dims\", &c->string_feature_dims));\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"string_features\", &c->string_features));\n  if (c->string_features.size() != c->string_feature_dims.size()) {\n    return errors::InvalidArgument(\n        \"Num of string features and string feature dims do not match\");\n  }\n\n  TF_RETURN_IF_ERROR(\n      ctx->GetAttr(\"misc_float_features\", &c->misc_float_features));\n\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"misc_float_dims\", &c->misc_float_dims));\n\n  if (c->misc_float_features.size() != c->misc_float_dims.size()) {\n    return errors::InvalidArgument(\n        \"Num of float features do not match it dims the size of \"\n        \"misc_float_features is \",\n        c->misc_float_features.size(),\n        \", while the size of misc_float_dims is \", c->misc_float_dims.size());\n  }\n\n  TF_RETURN_IF_ERROR(\n      ctx->GetAttr(\"misc_int64_features\", &c->misc_int64_features));\n\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"misc_int64_dims\", &c->misc_int64_dims));\n\n  if (c->misc_int64_features.size() != c->misc_int64_dims.size()) {\n    return errors::InvalidArgument(\n        \"Num of features do not match it dims the size of \"\n        \"misc_features is \",\n        c->misc_int64_features.size(), \", while the size of misc_dims is \",\n        c->misc_int64_dims.size());\n  }\n\n  TF_RETURN_IF_ERROR(\n      ctx->GetAttr(\"misc_string_features\", &c->misc_string_features));\n\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"misc_string_dims\", &c->misc_string_dims));\n\n  if (c->misc_string_features.size() != c->misc_string_dims.size()) {\n    return errors::InvalidArgument(\n        \"Num of features do not match it dims the size of \"\n        \"misc_features is \",\n        c->misc_string_features.size(), \", while the size of misc_dims is \",\n        c->misc_string_dims.size());\n  }\n\n  return Status::OK();\n}\n\nbool ParseInstance(const tstring &serialized, Instance *instance) {\n  return instance->ParseFromArray(serialized.data(), serialized.size());\n}\n\nclass ParseInstancesOp : public OpKernel {\n public:\n  explicit ParseInstancesOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    InstanceParserConfig config;\n    OP_REQUIRES_OK(ctx, GetParserConfig(ctx, &config));\n    config.collapse_batch_dim = false;\n\n    parser_ = std::make_unique<InstanceParser>(config);\n    OP_REQUIRES_OK(ctx, parser_->Init());\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    const Tensor *serialized;\n    OP_REQUIRES_OK(ctx, ctx->input(\"serialized\", &serialized));\n    TTypes<tstring>::ConstVec serialized_protos = serialized->vec<tstring>();\n    const int batch_size = serialized_protos.dimension(0);\n    std::vector<Instance> instances(batch_size);\n\n    for (int i = 0; i < batch_size; ++i) {\n      OP_REQUIRES(ctx, ParseInstance(serialized_protos(i), &instances[i]),\n                  errors::FailedPrecondition(\"Failed to parse the Instance.\"));\n    }\n\n    InstanceParser::Output output;\n    OP_REQUIRES_OK(ctx, parser_->Parse(ctx, instances, &output));\n    for (int i = 0; i < static_cast<int>(output.tensors.size()); ++i) {\n      ctx->set_output(i, output.tensors[i]);\n    }\n  }\n\n private:\n  std::unique_ptr<InstanceParser> parser_;\n};\n\n// This class is mainly for testing parser.\n// Do not use in the model code directly.\nclass RawParseInstanceOp : public OpKernel {\n public:\n  explicit RawParseInstanceOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    InstanceParserConfig config;\n    OP_REQUIRES_OK(ctx, GetParserConfig(ctx, &config));\n    OP_REQUIRES_OK(\n        ctx, ctx->GetAttr(\"collapse_batch_dim\", &config.collapse_batch_dim));\n    std::string fid_output_type;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"fid_output_type\", &fid_output_type));\n    absl::flat_hash_map<std::string, InstanceParserConfig::FidOutputType>\n        str_to_enum = {\n            {\"REGULAR\", InstanceParserConfig::REGULAR},\n            {\"CONCAT\", InstanceParserConfig::CONCAT},\n        };\n    config.fid_output_type = str_to_enum.at(fid_output_type);\n    parser_ = std::make_unique<InstanceParser>(config);\n    OP_REQUIRES_OK(ctx, parser_->Init());\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    // Grab the input tensor\n    const Tensor *serialized;\n    OP_REQUIRES_OK(ctx, ctx->input(\"serialized\", &serialized));\n\n    auto serialized_flat = serialized->flat<tstring>();\n    std::vector<Instance> instances(serialized_flat.size());\n    for (size_t i = 0; i < instances.size(); ++i) {\n      OP_REQUIRES(ctx, ParseInstance(serialized_flat(i), &instances[i]),\n                  errors::FailedPrecondition(\"Failed to parse the Instance.\"));\n    }\n\n    InstanceParser::Output output;\n    OP_REQUIRES_OK(ctx, parser_->Parse(ctx, instances, &output));\n    OpOutputList l;\n    ctx->output_list(\"tensors\", &l);\n    for (size_t i = 0; i < output.tensors.size(); ++i) {\n      l.set(i, output.tensors[i]);\n    }\n  }\n\n private:\n  std::unique_ptr<InstanceParser> parser_;\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithParseInstances\").Device(DEVICE_CPU),\n                        ParseInstancesOp);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithRawParseInstance\").Device(DEVICE_CPU),\n                        RawParseInstanceOp);\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/parse_instance_lib.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <functional>\n#include <memory>\n#include <vector>\n\n#include \"glog/logging.h\"\n#include \"tensorflow/core/platform/errors.h\"\n\n#include \"idl/matrix/compression/float16.h\"\n#include \"monolith/native_training/data/training_instance/cc/parse_instance_lib.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"monolith/native_training/data/training_instance/cc/ue_compress.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nusing ::google::protobuf::FieldDescriptor;\nusing ::idl::matrix::proto::Feature;\nusing ::parser::proto::Instance;\nusing tensorflow::monolith_tf::UECompress;\n\n// The spec that will be used by parser.\n// It includes some preprocessed data\nstruct InstanceParserSpec : InstanceParserConfig {\n  explicit InstanceParserSpec(const InstanceParserConfig &config)\n      : InstanceParserConfig(config) {}\n\n  Status Init() {\n    fidv1_features_set = {fidv1_features.begin(), fidv1_features.end()};\n    fidv2_features_set = {fidv2_features.begin(), fidv2_features.end()};\n    int index = 0;\n    for (int slot : fidv1_features) {\n      slot_to_index[slot] = index++;\n    }\n    for (const std::string &name : fidv2_features) {\n      fidv2_name_to_index[name] = index++;\n    }\n    n_ragged_tensors = fidv1_features.size() + fidv2_features.size();\n\n    float_features_set = {float_features.begin(), float_features.end()};\n    for (size_t i = 0; i < float_features.size(); ++i) {\n      float_feature_name_to_index[float_features[i]] = i;\n    }\n    n_float_tensors = float_features.size();\n\n    int64_features_set = {int64_features.begin(), int64_features.end()};\n    for (size_t i = 0; i < int64_features.size(); ++i) {\n      int64_feature_name_to_index[int64_features[i]] = i;\n    }\n    n_int64_tensors = int64_features.size();\n\n    string_features_set = {string_features.begin(), string_features.end()};\n    for (size_t i = 0; i < string_features.size(); ++i) {\n      string_feature_name_to_index[string_features[i]] = i;\n    }\n    n_string_tensors = string_features.size();\n\n    return Status::OK();\n  }\n\n  // Fid features attrs\n  absl::flat_hash_set<int> fidv1_features_set;\n  absl::flat_hash_map<int, int> slot_to_index;\n  absl::flat_hash_set<std::string> fidv2_features_set;\n  absl::flat_hash_map<std::string, int> fidv2_name_to_index;\n  int n_ragged_tensors;\n\n  // Float features attrs\n  int n_float_tensors;\n  absl::flat_hash_set<std::string> float_features_set;\n  absl::flat_hash_map<std::string, int> float_feature_name_to_index;\n\n  // Int64 features attrs\n  int n_int64_tensors;\n  absl::flat_hash_set<std::string> int64_features_set;\n  absl::flat_hash_map<std::string, int> int64_feature_name_to_index;\n\n  // String features attrs\n  int n_string_tensors;\n  absl::flat_hash_set<std::string> string_features_set;\n  absl::flat_hash_map<std::string, int> string_feature_name_to_index;\n};\n\nclass RaggedTensorProcessor {\n public:\n  explicit RaggedTensorProcessor(const InstanceParserSpec *spec)\n      : spec_(*spec) {}\n\n  virtual ~RaggedTensorProcessor() = default;\n\n  // Process the ragged tensor.\n  // The output will be added to the output.\n  virtual Status ParseRaggedTensors(OpKernelContext *ctx,\n                                    absl::Span<const Instance> instances,\n                                    InstanceParser::Output *output) = 0;\n\n protected:\n  const InstanceParserSpec &spec() const { return spec_; }\n\n  // A util function for child class to use.\n  template <typename Func>\n  void IterateFidFeatures(const Instance &instance, Func func) {\n    const bool apply_fid_v2 = !spec_.fidv2_features.empty();\n    for (const uint64_t fid : instance.fid()) {\n      int slot_id = slot_id_v1(fid);\n      if (!spec_.fidv1_features_set.contains(slot_id)) continue;\n      uint64_t converted_fid =\n          apply_fid_v2 ? convert_fid_v1_to_v2(slot_id, fid) : fid;\n      func(spec_.slot_to_index.at(slot_id), converted_fid);\n    }\n\n    // Feature v2 should never have 2 features with the same feature name.\n    for (const auto &feature : instance.feature()) {\n      if (!spec_.fidv2_features_set.contains(feature.name())) continue;\n      // This is a simple sample list.\n      for (const auto &fid : feature.fid()) {\n        func(spec_.fidv2_name_to_index.at(feature.name()), fid);\n      }\n      // this is a sequence feature list.\n      for (const auto &fidlist : feature.fid_list()) {\n        for (const auto &fid : fidlist.value()) {\n          func(spec_.fidv2_name_to_index.at(feature.name()), fid);\n        }\n      }\n    }\n  }\n\n private:\n  const InstanceParserSpec &spec_;\n};\n\nclass RegularRaggedTensorProcessor : public RaggedTensorProcessor {\n public:\n  explicit RegularRaggedTensorProcessor(const InstanceParserSpec *spec)\n      : RaggedTensorProcessor(spec) {}\n\n  Status ParseRaggedTensors(OpKernelContext *ctx,\n                            absl::Span<const Instance> instances,\n                            InstanceParser::Output *output) override {\n    int batch_size = instances.size();\n    std::vector<TTypes<int64>::Vec> splits_vec;\n    splits_vec.reserve(spec().n_ragged_tensors);\n    for (int i = 0; i < spec().n_ragged_tensors; ++i) {\n      output->tensors.emplace_back();\n      Tensor *t = &output->tensors.back();\n      TF_RETURN_IF_ERROR(ctx->allocate_temp(DT_INT64, {batch_size + 1}, t));\n      auto vec = t->vec<int64>();\n      vec(0) = 0;\n      splits_vec.emplace_back(vec);\n    }\n\n    std::vector<int> nums(spec().n_ragged_tensors);\n    for (int i = 0; i < batch_size; ++i) {\n      IterateFidFeatures(instances[i],\n                         [&nums](int idx, int64_t fid) { nums[idx]++; });\n      for (int j = 0; j < spec().n_ragged_tensors; ++j) {\n        splits_vec[j](i + 1) = nums[j];\n      }\n    }\n\n    std::vector<TTypes<int64>::Vec> values_vec;\n    for (int i = 0; i < spec().n_ragged_tensors; ++i) {\n      output->tensors.emplace_back();\n      Tensor *t = &output->tensors.back();\n      TF_RETURN_IF_ERROR(\n          ctx->allocate_temp(DT_INT64, {splits_vec[i](batch_size)}, t));\n      values_vec.emplace_back(t->vec<int64>());\n    }\n\n    std::fill(nums.begin(), nums.end(), 0);\n    for (int i = 0; i < batch_size; ++i) {\n      IterateFidFeatures(instances[i],\n                         [&nums, &values_vec](int idx, int64_t fid) {\n                           values_vec[idx](nums[idx]) = fid;\n                           nums[idx]++;\n                         });\n    }\n\n    return Status::OK();\n  }\n};\n\nclass ConcatRaggedTensorProcessor : public RaggedTensorProcessor {\n public:\n  explicit ConcatRaggedTensorProcessor(const InstanceParserSpec *spec)\n      : RaggedTensorProcessor(spec) {}\n\n  Status ParseRaggedTensors(OpKernelContext *ctx,\n                            absl::Span<const Instance> instances,\n                            InstanceParser::Output *output) override {\n    int batch_size = instances.size();\n    if (batch_size != 1) {\n      return errors::InvalidArgument(\n          \"ConcatRaggedTensorProcessor only support batch_size == 1\");\n    }\n    const Instance &instance = instances[0];\n    Tensor t;\n    TF_RETURN_IF_ERROR(\n        ctx->allocate_temp(DT_INT64, {spec().n_ragged_tensors + 1}, &t));\n    output->tensors.push_back(t);\n    auto split = t.vec<int64>().setZero();\n    IterateFidFeatures(instance,\n                       [&split](int idx, int64_t fid) { ++split(idx + 1); });\n    for (int i = 1; i <= spec().n_ragged_tensors; ++i) {\n      split(i) = split(i) + split(i - 1);\n    }\n\n    TF_RETURN_IF_ERROR(\n        ctx->allocate_temp(DT_INT64, {split(spec().n_ragged_tensors)}, &t));\n    output->tensors.push_back(t);\n    auto value = t.vec<int64>();\n    std::vector<int> pos(spec().n_ragged_tensors);\n    for (int i = 0; i < spec().n_ragged_tensors; ++i) {\n      pos[i] = split(i);\n    }\n    IterateFidFeatures(instance, [&value, &pos](int idx, int64_t fid) {\n      value(pos[idx]++) = fid;\n    });\n    return Status::OK();\n  }\n};\n\n}  // namespace\n\nclass InstanceParser::Impl {\n public:\n  explicit Impl(const InstanceParserConfig &config) : spec_(config) {\n    switch (config.fid_output_type) {\n      case InstanceParserConfig::REGULAR:\n        ragged_tensor_processor_ =\n            std::make_unique<RegularRaggedTensorProcessor>(&spec_);\n        break;\n      case InstanceParserConfig::CONCAT:\n        ragged_tensor_processor_ =\n            std::make_unique<ConcatRaggedTensorProcessor>(&spec_);\n        break;\n    }\n\n    ue_compress_ = std::make_unique<UECompress>();\n  }\n\n  Status Init() { return spec_.Init(); }\n\n  Status Parse(OpKernelContext *ctx, absl::Span<const Instance> instances,\n               Output *output) {\n    output->tensors.clear();\n    TF_RETURN_IF_ERROR(\n        ragged_tensor_processor_->ParseRaggedTensors(ctx, instances, output));\n    TF_RETURN_IF_ERROR(FillFloatFeatures(ctx, instances, output));\n    TF_RETURN_IF_ERROR(FillInt64Features(ctx, instances, output));\n    TF_RETURN_IF_ERROR(FillStringFeatures(ctx, instances, output));\n    TF_RETURN_IF_ERROR(ParseFloatTensors(ctx, instances, output));\n    TF_RETURN_IF_ERROR(ParseInt64Tensors(ctx, instances, output));\n    TF_RETURN_IF_ERROR(ParseStringTensors(ctx, instances, output));\n    return Status::OK();\n  }\n\n private:\n  Status FillFloatFeatures(OpKernelContext *ctx,\n                           absl::Span<const Instance> instances,\n                           Output *output) {\n    const int batch_size = instances.size();\n    std::vector<TTypes<float>::Matrix> values_mat;\n    for (int i = 0; i < spec_.n_float_tensors; ++i) {\n      output->tensors.emplace_back();\n      Tensor *t = &output->tensors.back();\n      TF_RETURN_IF_ERROR(ctx->allocate_temp(\n          DT_FLOAT, GetBatched1DShape(batch_size, spec_.float_feature_dims[i]),\n          t));\n      values_mat.emplace_back(\n          t->shaped<float, 2>({batch_size, spec_.float_feature_dims[i]}));\n      // To be safe, we initialize float tensors to zero by default.\n      values_mat.back().setZero();\n    }\n\n    for (int i = 0; i < batch_size; ++i) {\n      const Instance &instance = instances[i];\n      for (const Feature &feature : instance.feature()) {\n        if (spec_.float_features_set.contains(feature.name())) {\n          std::vector<float> embedding;\n          bool ret = ue_compress_->decompress_embeddings(\n              feature, &embedding, UECompressMethod::COMPRESS_QTZ8);\n          int idx = spec_.float_feature_name_to_index.at(feature.name());\n          if (ret) {\n            // Process data with qtz8 compression.\n            if (spec_.float_feature_dims[idx] != embedding.size()) {\n              return errors::Internal(\n                  \"Decompressed qtz8 data length doesn't match feature dim,\",\n                  \" feature dim: \", spec_.float_feature_dims[idx],\n                  \", uncompressed qtz8 size: \", embedding.size());\n            }\n            for (int j = 0; j < spec_.float_feature_dims[idx]; ++j) {\n              values_mat[idx](i, j) = embedding[j];\n            }\n          } else if (spec_.float_feature_dims[idx] ==\n                     feature.float_value_size()) {\n            for (int j = 0; j < spec_.float_feature_dims[idx]; ++j) {\n              values_mat[idx](i, j) = feature.float_value(j);\n            }\n          } else {\n            // TODO(zouxuan) Set the default value to 0 for now. Xuan will make\n            // an eventual fix for this later.\n            for (int j = 0; j < spec_.float_feature_dims[idx]; ++j) {\n              values_mat[idx](i, j) = 0;\n            }\n          }\n        }\n      }\n    }\n    return Status::OK();\n  }\n\n  Status FillInt64Features(OpKernelContext *ctx,\n                           absl::Span<const Instance> instances,\n                           Output *output) {\n    const int batch_size = instances.size();\n    std::vector<TTypes<int64>::Matrix> values_mat;\n    for (int i = 0; i < spec_.n_int64_tensors; ++i) {\n      output->tensors.emplace_back();\n      Tensor *t = &output->tensors.back();\n      TF_RETURN_IF_ERROR(ctx->allocate_temp(\n          DT_INT64, GetBatched1DShape(batch_size, spec_.int64_feature_dims[i]),\n          t));\n      values_mat.emplace_back(\n          t->shaped<int64, 2>({batch_size, spec_.int64_feature_dims[i]}));\n      // To be safe, we initialize int64 tensors to zero by default.\n      values_mat.back().setZero();\n    }\n\n    for (int i = 0; i < batch_size; ++i) {\n      const Instance &instance = instances[i];\n      for (const Feature &feature : instance.feature()) {\n        if (spec_.int64_features_set.contains(feature.name())) {\n          int idx = spec_.int64_feature_name_to_index.at(feature.name());\n          if (spec_.int64_feature_dims[idx] == feature.int64_value_size()) {\n            for (int j = 0; j < spec_.int64_feature_dims[idx]; ++j) {\n              values_mat[idx](i, j) = feature.int64_value(j);\n            }\n          } else {\n            // TODO(zouxuan) Set the default value to 0 for now. Xuan will make\n            // an eventual fix for this later.\n            for (int j = 0; j < spec_.int64_feature_dims[idx]; ++j) {\n              values_mat[idx](i, j) = 0;\n            }\n          }\n        }\n      }\n    }\n    return Status::OK();\n  }\n\n  Status FillStringFeatures(OpKernelContext *ctx,\n                            absl::Span<const Instance> instances,\n                            Output *output) {\n    const int batch_size = instances.size();\n    std::vector<TTypes<tstring>::Matrix> values_mat;\n    for (int i = 0; i < spec_.n_string_tensors; ++i) {\n      output->tensors.emplace_back();\n      Tensor *t = &output->tensors.back();\n      TF_RETURN_IF_ERROR(ctx->allocate_temp(\n          DT_STRING,\n          GetBatched1DShape(batch_size, spec_.string_feature_dims[i]), t));\n      values_mat.emplace_back(\n          t->shaped<tstring, 2>({batch_size, spec_.string_feature_dims[i]}));\n    }\n\n    for (int i = 0; i < batch_size; ++i) {\n      const Instance &instance = instances[i];\n      for (const Feature &feature : instance.feature()) {\n        if (spec_.string_features_set.contains(feature.name())) {\n          int idx = spec_.string_feature_name_to_index.at(feature.name());\n          if (spec_.string_feature_dims[idx] == feature.bytes_value_size()) {\n            for (int j = 0; j < spec_.string_feature_dims[idx]; ++j) {\n              values_mat[idx](i, j) = feature.bytes_value(j);\n            }\n          } else {\n            for (int j = 0; j < spec_.string_feature_dims[idx]; ++j) {\n              values_mat[idx](i, j) = \"\";\n            }\n          }\n        }\n      }\n    }\n    return Status::OK();\n  }\n\n  Status ParseFloatTensors(OpKernelContext *ctx,\n                           absl::Span<const Instance> instances,\n                           Output *output) {\n    const int batch_size = instances.size();\n    const auto *descriptor = ::idl::matrix::proto::LineId::GetDescriptor();\n    const auto *reflection = ::idl::matrix::proto::LineId::GetReflection();\n    for (size_t i = 0; i < spec_.misc_float_features.size(); ++i) {\n      output->tensors.emplace_back();\n      Tensor *t = &output->tensors.back();\n      TF_RETURN_IF_ERROR(ctx->allocate_temp(\n          DT_FLOAT, GetBatched1DShape(batch_size, spec_.misc_float_dims[i]),\n          t));\n      auto mat = t->shaped<float, 2>({batch_size, spec_.misc_float_dims[i]});\n      // To be safe, we initialize float tensors to zero by default.\n      mat.setZero();\n      const std::string &name = spec_.misc_float_features[i];\n      if (name == \"label\") {\n        for (size_t j = 0; j < instances.size(); ++j) {\n          const Instance &instance = instances[j];\n          int dim = spec_.misc_float_dims[i];\n          if (instance.label_size() < dim) {\n            LOG_EVERY_N_SEC(ERROR, 60)\n                << name << \" Dim is smaller than expected \"\n                << instance.label_size() << \" v.s. \" << dim;\n            dim = instance.label_size();\n          }\n          for (int k = 0; k < dim; ++k) {\n            mat(j, k) = instance.label(k);\n          }\n        }\n        continue;\n      } else if (name == \"instance_weight\") {\n        int dim = spec_.misc_float_dims[i];\n        if (dim != 1) {\n          LOG_EVERY_N_SEC(ERROR, 60) << name << \" Dim is illegal, expected 1 \"\n                                     << \" v.s. \" << dim;\n          dim = 1;\n        }\n        for (size_t j = 0; j < instances.size(); ++j) {\n          const Instance &instance = instances[j];\n          mat(j, 0) = instance.has_instance_weight()\n                          ? instance.instance_weight()\n                          : 1.0f;\n        }\n        continue;\n      }\n      const auto *field = descriptor->FindFieldByName(name);\n      if (field == nullptr) {\n        return errors::NotFound(name + \" not found in misc_float_features!\");\n      }\n      for (size_t j = 0; j < instances.size(); ++j) {\n        const Instance &instance = instances[j];\n        if (field->is_repeated()) {\n          int dim = spec_.misc_float_dims[i];\n          int field_size = reflection->FieldSize(instance.line_id(), field);\n          if (field_size < dim) {\n            LOG_EVERY_N_SEC(ERROR, 60)\n                << name << \" Dim is smaller than expected \" << field_size\n                << \" v.s. \" << dim;\n            dim = field_size;\n          }\n          for (int k = 0; k < dim; ++k) {\n            mat(j, k) =\n                reflection->GetRepeatedFloat(instance.line_id(), field, k);\n          }\n        } else {\n          mat(j, 0) = reflection->GetFloat(instance.line_id(), field);\n        }\n      }\n    }\n    return Status::OK();\n  }\n\n  Status ParseInt64Tensors(OpKernelContext *ctx,\n                           absl::Span<const Instance> instances,\n                           Output *output) {\n    const int batch_size = instances.size();\n    const auto *descriptor = ::idl::matrix::proto::LineId::GetDescriptor();\n    const auto *reflection = ::idl::matrix::proto::LineId::GetReflection();\n\n    for (size_t i = 0; i < spec_.misc_int64_features.size(); ++i) {\n      output->tensors.emplace_back();\n      Tensor *t = &output->tensors.back();\n      TF_RETURN_IF_ERROR(ctx->allocate_temp(\n          DT_INT64, GetBatched1DShape(batch_size, spec_.misc_int64_dims[i]),\n          t));\n      auto mat = t->shaped<int64, 2>({batch_size, spec_.misc_int64_dims[i]});\n      // To be safe, we initialize int64 tensors to zero by default.\n      mat.setZero();\n      const std::string &name = spec_.misc_int64_features[i];\n      const auto *field = descriptor->FindFieldByName(name);\n      if (field == nullptr) {\n        return errors::NotFound(name + \" not found in misc_int64_features!\");\n      }\n      if (field->is_repeated()) {\n        for (int j = 0; j < batch_size; ++j) {\n          int dim = spec_.misc_int64_dims[i];\n          const Instance &instance = instances[j];\n          const int field_size =\n              reflection->FieldSize(instance.line_id(), field);\n          if (field_size < dim) {\n            LOG_EVERY_N_SEC(ERROR, 60)\n                << name << \" Dim is smaller than expected \" << field_size\n                << \" v.s. \" << dim;\n            dim = field_size;\n          }\n          switch (field->cpp_type()) {\n            case FieldDescriptor::CPPTYPE_INT32:\n              for (int k = 0; k < dim; ++k) {\n                mat(j, k) =\n                    reflection->GetRepeatedInt32(instance.line_id(), field, k);\n              }\n              break;\n            case FieldDescriptor::CPPTYPE_UINT32:\n              for (int k = 0; k < dim; ++k) {\n                mat(j, k) =\n                    reflection->GetRepeatedUInt32(instance.line_id(), field, k);\n              }\n              break;\n            case FieldDescriptor::CPPTYPE_INT64:\n              for (int k = 0; k < dim; ++k) {\n                mat(j, k) =\n                    reflection->GetRepeatedInt64(instance.line_id(), field, k);\n              }\n              break;\n            case FieldDescriptor::CPPTYPE_UINT64:\n              for (int k = 0; k < dim; ++k) {\n                mat(j, k) =\n                    reflection->GetRepeatedUInt64(instance.line_id(), field, k);\n              }\n              break;\n            default:\n              return errors::InvalidArgument(\n                  name,\n                  \" Data type not match, only int32/int64/float32 supported.\");\n          }\n        }\n      } else {\n        for (int j = 0; j < batch_size; ++j) {\n          const Instance &instance = instances[j];\n          switch (field->cpp_type()) {\n            case FieldDescriptor::CPPTYPE_INT32:\n              mat(j, 0) = reflection->GetInt32(instance.line_id(), field);\n              break;\n            case FieldDescriptor::CPPTYPE_UINT32:\n              mat(j, 0) = reflection->GetUInt32(instance.line_id(), field);\n              break;\n            case FieldDescriptor::CPPTYPE_INT64:\n              mat(j, 0) = reflection->GetInt64(instance.line_id(), field);\n              break;\n            case FieldDescriptor::CPPTYPE_UINT64:\n              mat(j, 0) = reflection->GetUInt64(instance.line_id(), field);\n              break;\n            default:\n              return errors::InvalidArgument(\n                  name,\n                  \" Data type not match, only int32/int64/float32 supported.\");\n          }\n        }\n      }\n    }\n    return Status::OK();\n  }\n\n  Status ParseStringTensors(OpKernelContext *ctx,\n                            absl::Span<const Instance> instances,\n                            Output *output) {\n    const int batch_size = instances.size();\n    const auto *descriptor = ::idl::matrix::proto::LineId::GetDescriptor();\n    const auto *reflection = ::idl::matrix::proto::LineId::GetReflection();\n    for (size_t i = 0; i < spec_.misc_string_features.size(); ++i) {\n      output->tensors.emplace_back();\n      Tensor *t = &output->tensors.back();\n      TF_RETURN_IF_ERROR(ctx->allocate_temp(\n          DT_STRING, GetBatched1DShape(batch_size, spec_.misc_string_dims[i]),\n          t));\n      auto mat = t->shaped<tstring, 2>({batch_size, spec_.misc_string_dims[i]});\n      const std::string &name = spec_.misc_string_features[i];\n      const auto *field = descriptor->FindFieldByName(name);\n      if (field == nullptr) {\n        return errors::NotFound(name + \" not found in misc_string_features!\");\n      }\n      for (size_t j = 0; j < instances.size(); ++j) {\n        const Instance &instance = instances[j];\n        if (field->is_repeated()) {\n          int dim = spec_.misc_string_dims[i];\n          int field_size = reflection->FieldSize(instance.line_id(), field);\n          if (field_size < dim) {\n            LOG_EVERY_N_SEC(ERROR, 60)\n                << name << \" Dim is smaller than expected \" << field_size\n                << \" v.s. \" << dim;\n            dim = field_size;\n          }\n          for (int k = 0; k < dim; ++k) {\n            mat(j, k) =\n                reflection->GetRepeatedString(instance.line_id(), field, k);\n          }\n        } else {\n          mat(j, 0) = reflection->GetString(instance.line_id(), field);\n        }\n      }\n    }\n    return Status::OK();\n  }\n\n  TensorShape GetBatched1DShape(int batch_size, int64 dim) {\n    if (spec_.collapse_batch_dim) {\n      return {dim};\n    } else {\n      return {batch_size, dim};\n    }\n  }\n\n  InstanceParserSpec spec_;\n  std::unique_ptr<RaggedTensorProcessor> ragged_tensor_processor_;\n  std::unique_ptr<UECompress> ue_compress_;\n};\n\nInstanceParser::InstanceParser(const InstanceParserConfig &config)\n    : impl_(std::make_unique<Impl>(config)) {}\n\nInstanceParser::~InstanceParser() {}\n\nStatus InstanceParser::Init() { return impl_->Init(); }\n\nStatus InstanceParser::Parse(OpKernelContext *ctx,\n                             absl::Span<const Instance> instances,\n                             Output *output) const {\n  return impl_->Parse(ctx, instances, output);\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/parse_instance_lib.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_PARSE_INSTANCE_LIB_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_PARSE_INSTANCE_LIB_H_\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/types/span.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\n// The config to instantiate ParseInstanceSpec.\nstruct InstanceParserConfig {\n  // Fid features.\n  std::vector<int> fidv1_features;\n  std::vector<std::string> fidv2_features;\n  enum FidOutputType {\n    // Each fid will have its own ragged tensor.\n    REGULAR,\n    // All fids will be outputted as a single ragged tensor.\n    // Only available when collapse_batch_dim == True.\n    CONCAT,\n  };\n  FidOutputType fid_output_type = REGULAR;\n\n  // Float features.\n  std::vector<std::string> float_features;\n  std::vector<int> float_feature_dims;\n\n  // Int64 features.\n  std::vector<std::string> int64_features;\n  std::vector<int> int64_feature_dims;\n\n  // String features.\n  std::vector<std::string> string_features;\n  std::vector<int> string_feature_dims;\n\n  // LineId related features, including labels and others.\n  std::vector<std::string> misc_float_features;\n  std::vector<int> misc_float_dims;\n  std::vector<std::string> misc_int64_features;\n  std::vector<int> misc_int64_dims;\n  std::vector<std::string> misc_string_features;\n  std::vector<int> misc_string_dims;\n\n  bool collapse_batch_dim = false;\n};\n\n// A parser that is able to parse instance.\n// Must call Init() before used.\nclass InstanceParser {\n public:\n  explicit InstanceParser(const InstanceParserConfig &config);\n  ~InstanceParser();\n\n  Status Init();\n\n  struct Output {\n    std::vector<Tensor> tensors;\n  };\n\n  Status Parse(OpKernelContext *ctx,\n               absl::Span<const parser::proto::Instance> instances,\n               Output *tensors) const;\n\n private:\n  class Impl;\n  std::unique_ptr<Impl> impl_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_PARSE_INSTANCE_LIB_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/parse_instance_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nbool IsUnsetHandle(shape_inference::DimensionHandle handle) {\n  return handle.Handle() == 0;\n}\n\nshape_inference::ShapeHandle GetBatched1D(\n    shape_inference::InferenceContext *ctx,\n    shape_inference::DimensionHandle batch_size, int dim) {\n  if (IsUnsetHandle(batch_size)) {\n    return ctx->Vector(dim);\n  } else {\n    return ctx->Matrix(batch_size, dim);\n  }\n}\n\nStatus SetParseInstanceShape(shape_inference::InferenceContext *ctx,\n                             shape_inference::DimensionHandle batch_size) {\n  int offset = 0;\n\n  // Ragged tensor\n  int n;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"N\", &n));\n  for (int i = 0; i < n; ++i) {\n    if (IsUnsetHandle(batch_size)) {\n      ctx->set_output(offset + i, ctx->Vector(2));\n      continue;\n    }\n    int batch_size_value = ctx->Value(batch_size);\n    if (batch_size_value == shape_inference::InferenceContext::kUnknownDim) {\n      ctx->set_output(offset + i, ctx->Vector(ctx->UnknownDim()));\n    } else {\n      ctx->set_output(offset + i, ctx->Vector(batch_size_value + 1));\n    }\n  }\n  offset += n;\n\n  for (int i = 0; i < n; ++i) {\n    ctx->set_output(offset + i, ctx->Vector(ctx->UnknownDim()));\n  }\n  offset += n;\n\n  // float tensor\n  int m;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"M\", &m));\n  std::vector<int> float_feature_dims;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"float_feature_dims\", &float_feature_dims));\n  for (int i = 0; i < m; ++i) {\n    ctx->set_output(offset + i,\n                    GetBatched1D(ctx, batch_size, float_feature_dims[i]));\n  }\n  offset += m;\n\n  // int64 tensor\n  int o;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"O\", &o));\n  std::vector<int> int64_feature_dims;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"int64_feature_dims\", &int64_feature_dims));\n  for (int i = 0; i < o; ++i) {\n    ctx->set_output(offset + i,\n                    GetBatched1D(ctx, batch_size, int64_feature_dims[i]));\n  }\n  offset += o;\n\n  // string tensor\n  int p;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"P\", &p));\n  std::vector<int> string_feature_dims;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"string_feature_dims\", &string_feature_dims));\n  for (int i = 0; i < p; ++i) {\n    ctx->set_output(offset + i,\n                    GetBatched1D(ctx, batch_size, string_feature_dims[i]));\n  }\n  offset += p;\n\n  // misc_feature_float\n  int q;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"Q\", &q));\n  std::vector<int> misc_float_dims;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"misc_float_dims\", &misc_float_dims));\n  for (int i = 0; i < q; ++i) {\n    ctx->set_output(offset + i,\n                    GetBatched1D(ctx, batch_size, misc_float_dims[i]));\n  }\n  offset += q;\n\n  // misc_feature_int64\n  int r;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"R\", &r));\n  std::vector<int> misc_int64_dims;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"misc_int64_dims\", &misc_int64_dims));\n  for (int i = 0; i < r; ++i) {\n    ctx->set_output(offset + i,\n                    GetBatched1D(ctx, batch_size, misc_int64_dims[i]));\n  }\n  offset += r;\n\n  // misc_feature_string\n  int s;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"S\", &s));\n  std::vector<int> misc_string_dims;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"misc_string_dims\", &misc_string_dims));\n  for (int i = 0; i < s; ++i) {\n    ctx->set_output(offset + i,\n                    GetBatched1D(ctx, batch_size, misc_string_dims[i]));\n  }\n  offset += s;\n\n  return Status::OK();\n}\n\n// We use fid_features for FIDV1 key and str_features for FIDv2 keys.\n// In fid v1, we use the slot whitelist, and in fid v2, we use the\n// feature_name\n// whitelist.\nREGISTER_OP(\"MonolithParseInstances\")\n    .Input(\"serialized: string\")\n    .Output(\"ragged_feature_splits: N * int64\")\n    .Output(\"ragged_feature_values: N * int64\")\n    .Output(\"float_feature_values: M * float32\")\n    .Output(\"int64_feature_values: O * int64\")\n    .Output(\"string_feature_values: P * string\")\n    .Output(\"misc_float_feature_values: Q * float32\")\n    .Output(\"misc_int64_feature_values: R * int64\")\n    .Output(\"misc_string_feature_values: S * string\")\n    .Attr(\"N: int >= 0\")\n    .Attr(\"M: int >= 0\")\n    .Attr(\"O: int >= 0\")\n    .Attr(\"P: int >= 0\")\n    .Attr(\"Q: int >= 0\")\n    .Attr(\"R: int >= 0\")\n    .Attr(\"S: int >= 0\")\n    .Attr(\"fidv1_features: list(int)\")\n    .Attr(\"fidv2_features: list(string)\")\n    .Attr(\"float_features: list(string)\")\n    .Attr(\"float_feature_dims: list(int)\")\n    .Attr(\"int64_features: list(string)\")\n    .Attr(\"int64_feature_dims: list(int)\")\n    .Attr(\"string_features: list(string)\")\n    .Attr(\"string_feature_dims: list(int)\")\n    .Attr(\"misc_float_features: list(string)\")\n    .Attr(\"misc_float_dims: list(int)\")\n    .Attr(\"misc_int64_features: list(string)\")\n    .Attr(\"misc_int64_dims: list(int)\")\n    .Attr(\"misc_string_features: list(string)\")\n    .Attr(\"misc_string_dims: list(int)\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      shape_inference::DimensionHandle batch_size = ctx->Dim(ctx->input(0), 0);\n      return SetParseInstanceShape(ctx, batch_size);\n    });\n\nREGISTER_OP(\"MonolithRawParseInstance\")\n    .Attr(\"T: list(type)\")\n    .Input(\"serialized: string\")\n    .Output(\"tensors : T\")\n    .Attr(\"fidv1_features: list(int) = []\")\n    .Attr(\"fidv2_features: list(string) = []\")\n    .Attr(\"float_features: list(string) = []\")\n    .Attr(\"float_feature_dims: list(int) = []\")\n    .Attr(\"int64_features: list(string) = []\")\n    .Attr(\"int64_feature_dims: list(int) = []\")\n    .Attr(\"string_features: list(string) = []\")\n    .Attr(\"string_feature_dims: list(int) = []\")\n    .Attr(\"misc_float_features: list(string) = []\")\n    .Attr(\"misc_float_dims: list(int) = []\")\n    .Attr(\"misc_int64_features: list(string) = []\")\n    .Attr(\"misc_int64_dims: list(int) = []\")\n    .Attr(\"misc_string_features: list(string) = []\")\n    .Attr(\"misc_string_dims: list(int) = []\")\n    .Attr(\"collapse_batch_dim: bool = false\")\n    .Attr(\"fid_output_type: {'REGULAR', 'CONCAT'} = 'REGULAR'\")\n    .SetDoNotOptimize()  // Source dataset ops must disable constant folding.\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      return Status::OK();\n    });\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/pb_variant.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <unordered_map>\n#include \"absl/strings/str_cat.h\"\n\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n\nnamespace tensorflow {\nusing Example = ::monolith::io::proto::Example;\nusing ExampleBatch = ::monolith::io::proto::ExampleBatch;\nusing Instance = ::parser::proto::Instance;\n\ntemplate <>\nstd::string TypeNameVariant<Example>(const Example &value) {\n  return \"Example\";\n}\n\ntemplate <>\nstd::string DebugStringVariant<Example>(const Example &value) {\n  return \"Example DebugString\";\n}\n\ntemplate <>\nbool DecodeVariant<Example>(std::string *buf, Example *value) {\n  std::cout << \"DecodeVariant<EFeature> - 1\" << std::endl;\n  value->ParseFromArray(buf->data(), buf->size());\n  return true;\n}\n\ntemplate <>\nvoid EncodeVariant<Example>(const Example &value, std::string *buf) {\n  value.SerializeToString(buf);\n}\n\ntemplate <>\nbool DecodeVariant<Example>(VariantTensorData *data, Example *value) {\n  return false;\n}\n\ntemplate <>\nvoid EncodeVariant<Example>(const Example &value, VariantTensorData *data) {}\n\ntemplate <>\nstd::string TypeNameVariant<ExampleBatch>(const ExampleBatch &value) {\n  return \"ExampleBatch\";\n}\n\ntemplate <>\nstd::string DebugStringVariant<ExampleBatch>(const ExampleBatch &value) {\n  return \"ExampleBatch DebugString\";\n}\n\ntemplate <>\nbool DecodeVariant<ExampleBatch>(std::string *buf, ExampleBatch *value) {\n  std::cout << \"DecodeVariant<EFeature> - 1\" << std::endl;\n  value->ParseFromArray(buf->data(), buf->size());\n  return true;\n}\n\ntemplate <>\nvoid EncodeVariant<ExampleBatch>(const ExampleBatch &value, std::string *buf) {\n  value.SerializeToString(buf);\n}\n\ntemplate <>\nbool DecodeVariant<ExampleBatch>(VariantTensorData *data, ExampleBatch *value) {\n  return false;\n}\n\ntemplate <>\nvoid EncodeVariant<ExampleBatch>(const ExampleBatch &value,\n                                 VariantTensorData *data) {}\n\ntemplate <>\nstd::string TypeNameVariant<Instance>(const Instance &value) {\n  return \"Instance\";\n}\n\ntemplate <>\nstd::string DebugStringVariant<Instance>(const Instance &value) {\n  return \"Instance DebugString\";\n}\n\ntemplate <>\nbool DecodeVariant<Instance>(std::string *buf, Instance *value) {\n  std::cout << \"DecodeVariant<EFeature> - 1\" << std::endl;\n  value->ParseFromArray(buf->data(), buf->size());\n  return true;\n}\n\ntemplate <>\nvoid EncodeVariant<Instance>(const Instance &value, std::string *buf) {\n  value.SerializeToString(buf);\n}\n\ntemplate <>\nbool DecodeVariant<Instance>(VariantTensorData *data, Instance *value) {\n  return false;\n}\n\ntemplate <>\nvoid EncodeVariant<Instance>(const Instance &value, VariantTensorData *data) {}\n\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/pb_variant.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_PB_VARIANT_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_PB_VARIANT_H_\n\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n#include \"tensorflow/core/framework/variant.h\"\n\nnamespace tensorflow {\ntemplate <>\nstd::string TypeNameVariant<::monolith::io::proto::Example>(\n    const ::monolith::io::proto::Example &value);\n\ntemplate <>\nstd::string DebugStringVariant<::monolith::io::proto::Example>(\n    const ::monolith::io::proto::Example &value);\n\ntemplate <>\nbool DecodeVariant<::monolith::io::proto::Example>(\n    std::string *buf, ::monolith::io::proto::Example *value);\n\ntemplate <>\nvoid EncodeVariant<::monolith::io::proto::Example>(\n    const ::monolith::io::proto::Example &value, std::string *buf);\n\ntemplate <>\nbool DecodeVariant<::monolith::io::proto::Example>(\n    VariantTensorData *data, ::monolith::io::proto::Example *value);\n\ntemplate <>\nvoid EncodeVariant<::monolith::io::proto::Example>(\n    const ::monolith::io::proto::Example &value, VariantTensorData *data);\n\ntemplate <>\nstd::string TypeNameVariant<::monolith::io::proto::ExampleBatch>(\n    const ::monolith::io::proto::ExampleBatch &value);\n\ntemplate <>\nstd::string DebugStringVariant<::monolith::io::proto::ExampleBatch>(\n    const ::monolith::io::proto::ExampleBatch &value);\n\ntemplate <>\nbool DecodeVariant<::monolith::io::proto::ExampleBatch>(\n    std::string *buf, ::monolith::io::proto::ExampleBatch *value);\n\ntemplate <>\nvoid EncodeVariant<::monolith::io::proto::ExampleBatch>(\n    const ::monolith::io::proto::ExampleBatch &value, std::string *buf);\n\ntemplate <>\nbool DecodeVariant<::monolith::io::proto::ExampleBatch>(\n    VariantTensorData *data, ::monolith::io::proto::ExampleBatch *value);\n\ntemplate <>\nvoid EncodeVariant<::monolith::io::proto::ExampleBatch>(\n    const ::monolith::io::proto::ExampleBatch &value, VariantTensorData *data);\n\ntemplate <>\nstd::string TypeNameVariant<::parser::proto::Instance>(\n    const ::parser::proto::Instance &value);\n\ntemplate <>\nstd::string DebugStringVariant<::parser::proto::Instance>(\n    const ::parser::proto::Instance &value);\n\ntemplate <>\nbool DecodeVariant<::parser::proto::Instance>(std::string *buf,\n                                              ::parser::proto::Instance *value);\n\ntemplate <>\nvoid EncodeVariant<::parser::proto::Instance>(\n    const ::parser::proto::Instance &value, std::string *buf);\n\ntemplate <>\nbool DecodeVariant<::parser::proto::Instance>(VariantTensorData *data,\n                                              ::parser::proto::Instance *value);\n\ntemplate <>\nvoid EncodeVariant<::parser::proto::Instance>(\n    const ::parser::proto::Instance &value, VariantTensorData *data);\n\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_PB_VARIANT_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/reader_util.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n\nconst size_t FALLBACK_RESERVE_VALUE = 0xfefefefe;\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nvoid to_json(nlohmann::json& j, const FeatureNameMapperIdInfo& info) {\n  j[\"id\"] = info.id;\n  j[\"sorted_id\"] = info.sorted_id;\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/reader_util.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_READER_UTIL_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_READER_UTIL_H_\n\n#include <atomic>\n#include <fstream>\n#include <iostream>\n#include <memory>\n#include <string>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/synchronization/mutex.h\"\n#include \"tensorflow/core/platform/logging.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nconstexpr uint64_t fid_v1_mask = (1LL << 54) - 1;\nconstexpr uint64_t fid_v2_mask = (1LL << 48) - 1;\n\ninline int slot_id_v1(uint64_t fid) { return fid >> 54; }\n\ninline int slot_id_v2(uint64_t fid) {\n  return (fid >> 48) & (((int64_t)1 << 15) - 1);\n}\n\ninline uint64_t convert_fid_v1_to_v2(int slot, uint64_t fid) {\n  uint64_t slot_long = slot;\n  return ((fid & fid_v2_mask) | slot_long << 48);\n}\n\ninline uint64_t convert_fid_v1_to_v2(uint64_t fid) {\n  uint64_t slot_long = fid >> 54;\n  return ((fid & fid_v2_mask) | slot_long << 48);\n}\n\ninline uint64_t switch_slot_v1(uint64_t fid, uint64_t slot) {\n  return (slot << 54) | (fid & fid_v1_mask);\n}\n\ninline uint64_t switch_slot_v2(uint64_t fid, uint64_t slot) {\n  return (slot << 48) | (fid & fid_v2_mask);\n}\n\ninline int get_max_slot_number() { return 1 << 15; }\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ninline int64_t GetFidV1(int slot, int64_t signautre) {\n  return ((uint64_t)slot << 54) | (signautre & fid_v1_mask);\n}\n\ninline int64_t GetFidV2(int slot, int64_t signature) {\n  return ((uint64_t)slot << 48) | (signature & fid_v2_mask);\n}\n\nclass FeaturePruningByteCounter {\n public:\n  ~FeaturePruningByteCounter() {\n    LOG(INFO) << absl::StrFormat(\"Finally %s\", DebugString());\n  }\n\n  void AddByteSize(uint64_t byte_size) { byte_size_ += byte_size; }\n\n  void AddByteSizePruned(uint64_t byte_size) { byte_size_pruned_ += byte_size; }\n\n  std::string DebugString() const {\n    return absl::StrFormat(\n        \"read: %llu bytes (%s), after pruning: %llu bytes (%s)\", byte_size_,\n        PrettyBytes(byte_size_), byte_size_pruned_,\n        PrettyBytes(byte_size_pruned_));\n  }\n\n private:\n  static std::string PrettyBytes(uint64_t bytes) {\n    const std::vector<std::string> suffixes = {\"B\",  \"KB\", \"MB\", \"GB\",\n                                               \"TB\", \"PB\", \"EB\"};\n    int64_t s = 0;\n    auto count = static_cast<double>(bytes);\n    while (count >= 1024 && s < suffixes.size()) {\n      s++;\n      count /= 1024;\n    }\n\n    if (count - std::floor(count) == 0.0) {\n      return absl::StrFormat(\"%llu %s\", static_cast<uint64_t>(count),\n                             suffixes[s]);\n    } else {\n      return absl::StrFormat(\"%.2f %s\", count, suffixes[s]);\n    }\n  }\n\n  uint64_t byte_size_;\n  uint64_t byte_size_pruned_;\n};\n\nstruct FeatureNameMapperIdInfo {\n  int32_t id;\n  int32_t sorted_id;\n};\nvoid to_json(nlohmann::json& j, const FeatureNameMapperIdInfo& info);\n\nclass FeatureNameMapper {\n public:\n  FeatureNameMapper(FeatureNameMapper const&) = delete;\n  void operator=(FeatureNameMapper const&) = delete;\n\n  void TurnOn() { turned_on_ = true; }\n\n  bool IsAvailable() const { return turned_on_ && initialized_; }\n\n  bool RegisterValidIds(const std::vector<std::pair<int, int>>& valid_ids) {\n    absl::WriterMutexLock l(&mu_);\n    registered_feature_id_set_.insert(valid_ids.begin(), valid_ids.end());\n\n    if (id_to_name_.empty()) {\n      return true;\n    }\n\n    absl::flat_hash_set<std::pair<int, int>> invalid_ids;\n    for (std::pair<int, int> p : registered_feature_id_set_) {\n      if (!id_to_name_.contains(p.first) && !id_to_name_.contains(p.second)) {\n        invalid_ids.insert(p);\n      }\n    }\n    if (!invalid_ids.empty()) {\n      nlohmann::json j;\n      j[\"invalid_ids\"] = invalid_ids;\n      LOG(ERROR) << \"ResisterValidIds: \" << j.dump();\n      return false;\n    }\n\n    for (std::pair<int, int> p : registered_feature_id_set_) {\n      std::vector<int> ids = {p.first, p.second};\n      for (int id : ids) {\n        auto it = id_to_name_.find(id);\n        if (it != id_to_name_.end()) {\n          for (const std::string& name : it->second) {\n            valid_id_to_name_[it->first].push_back(name);\n            auto sorted_id = name_to_id_.at(name).sorted_id;\n            valid_name_to_id_.insert({name, {it->first, sorted_id}});\n          }\n        }\n      }\n    }\n\n    return true;\n  }\n\n  bool RegisterValidNames(const std::vector<std::string>& valid_names) {\n    absl::WriterMutexLock l(&mu_);\n    registered_feature_name_set_.insert(valid_names.begin(), valid_names.end());\n\n    if (name_to_id_.empty()) {\n      return true;\n    }\n\n    std::unordered_set<std::string> invalid_names;\n    for (const std::string& name : registered_feature_name_set_) {\n      if (!name_to_id_.contains(name)) {\n        invalid_names.insert(name);\n      }\n    }\n    if (!invalid_names.empty()) {\n      nlohmann::json j;\n      j[\"invalid_names\"] = invalid_names;\n      LOG(ERROR) << \"ResisterValidNames: \" << j.dump();\n      return false;\n    }\n\n    for (const std::string& name : registered_feature_name_set_) {\n      auto it = name_to_id_.find(name);\n      valid_name_to_id_.insert({it->first, it->second});\n      valid_id_to_name_[it->second.id].push_back(it->first);\n    }\n\n    return true;\n  }\n\n  bool SetMapping(const absl::flat_hash_map<std::string, int32_t>& name_to_id,\n                  const absl::flat_hash_map<int32_t, std::vector<std::string>>&\n                      id_to_name) {\n    absl::WriterMutexLock l(&mu_);\n\n    int sorted_id = 0;\n    for (auto& iter : name_to_id) {\n      name_to_id_[iter.first] = {iter.second, ++sorted_id};\n    }\n    id_to_name_ = id_to_name;\n    if (name_to_id_.empty()) {\n      return true;\n    }\n\n    std::unordered_set<std::string> invalid_names;\n    for (const std::string& name : registered_feature_name_set_) {\n      if (!name_to_id_.contains(name)) {\n        invalid_names.insert(name);\n      }\n    }\n\n    absl::flat_hash_set<std::pair<int, int>> invalid_ids;\n    for (std::pair<int, int> p : registered_feature_id_set_) {\n      if (!id_to_name_.contains(p.first) && !id_to_name_.contains(p.second)) {\n        invalid_ids.insert(p);\n      }\n    }\n\n    if (!invalid_names.empty() || !invalid_ids.empty()) {\n      name_to_id_.clear();\n      id_to_name_.clear();\n      nlohmann::json j;\n      j[\"invalid_names\"] = invalid_names;\n      j[\"invalid_ids\"] = invalid_ids;\n      LOG(ERROR) << \"SetMapping: \" << j.dump();\n      return false;\n    }\n\n    for (std::pair<int, int> p : registered_feature_id_set_) {\n      std::vector<int> ids = {p.first, p.second};\n      for (int id : ids) {\n        auto it = id_to_name_.find(id);\n        if (it != id_to_name_.end()) {\n          for (const std::string& name : it->second) {\n            valid_id_to_name_[it->first].push_back(name);\n            auto sorted_id = name_to_id_.at(name).sorted_id;\n            valid_name_to_id_.insert({name, {it->first, sorted_id}});\n          }\n        }\n      }\n    }\n\n    for (const std::string& name : registered_feature_name_set_) {\n      auto it = name_to_id_.find(name);\n      valid_name_to_id_.insert({it->first, it->second});\n      valid_id_to_name_[it->second.id].push_back(it->first);\n    }\n\n    initialized_ = true;\n    return true;\n  }\n\n  bool GetIdByName(const std::string& name, int32_t* id,\n                   int32_t* sorted_id = nullptr) {\n    absl::ReaderMutexLock l(&mu_);\n    auto it = valid_name_to_id_.find(name);\n    if (it == valid_name_to_id_.end()) {\n      return false;\n    }\n\n    *id = it->second.id;\n    if (sorted_id) {\n      *sorted_id = it->second.sorted_id;\n    }\n    return true;\n  }\n\n  std::string DebugString() {\n    absl::ReaderMutexLock l(&mu_);\n    nlohmann::json j = valid_name_to_id_;\n    return j.dump(2);\n  }\n\n  FeatureNameMapper() : turned_on_(false), initialized_(false) {}\n\n private:\n  std::atomic_bool turned_on_;\n  std::atomic_bool initialized_;\n  absl::flat_hash_map<int32_t, std::vector<std::string>> id_to_name_;\n\n  absl::flat_hash_map<std::string, FeatureNameMapperIdInfo> name_to_id_;\n  absl::flat_hash_map<int32_t, std::vector<std::string>> valid_id_to_name_;\n  absl::flat_hash_map<std::string, FeatureNameMapperIdInfo> valid_name_to_id_;\n  absl::flat_hash_set<std::string> registered_feature_name_set_;\n  absl::flat_hash_set<std::pair<int, int>> registered_feature_id_set_;\n  absl::Mutex mu_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_READER_UTIL_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/reader_util_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include <string>\n#include \"gtest/gtest.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nconst uint32_t kSlotBits = 15;\nconst uint32_t kFeatureV2Bits = 48;\n\nTEST(ReaderUtilTest, GetSlotID) {\n  int64_t slot_id = 123;\n  int64_t fid = slot_id << kFeatureV2Bits;\n  int64_t slot_id_ret = slot_id_v2(fid);\n  ASSERT_TRUE(slot_id_ret == slot_id);\n\n  slot_id = (1 << kSlotBits) - 1;\n  fid = slot_id << kFeatureV2Bits;\n  slot_id_ret = slot_id_v2(fid);\n  ASSERT_TRUE(slot_id_ret == slot_id);\n}\n\n\nTEST(ReaderUtilTest, FeatureNameMapperNormalCase1) {\n  auto mapper = std::make_unique<FeatureNameMapper>();\n  ASSERT_FALSE(mapper->IsAvailable());\n  mapper->TurnOn();\n  ASSERT_FALSE(mapper->IsAvailable());\n\n  std::string name1 = \"slot_1\", name2 = \"slot_2\", name3 = \"slot_3\",\n              name4 = \"slot_4\";\n  int id1 = -1, id2 = -1, id3 = -1, id4 = -1;\n\n  ASSERT_TRUE(mapper->RegisterValidNames({name1, name2}));\n  ASSERT_TRUE(mapper->RegisterValidIds({{3, 10003}}));\n\n  absl::flat_hash_map<std::string, int32_t> m;\n  absl::flat_hash_map<int32_t, std::vector<std::string>> m2;\n  m.insert({name1, 1});\n  m.insert({name2, 2});\n  m.insert({name3, 3});\n  m.insert({name4, 4});\n  m2.insert({1, {name1}});\n  m2.insert({2, {name2}});\n  m2.insert({3, {name3}});\n  m2.insert({4, {name4}});\n  ASSERT_TRUE(mapper->SetMapping(m, m2));\n  ASSERT_TRUE(mapper->GetIdByName(name1, &id1));\n  ASSERT_TRUE(mapper->GetIdByName(name2, &id2));\n  ASSERT_TRUE(mapper->GetIdByName(name3, &id3));\n  ASSERT_FALSE(mapper->GetIdByName(name4, &id4));\n  ASSERT_EQ(id1, 1);\n  ASSERT_EQ(id2, 2);\n  ASSERT_EQ(id3, 3);\n\n  LOG(INFO) << mapper->DebugString();\n}\n\nTEST(ReaderUtilTest, FeatureNameMapperNormalCase2) {\n  auto mapper = std::make_unique<FeatureNameMapper>();\n  ASSERT_FALSE(mapper->IsAvailable());\n  mapper->TurnOn();\n  ASSERT_FALSE(mapper->IsAvailable());\n\n  std::string name1 = \"slot_1\", name2 = \"slot_2\", name3 = \"slot_3\",\n              name4 = \"slot_4\";\n  int id1 = -1, id2 = -1, id3 = -1, id4 = -1;\n\n  absl::flat_hash_map<std::string, int32_t> m;\n  absl::flat_hash_map<int32_t, std::vector<std::string>> m2;\n  m.insert({name1, 1});\n  m.insert({name2, 2});\n  m.insert({name3, 3});\n  m.insert({name4, 4});\n  m2.insert({1, {name1}});\n  m2.insert({2, {name2}});\n  m2.insert({3, {name3}});\n  m2.insert({4, {name4}});\n  ASSERT_TRUE(mapper->SetMapping(m, m2));\n  ASSERT_FALSE(mapper->GetIdByName(name1, &id1));\n  ASSERT_FALSE(mapper->GetIdByName(name2, &id2));\n  ASSERT_FALSE(mapper->GetIdByName(name3, &id3));\n  ASSERT_FALSE(mapper->GetIdByName(name4, &id4));\n\n  ASSERT_TRUE(mapper->RegisterValidNames({name1, name2}));\n  ASSERT_TRUE(mapper->RegisterValidIds({{3, 10003}}));\n\n  ASSERT_TRUE(mapper->GetIdByName(name1, &id1));\n  ASSERT_TRUE(mapper->GetIdByName(name2, &id2));\n  ASSERT_TRUE(mapper->GetIdByName(name3, &id3));\n  ASSERT_FALSE(mapper->GetIdByName(name4, &id4));\n  ASSERT_EQ(id1, 1);\n  ASSERT_EQ(id2, 2);\n  ASSERT_EQ(id3, 3);\n\n  LOG(INFO) << mapper->DebugString();\n}\n\nTEST(ReaderUtilTest, FeatureNameMapperCornerCase1) {\n  auto mapper = std::make_unique<FeatureNameMapper>();\n  ASSERT_FALSE(mapper->IsAvailable());\n  mapper->TurnOn();\n  ASSERT_FALSE(mapper->IsAvailable());\n\n  std::string name1 = \"slot_1\", name2 = \"slot_2\", name3 = \"slot_3\",\n              name4 = \"slot_4\";\n  int id1 = -1, id2 = -1, id3 = -1, id4 = -1;\n\n  ASSERT_TRUE(mapper->RegisterValidNames({name1, name2}));\n  absl::flat_hash_map<std::string, int32_t> m;\n  absl::flat_hash_map<int32_t, std::vector<std::string>> m2;\n  m.insert({name1, 1});\n  m.insert({name2, 2});\n  m.insert({name3, 3});\n  m.insert({name4, 4});\n  m2.insert({1, {name1}});\n  m2.insert({2, {name2}});\n  m2.insert({3, {name3}});\n  m2.insert({4, {name4}});\n  ASSERT_TRUE(mapper->SetMapping(m, m2));\n  ASSERT_TRUE(mapper->RegisterValidIds({{3, 10003}}));\n\n  ASSERT_TRUE(mapper->GetIdByName(name1, &id1));\n  ASSERT_TRUE(mapper->GetIdByName(name2, &id2));\n  ASSERT_TRUE(mapper->GetIdByName(name3, &id3));\n  ASSERT_FALSE(mapper->GetIdByName(name4, &id4));\n  ASSERT_EQ(id1, 1);\n  ASSERT_EQ(id2, 2);\n  ASSERT_EQ(id3, 3);\n\n  LOG(INFO) << mapper->DebugString();\n}\n\nTEST(ReaderUtilTest, FeatureNameMapperCornerCase2) {\n  auto mapper = std::make_unique<FeatureNameMapper>();\n  ASSERT_FALSE(mapper->IsAvailable());\n  mapper->TurnOn();\n  ASSERT_FALSE(mapper->IsAvailable());\n\n  std::string name1 = \"slot_1\", name2 = \"slot_2\", name3 = \"slot_3\",\n              name4 = \"slot_4\";\n  int id1 = -1, id2 = -1, id3 = -1, id4 = -1;\n\n  ASSERT_TRUE(mapper->RegisterValidIds({{3, 10003}}));\n  absl::flat_hash_map<std::string, int32_t> m;\n  absl::flat_hash_map<int32_t, std::vector<std::string>> m2;\n  m.insert({name1, 1});\n  m.insert({name2, 2});\n  m.insert({name3, 3});\n  m.insert({name4, 4});\n  m2.insert({1, {name1}});\n  m2.insert({2, {name2}});\n  m2.insert({3, {name3}});\n  m2.insert({4, {name4}});\n  ASSERT_TRUE(mapper->SetMapping(m, m2));\n  ASSERT_TRUE(mapper->RegisterValidNames({name1, name2}));\n\n  ASSERT_TRUE(mapper->GetIdByName(name1, &id1));\n  ASSERT_TRUE(mapper->GetIdByName(name2, &id2));\n  ASSERT_TRUE(mapper->GetIdByName(name3, &id3));\n  ASSERT_FALSE(mapper->GetIdByName(name4, &id4));\n  ASSERT_EQ(id1, 1);\n  ASSERT_EQ(id2, 2);\n  ASSERT_EQ(id3, 3);\n\n  LOG(INFO) << mapper->DebugString();\n}\n\nTEST(ReaderUtilTest, FeatureNameMapperCornerCase3) {\n  auto mapper = std::make_unique<FeatureNameMapper>();\n  ASSERT_FALSE(mapper->IsAvailable());\n  mapper->TurnOn();\n  ASSERT_FALSE(mapper->IsAvailable());\n\n  std::string name1 = \"slot_1\";\n\n  ASSERT_TRUE(mapper->RegisterValidIds({{2, 10002}}));\n  absl::flat_hash_map<std::string, int32_t> m;\n  absl::flat_hash_map<int32_t, std::vector<std::string>> m2;\n  m.insert({name1, 1});\n  m2.insert({1, {name1}});\n  ASSERT_FALSE(mapper->SetMapping(m, m2));\n\n  LOG(INFO) << mapper->DebugString();\n}\n\nTEST(ReaderUtilTest, FeatureNameMapperCornerCase4) {\n  auto mapper = std::make_unique<FeatureNameMapper>();\n  ASSERT_FALSE(mapper->IsAvailable());\n  mapper->TurnOn();\n  ASSERT_FALSE(mapper->IsAvailable());\n\n  std::string name1 = \"slot_1\";\n\n  absl::flat_hash_map<std::string, int32_t> m;\n  absl::flat_hash_map<int32_t, std::vector<std::string>> m2;\n  m.insert({name1, 1});\n  m2.insert({1, {name1}});\n  ASSERT_TRUE(mapper->SetMapping(m, m2));\n  ASSERT_FALSE(mapper->RegisterValidIds({{2, 10002}}));\n\n  LOG(INFO) << mapper->DebugString();\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/snappy_inputbuffer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n#include <sys/time.h>\n#include \"glog/logging.h\"\n\n#include \"monolith/native_training/data/training_instance/cc/snappy_inputbuffer.h\"\n\nnamespace tensorflow {\nnamespace io {\n\nByteSnappyInputBuffer::ByteSnappyInputBuffer(\n    RandomAccessFile* file,\n    size_t input_buffer_bytes,  // size of input_buffer_\n    size_t output_buffer_bytes  // size of output_buffer_\n    )\n    : file_(file),\n      input_buffer_capacity_(input_buffer_bytes),\n      output_buffer_capacity_(output_buffer_bytes),\n      bytes_read_(0) {\n  cached_mem_pool_ = CachedMemPool::init(input_buffer_bytes);\n  input_buffer_ = cached_mem_pool_->allocate();\n  output_buffer_ = cached_mem_pool_->allocate();\n  next_in_ = input_buffer_.get();\n  LOG_IF(ERROR, !ReadFromFile().ok()) << \"Failed to read ahead from HDFS.\";\n}\n\nByteSnappyInputBuffer::~ByteSnappyInputBuffer() {\n  cached_mem_pool_->deallocate(output_buffer_);\n  cached_mem_pool_->deallocate(input_buffer_);\n}\n\nStatus ByteSnappyInputBuffer::ReadNBytes(int64 bytes_to_read, tstring* result) {\n  result->clear();\n  result->resize_uninitialized(bytes_to_read);\n\n  char* result_ptr = result->mdata();\n\n  // Read as many bytes as possible from cache.\n  size_t bytes_read = ReadBytesFromCache(bytes_to_read, result_ptr);\n  bytes_to_read -= bytes_read;\n  result_ptr += bytes_read;\n\n  while (bytes_to_read > 0) {\n    // Now that the cache is empty we need to inflate more data.\n    TF_RETURN_IF_ERROR(Inflate());\n\n    bytes_read = ReadBytesFromCache(bytes_to_read, result_ptr);\n    bytes_to_read -= bytes_read;\n    result_ptr += bytes_read;\n  }\n\n  return Status::OK();\n}\n\nint64 ByteSnappyInputBuffer::Tell() const { return bytes_read_; }\n\nStatus ByteSnappyInputBuffer::Reset() {\n  file_pos_ = 0;\n  avail_in_ = 0;\n  avail_out_ = 0;\n  next_in_ = input_buffer_.get();\n  bytes_read_ = 0;\n  return Status::OK();\n}\n\nsize_t ByteSnappyInputBuffer::ReadBytesFromCache(size_t bytes_to_read,\n                                                 char* result_ptr) {\n  size_t can_read_bytes = std::min(bytes_to_read, avail_out_);\n  if (can_read_bytes > 0) {\n    memcpy(result_ptr, next_out_, can_read_bytes);\n    next_out_ += can_read_bytes;\n    avail_out_ -= can_read_bytes;\n    bytes_read_ += can_read_bytes;\n  }\n  return can_read_bytes;\n}\n\nStatus ByteSnappyInputBuffer::Inflate() {\n  // Output buffer must have been cleared before uncompressing more input.\n  DCHECK_EQ(avail_out_, 0);\n\n  // Read origin length of a block.\n  if (block_length_ == 0) {\n    TF_RETURN_IF_ERROR(ReadBlockLength(&block_length_));\n\n    // Output buffer must be large enough to fit the uncompressed block.\n    DCHECK_GE(output_buffer_capacity_, block_length_);\n  }\n\n  // Read length of a compressed chunk.\n  uint32 compressed_chunk_length = 0;\n  TF_RETURN_IF_ERROR(ReadBlockLength(&compressed_chunk_length));\n\n  // Read bytes to buffer a chunk\n  if (avail_in_ < compressed_chunk_length) {\n    TF_RETURN_IF_ERROR(ReadFromFile());\n    if (avail_in_ < compressed_chunk_length) {\n      if (compressed_chunk_length > input_buffer_capacity_) {\n        // TODO(gaofei.gf): increase buffer size dynamically\n        return errors::ResourceExhausted(\n            \"Input buffer(size: \", input_buffer_capacity_,\n            \" bytes) too small. Should be larger \", \"than \",\n            compressed_chunk_length, \" bytes.\");\n      } else {\n        return errors::OutOfRange(\"EOF reached with incomplete tail bytes.\");\n      }\n    }\n  }\n\n  // Uncompress a chunk\n  size_t chunk_length = 0;\n  if (!port::Snappy_GetUncompressedLength(next_in_, compressed_chunk_length,\n                                          &chunk_length)) {\n    return errors::DataLoss(\"Snappy_GetUncompressedLength failed\");\n  }\n  next_out_ = output_buffer_.get();\n  if (!port::Snappy_Uncompress(next_in_, compressed_chunk_length, next_out_)) {\n    return errors::DataLoss(\"Snappy_Uncompress failed\");\n  }\n  next_in_ += compressed_chunk_length;\n  avail_in_ -= compressed_chunk_length;\n  avail_out_ += chunk_length;\n  uncompressed_bytes_in_block_ += chunk_length;\n\n  // Check a block is uncompressed\n  if (uncompressed_bytes_in_block_ == block_length_) {\n    block_length_ = 0;\n    uncompressed_bytes_in_block_ = 0;\n  }\n  return Status::OK();\n}\n\nStatus ByteSnappyInputBuffer::ReadBlockLength(uint32* length) {\n  *length = 0;\n  size_t bytes_to_read = 4;\n  while (bytes_to_read > 0) {\n    if (avail_in_ == 0) {\n      TF_RETURN_IF_ERROR(ReadFromFile());\n    }\n    size_t readable = std::min(bytes_to_read, avail_in_);\n\n    for (int i = 0; i < readable; i++) {\n      // The \"unsigned char\" type cast is intentional to avoid implicit type\n      // casting of the signed char to unsigned int during bitwise OR which\n      // causes weird overflow errors.\n      // Little endian\n      *length = (*length << 8) | static_cast<unsigned char>(next_in_[0]);\n      bytes_to_read--;\n      next_in_++;\n      avail_in_--;\n    }\n  }\n  return Status::OK();\n}\n\nStatus ByteSnappyInputBuffer::ReadFromFile() {\n  int bytes_to_read = input_buffer_capacity_;\n  char* read_offset = reinterpret_cast<char*>(input_buffer_.get());\n\n  // If there are unread bytes in the input stream we move them to the head\n  // of the stream to maximize the space available to read new data into.\n  // TODO(srbs): A circular buffer would be useful here.\n  if (avail_in_ > 0) {\n    size_t read_bytes = next_in_ - input_buffer_.get();\n    // Remove `read_bytes` from the head of the input stream.\n    // Move unread bytes to the head of the input stream.\n    if (read_bytes > 0) {\n      memmove(input_buffer_.get(), next_in_, avail_in_);\n    }\n\n    bytes_to_read -= avail_in_;\n    read_offset += avail_in_;\n  }\n  StringPiece data;\n  // Try to read enough data to fill up input_buffer_.\n  struct timeval t0;\n  struct timeval t1;\n  size_t old_size = data.size();\n  gettimeofday(&t0, NULL);\n  Status s = Status(error::OUT_OF_RANGE, \"Read less bytes than requested\");\n  if (!reached_eof_) {\n    read_round_++;\n    s = file_->Read(file_pos_, bytes_to_read, &data, read_offset);\n  }\n  gettimeofday(&t1, NULL);\n  int64_t elapsed = (t1.tv_sec - t0.tv_sec) * 1000000 + t1.tv_usec - t0.tv_usec;\n  elapsed /= 1000;\n  auto throughput =\n      (elapsed == 0) ? 0.0 : (data.size() - old_size) / (1024 * elapsed);\n  LOG_EVERY_N(INFO, 100) << \"********************************At round: \"\n                         << read_round_\n                         << \", the expected read: \" << bytes_to_read\n                         << \" and the actual read is:  \"\n                         << (data.size() - old_size) / (1024.0 * 1024)\n                         << \" MB at timestamp: \" << elapsed\n                         << \" ms with a bandwidth: \" << throughput\n                         << \" MBps. If out of range? \"\n                         << errors::IsOutOfRange(s)\n                         << \" .*********************************\";\n  if (data.data() != read_offset) {\n    memmove(read_offset, data.data(), data.size());\n  }\n\n  // Since we moved unread data to the head of the input stream we can point\n  // next_in to the head of the input stream.\n  next_in_ = input_buffer_.get();\n\n  // Note: data.size() could be different from bytes_to_read.\n  avail_in_ += data.size();\n  file_pos_ += data.size();\n\n  // Report failure if not EoF or normal reading.\n  if (!s.ok() && !errors::IsOutOfRange(s)) {\n    return s;\n  }\n\n  // We throw OutOfRange error iff no new data has been read from file.\n  // Since we never check how much data is remaining in the file, it is\n  // possible that on the last read there isn't enough data in the file to\n  // fill up the buffer in which case file_->ReadNBytes would return an\n  // OutOfRange error.\n  if (data.empty()) {\n    reached_eof_ = true;\n    return errors::OutOfRange(\"EOF reached\");\n  }\n  if (errors::IsOutOfRange(s)) {\n    reached_eof_ = true;\n    return Status::OK();\n  }\n\n  return s;\n}\n\n}  // namespace io\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/snappy_inputbuffer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n// Code is modified based on\n// https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/lib/io/snappy/snappy_inputbuffer.h\n\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_SNAPPY_INPUTBUFFER_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_SNAPPY_INPUTBUFFER_H_\n\n#include <string>\n#include \"tensorflow/core/lib/core/status.h\"\n#include \"tensorflow/core/lib/io/inputstream_interface.h\"\n#include \"tensorflow/core/platform/env.h\"\n#include \"tensorflow/core/platform/macros.h\"\n#include \"tensorflow/core/platform/snappy.h\"\n#include \"tensorflow/core/platform/types.h\"\n\n#include \"cached_mem_pool.h\"\n\nnamespace tensorflow {\nnamespace io {\n\nusing CachedMemPool = ::tensorflow::monolith_tf::CachedMemPool;\n\n// An SnappyInputBuffer provides support for reading from a hdfs file compressed\n// using snappy (https://github.com/google/snappy).\n//\n// A Hadoop snappy compressed file contains several compressed data blocks. The\n// format of a compressed block is,\n//    uint32_t                    Uncompressed Length\n//    uint32_t                    Compressed Length\n//    byte[compressed_length]     Compressed block\n// See:\n//    https://github.com/apache/hadoop/blob/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/main/native/src/codec/SnappyCodec.cc#L35\n//\n// A given instance of an SnappyInputBuffer is NOT safe for concurrent use\n// by multiple threads\nclass ByteSnappyInputBuffer : public InputStreamInterface {\n public:\n  // Create a SnappyInputBuffer for `file` with a buffer of size\n  // `input_buffer_bytes` bytes for reading contents from `file` and another\n  // buffer with size `output_buffer_bytes` for caching decompressed contents.\n  // Does *not* take ownership of \"file\".\n  ByteSnappyInputBuffer(RandomAccessFile* file, size_t input_buffer_bytes,\n                        size_t output_buffer_bytes);\n\n  ~ByteSnappyInputBuffer() override;\n\n  // Reads bytes_to_read bytes into *result, overwriting *result.\n  //\n  // Return Status codes:\n  // OK:\n  //   If successful.\n  // OUT_OF_RANGE:\n  //   If there are not enough bytes to read before the end of the file.\n  // DATA_LOSS:\n  //   If uncompression failed or if the file is corrupted.\n  // RESOURCE_EXHAUSTED:\n  //   If input_buffer_ is smaller in size than a compressed block.\n  // others:\n  //   If reading from file failed.\n  Status ReadNBytes(int64 bytes_to_read, tstring* result) override;\n\n  int64 Tell() const override;\n\n  Status Reset() override;\n\n private:\n  // Reads data from `file_` and tries to fill up `input_buffer_` if enough\n  // unread data is left in `file_`.\n  //\n  // Looks up `next_in_` to check how much data in `input_buffer_`\n  // has already been read. The used data is removed and new data is added to\n  // after any unread data in `input_buffer_`.\n  // After this call `next_in` points to the start of `input_buffer_`\n  // and `avail_in_` stores the number of readable bytes in\n  // `input_buffer_`.\n  //\n  // Returns OutOfRange error if NO data could be read from file. Note that this\n  // won't return an OutOfRange if there wasn't sufficient data in file to\n  // completely fill up `input_buffer_`.\n  Status ReadFromFile();\n\n  // 1. Reads the uncompressed length of the next compressed block\n  //    stored in the next 4 bytes at `next_in_`.\n  // 2. Reads the compressed length of the next compressed block\n  //    stored in the next 4 bytes at `next_in_`.\n  // 3. Uncompresses the next compressed block and writes the output\n  //    produced to the output_buffer_.\n  //\n  // Should be called only after the cached output has been consumed.\n  Status Inflate();\n\n  // Starts reading bytes at `next_out_` till either `bytes_to_read`\n  // bytes have been read or `next_out_` is reached.\n  // Returns the number of bytes read and advances the `next_out_`\n  // pointer to the next offset to read from.\n  size_t ReadBytesFromCache(size_t bytes_to_read, char* result);\n\n  // Reads the length of the next *compressed* block and stores in `length`.\n  // The length is stored in 4 bytes in little endian notation.\n  // For each block, call this method for two times. The first one read the\n  // uncompressed length, the second one read the compressed.\n  Status ReadBlockLength(uint32* length);\n\n  RandomAccessFile* file_;         // Not owned\n  int64 file_pos_ = 0;             // Next position to read from in `file_`\n  size_t input_buffer_capacity_;   // Size of `input_buffer_`.\n                                   // Must be at least as big as the size of\n                                   // the largest compressed block.\n  size_t output_buffer_capacity_;  // Size of `output_buffer_`\n\n  // Singleton memory pool.\n  CachedMemPool* cached_mem_pool_;\n\n  // Buffer for storing contents read from compressed file.\n  // TODO(srbs): Consider using circular buffers. That would greatly simplify\n  // the implementation.\n  std::unique_ptr<char[]> input_buffer_;\n  // Buffer for storing inflated contents of `file_`.\n  std::unique_ptr<char[]> output_buffer_;\n\n  // Next unread byte in `input_buffer_`.\n  char* next_in_;\n\n  // Next unread byte in `output_buffer_`\n  char* next_out_;\n\n  // Number of unread bytes bytes available at `next_in_` in `input_buffer_`.\n  size_t avail_in_ = 0;\n\n  // Number of unread bytes bytes available at `next_out_` in `output_buffer_`.\n  size_t avail_out_ = 0;\n\n  // Number of uncompressed bytes has been read.\n  int64 bytes_read_ = 0;\n\n  // States when uncompressing a block\n  uint32 block_length_ = 0;\n  uint32 uncompressed_bytes_in_block_ = 0;\n\n  bool reached_eof_ = false;\n  uint32 read_round_ = 0;\n  TF_DISALLOW_COPY_AND_ASSIGN(ByteSnappyInputBuffer);\n};\n\n}  // namespace io\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_SNAPPY_INPUTBUFFER_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/ue_compress.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <string.h>\n\n#include \"monolith/native_training/data/training_instance/cc/ue_compress.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing matrix::compression::Float16;\n\nconst char* UE_COMPRESS_FLAG = \"UE_QTZ\";\n\nbool UECompress::compress_embeddings(\n    ::idl::matrix::proto::Feature* feature_column,\n    UECompressMethod compress_method) {\n  if (compress_method == UECompressMethod::COMPRESS_QTZ8) {\n    auto* bytes_value = feature_column->add_bytes_value();\n    int embedding_size = feature_column->float_value_size();\n    std::vector<float> compress_input;\n    compress_input.reserve(embedding_size);\n    for (auto value : feature_column->float_value()) {\n      compress_input.push_back(value);\n    }\n    std::string compress_out;\n    bool ret = matrix::compression::compress_float_list_qtz8mm(\n        (const char*)compress_input.data(), embedding_size * sizeof(float),\n        &compress_out);\n    if (!ret) {\n      LOG(ERROR) << \"compress_embeddings failed, feature_column name=%s\"\n                 << feature_column->name().c_str();\n      return false;\n    }\n    *bytes_value = UE_COMPRESS_FLAG + compress_out;\n    return true;\n  } else {\n    LOG(ERROR) << \"compress_embeddings invalid compress method:%d\"\n               << compress_method;\n    return false;\n  }\n}\n\nbool UECompress::decompress_embeddings(\n    const idl::matrix::proto::Feature& feature_column,\n    std::vector<float>* embedding, UECompressMethod compress_method) {\n  if (compress_method == UECompressMethod::COMPRESS_QTZ8) {\n    std::string compress_out;\n    std::string bytes_value;\n    for (auto& value : feature_column.bytes_value()) {\n      if (value.find(UE_COMPRESS_FLAG) == 0) {\n        bytes_value = value.substr(strlen(UE_COMPRESS_FLAG));\n      }\n    }\n    if (bytes_value.empty()) {\n      return false;\n    }\n    bool ret = matrix::compression::decompress_float_list_qtz8mm(\n        (const char*)bytes_value.data(), bytes_value.size(), &compress_out);\n    if (!ret) {\n      LOG(ERROR) << \"decompress_embeddings failed, feature_column name=%s\"\n                 << feature_column.name().c_str();\n      return false;\n    }\n    size_t embedding_size = bytes_value.size() - 2 * sizeof(Float16);\n    const float* output = reinterpret_cast<const float*>(compress_out.data());\n    embedding->clear();\n    for (size_t i = 0; i < embedding_size; ++i) {\n      embedding->emplace_back(output[i]);\n    }\n    return true;\n  } else {\n    LOG(ERROR) << \"decompress_embeddings invalid compress method:%d\"\n               << compress_method;\n    return false;\n  }\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/ue_compress.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_UE_COMPRESS_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_UE_COMPRESS_H_\n\n#include \"glog/logging.h\"\n#include \"idl/matrix/compression/compression.h\"\n#include \"idl/matrix/compression/float16.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nenum UECompressMethod {\n  COMPRESS_QTZ8 = 0  // 8bit 量化\n};\n\nclass UECompress {\n public:\n  UECompress() = default;\n  virtual ~UECompress() = default;\n\n  bool compress_embeddings(::idl::matrix::proto::Feature* feature_column,\n                           UECompressMethod compress_method);\n  bool decompress_embeddings(\n      const ::idl::matrix::proto::Feature& feature_column,\n      std::vector<float>* embedding, UECompressMethod compress_method);\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRAINING_INSTANCE_CC_UE_COMPRESS_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/ue_compress_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"ue_compress.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\n\nTEST(UECompressTest, Basic) {\n  std::shared_ptr<UECompress> ue_compress_ = std::make_shared<UECompress>();\n  std::vector<float> float_values;\n  float_values.push_back(1.1);\n  float_values.push_back(0.1);\n  float_values.push_back(3.1);\n  float_values.push_back(5.1);\n  float_values.push_back(2.2);\n  float_values.push_back(3.3);\n  float_values.push_back(4.3);\n  ::idl::matrix::proto::Feature feature;\n  feature.set_name(\"fc_test\");\n  feature.mutable_float_value()->Reserve(float_values.size());\n  for (auto& v : float_values) {\n    feature.add_float_value(v);\n  }\n\n  ue_compress_->compress_embeddings(&feature, UECompressMethod::COMPRESS_QTZ8);\n  for (auto& values : feature.bytes_value()) {\n    std::cout << \"values \" << values << std::endl;\n  }\n\n  feature.clear_float_value();\n  std::vector<float> embedding;\n  bool ret = ue_compress_->decompress_embeddings(\n      feature, &embedding, UECompressMethod::COMPRESS_QTZ8);\n  ASSERT_TRUE(ret);\n\n  ASSERT_EQ(float_values.size(), embedding.size());\n  for (int i = 0; i < embedding.size(); i++) {\n    ASSERT_THAT(embedding, Pointwise(FloatNear(1e-2), float_values));\n  }\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/zstd_inputbuffer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n#include \"monolith/native_training/data/training_instance/cc/zstd_inputbuffer.h\"\n\n#include <sys/time.h>\n#include \"tensorflow/core/platform/errors.h\"\n#include \"tensorflow/core/platform/logging.h\"\n#include \"tensorflow/core/platform/strcat.h\"\n\nnamespace tensorflow {\nnamespace io {\n\nMonolithZstdInputStream::MonolithZstdInputStream(\n    InputStreamInterface* input_stream, size_t input_buffer_bytes,\n    size_t output_buffer_bytes, bool owns_input_stream)\n    : owns_input_stream_(owns_input_stream),\n      input_stream_(input_stream),\n      input_buffer_(new char[input_buffer_bytes]),\n      input_buffer_capacity_(input_buffer_bytes),\n      output_buffer_(new char[output_buffer_bytes]),\n      output_buffer_capacity_(output_buffer_bytes),\n      bytes_read_(0) {\n  InitZstdBuffer();\n}\n\nMonolithZstdInputStream::MonolithZstdInputStream(\n    InputStreamInterface* input_stream, size_t input_buffer_bytes,\n    size_t output_buffer_bytes)\n    : MonolithZstdInputStream(input_stream, input_buffer_bytes,\n                              output_buffer_bytes, false) {}\n\nMonolithZstdInputStream::~MonolithZstdInputStream() {\n  ZSTD_freeDCtx(context_);\n  if (owns_input_stream_) {\n    delete input_stream_;\n  }\n}\n\nvoid MonolithZstdInputStream::InitZstdBuffer() {\n  context_ = ZSTD_createDCtx();\n  if (context_ == nullptr) {\n    LOG(FATAL) << \"Creation of context failed.\";\n  }\n  next_in_byte_ = input_buffer_.get();\n  zstd_input_buffer_ = {next_in_byte_, 0, 0};\n  next_unread_byte_ = output_buffer_.get();\n  unread_bytes_ = 0;\n  avail_in_ = 0;\n}\n\nStatus MonolithZstdInputStream::Reset() {\n  TF_RETURN_IF_ERROR(input_stream_->Reset());\n  ZSTD_DCtx_reset(context_, ZSTD_reset_session_only);\n  InitZstdBuffer();\n  bytes_read_ = 0;\n  return Status::OK();\n}\n\nsize_t MonolithZstdInputStream::ReadBytesFromCache(size_t bytes_to_read,\n                                                   tstring* result) {\n  size_t can_read_bytes = std::min(bytes_to_read, unread_bytes_);\n  if (can_read_bytes > 0) {\n    tstring cached_result;\n    cached_result.append(next_unread_byte_, can_read_bytes);\n    result->append(next_unread_byte_, can_read_bytes);\n  }\n  next_unread_byte_ += can_read_bytes;\n  unread_bytes_ -= can_read_bytes;\n  bytes_read_ += can_read_bytes;\n  return can_read_bytes;\n}\n\nStatus MonolithZstdInputStream::ReadNBytes(int64 bytes_to_read,\n                                           tstring* result) {\n  result->clear();\n  bytes_to_read -= ReadBytesFromCache(bytes_to_read, result);\n\n  while (bytes_to_read > 0) {\n    // No bytes should be left in the cache.\n    CHECK_EQ(unread_bytes_, 0);\n\n    // Now that the cache is empty we need to inflate more data.\n    next_unread_byte_ = output_buffer_.get();\n\n    TF_RETURN_IF_ERROR(Inflate());\n\n    // If no progress was made by inflate, read more compressed data from the\n    // input stream.\n    if (unread_bytes_ == 0) {\n      TF_RETURN_IF_ERROR(ReadFromStream());\n      if (avail_in_ == 0) {\n        bytes_to_read = 0;\n      }\n    } else {\n      bytes_to_read -= ReadBytesFromCache(bytes_to_read, result);\n    }\n  }\n\n  return Status::OK();\n}\n\n#if defined(TF_CORD_SUPPORT)\nStatus MonolithZstdInputStream::ReadNBytes(int64 bytes_to_read,\n                                           absl::Cord* result) {\n  // TODO(frankchn): Optimize this instead of bouncing through the buffer.\n  tstring buf;\n  TF_RETURN_IF_ERROR(ReadNBytes(bytes_to_read, &buf));\n  result->Clear();\n  result->Append(buf.data());\n  return Status::OK();\n}\n#endif\n\nStatus MonolithZstdInputStream::Inflate() {\n  ZSTD_outBuffer output = {next_unread_byte_, output_buffer_capacity_, 0};\n  last_return_ = ZSTD_decompressStream(context_, &output, &zstd_input_buffer_);\n\n  if (ZSTD_isError(last_return_)) {\n    string error_name = ZSTD_getErrorName(last_return_);\n    string error_string =\n        strings::StrCat(\"ZSTD_decompressStream: \", error_name);\n    return errors::DataLoss(error_string);\n  }\n\n  avail_in_ = 0;\n  unread_bytes_ = output.pos;\n\n  return Status::OK();\n}\n\nStatus MonolithZstdInputStream::ReadFromStream() {\n  size_t bytes_to_read = input_buffer_capacity_;\n  char* read_location = input_buffer_.get();\n\n  // If there are unread bytes in the input stream we move them to the head\n  // of the stream to maximize the space available to read new data into.\n  if (avail_in_ > 0) {\n    size_t read_bytes = next_in_byte_ - input_buffer_.get();\n    // Remove `read_bytes` from the head of the input stream.\n    // Move unread bytes to the head of the input stream.\n    if (read_bytes > 0) {\n      memmove(input_buffer_.get(), next_in_byte_, avail_in_);\n    }\n\n    bytes_to_read -= avail_in_;\n    read_location += avail_in_;\n  }\n\n  tstring data;\n  Status s = input_stream_->ReadNBytes(bytes_to_read, &data);\n  memcpy(read_location, data.data(), data.size());\n\n  // Note: data.size() could be different from bytes_to_read.\n  avail_in_ += data.size();\n  zstd_input_buffer_.pos = 0;\n  zstd_input_buffer_.size = data.size();\n\n  if (!s.ok() && !errors::IsOutOfRange(s)) {\n    return s;\n  }\n\n  if (errors::IsOutOfRange(s)) {\n    return Status::OK();\n  }\n\n  return s;\n}\n\nint64 MonolithZstdInputStream::Tell() const { return bytes_read_; }\n\n}  // namespace io\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/cc/zstd_inputbuffer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n// Code is modified based on\n// https://github.com/tensorflow/tensorflow/compare/master...IAL32:tensorflow:ac/add-zstd-support\n\n#ifndef TENSORFLOW_MONOLITH_IO_ZSTD_ZSTD_INPUTSTREAM_H_\n#define TENSORFLOW_MONOLITH_IO_ZSTD_ZSTD_INPUTSTREAM_H_\n\n#define ZSTD_STATIC_LINKING_ONLY\n#include <zstd.h>\n\n#include \"tensorflow/core/lib/io/inputstream_interface.h\"\n#include \"tensorflow/core/platform/env.h\"\n#include \"tensorflow/core/platform/macros.h\"\n#include \"tensorflow/core/platform/types.h\"\n\nnamespace tensorflow {\nnamespace io {\n\nclass MonolithZstdInputStream : public InputStreamInterface {\n public:\n  // Creates a MonolithZstdInputStream for `input_stream`.\n  //\n  // Takes ownership  of `input_stream` iff `owns_input_stream` is true.\n  MonolithZstdInputStream(InputStreamInterface* input_stream,\n                          size_t input_buffer_bytes, size_t output_buffer_bytes,\n                          bool owns_input_stream);\n\n  // Equivalent to the previous constructor with owns_input_stream=false.\n  MonolithZstdInputStream(InputStreamInterface* input_stream,\n                          size_t input_buffer_bytes,\n                          size_t output_buffer_bytes);\n\n  ~MonolithZstdInputStream() override;\n\n  // Reads bytes_to_read bytes into *result, overwriting *result.\n  //\n  // Return Status codes:\n  // OK:           If successful.\n  // OUT_OF_RANGE: If there are not enough bytes to read before\n  //               the end of the stream.\n  // ABORTED:      If inflate() fails, we return the error code with the\n  //               error message in `z_stream_->msg`.\n  // others:       If reading from stream failed.\n  Status ReadNBytes(int64 bytes_to_read, tstring* result) override;\n\n#if defined(TF_CORD_SUPPORT)\n  Status ReadNBytes(int64 bytes_to_read, absl::Cord* result) override;\n#endif\n\n  int64 Tell() const override;\n\n  Status Reset() override;\n\n private:\n  // Decompress the next chunk of data and place the data into the cache.\n  Status Inflate();\n\n  Status ReadFromStream();\n\n  // There may be bytes leftover from last read. We read them so that we don't\n  // lose them, and we optimize resources.\n  size_t ReadBytesFromCache(size_t bytes_to_read, tstring* result);\n\n  void InitZstdBuffer();\n\n  const bool owns_input_stream_;\n  InputStreamInterface* input_stream_;\n  std::unique_ptr<char[]> input_buffer_;\n  size_t input_buffer_capacity_;  // Size of input_buffer_\n  char* next_in_byte_;            // Next unread byte to decompress\n  size_t avail_in_;  // Number of bytes available to be decompressed\n  ZSTD_inBuffer zstd_input_buffer_;\n\n  std::unique_ptr<char[]> output_buffer_;  // Inflated buffer\n  size_t output_buffer_capacity_;          // Size of output_buffer_\n  char* next_unread_byte_;                 // Next unread byte in output_buffer_\n  // bytes left in the output_buffer_ not yet read.\n  size_t unread_bytes_;\n\n  ZSTD_DCtx* context_;\n\n  // Specifies the number of decompressed bytes currently read.\n  size_t bytes_read_;\n\n  size_t last_return_;\n\n  TF_DISALLOW_COPY_AND_ASSIGN(MonolithZstdInputStream);\n};\n\n}  // namespace io\n}  // namespace tensorflow\n\n#endif  // TENSORFLOW_MONOLITH_IO_ZSTD_ZSTD_INPUTSTREAM_H_\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/python/instance_dataset_op.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport os\nfrom enum import Enum\n\nimport tensorflow as tf\n\nfrom tensorflow.python import tf2\nfrom tensorflow.python.data.experimental.ops import matching_files\nfrom tensorflow.python.data.ops import dataset_ops\nfrom tensorflow.python.data.util import convert\nfrom tensorflow.python.framework import dtypes\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.platform import resource_loader\nfrom tensorflow.python.framework import tensor_shape\nfrom tensorflow.python.framework import tensor_spec\nfrom tensorflow.python.util.tf_export import tf_export\n\nfrom monolith.native_training.distribute import distributed_dataset\nfrom monolith.native_training.hooks import ckpt_hooks\nfrom monolith.native_training.runner_utils import RunnerConfig\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\ninstance_dataset_op = gen_monolith_ops\n\n\nclass _PBInstanceDataset(dataset_ops.DatasetSource):\n\n  def __init__(self, file_name, use_snappy=False, **kwargs):\n    self._file_name = file_name\n    self._use_snappy = use_snappy\n\n    self._has_sort_id = kwargs.get('has_sort_id', True)\n    self._kafka_dump = kwargs.get('kafka_dump', False)\n    self._kafka_dump_prefix = kwargs.get('kafka_dump_prefix', False)\n\n    variant_tensor = instance_dataset_op.instance_dataset(\n        file_name=tf.convert_to_tensor(self._file_name, dtype=tf.string),\n        use_snappy=tf.convert_to_tensor(self._use_snappy, dtype=tf.bool),\n        has_sort_id=tf.convert_to_tensor(self._has_sort_id, dtype=tf.bool),\n        kafka_dump=tf.convert_to_tensor(self._kafka_dump, dtype=tf.bool),\n        kafka_dump_prefix=tf.convert_to_tensor(self._kafka_dump_prefix,\n                                               dtype=tf.bool))\n    logging.info(\"Start init of the pb instance dataset base.\")\n    super(_PBInstanceDataset, self).__init__(variant_tensor)\n\n  @property\n  def element_spec(self):\n    return tensor_spec.TensorSpec([], dtypes.string)\n\n\nclass PBInstanceDatasetV2(dataset_ops.DatasetV2):\n  \"\"\"从标准输入/pb文件中读取序列化Instance, 不反序列化\n\n  Args:\n    file_name (:obj:`str`): 文件名, 如果为空, 则从stdin读取数据\n    use_snappy (:obj:`str`): 输入文件是不否是snappy压缩的\n    has_sort_id (:obj:`bool`): 输入数据中是否带8 bytes前缀标识, 表明sort_id\n    kafka_dump (:obj:`bool`): 输入数据中是否带8 bytes前缀标识, 表明kafka_dump\n    kafka_dump_prefix (:obj:`bool`): 输入数据中是否带8 bytes前缀标识, 表明kafka_dump_prefix\n    \n  Raises:\n    TypeError: 如果有任何参数与类型不匹配, 则抛TypeError\n    ValueError: 如果有任何值与期望不匹配, 则抛ValueError\n  \n  \"\"\"\n\n  def __init__(self, file_name, use_snappy=False, **kwargs):\n    self._file_name = file_name\n    self._use_snappy = use_snappy\n    self._kwargs = kwargs\n\n    if isinstance(file_name, str) and not file_name:\n      # This is the special case that dataset uses stdin as the input.\n      # In this case, we should diable the ckpt save/restore.\n      ckpt_hooks.disable_iterator_save_restore()\n\n    def creator_fn():\n      return _PBInstanceDataset(file_name, use_snappy, **self._kwargs)\n\n    self._impl = creator_fn()\n    variant_tensor = self._impl._variant_tensor\n    logging.info(\"Start init of the pb instance dataset v2\")\n    super(PBInstanceDatasetV2, self).__init__(variant_tensor)\n    logging.info(\"Finish init of the pb instance dataset v2\")\n\n  def _clone(self, file_name, use_snappy=False, **kwargs):\n    _kwargs = self._kwargs.copy()\n    _kwargs.update(kwargs)\n    return PBInstanceDatasetV2(file_name or self._file_name, use_snappy or\n                               self._use_snappy, **_kwargs)\n\n  @property\n  def element_spec(self):\n    return tensor_spec.TensorSpec([], dtypes.string)\n\n  def _inputs(self):\n    return []\n\n\n#TODO(leqi.zou): We should rewrite this to make it more clear.\ndef create_instance_dataset(files_list=None,\n                            use_snappy=False,\n                            expand_glob_path=False,\n                            cycle_length=4,\n                            num_parallel_calls=4,\n                            block_length=1,\n                            enable_sharding: bool = False,\n                            shard_index: int = None,\n                            shard_num: int = None,\n                            enable_dynamic_sharding=False,\n                            **kwargs):\n  if files_list is None:\n    # use stdin\n    files_list = [\"\"]\n  if len(\n      files_list\n  ) == 1 and not expand_glob_path and not enable_sharding and not enable_dynamic_sharding:\n    if len(files_list[0]) > 0 and not tf.io.gfile.exists(files_list[0]):\n      logging.fatal('File not found: {}'.format(files_list[0]))\n    return PBInstanceDatasetV2(file_name=files_list[0],\n                               use_snappy=use_snappy,\n                               **kwargs)\n  map_func = lambda file_name: PBInstanceDatasetV2(\n      file_name=file_name, use_snappy=use_snappy, **kwargs)\n  if enable_dynamic_sharding:\n    files_list = distributed_dataset.create_dynamic_sharding_dataset(files_list)\n    return files_list.flat_map(map_func)\n  elif not enable_sharding:\n    if expand_glob_path:\n      files_list = matching_files.MatchingFilesDataset(files_list)\n    else:\n      files_list = tf.data.Dataset.from_tensor_slices(files_list)\n  else:\n    # We should use only 1 pattern for the sharded hdfs reading.\n    assert len(files_list) == 1\n    # List all the files via the list_files op.\n    files_list = matching_files.MatchingFilesDataset(files_list)\n    # Shard it according to the preallocated index.\n    files_list = files_list.shard(shard_num, shard_index)\n    logging.info(\"Shard the input files for shard {}/{}.\".format(\n        shard_index, shard_num))\n    use_snappy = True\n\n  dataset = files_list.interleave(map_func=map_func,\n                                  cycle_length=cycle_length,\n                                  block_length=block_length,\n                                  num_parallel_calls=num_parallel_calls,\n                                  deterministic=False)\n  return dataset\n\n\nPBInstanceDataset = PBInstanceDatasetV2\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/python/instance_dataset_op_test_stdin.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport tensorflow as tf\n\nfrom absl import logging\nfrom monolith.native_training.data.training_instance.python.instance_dataset_ops import PBInstanceDataset\nfrom monolith.native_training.data.training_instance.python.parse_instance_ops import parse_instances\nfrom tensorflow.python.framework import sparse_tensor\n\nFIDV1_FEATURES = [i for i in range(1, 10)]\nFIDV2_FEATURES = [\"fc_360d_ml_convert_cid\", \"fc_360d_ml_convert_advertiser_id\"]\nFLOAT_FEATURES = [\"fc_muse_finish_rough_10168_uid_d128\"]\nFLOAT_FEATURES_DIM = [128]\nINT64_FEATURES = [\"fc_dense_external_action\"]\nINT64_FEATURE_DIM = [1]\n\n\ndef parse(serialized):\n  return parse_instances(serialized, FIDV1_FEATURES, FIDV2_FEATURES,\n                         FLOAT_FEATURES, FLOAT_FEATURES_DIM, INT64_FEATURES,\n                         INT64_FEATURE_DIM)\n\n\ndef testInstanceDataset():\n\n  # with self.session() as sess:\n  with tf.compat.v1.Session() as sess:\n    logging.warning(\"PBInstanceDatasetV2 process is Starting\")\n    dataset = PBInstanceDataset(\n        file_name=\"\",\n        has_sort_id=True,\n        kafka_dump_prefix=True,\n    )\n    dataset = dataset.batch(32).map(parse)\n    it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n    element = it.get_next()\n    logging.warning(\"PBInstanceDatasetV2 next process is Finished\")\n    elements = sess.run(element)\n    logging.warning(element)\n    logging.warning(elements[\"sample_rate\"])\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  testInstanceDataset()\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/python/instance_negative_gen_dataset_op_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport tensorflow as tf\n\nfrom absl import logging\nimport numpy as np\nfrom collections import defaultdict\n\nfrom monolith.native_training.data.training_instance.python.instance_dataset_op import PBDataset, PbType, PBInstanceDataset, InstanceNegativeGenDataset\nfrom monolith.native_training.data.training_instance.python.parse_instance_ops import parse_variant_instances, parse_instances\nfrom monolith.native_training.data.training_instance.python.pb_datasource_ops import variant_dummy\nfrom tensorflow.python.framework import sparse_tensor\n\nFILE_NAME = 'monolith/native_training/data/training_instance/instance.pb'\nCHANNEL_SLOT = 357\nGROUP_SLOTS = [200,201,202,203,204,205,206,210,211,212,213,214,215,\\\n        216,217,218,219,220,221,222,223,224,225,230,231,232,233,234,235,236,237,238,239,240,241,242]\nLABEL_FIELD = 'actions'\nLABEL_INDEX = 0\nNEGATIVE_LABEL = -2\nNEGATIVE_LABEL2 = -1\nCHANNEL_FEATURE_NAME = \"\"\nGROUP_FEATURES_NAME = []\nGID = 'gid'\n\nCHANNEL_SLOT_NAME = 'slot_' + str(CHANNEL_SLOT)\nGROUP_SLOT_NAME = 'slot_200'\n\nCHANNEL = 6435440280980561277\n\n\ndef parse1(pb_varient: tf.Tensor):\n  FIDV1_FEATURES = [\n    1, 3, 4, 5, 7, 8, 9, 31, 32, 33, 35, 36, 37, 38, 42, 44, 60, 61, 62, 63, 65, 66, 67, 68, 72, 74, 90, 91, 92, 93, 95, 120, \\\n    121, 122, 123, 125, 126, 128, 150, 151, 152, 153, 155, 156, 158, 180, 181, 182, 183, 185, 186, 188, 192, 193, 194, 200, 201, \\\n    202, 204, 206, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 230, 231, 232, 233, 234, 235, \\\n    236, 237, 238, 239, 240, 242, 357, 358, 359, 360, 361, 410, 411, 412, 413, 415, 416, 418, 422, 423, 424, 446, 472, 475, 515, 516\n  ]\n  return parse_variant_instances(pb_varient,\n                                 FIDV1_FEATURES,\n                                 misc_int64_features=[GID])\n\n\nclass InsNegativeDatasetTest(tf.test.TestCase):\n\n  def testNegativeGen(self):\n    with self.session() as sess:\n      dataset = PBDataset(file_name=FILE_NAME,\n                          has_sort_id=True,\n                          kafka_dump=True,\n                          kafka_dump_prefix=False,\n                          input_pb_type=PbType.Instance,\n                          output_pb_type=PbType.Instance)\n\n      dataset = dataset.negative_gen(neg_num=7,\n                                     channel_slot=CHANNEL_SLOT,\n                                     group_slots=GROUP_SLOTS,\n                                     per_channel_sample=True,\n                                     start_num=0,\n                                     max_group_num_per_channel=10000,\n                                     label_field=LABEL_FIELD,\n                                     label_index=0,\n                                     negative_label=NEGATIVE_LABEL,\n                                     use_neg_ins=True)\n\n      dataset = dataset.batch(8, drop_remainder=True).map(parse1)\n      it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n      element = it.get_next()\n      count = 0\n\n      channel_res = []\n      group_res = []\n      label_res = []\n\n      while True:\n        try:\n          ret = sess.run(element)\n          channel_res.append(ret[CHANNEL_SLOT_NAME].flat_values)\n          group_res.append(ret[GROUP_SLOT_NAME].flat_values)\n          label_res.append(ret[LABEL_FIELD])\n          count += 8\n          if count > 16:\n            break\n        except tf.errors.OutOfRangeError:\n          logging.info(\"got eof\")\n          break\n\n      for i in range(1, 8):\n        self.assertEqual(channel_res[0][0], channel_res[0][i])\n      self.assertEqual(label_res[0][1], NEGATIVE_LABEL)\n\n  def testRingBufferCache(self):\n    with self.session() as sess:\n      dataset = PBDataset(file_name=FILE_NAME,\n                          has_sort_id=True,\n                          kafka_dump=True,\n                          kafka_dump_prefix=False,\n                          input_pb_type=PbType.Instance,\n                          output_pb_type=PbType.Instance)\n\n      max_group_num_per_channel = 2\n      dataset = dataset.negative_gen(\n          neg_num=7,\n          channel_slot=CHANNEL_SLOT,\n          group_slots=GROUP_SLOTS,\n          per_channel_sample=True,\n          start_num=0,\n          max_group_num_per_channel=max_group_num_per_channel,\n          label_field=LABEL_FIELD,\n          label_index=0,\n          negative_label=NEGATIVE_LABEL,\n          use_neg_ins=True)\n      dataset = dataset.batch(8, drop_remainder=True).map(parse1)\n      it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n      element = it.get_next()\n      count = 0\n\n      channel_res = []\n      group_res = []\n      label_res = []\n      gid_res = []\n\n      while True:\n        try:\n          ret = sess.run(element)\n          channel_res.append(ret[CHANNEL_SLOT_NAME].flat_values)\n          group_res.append(ret[GROUP_SLOT_NAME].flat_values)\n          label_res.append(ret[LABEL_FIELD])\n          gid_res.append(ret[GID])\n\n          count += 8\n          if count > 1024:\n            break\n        except tf.errors.OutOfRangeError:\n          logging.info(\"got eof\")\n          break\n\n      res_by_channel = defaultdict(list)\n      for i in range(100):\n        channel = channel_res[i][0]\n        res_by_channel[channel].append(i)\n\n      valid_count = 0\n      for channel in res_by_channel:\n        one_channel_res = res_by_channel[channel]\n        if len(one_channel_res) <= max_group_num_per_channel:\n          continue\n        idx0 = one_channel_res[0]\n        idx1 = one_channel_res[1]\n        idx2 = one_channel_res[2]\n\n\n        if gid_res[idx0][0] != gid_res[idx1][0] and gid_res[idx0][0] != gid_res[idx2][0] \\\n                and gid_res[idx1][0] != gid_res[idx2][0]:\n          for fid in group_res[idx2]:\n            self.assertNotIn(fid, group_res[idx0])\n            valid_count += 1\n      logging.info('checkout count ' + str(valid_count))\n\n  def testIgnoreReaNegInstance(self):\n    with self.session() as sess:\n      dataset = PBDataset(file_name=FILE_NAME,\n                          has_sort_id=True,\n                          kafka_dump=True,\n                          kafka_dump_prefix=False,\n                          input_pb_type=PbType.Instance,\n                          output_pb_type=PbType.Instance)\n\n      dataset = dataset.negative_gen(neg_num=7,\n                                     channel_slot=CHANNEL_SLOT,\n                                     group_slots=GROUP_SLOTS,\n                                     per_channel_sample=True,\n                                     start_num=0,\n                                     max_group_num_per_channel=10000,\n                                     label_field=LABEL_FIELD,\n                                     label_index=0,\n                                     negative_label=NEGATIVE_LABEL,\n                                     use_neg_ins=True)\n      dataset = InstanceNegativeGenDataset(input_dataset=dataset,\n                                           neg_num=2,\n                                           channel_slot=CHANNEL_SLOT,\n                                           group_slots=GROUP_SLOTS,\n                                           per_channel_sample=True,\n                                           start_num=0,\n                                           max_group_num_per_channel=10000,\n                                           label_field=LABEL_FIELD,\n                                           label_index=0,\n                                           negative_label=NEGATIVE_LABEL2,\n                                           use_neg_ins=False)\n      dataset = dataset.batch(8, drop_remainder=True).map(parse1)\n      it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n      element = it.get_next()\n      count = 0\n\n      channel_res = []\n      group_res = []\n      label_res = []\n\n      while True:\n        try:\n          ret = sess.run(element)\n          label_res.append(ret[LABEL_FIELD])\n          count += 8\n          if count > 16:\n            break\n        except tf.errors.OutOfRangeError:\n          logging.info(\"got eof\")\n          break\n\n      self.assertEqual(label_res[0][1], NEGATIVE_LABEL2)\n\n  def testUseNegInstance(self):\n    with self.session() as sess:\n      dataset = PBDataset(file_name=FILE_NAME,\n                          has_sort_id=True,\n                          kafka_dump=True,\n                          kafka_dump_prefix=False,\n                          input_pb_type=PbType.Instance,\n                          output_pb_type=PbType.Instance)\n\n      dataset = dataset.negative_gen(neg_num=2,\n                                     channel_slot=CHANNEL_SLOT,\n                                     group_slots=GROUP_SLOTS,\n                                     per_channel_sample=True,\n                                     start_num=0,\n                                     max_group_num_per_channel=10000,\n                                     label_field=LABEL_FIELD,\n                                     label_index=0,\n                                     negative_label=NEGATIVE_LABEL,\n                                     use_neg_ins=True)\n      dataset = InstanceNegativeGenDataset(input_dataset=dataset,\n                                           neg_num=2,\n                                           channel_slot=CHANNEL_SLOT,\n                                           group_slots=GROUP_SLOTS,\n                                           per_channel_sample=True,\n                                           start_num=0,\n                                           max_group_num_per_channel=10000,\n                                           label_field=LABEL_FIELD,\n                                           label_index=0,\n                                           negative_label=NEGATIVE_LABEL2,\n                                           use_neg_ins=True)\n      dataset = dataset.batch(8, drop_remainder=True).map(parse1)\n      it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n      element = it.get_next()\n      count = 0\n\n      channel_res = []\n      group_res = []\n      label_res = []\n\n      while True:\n        try:\n          ret = sess.run(element)\n          label_res.append(ret[LABEL_FIELD])\n          count += 8\n          if count > 16:\n            break\n        except tf.errors.OutOfRangeError:\n          logging.info(\"got eof\")\n          break\n\n      self.assertEqual(label_res[0][1], NEGATIVE_LABEL2)\n      self.assertEqual(label_res[0][2], NEGATIVE_LABEL2)\n      self.assertEqual(label_res[0][3], NEGATIVE_LABEL)\n      self.assertEqual(label_res[0][4], NEGATIVE_LABEL)\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/python/parse_instance_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport os\nimport struct\nfrom typing import Dict, List, Iterable, Callable\n\nimport tensorflow as tf\nfrom tensorflow.python.platform import resource_loader\nfrom tensorflow.python.framework import load_library\nfrom tensorflow.python.ops.ragged.row_partition import RowPartition, _row_partition_factory_key\n\nfrom monolith.native_training.data.utils import get_slot_feature_name\nfrom monolith.native_training.data.training_instance.python.parser_utils import \\\n  add_extra_parse_step, advanced_parse\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\nparse_instance_ops = gen_monolith_ops\n\n\ndef _parse_instance_impl(\n    serialized: tf.Tensor, fidv1_features: List[int], fidv2_features: List[str],\n    float_features: List[str], float_feature_dims: List[int],\n    int64_features: List[str], int64_feature_dims: List[int],\n    string_features: List[str], string_feature_dims: List[int],\n    misc_float_features: List[str], misc_float_dims: List[int],\n    misc_int64_features: List[str], misc_int64_dims: List[int],\n    misc_string_features: List[str], misc_string_dims: List[int],\n    cc_op: Callable):\n  fidv1_features = fidv1_features or []\n  fidv2_features = fidv2_features or []\n  float_features = float_features or []\n  float_feature_dims = float_feature_dims or []\n  int64_features = int64_features or []\n  int64_feature_dims = int64_feature_dims or []\n  string_features = string_features or []\n  string_feature_dims = string_feature_dims or []\n  misc_float_features = misc_float_features or []\n  misc_float_dims = misc_float_dims or []\n  misc_int64_features = misc_int64_features or []\n  misc_int64_dims = misc_int64_dims or []\n  misc_string_features = misc_string_features or []\n  misc_string_dims = misc_string_dims or []\n  (ragged_feature_splits, ragged_feature_values, float_feature_values,\n   int64_feature_values, string_feature_values, misc_float_feature_values,\n   misc_int64_feature_values, misc_string_feature_values) = cc_op(\n       serialized,\n       N=(len(fidv1_features) + len(fidv2_features)),\n       M=len(float_features),\n       O=len(int64_features),\n       P=len(string_features),\n       Q=len(misc_float_features),\n       R=len(misc_int64_features),\n       S=len(misc_string_features),\n       fidv1_features=fidv1_features,\n       fidv2_features=fidv2_features,\n       float_features=float_features,\n       float_feature_dims=float_feature_dims,\n       string_features=string_features,\n       string_feature_dims=string_feature_dims,\n       int64_features=int64_features,\n       int64_feature_dims=int64_feature_dims,\n       misc_float_features=misc_float_features,\n       misc_float_dims=misc_float_dims,\n       misc_int64_features=misc_int64_features,\n       misc_int64_dims=misc_int64_dims,\n       misc_string_features=misc_string_features,\n       misc_string_dims=misc_string_dims,\n   )\n  ragged_keys = [get_slot_feature_name(slot_id) for slot_id in fidv1_features\n                ] + fidv2_features\n\n  ragged_values = []\n  for values, row_splits in zip(ragged_feature_values, ragged_feature_splits):\n    row_partition = RowPartition(\n        row_splits,\n        # value_rowids=\n        # nrows=\n        #\n        # TODO(zhuoran): Besides the \"value\" and \"split\" parsed from proto above,\n        # precompute other two encodings \"value_rowids\" & \"nrows\" in Fountain also,\n        # so that we could construct the ragged tensor with 4 precomputed encodings,\n        # and would not need to recompute them later at training period again.\n        internal=_row_partition_factory_key\n        # Currently, we just compute and cache value_rowids and nrows here:\n    ).with_precomputed_value_rowids().with_precomputed_nrows()\n    ragged_values.append(tf.RaggedTensor(values, row_partition, internal=True))\n\n  float_keys = float_features\n  int64_keys = int64_features\n  string_keys = string_features\n  return dict(\n      zip(\n          ragged_keys + float_keys + int64_keys + string_keys +\n          misc_float_features + misc_int64_features + misc_string_features,\n          ragged_values + float_feature_values + int64_feature_values +\n          string_feature_values + misc_float_feature_values +\n          misc_int64_feature_values + misc_string_feature_values))\n\n\ndef parse_instances2(serialized: tf.Tensor,\n                     fidv1_features: List[int] = None,\n                     fidv2_features: List[str] = None,\n                     float_features: List[str] = None,\n                     float_feature_dims: List[int] = None,\n                     int64_features: List[str] = None,\n                     int64_feature_dims: List[int] = None,\n                     string_features: List[str] = None,\n                     string_feature_dims: List[int] = None,\n                     misc_float_features: List[str] = None,\n                     misc_float_dims: List[int] = None,\n                     misc_int64_features: List[str] = None,\n                     misc_int64_dims: List[int] = None,\n                     misc_string_features: List[str] = None,\n                     misc_string_dims: List[int] = None):\n  \"\"\"从序列化的instance Tensor中解析instance\n  \n  Args:\n    varient_tensor (:obj:`Tensor`): 输入数据\n    fidv1_features (:obj:`List[int]`): 在Instance中, fidv1_features是平铺的, 所以用slot指定, 可以是部分slot\n    fidv2_features (:obj:`List[str]`): 在Instance中, fidv2_features存放于feature中, 可以用名字指定, 可以是部分特征名\n    float_features (:obj:`List[str]`): 在Instance中, 连续特征存于feature中, 可以用名字指定, 可以是部分特征名\n    float_feature_dims (:obj:`List[int]`): 连续特征的维度, `float_feature_dims`的长度要与`float_features`一致\n    int64_features (:obj:`List[str]`): 在Instance中, int64特征(非FID)存于feature中, 可以用名字指定, 可以是部分特征名\n    int64_feature_dims (:obj:`List[int]`): int64特征的维度, `int64_feature_dims`的长度要与`int64_features`一致\n    string_features (:obj:`List[str]`): 在Instance中, syting特征存于feature中, 可以用名字指定, 可以是部分特征名\n    string_feature_dims (:obj:`List[int]`): string特征的维度, `string_feature_dims`的长度要与`string_features`一致\n    misc_float_features (:obj:`List[str]`): 在LineId中, float字段, 用名字指定, 可以有多个\n    misc_float_dims (:obj:`List[int]`): 在LineId中, float字段维度, `misc_float_dims`的长度要与`misc_float_features`一致\n    misc_int64_features (:obj:`List[str]`): 在LineId中, int64字段, 用名字指定, 可以有多个\n    misc_int64_dims (:obj:`List[int]`): 在LineId中, int64字段维度, `misc_int64_dims`的长度要与`misc_int64_features`一致\n    misc_string_features (:obj:`List[str]`): 在LineId中, string字段, 用名字指定, 可以有多个\n    misc_string_dims (:obj:`List[str]`): 在LineId中, string字段维度, `misc_string_dims`的长度要与`misc_string_features`一致\n\n  Returns:\n    Dict[str, Tensor] 解析出特征名到特征的字典\n  \n  \"\"\"\n\n  return _parse_instance_impl(\n      serialized, fidv1_features, fidv2_features, float_features,\n      float_feature_dims, int64_features, int64_feature_dims, string_features,\n      string_feature_dims, misc_float_features, misc_float_dims,\n      misc_int64_features, misc_int64_dims, misc_string_features,\n      misc_string_dims, parse_instance_ops.monolith_parse_instances)\n\n\ndef parse_instances(serialized: tf.Tensor,\n                    fidv1_features: List[int] = None,\n                    fidv2_features: List[str] = None,\n                    float_features: List[str] = None,\n                    float_feature_dims: List[int] = None,\n                    int64_features: List[str] = None,\n                    int64_feature_dims: List[int] = None,\n                    string_features: List[str] = None,\n                    string_feature_dims: List[int] = None,\n                    misc_float_features: List[str] = ['sample_rate'],\n                    misc_int64_features: List[str] = ['req_time', 'uid'],\n                    misc_string_features: List[str] = None,\n                    misc_repeated_float_features: List[str] = ['label'],\n                    misc_repeated_float_dims: List[int] = None,\n                    misc_repeated_int64_features: List[str] = None,\n                    misc_repeated_int64_dims: List[int] = None,\n                    misc_repeated_string_features: List[str] = None,\n                    misc_repeated_string_dims: List[str] = None):\n  \"\"\"从序列化的instance Tensor中解析instance, 但参数较多, 请使用`parse_instances2`\n  \n  Args:\n    varient_tensor (:obj:`Tensor`): 输入数据\n    fidv1_features (:obj:`List[int]`): 在Instance中, fidv1_features是平铺的, 所以用slot指定, 可以是部分slot\n    fidv2_features (:obj:`List[str]`): 在Instance中, fidv2_features存放于feature中, 可以用名字指定, 可以是部分特征名\n    float_features (:obj:`List[str]`): 在Instance中, 连续特征存于feature中, 可以用名字指定, 可以是部分特征名\n    float_feature_dims (:obj:`List[int]`): 连续特征的维度, `float_feature_dims`的长度要与`float_features`一致\n    int64_features (:obj:`List[str]`): 在Instance中, int64特征(非FID)存于feature中, 可以用名字指定, 可以是部分特征名\n    int64_feature_dims (:obj:`List[int]`): int64特征的维度, `int64_feature_dims`的长度要与`int64_features`一致\n    string_features (:obj:`List[str]`): 在Instance中, syting特征存于feature中, 可以用名字指定, 可以是部分特征名\n    string_feature_dims (:obj:`List[int]`): string特征的维度, `string_feature_dims`的长度要与`string_features`一致\n    misc_float_features (:obj:`List[str]`): 在LineId中, 非repeated float字段, 用名字指定, 可以有多个\n    misc_int64_features (:obj:`List[str]`): 在LineId中, 非repeated int64字段, 用名字指定, 可以有多个\n    misc_string_features (:obj:`List[str]`): 在LineId中, 非repeated string字段, 用名字指定, 可以有多个\n    misc_repeated_float_features (:obj:`List[str]`): 在LineId中, repeated float字段, 用名字指定, 可以有多个\n    misc_repeated_float_dims (:obj:`List[int]`): 在LineId中, repeated float字段维度, `misc_repeated_float_dims`的长度要与`misc_repeated_float_features`一致\n    misc_repeated_int64_features (:obj:`List[str]`): 在LineId中, repeated int64字段, 用名字指定, 可以有多个\n    misc_repeated_int64_dims (:obj:`List[int]`): 在LineId中, repeated int64字段维度, `misc_repeated_int64_dims`的长度要与`misc_repeated_int64_features`一致\n    misc_repeated_string_features (:obj:`List[str]`): 在LineId中, repeated string字段, 用名字指定, 可以有多个\n    misc_repeated_string_dims (:obj:`List[str]`): 在LineId中, repeated string字段维度, `misc_repeated_string_dims`的长度要与`misc_repeated_string_features`一致\n\n  Returns:\n    Dict[str, Tensor] 解析出特征名到特征的字典\n  \n  \"\"\"\n\n  fidv1_features = fidv1_features or []\n  fidv2_features = fidv2_features or []\n  float_features = float_features or []\n  float_feature_dims = float_feature_dims or []\n  int64_features = int64_features or []\n  int64_feature_dims = int64_feature_dims or []\n  string_features = string_features or []\n  string_feature_dims = string_feature_dims or []\n  misc_float_features = misc_float_features or []\n  misc_float_feature_dims = [1] * len(misc_float_features)\n  misc_int64_features = misc_int64_features or []\n  misc_int64_feature_dims = [1] * len(misc_int64_features)\n  misc_string_features = misc_string_features or []\n  misc_string_features_dims = [1] * len(misc_string_features)\n  misc_repeated_float_features = misc_repeated_float_features or []\n  misc_repeated_float_dims = misc_repeated_float_dims or [1] * len(\n      misc_repeated_float_features)\n  misc_repeated_int64_features = misc_repeated_int64_features or []\n  misc_repeated_int64_dims = misc_repeated_int64_dims or [1] * len(\n      misc_repeated_int64_features)\n  misc_repeated_string_features = misc_repeated_string_features or []\n  misc_repeated_string_dims = misc_repeated_string_dims or [1] * len(\n      misc_repeated_string_features)\n\n  features = parse_instances2(\n      serialized, fidv1_features, fidv2_features, float_features,\n      float_feature_dims, int64_features, int64_feature_dims, string_features,\n      string_feature_dims, misc_float_features + misc_repeated_float_features,\n      misc_float_feature_dims + misc_repeated_float_dims,\n      misc_int64_features + misc_repeated_int64_features,\n      misc_int64_feature_dims + misc_repeated_int64_dims,\n      misc_string_features + misc_repeated_string_features,\n      misc_string_features_dims + misc_repeated_string_dims)\n  for key in misc_float_features + misc_int64_features:\n    features[key] = tf.reshape(features[key], [-1])\n\n  return features\n\n\n# This is mainly for test purpose, DO NOT use it directly.\nmonolith_raw_parse_instance = parse_instance_ops.MonolithRawParseInstance\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/python/parse_instance_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport numpy as np\nimport random\nimport tensorflow as tf\n\nfrom idl.matrix.proto import proto_parser_pb2\nfrom monolith.native_training.data.training_instance.python import parse_instance_ops as ops\nfrom monolith.native_training.data.training_instance.python import parser_utils\n\n\ndef make_fid_v1(slot_id, fid):\n  return (slot_id << 54) | fid\n\n\ndef make_fid_v2(slot_id, fid):\n  return (slot_id << 48) | fid\n\n\ndef get_test_fidv2():\n  return [make_fid_v2(100, i) for i in range(10)]\n\n\ndef generate_instance():\n  instance = proto_parser_pb2.Instance()\n  v1_fids = [make_fid_v1(i, i) for i in range(10)]\n  v2_fids = get_test_fidv2()\n  instance.fid.extend(v1_fids)\n  fid_feature = instance.feature.add()\n  fid_feature.name = \"fidv2\"\n  fid_feature.fid.extend(v2_fids)\n  float_feature = instance.feature.add()\n  float_feature.name = \"ue\"\n  float_feature.float_value.extend([float(i * 1e-5) for i in range(16)])\n  int64_feature = instance.feature.add()\n  int64_feature.name = \"int64_feature\"\n  int64_feature.int64_value.append(100)\n  string_feature = instance.feature.add()\n  string_feature.name = \"string_feature\"\n  string_feature.bytes_value.append(b\"test_string\")\n  instance.label.extend([1.1, 2.2, 3.3])\n  instance.line_id.uid = 110\n  instance.line_id.sample_rate = 0.5\n  instance.line_id.req_time = 64\n  instance.line_id.actions.extend([0, 100])\n  instance.line_id.user_id = \"123\"\n  return instance\n\n\nclass RaggedEncodingHelperTest(tf.test.TestCase):\n\n  def testExpandContract(self):\n    with tf.compat.v1.Session() as sess:\n      rt = tf.RaggedTensor.from_row_splits(values=[3, 1, 4, 1, 5, 9, 2, 6],\n                                           row_splits=[0, 4, 4, 7, 8, 8])\n      rt_copy = tf.RaggedTensor.from_row_splits(values=[3, 1, 4, 1, 5, 9, 2, 6],\n                                                row_splits=[0, 4, 4, 7, 8, 8])\n      d = {\"slot_2\": rt}\n      assert rt._row_partition._value_rowids is None\n      d = parser_utils.RaggedEncodingHelper.expand(\n          d, with_precomputed_value_rowids=True)\n      print(d)\n      assert len(d) == 1\n      self.assertAllEqual(sess.run(d[\"slot_2\"][\"value_rowids\"]),\n                          sess.run(rt_copy.value_rowids()))\n      d = parser_utils.RaggedEncodingHelper.contract(d)\n      assert len(d) == 1\n      self.assertAllEqual(sess.run(d[\"slot_2\"]), sess.run(rt))\n      self.assertAllEqual(sess.run(d[\"slot_2\"]._row_partition._value_rowids),\n                          sess.run(rt_copy.value_rowids()))\n\n\nclass ParseInstancesTest(tf.test.TestCase):\n\n  def testParseInstance(self):\n    instance = generate_instance()\n    body = instance.SerializeToString()\n    with tf.compat.v1.Session() as sess:\n      features = ops.parse_instances2(\n          [body, body],\n          fidv1_features=list(range(10)),\n          fidv2_features=[\"fidv2\"],\n          float_features=[\"ue\"],\n          float_feature_dims=[16],\n          int64_features=[\"int64_feature\"],\n          int64_feature_dims=[1],\n          string_features=[\"string_feature\"],\n          string_feature_dims=[1],\n          misc_float_features=[\"sample_rate\", \"label\"],\n          misc_float_dims=[1, 3],\n          misc_int64_features=[\"uid\", \"actions\"],\n          misc_int64_dims=[1, 2],\n          misc_string_features=[\"user_id\"],\n          misc_string_dims=[1])\n      features = sess.run(features)\n      self.assertEqual(\n          len([fidv1_key for fidv1_key in features if \"slot\" in fidv1_key]), 10)\n      self.assertAllEqual(\n          features[\"slot_1\"],\n          tf.compat.v1.ragged.constant_value([[make_fid_v2(1, 1)]] * 2))\n      self.assertAllEqual(\n          features[\"fidv2\"],\n          tf.compat.v1.ragged.constant_value([get_test_fidv2()] * 2))\n      self.assertAllClose(features[\"int64_feature\"], [[100]] * 2)\n      self.assertAllEqual(features[\"string_feature\"], [[b\"test_string\"]] * 2)\n      self.assertAllClose(features[\"ue\"],\n                          [[float(i * 1e-5) for i in range(16)]] * 2)\n      self.assertAllClose(features[\"sample_rate\"], [[0.5]] * 2)\n      self.assertAllClose(features[\"label\"], [[1.1, 2.2, 3.3]] * 2)\n      self.assertAllEqual(features[\"uid\"], [[110]] * 2)\n      self.assertAllEqual(features[\"actions\"], [[0, 100]] * 2)\n      self.assertAllEqual(features[\"user_id\"], [[\"123\"]] * 2)\n\n  def testParseInstanceV1Only(self):\n    instance = generate_instance()\n    body = instance.SerializeToString()\n    with tf.compat.v1.Session() as sess:\n      features = ops.parse_instances2([body], fidv1_features=[1])\n      features = sess.run(features)\n      self.assertAllEqual(\n          features[\"slot_1\"],\n          tf.compat.v1.ragged.constant_value([[make_fid_v1(1, 1)]]))\n\n  def testParseInstanceWithMissingFields(self):\n    instance = generate_instance()\n    body = instance.SerializeToString()\n    with tf.compat.v1.Session() as sess:\n      features = ops.parse_instances2(\n          [body],\n          fidv1_features=list(range(11)),\n          fidv2_features=[\"fidv2\", \"fidv2_2\"],\n          float_features=[\"ue\", \"ue2\"],\n          float_feature_dims=[16, 8],\n          int64_features=[\"int64_feature\", \"missing_int64_feature\"],\n          int64_feature_dims=[1, 10],\n          string_features=[\"string_feature\", \"missing_string_feature\"],\n          string_feature_dims=[1, 10])\n      features = sess.run(features)\n      # It should be an empty tensor for the last FID element\n      self.assertAllEqual(features[\"slot_10\"],\n                          tf.compat.v1.ragged.constant_value([[]]))\n      self.assertAllEqual(features[\"fidv2_2\"],\n                          tf.compat.v1.ragged.constant_value([[]]))\n      # It should be an zero tensor for the second UE element\n      self.assertAllEqual(features[\"ue2\"], [[0 for i in range(8)]])\n      # It should be an zero tensor for the second int64 element\n      self.assertAllEqual(features[\"missing_int64_feature\"],\n                          [[0 for i in range(10)]])\n      self.assertAllEqual(features[\"missing_string_feature\"],\n                          [[\"\" for i in range(10)]])\n\n\nclass RawParseInstanceTest(tf.test.TestCase):\n\n  def test_concat(self):\n    serialized = [generate_instance().SerializeToString()]\n    tensors = ops.monolith_raw_parse_instance(T=[tf.int64, tf.int64],\n                                              serialized=serialized,\n                                              fidv1_features=[0, 1],\n                                              fidv2_features=[\"fidv2\"],\n                                              fid_output_type=\"CONCAT\")\n\n    with self.session() as sess:\n      tensors = sess.run(tensors)\n      self.assertAllEqual(tensors[0], [0, 1, 2, len(get_test_fidv2()) + 2])\n      self.assertAllEqual(\n          tensors[1], [make_fid_v2(0, 0), make_fid_v2(1, 1)] + get_test_fidv2())\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/python/parser_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom collections import deque\nfrom typing import Callable\n\nfrom monolith.native_training import ragged_utils\n\n_extra_parse_steps = deque([])\n\n\ndef add_extra_parse_step(parse_fn: Callable):\n  _extra_parse_steps.append(parse_fn)\n\n\nclass RaggedEncodingHelper:\n  \"\"\"Helper methods to precompute ragged encodings in input_fn, as a workaround\n  \n  Fundamentally, we should modify TensorFlow Dataset structure handler to compute\n  provided encoding tensor in RowParition of a RaggedTensor.\n  \"\"\"\n\n  @staticmethod\n  def expand(name_to_ragged_ids,\n             with_precomputed_nrows=True,\n             with_precomputed_value_rowids=False):\n    \"\"\"Expand the RaggedTensor format in dict to precompute encodings within data iterator.\"\"\"\n    d = {}\n    for k, v in name_to_ragged_ids.items():\n      if isinstance(v, tf.RaggedTensor):\n        d[k] = {\n            # Basics\n            \"values\":\n                v.values,\n            \"row_splits\":\n                v.row_splits,\n            \"nrows\":\n                v.nrows() if with_precomputed_nrows else None,\n            \"value_rowids\":\n                ragged_utils.fused_value_rowids(v)\n                if with_precomputed_value_rowids else None\n        }\n      else:\n        d[k] = v\n    return d\n\n  @staticmethod\n  def contract(name_to_ragged_ids):\n    \"\"\"Contract to recover RaggedTensor-only dict after computed.\"\"\"\n    d = {}\n    for k, v in name_to_ragged_ids.items():\n      if isinstance(v, dict) and (\"values\" in v) and (\"row_splits\" in v):\n        t = tf.RaggedTensor.from_row_splits(v[\"values\"],\n                                            v[\"row_splits\"],\n                                            validate=False)\n        if \"nrows\" in v:\n          assert t._row_partition._nrows is None, \"Shouldn't override the exisiting nrows.\"\n          t._row_partition._nrows = v[\"nrows\"]\n        if \"value_rowids\" in v:\n          assert t._row_partition._value_rowids is None, \"Shouldn't override the exisiting tensor.\"\n          t._row_partition._value_rowids = v[\"value_rowids\"]\n        d[k] = t\n      else:\n        d[k] = v\n    return d\n\n\ndef advanced_parse(features):\n  while _extra_parse_steps:\n    fn = _extra_parse_steps.popleft()\n    features = fn(features)\n\n  return features\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/python/pb_datasource_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport os\nfrom typing import Dict, List, Iterable, Callable\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import load_library\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\npb_datasource_ops = gen_monolith_ops\n\n\ndef filter_by_fids(variant: tf.Tensor,\n                   filter_fids: List[int] = None,\n                   has_fids: List[int] = None,\n                   select_fids: List[int] = None,\n                   has_actions: List[int] = None):\n  return pb_datasource_ops.set_filter(variant, filter_fids or [], has_fids or\n                                      [], select_fids or [], has_actions or [])\n\n\ndef filter_by_value(variant: tf.Tensor, field_name: str, op: str,\n                    operand: float):\n  return pb_datasource_ops.value_filter(variant, field_name, op, operand)\n\n\ndef negative_sample(variant: tf.Tensor, drop_rate: float, label_index: int,\n                    threshold: float):\n  return pb_datasource_ops.negative_sample(variant, drop_rate, label_index,\n                                           threshold)\n\n\ndef variant_dummy(variant: tf.Tensor):\n  return pb_datasource_ops.variant_dummy(variant)\n"
  },
  {
    "path": "monolith/native_training/data/training_instance/python/test_data_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\r\n"
  },
  {
    "path": "monolith/native_training/data/transform/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"cc_proto_library\", \"py_proto_library\")\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_test\")\nload(\"@pip_deps//:requirements.bzl\", \"requirement\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_proto_library(\n    name = \"transform_config_cc_proto\",\n    srcs = [\"transform_config.proto\"],\n)\n\npy_proto_library(\n    name = \"transform_config_py_proto\",\n    srcs = [\"transform_config.proto\"],\n    srcs_version = \"PY2AND3\",\n)\n\n\ncc_library(\n    name = \"transforms\",\n    srcs = [\"cc/transforms.cc\"],\n    hdrs = [\"cc/transforms.h\"],\n    deps = [\n        \":transform_config_cc_proto\",\n        \"//monolith/native_training/data/training_instance:instance_utils\",\n        \"//monolith/native_training/data/kernels/internal:label_utils\",\n        \"//monolith/native_training/data/kernels/internal:value_filter_by_line_id\",\n        \"//monolith/native_training/data/kernels/internal:relational_utils\",\n        \"//monolith/native_training/runtime/common:linalg_utils\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\n\npy_library(\n    name = \"transforms_py\",\n    srcs = [\n        \"transforms.py\",\n    ],\n    deps = [\n        \":transform_config_py_proto\",\n        \"//idl:example_py_proto\",\n        \"//idl:proto_parser_py_proto\",\n    ],\n)\n\npy_test(\n    name = \"transforms_test\",\n    srcs = [\"transforms_test.py\"],\n    deps = [\n        \":transforms_py\",\n    ],\n)\n\n\nexports_files([\n    \"cc/transforms.cc\",\n])\n"
  },
  {
    "path": "monolith/native_training/data/transform/cc/transforms.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/transform/cc/transforms.h\"\n\n#include <random>\n#include <utility>\n\n#include \"absl/base/internal/cycleclock.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_join.h\"\n#include \"glog/logging.h\"\n#include \"monolith/native_training/data/kernels/internal/label_utils.h\"\n#include \"monolith/native_training/data/kernels/internal/value_filter_by_line_id.h\"\n#include \"monolith/native_training/data/kernels/internal/relational_utils.h\"\n#include \"monolith/native_training/data/training_instance/cc/instance_utils.h\"\n#include \"monolith/native_training/runtime/common/linalg_utils.h\"\n#include \"tensorflow/core/platform/logging.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing ::google::protobuf::RepeatedField;\nusing ::idl::matrix::proto::LineId;\nusing internal::LineIdValueFilter;\nusing ::monolith::common::IsAlmostEqual;\nusing ::monolith::io::proto::Example;\nusing ::monolith::io::proto::ExampleBatch;\nusing ::parser::proto::Instance;\n\nclass LogEveryNSecState {\n public:\n  bool ShouldLog(double seconds) {\n    LossyIncrement(&counter_);\n    const int64 now_cycles = absl::base_internal::CycleClock::Now();\n    int64 next_cycles = next_log_time_cycles_.load(std::memory_order_relaxed);\n    do {\n      if (now_cycles <= next_cycles) return false;\n    } while (!next_log_time_cycles_.compare_exchange_weak(\n        next_cycles,\n        now_cycles + seconds * absl::base_internal::CycleClock::Frequency(),\n        std::memory_order_relaxed, std::memory_order_relaxed));\n    return true;\n  }\n\n  uint32 counter() { return counter_.load(std::memory_order_relaxed); }\n\n private:\n  // The following code behaves like AtomicStatsCounter::LossyAdd() for\n  // speed since it is fine to lose occasional updates.\n  // Returns old value of *counter.\n  uint32 LossyIncrement(std::atomic<uint32>* counter) {\n    const uint32 value = counter->load(std::memory_order_relaxed);\n    counter->store(value + 1, std::memory_order_relaxed);\n    return value;\n  }\n\n  std::atomic<uint32> counter_{0};\n  // Cycle count according to CycleClock that we should next log at.\n  std::atomic<int64> next_log_time_cycles_{0};\n};\n\nclass TransformSummary : public TransformInterface {\n public:\n  explicit TransformSummary(std::unique_ptr<TransformInterface> transform,\n                            bool print_summary = false)\n      : transform_(std::move(transform)),\n        offset_(0),\n        input_total_(0),\n        output_total_(0) {}\n\n  ~TransformSummary() override {\n    LOG(INFO) << \"Finally \" << this->DebugString();\n  }\n\n  std::string Name() override { return transform_->Name(); }\n\n  std::string DebugString() {\n    float rate = input_total_ == 0\n                     ? 0\n                     : static_cast<float>(output_total_) / input_total_;\n    return absl::StrFormat(\n        \"%s, input = %ld, output = %ld, retention rate = %.2f\",\n        transform_->Name(), input_total_, output_total_, rate);\n  }\n\n  void Transform(std::shared_ptr<Instance> instance,\n                 std::vector<std::shared_ptr<Instance>>* output) override {\n    offset_ = output->size();\n    input_total_ += 1;\n    transform_->Transform(instance, output);\n    output_total_ += output->size() - offset_;\n    if (every_n_sec_state_.ShouldLog(60 * 5)) {\n      LOG(INFO) << DebugString();\n    }\n  }\n\n  void Transform(std::shared_ptr<Example> example,\n                 std::vector<std::shared_ptr<Example>>* output) override {\n    offset_ = output->size();\n    input_total_ += 1;\n    transform_->Transform(example, output);\n    output_total_ += output->size() - offset_;\n    if (every_n_sec_state_.ShouldLog(60 * 5)) {\n      LOG(INFO) << DebugString();\n    }\n  }\n\n private:\n  std::unique_ptr<TransformInterface> transform_;\n\n  int64_t offset_;\n\n  int64_t input_total_;\n\n  int64_t output_total_;\n\n  LogEveryNSecState every_n_sec_state_;\n};\n\nclass Identity : public TransformInterface {\n public:\n  std::string Name() override { return \"Identity\"; }\n\n  void Transform(std::shared_ptr<Instance> instance,\n                 std::vector<std::shared_ptr<Instance>>* output) override {\n    output->push_back(instance);\n  }\n\n  void Transform(std::shared_ptr<Example> example,\n                 std::vector<std::shared_ptr<Example>>* output) override {\n    output->push_back(example);\n  }\n};\n\nclass FilterByFid : public TransformInterface {\n public:\n  explicit FilterByFid(FilterByFidConfig config) : config_(std::move(config)) {\n    filter_fids_.insert(config_.filter_fids().begin(),\n                        config_.filter_fids().end());\n    has_fids_.insert(config_.has_fids().begin(), config_.has_fids().end());\n    select_fids_.insert(config_.select_fids().begin(),\n                        config_.select_fids().end());\n    req_time_min_ = 0;\n  }\n\n  std::string Name() override { return \"FilterByFid\"; }\n\n  void Transform(std::shared_ptr<Instance> instance,\n                 std::vector<std::shared_ptr<Instance>>* output) override {\n    if (tensorflow::monolith_tf::IsInstanceOfInterest(*instance, filter_fids_,\n                                                      has_fids_, select_fids_,\n                                                      {}, req_time_min_, {})) {\n      output->push_back(instance);\n    }\n  }\n\n  void Transform(std::shared_ptr<Example> example,\n                 std::vector<std::shared_ptr<Example>>* output) override {\n    if (tensorflow::monolith_tf::IsInstanceOfInterest(*example, filter_fids_,\n                                                      has_fids_, select_fids_,\n                                                      {}, req_time_min_, {})) {\n      output->push_back(example);\n    }\n  }\n\n private:\n  std::set<uint64_t> filter_fids_;\n  std::set<uint64_t> has_fids_;\n  std::set<uint64_t> select_fids_;\n  int64_t req_time_min_;\n  FilterByFidConfig config_;\n};\n\nclass FilterByAction : public TransformInterface {\n public:\n  explicit FilterByAction(FilterByActionConfig config)\n      : config_(std::move(config)) {\n    has_actions_.insert(config_.has_actions().begin(),\n                        config_.has_actions().end());\n  }\n\n  std::string Name() override { return \"FilterByAction\"; }\n\n  void Transform(std::shared_ptr<Instance> instance,\n                 std::vector<std::shared_ptr<Instance>>* output) override {\n    if (tensorflow::monolith_tf::IsInstanceOfInterest(*instance, {}, {}, {},\n                                                      has_actions_, 0, {})) {\n      output->push_back(instance);\n    }\n  }\n\n  void Transform(std::shared_ptr<Example> example,\n                 std::vector<std::shared_ptr<Example>>* output) override {\n    if (tensorflow::monolith_tf::IsInstanceOfInterest(*example, {}, {}, {},\n                                                      has_actions_, 0, {})) {\n      output->push_back(example);\n    }\n  }\n\n private:\n  std::set<int32_t> has_actions_;\n  FilterByActionConfig config_;\n};\n\nclass FilterByLabel : public TransformInterface {\n public:\n  explicit FilterByLabel(FilterByLabelConfig config)\n      : config_(std::move(config)) {}\n\n  std::string Name() override { return \"FilterByLabel\"; }\n\n  void Transform(std::shared_ptr<Instance> instance,\n                 std::vector<std::shared_ptr<Instance>>* output) override {\n    if (IsInstanceOfInterest(instance->label())) {\n      output->push_back(instance);\n    }\n  }\n\n  void Transform(std::shared_ptr<Example> example,\n                 std::vector<std::shared_ptr<Example>>* output) override {\n    if (IsInstanceOfInterest(example->label())) {\n      output->push_back(example);\n    }\n  }\n\n private:\n  bool IsInstanceOfInterest(const RepeatedField<float>& labels) const {\n    if (labels.size() < config_.thresholds_size()) {\n      LOG_EVERY_N_SEC(ERROR, 60) << absl::StrFormat(\n          \"Label size(=%ld) should be >= label_threshold size(=%ld), please \"\n          \"investigate!\",\n          labels.size(), config_.thresholds_size());\n      return false;\n    }\n\n    for (int i = 0; i < config_.thresholds_size(); ++i) {\n      if (labels.Get(i) >= config_.thresholds(i)) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  FilterByLabelConfig config_;\n};\n\nclass FilterByValue : public TransformInterface {\n public:\n  explicit FilterByValue(FilterByValueConfig config)\n      : config_(std::move(config)) {\n    field_name_ = config_.field_name();\n    op_ = config_.op();\n    float_operand_.insert(float_operand_.end(), config_.float_operand().begin(),\n                          config_.float_operand().end());\n    int_operand_.insert(int_operand_.end(), config_.int_operand().begin(),\n                        config_.int_operand().end());\n    string_operand_.insert(string_operand_.end(),\n                           config_.string_operand().begin(),\n                           config_.string_operand().end());\n    keep_empty_ = config_.keep_empty();\n    operand_filepath_ = config_.operand_filepath();\n    line_id_value_filter_ = std::make_unique<LineIdValueFilter>(\n        field_name_, op_, float_operand_, int_operand_, string_operand_,\n        operand_filepath_, keep_empty_);\n  }\n\n  std::string Name() override { return \"FilterByValue\"; }\n\n  void Transform(std::shared_ptr<Instance> instance,\n                 std::vector<std::shared_ptr<Instance>>* output) override {\n    if (IsInstanceOfInterest(instance->line_id())) {\n      output->push_back(instance);\n    }\n  }\n\n  void Transform(std::shared_ptr<Example> example,\n                 std::vector<std::shared_ptr<Example>>* output) override {\n    if (IsInstanceOfInterest(example->line_id())) {\n      output->push_back(example);\n    }\n  }\n\n private:\n  // TODO(huangruiteng): support value filter by feature\n  bool IsInstanceOfInterest(const LineId& line_id) const {\n    tensorflow::Env* env = tensorflow::Env::Default();\n    return line_id_value_filter_->IsInstanceOfInterest(env, line_id);\n  }\n\n  FilterByValueConfig config_;\n\n  std::string field_name_;\n  std::string op_;  // gt, ge, eq, lt, le, neq, between\n  bool keep_empty_ = false;\n  std::string operand_filepath_;\n\n  std::vector<float> float_operand_;\n  std::vector<int64> int_operand_;\n  std::vector<std::string> string_operand_;\n\n  std::unique_ptr<LineIdValueFilter> line_id_value_filter_;\n};\n\nclass AddLabel : public TransformInterface {\n public:\n  explicit AddLabel(AddLabelConfig config) : config_(std::move(config)) {\n    task_configs_.reserve(config_.task_label_configs_size());\n    for (const auto& t : config_.task_label_configs()) {\n      std::set<int32_t> pos_actions, neg_actions;\n      CHECK(!t.pos_actions().empty());\n      pos_actions.insert(t.pos_actions().begin(), t.pos_actions().end());\n      neg_actions.insert(t.neg_actions().begin(), t.neg_actions().end());\n\n      CHECK(!internal::HasIntersection(pos_actions, neg_actions));\n\n      float sample_rate = t.sample_rate();\n      CHECK_GE(sample_rate, 0);\n      CHECK_LE(sample_rate, 1.0);\n\n      task_configs_.push_back({pos_actions, neg_actions, sample_rate});\n    }\n\n    for (size_t i = 0; i < task_configs_.size(); ++i) {\n      LOG(INFO) << absl::StrFormat(\"Task #%d config: %s\", i + 1,\n                                   task_configs_[i].ToString());\n    }\n    LOG(INFO) << absl::StrFormat(\"sample_rate = %.4f\",\n                                 config_.new_sample_rate());\n    std::size_t seed =\n        std::chrono::system_clock::now().time_since_epoch().count();\n    random_generator_.seed(seed);\n    random_neg_sample_ = std::uniform_real_distribution<float>(0.0, 1.0);\n  }\n\n  std::string Name() override { return \"AddLabel\"; }\n\n  void Transform(std::shared_ptr<Instance> instance,\n                 std::vector<std::shared_ptr<Instance>>* output) override {\n    DoAddLabel(instance->mutable_line_id(), instance->mutable_label());\n    output->push_back(instance);\n  }\n\n  void Transform(std::shared_ptr<Example> example,\n                 std::vector<std::shared_ptr<Example>>* output) override {\n    DoAddLabel(example->mutable_line_id(), example->mutable_label());\n    output->push_back(example);\n  }\n\n private:\n  void DoAddLabel(LineId* mutable_line_id,\n                  google::protobuf::RepeatedField<float>* mutable_label) {\n    std::set<int32_t> actions(mutable_line_id->actions().begin(),\n                              mutable_line_id->actions().end());\n\n    if (!mutable_label->empty() && mutable_label->Get(0) <= 0) {\n      mutable_label->Set(0, internal::INVALID_LABEL);\n    }\n\n    for (const auto& t : task_configs_) {\n      bool has_pos = internal::HasIntersection(actions, t.pos_actions);\n      bool has_neg = internal::HasIntersection(actions, t.neg_actions);\n\n      if (!t.neg_actions.empty()) {\n        // If there is given neg_actions\n        if (!has_pos && !has_neg) {\n          mutable_label->Add(internal::INVALID_LABEL);\n        } else if (has_pos) {\n          // (has_pos && !has_neg) || (has_pos && has_neg)\n          mutable_label->Add(internal::POSITIVE_LABEL);\n        } else {\n          // !has_pos && has_neg\n          if (SelectedByNegativeSampling(t)) {\n            mutable_label->Add(config_.negative_value());\n          } else {\n            mutable_label->Add(internal::INVALID_LABEL);\n          }\n        }\n      } else {\n        // If there is no given neg_actions\n        if (has_pos) {\n          mutable_label->Add(internal::POSITIVE_LABEL);\n        } else {\n          if (SelectedByNegativeSampling(t)) {\n            mutable_label->Add(config_.negative_value());\n          } else {\n            mutable_label->Add(internal::INVALID_LABEL);\n          }\n        }\n      }\n    }\n\n    mutable_line_id->set_sample_rate(config_.new_sample_rate());\n  }\n\n  bool SelectedByNegativeSampling(const internal::TaskConfig& t) {\n    return IsAlmostEqual(t.sample_rate, 1.0f) ||\n           random_neg_sample_(random_generator_) < t.sample_rate;\n  }\n\n  std::vector<internal::TaskConfig> task_configs_;\n\n  std::default_random_engine random_generator_;\n\n  std::uniform_real_distribution<float> random_neg_sample_;\n\n  AddLabelConfig config_;\n};\n\nclass LogicalOrTransform : public TransformInterface {\n public:\n  LogicalOrTransform(std::unique_ptr<TransformInterface> t1,\n                     std::unique_ptr<TransformInterface> t2)\n      : t1_(std::move(t1)), t2_(std::move(t2)) {}\n\n  std::string Name() override {\n    return absl::StrFormat(\"(%s or %s)\", t1_->Name(), t2_->Name());\n  }\n\n  void Transform(std::shared_ptr<Instance> instance,\n                 std::vector<std::shared_ptr<Instance>>* output) override {\n    std::vector<std::shared_ptr<Instance>> intermediates;\n    t1_->Transform(instance, &intermediates);\n    t2_->Transform(instance, &intermediates);\n    if (!intermediates.empty()) {\n      CHECK_LE(intermediates.size(), 2);\n      output->push_back(intermediates.front());\n    }\n  }\n\n  void Transform(std::shared_ptr<Example> example,\n                 std::vector<std::shared_ptr<Example>>* output) override {\n    std::vector<std::shared_ptr<Example>> intermediates;\n    t1_->Transform(example, &intermediates);\n    t2_->Transform(example, &intermediates);\n    if (!intermediates.empty()) {\n      CHECK_LE(intermediates.size(), 2);\n      output->push_back(intermediates.front());\n    }\n  }\n\n private:\n  std::unique_ptr<TransformInterface> t1_;\n  std::unique_ptr<TransformInterface> t2_;\n};\n\nclass CombinedTransform : public TransformInterface {\n public:\n  CombinedTransform(std::unique_ptr<TransformInterface> t1,\n                    std::unique_ptr<TransformInterface> t2)\n      : t1_(std::move(t1)), t2_(std::move(t2)) {}\n\n  std::string Name() override {\n    return absl::StrFormat(\"(%s and %s)\", t1_->Name(), t2_->Name());\n  }\n\n  void Transform(std::shared_ptr<Instance> instance,\n                 std::vector<std::shared_ptr<Instance>>* output) override {\n    std::vector<std::shared_ptr<Instance>> intermediates;\n    t1_->Transform(instance, &intermediates);\n    for (const auto& intermediate : intermediates) {\n      t2_->Transform(intermediate, output);\n    }\n  }\n\n  void Transform(std::shared_ptr<Example> example,\n                 std::vector<std::shared_ptr<Example>>* output) override {\n    std::vector<std::shared_ptr<Example>> intermediates;\n    t1_->Transform(example, &intermediates);\n    for (const auto& intermediate : intermediates) {\n      t2_->Transform(intermediate, output);\n    }\n  }\n\n private:\n  std::unique_ptr<TransformInterface> t1_;\n  std::unique_ptr<TransformInterface> t2_;\n};\n\nstd::unique_ptr<TransformInterface> NewTransformSummary(\n    std::unique_ptr<TransformInterface> transform, bool print_summary) {\n  return std::make_unique<TransformSummary>(std::move(transform),\n                                            print_summary);\n}\n\nstd::unique_ptr<TransformInterface> NewIdentity() {\n  return std::make_unique<Identity>();\n}\n\nstd::unique_ptr<TransformInterface> NewFilterByFid(FilterByFidConfig config) {\n  return std::make_unique<FilterByFid>(std::move(config));\n}\n\nstd::unique_ptr<TransformInterface> NewFilterByAction(\n    FilterByActionConfig config) {\n  return std::make_unique<FilterByAction>(std::move(config));\n}\n\nstd::unique_ptr<TransformInterface> NewFilterByLabel(\n    FilterByLabelConfig config) {\n  return std::make_unique<FilterByLabel>(std::move(config));\n}\n\nstd::unique_ptr<TransformInterface> NewAddLabel(AddLabelConfig config) {\n  return std::make_unique<AddLabel>(std::move(config));\n}\n\nstd::unique_ptr<TransformInterface> NewFilterByValue(\n    FilterByValueConfig config) {\n  return std::make_unique<FilterByValue>(std::move(config));\n}\n\nstd::unique_ptr<TransformInterface> CombineTransforms(\n    std::unique_ptr<TransformInterface> t1,\n    std::unique_ptr<TransformInterface> t2) {\n  return std::make_unique<CombinedTransform>(std::move(t1), std::move(t2));\n}\n\nstd::unique_ptr<TransformInterface> CombineLogicalOrTransforms(\n    std::unique_ptr<TransformInterface> t1,\n    std::unique_ptr<TransformInterface> t2) {\n  return std::make_unique<LogicalOrTransform>(std::move(t1), std::move(t2));\n}\n\nstd::unique_ptr<TransformInterface> NewTransformFromBasicConfig(\n    BasicTransformConfig config) {\n  std::string name;\n  std::unique_ptr<TransformInterface> transform = nullptr;\n  switch (config.type_case()) {\n    case (BasicTransformConfig::kFilterByFid):\n      transform = NewFilterByFid(std::move(*config.mutable_filter_by_fid()));\n      break;\n    case (BasicTransformConfig::kFilterByAction):\n      transform =\n          NewFilterByAction(std::move(*config.mutable_filter_by_action()));\n      break;\n    case (BasicTransformConfig::kFilterByLabel):\n      transform =\n          NewFilterByLabel(std::move(*config.mutable_filter_by_label()));\n      break;\n    case (BasicTransformConfig::kAddLabel):\n      transform = NewAddLabel(std::move(*config.mutable_add_label()));\n      break;\n    case (BasicTransformConfig::kFilterByValue):\n      transform =\n          NewFilterByValue(std::move(*config.mutable_filter_by_value()));\n      break;\n    default:\n      throw std::invalid_argument(absl::StrFormat(\n          \"transform is not implemented yet. %s\", config.ShortDebugString()));\n  }\n\n  return NewTransformSummary(std::move(transform));\n}\n\nstd::unique_ptr<TransformInterface> NewTransformFromConfig(\n    const TransformConfig& config) {\n  std::unique_ptr<TransformInterface> transform = nullptr;\n  for (const auto& c : config.configs()) {\n    std::unique_ptr<TransformInterface> t;\n    if (c.has_basic_config()) {\n      t = NewTransformFromBasicConfig(c.basic_config());\n    } else if (c.has_logical_or_config()) {\n      std::unique_ptr<TransformInterface> t1 =\n          NewTransformFromBasicConfig(c.logical_or_config().x());\n      std::unique_ptr<TransformInterface> t2 =\n          NewTransformFromBasicConfig(c.logical_or_config().y());\n      t = CombineLogicalOrTransforms(std::move(t1), std::move(t2));\n    }\n\n    AssignOrCombine(&transform, std::move(t), CombineTransforms);\n  }\n\n  if (transform == nullptr) {\n    transform = NewIdentity();\n  }\n  return NewTransformSummary(std::move(transform), true);\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/data/transform/cc/transforms.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_DATA_TRANSFORM_CC_TRANSFORMS_H_\n#define MONOLITH_NATIVE_TRAINING_DATA_TRANSFORM_CC_TRANSFORMS_H_\n\n#include <memory>\n#include <vector>\n\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"idl/matrix/proto/proto_parser.pb.h\"\n#include \"monolith/native_training/data/transform/transform_config.pb.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing monolith::native_training::data::AddLabelConfig;\nusing monolith::native_training::data::BasicTransformConfig;\nusing monolith::native_training::data::FilterByActionConfig;\nusing monolith::native_training::data::FilterByFidConfig;\nusing monolith::native_training::data::FilterByLabelConfig;\nusing monolith::native_training::data::FilterByValueConfig;\nusing monolith::native_training::data::LogicalOrTransformConfig;\nusing monolith::native_training::data::TransformConfig;\nusing monolith::native_training::data::TransformConfig_OneTransformConfig;\n\nclass TransformInterface {\n public:\n  virtual ~TransformInterface() = default;\n\n  virtual std::string Name() = 0;\n\n  virtual void Transform(\n      std::shared_ptr<::parser::proto::Instance>,\n      std::vector<std::shared_ptr<::parser::proto::Instance>>*) = 0;\n\n  virtual void Transform(\n      std::shared_ptr<::monolith::io::proto::Example>,\n      std::vector<std::shared_ptr<::monolith::io::proto::Example>>*) = 0;\n};\n\nstd::unique_ptr<TransformInterface> NewTransformSummary(\n    std::unique_ptr<TransformInterface> transform, bool print_summary = false);\n\nstd::unique_ptr<TransformInterface> NewIdentity();\n\nstd::unique_ptr<TransformInterface> NewFilterByFid(FilterByFidConfig config);\n\nstd::unique_ptr<TransformInterface> NewFilterByAction(\n    FilterByActionConfig config);\n\nstd::unique_ptr<TransformInterface> NewFilterByLabel(\n    FilterByLabelConfig config);\n\nstd::unique_ptr<TransformInterface> NewAddLabel(AddLabelConfig config);\n\nstd::unique_ptr<TransformInterface> NewFilterByValue(\n    FilterByValueConfig config);\n\nstd::unique_ptr<TransformInterface> CombineTransforms(\n    std::unique_ptr<TransformInterface> t1,\n    std::unique_ptr<TransformInterface> t2);\n\nstd::unique_ptr<TransformInterface> NewTransformFromBasicConfig(\n    BasicTransformConfig config);\n\nstd::unique_ptr<TransformInterface> NewTransformFromConfig(\n    const TransformConfig& config);\n\ntemplate <class T, class F>\nvoid AssignOrCombine(T* t1, T t2, F combine_fn) {\n  if (*t1 == nullptr) {\n    *t1 = std::move(t2);\n    return;\n  }\n  *t1 = combine_fn(std::move(*t1), std::move(t2));\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_DATA_TRANSFORM_CC_TRANSFORMS_H_\n"
  },
  {
    "path": "monolith/native_training/data/transform/transform_config.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\n\npackage monolith.native_training.data;\n\nmessage FilterByFidConfig {\n  repeated uint64 has_fids = 1;\n  repeated uint64 filter_fids = 2;\n  repeated uint64 select_fids = 3;\n}\n\nmessage FilterByActionConfig {\n  repeated int32 has_actions = 1;\n}\n\nmessage AddLabelConfig {\n  message TaskLabelConfig {\n    repeated int32 pos_actions = 1;\n    repeated int32 neg_actions = 2;\n    optional float sample_rate = 3 [default = 1.0];\n  }\n\n  repeated TaskLabelConfig task_label_configs = 1;\n  optional float negative_value = 2 [default = 0.0];\n  optional float new_sample_rate = 3 [default = 1.0];\n}\n\nmessage FilterByLabelConfig {\n  repeated float thresholds = 1;\n}\n\nmessage FilterByValueConfig {\n  required string field_name = 1;\n  required string op = 2;\n  repeated float float_operand = 3;\n  repeated int64 int_operand = 4;\n  repeated string string_operand = 5;\n  optional bool keep_empty = 6 [default = false];\n  optional string operand_filepath = 7 [default = \"\"];\n}\n\nmessage BasicTransformConfig {\n  oneof type {\n    FilterByFidConfig filter_by_fid = 1;\n    FilterByActionConfig filter_by_action = 2;\n    FilterByLabelConfig filter_by_label = 3;\n    AddLabelConfig add_label = 4;\n    FilterByValueConfig filter_by_value = 5;\n  }\n}\n\nmessage LogicalOrTransformConfig {\n  required BasicTransformConfig x = 1;\n  required BasicTransformConfig y = 2;\n}\n\nmessage TransformConfig {\n  message OneTransformConfig {\n    oneof type {\n      BasicTransformConfig basic_config = 1;\n      LogicalOrTransformConfig logical_or_config = 2;\n    }\n  }\n\n  repeated OneTransformConfig configs = 1;\n}\n\n\n"
  },
  {
    "path": "monolith/native_training/data/transform/transforms.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport abc\nfrom typing import List, Union\n\nfrom monolith.native_training.data.transform import transform_config_pb2\nfrom idl.matrix.proto.line_id_pb2 import LineId\n\n\nclass Transform(abc.ABC):\n\n  @abc.abstractmethod\n  def as_proto(self) -> transform_config_pb2.TransformConfig():\n    pass\n\n  @abc.abstractmethod\n  def _is_leaf_node(self) -> bool:\n    pass\n\n\nclass Compose(Transform):\n  \"\"\"Composes several transforms together.\n\n  Args:\n      transforms (list of ``Transform`` objects): list of transforms to compose.\n\n  Example:\n      >>> transforms.Compose([\n      >>>     transforms.FilterByFid(has_fids=[1]),\n      >>>     transforms.FilterByLabel(thresholds=[-100]),\n      >>> ])\n  \"\"\"\n\n  def __init__(self, transforms: List[Transform]):\n    assert all(isinstance(t, Transform) for t in transforms)\n    self.transforms = transforms\n\n  def as_proto(self) -> transform_config_pb2.TransformConfig():\n    config = transform_config_pb2.TransformConfig()\n    for t in self.transforms:\n      config.MergeFrom(t.as_proto())\n    return config\n\n  def _is_leaf_node(self) -> bool:\n    return False\n\n\nclass FilterByFid(Transform):\n\n  def __init__(self,\n               has_fids: List[int] = None,\n               filter_fids: List[int] = None,\n               select_fids: List[int] = None):\n    self.has_fids = has_fids\n    self.filter_fids = filter_fids\n    self.select_fids = select_fids\n\n  def as_proto(self) -> transform_config_pb2.TransformConfig():\n    config = transform_config_pb2.TransformConfig()\n    transform = config.configs.add()\n    transform.basic_config.filter_by_fid.has_fids.extend(self.has_fids)\n    transform.basic_config.filter_by_fid.filter_fids.extend(self.filter_fids)\n    transform.basic_config.filter_by_fid.select_fids.extend(self.select_fids)\n    return config\n\n  def _is_leaf_node(self) -> bool:\n    return True\n\n\nclass FilterByAction(Transform):\n\n  def __init__(self, has_actions: List[int] = None):\n    self.has_actions = has_actions\n\n  def as_proto(self) -> transform_config_pb2.TransformConfig():\n    config = transform_config_pb2.TransformConfig()\n    transform = config.configs.add()\n    transform.basic_config.filter_by_action.has_actions.extend(self.has_actions)\n    return config\n\n  def _is_leaf_node(self) -> bool:\n    return True\n\n\nclass FilterByLabel(Transform):\n\n  def __init__(self, thresholds=List[float]):\n    self.thresholds = thresholds\n\n  def as_proto(self) -> transform_config_pb2.TransformConfig():\n    config = transform_config_pb2.TransformConfig()\n    transform = config.configs.add()\n    transform.basic_config.filter_by_label.thresholds.extend(self.thresholds)\n    return config\n\n  def _is_leaf_node(self) -> bool:\n    return True\n\n\nclass FilterByValue(Transform):\n\n  def __init__(\n      self,\n      field_name: str,\n      op: str,\n      operand: Union[float, int, str, List[float], List[int], List[str]],\n      keep_empty: bool = False,\n  ):\n    assert op in {\n        'gt', 'ge', 'eq', 'lt', 'le', 'neq', 'between', 'in', 'not-in', 'all',\n        'any', 'diff', 'startswith', 'endswith'\n    }\n    fields = LineId.DESCRIPTOR.fields_by_name\n    assert field_name in fields\n    assert operand is not None\n\n    field = fields[field_name]\n    string_operand = []\n\n    if field.has_options:\n      assert op in {'all', 'any', 'diff'}\n      assert field.cpp_type in {\n          field.CPPTYPE_INT32, field.CPPTYPE_INT64, field.CPPTYPE_UINT32,\n          field.CPPTYPE_UINT64\n      }\n      if not isinstance(operand, (list, tuple)):\n        assert isinstance(operand, int)\n        int_operand, float_operand = [operand], []\n      else:\n        assert all(isinstance(o, int) for o in operand)\n        int_operand, float_operand = list(operand), []\n    elif field.cpp_type in {field.CPPTYPE_DOUBLE, field.CPPTYPE_FLOAT}:\n      if op == 'between':\n        assert all(isinstance(o, (int, float)) for o in operand)\n        int_operand, float_operand = [], [float(o) for o in operand]\n      else:\n        int_operand, float_operand = [], [float(operand)]\n    elif field.cpp_type in {\n        field.CPPTYPE_INT32, field.CPPTYPE_INT64, field.CPPTYPE_UINT32,\n        field.CPPTYPE_UINT64\n    }:\n      if op in {'in', 'not-in', 'between'}:\n        assert all(isinstance(o, int) for o in operand)\n        int_operand, float_operand = list(operand), []\n      else:\n        int_operand, float_operand = [int(operand)], []\n    elif field.cpp_type == field.CPPTYPE_STRING:\n      int_operand, float_operand = [], []\n      if isinstance(operand, str):\n        string_operand.append(operand)\n      elif isinstance(operand, (list, tuple)):\n        assert all(isinstance(o, str) for o in operand)\n        string_operand.extend(operand)\n      else:\n        raise RuntimeError(\"params error!\")\n    else:\n      raise RuntimeError(\"params error!\")\n\n    self.field_name = field_name\n    self.op = op\n    self.float_operand = float_operand\n    self.int_operand = int_operand\n    self.string_operand = string_operand\n    self.keep_empty = keep_empty\n\n  def as_proto(self) -> transform_config_pb2.TransformConfig():\n    config = transform_config_pb2.TransformConfig()\n    transform = config.configs.add()\n    transform.basic_config.filter_by_value.field_name = self.field_name\n    transform.basic_config.filter_by_value.op = self.op\n    transform.basic_config.filter_by_value.float_operand.extend(\n        self.float_operand)\n    transform.basic_config.filter_by_value.int_operand.extend(self.int_operand)\n    transform.basic_config.filter_by_value.string_operand.extend(\n        self.string_operand)\n    transform.basic_config.filter_by_value.keep_empty = self.keep_empty\n\n    return config\n\n  def _is_leaf_node(self) -> bool:\n    return True\n\n\nclass AddLabel(Transform):\n\n  def __init__(self, config: str, negative_value: float,\n               new_sample_rate: float):\n    self.config = config\n    self.negative_value = negative_value\n    self.new_sample_rate = new_sample_rate\n\n  def as_proto(self) -> transform_config_pb2.TransformConfig():\n    config = transform_config_pb2.TransformConfig()\n    transform = config.configs.add()\n    transform.basic_config.add_label.negative_value = self.negative_value\n    transform.basic_config.add_label.new_sample_rate = self.new_sample_rate\n\n    for task in self.config.split(';'):\n      # skip empty parts, e.g. config = '1,2:3:1.0;'\n      if len(task) == 0:\n        continue\n\n      task_label_config = transform.basic_config.add_label.task_label_configs.add(\n      )\n      pos_actions, neg_actions, sample_rate = task.split(':')\n      pos_actions_list = [\n          int(pos) for pos in pos_actions.split(',') if len(pos) > 0\n      ]\n      neg_actions_list = [\n          int(neg) for neg in neg_actions.split(',') if len(neg) > 0\n      ]\n      task_label_config.pos_actions.extend(pos_actions_list)\n      task_label_config.neg_actions.extend(neg_actions_list)\n      task_label_config.sample_rate = float(sample_rate)\n    return config\n\n  def _is_leaf_node(self) -> bool:\n    return True\n\n\nclass LogicalOr(Transform):\n\n  def __init__(self, x: Transform, y: Transform):\n    self.x = x\n    self.y = y\n    assert x._is_leaf_node() and y._is_leaf_node()\n\n  def as_proto(self) -> transform_config_pb2.TransformConfig():\n    config = transform_config_pb2.TransformConfig()\n    transform = config.configs.add()\n    transform.logical_or_config.x.CopyFrom(\n        self.x.as_proto().configs[0].basic_config)\n    transform.logical_or_config.y.CopyFrom(\n        self.y.as_proto().configs[0].basic_config)\n    return config\n\n  def _is_leaf_node(self) -> bool:\n    return False\n"
  },
  {
    "path": "monolith/native_training/data/transform/transforms_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport unittest\n\nfrom absl import app, logging\nfrom monolith.native_training.data.transform import transforms\n\n\nclass TransformsTest(unittest.TestCase):\n\n  def test_filter_by_fid(self):\n    proto = transforms.FilterByFid(has_fids=[1],\n                                   filter_fids=[2, 3],\n                                   select_fids=None).as_proto()\n    logging.info(proto)\n\n  def test_filter_by_action(self):\n    proto = transforms.FilterByAction(has_actions=[4]).as_proto()\n    logging.info(proto)\n\n  def test_filter_by_label(self):\n    proto = transforms.FilterByLabel(thresholds=[-100, -100]).as_proto()\n    logging.info(proto)\n\n  def test_add_label(self):\n    proto = transforms.AddLabel(config='1,2:3:1.0;4::0.5',\n                                negative_value=0.0,\n                                new_sample_rate=0.3).as_proto()\n    logging.info(proto)\n\n  def test_logical_or(self):\n    proto = transforms.LogicalOr(\n        x=transforms.FilterByAction(has_actions=[1, 2]),\n        y=transforms.FilterByFid(has_fids=[10000000])).as_proto()\n    logging.info(proto)\n\n  def test_compose(self):\n    transform = transforms.Compose([\n        transforms.FilterByFid(has_fids=[1],\n                               filter_fids=[2, 3],\n                               select_fids=None),\n        transforms.FilterByLabel(thresholds=[-100, -100]),\n        transforms.AddLabel(config='1,2:3:1.0;4::0.5',\n                            negative_value=0.0,\n                            new_sample_rate=0.3),\n        transforms.LogicalOr(x=transforms.FilterByAction(has_actions=[1, 2]),\n                             y=transforms.FilterByFid(has_fids=[10000000]))\n    ])\n    logging.info(transform.as_proto())\n\n\ndef main(_):\n  logging.set_verbosity(logging.INFO)\n  unittest.main()\n\n\nif __name__ == '__main__':\n  app.run(main)\n"
  },
  {
    "path": "monolith/native_training/data/transform_dataset_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport io\nimport time\n\nfrom absl import logging\nimport os\nimport uuid\nimport struct\nimport tensorflow as tf\nimport tempfile\nfrom typing import List, BinaryIO\nfrom idl.matrix.proto import proto_parser_pb2, example_pb2\nfrom monolith.native_training.data.datasets import PBDataset, PbType\nfrom monolith.native_training.data.parsers import (parse_instances,\n                                                   parse_examples)\nfrom monolith.native_training.data.transform import transforms\n\nfid_v1_mask = (1 << 54) - 1\nfid_v2_mask = (1 << 48) - 1\n\n\ndef get_fid_v1(slot: int, signautre: int):\n  return (slot << 54) | (signautre & fid_v1_mask)\n\n\ndef get_fid_v2(slot: int, signature: int):\n  return (slot << 48) | (signature & fid_v2_mask)\n\n\ndef mock_instance_line_id(index: int, instance, actions: List[int]):\n  instance.line_id.user_id = \"test_{}\".format(uuid.uuid4())\n  instance.line_id.uid = 100\n  instance.line_id.read_count = 0 if 20 <= index < 40 else 1\n  instance.line_id.video_play_time = 0.0 if 20 <= index < 30 else 1.0\n  instance.line_id.req_time = int(time.time())\n  instance.line_id.sample_rate = 0.5\n  instance.line_id.actions.extend(actions)\n\n\ndef generate_instance_or_example(variant_type: str,\n                                 index: int,\n                                 labels: List[int],\n                                 actions: List[int],\n                                 fid_v1_list: List[int] = None):\n  assert variant_type in {\"instance\", \"example\"}\n  if variant_type == \"instance\":\n    instance = proto_parser_pb2.Instance()\n    instance.fid.extend(fid_v1_list if fid_v1_list else [])\n  else:\n    instance = example_pb2.Example()\n    named_feature = instance.named_feature.add()\n    named_feature.name = \"fc_slot_1\"\n    named_feature.feature.fid_v1_list.value.extend(fid_v1_list)\n\n  instance.label.extend(labels)\n  mock_instance_line_id(index, instance, actions)\n  return instance\n\n\ndef write_instance_into_file(file: BinaryIO, instance):\n  sort_id = str(instance.line_id.user_id)\n  file.write(struct.pack('<Q', len(sort_id)))\n  file.write(sort_id.encode())\n  instance_serialized = instance.SerializeToString()\n  file.write(struct.pack('<Q', len(instance_serialized)))\n  file.write(instance_serialized)\n\n\nclass DataOpsTest(tf.test.TestCase):\n\n  def mock_instance_or_example(self, variant_type: str, batch_num: int,\n                               batch_size: int):\n    tmpfile = tempfile.mkstemp()[1]\n    with io.open(tmpfile, 'wb') as writer:\n      for i in range(batch_num * batch_size):\n        instance = generate_instance_or_example(\n            variant_type=variant_type,\n            index=i,\n            labels=[i, 1 if i <= 2 else 0],\n            actions=[2 if 30 <= i < 35 else 0],\n            fid_v1_list=[get_fid_v1(2, i), get_fid_v1(3, i)]\n            if i < 10 else [290956192322012601])\n        write_instance_into_file(writer, instance)\n    return tmpfile\n\n  def instance_or_example_test(self, variant_type: str):\n    mock_batch_num = 10\n    batch_size = 4\n    file_name = self.mock_instance_or_example(variant_type, mock_batch_num,\n                                              batch_size)\n    logging.info('file_name: %s', file_name)\n\n    def parser(tensor: tf.Tensor):\n      if variant_type == \"instance\":\n        return parse_instances(tensor,\n                               fidv1_features=[2, 3],\n                               dense_features=['label'],\n                               dense_feature_shapes=[2])\n      else:\n        return parse_examples(tensor,\n                              sparse_features=[\"fc_slot_1\"],\n                              dense_features=['label'],\n                              dense_feature_shapes=[2])\n\n    with tf.Graph().as_default():\n      config = tf.compat.v1.ConfigProto()\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with self.session(config=config) as sess:\n        dataset = PBDataset(file_name=file_name,\n                            lagrangex_header=False,\n                            has_sort_id=True,\n                            kafka_dump=False,\n                            kafka_dump_prefix=False,\n                            input_pb_type=PbType.INSTANCE\n                            if variant_type == 'instance' else PbType.EXAMPLE,\n                            output_pb_type=PbType.INSTANCE\n                            if variant_type == 'instance' else PbType.EXAMPLE)\n\n        dataset = dataset.transform(t=transforms.Compose([\n            transforms.FilterByFid(select_fids=[290956192322012601]),\n            transforms.FilterByValue(field_name='read_count',\n                                     op='in',\n                                     operand=[0]),\n            transforms.LogicalOr(\n                transforms.FilterByValue(field_name='video_play_time',\n                                         op='eq',\n                                         operand=0.0),\n                transforms.FilterByAction(has_actions=[2]))\n        ]),\n                                    variant_type=variant_type)\n        dataset = dataset.batch(batch_size, drop_remainder=False).map(parser)\n        it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n\n        total_count = 0\n        while True:\n          try:\n            element = it.get_next()\n            element_result = sess.run(element)\n            logging.info('element: %s', element_result)\n            total_count += element_result[\"label\"].shape[0]\n          except tf.errors.OutOfRangeError:\n            break\n        self.assertEqual(total_count, 15)\n    os.remove(file_name)\n\n  def test_instance(self):\n    self.instance_or_example_test(variant_type='instance')\n\n  def test_example(self):\n    self.instance_or_example_test(variant_type='example')\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/data/utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom typing import Dict, Union, List\nTOBENV = False\n\nUSED_FREATUE_NAMES = {}\nNAME_TO_SLOT = {}\n\n\ndef enable_tob_env():\n  global TOBENV\n  TOBENV = True\n\n\ndef get_slot_feature_name(slot: int):\n  if TOBENV:\n    return \"fc_slot_{}\".format(slot)\n  else:\n    return \"slot_{}\".format(slot)\n\n\ndef get_slot_from_feature_name(feature_name: str):\n  if feature_name in NAME_TO_SLOT:\n    return NAME_TO_SLOT[feature_name]\n  elif feature_name.startswith('slot_') or feature_name.startswith('fc_slot_'):\n    slot = feature_name.split('_')[-1]\n    return int(slot) if slot.isdigit() else None\n  else:\n    if feature_name in USED_FREATUE_NAMES:\n      return USED_FREATUE_NAMES[feature_name]\n    else:\n      USED_FREATUE_NAMES[feature_name] = len(USED_FREATUE_NAMES) + 1\n      return len(USED_FREATUE_NAMES)\n\n\ndef register_slots(sparse_features: Union[List[int], Dict[str, int]]):\n  if isinstance(sparse_features, (list, tuple)):\n    assert all([isinstance(x, int) for x in sparse_features])\n    sparse_features = {get_slot_feature_name(slot): slot for slot in sparse_features}\n  else:\n    assert isinstance(sparse_features, dict)\n\n  NAME_TO_SLOT.update(sparse_features)\n"
  },
  {
    "path": "monolith/native_training/debugging/BUILD",
    "content": "load(\"@pip_deps//:requirements.bzl\", \"requirement\")\nload(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_test\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\npy_binary(\n    name = \"debugging_server\",\n    srcs = [\"debugging_server.py\"],\n    deps = [\n        \"//monolith:utils\",\n        \"//monolith/native_training:cluster_manager\",\n        \"//monolith/native_training:env_utils\",\n        \"//monolith/native_training:multi_type_hash_table\",\n        \"//monolith/native_training:utils\",\n        \"//monolith/native_training/proto:debugging_info_py_proto\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n        requirement(\"flask\"),\n        requirement(\"flask_api\"),\n    ],\n)\n\npy_binary(\n    name = \"debugging_client\",\n    srcs = [\"debugging_client.py\"],\n    deps = [\n        \":debugging_server\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/debugging/README.md",
    "content": ""
  },
  {
    "path": "monolith/native_training/debugging/debugging_client.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nimport requests\nfrom google.protobuf import text_format\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport tensorflow as tf\nfrom monolith.native_training.debugging.debugging_server import \\\n    STATUS, SUCCESS, FAIL, MSG\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\nFLAGS = flags.FLAGS\nflags.DEFINE_string(\n    \"type\", \"\",\n    \"The debugging type: debugging_variables or debugging_features.\")\nflags.DEFINE_list(\"variable_names\", \"\", \"The variable names for debugging.\")\nflags.DEFINE_list(\"feature_ids\", \"\", \"The feature ids for debugging.\")\nflags.DEFINE_string(\"feature_name\", \"\", \"The feature name of all ids.\")\nflags.DEFINE_list(\n    \"feature_names\", \"\",\n    \"The feature names of all ids. If use this flag, the size of names and the size of ids must be equal.\"\n)\n\n\ndef main(_):\n  if FLAGS.type == \"debugging_variables\":\n    if not FLAGS.variable_names:\n      logging.info(\"Empty variable names\")\n      return\n\n    data = json.dumps({\"variable_names\": FLAGS.variable_names})\n    res = requests.post(\"http://127.0.0.1:%s/debugging/variables\" % FLAGS.port,\n                        data=data)\n    res = json.loads(res.text)\n    if res[STATUS] == FAIL:\n      logging.info(\"Request fail! Reason: %s\" % res[MSG])\n      return\n    msg = json.loads(res[MSG])\n    for name in FLAGS.variable_names:\n      logging.info(\"Variables: name[%s], value[%s]\" %\n                   (name, msg.get(name, \"Not exist\")))\n\n  elif FLAGS.type == \"debugging_features\":\n    if FLAGS.feature_name and FLAGS.feature_names:\n      raise Exception(\"Can not provide both feature_name and feature_names.\")\n    if not FLAGS.feature_ids:\n      logging.info(\"Empty feature ids\")\n      return\n    if FLAGS.feature_name:\n      FLAGS.feature_names = [FLAGS.feature_name] * len(FLAGS.feature_ids)\n    if len(FLAGS.feature_names) != len(FLAGS.feature_ids):\n      raise Exception(\n          \"Size of feature names [%s] and size of feature ids [%s] must be equal.\"\n          % (len(FLAGS.feature_names), len(FLAGS.feature_ids)))\n\n    data = json.dumps({\n        \"feature_names\": FLAGS.feature_names,\n        \"feature_ids\": FLAGS.feature_ids\n    })\n    res = requests.post(\"http://127.0.0.1:%s/debugging/features\" % FLAGS.port,\n                        data=data)\n    res = json.loads(res.text)\n    if res[STATUS] == FAIL:\n      logging.info(\"Request fail! Reason: %s\" % res[MSG])\n      return\n    msg = json.loads(res[MSG])\n    for fname, fid in zip(FLAGS.feature_names, FLAGS.feature_ids):\n      if fname in msg and fid in msg[fname]:\n        entry_dump = embedding_hash_table_pb2.EntryDump()\n        text_format.Parse(msg[fname][fid], entry_dump)\n        logging.info(\"Features: name[%s], id[%s], value[\\n%s]\" %\n                     (fname, fid, entry_dump))\n      else:\n        logging.info(\"Features: name[%s], id[%s], value[Not exist]\" %\n                     (fname, fid))\n\n\nif __name__ == '__main__':\n  logging.set_verbosity(logging.INFO)\n  tf.compat.v1.disable_eager_execution()\n  app.run(main)\n"
  },
  {
    "path": "monolith/native_training/debugging/debugging_server.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nimport traceback\nfrom collections import defaultdict\nfrom google.protobuf import text_format\n\nimport flask\nfrom flask import request\nfrom typing import List\nfrom urllib.parse import urlparse\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import meta_graph\nfrom tensorflow.python.lib.io import file_io\n\nfrom monolith.utils import get_libops_path\nfrom monolith.native_training import cluster_manager\nfrom monolith.native_training import env_utils\nfrom monolith.native_training import multi_type_hash_table\nfrom monolith.native_training import utils\nfrom monolith.native_training.proto import debugging_info_pb2\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\nSTATUS = \"status\"\nSUCCESS = \"success\"\nFAIL = \"fail\"\n\nMSG = \"msg\"\n\nFLAGS = flags.FLAGS\nflags.DEFINE_string(\"host\", \"\", \"The host of server\")\nflags.DEFINE_integer(\"port\", 0, \"The port of server\")\nflags.DEFINE_string(\n    \"model_dir\",\n    default=\"\",\n    help=\"Directory where model parameters, graph, etc are saved.\")\n\nclip_ops = gen_monolith_ops\n\nfilter_ops = gen_monolith_ops\nfilter_save_op = gen_monolith_ops\nfilter_restore_op = gen_monolith_ops\n\nmonolith_custom_ops = gen_monolith_ops\n\ntraining_ops = gen_monolith_ops\n\npb_datasource_ops = gen_monolith_ops\n\n\nclass DebuggingWorker:\n  \"\"\"A debugging worker that connected to training servers.\"\"\"\n\n  def __init__(self, model_dir):\n\n    debugging_info_str = file_io.read_file_to_string(\n        utils.get_debugging_info_file_name(model_dir), binary_mode=True)\n    self._debugging_info = debugging_info_pb2.DebuggingInfo()\n    self._debugging_info.ParseFromString(debugging_info_str)\n\n    self._server = tf.distribute.Server(\n        {\"local\": [utils.get_local_host() + \":0\"]})\n    self._addr = urlparse(self._server.target).netloc\n\n    fake_worker_list = [\n        \"0.0.0.0:{}\".format(i)\n        for i in range(1, self._debugging_info.num_workers)\n    ]\n    fake_worker_list.append(self._addr)\n    cluster = {\n        \"chief\": [self._debugging_info.cluster.chief_addr],\n        \"ps\": [addr for addr in self._debugging_info.cluster.ps_addrs],\n        \"worker\": fake_worker_list,\n    }\n    task = {\"type\": \"worker\", \"index\": self._debugging_info.num_workers - 1}\n\n    feature_name_config_map = {}\n    for feature_name_config in self._debugging_info.feature_name_configs:\n      feature_name_config_map[\n          feature_name_config.feature_name] = feature_name_config.config_str\n\n    def dummy_factory(*args, **kwargs):\n      pass\n\n    self._dummy_merged_table = multi_type_hash_table.MergedMultiTypeHashTable(\n        feature_name_config_map, dummy_factory)\n\n    self._session_config = cluster_manager.generate_session_config(\n        (cluster, task))\n    logging.info(self._session_config)\n    self._graph = tf.Graph()\n    with tf.compat.v1.Session(self._server.target,\n                              config=self._session_config,\n                              graph=self._graph) as sess:\n      self._imported_vars, self._imported_return_elements = (\n          meta_graph.import_scoped_meta_graph_with_return_elements(\n              utils.get_meta_graph_file_name(model_dir)))\n    logging.info(\"Finish import meta graph.\")\n\n  def _get_table_name(self, merged_name, index):\n    return \"MonolithHashTable_%s_%s\" % (merged_name, index)\n\n  def fetch_variables(self, variable_names: List[str]):\n    with tf.compat.v1.Session(self._server.target,\n                              config=self._session_config,\n                              graph=self._graph) as sess:\n      req_variables, req_names = [], []\n      for var_name in variable_names:\n        if var_name in self._imported_vars:\n          req_variables.append(self._imported_vars[var_name])\n          req_names.append(var_name)\n      resp_variables = sess.run(req_variables)\n    return {k: str(v) for k, v in zip(req_names, resp_variables)}\n\n  def fetch_features(self, feature_names: List[str], feature_ids: List[str]):\n    merged_name_to_features = defaultdict(list)\n    for fea_name, fid in zip(feature_names, feature_ids):\n      if fea_name not in self._dummy_merged_table.slot_mapping:\n        continue\n      merged_name_to_features[\n          self._dummy_merged_table.slot_mapping[fea_name]].append(\n              (fea_name, int(fid)))\n\n    feature_to_entry_dump_str = defaultdict(dict)\n    with tf.compat.v1.Session(self._server.target,\n                              config=self._session_config,\n                              graph=self._graph) as sess:\n      for merged_name, features in merged_name_to_features.items():\n        indices = tf.math.floormod(\n            [f[1] for f in features],\n            len(self._debugging_info.cluster.ps_addrs)).eval()\n        index_to_features = defaultdict(list)\n        for index, fea in zip(indices, features):\n          index_to_features[index].append(fea)\n\n        for index, features in index_to_features.items():\n          table_name = self._get_table_name(merged_name, index)\n          table = self._graph.get_tensor_by_name(table_name + \":0\")\n          entry_dump_strs = sess.run(\n              monolith_custom_ops.monolith_hash_table_lookup_entry(\n                  table, tf.cast([f[1] for f in features], dtype=tf.int64)))\n          for (fname, fid), entry_dump_str in zip(features, entry_dump_strs):\n            if entry_dump_str:\n              entry_dump = embedding_hash_table_pb2.EntryDump()\n              entry_dump.ParseFromString(entry_dump_str)\n              feature_to_entry_dump_str[fname][str(\n                  fid)] = text_format.MessageToString(entry_dump)\n\n    return feature_to_entry_dump_str\n\n\ndef create_app() -> flask.Flask:\n  app = flask.Flask(\"Monolith_Debugging_Server\")\n  worker = DebuggingWorker(FLAGS.model_dir)\n\n  @app.route(\"/debugging/variables\", methods=[\"POST\"])\n  def fetch_variables():\n    try:\n      data = request.get_data()\n      data = json.loads(data)\n      logging.info(\"Fetch variables req: %s\" % data)\n      result = worker.fetch_variables(data.get(\"variable_names\", []))\n      resp = {STATUS: SUCCESS, MSG: json.dumps(result)}\n    except:\n      resp = {STATUS: FAIL, MSG: traceback.format_exc()}\n    logging.info(\"Fetch variables resp: %s\" % resp)\n    return resp\n\n  @app.route(\"/debugging/features\", methods=[\"POST\"])\n  def fetch_features():\n    try:\n      data = request.get_data()\n      data = json.loads(data)\n      logging.info(\"Fetch features req: %s\" % data)\n      feature_names = data.get(\"feature_names\", [])\n      feature_ids = data.get(\"feature_ids\", [])\n      if len(feature_names) != len(feature_ids):\n        raise Exception(\n            \"Size of feature names [%s] and size of feature ids [%s] must be equal.\"\n            % (len(feature_names), len(feature_ids)))\n      result = worker.fetch_features(feature_names, feature_ids)\n      resp = {STATUS: SUCCESS, MSG: json.dumps(result)}\n    except:\n      resp = {STATUS: FAIL, MSG: traceback.format_exc()}\n    logging.info(\"Fetch features resp: %s\" % resp)\n    return resp\n\n  return app\n\n\ndef main(_):\n  env_utils.setup_hdfs_env()\n  server_app = create_app()\n  server_app.run(host=FLAGS.host, port=FLAGS.port)\n\n\nif __name__ == \"__main__\":\n  app.run(main)\n"
  },
  {
    "path": "monolith/native_training/demo.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"bazel run -c opt monolith/native_training:demo -- --num_ps=0\"\"\"\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport tensorflow as tf\n\nfrom monolith.native_training import cpu_training\nfrom monolith.native_training.model import TestFFMModel\nfrom monolith.native_training.model_export.export_context import ExportMode\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_integer(\n    \"num_ps\",\n    default=0,\n    help=(\n        \"Number of parameter servers. 0 means no parameter server. Everything \"\n        \"runs on the single local server.\"))\nflags.DEFINE_string(\n    \"model_dir\",\n    default=None,\n    help=\"Directory where model parameters, graph, etc are saved.\")\n\n\ndef main(_):\n  params = TestFFMModel.params()\n  params.name = \"test_ffm_model\"\n  params.train.per_replica_batch_size = 64\n  params.serving.export_when_saving = True\n  params.serving.export_mode = ExportMode.DISTRIBUTED\n  cpu_training.local_train(params,\n                           num_ps=FLAGS.num_ps,\n                           model_dir=FLAGS.model_dir,\n                           steps=100,\n                           save_checkpoints_steps=50)\n\n\nif __name__ == '__main__':\n  logging.set_verbosity(logging.INFO)\n  tf.compat.v1.disable_eager_execution()\n  app.run(main)\n"
  },
  {
    "path": "monolith/native_training/dense_reload_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport re\nimport tensorflow as tf\nfrom absl import logging, flags\nimport numpy as np\nfrom collections import defaultdict\nfrom typing import Dict, List, Optional, Union, Set, Iterator, Tuple\n\nfrom tensorflow.python.training.saver import Saver\nfrom tensorflow.python.client.session import Session\nfrom tensorflow.python.training.py_checkpoint_reader import NewCheckpointReader, CheckpointReader\nfrom monolith.native_training.basic_restore_hook import CheckpointRestorerListener\nfrom monolith.native_training.model_export.export_context import is_exporting\n\nCUSTOM_RESTORE_OP = 'custom_restore_op'\nCustomRestoreListenerKey = 'CustomRestoreListener'\n# [TODO](fitz) this may not cover all situation\nPAT = re.compile(r\"^.+/part_(\\d+)(/.*)?$\")\nDensePat = re.compile(\n    r'''^(.*/)?(\\w+(?:_\\d+)?(?:\\d+)?)/(bias|kernel|trainable_kernel_norm)(.*)'''\n)\nFLAGS = flags.FLAGS\n\n# for those name cannot convert auto, ue use re-express to convert them\n_NameMapping = {\n    re.compile('c_dot/mlp(.*)?$'):\n        'c_dot/compress_tower{}',\n    re.compile('^dcn/kernel_(\\d+)/trainable_norm(.*)?$'):\n        'kernel_{}_trainable_norm{}',\n    re.compile('^dcn/kernel_(\\d+)(.*)?$'):\n        'kernel_{}{}',\n    re.compile('^dcn/bias_(\\d+)/trainable_norm(.*)?$'):\n        'bias_{}_trainable_norm{}',\n    re.compile('^dcn/bias_(\\d+)(.*)?$'):\n        'bias_{}{}',\n}\n\n\ndef add_mapping_rules(rules: Dict[str, str]):\n  global _NameMapping\n  _NameMapping.update({re.compile(pat): fmt for pat, fmt in rules.items()})\n\n\ndef node_name(name: str):\n  name = name.strip().rstrip('/')\n  if name.startswith('^'):\n    name = name[1:]\n  if ':' in name:\n    frist, second = name.rsplit(':', 1)\n    if second.isdigit():\n      name = frist\n  return name\n\n\ndef get_new_name(name: str):\n  selected = []\n  name = node_name(name)\n  for term in name.split('/'):\n    if term not in selected:\n      selected.append(term)\n  return '/'.join(selected)\n\n\ndef get_guess_name(name: str):\n  for pat, fmt in _NameMapping.items():\n    matched = pat.match(name)\n    if matched:\n      print(pat, matched.groups())\n      guess_name = fmt.format(*matched.groups())\n      return guess_name\n  return name\n\n\ndef split_name(name: str) -> int:\n  out = []\n  for i in range(-1, -len(name), -1):\n    x = name[i]\n    if x.isdigit():\n      out.append(x)\n    else:\n      break\n\n  if out:\n    out.reverse()\n    return name[0:len(name) - len(out)], int(''.join(out))\n  else:\n    return name, 0\n\n\ndef calc_reorder_info(names: List[str],\n                      is_ordered: bool = True) -> Tuple[bool, str]:\n  assert names is not None and len(names) > 0, str(names)\n\n  if not is_ordered:\n    names = names.copy()\n    names.sort(key=lambda x: split_name(x)[1])\n\n  _, start = split_name(names[0])\n  base_name, end = split_name(names[-1])\n  if start in {0, 1} and end - start == len(names) - 1 and len(names) == 1:\n    return False, 'dense_' if base_name == 'dense' else base_name\n  else:\n    return True, 'dense_' if base_name == 'dense' else base_name\n\n\ndef get_full_prefix(short_prefix: str, prefix_set: Set[str]) -> str:\n  out = None\n  for p in prefix_set:\n    if p.endswith(short_prefix):\n      if out is None:\n        out = p\n      elif len(out) < len(p):\n        out = p\n  return out or short_prefix\n\n\ndef update_var_name_mapping_for_dense(\n    var_name_mapping: Dict[str, str]) -> Dict[str, str]:\n  dense_layers, prefixs = defaultdict(list), defaultdict(set)\n  for name, origin in var_name_mapping.items():\n    matched = DensePat.match(name)\n    if matched:\n      prefix = matched.group(1).rstrip('/') if matched.group(1) else ''\n      dense_name = matched.group(2)\n      dense_local_var = matched.group(3)\n      surfix = matched.group(4).lstrip('/')\n      key = f'{dense_name}/{dense_local_var}'\n      dense_layers[key].append(\n          (prefix, dense_name, dense_local_var, surfix, origin))\n      if dense_local_var == 'bias':\n        prefixs[key].add(prefix)\n\n  dense_layers_refactor = defaultdict(list)\n  for key, terms_list in dense_layers.items():\n    for terms in terms_list:\n      prefix, dense_name, dense_local_var, surfix, origin = terms\n      prefix_set = prefixs[f'{dense_name}/bias']\n      prefix = get_full_prefix(prefix, prefix_set)\n\n      if prefix not in dense_layers_refactor:\n        dense_layers_refactor[prefix] = defaultdict(list)\n      dense_layers_refactor[prefix][dense_name].append(\n          (prefix, dense_name, dense_local_var, surfix, origin))\n\n  for bias_prefix, layers_vars in dense_layers_refactor.items():\n    dense_names = list(layers_vars)\n    if len(dense_names) > 1:\n      dense_names.sort(key=lambda x: split_name(x)[1])\n    need_reorder, base = calc_reorder_info(dense_names)\n    for i, dense_name in enumerate(dense_names):\n      new_dense_name = f'{base}{i}' if need_reorder else dense_name\n      for var_terms in layers_vars[dense_name]:\n        prefix, local_var_name, origin = var_terms[0], var_terms[2], var_terms[\n            -1]\n        if prefix == '' or bias_prefix.endswith(prefix):\n          new_name = '/'.join(\n              [bias_prefix, new_dense_name, local_var_name,\n               var_terms[3]]).rstrip('/')\n          if local_var_name == 'bias':\n            var_name_mapping[new_name] = origin\n          else:\n            if new_name not in var_name_mapping:\n              var_name_mapping[new_name] = origin\n\n  # note: this may introduce problem, we deal with it at calc_feed_dict\n  for layers_vars in dense_layers_refactor.values():\n    for var_terms_list in layers_vars.values():\n      for var_terms in var_terms_list:\n        new_name = '/'.join(var_terms[0:-1]).rstrip('/')\n        if new_name not in var_name_mapping:\n          var_name_mapping[new_name] = var_terms[-1]\n\n\nclass CustomRestoreListener(CheckpointRestorerListener):\n\n  def __init__(self,\n               alias_map: Dict[str, str] = None,\n               clear_nn: bool = False,\n               continue_training: bool = False,\n               model_dir: str = None,\n               enable_alias_map_auto_gen: bool = None):\n    self._alias_map = alias_map\n    self._clear_nn = clear_nn\n    self._continue_training = continue_training\n    self.model_dir = model_dir\n    self.ckpt_name = None\n    self.enable_alias_map_auto_gen = True if enable_alias_map_auto_gen is None else enable_alias_map_auto_gen\n\n  def begin(self):\n    logging.info('CustomRestoreListener begin ...')\n    if is_exporting():\n      return\n\n    checkpoint_state = None\n    try:\n      checkpoint_state = tf.train.get_checkpoint_state(\n          checkpoint_dir=self.model_dir)\n      self.ckpt_name = checkpoint_state.model_checkpoint_path\n    except Exception as e:\n      return\n    if checkpoint_state is None:\n      return\n\n    graph: tf.Graph = tf.compat.v1.get_default_graph()\n    variables = graph.get_collection('variables')\n\n    if self._clear_nn:\n      assert self._alias_map is None\n      if self.model_dir:\n        flag_file = os.path.join(self.model_dir, 'clear_nn')\n        if tf.io.gfile.exists(flag_file):\n          logging.info(\n              f'the clear nn flag_file exists, skip clear, {flag_file}')\n          return\n\n      init_op = tf.compat.v1.global_variables_initializer()\n      setattr(init_op, 'model_dir', self.model_dir)\n      if self._continue_training:\n        gs_var = tf.compat.v1.train.get_or_create_global_step(graph=graph)\n        ph = tf.compat.v1.placeholder(dtype=gs_var.dtype,\n                                      shape=gs_var.shape,\n                                      name=\"global_step_ph\")\n        update_gs_op = gs_var.assign(value=ph)\n        graph.add_to_collection(CUSTOM_RESTORE_OP,\n                                ([init_op, update_gs_op], [ph], None))\n      else:\n        graph.add_to_collection(CUSTOM_RESTORE_OP, ([init_op], [None], None))\n    elif self._need_build_custom_init_graph(variables):\n      assign_ops, placeholders = [], []\n      for variable in variables:\n        # [TODO](fitz) usually after getting variables from collection,\n        # the variable name is tensor like, with the surfix ':0', add test to ensure it\n        var_name = node_name(variable.name)\n        ph = tf.compat.v1.placeholder(dtype=variable.dtype,\n                                      shape=variable.shape,\n                                      name=var_name)\n        # (fitz) since tf name scope mechanism, '_\\d' may add as a suffix,\n        # as a result we record the origin_name variable name\n        ph.origin_name = var_name\n        assign_op = variable.assign(value=ph)\n        assign_ops.append(assign_op)\n        placeholders.append(ph)\n\n      init_op = tf.group(assign_ops)\n      graph.add_to_collection(CUSTOM_RESTORE_OP,\n                              ([init_op], placeholders, self._alias_map))\n    else:\n      logging.info(\"nothing to do in CustomRestoreListener\")\n\n  def _need_build_custom_init_graph(self, variables: List[tf.Variable]) -> bool:\n    assert self._clear_nn == False\n    # for compat, this may not cover all satuation\n    if not self._alias_map and self.enable_alias_map_auto_gen:\n      # 1) load variable name from ckpt\n      ckpt: CheckpointReader = NewCheckpointReader(self.ckpt_name)\n      all_old_var_names = set(ckpt.get_variable_to_dtype_map().keys())\n      # 2) check if need alias reload, if we can find all variables in ckpt, no alias reload required\n      cnt = 0\n      pat = re.compile(r\"/part_\\d+\")\n      for variable in variables:\n        expected_saved_varibale_name = node_name(''.join(\n            pat.split(variable.name)))\n        if expected_saved_varibale_name in all_old_var_names:\n          cnt += 1\n      if len(variables) == cnt:\n        logging.info(\"The ckpt is compatable, no need alias reload\")\n        return False\n\n      logging.info(\n          \"The ckpt is incompatable, begin to generate alias reload automatical ...\"\n      )\n      logging.info(f'all_old_var_names = {all_old_var_names}')\n\n      # 3) try to convert old variable name to new one\n      var_name_mapping = {}\n      for name in all_old_var_names:\n        var_name_mapping[get_new_name(name)] = name\n      logging.info(f'var_name_mapping = {var_name_mapping}')\n      update_var_name_mapping_for_dense(var_name_mapping)\n      logging.info(f'var_name_mapping after update = {var_name_mapping}')\n\n      # 4) generate alias_map\n      alias_map = {}\n      miss_dense_names = defaultdict(list)\n      miss_dense_map = {}\n      for variable in variables:\n        expected_saved_varibale_name = node_name(''.join(\n            pat.split(variable.name)))\n        var_name = node_name(variable.name)\n        if var_name_mapping.get(expected_saved_varibale_name) == None:\n          # record needed info to deal with None value dense variables\n          matched = DensePat.match(expected_saved_varibale_name)\n          if matched:\n            prefix = matched.group(1).rstrip('/') if matched.group(1) else ''\n            dense_name = matched.group(2)\n            dense_local_var = matched.group(3)\n            surfix = matched.group(4).lstrip('/')\n            key = f'{prefix}/{dense_local_var}/{surfix}'\n            miss_dense_names[key].append(dense_name)\n        alias_map[var_name] = var_name_mapping.get(expected_saved_varibale_name)\n      logging.info(f'miss_dense_names : {miss_dense_names}')\n      for k, v in miss_dense_names.items():\n        if len(v) >= 1:\n          v.sort()\n          sub_value = int(v[0].split('_')[1])\n          insert_pos = k[:k.rfind('/')].rfind('/')\n          if k[-1] == '/':\n            k = k.rstrip('/')\n          for i, name in enumerate(v):\n            old_name = k[:insert_pos + 1] + v[i] + '/' + k[insert_pos + 1:]\n            new_name = k[:insert_pos + 1] + v[i].split('_')[0] + '_' + str(\n                int(v[i].split('_')[1]) - sub_value) + '/' + k[insert_pos + 1:]\n            miss_dense_map[old_name] = new_name\n      logging.info(f'miss_dense_map : {miss_dense_map}')\n\n      # 5) check whether alias_map is validate\n      none_values = {name for name, value in alias_map.items() if value is None}\n      logging.info(f'none_values = {none_values}')\n      for name in none_values:\n        expected_saved_varibale_name = node_name(''.join(pat.split(name)))\n        guess_name = get_guess_name(expected_saved_varibale_name)\n        if guess_name == expected_saved_varibale_name:\n          guess_name = miss_dense_map.get(name)\n        if guess_name in var_name_mapping:\n          alias_map[name] = var_name_mapping[guess_name]\n        else:\n          logging.info(f'the guess_name = {guess_name} with name = {name}')\n          logging.warning(\n              'The ckpt is incompatable, but cannot alias reload automatical, pls spectify an alias_map'\n          )\n          logging.info(f'alias_map = {alias_map}')\n          return False\n\n      # logging.info(f\"The ckpt is incompatable, begin to generate alias reload automatical done!\")\n      logging.info(\n          f\"The ckpt is incompatable, begin to generate alias reload automatical done! alias_map = {alias_map}\"\n      )\n      # 6) assign alias_map\n      self._alias_map = alias_map\n\n    if self._alias_map:\n      new_names_alias_map = set(self._alias_map.values())\n      new_names_from_var = {node_name(variable.name) for variable in variables}\n      return len(new_names_from_var - new_names_alias_map) > 0\n    else:\n      return False\n\n  @classmethod\n  def get(cls):\n    return cls.__instance\n\n\ndef infer_variable_name(names: List[str]) -> Set[str]:\n  new_names = set()\n  pat = re.compile(r'/part_\\d+')\n\n  for name in names:\n    items = pat.split(name)\n    if len(items) == 1:\n      new_names.add(items[0])\n    else:\n      new_names.add(''.join(items))\n\n  return new_names\n\n\ndef calc_feed_dict(ckpt: CheckpointReader, alias_map: Dict[str, str],\n                   placeholders: list) -> Dict[str, np.ndarray]:\n  all_old_var_names = set(ckpt.get_variable_to_dtype_map().keys())\n  reversed_alias_map = defaultdict(list)\n  for new_name, old_name in alias_map.items():\n    reversed_alias_map[old_name].append(new_name)\n\n  all_required_new_names = set(alias_map.keys())\n  # because tf will merge partitioned variable when saving\n  # we need to infer_variable_name by remove part_xx form variable name\n  all_new_var_names = infer_variable_name(all_required_new_names)\n  if len(all_new_var_names - all_old_var_names) == 0:\n    logging.info('no need to use alias_map to restore ...')\n    return None\n  else:\n    logging.info(\n        f'need restore form alias_map: {all_required_new_names - all_old_var_names}'\n    )\n\n  ph_dict = {}\n  for ph in placeholders:\n    if hasattr(ph, 'origin_name'):\n      new_var_name = ph.origin_name\n    else:\n      raise Exception(f'Cannot get origin_name of {ph}')\n    ph_dict[new_var_name] = ph\n\n  result = {}\n  for old_name, new_name_list in reversed_alias_map.items():\n    if len(new_name_list) == 1:\n      new_name = new_name_list[0]\n      result[ph_dict[new_name]] = ckpt.get_tensor(old_name)\n    else:\n      # this branch is for partitioned variables\n      old_tensor = ckpt.get_tensor(old_name)\n\n      # deal with problem maybe introduced by update_var_name_mapping_for_dense\n      new_groups = defaultdict(list)\n      for new_name in new_name_list:\n        matched = DensePat.match(new_name)\n        if matched:\n          key = matched.group(2)\n          new_groups[key].append(new_name)\n\n      if len(new_groups) > 1:\n        denses = sorted(new_groups, key=lambda x: split_name(x)[1])\n        new_name_list = new_groups[denses[0]]\n\n      if len(new_name_list) == 1:\n        new_name = new_name_list[0]\n        result[ph_dict[new_name]] = old_tensor\n        continue\n\n      # sort the partitioned sub variable by partition index\n      # .+/part_xx/.*, we extract the last part_xx, and sort accrodingly\n      new_name_list = sorted(new_name_list,\n                             key=lambda x: int(PAT.match(x).group(1)))\n\n      # get the first dim for placeholder as splits\n      splits = [ph_dict[name].shape[0] for name in new_name_list]\n\n      # construct indices_or_sections for numpy.split function\n      indices_or_sections = [0] * (len(splits) - 1)\n      for i, val in enumerate(splits):\n        if i == 0:\n          indices_or_sections[i] = val\n        elif i == len(splits) - 1:\n          break\n        else:\n          indices_or_sections[i] = indices_or_sections[i - 1] + val\n\n      # split old_tensor into partition,\n      sub_tensors = np.split(old_tensor, indices_or_sections, axis=0)\n      for name, tensor in zip(new_name_list, sub_tensors):\n        result[ph_dict[name]] = tensor\n\n  return result\n"
  },
  {
    "path": "monolith/native_training/dense_reload_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nfrom typing import List\n\nimport tensorflow as tf\nfrom tensorflow.keras.initializers import Ones, GlorotNormal\nfrom monolith.native_training.dense_reload_utils import infer_variable_name, calc_feed_dict, \\\n  CustomRestoreListener\nfrom tensorflow.python.training.py_checkpoint_reader import NewCheckpointReader, CheckpointReader\n\n\nclass DenseReloadUtilsTest(tf.test.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    with tf.Graph().as_default():\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      partitioner = tf.compat.v1.variable_axis_size_partitioner(\n          max_shard_bytes=1 << 17, max_shards=100)\n      partition_var = tf.compat.v1.get_variable(name='partition',\n                                                shape=(1280, 512),\n                                                dtype=tf.float32,\n                                                partitioner=partitioner,\n                                                initializer=GlorotNormal())\n\n      var = tf.compat.v1.get_variable(name='small_var',\n                                      shape=(10, 5),\n                                      dtype=tf.float32,\n                                      initializer=Ones())\n      # initialize all of the variables\n      init = tf.compat.v1.global_variables_initializer()\n\n      saver = tf.compat.v1.train.Saver([partition_var, var, global_step])\n\n      with tf.compat.v1.Session() as sses:\n        sses.run(init)\n        saver.save(sses,\n                   save_path=f\"{os.getcwd()}/ckpt/test\",\n                   global_step=global_step)\n\n  @classmethod\n  def tearDownClass(cls):\n    if tf.io.gfile.exists(path=f\"{os.getcwd()}/ckpt\"):\n      tf.io.gfile.rmtree(path=f\"{os.getcwd()}/ckpt\")\n\n  def test_infer_variable_name(self):\n    with tf.Graph().as_default():\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      partitioner = tf.compat.v1.variable_axis_size_partitioner(\n          max_shard_bytes=1 << 17, max_shards=100)\n      partition_var = tf.compat.v1.get_variable(name='partition',\n                                                shape=(1280, 512),\n                                                dtype=tf.float32,\n                                                partitioner=partitioner,\n                                                initializer=GlorotNormal())\n\n      var = tf.compat.v1.get_variable(name='small_var',\n                                      shape=(10, 5),\n                                      dtype=tf.float32,\n                                      initializer=Ones())\n\n      names = [part.name for part in partition_var._get_variable_list()]\n      self.assertEqual(var.name, 'small_var:0')\n      self.assertSetEqual(infer_variable_name(names),\n                          {f'{partition_var.name}:0'})\n\n  def test_calc_feed_dict(self):\n    with tf.Graph().as_default():\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      partitioner = tf.compat.v1.variable_axis_size_partitioner(\n          max_shard_bytes=1 << 17, max_shards=100)\n      partition_var = tf.compat.v1.get_variable(name='partition2',\n                                                shape=(1280, 512),\n                                                dtype=tf.float32,\n                                                partitioner=partitioner,\n                                                initializer=GlorotNormal())\n\n      var = tf.compat.v1.get_variable(name='small_var2',\n                                      shape=(10, 5),\n                                      dtype=tf.float32,\n                                      initializer=Ones())\n      alias_map = {'small_var2': 'small_var', 'global_step': 'global_step'}\n      var_ph = tf.compat.v1.placeholder(dtype=tf.float32, shape=(10, 5))\n      var_ph.origin_name = 'small_var2'\n      gs_ph = tf.compat.v1.placeholder(dtype=tf.int64)\n      gs_ph.origin_name = 'global_step'\n\n      placeholders = [var_ph, gs_ph]\n      for part in partition_var._get_variable_list():\n        if part.name.endswith(':0'):\n          var_name = part.name[0:-2]\n        else:\n          var_name = part.name\n\n        alias_map[var_name] = 'partition'\n        ph = tf.compat.v1.placeholder(dtype=part.dtype, shape=part.shape)\n        ph.origin_name = var_name\n        placeholders.append(ph)\n\n      ckpt: CheckpointReader = NewCheckpointReader(f\"{os.getcwd()}/ckpt/test-0\")\n      ph_dict = calc_feed_dict(ckpt,\n                               alias_map=alias_map,\n                               placeholders=placeholders)\n      self.assertEqual(len(ph_dict), len(alias_map))\n      for part in partition_var._get_variable_list():\n        if part.name.endswith(':0'):\n          var_name = part.name[0:-2]\n        else:\n          var_name = part.name\n\n        vph = None\n        for ph in ph_dict:\n          if ph.origin_name == var_name:\n            vph = ph\n            break\n        assert vph is not None\n        self.assertEqual(part.shape, vph.shape)\n        self.assertEqual(part.shape, ph_dict[vph].shape)\n\n  def test_alias_map_listener(self):\n    with tf.Graph().as_default():\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      partitioner = tf.compat.v1.variable_axis_size_partitioner(\n          max_shard_bytes=1 << 17, max_shards=100)\n      partition_var = tf.compat.v1.get_variable(name='partition2',\n                                                shape=(1280, 512),\n                                                dtype=tf.float32,\n                                                partitioner=partitioner,\n                                                initializer=GlorotNormal())\n\n      var = tf.compat.v1.get_variable(name='small_var2',\n                                      shape=(10, 5),\n                                      dtype=tf.float32,\n                                      initializer=Ones())\n      alias_map = {'small_var2': 'small_var', 'global_step': 'global_step'}\n      var_ph = tf.compat.v1.placeholder(dtype=tf.float32, shape=(10, 5))\n      var_ph.origin_name = 'small_var2'\n      gs_ph = tf.compat.v1.placeholder(dtype=tf.int64)\n      gs_ph.origin_name = 'global_step'\n\n      placeholders = [var_ph, gs_ph]\n      for part in partition_var._get_variable_list():\n        if part.name.endswith(':0'):\n          var_name = part.name[0:-2]\n        else:\n          var_name = part.name\n\n        alias_map[var_name] = 'partition'\n        ph = tf.compat.v1.placeholder(dtype=part.dtype, shape=part.shape)\n        ph.origin_name = var_name\n        placeholders.append(ph)\n\n      listener = CustomRestoreListener(alias_map=alias_map,\n                                       model_dir=f\"{os.getcwd()}/ckpt\")\n      listener.begin()\n\n  def test_clear_nn_listener(self):\n    with tf.Graph().as_default():\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      partitioner = tf.compat.v1.variable_axis_size_partitioner(\n          max_shard_bytes=1 << 17, max_shards=100)\n      partition_var = tf.compat.v1.get_variable(name='partition2',\n                                                shape=(1280, 512),\n                                                dtype=tf.float32,\n                                                partitioner=partitioner,\n                                                initializer=GlorotNormal())\n\n      var = tf.compat.v1.get_variable(name='small_var2',\n                                      shape=(10, 5),\n                                      dtype=tf.float32,\n                                      initializer=Ones())\n      listener = CustomRestoreListener(clear_nn=True,\n                                       model_dir=f\"{os.getcwd()}/ckpt\")\n      listener.begin()\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/device_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"\nDevice Utils.\n\nProvide device placement utils and strategies.\n\"\"\"\nimport os\nfrom typing import Callable\nimport contextlib\nfrom absl import logging, flags\nimport tensorflow as tf\nfrom tensorflow.python.training import device_setter\n\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.compiler.xla import xla\nfrom monolith.native_training.distribution_utils import get_mpi_rank, enable_sync_training\n\n\nFLAGS = flags.FLAGS\n_GPU_PLACEMENT_ALLOWED = False\n\n\ndef enable_gpu_training():\n  global _GPU_PLACEMENT_ALLOWED\n  _GPU_PLACEMENT_ALLOWED = True\n\n\ndef disable_gpu_training():\n  global _GPU_PLACEMENT_ALLOWED\n  _GPU_PLACEMENT_ALLOWED = False\n\n\ndef is_gpu_training():\n  global _GPU_PLACEMENT_ALLOWED\n  return _GPU_PLACEMENT_ALLOWED\n\n\ndef get_visible_gpus(local_rank, processes_per_gpu=1):\n  \"\"\"\n  Visible GPU devices string for session config.\n  \n  Args:\n    local_rank: the process local rank, for example `hvd.local_rank()`.\n    process_per_gpu: the integer number of processes per gpu.\n  \n  Return:\n    String compatible for session_config.gpu_options.visible_device_list,\n    for example, \"2\" indicates TensorFlow session will map the physical\n    gpu:2 into the TensorFlow virtual string-specified device \"GPU:0\".\n  \"\"\"\n  # TODO: processes_per_gpu :float < 0 allows str of gpus.\n  assert isinstance(processes_per_gpu, int) and processes_per_gpu >= 1\n  return str(int(local_rank / processes_per_gpu))\n\n\n_default_device = tf.DeviceSpec.from_string(\"/device:CPU:0\")\n\n\ndef _device_rule(device_name):\n  # Guarantee default CPU:0 at op creation. Because\n  # otherwise if any GPU is visiable and kernel is available,\n  # op would be placed on GPU when no device string specified.\n  if not device_name:\n    return _default_device.to_string()\n\n  # Enforce general placement rule.\n  d = tf.DeviceSpec.from_string(device_name)\n  if (d.device_type == \"GPU\" and\n      not _GPU_PLACEMENT_ALLOWED) or not d.device_type:\n    # If GPU is illegally assigned, or, device type is empty,\n    # Merge with the _default_device while keep the assigned job,task,replica\n    return d.make_merged_spec(_default_device).to_string()\n  # Don't override the assigned and allowed device string\n  return device_name\n\n\ndef skip_device(op: tf.Operation) -> bool:\n  # Enforce commonly-used summary op on CPU.\n  return op.type.startswith(\"Write\") or op.type.endswith(\"Summary\") or \\\n    (op.type == \"Const\" and op.get_attr(\"dtype\") == tf.string)\n\n\ndef default_device_fn(op: tf.Operation):\n  \"\"\"Default device_fn for Estimator RunConfig.\"\"\"\n  return _default_device.to_string() if skip_device(op) else _device_rule(op.device)\n\n\n@contextlib.contextmanager\ndef maybe_device_if_allowed(device_name):\n  \"\"\"\n  Monolith disallows soft device placement for training.\n  This is an insurance when default_device_fn is missed/not-enforced in Estimator Runconfig.\n  \"\"\"\n  dev = _device_rule(device_name)\n  with tf.device(dev):\n    yield\n\n\nclass _FakeNodeDef(object):\n  \"\"\"A fake NodeDef for _FakeOperation.\"\"\"\n\n  __slots__ = [\"op\", \"name\"]\n\n  def __init__(self):\n    self.op = \"\"\n    self.name = \"\"\n\n\nclass _FakeOp(object):\n  \"\"\"A helper class to determine the current device.\n\n  Supports only the type and device set/get methods needed to run the\n  graph's _apply_device_function method.\n  \"\"\"\n\n  def __init__(self):\n    self._device = \"\"\n    self.type = \"FakeOpPyObj\"\n    self.name = \"\"\n    self.node_def = _FakeNodeDef()\n\n  @property\n  def device(self):\n    return self._device\n\n  def _set_device(self, device):\n    self._device = ops._device_string(device)  # pylint: disable=protected-access\n\n  def _set_device_from_string(self, device_str):\n    self._device = device_str\n\n\ndef within_placement_context_of(device_name):\n  \"\"\"Check if the current placement context is .\"\"\"\n  fake_op = _FakeOp()\n  ops.get_default_graph()._apply_device_functions(fake_op)\n  return tf.DeviceSpec.from_string(fake_op.device).device_type == device_name.upper()\n\ndef get_device_fn(cluster=None, task=None) -> Callable:\n  is_mpi_mode = True if 'OMPI_COMM_WORLD_LOCAL_RANK' in os.environ else False\n  is_ps_mode = True if FLAGS.num_ps > 0 else False\n\n  device = 'GPU' if FLAGS.enable_gpu_training or _GPU_PLACEMENT_ALLOWED else 'CPU'\n  if is_mpi_mode and is_ps_mode and FLAGS.enable_sync_training:\n    rank = get_mpi_rank()\n    job = 'chief' if rank == 0 else 'worker'\n    task = rank if rank == 0 else rank - 1\n    device_spec = tf.DeviceSpec.from_string(f'/job:{job}/replica:0/task:{task}')\n  else:\n    device_spec = tf.DeviceSpec.from_string(f'/device:{device}:0')\n\n  def _device_fn(op: tf.Operation) -> str:\n    if skip_device(op):\n      return device_spec.make_merged_spec(_default_device).to_string()\n\n    if op.device:\n      cur_dev = tf.DeviceSpec.from_string(op.device)\n      return device_spec.make_merged_spec(cur_dev).to_string()\n    else:\n      try:\n        op.get_attr('_class')\n        return op.device\n      except:\n        return device_spec.to_string()\n\n  if FLAGS.enable_sync_training:\n    assert is_mpi_mode, 'sync training must running under mpi mode'\n    if is_ps_mode:\n      return _device_fn\n    else:\n      return default_device_fn\n  else:\n    if FLAGS.is_local or cluster is None or task is None:\n      return None\n    if task['type']:\n      worker_device = f\"/job:{task['type']}/task:{task['index']}\"\n    else:\n      worker_device = '/job:worker'\n    return tf.compat.v1.train.replica_device_setter(\n        ps_tasks=FLAGS.num_ps,\n        worker_device=worker_device,\n        merge_devices=True,\n        ps_ops=list(device_setter.STANDARD_PS_OPS),\n        cluster=cluster)\n\n\ndef input_device_fn(op: tf.Operation):\n  is_mpi_mode = True if 'OMPI_COMM_WORLD_LOCAL_RANK' in os.environ else False\n  is_ps_mode = True if FLAGS.num_ps > 0 else False\n  if is_mpi_mode and is_ps_mode and FLAGS.enable_sync_training:\n    rank = get_mpi_rank()\n    job = 'chief' if rank == 0 else 'worker'\n    task = rank if rank == 0 else rank - 1\n    return f'/job:{job}/replica:0/task:{task}/device:CPU:0'\n  return '/device:CPU:0'\n\n\ndef model_device_fn(op: tf.Operation) -> str:\n  if skip_device(op):\n    return _default_device.to_string()\n\n  device = 'GPU' if FLAGS.enable_gpu_training or _GPU_PLACEMENT_ALLOWED else 'CPU'\n  device_spec = tf.DeviceSpec.from_string(f'/device:{device}:0')\n  if op.device:\n    cur_dev = tf.DeviceSpec.from_string(op.device)\n    return device_spec.make_merged_spec(cur_dev).to_string()\n  else:\n    try:\n      op.get_attr('_class')\n      return op.device\n    except:\n      return device_spec.to_string()\n\n\ndef serving_input_device_fn(op: tf.Operation) -> str:\n  if op.device:\n    return op.device\n  return '/device:CPU:0'\n"
  },
  {
    "path": "monolith/native_training/device_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport tensorflow as tf\n\nfrom monolith.native_training import device_utils\n\n\nclass DeviceUtilsTest(tf.test.TestCase):\n\n  def test_basic(self):\n    with tf.Graph().as_default() as g, g.device(device_utils.default_device_fn):\n      a = tf.constant(1)\n      self.assertEqual(a.device, \"/device:CPU:0\")\n\n  def test_cpu_only(self):\n    device_utils.disable_gpu_training()\n    with tf.Graph().as_default() as g, g.device(device_utils.default_device_fn):\n      with tf.device(\"/device:GPU:0\"):\n        a = tf.constant(1)\n      self.assertEqual(a.device, \"/device:CPU:0\")\n\n  def test_str_context(self):\n    device_utils.enable_gpu_training()\n    with tf.Graph().as_default() as g, g.device(device_utils.default_device_fn):\n      a = tf.constant(1)\n      with tf.device(\"GPU:0\"):\n        b = tf.constant(1)\n      c = tf.constant(1)\n      self.assertEqual(a.device, \"/device:CPU:0\")\n      self.assertEqual(b.device, \"/device:GPU:0\")\n      self.assertEqual(c.device, \"/device:CPU:0\")\n\n  def test_str_nested_contexts(self):\n    device_utils.enable_gpu_training()\n    with tf.Graph().as_default() as g, g.device(device_utils.default_device_fn):\n      a = tf.constant(1)\n      with tf.device(\"CPU:0\"):\n        b = tf.constant(1)\n        with tf.device(\"GPU:0\"):\n          c = tf.constant(1)\n          with tf.device(\"GPU:1\"):\n            d = tf.constant(1)\n      self.assertEqual(a.device, \"/device:CPU:0\")\n      self.assertEqual(b.device, \"/device:CPU:0\")\n      self.assertEqual(c.device, \"/device:GPU:0\")\n      self.assertEqual(d.device, \"/device:GPU:1\")\n\n  def test_cpu_device_merge(self):\n    # For example, in async training case, we have device job and task string.\n    device_utils.disable_gpu_training()\n    with tf.Graph().as_default() as g, g.device(device_utils.default_device_fn):\n      with tf.device(\"/job:my_ps/task:0\"):\n        a = tf.constant(1)\n        with tf.device(\"GPU:0\"):\n          assert not device_utils.within_placement_context_of(\"GPU\")\n          assert device_utils.within_placement_context_of(\"CPU\")\n          b = tf.constant(1)\n    self.assertEqual(a.device, \"/job:my_ps/task:0/device:CPU:0\")\n    self.assertEqual(b.device, \"/job:my_ps/task:0/device:CPU:0\")\n\n  def test_gpu_device_merge(self):\n    device_utils.enable_gpu_training()\n    with tf.Graph().as_default() as g, g.device(device_utils.default_device_fn):\n      with tf.device(\"/job:worker/task:0\"):\n        with tf.device(\"/job:ps\"):\n          a = tf.constant(1)\n        with tf.device(\"GPU:0\"):\n          b = tf.constant(1)\n        with device_utils.maybe_device_if_allowed(\"GPU:1\"):\n          assert device_utils.within_placement_context_of(\"GPU\")\n          assert not device_utils.within_placement_context_of(\"CPU\")\n          c = tf.constant(1)\n    self.assertEqual(a.device, \"/job:ps/task:0/device:CPU:0\")\n    self.assertEqual(b.device, \"/job:worker/task:0/device:GPU:0\")\n    self.assertEqual(c.device, \"/job:worker/task:0/device:GPU:1\")\n\n  def test_process_gpu_map(self):\n    self.assertEqual(\n        device_utils.get_visible_gpus(local_rank=2, processes_per_gpu=1), \"2\")\n    self.assertEqual(\n        device_utils.get_visible_gpus(local_rank=1, processes_per_gpu=2), \"0\")\n    self.assertEqual(\n        device_utils.get_visible_gpus(local_rank=2, processes_per_gpu=2), \"1\")\n    self.assertEqual(\n        device_utils.get_visible_gpus(local_rank=3, processes_per_gpu=2), \"1\")\n\n\nif __name__ == \"__main__\":\n  logging.set_verbosity(logging.INFO)\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distribute/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_library\", \"py_test\")\n\npackage(\n    default_visibility = [\n        \"//monolith/native_training:__subpackages__\",\n        \"//monolith/native_training/data/training_instance:__subpackages__\",\n    ],\n)\n\npy_library(\n    name = \"str_queue\",\n    srcs = [\"str_queue.py\"],\n)\n\npy_test(\n    name = \"str_queue_test\",\n    srcs = [\"str_queue_test.py\"],\n    deps = [\":str_queue\"],\n)\n\npy_library(\n    name = \"distributed_dataset\",\n    srcs = [\"distributed_dataset.py\"],\n    deps = [\n        \":str_queue\",\n        \"//monolith/native_training:native_task_context\",\n        \"//monolith/native_training:utils\",\n        \"//monolith/native_training/hooks:session_hooks\",\n    ],\n)\n\npy_test(\n    name = \"distributed_dataset_test\",\n    srcs = [\"distributed_dataset_test.py\"],\n    deps = [\n        \":distributed_dataset\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/distribute/distributed_dataset.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom collections import defaultdict\nimport dataclasses\nfrom typing import List\nimport traceback\n\nfrom absl import logging\nimport tensorflow as tf\nfrom tensorflow.python.data.ops import dataset_ops\n\nfrom monolith.native_training import utils\nfrom monolith.native_training import native_task_context\nfrom monolith.native_training.distribute import str_queue\nfrom monolith.native_training.hooks import session_hooks\n\n\ndef create_dynamic_sharding_dataset(\n    glob_patterns: List[str],\n    name=\"dynamic_sharding_dataset\") -> tf.data.Dataset:\n  \"\"\"The idea here is create 2 queues to create the filename database\n  shared: glob_patterns_queue (element is like /some/path/*)\n  shared: filenames_queue (element is like /some/path/data0)\n\n  The reason why we have two shared queues is the list of filename is too long\n  and can't fit into the memory. So we need expand on demand.\n  \"\"\"\n  with tf.name_scope(name):\n    device = utils.ps_device(0) if native_task_context.get().num_ps > 0 else \"\"\n\n    # Queues on ps 0 or host if no ps.\n    with tf.device(device):\n      pattern_queue = str_queue.StrQueue(initial_elements=glob_patterns,\n                                         name=\"glob_patterns_queue\")\n\n      @tf.function\n      def glob_pattern():\n        # We are in critical section already.\n        pattern, out_of_range = pattern_queue._raw_dequeue()\n        if not out_of_range:\n          filenames = tf.io.matching_files(pattern)\n        else:\n          filenames = tf.constant([\"\"])\n        return filenames, out_of_range\n\n      filenames_queue = str_queue.StrQueue(\n          critical_section=pattern_queue.critical_section,\n          auto_enqueue_fn=glob_pattern,\n          name=\"filenames_queue\")\n\n      dequeued_filename = filenames_queue.dequeue()\n\n    def filename_generator():\n      filename_bytes, out_of_range = session_hooks.get_current_session().run(\n          dequeued_filename)\n      if out_of_range:\n        raise StopIteration()\n      return filename_bytes.decode()\n\n    dummy_dataset = tf.data.Dataset.from_tensors(0).repeat()\n\n    # Instead of map, we directly instantiate the MapDataset\n    # because we don't want to keep preserve_cardinality.\n    filename_dataset = dataset_ops.MapDataset(\n        dummy_dataset,\n        lambda _: tf.py_function(\n            func=filename_generator, inp=[], Tout=tf.string),\n        preserve_cardinality=False)\n    return filename_dataset\n"
  },
  {
    "path": "monolith/native_training/distribute/distributed_dataset_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nimport tensorflow as tf\nfrom tensorflow.data.experimental import CheckpointInputPipelineHook\n\nfrom monolith.native_training.distribute import distributed_dataset\nfrom monolith.native_training import native_task_context\nfrom monolith.native_training.hooks import session_hooks\n\n\ndef gen_test_files(files_dir):\n  \"\"\"\n  Generates following files under the folder.\n  a_0.txt\n  a_1.txt\n  ...\n  e_1.txt\n  In each file, it will be some like (this is coming from a_0.txt)\n  a.0.0\n  a.0.1\n  \"\"\"\n  for c in range(97, 102):\n    for i in range(2):\n      with tf.io.gfile.GFile(\n          os.path.join(files_dir, '{}_{}.txt'.format(chr(c), i)), 'w+') as f:\n        f.write('\\n'.join(['{}.{}.{}'.format(chr(c), i, j) for j in range(2)]))\n\n\nclass DynamicShardingDatasetTest(tf.test.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    self.test_dir = os.environ[\"TEST_TMPDIR\"]\n    self.data_dir = os.path.join(self.test_dir, 'test_data')\n    if not tf.io.gfile.exists(self.data_dir):\n      tf.io.gfile.makedirs(self.data_dir)\n      gen_test_files(self.data_dir)\n    self.glob_patterns = [\n        os.path.join(self.data_dir, basename)\n        for basename in ['a_*.txt', 'b_*.txt', 'c_*.txt', 'd_*.txt', 'e_*.txt']\n    ]\n\n  def get_test_session(self):\n    return tf.compat.v1.train.SingularMonitoredSession(\n        hooks=[session_hooks.SetCurrentSessionHook()])\n\n  def testBasic(self):\n    ds = distributed_dataset.create_dynamic_sharding_dataset(self.glob_patterns)\n    it = tf.compat.v1.data.make_one_shot_iterator(ds)\n    element = it.get_next()\n    with self.get_test_session() as sess:\n      names = []\n      for i in range(10):\n        names.append(sess.run(element))\n\n      expected = []\n      for i in range(97, 102):\n        for j in range(2):\n          expected.append(\n              os.path.join(self.data_dir, \"{}_{}.txt\".format(chr(i), j)))\n      self.assertAllEqual(names, expected)\n\n  def testEof(self):\n    ds = distributed_dataset.create_dynamic_sharding_dataset([])\n    it = tf.compat.v1.data.make_one_shot_iterator(ds)\n    v = tf.Variable(0)\n    element = it.get_next()\n    with tf.control_dependencies([element]):\n      add_op = v.assign_add(1)\n    with self.get_test_session() as sess:\n      with self.assertRaises(tf.errors.OutOfRangeError):\n        sess.run(add_op)\n      # Make sure v is not changed\n      self.assertAllEqual(sess.run(v), 0)\n\n  def testWithOtherDataset(self):\n    filename_dataset = distributed_dataset.create_dynamic_sharding_dataset(\n        self.glob_patterns)\n    dataset = filename_dataset.flat_map(tf.data.TextLineDataset)\n    it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n    element = it.get_next()\n    with self.get_test_session() as sess:\n      lines = []\n      for i in range(3):\n        lines.append(sess.run(element).decode())\n      self.assertAllEqual(lines, [\"a.0.0\", \"a.0.1\", \"a.1.0\"])\n\n  def testSaveRestore(self):\n    filename_dataset = distributed_dataset.create_dynamic_sharding_dataset(\n        self.glob_patterns)\n    dataset = filename_dataset.flat_map(tf.data.TextLineDataset)\n    it = tf.compat.v1.data.make_one_shot_iterator(dataset)\n    element = it.get_next()\n    saveable_obj = tf.data.experimental.make_saveable_from_iterator(\n        it, external_state_policy=\"ignore\")\n    saver = tf.compat.v1.train.Saver(var_list=[saveable_obj] +\n                                     tf.compat.v1.global_variables())\n    with self.get_test_session() as sess:\n      real_sess = session_hooks.get_current_session()\n      self.assertAllEqual(sess.run(element).decode(), \"a.0.0\")\n      save_path = saver.save(\n          real_sess, os.path.join(os.environ[\"TEST_TMPDIR\"], \"save_restore\"))\n      self.assertAllEqual(sess.run(element).decode(), \"a.0.1\")\n      saver.restore(real_sess, save_path)\n      self.assertAllEqual(sess.run(element).decode(), \"a.0.1\")\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distribute/str_queue.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom typing import Callable\n\nimport tensorflow as tf\n\n\nclass StrQueue:\n  \"\"\"A queue whose element is a string, and supports save/restore.\n  When queue is running out, it will throw OutOfRange error.\n  \"\"\"\n\n  def __init__(self,\n               initial_elements=None,\n               critical_section=None,\n               auto_enqueue_fn=None,\n               capacity=100000,\n               name=\"StrQueue\"):\n    \"\"\"Args:\n      critical_section - if not None, queue will use this as a critical section instead of creating new one.\n      auto_enqueue_fn - when queue is empty, we will use this enqueue op to fill the queue. Should be a callable\n      returns 2 tensors: 1-D string tensor represents strings to be enqueued and 0-D bool tensor to indicate if\n      it is out of range.\n    \"\"\"\n    with tf.name_scope(name) as scope:\n      self._name = name\n      self._auto_enqueue_fn = auto_enqueue_fn\n      self._capacity = capacity\n\n      self._cs = critical_section or tf.CriticalSection(\n          name=\"CriticalSection\", shared_name=scope + \"/CriticalSection\")\n      self._arr = tf.Variable(initial_value=tf.constant_initializer(\"\")(\n          shape=[self._capacity],\n          dtype=tf.string,\n      ),\n                              trainable=False,\n                              name=\"Queue\")\n      self._offset = tf.Variable(0, trainable=False, name=\"Offset\")\n      self._arr_size = tf.Variable(0, trainable=False, name=\"Size\")\n\n      if initial_elements is None:\n        initial_elements = []\n\n      # Here we use a dummy var to init queue\n      with tf.control_dependencies([\n          self._arr.initializer,\n          self._offset.initializer,\n          self._arr_size.initializer,\n      ]):\n        with tf.control_dependencies([self.enqueue_many(initial_elements)]):\n          var_for_init_value = tf.constant(0)\n\n      self._var_for_init = tf.Variable(initial_value=var_for_init_value,\n                                       trainable=False,\n                                       name=\"VarForInit\")\n\n  @property\n  def critical_section(self):\n    return self._cs\n\n  def enqueue_many(self, elements: tf.Tensor, name=None):\n    elements = tf.convert_to_tensor(elements, tf.string)\n    return self._cs.execute(lambda: self._raw_enqueue_many(elements), name=name)\n\n  def dequeue(self, name=None):\n    \"\"\"Dequeues an element. Returns 2 elements: element & a bool indicating if we're out of range.\"\"\"\n    return self._cs.execute(self._raw_dequeue, name=name)\n\n  @tf.function\n  def _raw_enqueue_many(self, elements: tf.Tensor):\n    size = tf.size(elements)\n    old_arr_size = self._arr_size - self._offset\n    new_arr_size = old_arr_size + size\n    tf.debugging.Assert(new_arr_size <= self._capacity, [\n        self._name, \" excceeds capacity \", new_arr_size, \" v.s. \",\n        self._capacity\n    ])\n    self._arr[0:old_arr_size].assign(self._arr[self._offset:self._arr_size])\n    self._arr[old_arr_size:old_arr_size + size].assign(elements)\n    self._offset.assign(0)\n    self._arr_size.assign(new_arr_size)\n\n  @tf.function\n  def _raw_dequeue(self):\n    tf.debugging.Assert(self._offset <= self._arr_size, [\n        \"Offset should always be less than or equal to arr_size.\",\n        \"This may indicate an internal error. offset: \", self._offset,\n        \" arr_size: \", self._arr_size\n    ])\n    if self._auto_enqueue_fn is not None:\n      while tf.math.equal(self._offset, self._arr_size):\n        elements, out_of_range = self._auto_enqueue_fn()\n        elements = tf.convert_to_tensor(elements)\n        if out_of_range:\n          break\n        self._raw_enqueue_many(elements)\n    if tf.math.equal(self._offset, self._arr_size):\n      return \"\", True\n    else:\n      single_element = self._arr[self._offset]\n      self._offset.assign_add(1)\n      return single_element, False\n"
  },
  {
    "path": "monolith/native_training/distribute/str_queue_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom monolith.native_training.distribute import str_queue\n\nimport tensorflow as tf\n\n\nclass QueueTest(tf.test.TestCase):\n\n  def testBasic(self):\n    q = str_queue.StrQueue()\n    enqueue_op = q.enqueue_many([\"test1\", \"test2\"])\n    dequeue = q.dequeue()\n    self.evaluate(tf.compat.v1.global_variables_initializer())\n    self.evaluate(enqueue_op)\n    self.assertEqual(self.evaluate(dequeue)[0].decode(), \"test1\")\n    self.assertEqual(self.evaluate(dequeue)[0].decode(), \"test2\")\n\n  def testInit(self):\n    q = str_queue.StrQueue(initial_elements=[\"test1\"])\n    self.evaluate(tf.compat.v1.global_variables_initializer())\n    self.assertEqual(self.evaluate(q.dequeue())[0].decode(), \"test1\")\n\n  def testOutOfRange(self):\n    q = str_queue.StrQueue()\n    dequeue = q.dequeue()\n    self.evaluate(tf.compat.v1.global_variables_initializer())\n    _, out_of_range = self.evaluate(dequeue)\n    self.assertEqual(out_of_range, True)\n\n  def testAutoEnqueue(self):\n    v = tf.Variable([0])\n    self.evaluate(tf.compat.v1.global_variables_initializer())\n    ds = tf.data.Dataset.from_tensor_slices([])\n    it = tf.compat.v1.data.make_one_shot_iterator(ds)\n\n    @tf.function\n    def auto_enqueue():\n      new_v = v.assign_add([1])\n      if new_v > 2:\n        return tf.constant([\"\"]), True\n      return tf.as_string(v), False\n\n    q = str_queue.StrQueue(auto_enqueue_fn=auto_enqueue)\n    self.evaluate(tf.compat.v1.global_variables_initializer())\n    self.assertEqual(self.evaluate(q.dequeue())[0].decode(), \"1\")\n    self.assertEqual(self.evaluate(q.dequeue())[0].decode(), \"2\")\n    self.assertEqual(self.evaluate(q.dequeue())[1], True)\n    # Simulating in the distributed training, multiple dequeues will be called.\n    self.assertEqual(self.evaluate(q.dequeue())[1], True)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distributed_ps.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom __future__ import annotations\n\nimport re\nimport hashlib\nimport collections\nimport contextlib\nimport itertools\nfrom contextlib import nullcontext\nimport copy\nimport os\nimport sys\nfrom collections import defaultdict, namedtuple\nfrom typing import Callable, DefaultDict, Dict, Iterable, List, Tuple, Optional, NewType\n\nfrom absl import flags\nfrom absl import logging\nimport tensorflow as tf\nfrom tensorflow.python.framework.ops import Tensor\nfrom tensorflow.python.types.core import Value\n\nfrom monolith.native_training import distribution_ops\nfrom monolith.native_training import distributed_serving_ops\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import logging_ops\nfrom monolith.native_training import multi_type_hash_table\nfrom monolith.native_training import multi_hash_table_ops\nfrom monolith.native_training import native_task_context\nfrom monolith.native_training import tensor_utils\nfrom monolith.native_training import utils\nfrom monolith.native_training import entry\nfrom monolith.native_training.hash_table_utils import infer_dim_size\nfrom monolith.native_training.model_export import export_context\nfrom monolith.native_training.data.parsers import sharding_sparse_fids\nfrom idl.matrix.proto.example_pb2 import FeatureConfigs, FeatureConfig, PoolingType, OutType, OutConfig\nimport monolith.native_training.embedding_combiners as embedding_combiners\nfrom monolith.native_training.data.parsers import get_default_parser_ctx, ParserCtx, ShardingSparseFidsOpParams\nfrom monolith.native_training.prefetch_queue import \\\n    enqueue_dicts_with_queue_return, AsyncPushHook, EnqueueHook\nfrom monolith.native_training import prefetch_queue\n\nFLAGS = flags.FLAGS\n\nenable_hvd = os.getenv(\"MONOLITH_WITH_HOROVOD\")\nif enable_hvd != None:\n  import horovod.tensorflow as hvd\n  from horovod.tensorflow.compression import FP16Compressor\n\n# For mock test\nremote_predict = distributed_serving_ops.remote_predict\n\n\n@contextlib.contextmanager\ndef ps_device(i: int):\n  \"\"\"We need to clean the device stack first to make tf.function work properly.\"\"\"\n  with tf.compat.v1.get_default_graph().colocate_with(None, True), tf.device(\n      utils.ps_device(i)):\n    yield\n\n\nclass DistributedHashTable(hash_table_ops.BaseHashTable):\n  \"\"\"The distribution version of hash table. \"\"\"\n\n  def __init__(\n      self, ps_num, config: entry.HashTableConfigInstance,\n      hash_table_factory: Callable[[int, entry.HashTableConfigInstance],\n                                   hash_table_ops.BaseHashTable]):\n    self._ps_num = ps_num\n    self._hash_tables = []\n    # Build learning rate tensor on worker side\n    learning_rate_tensor = config.call_learning_rate_fns()\n    for i in range(self._ps_num):\n      with nullcontext() if export_context.is_exporting_standalone(\n      ) else ps_device(i):\n        # Send learning rate tensor to ps\n        learning_rate_tensor_on_ps = tf.identity(learning_rate_tensor)\n        config.set_learning_rate_tensor(learning_rate_tensor_on_ps)\n        self._hash_tables.append(hash_table_factory(i, config))\n\n    self._input_lookup_tensors = {}\n    self._output_lookup_tensors = set()\n\n  @property\n  def dim_size(self):\n    return self._hash_tables[0].dim_size\n\n  # Once `lookup` is edited, remember to edit `apply_gradients` too.\n  def lookup(self, ids: tf.Tensor, use_multi_threads=False) -> tf.Tensor:\n    unique_ids = ids\n    unique_ids, idx = tf.unique(ids)\n    indices = tf.math.floormod(unique_ids, self._ps_num)\n    split_ids = distribution_ops.split_by_indices(indices, unique_ids,\n                                                  self._ps_num)\n    split_embeddings = []\n    for i in range(self._ps_num):\n      with nullcontext() if export_context.is_exporting_standalone(\n      ) else ps_device(i), tf.name_scope(\"ps_{}\".format(i)):\n        hash_table = self._hash_tables[i]\n        ids_part = split_ids[i]\n        embeddings_part = hash_table.lookup(ids_part)\n        self._input_lookup_tensors.update({embeddings_part: i})\n        split_embeddings.append(embeddings_part)\n    lookup_tensor = distribution_ops.map_id_to_embedding(\n        split_ids, split_embeddings, ids)\n    self._output_lookup_tensors.add(lookup_tensor)\n    return lookup_tensor\n\n  def _update(self, method_name: str, ids: tf.Tensor, values: tf.Tensor,\n              req_time: tf.Tensor) -> \"DistributedHashTable\":\n    indices = tf.math.floormod(ids, self._ps_num)\n    split_ids = distribution_ops.split_by_indices(indices, ids, self._ps_num)\n    split_values = distribution_ops.split_by_indices(indices, values,\n                                                     self._ps_num)\n    updated_tables = []\n    for i in range(self._ps_num):\n      with ps_device(i):\n        ids_part = split_ids[i]\n        values_part = split_values[i]\n        updated_tables.append(\n            getattr(self._hash_tables[i], method_name)(ids_part, values_part,\n                                                       req_time))\n    return self._copy_with_new_tables(updated_tables)\n\n  def assign(self,\n             ids: tf.Tensor,\n             values: tf.Tensor,\n             req_time: tf.Tensor = None) -> \"DistributedHashTable\":\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    return self._update(\"assign\", ids, values, req_time)\n\n  def assign_add(self,\n                 ids: tf.Tensor,\n                 values: tf.Tensor,\n                 req_time: tf.Tensor = None) -> \"DistributedHashTable\":\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    return self._update(\"assign_add\", ids, values, req_time)\n\n  def apply_gradients(self,\n                      ids: tf.Tensor,\n                      grads: tf.Tensor,\n                      global_step: tf.Tensor,\n                      req_time: tf.Tensor = None) -> \"DistributedHashTable\":\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    unique_ids, idx = tf.unique(ids)\n    indices = tf.math.floormod(unique_ids, self._ps_num)\n    split_ids = distribution_ops.split_by_indices(indices, unique_ids,\n                                                  self._ps_num)\n    split_grads = distribution_ops.map_id_to_embedding_gradient_back_prop(\n        split_ids, ids, grads)\n    updated_tables = []\n    for i in range(self._ps_num):\n      with ps_device(i), tf.name_scope(\"ps_{}\".format(i)):\n        # TODO(leqi.zou): Think of the meaning of dedup here\n        updated_tables.append(self._hash_tables[i].apply_gradients(\n            split_ids[i],\n            split_grads[i],\n            global_step=global_step,\n            enable_dedup=False,\n            req_time=req_time))\n    return self._copy_with_new_tables(updated_tables)\n\n  def as_op(self, name=None) -> tf.Operation:\n    name = name or \"dht_ao\"\n    with tf.control_dependencies([table.as_op() for table in self._hash_tables\n                                 ]):\n      c = tf.no_op(name=(\"{}/done\".format(name)))\n    return c\n\n  def _copy_with_new_tables(\n      self, new_tables: List[tf.Tensor]) -> \"DistributedHashTable\":\n    copied = copy.copy(self)\n    copied.__dict__[\"_hash_tables\"] = new_tables\n    return copied\n\n\nclass DistributedMultiTypeHashTable(multi_type_hash_table.BaseMultiTypeHashTable\n                                   ):\n\n  def __init__(\n      self,\n      num_ps: int,\n      slot_to_config: Dict[str, entry.HashTableConfigInstance],\n      table_factory: Callable[[int, Dict[str, entry.HashTableConfigInstance]],\n                              multi_type_hash_table.BaseMultiTypeHashTable],\n      transfer_float16: bool = False,\n      max_rpc_deadline_millis: int = 30):\n    self._num_ps = num_ps\n    self._slot_to_config = slot_to_config\n    self._tables = []\n    self._table_support_raw_api = True\n    self.transfer_float16 = transfer_float16\n    self._max_rpc_deadline_millis = max_rpc_deadline_millis\n    # Build learning rate tensor on worker side\n    slot_to_learning_rate_tensor = dict()\n    for slot, config in slot_to_config.items():\n      slot_to_learning_rate_tensor[slot] = config.call_learning_rate_fns()\n    packed_slot_to_learning_rate_tensor = tensor_utils.pack_tensors(\n        slot_to_learning_rate_tensor)\n\n    def support_raw_api(table):\n      return isinstance(table, multi_hash_table_ops.RawMultiTypeHashTable)\n\n    for i in range(self._num_ps):\n      if export_context.is_exporting_distributed():\n        ps_graph = export_context.get_current_export_ctx().sub_graph(f\"ps_{i}\")\n        with ps_graph.as_default():\n          table = table_factory(i, slot_to_config)\n          self._tables.append(table)\n          # Build lookup graph on the PS side\n          remote_lookup_input = {\n              k: tf.compat.v1.placeholder(dtype=tf.int64, shape=(None,))\n              for k in slot_to_config\n          }\n          remote_lookup_output = table.lookup(remote_lookup_input)\n          export_context.get_current_export_ctx().add_signature(\n              ps_graph, 'lookup', remote_lookup_input, remote_lookup_output)\n          if support_raw_api(table):\n            raw_remote_lookup_input = {\n                \"id\":\n                    tf.compat.v1.placeholder(dtype=tf.int64, shape=(None,)),\n                \"id_split\":\n                    tf.compat.v1.placeholder(dtype=tf.int64, shape=(None,)),\n            }\n            raw_remote_lookup_output = {\n                \"flat_emb\":\n                    table.raw_lookup(\n                        tf.RaggedTensor.from_row_splits(\n                            raw_remote_lookup_input[\"id\"],\n                            raw_remote_lookup_input[\"id_split\"],\n                            validate=False))\n            }\n            export_context.get_current_export_ctx().add_signature(\n                ps_graph, 'raw_lookup', raw_remote_lookup_input,\n                raw_remote_lookup_output)\n      elif export_context.is_exporting_standalone():\n        self._tables.append(table_factory(i, slot_to_config))\n      else:\n        with ps_device(i):\n          # Send learning rate tensor to ps\n          # TODO(leqi.zou): Here we can do some optimization to optimize raw hash table.\n          slot_to_learning_rate_tensor_on_ps = tensor_utils.unpack_tensors(\n              tensor_utils.get_keyed_shape(slot_to_learning_rate_tensor),\n              packed_slot_to_learning_rate_tensor)\n          for slot, config in slot_to_config.items():\n            config.set_learning_rate_tensor(\n                slot_to_learning_rate_tensor_on_ps[slot])\n          self._tables.append(table_factory(i, slot_to_config))\n      self._table_support_raw_api &= support_raw_api(self._tables[-1])\n\n  def lookup(self, slot_to_id: Dict[str, tf.Tensor]) -> Dict[str, tf.Tensor]:\n    with tf.name_scope(\"dmtht_lu\"):\n\n      def emit_lookup_timer_ops(interval):\n        if not export_context.is_exporting():\n          return [\n              logging_ops.emit_timer(\n                  \"embedding_lookup\",\n                  tf.cast(interval, tf.float32),\n                  tags={\n                      \"model_name\": native_task_context.get().model_name,\n                      \"ps\": str(i)\n                  })\n          ]\n        return []\n\n      if self._table_support_raw_api and not self.transfer_float16:\n        table_0 = self._tables[0]\n        dims = table_0.get_table_dim_sizes()\n        ragged_id = table_0.get_ragged_id(slot_to_id)\n        result = distribution_ops.unique_key_with_value_and_offset(\n            ragged_id, dims)\n\n        index = tf.math.floormod(result.unique_key.values, self._num_ps)\n        splitted_ids, splitted_pos = distribution_ops.ragged_split_by_indices(\n            index, result.unique_key, self._num_ps)\n        filled_buffers = []\n        interval_ops = []\n        for i in range(self._num_ps):\n          table: multi_hash_table_ops.RawMultiTypeHashTable = self._tables[i]\n          splitted_id = splitted_ids[i]\n          (splitted_id_values,), send_ts = logging_ops.tensors_timestamp(\n              [splitted_id.values])\n          splitted_id = tf.RaggedTensor.from_row_splits(splitted_id_values,\n                                                        splitted_id.row_splits,\n                                                        validate=False)\n          if export_context.is_exporting_distributed():\n            flat_emb, = remote_predict(\n                [\"id\", \"id_split\"],\n                [splitted_id.values, splitted_id.row_splits], [\"flat_emb\"],\n                task=i,\n                old_model_name=\"ps_{}\".format(i),\n                model_name=\n                f\"{native_task_context.get().model_name or ''}:ps_{i}\",\n                model_version=-1,\n                max_rpc_deadline_millis=self._max_rpc_deadline_millis,\n                output_types=[tf.float32],\n                signature_name=\"raw_lookup\")\n          else:\n            with nullcontext() if export_context.is_exporting_standalone(\n            ) else ps_device(i):\n              flat_emb = table.raw_lookup(splitted_id)\n          (flat_emb,), end_ts = logging_ops.tensors_timestamp([flat_emb])\n          interval_ops.extend(emit_lookup_timer_ops(end_ts - send_ts))\n          filled_buffers.append(\n              distribution_ops.fill_with_offset_map(splitted_pos[i], flat_emb,\n                                                    result.value_offset,\n                                                    result.value_buffer, dims))\n\n        with tf.control_dependencies(interval_ops):\n          flat_emb = distribution_ops.finalize_shared_tensor(filled_buffers,\n                                                             dtype=tf.float32,\n                                                             shape=[None])\n        emb = table_0.get_embeddings(ragged_id, flat_emb)\n        polished_emb = {}\n        # Remove unpresented keys and make emb shape known if input shape is known.\n        for k, v in emb.items():\n          if k in slot_to_id:\n            id = slot_to_id[k]\n            if id.shape[0]:\n              v = tf.reshape(v, shape=[id.shape[0], v.shape[1]])\n            polished_emb[k] = v\n        return polished_emb\n      else:\n        sharded_slot_to_id: Dict[int, Dict[\n            str, tf.Tensor]] = collections.defaultdict(dict)\n        slot_to_split_ids = {}\n        for slot in slot_to_id:\n          id = slot_to_id[slot]\n          unique_id, idx = tf.unique(id)\n          index = tf.math.floormod(unique_id, self._num_ps)\n          split_ids = distribution_ops.split_by_indices(index, unique_id,\n                                                        self._num_ps)\n          slot_to_split_ids[slot] = split_ids\n          for i in range(self._num_ps):\n            sharded_slot_to_id[i][slot] = split_ids[i]\n\n        sharded_slot_to_embedding: Dict[int, Dict[str, tf.Tensor]] = {}\n\n        if export_context.is_exporting_distributed():\n          slot_names = sorted(slot_to_split_ids.keys())\n          slot_to_dim = [\n              infer_dim_size(self._slot_to_config[slot].table_config)\n              for slot in slot_names\n          ]\n          for i in range(self._num_ps):\n            per_ps_slot_to_id = sharded_slot_to_id[i]\n            # Remote call from Entry to PS\n            # TODO(leqi.zou): Consider a better way to get model name.\n            results = remote_predict(\n                slot_names, [per_ps_slot_to_id[slot] for slot in slot_names],\n                slot_names,\n                task=i,\n                old_model_name=\"ps_{}\".format(i),\n                model_name=\n                f\"{native_task_context.get().model_name or ''}:ps_{i}\",\n                model_version=-1,\n                max_rpc_deadline_millis=self._max_rpc_deadline_millis,\n                output_types=[tf.float32] * len(slot_names),\n                signature_name=\"lookup\")\n            sharded_slot_to_embedding[i] = {\n                slot_names[j]: tf.reshape(results[j], [-1, slot_to_dim[j]])\n                for j in range(len(slot_names))\n            }\n        else:\n          for i in range(self._num_ps):\n            per_ps_slot_to_id = sharded_slot_to_id[i]\n            packed_id = tensor_utils.pack_tensors(per_ps_slot_to_id)\n            packed_id, send_ts = logging_ops.tensors_timestamp(packed_id)\n            with nullcontext() if export_context.is_exporting_standalone(\n            ) else ps_device(i):\n              slot_to_id_on_ps = tensor_utils.unpack_tensors(\n                  tensor_utils.get_keyed_shape(per_ps_slot_to_id), packed_id)\n              slot_to_embedding_on_ps = self._tables[i].lookup(slot_to_id_on_ps)\n              packed_embedding = tensor_utils.pack_tensors(\n                  slot_to_embedding_on_ps)\n              if self.transfer_float16:\n                packed_embedding = (tf.cast(\n                    packed_embedding[0],\n                    dtype=tf.float16,\n                    name='{}_send_{}_CastToFloat16'.format(\n                        packed_embedding[0].op.name, i)), packed_embedding[1])\n\n            packed_embedding, recv_ts = logging_ops.tensors_timestamp(\n                packed_embedding)\n            interval = recv_ts - send_ts\n            with tf.control_dependencies(emit_lookup_timer_ops(interval)):\n              packed_embedding = tf.identity_n(packed_embedding)\n\n            if self.transfer_float16:\n              packed_embedding = (tf.cast(\n                  packed_embedding[0],\n                  dtype=tf.float32,\n                  name='{}_recv_{}_CastToFloat32'.format(\n                      packed_embedding[0].op.name, i)), packed_embedding[1])\n            slot_to_embedding = tensor_utils.unpack_tensors(\n                tensor_utils.get_keyed_shape(slot_to_embedding_on_ps),\n                packed_embedding)\n            sharded_slot_to_embedding[i] = slot_to_embedding\n\n        slot_to_split_embeddings = {}\n        for slot in slot_to_id:\n          slot_to_split_embeddings[slot] = [\n              sharded_slot_to_embedding[i][slot] for i in range(self._num_ps)\n          ]\n\n        slot_to_embedding = {}\n        for slot in slot_to_id:\n          slot_to_embedding[slot] = distribution_ops.map_id_to_embedding(\n              slot_to_split_ids[slot], slot_to_split_embeddings[slot],\n              slot_to_id[slot])\n\n      return slot_to_embedding\n\n  def _update(\n      self, method_name: str, name_scope: str,\n      slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> DistributedMultiTypeHashTable:\n    with tf.name_scope(name_scope):\n      sharded_slot_to_id_and_value: Dict[int, Dict[str, Tuple[\n          tf.Tensor, tf.Tensor]]] = collections.defaultdict(dict)\n      for slot, (id, value) in slot_to_id_and_value.items():\n        index = tf.math.floormod(id, self._num_ps)\n        split_ids = distribution_ops.split_by_indices(index, id, self._num_ps)\n        split_values = distribution_ops.split_by_indices(\n            index, value, self._num_ps)\n        for i in range(self._num_ps):\n          sharded_slot_to_id_and_value[i][slot] = (split_ids[i],\n                                                   split_values[i])\n      new_tables = []\n      for i in range(self._num_ps):\n        new_tables.append(\n            getattr(self._tables[i],\n                    method_name)(sharded_slot_to_id_and_value[i]))\n\n      return self._copy_with_new_table(new_tables)\n\n  def assign(\n      self, slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> DistributedMultiTypeHashTable:\n    return self._update(\"assign\", \"dmtht_a\", slot_to_id_and_value)\n\n  def assign_add(\n      self, slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> DistributedMultiTypeHashTable:\n    return self._update(\"assign_add\", \"dmtht_aa\", slot_to_id_and_value)\n\n  def reinitialize(\n      self, slot: str,\n      ids: tf.Tensor) -> Tuple[DistributedMultiTypeHashTable, tf.Tensor]:\n    if self._table_support_raw_api:\n      with tf.name_scope(\"dmtht_reinit\"):\n        index = tf.math.floormod(ids, self._num_ps)\n        split_ids = distribution_ops.split_by_indices(index, ids, self._num_ps)\n        new_tables, status = [], []\n        for i in range(self._num_ps):\n          new_table, split_status = self._tables[i].reinitialize(\n              slot, split_ids[i])\n          new_tables.append(new_table)\n          status.append(split_status)\n        return self._copy_with_new_table(new_tables), tf.concat(status, axis=0)\n    else:\n      raise NotImplementedError(\n          \"DistributedMultiTypeHashTable dost not support reinitialize!\")\n\n  def apply_gradients(\n      self,\n      slot_to_id_and_grad: Dict[str, Tuple[tf.Tensor, tf.Tensor]],\n      global_step: tf.Tensor,\n      req_time: Optional[tf.Tensor] = None) -> DistributedMultiTypeHashTable:\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    with tf.name_scope(\"dmtht_ag\"):\n      if self._table_support_raw_api and not self.transfer_float16:\n        slot_to_id = {k: v[0] for k, v in slot_to_id_and_grad.items()}\n        slot_to_grad = {k: v[1] for k, v in slot_to_id_and_grad.items()}\n        table_0 = self._tables[0]\n        dims = table_0.get_table_dim_sizes()\n        ragged_id = table_0.get_ragged_id(slot_to_id)\n        result = distribution_ops.unique_key_with_value_and_offset(\n            ragged_id, dims, generate_buffer=False)\n        index = tf.math.floormod(result.unique_key.values, self._num_ps)\n        splitted_ids, splitted_pos = distribution_ops.ragged_split_by_indices(\n            index, result.unique_key, self._num_ps)\n        flat_grad = table_0.get_flat_value(slot_to_grad)\n        new_tables = []\n        for i in range(self._num_ps):\n          splitted_id = splitted_ids[i]\n          splitted_flat_grad = distribution_ops.fill_with_offset_map_gradient(\n              splitted_pos[i], flat_grad, result.value_offset, dims)\n          table: multi_hash_table_ops.RawMultiTypeHashTable = self._tables[i]\n\n          with ps_device(i):\n            new_tables.append(\n                table.raw_apply_gradients(splitted_id,\n                                          splitted_flat_grad,\n                                          global_step=global_step,\n                                          req_time=req_time))\n        return self._copy_with_new_table(new_tables)\n      else:\n        sharded_slot_to_id_and_grad: Dict[int, Dict[str, Tuple[\n            tf.Tensor, tf.Tensor]]] = collections.defaultdict(dict)\n        for slot, (id, grad) in slot_to_id_and_grad.items():\n          unique_id, _ = tf.unique(id)\n          index = tf.math.floormod(unique_id, self._num_ps)\n          split_ids = distribution_ops.split_by_indices(index, unique_id,\n                                                        self._num_ps)\n          split_grads = distribution_ops.map_id_to_embedding_gradient_back_prop(\n              split_ids, id, grad)\n          for i in range(self._num_ps):\n            sharded_slot_to_id_and_grad[i][slot] = (split_ids[i],\n                                                    split_grads[i])\n\n        new_tables = []\n        for i in range(self._num_ps):\n          keyed_id = {\n              k: v[0] for k, v in sharded_slot_to_id_and_grad[i].items()\n          }\n          keyed_grad = {\n              k: v[1] for k, v in sharded_slot_to_id_and_grad[i].items()\n          }\n          packed_list = tensor_utils.pack_typed_keyed_tensors(\n              [keyed_id, keyed_grad])\n          if self.transfer_float16:\n            packed_list[1] = tf.cast(packed_list[1],\n                                     dtype=tf.float16,\n                                     name='{}_send_{}_CastToFloat16'.format(\n                                         packed_list[1].op.name, i))\n          with ps_device(i):\n            if self.transfer_float16:\n              packed_list[1] = tf.cast(packed_list[1],\n                                       dtype=tf.float32,\n                                       name='{}_recv_{}_CastToFloat32'.format(\n                                           packed_list[1].op.name, i))\n            keyed_list_on_ps = tensor_utils.unpack_packed_tensors(\n                tensor_utils.get_typed_keyed_shape([keyed_id, keyed_grad]),\n                packed_list)\n            keyed_id_on_ps = keyed_list_on_ps[0]\n            keyed_grad_on_ps = keyed_list_on_ps[1]\n            slot_to_id_and_grad_on_ps = {\n                slot: (keyed_id_on_ps[slot], keyed_grad_on_ps[slot])\n                for slot in keyed_id_on_ps\n            }\n            new_tables.append(self._tables[i].apply_gradients(\n                slot_to_id_and_grad_on_ps, global_step, req_time=req_time))\n        return self._copy_with_new_table(new_tables)\n\n  def as_op(self, name=None):\n    name = name or \"dmtht_ao\"\n    ops = []\n    for i in range(self._num_ps):\n      with ps_device(i):\n        ops.append(self._tables[i].as_op(name=\"{}/sub_{}\".format(name, i)))\n    with tf.control_dependencies(ops):\n      return tf.no_op(name=(\"{}/done\".format(name)))\n\n  def get_table_dim_sizes(self):\n    return self._cc.dims\n\n  def _copy_with_new_table(\n      self, new_tables: List[tf.Tensor]) -> DistributedMultiTypeHashTable:\n    copied = copy.copy(self)\n    copied._tables = new_tables\n    return copied\n\n\ndef get_sub_table_name(strs: List[str]):\n  concat = \",\".join(strs)\n  return concat, hashlib.md5(concat.encode()).hexdigest()\n\n\nPartition = NewType(\"Partition\", int)\nTableName = NewType(\"TableName\", str)\nFids = NewType(\"Fids\", tf.Tensor)\nEmb = NewType(\"Emb\", tf.Tensor)\nEmbGrad = NewType(\"EmbGrad\", tf.Tensor)\n\nFidEmbPair = NewType(\"FidEmbPair\", Tuple[Fids, Emb])\nFidEmbGradPair = NewType(\"FidEmbGradPair\", Tuple[Fids, EmbGrad])\nLookupData = NewType(\"LookupData\", Dict[Partition, Dict[TableName, Fids]])\nUpdateData = NewType(\"UpdateData\", Dict[Partition, Dict[TableName,\n                                                        FidEmbGradPair]])\nAssignData = NewType(\"UpdateData\", Dict[Partition, Dict[TableName, FidEmbPair]])\nTableFactory = NewType(\n    \"TableFactory\",\n    Callable[[Partition, Dict[TableName, entry.HashTableConfigInstance]],\n             multi_type_hash_table.BaseMultiTypeHashTable])\nFeatureInfo = namedtuple('FeatureInfo', 'slice_dims combiner sub_table')\n\nfrom monolith.native_training.distributed_ps_sync import enable_custom_optimized_hvd, enable_hvd_fid_g2g, \\\n  enable_hvd_fwd_g2g, enable_hvd_bwd_g2g, enable_bps, enable_bps_fid, enable_bps_fwd, enable_bps_bwd, \\\n  enable_bps_bwd_cast, enable_bps_bwd_fake_cast, enable_bps_fwd_gdr, enable_bps_fwd_gdr_g2g, \\\n  enable_bps_bwd_gdr, enable_bps_bwd_gdr_g2g\n\n\nclass PartitionedHashTable(object):\n  # Allow pipelined graph execution.\n  _local_queue_hooks: List[prefetch_queue.EnqueueHook |\n                           prefetch_queue.AsyncPushHook]\n  _native_multi_hash_table_fake_table = \"native_multi_hash_table_fake_table\"\n\n  @classmethod\n  def gen_feature_configs(\n      cls,\n      num_ps: int,\n      feature_name_to_config: Dict[str, entry.HashTableConfigInstance],\n      layout_configs: Dict[str, OutConfig],\n      feature_to_unmerged_slice_dims: Dict[str, List[int]],\n      feature_to_combiner: Dict[str, embedding_combiners.Combiner],\n      use_native_multi_hash_table: bool,\n      transfer_float16: bool = False,\n      unique: Callable = None,\n      enable_gpu_emb: bool = False,\n      use_gpu: bool = False,\n  ):\n    _num_ps: int = 1 if num_ps == 0 else num_ps\n    _use_native_multi_hash_table = use_native_multi_hash_table and not transfer_float16\n    # feature/slot -> sub_hashtable_name\n    if _use_native_multi_hash_table:\n      _sub_table_name_to_config, feature_to_sub_table = cls.no_merge_feature_config(\n          feature_name_to_config, use_same_table=not enable_gpu_emb)\n    else:\n      _sub_table_name_to_config, feature_to_sub_table = cls.merge_feature_config(\n          feature_name_to_config)\n\n    feature_info: Dict[str, FeatureInfo] = {}\n    for feature_name, sub_table in feature_to_sub_table.items():\n      feature_info[feature_name] = FeatureInfo(\n          feature_to_unmerged_slice_dims[feature_name],\n          feature_to_combiner[feature_name], sub_table)\n\n    feature_configs = FeatureConfigs()\n\n    # fill feature config for feature_configs\n    for feature_name, info in feature_info.items():\n      combiner = info.combiner\n      if isinstance(combiner, embedding_combiners.ReduceSum):\n        pooling_type = PoolingType.SUM\n      elif isinstance(combiner, embedding_combiners.ReduceMean):\n        pooling_type = PoolingType.MEAN\n      elif isinstance(combiner, embedding_combiners.FirstN):\n        pooling_type = PoolingType.FIRSTN\n      else:\n        raise Exception(\"pooling_type error!\")\n\n      max_sequence_length = combiner.max_seq_length\n      fc = FeatureConfig(table=info.sub_table,\n                         pooling_type=pooling_type,\n                         max_sequence_length=max_sequence_length)\n      fc.slice_dims.extend(info.slice_dims)\n      feature_configs.feature_configs[feature_name].CopyFrom(fc)\n\n    for out_name, oc in layout_configs.items():\n      feature_configs.out_configs[out_name].CopyFrom(oc)\n\n    return ShardingSparseFidsOpParams(_num_ps, _use_native_multi_hash_table,\n                                      unique, transfer_float16,\n                                      _sub_table_name_to_config,\n                                      feature_configs, enable_gpu_emb, use_gpu)\n\n  @classmethod\n  def no_merge_feature_config(\n      cls, feature_name_to_config: Dict[str, entry.HashTableConfigInstance],\n      use_same_table: bool):\n    sub_table_name_to_config, feature_to_sub_table = {}, {}\n    for feature_name in sorted(feature_name_to_config):\n      feature_to_sub_table[\n          feature_name] = cls._native_multi_hash_table_fake_table if use_same_table else feature_name\n    return feature_name_to_config, feature_to_sub_table\n\n  @classmethod\n  def merge_feature_config(\n      cls, feature_name_to_config: Dict[str, entry.HashTableConfigInstance]):\n    # create merged config\n    config_to_feature_name_list: Dict[\n        str, List[entry.HashTableConfigInstance]] = defaultdict(list)\n    for feature_name in sorted(feature_name_to_config):\n      config = feature_name_to_config[feature_name]\n      config_to_feature_name_list[str(config)].append(feature_name)\n\n    sub_table_name_to_config, feature_to_sub_table = {}, {}  # merged config\n    for config_str, feature_name_list in config_to_feature_name_list.items():\n      _, sub_table_name = get_sub_table_name(feature_name_list)\n      sub_table_config = copy.copy(feature_name_to_config[feature_name_list[0]])\n\n      # replace \"fc_slot_*\" to \"slot_*\"\n      old_feature_name_list = [\n          feature_name[3:]\n          if re.match(\"^fc_slot_[0-9]*$\", feature_name) else feature_name\n          for feature_name in feature_name_list\n      ]\n      _, old_sub_table_name = get_sub_table_name(old_feature_name_list)\n      if old_sub_table_name != sub_table_name:\n        sub_table_config.extra_restore_names.append(old_sub_table_name)\n\n      sub_table_name_to_config[sub_table_name] = sub_table_config\n      for feature_name in feature_name_list:\n        feature_to_sub_table[feature_name] = sub_table_name\n\n    return sub_table_name_to_config, feature_to_sub_table\n\n  def __init__(self,\n               num_ps: int,\n               table_factory: TableFactory,\n               use_native_multi_hash_table: bool,\n               max_rpc_deadline_millis: int = 30,\n               queue_configs: Dict[str, int] = None,\n               parser_ctx=None):\n    self._local_ps: bool = True if num_ps == 0 else False\n    self._max_rpc_deadline_millis = max_rpc_deadline_millis\n    self._queue_configs = queue_configs or {}\n\n    if parser_ctx is None:\n      parser_ctx = get_default_parser_ctx()\n    self._inner_data_type = parser_ctx.parser_type\n    assert parser_ctx.sharding_sparse_fids_op_params is not None\n    self._num_ps = parser_ctx.sharding_sparse_fids_op_params.num_ps\n    self._use_native_multi_hash_table = parser_ctx.sharding_sparse_fids_op_params.use_native_multi_hash_table\n    self._unique = parser_ctx.sharding_sparse_fids_op_params.unique\n    self.transfer_float16 = parser_ctx.sharding_sparse_fids_op_params.transfer_float16\n    self._sub_table_name_to_config = parser_ctx.sharding_sparse_fids_op_params.sub_table_name_to_config\n    self._feature_configs = parser_ctx.sharding_sparse_fids_op_params.feature_configs\n    self._enable_gpu_emb = parser_ctx.sharding_sparse_fids_op_params.enable_gpu_emb\n    self._use_gpu = parser_ctx.sharding_sparse_fids_op_params.use_gpu\n\n    sub_table_to_learning_rate_tensor = {\n        sub_table_name: config.call_learning_rate_fns()\n        for sub_table_name, config in self._sub_table_name_to_config.items()\n    }\n    packed_sub_table_to_learning_rate_tensor = tensor_utils.pack_tensors(\n        sub_table_to_learning_rate_tensor)\n    if self._use_native_multi_hash_table:\n      self._sub_table_names = [self._native_multi_hash_table_fake_table]\n    else:\n      self._sub_table_names = sorted(self._sub_table_name_to_config)\n\n    if self._enable_gpu_emb:\n      self._shard_num = self._num_ps\n      if enable_bps:\n        import byteps.tensorflow as bps\n        assert bps.size() == self._shard_num\n        self._index = bps.rank()\n      else:\n        assert hvd.size() == self._shard_num\n        self._index = hvd.rank()\n      self._table = table_factory(self._index, self._sub_table_name_to_config)\n      self._output_dims = self._table.get_table_dim_sizes()\n      self._dependency_ops = []\n      table_name_list = []\n      feautres_name_list = []\n      for feature_name, cfg in self._feature_configs.feature_configs.items():\n        if cfg.table not in table_name_list:\n          table_name_list.append(cfg.table)\n        if feature_name not in feautres_name_list:\n          feautres_name_list.append(feature_name)\n      table_name_list.sort()\n      self._num_table = len(table_name_list)\n      self._num_feature = len(feautres_name_list)\n    else:\n      self._tables = []\n      for i in range(self._num_ps):\n        if export_context.is_exporting_distributed():\n          ps_graph = export_context.get_current_export_ctx().sub_graph(\n              f\"ps_{i}\")\n          with ps_graph.as_default():\n            table = table_factory(i, self._sub_table_name_to_config)\n            self._tables.append(table)\n            # Build lookup graph on the PS side\n            remote_lookup_input = {\n                sub_table_name: tf.compat.v1.placeholder(dtype=tf.int64,\n                                                         shape=(None,))\n                for sub_table_name in self._sub_table_name_to_config\n            }\n            remote_lookup_output = table.lookup(remote_lookup_input)\n            export_context.get_current_export_ctx().add_signature(\n                ps_graph, 'lookup', remote_lookup_input, remote_lookup_output)\n            if use_native_multi_hash_table:\n              raw_remote_lookup_input = {\n                  \"id\":\n                      tf.compat.v1.placeholder(dtype=tf.int64, shape=(None,)),\n                  \"id_split\":\n                      tf.compat.v1.placeholder(dtype=tf.int64, shape=(None,)),\n              }\n              raw_remote_lookup_output = {\n                  \"flat_emb\":\n                      table.raw_lookup(\n                          tf.RaggedTensor.from_row_splits(\n                              raw_remote_lookup_input[\"id\"],\n                              raw_remote_lookup_input[\"id_split\"],\n                              validate=False))\n              }\n              export_context.get_current_export_ctx().add_signature(\n                  ps_graph, 'raw_lookup', raw_remote_lookup_input,\n                  raw_remote_lookup_output)\n        elif export_context.is_exporting_standalone():\n          self._tables.append(table_factory(i, self._sub_table_name_to_config))\n        else:\n          with nullcontext() if self._local_ps else ps_device(i):\n            # Send learning rate tensor to ps\n            sub_table_to_learning_rate_tensor_on_ps = tensor_utils.unpack_tensors(\n                tensor_utils.get_keyed_shape(sub_table_to_learning_rate_tensor),\n                packed_sub_table_to_learning_rate_tensor)\n            for sub_table_name, config in self._sub_table_name_to_config.items(\n            ):\n              config.set_learning_rate_tensor(\n                  sub_table_to_learning_rate_tensor_on_ps[sub_table_name])\n            self._tables.append(table_factory(i,\n                                              self._sub_table_name_to_config))\n\n  @property\n  def slot_mapping(self):\n    \"\"\"Returns slot mapping.\"\"\"\n    return self._slot_mapping\n\n  def _native_hash_table_lookup_raw(self, lookup_data_on_wk: LookupData,\n                                    lookup_data_on_wk_row_split: LookupData):\n    ps_idx_to_multi_type_resp = {}\n\n    def emit_lookup_timer_ops(i, interval):\n      return [\n          logging_ops.emit_timer(\n              \"embedding_lookup\",\n              tf.cast(interval, tf.float32),\n              tags={\n                  \"model_name\": native_task_context.get().model_name,\n                  \"ps\": str(i)\n              })\n      ]\n\n    interval_ops = []\n    for i in range(self._num_ps):\n      table: multi_hash_table_ops.RawMultiTypeHashTable = self._tables[i]\n      (splitted_id_values,), send_ts = logging_ops.tensors_timestamp(\n          [lookup_data_on_wk[i][self._native_multi_hash_table_fake_table]])\n      splitted_id = tf.RaggedTensor.from_row_splits(\n          splitted_id_values,\n          lookup_data_on_wk_row_split[i][\n              self._native_multi_hash_table_fake_table],\n          validate=False)\n      is_standalone = export_context.is_exporting_standalone() or self._local_ps\n      with nullcontext() if is_standalone else ps_device(i):\n        flat_emb = table.raw_lookup(splitted_id)\n      (flat_emb,), end_ts = logging_ops.tensors_timestamp([flat_emb])\n      interval_ops.extend(emit_lookup_timer_ops(i, end_ts - send_ts))\n      ps_idx_to_multi_type_resp[i] = {\n          self._native_multi_hash_table_fake_table: flat_emb\n      }\n\n    ret = {}\n    with tf.control_dependencies(interval_ops):\n      for i, sub_item in ps_idx_to_multi_type_resp.items():\n        ret[i] = {}\n        for tname, ts in sub_item.items():\n          ret[i][tname] = tf.identity(ts)\n    return ret\n\n  def _lookup_raw(self, lookup_data_on_wk: LookupData):\n    ps_idx_to_multi_type_resp = {}\n\n    def emit_lookup_timer_ops(i, interval):\n      return [\n          logging_ops.emit_timer(\n              \"embedding_lookup\",\n              tf.cast(interval, tf.float32),\n              tags={\n                  \"model_name\": native_task_context.get().model_name,\n                  \"ps\": str(i)\n              })\n      ]\n\n    interval_ops = []\n    for i in range(self._num_ps):\n      # sub_table_name -> fids tensor\n      multi_type_query: Dict[str, tf.Tensor] = lookup_data_on_wk[i]\n      # Note: this is a python object, use it outside this device context required no data transfer\n      multi_type_query_shape: Dict[\n          str, List[int]] = tensor_utils.get_keyed_shape(multi_type_query)\n\n      # to reduce the number of rpc (send/recv ops) call, we pack fids by concat\n      packed_fids_on_worker, send_ts = logging_ops.tensors_timestamp(\n          tensor_utils.pack_tensors(multi_type_query))\n\n      is_standalone = export_context.is_exporting_standalone() or self._local_ps\n      with nullcontext() if is_standalone else ps_device(i):\n        packed_fids_on_ps = packed_fids_on_worker  # data transfer in logic\n        unpacked_multi_type_query = tensor_utils.unpack_tensors(\n            multi_type_query_shape, packed_fids_on_ps)\n        multi_type_resp_on_ps = self._tables[i].lookup(\n            unpacked_multi_type_query)\n        # Note: this is a python object, use it outside this device context required no data transfer\n        multi_type_resp_shape = tensor_utils.get_keyed_shape(\n            multi_type_resp_on_ps)\n\n        # to reduce rpc (send/recv ops), we pack embeddings by concat\n        packed_embeddings, emb_sizes = tensor_utils.pack_tensors(\n            multi_type_resp_on_ps)\n        if self.transfer_float16:\n          packed_embeddings_fp16 = tf.cast(\n              packed_embeddings,\n              dtype=tf.float16,\n              name='{}_send_{}_CastToFloat16'.format(packed_embeddings.op.name,\n                                                     i))\n          packed_embedding_on_ps = (packed_embeddings_fp16, emb_sizes)\n        else:\n          packed_embedding_on_ps = (packed_embeddings, emb_sizes)\n\n      packed_embedding_on_worker, end_ts = logging_ops.tensors_timestamp(\n          packed_embedding_on_ps)  # data transfer in logic\n      interval_ops.extend(emit_lookup_timer_ops(i, end_ts - send_ts))\n\n      # on worker, uppack\n      if self.transfer_float16:\n        packed_embeddings_fp16_recv, emb_sizes_recv = packed_embedding_on_worker\n        packed_embeddings_fp32 = tf.cast(packed_embeddings_fp16_recv,\n                                         dtype=tf.float32,\n                                         name='{}_recv_{}_CastToFloat32'.format(\n                                             packed_embeddings_fp16.op.name, i))\n        packed_embedding_on_worker = (packed_embeddings_fp32, emb_sizes)\n\n      multi_type_resp_on_worker = tensor_utils.unpack_tensors(\n          multi_type_resp_shape, packed_embedding_on_worker)\n      ps_idx_to_multi_type_resp[i] = multi_type_resp_on_worker\n\n    ret = {}\n    with tf.control_dependencies(interval_ops):\n      for i, sub_item in ps_idx_to_multi_type_resp.items():\n        ret[i] = {}\n        for tname, ts in sub_item.items():\n          ret[i][tname] = tf.identity(ts)\n    return ret\n\n  def _native_hash_table_lookup_with_remote_predict(\n      self, lookup_data_on_wk: LookupData,\n      lookup_data_on_wk_row_split: LookupData):\n    ps_idx_to_multi_type_resp = {}\n    for i in range(self._num_ps):\n      flat_emb, = remote_predict(\n          [\"id\", \"id_split\"], [\n              lookup_data_on_wk[i][self._native_multi_hash_table_fake_table],\n              lookup_data_on_wk_row_split[i][\n                  self._native_multi_hash_table_fake_table]\n          ], [\"flat_emb\"],\n          task=i,\n          old_model_name=\"ps_{}\".format(i),\n          model_name=f\"{native_task_context.get().model_name or ''}:ps_{i}\",\n          model_version=-1,\n          max_rpc_deadline_millis=self._max_rpc_deadline_millis,\n          output_types=[tf.float32],\n          signature_name=\"raw_lookup\")\n      ps_idx_to_multi_type_resp[i] = {\n          self._native_multi_hash_table_fake_table: flat_emb\n      }\n    return ps_idx_to_multi_type_resp\n\n  def _lookup_with_remote_predict(self, lookup_data_on_wk: LookupData):\n    sub_table_to_dim = [\n        infer_dim_size(\n            self._sub_table_name_to_config[sub_table_name].table_config)\n        for sub_table_name in self._sub_table_names\n    ]\n\n    ps_idx_to_multi_type_resp = {}\n    for i in range(self._num_ps):\n      multi_type_query = lookup_data_on_wk[i]\n      # Remote call from Entry to PS\n      # TODO(leqi.zou): Consider a better way to get model name.\n      results = remote_predict(\n          input_tensor_alias=self._sub_table_names,\n          input_tensors=[\n              multi_type_query[sub_table_name]\n              for sub_table_name in self._sub_table_names\n          ],\n          output_tensor_alias=self._sub_table_names,\n          task=i,\n          old_model_name=\"ps_{}\".format(i),\n          model_name=f\"{native_task_context.get().model_name or ''}:ps_{i}\",\n          model_version=-1,\n          max_rpc_deadline_millis=self._max_rpc_deadline_millis,\n          output_types=[tf.float32] * len(self._sub_table_names),\n          signature_name=\"lookup\")\n      ps_idx_to_multi_type_resp[i] = {\n          sub_table_name: tf.reshape(results[j], [-1, sub_table_to_dim[j]])\n          for j, sub_table_name in enumerate(self._sub_table_names)\n      }\n\n    return ps_idx_to_multi_type_resp\n\n  def lookup(\n      self,\n      features: Dict[str, tf.Tensor],\n      auxiliary_bundle: Dict[str, tf.Tensor] = None,\n      ret_fused_layout_callable_fn=False,\n      ret_lookup_callable_fn=False,\n      embedding_prefetch_capacity=0,\n  ) -> Dict[str, Union[tf.Tensor, List[tf.Tensor]]]:\n    if self._enable_gpu_emb:\n      ret = self._lookup_gpu(features, auxiliary_bundle)\n      if ret_fused_layout_callable_fn or ret_lookup_callable_fn:\n\n        def lookup_callable_fn(auxiliary_bundle_, features_):\n          return ret\n\n        return lookup_callable_fn\n      else:\n        return ret\n\n    with tf.name_scope(\"pht_lookup\"):\n      if ParserCtx.sharding_sparse_fids_sparse_features_key in features:\n        #assert False, \"not support, please use sharding_sparse_fids_with_context before call lookup\"\n        # only support for cpu training(without dsworker)\n        shards, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, \\\n          feature_size, fid_size, emb_size, shards_row_split, shards_row_split_size, \\\n          fid_list_emb_row_lenth, fid_list_table_row_length, fid_list_shard_row_lenth = \\\n          sharding_sparse_fids(\n              features[ParserCtx.sharding_sparse_fids_sparse_features_key],\n              ps_num=self._num_ps,\n              feature_cfgs=self._feature_configs,\n              unique=self._unique(),\n              input_type=self._inner_data_type,\n              parallel_flag=0)\n      else:\n        sharding_features = ParserCtx.sharding_sparse_fids_features_parse_from_features(\n            features)\n        shards, fid_offset, feature_offset, nfl_offset, batch_size, nfl_size, \\\n        feature_size, fid_size, emb_size, shards_row_split, shards_row_split_size = \\\n          sharding_features.get(\"shards\"), sharding_features.get(\"fid_offset\") ,\\\n          sharding_features.get(\"feature_offset\"), sharding_features.get(\"nfl_offset\") ,\\\n          sharding_features.get(\"batch_size\"), sharding_features.get(\"nfl_size\", None), \\\n          sharding_features.get(\"feature_size\", None), sharding_features.get(\"fid_size\", None), \\\n          sharding_features.get(\"emb_size\", None), sharding_features.get(\"shards_row_split\", None), \\\n          sharding_features.get(\"shards_row_split_size\", None)\n      if auxiliary_bundle is None:\n        auxiliary_bundle = {}\n      auxiliary_bundle['__sharding_sparse_fids__fid_offset'] = fid_offset\n      auxiliary_bundle[\n          '__sharding_sparse_fids__feature_offset'] = feature_offset\n      auxiliary_bundle['__sharding_sparse_fids__nfl_offset'] = nfl_offset\n      auxiliary_bundle['__sharding_sparse_fids__batch_size'] = tf.identity(\n          batch_size, name=\"batch_size\")\n      if nfl_size is not None:\n        auxiliary_bundle['__sharding_sparse_fids__nfl_size'] = nfl_size\n      if feature_size is not None:\n        auxiliary_bundle['__sharding_sparse_fids__feature_size'] = feature_size\n      if fid_size is not None:\n        auxiliary_bundle['__sharding_sparse_fids__fid_size'] = fid_size\n      if emb_size is not None:\n        auxiliary_bundle['__sharding_sparse_fids__emb_size'] = emb_size\n      logging.info(f\"sharding_sparse_fids done, {shards}\")\n\n      if ret_lookup_callable_fn:\n        for key, shard_fids in shards.items():\n          sub_table_name, ps_idx = key.split(':')\n          ps_idx = int(ps_idx)\n          name = '__sharding_sparse_fids__shards@{}@{}'.format(\n              ps_idx, sub_table_name)\n          auxiliary_bundle[name] = shard_fids\n          if self._use_native_multi_hash_table:\n            name = '__sharding_sparse_fids__shards_row_split@{}@{}'.format(\n                ps_idx, sub_table_name)\n            auxiliary_bundle[name] = shards_row_split[key]\n            if shards_row_split_size is not None and shards_row_split_size[\n                key] is not None:\n              name = '__sharding_sparse_fids__shards_row_split_size@{}@{}'.format(\n                  ps_idx, sub_table_name)\n              auxiliary_bundle[name] = shards_row_split_size[key]\n\n    def fused_layout_callable_fn(auxiliary_bundle_, features_):\n      flattened_embs = []\n      assert auxiliary_bundle_ is not None\n      for sub_table_name in self._sub_table_names:\n        for ps_idx in range(self._num_ps):\n          flattened_embs.append(auxiliary_bundle_[\n              f'__sharding_sparse_fids__{sub_table_name}:{ps_idx}:embs'])\n\n      nfl_offset_ = auxiliary_bundle_['__sharding_sparse_fids__nfl_offset']\n      feature_offset_ = auxiliary_bundle_[\n          '__sharding_sparse_fids__feature_offset']\n      fid_offset_ = auxiliary_bundle_['__sharding_sparse_fids__fid_offset']\n      batch_size_ = auxiliary_bundle_['__sharding_sparse_fids__batch_size']\n      nfl_size_ = auxiliary_bundle_.get('__sharding_sparse_fids__nfl_size',\n                                        None)\n      feature_size_ = auxiliary_bundle_.get(\n          '__sharding_sparse_fids__feature_size', None)\n      fid_size_ = auxiliary_bundle_.get('__sharding_sparse_fids__fid_size',\n                                        None)\n      emb_size_ = auxiliary_bundle_.get('__sharding_sparse_fids__emb_size',\n                                        None)\n\n      if export_context.is_exporting():\n        fused_layout_use_gpu = export_context.get_current_export_ctx(\n        ).with_remote_gpu\n      else:\n        fused_layout_use_gpu = self._use_gpu\n\n      with tf.device(\n          \"/device:GPU:0\" if fused_layout_use_gpu else \"/device:CPU:0\"):\n        layout_tensors = distribution_ops.fused_embedding_to_layout(\n            flattened_embs,\n            None,  #self.fids_list_row_split, v3 not need fids_list_row_split\n            fid_offset=fid_offset_,\n            feature_offset=feature_offset_,\n            nfl_offset=nfl_offset_,\n            batch_size=batch_size_,\n            nfl_size=nfl_size_,\n            feature_size=feature_size_,\n            fid_size=fid_size_,\n            emb_size=emb_size_,\n            variant_type=self._inner_data_type,\n            feature_cfgs=self._feature_configs,\n            ps_num=self._num_ps,\n            version=5)\n        layout_embeddings = self.nest_layout(layout_tensors)\n      '''\n      if not self._use_gpu:\n        # embedding_prefetch\n        logging.info(\n            f\"PartitionedHashTable lookup fused_layout enqueue: {auxiliary_bundle_} {features_}\"\n        )\n        (deq_layout_embeddings, deq_auxiliary_bundle,\n         deq_features), queue = enqueue_dicts_with_queue_return(\n             (layout_embeddings, auxiliary_bundle_, features_),\n             capacity=embedding_prefetch_capacity)\n        if queue:\n          self.add_queue_hook(EnqueueHook(queue))\n        features_.update(deq_features)\n        auxiliary_bundle_.update(deq_auxiliary_bundle)\n        logging.info(\n            f\"PartitionedHashTable lookup fused_layout dequeue: {auxiliary_bundle_} {features_}\"\n        )\n      else:\n        deq_layout_embeddings = layout_embeddings\n      '''\n      logging.info(\"fused_embedding_to_layout done!\")\n      return layout_embeddings  #deq_layout_embeddings\n\n    def call_lookup(lookup_data_on_wk: LookupData,\n                    lookup_data_on_wk_row_split: LookupData, auxiliary_bundle_,\n                    features_):\n      with tf.name_scope(\"pht_lookup\"):\n        # ps_idx_to_multi_type_resp: Dict[int, Dict[str, tf.Tensor]] = {}\n        with tf.device(\"/device:CPU:0\"):\n          if export_context.is_exporting_distributed():\n            if self._use_native_multi_hash_table:\n              ps_idx_to_multi_type_resp = self._native_hash_table_lookup_with_remote_predict(\n                  lookup_data_on_wk, lookup_data_on_wk_row_split)\n            else:\n              ps_idx_to_multi_type_resp = self._lookup_with_remote_predict(\n                  lookup_data_on_wk)\n          else:\n            if self._use_native_multi_hash_table:\n              ps_idx_to_multi_type_resp = self._native_hash_table_lookup_raw(\n                  lookup_data_on_wk, lookup_data_on_wk_row_split)\n            else:\n              ps_idx_to_multi_type_resp = self._lookup_raw(lookup_data_on_wk)\n\n        for sub_table_name in self._sub_table_names:\n          for ps_idx in range(self._num_ps):\n            embeddings_tensor = ps_idx_to_multi_type_resp[ps_idx][\n                sub_table_name]\n            auxiliary_bundle_[\n                f'__sharding_sparse_fids__{sub_table_name}:{ps_idx}:embs'] = embeddings_tensor\n            if not export_context.is_exporting():\n              fids_tensor = lookup_data_on_wk[ps_idx][sub_table_name]\n              auxiliary_bundle_[\n                  f'__sharding_sparse_fids__{sub_table_name}:{ps_idx}:fids'] = fids_tensor\n              if self._use_native_multi_hash_table:\n                fids_tensor_row_split = lookup_data_on_wk_row_split[ps_idx][\n                    sub_table_name]\n                auxiliary_bundle_[\n                    f'__sharding_sparse_fids__{sub_table_name}:{ps_idx}:fids_row_split'] = fids_tensor_row_split\n\n      if self._use_gpu:\n        logging.info(\n            f\"PartitionedHashTable lookup gpu fused_layout tensor to gpu before: {auxiliary_bundle} {features}\"\n        )\n        self.tensor_move_to_gpu(((auxiliary_bundle_, [\n            \"__sharding_sparse_fids__batch_size\",\n            \"__sharding_sparse_fids__nfl_size\",\n            \"__sharding_sparse_fids__feature_size\",\n            \"__sharding_sparse_fids__fid_size\",\n            \"__sharding_sparse_fids__emb_size\"\n        ]), (features_, [\"req_time\"])))\n      logging.info(\n          f\"PartitionedHashTable lookup fused_layout enqueue before: {auxiliary_bundle} {features}\"\n      )\n      (dequeued_features,\n       deq_auxiliary_bundle), queue = enqueue_dicts_with_queue_return(\n           (features_, auxiliary_bundle_), capacity=embedding_prefetch_capacity)\n      if queue:\n        self.add_queue_hook(EnqueueHook(queue))\n      features_.update(dequeued_features)\n      auxiliary_bundle_.update(deq_auxiliary_bundle)\n      logging.info(\n          f\"PartitionedHashTable lookup fused_layout dequeue: {auxiliary_bundle} {features}\"\n      )\n\n    def lookup_callable_fn(auxiliary_bundle_, features_):\n      with tf.name_scope(\"pht_lookup\"):\n        lookup_data_on_wk: LookupData = {}\n        lookup_data_on_wk_row_split: LookupData = {}\n        for sub_table_name in self._sub_table_names:\n          for ps_idx in range(self._num_ps):\n            key = '__sharding_sparse_fids__shards@{}@{}'.format(\n                ps_idx, sub_table_name)\n            if ps_idx not in lookup_data_on_wk:\n              lookup_data_on_wk[ps_idx] = {}\n            lookup_data_on_wk[ps_idx][sub_table_name] = auxiliary_bundle_[key]\n\n            if self._use_native_multi_hash_table:\n              key = '__sharding_sparse_fids__shards_row_split@{}@{}'.format(\n                  ps_idx, sub_table_name)\n              size_key = '__sharding_sparse_fids__shards_row_split_size@{}@{}'.format(\n                  ps_idx, sub_table_name)\n              if ps_idx not in lookup_data_on_wk_row_split:\n                lookup_data_on_wk_row_split[ps_idx] = {}\n              if size_key not in auxiliary_bundle_:\n                lookup_data_on_wk_row_split[ps_idx][\n                    sub_table_name] = auxiliary_bundle_[key]\n              else:\n                lookup_data_on_wk_row_split[ps_idx][\n                    sub_table_name] = distribution_ops.normalize_merged_split(\n                        auxiliary_bundle_[key], auxiliary_bundle_[size_key])\n\n      call_lookup(lookup_data_on_wk, lookup_data_on_wk_row_split,\n                  auxiliary_bundle_, features_)\n      return fused_layout_callable_fn(auxiliary_bundle_, features_)\n\n    if ret_lookup_callable_fn:\n      return lookup_callable_fn\n\n    with tf.name_scope(\"pht_lookup\"):\n      lookup_data_on_wk: LookupData = {}\n      lookup_data_on_wk_row_split: LookupData = {}\n      for key, shard_fids in shards.items():\n        sub_table_name, ps_idx = key.split(':')\n        ps_idx = int(ps_idx)\n        if self._use_native_multi_hash_table:\n          shards_row_split_part = shards_row_split[key]\n        else:\n          shards_row_split_part = None\n\n        if ps_idx in lookup_data_on_wk:\n          lookup_data_on_wk[ps_idx][sub_table_name] = shard_fids\n          lookup_data_on_wk_row_split[ps_idx][\n              sub_table_name] = shards_row_split_part\n        else:\n          lookup_data_on_wk[ps_idx] = {sub_table_name: shard_fids}\n          lookup_data_on_wk_row_split[ps_idx] = {\n              sub_table_name: shards_row_split_part\n          }\n\n    call_lookup(lookup_data_on_wk, lookup_data_on_wk_row_split,\n                auxiliary_bundle, features)\n\n    if ret_fused_layout_callable_fn:\n      return fused_layout_callable_fn\n    else:\n      return fused_layout_callable_fn(auxiliary_bundle, features)\n\n  def apply_gradients(\n      self,\n      layout_grads_and_vars: List[Tuple[tf.Tensor, tf.Tensor]],\n      global_step: tf.Tensor,\n      req_time: Optional[tf.Tensor] = None,\n      auxiliary_bundle: Dict[str, tf.Tensor] = None,\n      async_function_mgr: prefetch_queue.AsyncFunctionMgr = None,\n      async_push: bool = False,\n      grad_scale: tf.Tensor = None) -> PartitionedHashTable:\n    logging.info(\n        f\"PartitionedHashTable apply_gradients {async_push} {async_function_mgr}\"\n    )\n    with tf.device(\n        \"/device:GPU:0\" if self._enable_gpu_emb else \"/device:CPU:0\"):\n      if req_time is None:\n        req_time = tf.constant(0, dtype=tf.int64)\n      else:\n        req_time = tf.reduce_max(req_time)\n    assert auxiliary_bundle is not None\n    if self._enable_gpu_emb:\n      assert not async_push\n      return self._apply_gradients_gpu(layout_grads_and_vars,\n                                       global_step,\n                                       req_time,\n                                       auxiliary_bundle,\n                                       grad_scale=grad_scale)\n    with tf.name_scope(\"pht_apply_gradients\"):\n      layout_grad, layout = zip(*layout_grads_and_vars)\n      flattened_fids, flattened_fids_row_split, flattened_embs = [], [], []\n      for sub_table_name in self._sub_table_names:\n        for ps_idx in range(self._num_ps):\n          flattened_fids.append(auxiliary_bundle[\n              f'__sharding_sparse_fids__{sub_table_name}:{ps_idx}:fids'])\n          if self._use_native_multi_hash_table:\n            flattened_fids_row_split.append(auxiliary_bundle[\n                f'__sharding_sparse_fids__{sub_table_name}:{ps_idx}:fids_row_split']\n                                           )\n          flattened_embs.append(auxiliary_bundle[\n              f'__sharding_sparse_fids__{sub_table_name}:{ps_idx}:embs'])\n      nfl_offset = auxiliary_bundle['__sharding_sparse_fids__nfl_offset']\n      feature_offset = auxiliary_bundle[\n          '__sharding_sparse_fids__feature_offset']\n      fid_offset = auxiliary_bundle['__sharding_sparse_fids__fid_offset']\n      batch_size = auxiliary_bundle['__sharding_sparse_fids__batch_size']\n\n      with tf.device(\"/device:GPU:0\" if self._use_gpu else \"/device:CPU:0\"):\n        embeddings_grad = distribution_ops.fused_embedding_to_layout_grad(\n            nfl_offset=nfl_offset,\n            feature_offset=feature_offset,\n            fid_offset=fid_offset,\n            batch_size=batch_size,\n            embeddings_list=flattened_embs,\n            fid_list_row_split=None,  #flattened_fids_row_split, v3 no need\n            layout_tensors_grad=layout_grad,\n            layout_tensors_grad_scale=grad_scale,\n            variant_type=self._inner_data_type,\n            feature_cfgs=self._feature_configs,\n            ps_num=self._num_ps)\n\n      def hash_table_apply_gradients(flattened_fids_, flattened_fids_row_split_,\n                                     embeddings_grad_, global_step_, req_time_):\n        if self._use_gpu:\n          logging.info(\n              f\"PartitionedHashTable apply_gradients fused_layout before tensor_move_to_cpu: \\\n              {flattened_fids_}, {flattened_fids_row_split_}, {embeddings_grad_}, \\\n              {global_step_}, {req_time_}\")\n\n          def tensor_move_to_cpu(*inputs):\n            inputs_info = []\n            to_cpu_value_list = []\n            for tensor_ in inputs:\n              to_cpu_dict = defaultdict(int)\n              if isinstance(tensor_, List):\n                for idx in range(len(tensor_)):\n                  part_tensor = tensor_[idx]\n                  to_cpu_dict[idx] = len(to_cpu_value_list)\n                  to_cpu_value_list.append(part_tensor)\n              elif tensor_ is not None:\n                to_cpu_dict[-1] = len(to_cpu_value_list)\n                to_cpu_value_list.append(tensor_)\n              inputs_info.append(to_cpu_dict)\n            with tf.device(\"/device:CPU:0\"):\n              gpu_value_list = tf.identity_n(to_cpu_value_list)\n            outputs = []\n            for input_idx in range(len(inputs)):\n              to_cpu_dict = inputs_info[input_idx]\n              part_input = inputs[input_idx]\n              for k, idx in to_cpu_dict.items():\n                if k == -1:\n                  part_input = gpu_value_list[idx]\n                  continue\n                else:\n                  part_input[k] = gpu_value_list[idx]\n              outputs.append(part_input)\n            return tuple(outputs)\n\n          flattened_fids_, flattened_fids_row_split_, embeddings_grad_, \\\n            global_step_, req_time_ = tensor_move_to_cpu(\n              flattened_fids_, flattened_fids_row_split_, embeddings_grad_,\n              global_step_, req_time_)\n          logging.info(\n              f\"PartitionedHashTable apply_gradients fused_layout tensor_move_to_cpu: \\\n              {flattened_fids_}, {flattened_fids_row_split_}, {embeddings_grad_}, \\\n              {global_step_}, {req_time_}\")\n        with tf.device(\"/device:CPU:0\"):\n          if self._use_native_multi_hash_table:\n            new_tables = []\n            for i in range(self._num_ps):\n              splitted_id = tf.RaggedTensor.from_row_splits(\n                  flattened_fids_[i],\n                  flattened_fids_row_split_[i],\n                  validate=False)\n              splitted_flat_grad = embeddings_grad_[i]\n              table: multi_hash_table_ops.RawMultiTypeHashTable = self._tables[\n                  i]\n              with nullcontext() if self._local_ps else ps_device(i):\n                new_tables.append(\n                    table.raw_apply_gradients(splitted_id,\n                                              splitted_flat_grad,\n                                              global_step=global_step,\n                                              req_time=req_time_))\n          else:\n            fids_and_embgrad_pairs = list(zip(flattened_fids_,\n                                              embeddings_grad_))\n            logging.info(\n                f\"PartitionedHashTable apply_gradients fused_embedding_to_layout_grad done, {fids_and_embgrad_pairs}\"\n            )\n            # reconstruct update data\n            offset = 0\n            update_data_on_worker: UpdateData = {\n                ps_idx: {} for ps_idx in range(self._num_ps)\n            }\n            for sub_table_name in self._sub_table_names:\n              for ps_idx in range(self._num_ps):\n                update_data_on_worker[ps_idx][\n                    sub_table_name] = fids_and_embgrad_pairs[offset]\n                offset += 1\n            new_tables = []\n            for i in range(self._num_ps):\n              keyed_fids, keyed_grads = {}, {}\n              for tbname, (fids, emb_grad) in update_data_on_worker[i].items():\n                keyed_fids[tbname] = fids\n                keyed_grads[tbname] = emb_grad\n              packed_list = tensor_utils.pack_typed_keyed_tensors(\n                  [keyed_fids, keyed_grads])\n              (packed_fids_on_wk, packed_emb_grad_on_wk,\n               packed_sizes_on_wk) = packed_list\n              typed_keyed_shape = tensor_utils.get_typed_keyed_shape(\n                  [keyed_fids, keyed_grads])\n\n              if self.transfer_float16:\n                packed_emb_grad_on_wk = tf.cast(\n                    packed_emb_grad_on_wk,\n                    dtype=tf.float16,\n                    name='{}_send_{}_CastToFloat16'.format(\n                        packed_emb_grad_on_wk.op.name, i))\n\n              with nullcontext() if self._local_ps else ps_device(i):\n                packed_fids_on_ps = packed_fids_on_wk\n                packed_emb_grad_on_ps = packed_emb_grad_on_wk\n                packed_sizes_on_ps = packed_sizes_on_wk\n                if self.transfer_float16:\n                  packed_emb_grad_on_ps = tf.cast(\n                      packed_emb_grad_on_ps,\n                      dtype=tf.float32,\n                      name='{}_recv_{}_CastToFloat32'.format(\n                          packed_emb_grad_on_ps.op.name, i))\n                keyed_fids_on_ps, keyed_grads_on_ps = tensor_utils.unpack_packed_tensors(\n                    typed_keyed_shape,\n                    packed_list=[\n                        packed_fids_on_ps, packed_emb_grad_on_ps,\n                        packed_sizes_on_ps\n                    ])\n                partitioned_update_data_on_ps = {\n                    tbname:\n                    (keyed_fids_on_ps[tbname], keyed_grads_on_ps[tbname])\n                    for tbname in keyed_fids_on_ps\n                }\n                new_tables.append(self._tables[i].apply_gradients(\n                    partitioned_update_data_on_ps,\n                    global_step_,\n                    req_time=req_time_))\n          return self._copy_with_new_table(new_tables).as_op()\n\n      if async_function_mgr is None or not async_push:\n        return hash_table_apply_gradients(flattened_fids,\n                                          flattened_fids_row_split,\n                                          embeddings_grad, global_step,\n                                          req_time)\n      else:\n        return async_function_mgr.add_async_function(\n            hash_table_apply_gradients,\n            (flattened_fids, flattened_fids_row_split, embeddings_grad,\n             global_step, req_time),\n            is_async=async_push,\n            queue_name=\"postpush_queue\")\n\n  def tensor_move_to_gpu(self, inputs):\n    inputs_info = []\n    to_gpu_value_list = []\n    for tensor_dict, except_list in inputs:\n      to_gpu_dict = defaultdict(int)\n      for k, v in tensor_dict.items():\n        if k in except_list:\n          continue\n        to_gpu_dict[k] = len(to_gpu_value_list)\n        to_gpu_value_list.append(v)\n      inputs_info.append((to_gpu_dict, tensor_dict))\n    with tf.device(\"/device:GPU:0\"):\n      gpu_value_list = tf.identity_n(to_gpu_value_list)\n    for to_gpu_dict, tensor_dict in inputs_info:\n      for k, idx in to_gpu_dict.items():\n        tensor_dict[k] = gpu_value_list[idx]\n\n  def as_op(self, name=None):\n    name = name or \"pht_as_op\"\n    if self._enable_gpu_emb:\n      with tf.control_dependencies(self._dependency_ops):\n        return self._table.as_op(name)\n    ops = []\n    for i in range(self._num_ps):\n      with nullcontext() if self._local_ps else ps_device(i):\n        ops.append(self._tables[i].as_op(name=\"{}/sub_{}\".format(name, i)))\n    with tf.control_dependencies(ops):\n      return tf.no_op(name=(\"{}/done\".format(name)))\n\n  def _lookup_gpu(\n      self,\n      features: Dict[str, tf.Tensor],\n      auxiliary_bundle: Dict[str, tf.Tensor] = None,\n  ) -> Dict[str, Union[tf.Tensor, List[tf.Tensor]]]:\n    if enable_bps:\n      import byteps.tensorflow as bps\n    with tf.name_scope(\"pht_lookup_gpu\"):\n      logging.info(\n          f\"PartitionedHashTable lookup_gpu fused_layout tensor to gpu before: {features}\"\n      )\n\n      slot_num = self._num_table\n      recv_emb_splits_tmp = tf.reshape(\n          tf.matmul(\n              tf.reshape(\n                  features[\"__sharding_sparse_fids__shards_table_row_lengths\"],\n                  [self._shard_num, slot_num]),\n              tf.expand_dims(tf.constant(self._output_dims, dtype=tf.int32),\n                             -1)  # [slot_num, 1]\n          ),\n          [-1]  # flatten\n      )\n      features[\"__sharding_sparse_fids__recv_emb_splits\"] = recv_emb_splits_tmp\n\n      #self.tensor_move_to_gpu(\n      #    ((features, [\"__sharding_sparse_fids__batch_size\"]),))\n      logging.info(\n          f\"PartitionedHashTable lookup_gpu fused_layout enqueue before: {features}\"\n      )\n\n      sharding_features = ParserCtx.sharding_sparse_fids_features_parse_from_features(\n          features)\n      shards_value, shards_row_lengths, shards_table_row_lengths, fid_offset, feature_offset, \\\n      nfl_offset, batch_size, fid_list_emb_row_lenth, recv_emb_splits = \\\n        sharding_features.get(\"shards_value\"), sharding_features.get(\"shards_row_lengths\"), \\\n        sharding_features.get(\"shards_table_row_lengths\"), sharding_features.get(\"fid_offset\") ,\\\n        sharding_features.get(\"feature_offset\"), sharding_features.get(\"nfl_offset\") ,\\\n        sharding_features.get(\"batch_size\"), sharding_features.get(\"fid_list_emb_row_lenth\"), \\\n        sharding_features.get(\"recv_emb_splits\")\n\n      if auxiliary_bundle is None:\n        auxiliary_bundle = {}\n      auxiliary_bundle['__sharding_sparse_fids__fid_offset'] = fid_offset\n      auxiliary_bundle[\n          '__sharding_sparse_fids__feature_offset'] = feature_offset\n      auxiliary_bundle['__sharding_sparse_fids__nfl_offset'] = nfl_offset\n      auxiliary_bundle['__sharding_sparse_fids__batch_size'] = batch_size\n      auxiliary_bundle[\n          \"__sharding_sparse_fids__recv_emb_splits\"] = recv_emb_splits\n      auxiliary_bundle[\n          \"__sharding_sparse_fids__fid_list_emb_row_lenth\"] = fid_list_emb_row_lenth\n\n      logging.info(\n          f\"sharding_sparse_fids done, {shards_value} {shards_row_lengths}\")\n\n      all_fids = shards_value\n      shard_sizes = shards_row_lengths\n      sharded_slot_sizes = shards_table_row_lengths\n\n      # We exchange the flattened IDs and their splits.\n      # M: num_of_ids,\n      # N: num_of_shards,\n      # K: num_of_merged_tables,\n      # E: num_of_total_embedding_dim.\n\n      # id_flat_t: [M], id_flat_split_t: [N]\n      # id_size_flat_t: [K*N], id_size_flat_split_t: [N]\n      if enable_bps and enable_bps_fid:\n        logging.info('Enabled BPS for fid alltoall')\n        id_flat_t, id_flat_split_t = bps.alltoall(all_fids,\n                                                  splits=shard_sizes,\n                                                  with_size=True,\n                                                  name='fid_data')\n        # We also add the flat_t sizes.\n        id_size_flat_t = bps.alltoall(sharded_slot_sizes,\n                                      splits=[slot_num] * self._shard_num,\n                                      recv_splits=([slot_num] *\n                                                   self._shard_num),\n                                      name='fid_size')\n      elif enable_custom_optimized_hvd:\n        id_flat_t, id_flat_split_t = hvd.alltoall(all_fids,\n                                                  splits=shard_sizes,\n                                                  with_size=True)\n        # We also add the flat_t sizes.\n        id_size_flat_t = hvd.alltoall(sharded_slot_sizes,\n                                      splits=[slot_num] * self._shard_num,\n                                      recv_splits=[slot_num] * self._shard_num)\n      elif enable_hvd:\n        if enable_hvd_fid_g2g:\n          logging.info('Enabled hvd for fid alltoall g2g')\n          with tf.device(\"/device:GPU:0\"):\n            id_flat_t = hvd.alltoall(all_fids, splits=shard_sizes)\n            id_flat_split_t = hvd.alltoall(shard_sizes)\n            id_size_flat_t = hvd.alltoall(sharded_slot_sizes,\n                                          splits=[slot_num] * self._shard_num)\n        else:\n          id_flat_t = hvd.alltoall(all_fids, splits=shard_sizes)\n          id_flat_split_t = hvd.alltoall(shard_sizes)\n          id_size_flat_t = hvd.alltoall(sharded_slot_sizes,\n                                        splits=[slot_num] * self._shard_num)\n\n      auxiliary_bundle[\"__sharding_sparse_fids__shard_sizes\"] = shard_sizes\n      auxiliary_bundle[\"__sharding_sparse_fids__id_flat_t\"] = id_flat_t\n      auxiliary_bundle[\n          \"__sharding_sparse_fids__id_size_flat_t\"] = id_size_flat_t\n\n      # fused_embeddings: [E], fused_splits: [N]\n      # id_offsets: [K*N], emb_offsets: [K*N]\n      req_time = features.get(\"req_time\", None)\n      with tf.device(\n          \"/device:GPU:0\" if self._enable_gpu_emb else \"/device:CPU:0\"):\n        if req_time is None:\n          logging.warning(f\"fused_embedding_to_layout use default req_time\")\n          req_time = tf.constant(0, dtype=tf.int64)\n        else:\n          req_time = tf.reduce_max(req_time)\n\n      with tf.device(\"/GPU:0\"):\n        fused_embeddings, embedding_splits, id_offsets, emb_offsets, indices = \\\n            self._table.fused_lookup(id_flat_t, id_size_flat_t, self._shard_num, req_time)\n      if FLAGS.enable_alltoall_metrics:\n        with tf.device(\"/CPU:0\"):\n          tf.compat.v1.summary.histogram(\"fused_embedding_splits\",\n                                         embedding_splits)\n\n      auxiliary_bundle[\n          \"__sharding_sparse_fids__fused_embeddings\"] = fused_embeddings\n      auxiliary_bundle[\n          \"__sharding_sparse_fids__embedding_splits\"] = embedding_splits\n      auxiliary_bundle[\"__sharding_sparse_fids__id_offsets\"] = id_offsets\n      auxiliary_bundle[\"__sharding_sparse_fids__emb_offsets\"] = emb_offsets\n      auxiliary_bundle[\"__sharding_sparse_fids__indices\"] = indices\n\n      deq_auxiliary_bundle, queue = enqueue_dicts_with_queue_return(\n          auxiliary_bundle,\n          capacity=int(self._queue_configs.get(\"enable_pipelined_fwda2a\", 0)),\n          queue_name=\"queue_lookup_to_fusedEmbA2A\")\n      if queue:\n        self.add_queue_hook(EnqueueHook(queue))\n      auxiliary_bundle.update(deq_auxiliary_bundle)\n\n      fused_embeddings = auxiliary_bundle.pop(\n          \"__sharding_sparse_fids__fused_embeddings\")\n      embedding_splits = auxiliary_bundle[\n          \"__sharding_sparse_fids__embedding_splits\"]\n      recv_emb_splits = auxiliary_bundle[\n          \"__sharding_sparse_fids__recv_emb_splits\"]\n\n      # recv_embeddings: [E'], recv_embedding_sizes: [N]\n      if enable_bps and enable_bps_fwd:\n        if enable_bps_fwd_gdr:\n          if enable_bps_fwd_gdr_g2g:\n            logging.info('Enabled BPS for fwd embed alltoall GDR (G2G)')\n            with tf.device(\"/device:GPU:0\"):\n              fused_embeddings_gpu = fused_embeddings\n            with tf.device(\"/device:GPU:0\"):\n              recv_embeddings = bps.alltoall(fused_embeddings_gpu,\n                                             embedding_splits,\n                                             recv_splits=recv_emb_splits,\n                                             name=\"fwd_alltoall_g2g\")\n          else:\n            logging.info('Enabled BPS for fwd embed alltoall GDR (C2G)')\n            with tf.device(\"/device:GPU:0\"):\n              recv_embeddings = bps.alltoall_cpu2gpu(\n                  fused_embeddings,\n                  embedding_splits,\n                  recv_splits=recv_emb_splits,\n                  name=\"fwd_alltoall_c2g\")\n        else:\n          logging.info('Enabled BPS for fwd embed alltoall')\n          recv_embeddings = bps.alltoall(fused_embeddings,\n                                         embedding_splits,\n                                         recv_splits=recv_emb_splits,\n                                         name=\"fwd_alltoall\")\n      elif enable_custom_optimized_hvd:\n        if enable_hvd_fwd_g2g:\n          logging.info('Enabled optimized hvd for fwd embed alltoall g2g')\n          with tf.device(\"/device:GPU:0\"):\n            recv_embeddings = hvd.alltoall(\n                fused_embeddings,\n                embedding_splits,\n                recv_splits=recv_emb_splits,\n            )\n        else:\n          logging.info('Enabled optimized hvd for fwd embed alltoall')\n          recv_embeddings = hvd.alltoall(\n              fused_embeddings,\n              embedding_splits,\n              recv_splits=recv_emb_splits,\n          )\n      elif enable_hvd:\n        if enable_hvd_fwd_g2g:\n          logging.info('Enabled hvd for fwd embed alltoall g2g')\n          with tf.device(\"/device:GPU:0\"):\n            recv_embeddings = hvd.alltoall(fused_embeddings,\n                                           embedding_splits,\n                                           name='hvd_fwd_a2a_g2g')\n        else:\n          logging.info('Enabled hvd for fwd embed alltoall')\n          recv_embeddings = hvd.alltoall(fused_embeddings,\n                                         embedding_splits,\n                                         name='hvd_fwd_a2a')\n\n      auxiliary_bundle[\n          \"__sharding_sparse_fids__recv_embeddings\"] = recv_embeddings\n      #TODO enable embedding_prefetch_capacity train will slow down\n      '''\n      deq_auxiliary_bundle, queue = enqueue_dicts_with_queue_return(\n          auxiliary_bundle,\n          capacity=int(self._queue_configs.get(\"embedding_prefetch_capacity\",\n                                               0)),\n          queue_name=\"queue_fusedEmbA2A_to_fusedGather\")\n      if queue:\n        self.add_queue_hook(EnqueueHook(queue))\n      auxiliary_bundle.update(deq_auxiliary_bundle)\n      '''\n\n      recv_embeddings = auxiliary_bundle[\n          \"__sharding_sparse_fids__recv_embeddings\"]\n\n      fid_offset = auxiliary_bundle['__sharding_sparse_fids__fid_offset']\n      feature_offset = auxiliary_bundle[\n          '__sharding_sparse_fids__feature_offset']\n      nfl_offset = auxiliary_bundle['__sharding_sparse_fids__nfl_offset']\n      batch_size = auxiliary_bundle['__sharding_sparse_fids__batch_size']\n      fid_list_emb_row_lenth = auxiliary_bundle[\n          '__sharding_sparse_fids__fid_list_emb_row_lenth']\n\n      with tf.device(\"/device:GPU:0\"):\n        '''\n        recv_embeddings_split = tf.split(recv_embeddings,\n                                         fid_list_emb_row_lenth)\n\n        flattened_embs = [None] * (self._num_ps * self._num_table)\n        recv_embeddings_split_index = 0\n        for ps_index in range(self._num_ps):\n          for table_idx in range(self._num_table):\n            flattened_embs[\n                table_idx * self._num_ps +\n                ps_index] = recv_embeddings_split[recv_embeddings_split_index]\n            recv_embeddings_split_index += 1\n        '''\n\n        layout_tensors = distribution_ops.fused_embedding_to_layout(\n            [recv_embeddings],  #flattened_embs,\n            None,  #self.fids_list_row_split, v3 not need fids_list_row_split\n            fid_list_emb_row_lenth=fid_list_emb_row_lenth,\n            fid_offset=fid_offset,\n            feature_offset=feature_offset,\n            nfl_offset=nfl_offset,\n            batch_size=batch_size,\n            variant_type=self._inner_data_type,\n            feature_cfgs=self._feature_configs,\n            ps_num=self._num_ps,\n            version=4)\n      #auxiliary_bundle[\n      #    '__sharding_sparse_fids__flattened_embs'] = flattened_embs\n      logging.info(\"fused_embedding_to_layout done!\")\n      return self.nest_layout(layout_tensors)\n\n  def _apply_gradients_gpu(\n      self,\n      layout_grads_and_vars: List[Tuple[tf.Tensor, tf.Tensor]],\n      global_step: tf.Tensor,\n      req_time: Optional[tf.Tensor] = None,\n      auxiliary_bundle: Dict[str, tf.Tensor] = None,\n      grad_scale: tf.Tensor = None) -> PartitionedHashTable:\n    with tf.name_scope(\"pht_apply_gradients_gpu\"):\n\n      auxiliary_bundle['__sharding_sparse_fids__global_step'] = global_step\n      auxiliary_bundle[\"__sharding_sparse_fids__req_time\"] = req_time\n\n      layout_grad, layout = zip(*layout_grads_and_vars)\n      assert auxiliary_bundle is not None\n\n      fid_offset = auxiliary_bundle.pop('__sharding_sparse_fids__fid_offset')\n      feature_offset = auxiliary_bundle.pop(\n          '__sharding_sparse_fids__feature_offset')\n      nfl_offset = auxiliary_bundle.pop('__sharding_sparse_fids__nfl_offset')\n      batch_size = auxiliary_bundle.pop('__sharding_sparse_fids__batch_size')\n\n      recv_embeddings = auxiliary_bundle.pop(\n          \"__sharding_sparse_fids__recv_embeddings\")\n      fid_list_emb_row_lenth = auxiliary_bundle.pop(\n          '__sharding_sparse_fids__fid_list_emb_row_lenth')\n\n      #flattened_embs = auxiliary_bundle.pop(\n      #    '__sharding_sparse_fids__flattened_embs')\n\n      with tf.device(\"/device:GPU:0\"):\n        embeddings_grad = distribution_ops.fused_embedding_to_layout_grad(\n            nfl_offset=nfl_offset,\n            feature_offset=feature_offset,\n            fid_offset=fid_offset,\n            batch_size=batch_size,\n            embeddings_list=[recv_embeddings],  #flattened_embs,\n            fid_list_row_split=None,  #flattened_fids_row_split, v3 no need\n            fid_list_emb_row_lenth=fid_list_emb_row_lenth,\n            layout_tensors_grad=layout_grad,\n            layout_tensors_grad_scale=grad_scale,\n            variant_type=self._inner_data_type,\n            feature_cfgs=self._feature_configs,\n            ps_num=self._num_ps,\n            version=4)\n        '''\n        embeddings_grad_reorder = [None] * (self._num_ps * self._num_table)\n        embeddings_grad_index = 0\n        for table_idx in range(self._num_table):\n          for ps_index in range(self._num_ps):\n            embeddings_grad_reorder[\n                table_idx * self._num_ps +\n                ps_index] = embeddings_grad[embeddings_grad_index]\n            embeddings_grad_index += 1\n        grad_flat = tf.concat(embeddings_grad_reorder, axis=0)\n        '''\n        grad_flat = embeddings_grad[0]\n\n      auxiliary_bundle['__sharding_sparse_fids__grad_flat'] = grad_flat\n\n      deq_auxiliary_bundle, async_optimize_queue = enqueue_dicts_with_queue_return(\n          auxiliary_bundle,\n          capacity=int(self._queue_configs.get(\"enable_async_optimize\", 0)),\n          queue_name=\"queue_fusedGatherGrad_to_fusedEmbGradA2A\")\n      auxiliary_bundle.update(deq_auxiliary_bundle)\n\n      grad_flat = auxiliary_bundle.pop(\"__sharding_sparse_fids__grad_flat\")\n      recv_emb_splits = auxiliary_bundle.pop(\n          \"__sharding_sparse_fids__recv_emb_splits\")\n      embedding_splits = auxiliary_bundle.pop(\n          \"__sharding_sparse_fids__embedding_splits\")\n\n      if enable_bps and enable_bps_bwd:\n        import byteps.tensorflow as bps\n        from byteps.tensorflow.compression import FP16Compressor as BPSFP16Compressor\n        if enable_bps_bwd_gdr:\n          with tf.device(\"/device:GPU:0\"), tf.name_scope(\"bps_bwd_alltoall\"):\n            if enable_bps_bwd_gdr_g2g:\n              logging.info('Enabled BPS for bwd embed alltoall GDR (G2G)')\n              bwd_tensor_name = \"bwd_alltoall_g2g\"\n              grad_flat_t = bps.alltoall(grad_flat,\n                                         recv_emb_splits,\n                                         recv_splits=embedding_splits,\n                                         name=bwd_tensor_name)\n              if enable_bps_bwd_cast == 16:\n                # do cast on GPU\n                if enable_bps_bwd_fake_cast:\n                  grad_flat_t = grad_flat_t * 0.0\n                grad_flat_t = tf.cast(grad_flat_t, tf.float32)\n              with tf.device(\"/device:CPU:0\"):\n                grad_flat_t = tf.identity(grad_flat_t)\n            else:\n              logging.info('Enabled BPS for bwd embed alltoall GDR (G2C)')\n              bwd_tensor_name = \"bwd_alltoall_g2c\"\n              grad_flat_t = bps.alltoall_gpu2cpu(grad_flat,\n                                                 recv_emb_splits,\n                                                 recv_splits=embedding_splits,\n                                                 name=bwd_tensor_name)\n              with tf.device(\"/device:CPU:0\"):\n                # grad_flat_t<tensor>.device = <tensor>._op.device (bps.alltoall_gpu2cpu as GPU op)\n                # However the tensor is on CPU, so we fixed the tensor placement info with an identity.\n                grad_flat_t = tf.identity(grad_flat_t)\n              if enable_bps_bwd_cast == 16:\n                if enable_bps_bwd_fake_cast:\n                  grad_flat_t = grad_flat_t * 0.0\n                grad_flat_t = tf.cast(grad_flat_t, tf.float32)\n        else:\n          logging.info(\n              'Enabled BPS for bwd embed alltoall with cast optimization')\n          grad_flat_t = bps.alltoall(grad_flat,\n                                     recv_emb_splits,\n                                     recv_splits=embedding_splits,\n                                     name=\"bwd_alltoall\")\n          if enable_bps_bwd_cast == 16:\n            if enable_bps_bwd_fake_cast:\n              grad_flat_t = grad_flat_t * 0.0\n            grad_flat_t = tf.cast(grad_flat_t, tf.float32)\n\n        sent_grad_split_size = embedding_splits\n      elif enable_custom_optimized_hvd:\n        if enable_hvd_bwd_g2g:\n          logging.info('Enabled optimized hvd for bwd embed alltoall g2g')\n          with tf.device(\"/device:GPU:0\"):\n            grad_flat_t, sent_grad_split_size = hvd.alltoall(\n                grad_flat,\n                recv_emb_splits,\n                recv_splits=embedding_splits,\n                with_size=True,\n                compression=FP16Compressor)\n            with tf.device(\"/device:CPU:0\"):\n              grad_flat_t = tf.identity(grad_flat_t)\n        else:\n          logging.info('Enabled optimized hvd for bwd embed alltoall')\n          grad_flat_t, sent_grad_split_size = hvd.alltoall(\n              grad_flat,\n              recv_emb_splits,\n              recv_splits=embedding_splits,\n              with_size=True,\n              compression=FP16Compressor)\n        if FLAGS.enable_alltoall_metrics and (self._shard_num > 1):\n          # There is some issue with tf.compat.v1.summary on the horovod alltoall input,\n          # using output instead. They are almost equivalent.\n          shard_sizes = auxiliary_bundle[\"__sharding_sparse_fids__shard_sizes\"]\n          shard_sizes = tf.slice(shard_sizes, [1], [self._shard_num - 1])\n          total_alltoall_id_size = tf.reduce_sum(shard_sizes)\n          recv_emb_splits = tf.slice(tf.identity(recv_emb_splits), [1],\n                                     [self._shard_num - 1])\n          total_alltoall_emb_size = tf.reduce_sum(tf.identity(recv_emb_splits))\n          tmp_result = tf.reshape(total_alltoall_id_size, [1])\n          tmp_result2 = tf.reshape(total_alltoall_emb_size, [1])\n          total_id_dist = hvd.allgather(tmp_result)\n          total_emb_dist = hvd.allgather(tmp_result2)\n          with tf.device(\"/CPU:0\"):\n            tf.compat.v1.summary.histogram(\"Alltoall_id_dist\", total_id_dist)\n            tf.compat.v1.summary.histogram(\"Alltoall_emb_dist\", total_emb_dist)\n          if self._index == 0:\n            min_idx = tf.math.argmin(shard_sizes)\n            max_idx = tf.math.argmax(shard_sizes)\n            min_idx_size = tf.reshape(\n                tf.slice(shard_sizes, tf.reshape(min_idx, [-1]), [1]), [])\n            max_idx_size = tf.reshape(\n                tf.slice(shard_sizes, tf.reshape(max_idx, [-1]), [1]), [])\n            with tf.device(\"/CPU:0\"):\n              tf.compat.v1.summary.histogram(\"Alltoall_id_splits\", shard_sizes)\n              tf.compat.v1.summary.scalar(\"Alltoall_id_sizes\",\n                                          total_alltoall_id_size)\n              tf.compat.v1.summary.scalar(\"Alltoall_id_min_idx\", min_idx)\n              tf.compat.v1.summary.scalar(\"Alltoall_id_max_idx\", max_idx)\n              tf.compat.v1.summary.scalar(\"Alltoall_id_min_size\", min_idx_size)\n              tf.compat.v1.summary.scalar(\"Alltoall_id_max_size\", max_idx_size)\n            sent_grad_split_size = tf.slice(sent_grad_split_size, [1],\n                                            [self._shard_num - 1])\n            total_alltoall_grad_size = tf.reduce_sum(sent_grad_split_size)\n            with tf.device(\"/CPU:0\"):\n              tf.compat.v1.summary.histogram(\"Alltoall_grad_splits\",\n                                             sent_grad_split_size)\n              tf.compat.v1.summary.scalar(\"Alltoall_grad_sizes\",\n                                          total_alltoall_grad_size)\n            min_emb = tf.math.argmin(recv_emb_splits)\n            max_emb = tf.math.argmax(recv_emb_splits)\n            min_emb_size = tf.reshape(\n                tf.slice(recv_emb_splits, tf.reshape(min_emb, [-1]), [1]), [])\n            max_emb_size = tf.reshape(\n                tf.slice(recv_emb_splits, tf.reshape(max_emb, [-1]), [1]), [])\n            with tf.device(\"/CPU:0\"):\n              tf.compat.v1.summary.histogram(\"Alltoall_emb_splits\",\n                                             tf.identity(recv_emb_splits))\n              tf.compat.v1.summary.scalar(\"Alltoall_emb_sizes\",\n                                          tf.identity(total_alltoall_emb_size))\n              tf.compat.v1.summary.scalar(\"Alltoall_emb_min_idx\", min_emb)\n              tf.compat.v1.summary.scalar(\"Alltoall_emb_max_idx\", max_emb)\n              tf.compat.v1.summary.scalar(\"Alltoall_emb_min_size\", min_emb_size)\n              tf.compat.v1.summary.scalar(\"Alltoall_emb_max_size\", max_emb_size)\n      elif enable_hvd:\n        if enable_hvd_bwd_g2g:\n          logging.info('Enabled hvd for bwd embed alltoall g2g')\n          with tf.device(\"/device:GPU:0\"):\n            grad_flat_t = hvd.alltoall(grad_flat,\n                                       recv_emb_splits,\n                                       name='hvd_bwd_a2a_g2g')\n            #with tf.device(\"/device:CPU:0\"):\n            #  grad_flat_t = tf.identity(grad_flat_t)\n        else:\n          logging.info('Enabled hvd for bwd embed alltoall')\n          grad_flat_t = hvd.alltoall(grad_flat, recv_emb_splits)\n      else:\n        grad_flat_t = grad_flat\n\n      auxiliary_bundle[\"__sharding_sparse_fids__grad_flat_t\"] = grad_flat_t\n      auxiliary_bundle.pop(\"__sharding_sparse_fids__shard_sizes\")\n\n      deq_auxiliary_bundle, q = enqueue_dicts_with_queue_return(\n          auxiliary_bundle,\n          capacity=int(self._queue_configs.get(\"enable_pipelined_bwda2a\", 0)),\n          queue_name=\"queue_fusedEmbGradA2A_to_sparseOptimize\")\n      if q:\n        self.add_queue_hook(EnqueueHook(q))\n      auxiliary_bundle.update(deq_auxiliary_bundle)\n\n      with tf.device(\"/GPU:0\"):\n        updated_table = self._table.fused_apply_gradient(\n            auxiliary_bundle.pop(\"__sharding_sparse_fids__id_flat_t\"),\n            auxiliary_bundle.pop(\"__sharding_sparse_fids__indices\"),\n            auxiliary_bundle.pop(\"__sharding_sparse_fids__id_size_flat_t\"),\n            auxiliary_bundle.pop(\"__sharding_sparse_fids__grad_flat_t\"),\n            auxiliary_bundle.pop(\"__sharding_sparse_fids__id_offsets\"),\n            auxiliary_bundle.pop(\"__sharding_sparse_fids__emb_offsets\"),\n            auxiliary_bundle.pop(\"__sharding_sparse_fids__global_step\"),\n            auxiliary_bundle.pop(\"__sharding_sparse_fids__req_time\"),\n            self._shard_num)\n\n      update_op = self._copy_with_new_table_gpu(updated_table)\n      # TODO(zouxuan): add better tests to test the async optimize.\n      if async_optimize_queue:\n        self.add_queue_hook(\n            AsyncPushHook(async_optimize_queue, update_op.as_op()))\n        self._dependency_ops.append(async_optimize_queue.enqueue_op)\n        # return self essentially means to call dependency_ops\n        return self.as_op()\n      else:\n        return update_op.as_op()\n\n  def _copy_with_new_table(self,\n                           new_tables: List[tf.Tensor]) -> PartitionedHashTable:\n    copied = copy.copy(self)\n    copied._tables = new_tables\n    return copied\n\n  def _copy_with_new_table_gpu(self,\n                               new_table: tf.Tensor) -> PartitionedHashTable:\n    copied = copy.copy(self)\n    copied._dependency_ops = copy.copy(self._dependency_ops)\n    copied._table = new_table\n    return copied\n\n  def _native_hash_table_update(\n      self, method_name: str, name_scope: str,\n      update_data: AssignData) -> PartitionedHashTable:\n    with tf.name_scope(name_scope):\n      sharded_slot_to_id_and_value: Dict[int, Dict[str, Tuple[\n          tf.Tensor, tf.Tensor]]] = collections.defaultdict(dict)\n      for slot, (id, value) in update_data.items():\n        index = tf.math.floormod(id, self._num_ps)\n        split_ids = distribution_ops.split_by_indices(index, id, self._num_ps)\n        split_values = distribution_ops.split_by_indices(\n            index, value, self._num_ps)\n        for i in range(self._num_ps):\n          sharded_slot_to_id_and_value[i][slot] = (split_ids[i],\n                                                   split_values[i])\n      new_tables = []\n      for i in range(self._num_ps):\n        new_tables.append(\n            getattr(self._tables[i],\n                    method_name)(sharded_slot_to_id_and_value[i]))\n\n      return self._copy_with_new_table(new_tables)\n\n  def _update(self, method_name: str, name_scope: str,\n              update_data: AssignData) -> PartitionedHashTable:\n    if self._enable_gpu_emb:\n      raise NotImplementedError\n    with tf.name_scope(name_scope):\n      new_tables = []\n      for i in range(self._num_ps):\n        new_tables.append(getattr(self._tables[i], method_name)(update_data[i]))\n      return self._copy_with_new_table(new_tables)\n\n  def assign(self, data: AssignData) -> PartitionedHashTable:\n    if self._enable_gpu_emb:\n      raise NotImplementedError\n    if self._use_native_multi_hash_table:\n      return self._update(\"assign\", \"dmtht_a\", data)\n    else:\n      return self._update(\"assign\", \"pht_assign\", data)\n\n  def assign_add(self, data: AssignData) -> PartitionedHashTable:\n    if self._enable_gpu_emb:\n      raise NotImplementedError\n    if self._use_native_multi_hash_table:\n      return self._update(\"assign_add\", \"dmtht_aa\", data)\n    else:\n      return self._update(\"assign_add\", \"pht_assign_add\", data)\n\n  def flatten_layout(\n      self, nested: Dict[str, Union[tf.Tensor,\n                                    List[tf.Tensor]]]) -> List[tf.Tensor]:\n    result = []\n    for name in sorted(self._feature_configs.out_configs):\n      value = nested[name]\n      if isinstance(value, (list, tuple)):\n        assert all(isinstance(v, tf.Tensor) for v in value)\n        result.extend(value)\n      else:\n        assert isinstance(value, tf.Tensor)\n        result.append(value)\n    return result\n\n  def nest_layout(\n      self,\n      tensors: List[tf.Tensor]) -> Dict[str, Union[tf.Tensor, List[tf.Tensor]]]:\n    offset, result = 0, {}\n    for name in sorted(self._feature_configs.out_configs):\n      conf = self._feature_configs.out_configs[name]\n      if conf.out_type == OutType.NONE:\n        sub_list = []\n        for _ in range(len(conf.slice_configs)):\n          sub_list.append(tensors[offset])\n          offset += 1\n        result[name] = sub_list\n      else:\n        result[name] = tensors[offset]\n        offset += 1\n\n    return result\n\n  def add_queue_hook(self, hook):\n    # Allow pipelined graph execution.\n    if not getattr(self, \"_local_queue_hooks\", None):\n      self._local_queue_hooks = []\n    self._local_queue_hooks.append(hook)\n\n  def get_queue_hooks(self):\n    hooks = copy.copy(getattr(self, \"_local_queue_hooks\", []))\n    if getattr(self, \"_tables\", None):\n      hooks.extend(\n          itertools.chain.from_iterable(\n              [t.get_queue_hooks() for t in self._tables]))\n    return hooks\n"
  },
  {
    "path": "monolith/native_training/distributed_ps_benchmark.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport time\nimport os\nimport shutil\nfrom absl import flags\nimport tensorflow as tf\nfrom tensorflow.core.protobuf import cluster_pb2, config_pb2\n\nfrom monolith.native_training import (distributed_ps, hash_filter_ops, \\\n                                      hash_table_ops, utils)\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\nPROFILE = False\n\n\ndef _generate_config(servers, job_name=utils.PS_JOB_NAME):\n  \"\"\"Generates a config based on servers\"\"\"\n  cluster_def = cluster_pb2.ClusterDef()\n  job = cluster_def.job.add()\n  job.name = job_name\n  for i, server in enumerate(servers):\n    job.tasks[i] = server.target[len('grpc://'):]\n  return config_pb2.ConfigProto(cluster_def=cluster_def)\n\n\ndef _get_vocab_hash_table_factory(dim: int):\n\n  def factory(name_suffix: str, hash_filter: tf.Tensor):\n    config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n    config.cuckoo.SetInParent()\n    segment = config.entry_config.segments.add()\n    segment.dim_size = dim\n    segment.opt_config.sgd.learning_rate = 1.0\n    segment.init_config.zeros.SetInParent()\n    return hash_table_ops.hash_table_from_config(config=config,\n                                                 hash_filter=hash_filter,\n                                                 name_suffix=name_suffix)\n\n  return factory\n\n\nclass DistributedHashTableTest(tf.test.TestCase):\n\n  def lookup(self, enable_dedup, real_run=True):\n    ps_num = 10\n    servers = [\n        tf.distribute.Server.create_local_server() for _ in range(ps_num)\n    ]\n    server0 = servers[0]\n    num_elements, dim = 1000000, 16\n    config = _generate_config(servers)\n    if PROFILE and real_run:\n      log_dir = \"/tmp/distributed_ps_benchmark/lookup{}\".format(\n          \"_dedup\" if enable_dedup else \"\")\n      if os.path.exists(log_dir):\n        shutil.rmtree(log_dir)\n      tf.profiler.experimental.start(log_dir)\n    with tf.compat.v1.Session(server0.target, config=config) as sess:\n      hash_filters = hash_filter_ops.create_hash_filters(ps_num, False)\n      hash_table = distributed_ps.DistributedHashTable(\n          ps_num, hash_filters, _get_vocab_hash_table_factory(dim))\n      hash_table = hash_table.assign_add(\n          tf.constant([x for x in range(num_elements)], dtype=tf.int64),\n          tf.constant([[x for _ in range(dim)] for x in range(num_elements)],\n                      dtype=tf.float32))\n      start = time.time()\n      if real_run:\n        values = hash_table.lookup(tf.constant(\n            [x // 2 for x in range(num_elements)], dtype=tf.int64),\n                                   use_multi_threads=True,\n                                   enable_dedup=enable_dedup)\n        values = sess.run(values)\n        print(\"wall time(MT) enable_dedup={}: cost {}\".format(\n            str(enable_dedup),\n            time.time() - start))\n        self.assertAllEqual(\n            values, [[x // 2 for _ in range(dim)] for x in range(num_elements)])\n      else:\n        sess.run(hash_table.as_op())\n        print(\"wall time(overhead): cost {}\".format(time.time() - start))\n    if PROFILE and real_run:\n      tf.profiler.experimental.stop()\n\n  def apply_gradients(self, real_run=True):\n    ps_num = 10\n    servers = [\n        tf.distribute.Server.create_local_server() for _ in range(ps_num)\n    ]\n    server0 = servers[0]\n    num_elements, dim = 1000000, 16\n    config = _generate_config(servers)\n    if PROFILE and real_run:\n      log_dir = \"/tmp/distributed_ps_benchmark/apply_gradients\"\n      if os.path.exists(log_dir):\n        shutil.rmtree(log_dir)\n      tf.profiler.experimental.start(log_dir)\n    with tf.compat.v1.Session(server0.target, config=config) as sess:\n      hash_filters = hash_filter_ops.create_hash_filters(ps_num, False)\n      hash_table = distributed_ps.DistributedHashTable(\n          ps_num, hash_filters, _get_vocab_hash_table_factory(dim))\n      hash_table = hash_table.assign_add(\n          tf.constant([x for x in range(num_elements)], dtype=tf.int64),\n          tf.constant([[1 for _ in range(dim)] for _ in range(num_elements)],\n                      dtype=tf.float32))\n      embeddings = hash_table.lookup(tf.constant(\n          [x // 2 for x in range(num_elements)], dtype=tf.int64),\n                                     use_multi_threads=True,\n                                     enable_dedup=True)\n      loss = tf.multiply(0.3, embeddings)\n      grads = tf.gradients(loss, embeddings)\n      start = time.time()\n      if real_run:\n        hash_table = hash_table.apply_gradients(\n            tf.constant([x // 2 for x in range(num_elements)], dtype=tf.int64),\n            grads[0])\n        if PROFILE:\n          sess.run(hash_table.as_op())\n        else:\n          values = hash_table.lookup(tf.constant(\n              [x // 2 for x in range(num_elements)], dtype=tf.int64),\n                                     use_multi_threads=True,\n                                     enable_dedup=True)\n          values = sess.run(values)\n          self.assertAllClose(\n              values, [[0.4 for _ in range(dim)] for x in range(num_elements)])\n        print(\"wall time(MT): cost {}\".format(time.time() - start))\n      else:\n        grads = sess.run(grads[0])\n        if not PROFILE:\n          self.assertAllClose(\n              grads, [[0.3 for _ in range(dim)] for x in range(num_elements)])\n        print(\"wall time(overhead): cost {}\".format(time.time() - start))\n    if PROFILE and real_run:\n      tf.profiler.experimental.stop()\n\n  def test_lookup_overhead(self):\n    self.lookup(False, False)\n\n  def test_lookup(self):\n    self.lookup(False)\n\n  def test_lookup_dedup(self):\n    self.lookup(True)\n\n  def test_apply_gradients_overhead(self):\n    self.apply_gradients(False)\n\n  def test_apply_gradients(self):\n    self.apply_gradients(True)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distributed_ps_factory.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Several methods to create hash tables.\"\"\"\nfrom typing import Dict, Iterable, List, Tuple\n\nimport tensorflow as tf\n\nfrom idl.matrix.proto.example_pb2 import OutConfig\nfrom monolith.native_training import distributed_ps\nfrom monolith.native_training import distributed_ps_sync\nfrom monolith.native_training import entry\nfrom monolith.native_training import hash_filter_ops\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import multi_type_hash_table\nfrom monolith.native_training import multi_hash_table_ops\nimport monolith.native_training.embedding_combiners as embedding_combiners\n\n\nclass MultiHashTableFactory:\n\n  def __init__(self, hash_filters, sync_clients):\n    self._cc_dict = {}\n    self.hash_filters = hash_filters\n    self.sync_clients = sync_clients\n\n  def __call__(self, idx: int, slot_to_config):\n    k = id(slot_to_config)\n    cc = self._cc_dict.get(k, None)\n    if cc is None:\n      cc = multi_hash_table_ops.convert_to_cached_config(slot_to_config)\n      self._cc_dict[k] = cc\n    return multi_hash_table_ops.MultiHashTable.from_cached_config(\n        cc=cc,\n        hash_filter=self.hash_filters[idx],\n        sync_client=self.sync_clients[idx],\n        name_suffix=str(idx))\n\n\ndef create_in_worker_multi_type_hash_table(\n    shard_num: int,\n    slot_to_config: Dict[str, entry.HashTableConfigInstance],\n    hash_filter: tf.Tensor,\n    sync_client: tf.Tensor = None,\n    queue_configs: Dict[str, int] = None,\n):\n  \"\"\"\n  Creates a in worker multi-type hash table factory.\n  Args:\n  shard_num: the number of shards for distributing hash tables.\n  \"\"\"\n\n  # The logic here is\n  # merged_slots -> distributed_fused_multitype_table -> alltoall -> hash_table\n  def distributed_multi_type_table_factory(merged_slot_to_config):\n\n    def multi_type_table_factory(idx):\n\n      def table_factory(name_suffix, config):\n        return hash_table_ops.hash_table_from_config(\n            config=config,\n            hash_filter=hash_filter,\n            name_suffix=\"_\".join([name_suffix, str(idx)]),\n            sync_client=sync_client)\n\n      return multi_type_hash_table.MultiTypeHashTable(merged_slot_to_config,\n                                                      table_factory)\n\n    return distributed_ps_sync.DistributedMultiTypeHashTableMpi(\n        shard_num, multi_type_table_factory, queue_configs)\n\n  return multi_type_hash_table.MergedMultiTypeHashTable(\n      slot_to_config, distributed_multi_type_table_factory)\n\n\ndef create_multi_type_hash_table(\n    num_ps: int,\n    slot_to_config: Dict[str, entry.HashTableConfigInstance],\n    hash_filters: List[tf.Tensor],\n    sync_clients: List[tf.Tensor] = None,\n    reduce_network_packets: bool = False,\n    max_rpc_deadline_millis: int = 30,\n):\n  \"\"\"Create a distributed multi type hash table.\n  Args:\n  reduce_network_packets - if True, it will compact all tensors locally so ps will get less load.\n  Useful when there are a lot of workers.\n  \"\"\"\n  if num_ps and sync_clients:\n    assert num_ps == len(\n        sync_clients\n    ), \"Number of PS should be equal to number of sync clients, while got {} vs {}\".format(\n        num_ps, len(sync_clients))\n  if not sync_clients:\n    sync_clients = [None] * max(num_ps, 1)\n\n  if num_ps == 0:\n\n    def factory(name_suffix, config):\n      return hash_table_ops.hash_table_from_config(config,\n                                                   hash_filter=hash_filters[0],\n                                                   name_suffix=name_suffix,\n                                                   sync_client=sync_clients[0])\n\n    def multi_type_factory(merged_slot_to_config):\n      return multi_type_hash_table.MultiTypeHashTable(merged_slot_to_config,\n                                                      factory)\n\n    return multi_type_hash_table.MergedMultiTypeHashTable(\n        slot_to_config, multi_type_factory)\n  elif not reduce_network_packets:\n    # The logic here is\n    # dedup_slots -> multi hash table -> distributed_hash_table -> hash_table\n    # |                          worker                         |     ps     |\n    def multi_type_factory(merged_slot_to_config):\n\n      def distributed_factory(name_suffix, config):\n\n        def factory(idx, config_on_ps):\n          return hash_table_ops.hash_table_from_config(\n              config_on_ps,\n              hash_filter=hash_filters[idx],\n              name_suffix=\"_\".join([name_suffix, str(idx)]),\n              sync_client=sync_clients[idx])\n\n        return distributed_ps.DistributedHashTable(num_ps, config, factory)\n\n      return multi_type_hash_table.MultiTypeHashTable(merged_slot_to_config,\n                                                      distributed_factory)\n\n    return multi_type_hash_table.MergedMultiTypeHashTable(\n        slot_to_config, multi_type_factory)\n  else:\n    # The logic here is\n    # dedup_slots -> distributed multi hash table -> multi hash table -> hash table\n    # |                worker                     |              ps                |\n    def distributed_multi_type_factory(merged_slot_to_config):\n\n      def multi_type_factory(idx: int, slot_to_config_on_ps):\n\n        def factory(name_suffix, config):\n          return hash_table_ops.hash_table_from_config(\n              config,\n              hash_filter=hash_filters[idx],\n              name_suffix=\"_\".join([name_suffix, str(idx)]),\n              sync_client=sync_clients[idx])\n\n        return multi_type_hash_table.MultiTypeHashTable(slot_to_config_on_ps,\n                                                        factory)\n\n      return distributed_ps.DistributedMultiTypeHashTable(\n          num_ps,\n          merged_slot_to_config,\n          multi_type_factory,\n          max_rpc_deadline_millis=max_rpc_deadline_millis)\n\n    return multi_type_hash_table.MergedMultiTypeHashTable(\n        slot_to_config, distributed_multi_type_factory)\n\n\ndef create_native_multi_hash_table(\n    num_ps: int,\n    slot_to_config: Dict[str, entry.HashTableConfigInstance],\n    hash_filters: List[tf.Tensor],\n    sync_clients: List[tf.Tensor] = None,\n    max_rpc_deadline_millis: int = 30,\n):\n  \"\"\"Create a distributed native multi hash table.\"\"\"\n  if num_ps and sync_clients:\n    assert num_ps == len(\n        sync_clients\n    ), \"Number of PS should be equal to number of sync clients, while got {} vs {}\".format(\n        num_ps, len(sync_clients))\n  if not sync_clients:\n    sync_clients = [None] * max(num_ps, 1)\n\n  if num_ps == 0:\n    return multi_hash_table_ops.MultiHashTable.from_configs(\n        configs=slot_to_config,\n        hash_filter=hash_filters[0],\n        sync_client=sync_clients[0])\n  else:\n    # The logic here is\n    # slots -> distributed multi hash table -> multi hash table\n    # |              worker                 |       ps        |\n    return distributed_ps.DistributedMultiTypeHashTable(\n        num_ps,\n        slot_to_config,\n        MultiHashTableFactory(hash_filters, sync_clients),\n        max_rpc_deadline_millis=max_rpc_deadline_millis)\n\n\ndef create_in_worker_native_multi_hash_table(\n    shard_num: int,\n    slot_to_config: Dict[str, entry.HashTableConfigInstance],\n    hash_filter: tf.Tensor,\n    sync_client: tf.Tensor = None,\n    queue_configs: Dict[str, int] = None,\n):\n  # The logic here is\n  # DistributedMultiTypeHashTableMpi -> alltoall -> multi_hash_table\n  def table_factory(idx):\n    return multi_hash_table_ops.MultiHashTable.from_configs(\n        configs=slot_to_config,\n        hash_filter=hash_filter,\n        sync_client=sync_client,\n        name_suffix=str(idx))\n\n  return distributed_ps_sync.DistributedMultiTypeHashTableMpi(\n      shard_num, table_factory, queue_configs)\n\n\ndef create_partitioned_hash_table(\n    num_ps: int,\n    use_native_multi_hash_table: bool,\n    max_rpc_deadline_millis: int = 30,\n    hash_filters: List[tf.Tensor] = None,\n    sync_clients: List[tf.Tensor] = None,\n    enable_gpu_emb: bool = False,\n    queue_configs: Dict[str, int] = None,\n) -> distributed_ps.PartitionedHashTable:\n  num_ps_tmp = num_ps if num_ps > 0 else 1\n  if hash_filters is None:\n    hash_filters = [None] * num_ps_tmp\n  if sync_clients is None:\n    sync_clients = [None] * num_ps_tmp\n\n  if use_native_multi_hash_table:\n    # assert enable_gpu_emb == False, \"gpu_emb not imple native_multi_hash_table\"\n    multi_type_factory = MultiHashTableFactory(hash_filters, sync_clients)\n  else:\n\n    def multi_type_factory(idx: int, slot_to_config_on_ps):\n\n      def factory(name_suffix, config):\n        name_suffix = name_suffix if num_ps == 0 else \"_\".join(\n            [name_suffix, str(idx)])\n        return hash_table_ops.hash_table_from_config(\n            config,\n            hash_filter=hash_filters[idx],\n            name_suffix=name_suffix,\n            sync_client=sync_clients[idx])\n\n      return multi_type_hash_table.MultiTypeHashTable(slot_to_config_on_ps,\n                                                      factory)\n\n  return distributed_ps.PartitionedHashTable(num_ps,\n                                             multi_type_factory,\n                                             use_native_multi_hash_table,\n                                             max_rpc_deadline_millis,\n                                             queue_configs=queue_configs)\n"
  },
  {
    "path": "monolith/native_training/distributed_ps_factory_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nos.environ[\"MONOLITH_WITH_HOROVOD\"] = \"True\"\n\nimport tensorflow as tf\n\nfrom monolith.native_training import distributed_ps_factory\nfrom monolith.native_training import hash_filter_ops\nfrom monolith.native_training import test_utils\n\nimport horovod.tensorflow as hvd\n\n\ndef _get_test_slot_to_config():\n  config = test_utils.generate_test_hash_table_config(4, learning_rate=0.1)\n  return {\n      \"1\": config,\n      \"2\": config,\n  }\n\n\ndef _get_test_hash_filters(num):\n  return hash_filter_ops.create_hash_filters(num, False)\n\n\nclass FactoryTest(tf.test.TestCase):\n  # Since factory itself is very difficult to test. Here we just perform grammar check.\n\n  def test_create_in_worker_multi_type_hash_table(self):\n    hvd.init()\n    distributed_ps_factory.create_in_worker_multi_type_hash_table(\n        1, _get_test_slot_to_config(),\n        _get_test_hash_filters(0)[0])\n\n  def test_create_in_worker_multi_type_hash_table_with_reduced_alltoall(self):\n    hvd.init()\n    distributed_ps_factory.create_in_worker_multi_type_hash_table(\n        1, _get_test_slot_to_config(),\n        _get_test_hash_filters(0)[0])\n\n  def test_create_multi_type_hash_table_0_ps(self):\n    distributed_ps_factory.create_multi_type_hash_table(\n        0, _get_test_slot_to_config(), _get_test_hash_filters(0))\n\n  def test_create_multi_type_hash_table_2_ps(self):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.compat.v1.Session(servers[0].target, config=config):\n      distributed_ps_factory.create_multi_type_hash_table(\n          2, _get_test_slot_to_config(), _get_test_hash_filters(2))\n\n  def test_create_multi_type_hash_table_2_ps_with_reduced_packets(self):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.compat.v1.Session(servers[0].target, config=config):\n      distributed_ps_factory.create_multi_type_hash_table(\n          2,\n          _get_test_slot_to_config(),\n          _get_test_hash_filters(2),\n          reduce_network_packets=True)\n\n  def test_create_native_multi_hash_table_0_ps(self):\n    distributed_ps_factory.create_native_multi_hash_table(\n        0, _get_test_slot_to_config(), _get_test_hash_filters(0))\n\n  def test_create_native_multi_hash_table_2_ps(self):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.compat.v1.Session(servers[0].target, config=config):\n      distributed_ps_factory.create_native_multi_hash_table(\n          2, _get_test_slot_to_config(), _get_test_hash_filters(2))\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_v2_behavior()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distributed_ps_sync.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom __future__ import annotations\n\nimport copy\nimport collections\nimport os\nfrom typing import Callable, Dict, Tuple, List, Union\n\nfrom absl import flags, logging\nimport tensorflow as tf\n\nfrom monolith.native_training import multi_type_hash_table\nfrom monolith.native_training import multi_hash_table_ops\nfrom monolith.native_training import distribution_ops\nfrom monolith.native_training.prefetch_queue import \\\n    enqueue_dicts_with_queue_return, AsyncPushHook, EnqueueHook\nfrom monolith.native_training import feature_utils\n\nenable_hvd = os.getenv(\"MONOLITH_WITH_HOROVOD\")\nenable_custom_optimized_hvd = os.getenv(\"MONOLITH_WITH_OPTIMIZED_HOROVOD\")\nif enable_hvd != None:\n  import horovod.tensorflow as hvd\n  from horovod.tensorflow.compression import FP16Compressor\n\nenable_hvd_fid_g2g = int(os.getenv(\"MONOLITH_WITH_HOROVOD_FID_G2G\", 1))\nenable_hvd_fwd_g2g = int(os.getenv(\"MONOLITH_WITH_HOROVOD_FWD_G2G\", 1))\nenable_hvd_bwd_g2g = int(os.getenv(\"MONOLITH_WITH_HOROVOD_BWD_G2G\", 1))\nenable_bps = int(os.getenv(\"MONOLITH_WITH_BYTEPS\", \"0\"))\nenable_bps_fid = int(os.getenv(\"MONOLITH_WITH_BYTEPS_FID\", \"1\"))\nenable_bps_fwd = int(os.getenv(\"MONOLITH_WITH_BYTEPS_FWD\", \"1\"))\nenable_bps_bwd = int(os.getenv(\"MONOLITH_WITH_BYTEPS_BWD\", \"1\"))\n# MONOLITH_WITH_BYTEPS_BWD_CAST\n# 32: fp32 for embed grad (default)\n# 16: fp16 for embed grad\nenable_bps_bwd_cast = int(os.getenv(\"MONOLITH_WITH_BYTEPS_BWD_CAST\", \"32\"))\nenable_bps_bwd_fake_cast = int(\n    os.getenv(\"MONOLITH_WITH_BYTEPS_BWD_FAKE_CAST\", \"0\"))\n# enable forward alltoall with GDR\nenable_bps_fwd_gdr = int(os.getenv(\"MONOLITH_WITH_BYTEPS_FWD_GDR\", \"0\"))\nenable_bps_fwd_gdr_g2g = int(os.getenv(\"MONOLITH_WITH_BYTEPS_FWD_GDR_G2G\", \"0\"))\n# enable backward alltoall with GDR\nenable_bps_bwd_gdr = int(os.getenv(\"MONOLITH_WITH_BYTEPS_BWD_GDR\", \"0\"))\nenable_bps_bwd_gdr_g2g = int(os.getenv(\"MONOLITH_WITH_BYTEPS_BWD_GDR_G2G\", \"0\"))\n\nFLAGS = flags.FLAGS\nflags.DEFINE_bool(\"enable_alltoall_metrics\",\n                  default=False,\n                  help=(\"Whether to turn on alltoall detailed stats.\"))\nflags.DEFINE_string(\n    \"enable_alltoall_metrics_for_slot\",\n    default=None,\n    help=\"ID of the merged slot to summary alltoall stats. For example:\"\n    \"(af17bbdba2be72580bf5c8c43975078c for merged slot of fc_clk_ads_4d)\")\n\n\nclass DistributedMultiTypeHashTableMpi(\n    multi_type_hash_table.BaseMultiTypeHashTable):\n\n  def __init__(\n      self,\n      shard_num: int,\n      table_factory: Callable[[int], Union[\n          # when use_native_multi_hash_table=False\n          multi_type_hash_table.MultiTypeHashTable,\n          # when use_native_multi_hash_table=True\n          multi_hash_table_ops.MultiHashTable]],\n      queue_configs: Dict[str, int] = None):\n\n    self._shard_num = shard_num\n    if enable_bps:\n      import byteps.tensorflow as bps\n      assert bps.size() == self._shard_num\n      self._index = bps.rank()\n    else:\n      assert hvd.size() == self._shard_num\n      self._index = hvd.rank()\n    self._table = table_factory(self._index)\n    self._output_dims = self._table.get_table_dim_sizes()\n    self._queue_configs = queue_configs or {}\n    self._dependency_ops = []\n\n  def lookup(self,\n             slot_to_id: Dict[str, tf.Tensor],\n             auxiliary_bundle: Dict[str, tf.Tensor | tf.RaggedTensor],\n             early_reorder_indicies_res_pack=None) -> Dict[str, tf.Tensor]:\n    if enable_bps:\n      import byteps.tensorflow as bps\n    sorted_slot_keys = sorted(slot_to_id.keys())\n    slot_num = len(sorted_slot_keys)\n    assert early_reorder_indicies_res_pack is not None, \\\n      \"Support for reorder_fids_in_data_pipeline=False is dropped. Please set it to True\"\n    all_fids, shard_sizes, sharded_slot_sizes, emb_offset_sz, fused_embedding_offsets, req_time = \\\n      early_reorder_indicies_res_pack\n    if FLAGS.enable_alltoall_metrics:\n      slot_name = FLAGS.enable_alltoall_metrics_for_slot\n      if slot_name and slot_name in sorted_slot_keys:\n        m = sorted_slot_keys.index(slot_name)\n        with tf.device(\"/CPU:0\"):\n          tf.compat.v1.summary.scalar(\n              \"{}_size\".format(slot_name),\n              tf.reduce_sum(\n                  tf.gather(\n                      sharded_slot_sizes,\n                      [m + i * slot_num for i in range(self._shard_num)])))\n      with tf.device(\"/CPU:0\"):\n        tf.compat.v1.summary.scalar(\"all_fids_size\", tf.size(all_fids))\n        tf.compat.v1.summary.histogram(\"shard_sizes\", shard_sizes)\n        tf.compat.v1.summary.histogram(\"sharded_slot_sizes\", sharded_slot_sizes)\n    # We exchange the flattened IDs and their splits.\n    # M: num_of_ids,\n    # N: num_of_shards,\n    # K: num_of_merged_tables,\n    # E: num_of_total_embedding_dim.\n\n    # id_flat_t: [M], id_flat_split_t: [N]\n    # id_size_flat_t: [K*N], id_size_flat_split_t: [N]\n    if enable_bps and enable_bps_fid:\n      logging.info('Enabled BPS for fid alltoall')\n      id_flat_t, id_flat_split_t = bps.alltoall(all_fids,\n                                                splits=shard_sizes,\n                                                with_size=True,\n                                                name='fid_data')\n      # We also add the flat_t sizes.\n      id_size_flat_t = bps.alltoall(sharded_slot_sizes,\n                                    splits=[slot_num] * self._shard_num,\n                                    recv_splits=([slot_num] * self._shard_num),\n                                    name='fid_size')\n    elif enable_custom_optimized_hvd:\n      id_flat_t, id_flat_split_t = hvd.alltoall(all_fids,\n                                                splits=shard_sizes,\n                                                with_size=True)\n      # We also add the flat_t sizes.\n      id_size_flat_t = hvd.alltoall(sharded_slot_sizes,\n                                    splits=[slot_num] * self._shard_num,\n                                    recv_splits=[slot_num] * self._shard_num)\n    elif enable_hvd:\n      if enable_hvd_fid_g2g:\n        logging.info('Enabled hvd for fid alltoall g2g')\n        with tf.device(\"/device:GPU:0\"):\n          id_flat_t = hvd.alltoall(all_fids, splits=shard_sizes)\n          id_size_flat_t = hvd.alltoall(sharded_slot_sizes,\n                                        splits=[slot_num] * self._shard_num)\n      else:\n        id_flat_t = hvd.alltoall(all_fids, splits=shard_sizes)\n        id_size_flat_t = hvd.alltoall(sharded_slot_sizes,\n                                      splits=[slot_num] * self._shard_num)\n\n    auxiliary_bundle[\"shard_sizes\"] = shard_sizes\n    with tf.device(\"/device:GPU:0\"):\n      auxiliary_bundle[\"fused_embedding_offsets\"] = tf.split(\n          fused_embedding_offsets,\n          emb_offset_sz,\n          axis=0,\n          name=\"concat_emb_offsets_split\")\n    auxiliary_bundle[\"emb_offset_sz\"] = emb_offset_sz\n    auxiliary_bundle[\"id_flat_t\"] = id_flat_t\n    # Note: id_flat_split_t is not being used in later computation.\n    auxiliary_bundle[\"id_size_flat_t\"] = id_size_flat_t\n\n    # fused_embeddings: [E], fused_splits: [N]\n    # id_offsets: [K*N], emb_offsets: [K*N]\n    with tf.device(\"/GPU:0\"):\n      fused_embeddings, embedding_splits, id_offsets, emb_offsets, indices = \\\n          self._table.fused_lookup(id_flat_t, id_size_flat_t, self._shard_num, req_time)\n    if FLAGS.enable_alltoall_metrics:\n      with tf.device(\"/CPU:0\"):\n        tf.compat.v1.summary.histogram(\"fused_embedding_splits\",\n                                       embedding_splits)\n\n    auxiliary_bundle[\"fused_embeddings\"] = fused_embeddings\n    auxiliary_bundle[\"embedding_splits\"] = embedding_splits\n    auxiliary_bundle[\"id_offsets\"] = id_offsets\n    auxiliary_bundle[\"emb_offsets\"] = emb_offsets\n    auxiliary_bundle[\"indices\"] = indices\n    auxiliary_bundle[\"recv_emb_splits\"] = tf.reshape(\n        tf.matmul(\n            tf.reshape(sharded_slot_sizes, [self._shard_num, slot_num]),\n            tf.expand_dims(tf.constant(self._output_dims, dtype=tf.int32),\n                           -1)  # [slot_num, 1]\n        ),\n        [-1]  # flatten\n    )\n    auxiliary_bundle[\"recv_embeddings_size\"] = tf.reduce_sum(\n        auxiliary_bundle[\"recv_emb_splits\"])\n\n    auxiliary_bundle, queue = enqueue_dicts_with_queue_return(\n        auxiliary_bundle,\n        capacity=int(self._queue_configs.get(\"enable_pipelined_fwda2a\", 0)),\n        queue_name=\"queue_lookup_to_fusedEmbA2A\")\n    if queue:\n      self.add_queue_hook(EnqueueHook(queue))\n\n    fused_embeddings = auxiliary_bundle.pop(\"fused_embeddings\")\n    embedding_splits = auxiliary_bundle[\"embedding_splits\"]\n    recv_emb_splits = auxiliary_bundle[\"recv_emb_splits\"]\n    # recv_embeddings: [E'], recv_embedding_sizes: [N]\n    if enable_bps and enable_bps_fwd:\n      if enable_bps_fwd_gdr:\n        if enable_bps_fwd_gdr_g2g:\n          logging.info('Enabled BPS for fwd embed alltoall GDR (G2G)')\n          with tf.device(\"/device:GPU:0\"):\n            fused_embeddings_gpu = fused_embeddings\n          with tf.device(\"/device:GPU:0\"):\n            recv_embeddings = bps.alltoall(fused_embeddings_gpu,\n                                           embedding_splits,\n                                           recv_splits=recv_emb_splits,\n                                           name=\"fwd_alltoall_g2g\")\n        else:\n          logging.info('Enabled BPS for fwd embed alltoall GDR (C2G)')\n          with tf.device(\"/device:GPU:0\"):\n            recv_embeddings = bps.alltoall_cpu2gpu(fused_embeddings,\n                                                   embedding_splits,\n                                                   recv_splits=recv_emb_splits,\n                                                   name=\"fwd_alltoall_c2g\")\n      else:\n        logging.info('Enabled BPS for fwd embed alltoall')\n        recv_embeddings = bps.alltoall(fused_embeddings,\n                                       embedding_splits,\n                                       recv_splits=recv_emb_splits,\n                                       name=\"fwd_alltoall\")\n    elif enable_custom_optimized_hvd:\n      if enable_hvd_fwd_g2g:\n        logging.info('Enabled optimized hvd for fwd embed alltoall g2g')\n        with tf.device(\"/device:GPU:0\"):\n          recv_embeddings = hvd.alltoall(\n              fused_embeddings,\n              embedding_splits,\n              recv_splits=recv_emb_splits,\n          )\n      else:\n        logging.info('Enabled optimized hvd for fwd embed alltoall')\n        recv_embeddings = hvd.alltoall(\n            fused_embeddings,\n            embedding_splits,\n            recv_splits=recv_emb_splits,\n        )\n    elif enable_hvd:\n      if enable_hvd_fwd_g2g:\n        logging.info('Enabled hvd for fwd embed alltoall g2g')\n        with tf.device(\"/device:GPU:0\"):\n          recv_embeddings = hvd.alltoall(fused_embeddings,\n                                         embedding_splits,\n                                         name='hvd_fwd_a2a_g2g')\n      else:\n        logging.info('Enabled hvd for fwd embed alltoall')\n        recv_embeddings = hvd.alltoall(fused_embeddings,\n                                       embedding_splits,\n                                       name='hvd_fwd_a2a')\n\n    auxiliary_bundle[\"recv_embeddings\"] = recv_embeddings\n\n    with tf.device(\"/device:GPU:0\"):\n      # GPUQueue: Pass to GPU at Enqueue\n      auxiliary_bundle[\"recv_embeddings\"] = tf.identity(\n          auxiliary_bundle[\"recv_embeddings\"])\n\n    auxiliary_bundle, queue = enqueue_dicts_with_queue_return(\n        auxiliary_bundle,\n        capacity=int(self._queue_configs.get(\"embedding_prefetch_capacity\", 0)),\n        queue_name=\"queue_fusedEmbA2A_to_fusedGather\")\n    if queue:\n      self.add_queue_hook(EnqueueHook(queue))\n\n    # auxiliary_bundle includes all dequeued tensors, if a prefetch queue in-between.\n    recv_embeddings = auxiliary_bundle.pop(\"recv_embeddings\")\n    fused_embedding_offsets = auxiliary_bundle[\"fused_embedding_offsets\"]\n\n    with tf.device(\"/device:GPU:0\"):\n      outputs = distribution_ops.fused_gather_embeddings_by_input(\n          recv_embeddings, fused_embedding_offsets, self._output_dims)\n\n    # a.k.a merged_slot_to_embedding\n    slot_to_embedding = {k: outputs[i] for i, k in enumerate(sorted_slot_keys)}\n    return slot_to_embedding, auxiliary_bundle\n\n  # TODO(zouxuan): assign is broken.\n  def assign(\n      self, slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> DistributedMultiTypeHashTableMpi:\n    raise NotImplementedError\n\n  # TODO(zouxuan): assign_add is broken.\n  def assign_add(\n      self, slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> DistributedMultiTypeHashTableMpi:\n    raise NotImplementedError\n\n  def reinitialize(\n      self, slot: str,\n      ids: tf.Tensor) -> Tuple[DistributedMultiTypeHashTableMpi, tf.Tensor]:\n    raise NotImplementedError(\n        \"DistributedMultiTypeHashTableMpi dost not support reinitialize!\")\n\n  # Apply_gradients uses fused update.\n  def apply_gradients(self,\n                      slot_to_grad: Dict[str, tf.Tensor],\n                      auxiliary_bundle: Dict[str, tf.Tensor],\n                      global_step: tf.Tensor,\n                      req_time: tf.Tensor = None,\n                      scale: tf.Tensor = 1) -> DistributedMultiTypeHashTableMpi:\n\n    auxiliary_bundle['global_step'] = global_step\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    auxiliary_bundle[\"req_time\"] = req_time\n\n    sorted_slot_keys = sorted(slot_to_grad.keys())\n    sorted_grads = [slot_to_grad[k] for k in sorted_slot_keys]\n\n    recv_embeddings_size = auxiliary_bundle.pop(\"recv_embeddings_size\")\n    fused_embedding_offsets = auxiliary_bundle.pop(\"fused_embedding_offsets\")\n    # make this depend on fusion op before allreduce,\n    # so allreduce can be dispatched before alltoall\n    with tf.control_dependencies(feature_utils.control_ops):\n      with tf.device(\"/device:GPU:0\"):\n        grad_flat = distribution_ops.fused_gather_embeddings_by_input_gradient(\n            recv_embeddings_size, sorted_grads, fused_embedding_offsets,\n            self._output_dims, scale)\n\n    with tf.device(\"/device:GPU:0\"):\n      if enable_bps_bwd_cast == 16:\n        auxiliary_bundle['grad_flat'] = tf.cast(grad_flat, tf.float16)\n      else:\n        auxiliary_bundle['grad_flat'] = tf.identity(grad_flat)\n\n    # Here we add a queue to let the optimize stage non-blocking and\n    # interleaving at the next round of update.\n    auxiliary_bundle, async_optimize_queue = enqueue_dicts_with_queue_return(\n        auxiliary_bundle,\n        capacity=int(self._queue_configs.get(\"enable_async_optimize\", 0)),\n        queue_name=\"queue_fusedGatherGrad_to_fusedEmbGradA2A\")\n    grad_flat = auxiliary_bundle.pop(\"grad_flat\")\n    recv_emb_splits = auxiliary_bundle.pop(\"recv_emb_splits\")\n    embedding_splits = auxiliary_bundle.pop(\"embedding_splits\")\n\n    if enable_bps and enable_bps_bwd:\n      import byteps.tensorflow as bps\n      from byteps.tensorflow.compression import FP16Compressor as BPSFP16Compressor\n      if enable_bps_bwd_gdr:\n        with tf.device(\"/device:GPU:0\"), tf.name_scope(\"bps_bwd_alltoall\"):\n          if enable_bps_bwd_gdr_g2g:\n            logging.info('Enabled BPS for bwd embed alltoall GDR (G2G)')\n            bwd_tensor_name = \"bwd_alltoall_g2g\"\n            grad_flat_t = bps.alltoall(grad_flat,\n                                       recv_emb_splits,\n                                       recv_splits=embedding_splits,\n                                       name=bwd_tensor_name)\n            if enable_bps_bwd_cast == 16:\n              # do cast on GPU\n              if enable_bps_bwd_fake_cast:\n                grad_flat_t = grad_flat_t * 0.0\n              grad_flat_t = tf.cast(grad_flat_t, tf.float32)\n            with tf.device(\"/device:CPU:0\"):\n              grad_flat_t = tf.identity(grad_flat_t)\n          else:\n            logging.info('Enabled BPS for bwd embed alltoall GDR (G2C)')\n            bwd_tensor_name = \"bwd_alltoall_g2c\"\n            grad_flat_t = bps.alltoall_gpu2cpu(grad_flat,\n                                               recv_emb_splits,\n                                               recv_splits=embedding_splits,\n                                               name=bwd_tensor_name)\n            with tf.device(\"/device:CPU:0\"):\n              # grad_flat_t<tensor>.device = <tensor>._op.device (bps.alltoall_gpu2cpu as GPU op)\n              # However the tensor is on CPU, so we fixed the tensor placement info with an identity.\n              grad_flat_t = tf.identity(grad_flat_t)\n            if enable_bps_bwd_cast == 16:\n              if enable_bps_bwd_fake_cast:\n                grad_flat_t = grad_flat_t * 0.0\n              grad_flat_t = tf.cast(grad_flat_t, tf.float32)\n      else:\n        logging.info(\n            'Enabled BPS for bwd embed alltoall with cast optimization')\n        grad_flat_t = bps.alltoall(grad_flat,\n                                   recv_emb_splits,\n                                   recv_splits=embedding_splits,\n                                   name=\"bwd_alltoall\")\n        if enable_bps_bwd_cast == 16:\n          if enable_bps_bwd_fake_cast:\n            grad_flat_t = grad_flat_t * 0.0\n          grad_flat_t = tf.cast(grad_flat_t, tf.float32)\n\n      sent_grad_split_size = embedding_splits\n    elif enable_custom_optimized_hvd:\n      if enable_hvd_bwd_g2g:\n        logging.info('Enabled optimized hvd for bwd embed alltoall g2g')\n        with tf.device(\"/device:GPU:0\"):\n          grad_flat_t, sent_grad_split_size = hvd.alltoall(\n              grad_flat,\n              recv_emb_splits,\n              recv_splits=embedding_splits,\n              with_size=True,\n              compression=FP16Compressor)\n          with tf.device(\"/device:CPU:0\"):\n            grad_flat_t = tf.identity(grad_flat_t)\n      else:\n        logging.info('Enabled optimized hvd for bwd embed alltoall')\n        grad_flat_t, sent_grad_split_size = hvd.alltoall(\n            grad_flat,\n            recv_emb_splits,\n            recv_splits=embedding_splits,\n            with_size=True,\n            compression=FP16Compressor)\n      if FLAGS.enable_alltoall_metrics and (self._shard_num > 1):\n        # There is some issue with tf.compat.v1.summary on the horovod alltoall input,\n        # using output instead. They are almost equivalent.\n        shard_sizes = auxiliary_bundle[\"shard_sizes\"]\n        shard_sizes = tf.slice(shard_sizes, [1], [self._shard_num - 1])\n        total_alltoall_id_size = tf.reduce_sum(shard_sizes)\n        recv_emb_splits = tf.slice(tf.identity(recv_emb_splits), [1],\n                                   [self._shard_num - 1])\n        total_alltoall_emb_size = tf.reduce_sum(tf.identity(recv_emb_splits))\n        tmp_result = tf.reshape(total_alltoall_id_size, [1])\n        tmp_result2 = tf.reshape(total_alltoall_emb_size, [1])\n        total_id_dist = hvd.allgather(tmp_result)\n        total_emb_dist = hvd.allgather(tmp_result2)\n        with tf.device(\"/CPU:0\"):\n          tf.compat.v1.summary.histogram(\"Alltoall_id_dist\", total_id_dist)\n          tf.compat.v1.summary.histogram(\"Alltoall_emb_dist\", total_emb_dist)\n        if self._index == 0:\n          min_idx = tf.math.argmin(shard_sizes)\n          max_idx = tf.math.argmax(shard_sizes)\n          min_idx_size = tf.reshape(\n              tf.slice(shard_sizes, tf.reshape(min_idx, [-1]), [1]), [])\n          max_idx_size = tf.reshape(\n              tf.slice(shard_sizes, tf.reshape(max_idx, [-1]), [1]), [])\n          with tf.device(\"/CPU:0\"):\n            tf.compat.v1.summary.histogram(\"Alltoall_id_splits\", shard_sizes)\n            tf.compat.v1.summary.scalar(\"Alltoall_id_sizes\",\n                                        total_alltoall_id_size)\n            tf.compat.v1.summary.scalar(\"Alltoall_id_min_idx\", min_idx)\n            tf.compat.v1.summary.scalar(\"Alltoall_id_max_idx\", max_idx)\n            tf.compat.v1.summary.scalar(\"Alltoall_id_min_size\", min_idx_size)\n            tf.compat.v1.summary.scalar(\"Alltoall_id_max_size\", max_idx_size)\n          sent_grad_split_size = tf.slice(sent_grad_split_size, [1],\n                                          [self._shard_num - 1])\n          total_alltoall_grad_size = tf.reduce_sum(sent_grad_split_size)\n          with tf.device(\"/CPU:0\"):\n            tf.compat.v1.summary.histogram(\"Alltoall_grad_splits\",\n                                           sent_grad_split_size)\n            tf.compat.v1.summary.scalar(\"Alltoall_grad_sizes\",\n                                        total_alltoall_grad_size)\n          min_emb = tf.math.argmin(recv_emb_splits)\n          max_emb = tf.math.argmax(recv_emb_splits)\n          min_emb_size = tf.reshape(\n              tf.slice(recv_emb_splits, tf.reshape(min_emb, [-1]), [1]), [])\n          max_emb_size = tf.reshape(\n              tf.slice(recv_emb_splits, tf.reshape(max_emb, [-1]), [1]), [])\n          with tf.device(\"/CPU:0\"):\n            tf.compat.v1.summary.histogram(\"Alltoall_emb_splits\",\n                                           tf.identity(recv_emb_splits))\n            tf.compat.v1.summary.scalar(\"Alltoall_emb_sizes\",\n                                        tf.identity(total_alltoall_emb_size))\n            tf.compat.v1.summary.scalar(\"Alltoall_emb_min_idx\", min_emb)\n            tf.compat.v1.summary.scalar(\"Alltoall_emb_max_idx\", max_emb)\n            tf.compat.v1.summary.scalar(\"Alltoall_emb_min_size\", min_emb_size)\n            tf.compat.v1.summary.scalar(\"Alltoall_emb_max_size\", max_emb_size)\n    elif enable_hvd:\n      if enable_hvd_bwd_g2g:\n        logging.info('Enabled hvd for bwd embed alltoall g2g')\n        with tf.device(\"/device:GPU:0\"):\n          grad_flat_t = hvd.alltoall(grad_flat,\n                                     recv_emb_splits,\n                                     name='hvd_bwd_a2a_g2g')\n      else:\n        logging.info('Enabled hvd for bwd embed alltoall')\n        grad_flat_t = hvd.alltoall(grad_flat, recv_emb_splits)\n    else:\n      grad_flat_t = grad_flat\n\n    auxiliary_bundle[\"grad_flat_t\"] = grad_flat_t\n    auxiliary_bundle.pop(\"shard_sizes\")\n    auxiliary_bundle, q = enqueue_dicts_with_queue_return(\n        auxiliary_bundle,\n        capacity=int(self._queue_configs.get(\"enable_pipelined_bwda2a\", 0)),\n        queue_name=\"queue_fusedEmbGradA2A_to_sparseOptimize\")\n    if q:\n      self.add_queue_hook(EnqueueHook(q))\n\n    with tf.control_dependencies(feature_utils.dense_opt_ops):\n      with tf.device(\"/GPU:0\"):\n        updated_table = self._table.fused_apply_gradient(\n            auxiliary_bundle.pop(\"id_flat_t\"), auxiliary_bundle.pop(\"indices\"),\n            auxiliary_bundle.pop(\"id_size_flat_t\"),\n            auxiliary_bundle.pop(\"grad_flat_t\"),\n            auxiliary_bundle.pop(\"id_offsets\"),\n            auxiliary_bundle.pop(\"emb_offsets\"),\n            auxiliary_bundle.pop(\"global_step\"),\n            auxiliary_bundle.pop(\"req_time\"), self._shard_num)\n\n    update_op = self._copy_with_new_table(updated_table)\n    # TODO(zouxuan): add better tests to test the async optimize.\n    if async_optimize_queue:\n      self.add_queue_hook(AsyncPushHook(async_optimize_queue,\n                                        update_op.as_op()))\n      self._dependency_ops.append(async_optimize_queue.enqueue_op)\n      # return self essentially means to call dependency_ops\n      return self\n    else:\n      return update_op\n\n  def _update(self, method: str,\n              slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]],\n              *args, **kwargs):\n    raise NotImplementedError\n\n  def as_op(self, name=None):\n    with tf.control_dependencies(self._dependency_ops):\n      return self._table.as_op(name)\n\n  def get_table_dim_sizes(self):\n    return self._tables[0].get_table_dim_sizes()\n\n  def _copy_with_new_table(self,\n                           table: multi_type_hash_table.BaseMultiTypeHashTable):\n    copied = copy.copy(self)\n    copied._dependency_ops = copy.copy(self._dependency_ops)\n    copied._table = table\n    return copied\n"
  },
  {
    "path": "monolith/native_training/distributed_ps_sync_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nfrom absl.testing import parameterized\n\nos.environ[\"MONOLITH_WITH_HOROVOD\"] = \"True\"\n\nimport tensorflow as tf\n\nfrom monolith.native_training import distributed_ps\nfrom monolith.native_training import distribution_ops\nfrom monolith.native_training import distributed_ps_sync\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import learning_rate_functions\nfrom monolith.native_training import multi_type_hash_table\nfrom monolith.native_training import test_utils\nfrom monolith.native_training.multi_hash_table_ops import MultiHashTable\n\nimport horovod.tensorflow as hvd\n\n\ndef gen_test_configs():\n  return {\n      \"1\":\n          test_utils.generate_test_hash_table_config(1, learning_rate=1.0),\n      \"2\":\n          test_utils.generate_test_hash_table_config(\n              2,\n              learning_rate=learning_rate_functions.PolynomialDecay(\n                  initial_learning_rate=1.0,\n                  decay_steps=10,\n                  end_learning_rate=2.0))\n  }\n\n\ndef multi_type_table_factory(idx: int):\n\n  def table_factory(name_suffix: str, config):\n    return hash_table_ops.hash_table_from_config(config,\n                                                 name_suffix=name_suffix +\n                                                 str(idx))\n\n  return multi_type_hash_table.MultiTypeHashTable(gen_test_configs(),\n                                                  table_factory)\n\n\ndef native_multi_hash_table_factory(idx: int):\n  return MultiHashTable.from_configs(configs=gen_test_configs(),\n                                     name_suffix=str(idx))\n\n\nclass DistributedMultiTypeHashTableMpiTest(tf.test.TestCase,\n                                           parameterized.TestCase):\n\n  @parameterized.parameters([(False,)])\n  def testBasic(self, use_native_multi_hash_table):\n    table_factory = (native_multi_hash_table_factory if\n                     use_native_multi_hash_table else multi_type_table_factory)\n    hvd.init()\n    with self.session() as sess:\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      self.evaluate(tf.compat.v1.assign(global_step, 0))\n      table = distributed_ps_sync.DistributedMultiTypeHashTableMpi(\n          hvd.size(), table_factory)\n      slot_to_ids = {\n          \"1\": tf.constant([1, 1], dtype=tf.int64),\n          \"2\": tf.constant([2], dtype=tf.int64)\n      }\n      # First lookup, nothing exists, returns 0 simply.\n      reordred = distribution_ops.fused_reorder_by_indices(\n        [slot_to_ids[\"1\"], slot_to_ids[\"2\"]], 1, [1, 2])\n      reordred = (*reordred, None) # add timestamp\n      emb, auxiliary_bundle = table.lookup(slot_to_ids, {}, reordred)\n      emb_value = sess.run(emb)\n      self.assertAllClose(emb_value[\"1\"], [[0], [0]])\n      self.assertAllClose(emb_value[\"2\"], [[0, 0]])\n      updated_table = table.apply_gradients(\n          {\n              \"1\": tf.constant([[0.5], [0.5]], dtype=tf.float32),\n              \"2\": tf.constant([[0.5, 1.0]], dtype=tf.float32)\n          },\n          auxiliary_bundle=auxiliary_bundle,\n          global_step=tf.constant(0, dtype=tf.int64),\n          req_time=tf.constant(0, dtype=tf.int64))\n      emb, auxiliary_bundle = updated_table.lookup(slot_to_ids, {}, reordred)\n      emb_value = sess.run(emb)\n      sum_multiplier = hvd.size()\n      self.assertAllClose(emb_value[\"1\"],\n                          [[-1 * sum_multiplier], [-1 * sum_multiplier]])\n      self.assertAllClose(emb_value[\"2\"],\n                          [[-0.5 * sum_multiplier, -1.0 * sum_multiplier]])\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distributed_ps_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom collections import defaultdict\nimport math\nimport os\nimport copy\n\nos.environ[\"MONOLITH_WITH_HOROVOD\"] = \"1\"\nimport itertools\nfrom typing import Dict, List\nimport numpy as np\nfrom absl.testing import parameterized\nimport logging\nimport tensorflow as tf\nimport tensorflow.python.ops.resources as resources\nfrom tensorflow.python.framework import test_util\n\nfrom monolith.native_training import distributed_ps\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training.multi_hash_table_ops import MultiHashTable\nfrom monolith.native_training import learning_rate_functions\nfrom monolith.native_training import multi_type_hash_table\nfrom monolith.native_training import utils\nfrom monolith.native_training import test_utils\nfrom monolith.native_training.model_export import export_context\nfrom monolith.native_training import entry\nfrom idl.matrix.proto.example_pb2 import FeatureConfigs, FeatureConfig, PoolingType, OutType, OutConfig\nimport monolith.native_training.embedding_combiners as embedding_combiners\nfrom monolith.native_training.runtime.hash_table import embedding_hash_table_pb2\nfrom monolith.native_training.data.feature_utils import string_to_variant\nfrom idl.matrix.proto.example_pb2 import Example\nfrom monolith.native_training.data.parsers import ParserCtx, sharding_sparse_fids_with_context\n\n\ndef factory(idx: int, config):\n  return hash_table_ops.hash_table_from_config(config=config,\n                                               name_suffix=str(idx))\n\n\nclass DistributedHashTableTest(tf.test.TestCase):\n\n  def test_basic(self):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    table_config = test_utils.generate_test_hash_table_config(2)\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      hash_table = distributed_ps.DistributedHashTable(2, table_config, factory)\n      hash_table = hash_table.assign_add(\n          tf.constant([1, 2, 3], dtype=tf.int64),\n          tf.constant([[1, 1], [2, 2], [3, 3]], dtype=tf.float32))\n      values = hash_table.lookup(tf.constant([1, 2, 3], dtype=tf.int64))\n      values = sess.run(values)\n    self.assertAllEqual(values, [[1, 1], [2, 2], [3, 3]])\n\n  def test_assign(self):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    table_config = test_utils.generate_test_hash_table_config(2)\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      hash_table = distributed_ps.DistributedHashTable(2, table_config, factory)\n      hash_table = hash_table.assign(\n          tf.constant([1, 2, 3], dtype=tf.int64),\n          tf.constant([[1, 1], [2, 2], [3, 3]], dtype=tf.float32))\n      values1 = hash_table.lookup(tf.constant([1, 2, 3], dtype=tf.int64))\n\n      # Ensure the second assign happens after the first lookup\n      with tf.control_dependencies([values1]):\n        hash_table = hash_table.assign(\n            tf.constant([2, 3, 4], dtype=tf.int64),\n            tf.constant([[1, 1], [2, 2], [3, 3]], dtype=tf.float32))\n        values2 = hash_table.lookup(tf.constant([1, 2, 3, 4], dtype=tf.int64))\n      values1, values2 = sess.run([values1, values2])\n    self.assertAllEqual(values1, [[1, 1], [2, 2], [3, 3]])\n    self.assertAllEqual(values2, [[1, 1], [1, 1], [2, 2], [3, 3]])\n\n  def test_lookup_dedup(self):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    table_config = test_utils.generate_test_hash_table_config(2)\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      hash_table = distributed_ps.DistributedHashTable(2, table_config, factory)\n      hash_table = hash_table.assign_add(\n          tf.constant([1, 2, 3], dtype=tf.int64),\n          tf.constant([[1, 1], [2, 2], [3, 3]], dtype=tf.float32))\n      values = hash_table.lookup(tf.constant([1, 1, 3], dtype=tf.int64))\n      values = sess.run(values)\n    self.assertAllEqual(values, [[1, 1], [1, 1], [3, 3]])\n\n  def test_apply_gradients(self):\n    table_config = test_utils.generate_test_hash_table_config(1)\n    g = tf.Graph()\n    with g.as_default():\n      hash_table = distributed_ps.DistributedHashTable(2, table_config, factory)\n      ids = tf.constant([0, 1], dtype=tf.int64)\n      values = hash_table.lookup(ids)\n      loss = 2 * values\n      grads = tf.gradients(loss, values)\n      global_step = tf.constant(0, dtype=tf.int64)\n      hash_table = hash_table.apply_gradients(ids, grads[0], global_step)\n\n      new_values = hash_table.lookup(ids)\n\n    servers, config = test_utils.create_test_ps_cluster(2)\n\n    with tf.compat.v1.Session(servers[0].target, config=config,\n                              graph=g) as sess:\n      new_values = sess.run(new_values)\n    self.assertAllEqual(new_values, [[-2], [-2]])\n\n  def test_apply_gradients_with_learning_rate_function(self):\n    table_config = test_utils.generate_test_hash_table_config(\n        1,\n        learning_rate=learning_rate_functions.PolynomialDecay(\n            initial_learning_rate=1.0, decay_steps=10, end_learning_rate=2.0))\n\n    g = tf.Graph()\n    with g.as_default():\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      hash_table = distributed_ps.DistributedHashTable(2, table_config, factory)\n      ids = tf.constant([0, 1], dtype=tf.int64)\n      values = hash_table.lookup(ids)\n      loss = 2 * values\n      grads = tf.gradients(loss, values)\n      hash_table = hash_table.apply_gradients(ids, grads[0], global_step)\n\n      new_values = hash_table.lookup(ids)\n\n    servers, config = test_utils.create_test_ps_cluster(2)\n\n    with tf.compat.v1.Session(servers[0].target, config=config,\n                              graph=g) as sess:\n      resources.initialize_resources(resources.shared_resources()).run()\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      values_eval = sess.run(new_values)\n      self.assertAllEqual(values_eval, [[-2], [-2]])\n\n      self.evaluate(tf.compat.v1.assign_add(global_step, 1))\n      values_eval = sess.run(new_values)\n      self.assertAllClose(values_eval, [[-4.2], [-4.2]])\n\n  def test_apply_gradients_with_duplicates(self):\n    table_config = test_utils.generate_test_hash_table_config(1)\n    g = tf.Graph()\n    with g.as_default():\n      hash_table = distributed_ps.DistributedHashTable(2, table_config, factory)\n      ids = tf.constant([0, 3, 0, 1], dtype=tf.int64)\n      values = hash_table.lookup(ids)\n      loss = 2 * values\n      grads = tf.gradients(loss, values)\n      global_step = tf.constant(0, dtype=tf.int64)\n      hash_table = hash_table.apply_gradients(ids, grads[0], global_step)\n\n      new_values = hash_table.lookup(ids)\n\n    servers, config = test_utils.create_test_ps_cluster(2)\n\n    with tf.compat.v1.Session(servers[0].target, config=config,\n                              graph=g) as sess:\n      new_values = sess.run(new_values)\n    self.assertAllEqual(new_values, [[-4], [-2], [-4], [-2]])\n\n  def test_apply_gradients_with_different_ids(self):\n    table_config = test_utils.generate_test_hash_table_config(1)\n    g = tf.Graph()\n    with g.as_default():\n      hash_table = distributed_ps.DistributedHashTable(2, table_config, factory)\n      ids = tf.constant([1, 0], dtype=tf.int64)\n      bp_ids = tf.constant([1, 1], dtype=tf.int64)\n      values = hash_table.lookup(ids)\n      loss = -2 * values\n      grads = tf.gradients(loss, values)\n      global_step = tf.constant(0, dtype=tf.int64)\n      hash_table = hash_table.apply_gradients(bp_ids, grads[0], global_step)\n\n      new_values = hash_table.lookup(ids)\n\n    servers, config = test_utils.create_test_ps_cluster(2)\n\n    with tf.compat.v1.Session(servers[0].target, config=config,\n                              graph=g) as sess:\n      new_values = sess.run(new_values)\n    self.assertAllEqual(new_values, [[4], [0]])\n\n\ndef gen_multi_type_table_factory(global_name_prefix=\"\"):\n\n  def multi_type_table_factory(ps_num: int, slot_to_config_on_ps):\n\n    def table_factory(name_suffix: str, config):\n      return hash_table_ops.hash_table_from_config(\n          config, name_suffix=global_name_prefix + name_suffix + str(ps_num))\n\n    return multi_type_hash_table.MultiTypeHashTable(slot_to_config_on_ps,\n                                                    table_factory)\n\n  return multi_type_table_factory\n\n\ndef gen_native_multi_hash_table_factory(global_name_prefix=\"\"):\n\n  def native_multi_hash_table_factory(ps_num: int, slot_to_config):\n    return MultiHashTable.from_configs(configs=slot_to_config,\n                                       name_suffix=global_name_prefix +\n                                       str(ps_num))\n\n  return native_multi_hash_table_factory\n\n\nclass DistributedMultiTypeHashTableTest(tf.test.TestCase,\n                                        parameterized.TestCase):\n\n  @parameterized.parameters([(True,), (False,)])\n  def testBasic(self, use_native_multi_hash_table):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      slot_to_config = {\n          \"1\":\n              test_utils.generate_test_hash_table_config(1),\n          \"2\":\n              test_utils.generate_test_hash_table_config(\n                  2, learning_rate=lambda: 1.0)\n      }\n      table_factory = (gen_native_multi_hash_table_factory()\n                       if use_native_multi_hash_table else\n                       gen_multi_type_table_factory())\n      hash_table = distributed_ps.DistributedMultiTypeHashTable(\n          2, slot_to_config, table_factory)\n      ids1 = tf.constant([1, 2], dtype=tf.int64)\n      values1 = tf.constant([[-1], [-2]], dtype=tf.float32)\n      ids2 = tf.constant([3], dtype=tf.int64)\n      values2 = tf.constant([[-3, -3]], dtype=tf.float32)\n      updated_hash_table = hash_table.assign_add({\n          \"1\": (ids1, values1),\n          \"2\": (ids2, values2)\n      })\n      values = updated_hash_table.lookup({\"1\": ids1, \"2\": ids2})\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(tf.compat.v1.local_variables_initializer())\n      resources.initialize_resources(resources.shared_resources()).run()\n      values = sess.run(values)\n      self.assertAllEqual(values[\"1\"], [[-1], [-2]])\n      self.assertAllEqual(values[\"2\"], [[-3, -3]])\n      global_step = tf.constant(0, dtype=tf.int64)\n      updated_hash_table = hash_table.apply_gradients(\n          {\n              \"1\": (ids1, values1 / 2),\n              \"2\": (ids2, values2 / 2)\n          },\n          global_step,\n          req_time=tf.constant(0, dtype=tf.int64))\n      values = updated_hash_table.lookup({\"1\": ids1, \"2\": ids2})\n      values, _ = sess.run([values, updated_hash_table.as_op()])\n      self.assertAllEqual(values[\"1\"], [[-0.5], [-1]])\n      self.assertAllEqual(values[\"2\"], [[-1.5, -1.5]])\n\n  @parameterized.parameters([(True,), (False,)])\n  def test_assign_and_reinitialize(self, use_native_multi_hash_table):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      slot_to_config = {\n          \"1\": test_utils.generate_test_hash_table_config(1),\n          \"2\": test_utils.generate_test_hash_table_config(2)\n      }\n      table_factory = (gen_native_multi_hash_table_factory()\n                       if use_native_multi_hash_table else\n                       gen_multi_type_table_factory())\n      hash_table = distributed_ps.DistributedMultiTypeHashTable(\n          2, slot_to_config, table_factory)\n      ids1 = tf.constant([1, 2], dtype=tf.int64)\n      values1 = tf.constant([[-1], [-2]], dtype=tf.float32)\n      ids2 = tf.constant([3], dtype=tf.int64)\n      values2 = tf.constant([[-3, -3]], dtype=tf.float32)\n      updated_hash_table = hash_table.assign({\n          \"1\": (ids1, values1),\n          \"2\": (ids2, values2)\n      })\n      values = updated_hash_table.lookup({\"1\": ids1, \"2\": ids2})\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(tf.compat.v1.local_variables_initializer())\n      resources.initialize_resources(resources.shared_resources()).run()\n      values = sess.run(values)\n      self.assertAllEqual(values[\"1\"], [[-1], [-2]])\n      self.assertAllEqual(values[\"2\"], [[-3, -3]])\n\n      updated_hash_table = hash_table.assign({\n          \"1\": (ids1, values1 / 2),\n          \"2\": (ids2, values2 / 2)\n      })\n      values = updated_hash_table.lookup({\"1\": ids1, \"2\": ids2})\n      values = sess.run(values)\n      self.assertAllEqual(values[\"1\"], [[-0.5], [-1]])\n      self.assertAllEqual(values[\"2\"], [[-1.5, -1.5]])\n\n      if use_native_multi_hash_table:\n        ids11 = tf.constant([1, 2, 3], dtype=tf.int64)\n        updated_hash_table, status1 = updated_hash_table.reinitialize(\n            \"1\", ids11)\n        updated_hash_table, status2 = updated_hash_table.reinitialize(\n            \"3\", ids11)\n        values = updated_hash_table.lookup({\"1\": ids11, \"2\": ids2, \"3\": ids11})\n        values, status1, status2 = sess.run([values, status1, status2])\n        self.assertAllEqual(values[\"1\"], [[0], [0], [0]])\n        self.assertAllEqual(values[\"2\"], [[-1.5, -1.5]])\n        self.assertAllEqual(status1, [1, 1, 0])\n        self.assertAllEqual(status2, [-1, -1, -1])\n\n  @parameterized.parameters([(True,), (False,)])\n  def test_apply_gradients_with_learning_rate_function(\n      self, use_native_multi_hash_table):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      slot_to_config = {\n          \"1\":\n              test_utils.generate_test_hash_table_config(\n                  1,\n                  learning_rate=learning_rate_functions.PolynomialDecay(\n                      initial_learning_rate=1.0,\n                      decay_steps=10,\n                      end_learning_rate=2.0)),\n          \"2\":\n              test_utils.generate_test_hash_table_config(\n                  2, learning_rate=lambda: 1.0)\n      }\n      table_factory = (gen_native_multi_hash_table_factory()\n                       if use_native_multi_hash_table else\n                       gen_multi_type_table_factory())\n      hash_table = distributed_ps.DistributedMultiTypeHashTable(\n          2, slot_to_config, table_factory)\n      ids1 = tf.constant([1, 2], dtype=tf.int64)\n      values1 = tf.constant([[-1], [-2]], dtype=tf.float32)\n      ids2 = tf.constant([3], dtype=tf.int64)\n      values2 = tf.constant([[-3, -3]], dtype=tf.float32)\n      updated_hash_table = hash_table.assign_add({\n          \"1\": (ids1, values1),\n          \"2\": (ids2, values2)\n      })\n      values = updated_hash_table.lookup({\"1\": ids1, \"2\": ids2})\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(tf.compat.v1.local_variables_initializer())\n      resources.initialize_resources(resources.shared_resources()).run()\n      values = sess.run(values)\n      self.assertAllEqual(values[\"1\"], [[-1], [-2]])\n      self.assertAllEqual(values[\"2\"], [[-3, -3]])\n      updated_hash_table = hash_table.apply_gradients(\n          {\n              \"1\": (ids1, values1 / 2),\n              \"2\": (ids2, values2 / 2)\n          }, global_step)\n      values = updated_hash_table.lookup({\"1\": ids1, \"2\": ids2})\n      values, _ = sess.run([values, updated_hash_table.as_op()])\n      self.assertAllEqual(values[\"1\"], [[-0.5], [-1]])\n      self.assertAllEqual(values[\"2\"], [[-1.5, -1.5]])\n\n      self.evaluate(tf.compat.v1.assign_add(global_step, 1))\n      updated_hash_table = hash_table.apply_gradients(\n          {\n              \"1\": (ids1, values1 / 2),\n              \"2\": (ids2, values2 / 2)\n          }, global_step)\n      values = updated_hash_table.lookup({\"1\": ids1, \"2\": ids2})\n      values, _ = sess.run([values, updated_hash_table.as_op()])\n      self.assertAllClose(values[\"1\"], [[0.05], [0.1]])\n      self.assertAllClose(values[\"2\"], [[0, 0]])\n\n  @parameterized.parameters([(True,), (False,)])\n  def test_apply_gradients_float16(self, use_native_multi_hash_table):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      slot_to_config = {\n          \"1\":\n              test_utils.generate_test_hash_table_config(dim=1,\n                                                         use_float16=True),\n          \"2\":\n              test_utils.generate_test_hash_table_config(dim=2,\n                                                         use_float16=True),\n      }\n      table_factory = (gen_native_multi_hash_table_factory()\n                       if use_native_multi_hash_table else\n                       gen_multi_type_table_factory())\n      hash_table = distributed_ps.DistributedMultiTypeHashTable(\n          num_ps=2,\n          slot_to_config=slot_to_config,\n          table_factory=table_factory,\n          transfer_float16=True)\n      ids1 = tf.constant([1, 2], dtype=tf.int64)\n      values1 = tf.constant([[-1], [-2]], dtype=tf.float32)\n      ids2 = tf.constant([3], dtype=tf.int64)\n      values2 = tf.constant([[-3, -3]], dtype=tf.float32)\n      loss1 = 2 * values1\n      loss2 = 3 * values2\n      grads1 = tf.gradients(loss1, values1)\n      grads2 = tf.gradients(loss2, values2)\n      hash_table = hash_table.apply_gradients(\n          {\n              '1': {ids1, grads1[0]},\n              '2': {ids2, grads2[0]},\n          },\n          global_step=tf.constant(1, dtype=tf.int64))\n      values = hash_table.lookup({\"1\": ids1, \"2\": ids2})\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(tf.compat.v1.local_variables_initializer())\n      resources.initialize_resources(resources.shared_resources()).run()\n      res = sess.run(values)\n      self.assertAllEqual(res[\"1\"], [[-2.], [-2.]])\n      self.assertAllEqual(res[\"2\"], [[-3., -3.]])\n\n\nclass DistributedMultiTypeHashTableServingTest(tf.test.TestCase,\n                                               parameterized.TestCase):\n\n  @parameterized.parameters([(True,), (False,)])\n  def test_export_model(self, use_native_multi_hash_table):\n    table_factory = (gen_native_multi_hash_table_factory()\n                     if use_native_multi_hash_table else\n                     gen_multi_type_table_factory())\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      slot_to_config = {\n          \"1\": test_utils.generate_test_hash_table_config(1),\n          \"2\": test_utils.generate_test_hash_table_config(2)\n      }\n      # Exporting distributed saved model\n      with tf.Graph().as_default():\n        export_ctx = export_context.ExportContext()\n        with export_context.enter_export_mode(\n            export_context.ExportMode.DISTRIBUTED, export_ctx):\n          hash_table = distributed_ps.DistributedMultiTypeHashTable(\n              2, slot_to_config, table_factory)\n          self.assertAllEqual(export_ctx.sub_graph_num, 2)\n          result = hash_table.lookup({\"1\": tf.constant([1, 2], dtype=tf.int64)})\n        self.assertEqual(result[\"1\"].shape, [2, 1])\n\n      # Exporting standalone saved model\n      with tf.Graph().as_default():\n        export_ctx = export_context.ExportContext()\n        with export_context.enter_export_mode(\n            export_context.ExportMode.STANDALONE, export_ctx):\n          hash_table = distributed_ps.DistributedMultiTypeHashTable(\n              2, slot_to_config, table_factory)\n          self.assertAllEqual(export_ctx.sub_graph_num, 0)\n          hash_table.lookup({\"1\": tf.constant([1], dtype=tf.int64)})\n\n      # Normal training\n      with tf.Graph().as_default():\n        hash_table = distributed_ps.DistributedMultiTypeHashTable(\n            2, slot_to_config, table_factory)\n        hash_table.lookup({\"1\": tf.constant([1], dtype=tf.int64)})\n\n\ndef gen_multi_table_factory(global_name_prefix=\"\"):\n\n  def multi_table_factory(idx: int,\n                          configs: Dict[str, entry.HashTableConfigInstance]):\n\n    def factory(name_suffix, config):\n      return hash_table_ops.hash_table_from_config(\n          config,\n          name_suffix=global_name_prefix +\n          \"_\".join([name_suffix, str(idx)]))\n\n    return multi_type_hash_table.MultiTypeHashTable(configs, factory)\n\n  return multi_table_factory\n\n\nclass PartitionedHashTableTest(tf.test.TestCase, parameterized.TestCase):\n\n  @classmethod\n  def gen_table_config(cls,\n                       dims: List[int],\n                       use_float16: bool = False,\n                       learning_rate: float = 1.0,\n                       enable_gpu_emb: bool = False):\n    assert len(dims) >= 1\n    table_config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n    if enable_gpu_emb:\n      table_config.gpucuco.SetInParent()\n    else:\n      table_config.cuckoo.SetInParent()\n    for i, dim in enumerate(dims):\n      segment = table_config.entry_config.segments.add()\n      segment.dim_size = dim\n      segment.init_config.zeros.SetInParent()\n      segment.comp_config.fp32.SetInParent()\n\n      if i == 0:\n        segment.opt_config.ftrl.SetInParent()\n        segment.opt_config.stochastic_rounding_float16 = use_float16\n      else:\n        segment.opt_config.adagrad.SetInParent()\n        segment.opt_config.stochastic_rounding_float16 = use_float16\n\n    return entry.HashTableConfigInstance(table_config,\n                                         [learning_rate] * len(dims))\n\n  @classmethod\n  def gen_out_config(cls, feature_to_unmerged_slice_dims: Dict[str, List[int]],\n                     layout_names: List[str]):\n    features = list(feature_to_unmerged_slice_dims.keys())\n    feature_stats = {name: 0 for name in features}\n\n    layout_configs = {}\n    for i, layout in enumerate(\n        itertools.zip_longest(*feature_to_unmerged_slice_dims.values())):\n      out_conf = OutConfig(out_type=OutType.CONCAT)\n      assert len(features) == len(layout)\n      out_dim = 0\n      for name, dim_size in zip(features, layout):\n        if dim_size is None:\n          continue\n        out_dim += dim_size\n        slice_config = out_conf.slice_configs.add()\n        slice_config.feature_name = name\n        slice_config.start = feature_stats[name]\n        slice_config.end = slice_config.start + dim_size\n        pooling_type = PoolingType.SUM\n        feature_stats[name] += dim_size\n\n      shape = out_conf.shape.add()\n      shape.dims.extend([-1, out_dim])\n\n      layout_configs[layout_names[i]] = out_conf\n\n    return layout_configs\n\n  @classmethod\n  def setUpClass(cls):\n    if test_util.is_gpu_available(cuda_only=True):\n      import horovod.tensorflow as hvd\n      hvd.init()\n\n  @classmethod\n  def get_parser_ctx(cls, num_ps, enable_gpu_emb, use_gpu,\n                     use_native_multi_hash_table):\n\n    feature_to_unmerged_slice_dims = {\n        \"uid\": [1, 4],\n        'gid': [1, 4, 8],\n        \"cid\": [1, 4, 8],\n    }\n\n    feature_to_combiner = {\n        'uid': embedding_combiners.ReduceSum(),\n        'gid': embedding_combiners.ReduceSum(),\n        'cid': embedding_combiners.ReduceSum(),\n    }\n\n    feature_name_to_config = {\n        name: cls.gen_table_config(dims=dims, enable_gpu_emb=enable_gpu_emb)\n        for name, dims in feature_to_unmerged_slice_dims.items()\n    }\n\n    layout_configs = cls.gen_out_config(feature_to_unmerged_slice_dims,\n                                        layout_names=['bias', 'vec', 'deep'])\n\n    parser_ctx = ParserCtx(True)\n    parser_ctx.parser_type = 'example'\n    parser_ctx.sharding_sparse_fids_op_params = distributed_ps.PartitionedHashTable.gen_feature_configs(\n        num_ps=num_ps,\n        feature_name_to_config=feature_name_to_config,\n        layout_configs=layout_configs,\n        feature_to_combiner=feature_to_combiner,\n        feature_to_unmerged_slice_dims=feature_to_unmerged_slice_dims,\n        use_native_multi_hash_table=use_native_multi_hash_table,\n        unique=lambda: True,\n        transfer_float16=False,\n        enable_gpu_emb=enable_gpu_emb,\n        use_gpu=use_gpu)\n    return parser_ctx\n\n  @classmethod\n  def gen_data(cls,\n               num_ps,\n               sub_table_name_to_config,\n               with_emb: bool = False,\n               method: str = 'random',\n               value: float = 1.0):\n    assert method in {'random', 'const'}\n    data_tf, data_np = {}, {}\n    for i in range(num_ps):\n      data_tf[i], data_np[i] = {}, {}\n      for tbname, conf in sub_table_name_to_config.items():\n        size = sum(\n            seg.dim_size for seg in conf._table_config.entry_config.segments)\n        fids_np = np.array([i + num_ps * j for j in range(size)],\n                           dtype=np.int64)\n        fids_tf = tf.constant(value=fids_np,\n                              dtype=tf.int64,\n                              name=f'{tbname}_fids')\n\n        if with_emb:\n          emb_size = sum(\n              segment.dim_size\n              for segment in conf._table_config.entry_config.segments)\n          if method == 'random':\n            embs_np = np.random.uniform(-1, 1, size=(size, emb_size))\n          else:\n            embs_np = np.ones(\n                shape=(size, emb_size),\n                dtype=np.float32) * value  #fids_np.reshape([size, 1])\n          embs_tf = tf.constant(value=embs_np,\n                                dtype=tf.float32,\n                                name=f'{tbname}_embss')\n          data_tf[i][tbname] = (fids_tf, embs_tf)\n          data_np[i][tbname] = (fids_np, embs_np)\n        else:\n          data_tf[i][tbname] = fids_tf\n          data_np[i][tbname] = fids_np\n    return data_tf, data_np\n\n  @classmethod\n  def gen_variant_tensor(cls, batch_size: int):\n    examples = []\n    for i in range(batch_size):\n      example = Example()\n      start = i * 3\n      named_feature = example.named_feature.add()\n      named_feature.name = 'uid'\n      named_feature.feature.fid_v2_list.value.append(start)\n\n      named_feature = example.named_feature.add()\n      named_feature.name = 'gid'\n      named_feature.feature.fid_v2_list.value.append(start + 1)\n\n      named_feature = example.named_feature.add()\n      named_feature.name = 'cid'\n      named_feature.feature.fid_v2_list.value.append(start + 2)\n\n      logging.info(f\" {i}/{batch_size}:{example}\")\n      examples.append(example.SerializeToString())\n\n    example_strs = tf.constant(value=examples, dtype=tf.string, name='examples')\n    return string_to_variant(example_strs, variant_type='example')\n\n  #with tf.compat.v1.Session(servers[0].target, config=config) as sess, test_util.use_gpu() if use_gpu else sess.graph.device(lambda op: '/CPU:0')\n  def _test_basic(self, use_native_multi_hash_table, use_gpu):\n    enable_gpu_emb = False  #not support hash_table.assign\n    num_ps = 2\n    parser_ctx = self.get_parser_ctx(num_ps, enable_gpu_emb, use_gpu,\n                                     use_native_multi_hash_table)\n    sub_table_name_to_config = parser_ctx.sharding_sparse_fids_op_params.sub_table_name_to_config\n\n    servers, config = test_utils.create_test_ps_cluster(num_ps)\n    config.share_cluster_devices_in_session = True\n    config.experimental.share_session_state_in_clusterspec_propagation = True\n    # grappler doesn't really understand RaggedTensor.\n    config.graph_options.rewrite_options.disable_meta_optimizer = True\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      hash_table = distributed_ps.PartitionedHashTable(\n          num_ps,\n          gen_native_multi_hash_table_factory()\n          if use_native_multi_hash_table else gen_multi_table_factory(),\n          use_native_multi_hash_table=use_native_multi_hash_table,\n          parser_ctx=parser_ctx)\n\n      if use_native_multi_hash_table:\n        sess.run(tf.compat.v1.global_variables_initializer())\n        sess.run(tf.compat.v1.local_variables_initializer())\n        resources.initialize_resources(resources.shared_resources()).run()\n      assign_data, assign_data_np = self.gen_data(num_ps,\n                                                  sub_table_name_to_config,\n                                                  with_emb=True)\n      hash_table = hash_table.assign(assign_data)\n      assign_add_data, assign_add_data_np = self.gen_data(\n          num_ps, sub_table_name_to_config, with_emb=True)\n      hash_table = hash_table.assign_add(assign_add_data)\n      lookup_data, _ = self.gen_data(num_ps,\n                                     sub_table_name_to_config,\n                                     with_emb=False)\n      values = hash_table._lookup_raw(lookup_data)\n\n      real_result = sess.run(values)\n      #logging.info(\n      #    f\"xxx {lookup_data} {assign_data_np} {assign_add_data_np} {real_result}\"\n      #)\n      for part in assign_data_np:\n        for tbname in assign_data_np[part]:\n          fid1, emb1 = assign_data_np[part][tbname]\n          fid2, emb2 = assign_add_data_np[part][tbname]\n          self.assertAllClose(real_result[part][tbname], emb1 + emb2)\n\n  @parameterized.parameters([(True,), (False,)])\n  def test_basic(self, use_native_multi_hash_table):\n    self._test_basic(use_native_multi_hash_table, use_gpu=False)\n\n  @parameterized.parameters([(True,), (False,)])\n  @test_util.run_gpu_only\n  def test_basic_gpu(self, use_native_multi_hash_table):\n    self._test_basic(use_native_multi_hash_table, use_gpu=True)\n\n  def _test_lookup(self, use_native_multi_hash_table, use_gpu):\n    enable_gpu_emb = False  #not support hash_table.assign\n    num_ps = 2\n    parser_ctx = self.get_parser_ctx(num_ps, enable_gpu_emb, use_gpu,\n                                     use_native_multi_hash_table)\n    sub_table_name_to_config = parser_ctx.sharding_sparse_fids_op_params.sub_table_name_to_config\n    servers, config = test_utils.create_test_ps_cluster(num_ps)\n    config.share_cluster_devices_in_session = True\n    config.experimental.share_session_state_in_clusterspec_propagation = True\n    # grappler doesn't really understand RaggedTensor.\n    config.graph_options.rewrite_options.disable_meta_optimizer = True\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      hash_table = distributed_ps.PartitionedHashTable(\n          num_ps,\n          gen_native_multi_hash_table_factory()\n          if use_native_multi_hash_table else gen_multi_table_factory(),\n          use_native_multi_hash_table=use_native_multi_hash_table,\n          parser_ctx=parser_ctx)\n      hash_table._inner_data_type = 'example'\n\n      if use_native_multi_hash_table:\n        sess.run(tf.compat.v1.global_variables_initializer())\n        sess.run(tf.compat.v1.local_variables_initializer())\n        resources.initialize_resources(resources.shared_resources()).run()\n      x = 2.0\n      assign_data, assign_data_np = self.gen_data(num_ps,\n                                                  sub_table_name_to_config,\n                                                  with_emb=True,\n                                                  method='const',\n                                                  value=x)\n      logging.info(f\"show assign_data_np {assign_data_np}\")\n      hash_table = hash_table.assign(assign_data)\n\n      sparse_features = self.gen_variant_tensor(batch_size=num_ps * 3)\n      auxiliary_bundle = {}\n\n      features = {}\n      sharding_sparse_fids_with_context(sparse_features, features, parser_ctx)\n      layouts = hash_table.lookup(features, auxiliary_bundle=auxiliary_bundle)\n      layouts = sess.run(layouts)\n      auxiliary_bundle_ret = sess.run(auxiliary_bundle)\n\n      expect = {\n          'bias':\n              np.array([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.],\n                        [0., 1., 1.], [0., 1., 1.]],\n                       dtype=np.float32),\n          'deep':\n              np.array([[\n                  1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.\n              ], [\n                  1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.\n              ], [\n                  1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.\n              ], [\n                  1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.\n              ], [\n                  1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.\n              ], [\n                  1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.\n              ]],\n                       dtype=np.float32),\n          'vec':\n              np.array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n                        [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n                        [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n                        [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],\n                        [0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1.],\n                        [0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1.]],\n                       dtype=np.float32)\n      }\n\n      for key, value in layouts.items():\n        logging.info(f\" {key} {value}  ---  {expect[key] * x}\")\n        self.assertAllClose(value, expect[key] * x)\n\n  @parameterized.parameters([(True,), (False,)])\n  def test_lookup(self, use_native_multi_hash_table):\n    self._test_lookup(use_native_multi_hash_table, use_gpu=False)\n\n  @parameterized.parameters([(True,), (False,)])\n  @test_util.run_gpu_only\n  def test_lookup_gpu(self, use_native_multi_hash_table):\n    self._test_lookup(use_native_multi_hash_table, use_gpu=True)\n\n  def _test_apply_gradients(self, use_native_multi_hash_table, use_gpu):\n    enable_gpu_emb = False  #not support hash_table.assign\n    num_ps = 2\n    parser_ctx = self.get_parser_ctx(num_ps, enable_gpu_emb, use_gpu,\n                                     use_native_multi_hash_table)\n    sub_table_name_to_config = parser_ctx.sharding_sparse_fids_op_params.sub_table_name_to_config\n\n    servers, config = test_utils.create_test_ps_cluster(num_ps)\n    config.share_cluster_devices_in_session = True\n    config.experimental.share_session_state_in_clusterspec_propagation = True\n    # grappler doesn't really understand RaggedTensor.\n    config.graph_options.rewrite_options.disable_meta_optimizer = True\n    with tf.compat.v1.Session(servers[0].target, config=config) as sess:\n      hash_table = distributed_ps.PartitionedHashTable(\n          num_ps,\n          gen_native_multi_hash_table_factory()\n          if use_native_multi_hash_table else gen_multi_table_factory(),\n          use_native_multi_hash_table=use_native_multi_hash_table,\n          parser_ctx=parser_ctx)\n      hash_table._inner_data_type = 'example'\n\n      if use_native_multi_hash_table:\n        sess.run(tf.compat.v1.global_variables_initializer())\n        sess.run(tf.compat.v1.local_variables_initializer())\n        resources.initialize_resources(resources.shared_resources()).run()\n\n      init_val = 3.0\n      assign_data, assign_data_np = self.gen_data(num_ps,\n                                                  sub_table_name_to_config,\n                                                  with_emb=True,\n                                                  method='const',\n                                                  value=init_val)\n      hash_table = hash_table.assign(assign_data)\n      sparse_features = self.gen_variant_tensor(batch_size=num_ps * 3)\n      auxiliary_bundle = {}\n      features = {}\n      sharding_sparse_fids_with_context(sparse_features, features, parser_ctx)\n      layouts = hash_table.lookup(features, auxiliary_bundle=auxiliary_bundle)\n      layout_grads_and_vars, init_grad = [], 2.0\n      for name in sorted(hash_table._feature_configs.out_configs):\n        layout = layouts[name]\n        layout_grads_and_vars.append(\n            (tf.ones_like(layout, dtype=tf.float32) * init_grad, layout))\n\n      global_step = tf.constant(0, dtype=tf.int64)\n      apply_gradients_op = hash_table.apply_gradients(\n          layout_grads_and_vars, global_step, auxiliary_bundle=auxiliary_bundle)\n      lookup_data, lookup_data_np = self.gen_data(num_ps,\n                                                  sub_table_name_to_config,\n                                                  with_emb=False)\n      with tf.control_dependencies([apply_gradients_op]):\n        values = hash_table._lookup_raw(lookup_data)\n      values = sess.run(values)\n      #logging.info(f\"xx values: {lookup_data_np} {values}\")\n\n      if use_native_multi_hash_table:\n        shards = {\n            'uid:0': [0, 6, 12],\n            'uid:1': [3, 9, 15],\n            'cid:0': [2, 8, 14],\n            'cid:1': [5, 11, 17],\n            'gid:0': [4, 10, 16],\n            'gid:1': [1, 7, 13]\n        }\n      else:\n        shards = {\n            '9871d3a2c554b27151cacf1422eec048:0': [0, 6, 12],\n            '9871d3a2c554b27151cacf1422eec048:1': [3, 9, 15],\n            'c4a398dea30b21551ae4c09454001dba:0': [2, 8, 14, 4, 10, 16],\n            'c4a398dea30b21551ae4c09454001dba:1': [5, 11, 17, 1, 7, 13]\n        }\n\n      learning_rate = 1.0\n      initial_accumulator_value = 0.1\n      beta = 0.0\n      ada_grad = learning_rate / math.sqrt(\n          init_grad * init_grad + initial_accumulator_value) * init_grad\n\n      n = initial_accumulator_value + init_grad * init_grad\n      sigma = (math.sqrt(n) -\n               math.sqrt(initial_accumulator_value)) / learning_rate\n      z = init_grad - sigma * init_val\n      ftrl_grad = -learning_rate * z / (math.sqrt(n) + beta)\n\n      for name, value in shards.items():\n        tb_name, idx = name.split(':')\n        lu_value = values[int(idx)][tb_name]\n        for i in value:\n          k = int(i / num_ps)\n          if k < lu_value.shape[0]:\n            for j, x in enumerate(lu_value[k]):\n              if j == 0:\n                self.assertAlmostEqual(x, ftrl_grad, delta=1e-6)\n              else:\n                self.assertAlmostEqual(init_val - ada_grad, x, delta=1e-6)\n\n  @parameterized.parameters([(True,), (False,)])\n  def test_apply_gradients(self, use_native_multi_hash_table):\n    self._test_apply_gradients(use_native_multi_hash_table, use_gpu=False)\n\n  @parameterized.parameters([(True,), (False,)])\n  @test_util.run_gpu_only\n  def test_apply_gradients_gpu(self, use_native_multi_hash_table):\n    self._test_apply_gradients(use_native_multi_hash_table, use_gpu=True)\n\n  @parameterized.parameters([(True,), (False,)])\n  @test_util.run_gpu_only\n  def test_apply_gradients_for_gpu_emb(self, use_native_multi_hash_table):\n    use_gpu = True\n    enable_gpu_emb = True\n    num_ps = 1\n    parser_ctx = self.get_parser_ctx(num_ps, enable_gpu_emb, use_gpu,\n                                     use_native_multi_hash_table)\n    sub_table_name_to_config = parser_ctx.sharding_sparse_fids_op_params.sub_table_name_to_config\n\n    def run_once(task_name, loop, parser_ctx_, grad_list=None):\n      servers, config = test_utils.create_test_ps_cluster(num_ps)\n      config.share_cluster_devices_in_session = True\n      config.experimental.share_session_state_in_clusterspec_propagation = True\n      # grappler doesn't really understand RaggedTensor.\n      config.graph_options.rewrite_options.disable_meta_optimizer = True\n      with tf.Graph().as_default(), tf.compat.v1.Session(servers[0].target,\n                                                         config=config) as sess:\n        hash_table = distributed_ps.PartitionedHashTable(\n            num_ps,\n            gen_native_multi_hash_table_factory(task_name) if\n            use_native_multi_hash_table else gen_multi_table_factory(task_name),\n            use_native_multi_hash_table=use_native_multi_hash_table,\n            parser_ctx=parser_ctx_)\n        hash_table._inner_data_type = 'example'\n\n        if use_native_multi_hash_table:\n          sess.run(tf.compat.v1.global_variables_initializer())\n          sess.run(tf.compat.v1.local_variables_initializer())\n          resources.initialize_resources(resources.shared_resources()).run()\n\n        sparse_features = self.gen_variant_tensor(batch_size=num_ps * 3)\n        auxiliary_bundle = {}\n        features = {}\n        sharding_sparse_fids_with_context(sparse_features, features,\n                                          parser_ctx_)\n        ret_grad_list = defaultdict(lambda: [])\n        apply_gradients_op = tf.no_op()\n        for loop_idx in range(loop):\n          with tf.control_dependencies([apply_gradients_op]):\n            layouts = hash_table.lookup(features.copy(),\n                                        auxiliary_bundle=auxiliary_bundle)\n          layouts = sess.run(layouts)\n          layout_grads_and_vars = []\n          for name in sorted(hash_table._feature_configs.out_configs):\n            layout = layouts[name]\n            if grad_list:\n              grad = grad_list[name][loop_idx]\n            else:\n              grad = tf.random.uniform(layout.shape)\n              grad = sess.run(grad)\n            ret_grad_list[name].append(grad)\n            layout_grads_and_vars.append((grad, layout))\n\n          global_step = tf.constant(0, dtype=tf.int64)\n          apply_gradients_op = hash_table.apply_gradients(\n              layout_grads_and_vars,\n              global_step,\n              auxiliary_bundle=auxiliary_bundle)\n        with tf.control_dependencies([apply_gradients_op]):\n          layouts = hash_table.lookup(features,\n                                      auxiliary_bundle=auxiliary_bundle)\n        layouts = sess.run(layouts)\n        return layouts, ret_grad_list\n\n    layouts, ret_grad_list = run_once('gpu_emb_', 2, parser_ctx)\n    #logging.info(f\"xx values: {layouts} {ret_grad_list}\")\n    layouts_cpu, ret_grad_list = run_once(\n        \"cpu_\", 2,\n        self.get_parser_ctx(num_ps, False, False, use_native_multi_hash_table),\n        ret_grad_list)\n    #logging.info(f\"xx values: {layouts_cpu} {ret_grad_list}\")\n\n    assert len(layouts) == len(layouts_cpu)\n    for name, value in layouts.items():\n      assert name in layouts_cpu\n      value_2 = layouts_cpu[name]\n      assert len(value) == len(value_2)\n      for a, b in zip(value, value_2):\n        assert len(a) == len(b)\n        #print(a, b)\n        for i in range(len(a)):\n          self.assertAlmostEqual(a[i], b[i], delta=1e-6)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distributed_serving_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom contextlib import nullcontext\nfrom typing import List\n\nfrom absl import logging\nimport tensorflow as tf\nfrom monolith.agent_service.agent_service_pb2 import ServerType\nfrom monolith.agent_service.backends import SyncBackend\nfrom monolith.utils import get_libops_path\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\nfrom monolith.native_training import utils\nfrom monolith.native_training.model_export.export_context import is_exporting_standalone\nfrom monolith.native_training.runtime.parameter_sync import \\\n  parameter_sync_pb2\n\ngen_distributed_serving_ops = gen_monolith_ops\n\n\ndef remote_predict(input_tensor_alias,\n                   input_tensors,\n                   output_tensor_alias,\n                   model_name,\n                   task,\n                   old_model_name,\n                   model_version=-1,\n                   fail_op_on_rpc_error=True,\n                   max_rpc_deadline_millis=30,\n                   output_types=None,\n                   name=None,\n                   signature_name='serving_default'):\n  \"\"\"Runs a predict in remote process through rpc.\n  Args:\n    input_tensor_alias: input tensor alias for Predict\n    input_tensors: input tensors for Predict\n    output_tensor_alias: output tensor alias for Predict\n    task: Parameter Server index\n    model_name: model_name that the Predict is running on\n    model_version: the model version for the Predict call. If unset, the highest\n      version available for serving will be targeted.\n    max_rpc_deadline_millis: rpc deadline in millis\n    output_types: output types for Predict\n    name: name for the op in the graph\n    signature_name: the signature def for remote graph inference\n  Returns:\n    output_tensors as a result of the Predict.\n  Raises ValueError if model_name value is missing.\n  \"\"\"\n  if model_name is None:\n    raise ValueError('model_name must be specified.')\n  return (gen_distributed_serving_ops.tf_serving_remote_predict(\n      input_tensor_alias,\n      input_tensors,\n      output_tensor_alias,\n      model_name=model_name,\n      old_model_name=old_model_name,\n      task=task,\n      model_version=model_version,\n      fail_op_on_rpc_error=fail_op_on_rpc_error,\n      max_rpc_deadline_millis=max_rpc_deadline_millis,\n      signature_name=signature_name,\n      output_types=output_types,\n      name=name))[2]\n\n\ndef create_parameter_sync_clients(ps_num: int,) -> List[tf.Tensor]:\n  logging.info(\"Create parameter sync clients.\")\n  if ps_num == 0:\n    return [parameter_sync_client_from_config()]\n\n  sync_clients = list()\n  for i in range(ps_num):\n    ps_device_name = utils.ps_device(i)\n    with nullcontext() if is_exporting_standalone() else tf.device(\n        ps_device_name):\n      sync_clients.append(parameter_sync_client_from_config(name_suffix=str(i)))\n  return sync_clients\n\n\ndef parameter_sync_client_from_config(\n    config: parameter_sync_pb2.ClientConfig = None,\n    name_suffix: str = \"\") -> tf.Tensor:\n  return gen_distributed_serving_ops.MonolithParameterSyncClient(\n      config=config.SerializeToString() if config else '',\n      shared_name=\"MonolithSyncClient_\" + name_suffix)\n\n\ndef refresh_sync_config(sync_backend: SyncBackend, ps_index: int) -> bytes:\n  saved_model, online_ps_replicas = sync_backend.get_sync_targets(\n      f\"ps_{ps_index}\")\n  config = parameter_sync_pb2.ClientConfig()\n  if isinstance(online_ps_replicas, list):\n    config.targets.extend(online_ps_replicas)\n  elif isinstance(online_ps_replicas, dict):\n    for addr, target_extra_info in online_ps_replicas.items():\n      config.targets.append(addr)\n      config.targets_extra_info.append(target_extra_info)\n  config.model_name = saved_model\n  config.signature_name = \"hashtable_assign\"\n  config.timeout_in_ms = 3000\n  return config.SerializeToString()\n\n\ndef create_dummy_sync_client() -> tf.Tensor:\n  return gen_distributed_serving_ops.MonolithDummySyncClient(\n      shared_name=\"MonolithDummySyncClient\")\n\n\ndef create_dummy_sync_server(address: str) -> tf.Tensor:\n  return gen_distributed_serving_ops.MonolithDummySyncServer(address=address)\n\n\nclass ParameterSyncClient(object):\n\n  def __init__(self, client: tf.Tensor):\n    self._client = client\n\n  def create_sync_op(self, config_str: tf.Tensor):\n    return gen_distributed_serving_ops.monolith_parameter_sync(\n        self._client, config_str)\n\n  def as_op(self):\n    return tf.group(self._client)\n\n  @property\n  def handle(self):\n    return self._client\n\n\nclass DummySyncServer(object):\n\n  def __init__(self, address: str):\n    self._server = create_dummy_sync_server(address)\n\n  def shutdown(self):\n    return gen_distributed_serving_ops.monolith_dummy_sync_server_shutdown(\n        self._server)\n\n  def get_port(self):\n    return gen_distributed_serving_ops.monolith_dummy_sync_server_get_port(\n        self._server)\n\n  def as_op(self):\n    return tf.group(self._server)\n\n  @property\n  def handle(self):\n    return self._server\n"
  },
  {
    "path": "monolith/native_training/distributed_serving_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nimport tensorflow as tf\n\nfrom absl import logging\nfrom monolith.agent_service import utils\nfrom monolith.agent_service import backends\nfrom monolith.agent_service.replica_manager import ReplicaWatcher\nfrom monolith.agent_service.mocked_zkclient import FakeKazooClient\nfrom monolith.agent_service.data_def import ReplicaMeta\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import distributed_serving_ops\nfrom monolith.native_training.distributed_serving_ops import ParameterSyncClient, \\\n  DummySyncServer\nfrom monolith.native_training.runtime.parameter_sync import \\\n  parameter_sync_pb2\n\n\ndef test_dummy_sync_server(server_num: int):\n  return [DummySyncServer(\"localhost:0\") for _ in range(server_num)]\n\n\ndef test_parameter_sync_client(targets):\n  config = parameter_sync_pb2.ClientConfig()\n  config.targets.extend(targets)\n  return ParameterSyncClient(\n      distributed_serving_ops.parameter_sync_client_from_config(config=config))\n\n\ndef _get_id_tensor(x):\n  return tf.constant(x, dtype=tf.int64)\n\n\nclass ParameterSyncOpsTest(tf.test.TestCase):\n\n  def test_parameter_sync_client(self):\n    servers = test_dummy_sync_server(2)\n    ports = [server.get_port() for server in servers]\n    with self.session() as sess:\n      ports = sess.run(ports)\n      targets = [\"localhost:{}\".format(port[0]) for port in ports]\n      client = test_parameter_sync_client(targets)\n      dim = 3\n      hash_table = hash_table_ops.test_hash_table(dim,\n                                                  learning_rate=0.1,\n                                                  sync_client=client.handle)\n      id_tensor = _get_id_tensor([0, 0, 1])\n      embeddings = hash_table.lookup(id_tensor)\n      loss = -embeddings\n      grads = tf.gradients(loss, embeddings)\n      global_step = _get_id_tensor(0)\n      hash_table = hash_table.apply_gradients(id_tensor,\n                                              grads[0],\n                                              global_step=global_step)\n      new_embeddings = hash_table.lookup(_get_id_tensor([0, 1]))\n      new_embeddings = sess.run(new_embeddings)\n      self.assertAllClose(new_embeddings, [[0.2, 0.2, 0.2], [0.1, 0.1, 0.1]])\n\n      config = parameter_sync_pb2.ClientConfig()\n      config.targets.extend(targets)\n      result = sess.run(client.create_sync_op(config.SerializeToString()))\n      print(json.dumps(json.loads(result[0]), indent=2))\n      sess.run([server.shutdown() for server in servers])\n\n  def test_refresh_sync_config_1(self):\n\n    def mock_replica_watcher(ps_index: int):\n      zk = FakeKazooClient()\n      zk.start()\n      config = utils.AgentConfig(bzid=\"demo\",\n                                 base_name=\"test_ffm_model\",\n                                 deploy_type='ps',\n                                 replica_id=0,\n                                 num_ps=10)\n      path_prefix = f'/{config.bzid}/service/{config.base_name}'\n      replica_path = f'{path_prefix}/ps:{ps_index}/{config.replica_id}'\n      replica_meta = ReplicaMeta(address=\"localhost:8500\",\n                                 stat=utils.ModelState.AVAILABLE)\n      replica_meta_bytes = bytes(replica_meta.to_json(), encoding='utf-8')\n      zk.ensure_path(replica_path)\n      zk.set(replica_path, replica_meta_bytes)\n      replica_watcher = ReplicaWatcher(zk, config)\n      replica_watcher.watch_data()\n      return replica_watcher, zk\n\n    replica_watcher, zk = mock_replica_watcher(1)\n    config_str = distributed_serving_ops.refresh_sync_config(\n        replica_watcher.to_sync_wrapper(), 1)\n    config = parameter_sync_pb2.ClientConfig()\n    config.ParseFromString(config_str)\n    self.assertEqual(config.model_name, \"ps_1\")\n    logging.info('targets: %s', config.targets)\n    self.assertEqual(config.targets, [\"localhost:8500\"])\n    replica_watcher.stop()\n    zk.stop()\n\n  def test_refresh_sync_config_2(self):\n    # prepare envs\n    bd = backends.ZKBackend('demo', zk_servers='127.0.0.1:9999')\n    bd._zk = FakeKazooClient()\n    bd.start()\n    container = backends.Container(\"default\", \"asdf\")\n    service_info = backends.ContainerServiceInfo(grpc=\"localhost:8888\",\n                                                 http=\"localhost:8889\",\n                                                 archon=\"localhost:8890\",\n                                                 agent=\"localhost:8891\",\n                                                 idc=\"lf\")\n    bd.report_service_info(container, service_info)\n    bd.sync_available_saved_models(\n        container, {\n            backends.SavedModel(\"test_ffm_model\", \"ps_0\"),\n            backends.SavedModel(\"test_ffm_model\", \"ps_1\"),\n            backends.SavedModel(\"test_ffm_model\", \"ps_2\"),\n        })\n\n    # test sync targets\n    bd.subscribe_model(\"test_ffm_model\")\n    config = parameter_sync_pb2.ClientConfig()\n    config_str = distributed_serving_ops.refresh_sync_config(bd, 1)\n    config.ParseFromString(config_str)\n    self.assertEqual(config.model_name, \"test_ffm_model:ps_1\")\n    self.assertEqual(config.targets, [\"localhost:8888\"])\n    bd.stop()\n\n\nif __name__ == \"__main__\":\n  logging.set_verbosity(logging.INFO)\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distribution_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport logging\nimport os\nfrom typing import List, Tuple, Optional, NamedTuple\n\nimport tensorflow as tf\n\nfrom idl.matrix.proto.example_pb2 import FeatureConfigs\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\ngen_distribution_ops = gen_monolith_ops\n\n\ndef split_by_indices(indices: tf.Tensor, tensor: tf.Tensor,\n                     num_splits: int) -> tf.Tensor:\n  \"\"\"\n  Split |input| elements in into |num_splits| tensors based on indices.\n  |input| is treated as a list of tensors.\n  \"\"\"\n  return gen_distribution_ops.monolith_split_by_indices(indices, tensor,\n                                                        num_splits)\n\n\n@tf.RegisterGradient(\"MonolithSplitByIndices\")\ndef _split_by_indices_gradient(op: tf.Operation, *grads):\n  indices = op.inputs[0]\n  tensor = op.inputs[1]\n  tensor_grad = gen_distribution_ops.monolith_split_by_indices_gradient(\n      indices, tensor, grads)\n  return None, tensor_grad\n\n\ndef ragged_split_by_indices(\n    indices: tf.Tensor, num: tf.RaggedTensor,\n    num_splits: int) -> Tuple[List[tf.RaggedTensor], List[tf.RaggedTensor]]:\n  \"\"\"Split a int64 ragged tensor into |num_splits| ragged tensor based on indices.\n  Returns splitted ragged tensor and splitted original position of each number in ragged tensor.\n  For example,\n  indices = [0, 1, 0, 1]\n  num = [[4, 3, 2], [1]]\n  num_splits = 2\n  ===>\n  [\n    [[4, 2], []],\n    [[3], [1]],\n  ],\n  [\n    [[0, 2], []],\n    [[1], [3]],\n  ]\n  \"\"\"\n  splitted_num, splitted_num_splits, splitted_pos = gen_distribution_ops.monolith_ragged_split_by_indices(\n      indices, num.values, num.row_splits, num_splits=num_splits)\n  results = []\n  pos = []\n  for i in range(num_splits):\n    results.append(\n        tf.RaggedTensor.from_row_splits(splitted_num[i],\n                                        splitted_num_splits[i],\n                                        validate=False))\n    pos.append(\n        tf.RaggedTensor.from_row_splits(splitted_pos[i],\n                                        splitted_num_splits[i],\n                                        validate=False))\n  return results, pos\n\n\nclass _UniqueKeyWithValueAndOffsetResult(NamedTuple):\n  unique_key: tf.RaggedTensor\n  value_offset: tf.RaggedTensor\n  value_buffer: tf.Tensor\n\n\ndef unique_key_with_value_and_offset(key: tf.RaggedTensor,\n                                     dims: List[int],\n                                     generate_buffer=True):\n  \"\"\"Uniques the keys within each key[i], and generates the corresponding value offset map.\n  For key[i][j], the coresponding value's length is dims[i].\n\n  unique_key - the unique result of each key[i]\n  value_buffer - a SharedTensor represents all values concated.\n  value_offset - a ragged tensor with ragged_rank=2. value_offset[i][j] repensents the all offsets\n  in value_buffer for unique_key[i][j]. So if we know the value of key[i][j] is v. We need to fill\n  value_buffer[value_offset[i][j][0]:value_offset[i][j]+dims[i]] = v\n  value_buffer[value_offset[i][j][1]:value_offset[i][j]+dims[i]] = v\n  ...\n\n  For example,\n  key = [[0, 1, 0], [0]]\n  dims = [2, 3]\n  =>\n  unique_key = [[0, 1], [0]]\n  value_offset = [[[0, 4], [2]], [[6]]]\n  value_buffer = float buffer with length 2*3 + 3*1 = 9\n  \"\"\"\n  results = gen_distribution_ops.monolith_unique_key_with_value_and_offset(\n      key.values, key.row_splits, dims=dims, generate_buffer=generate_buffer)\n  return _UniqueKeyWithValueAndOffsetResult(\n      unique_key=tf.RaggedTensor.from_row_splits(results[0],\n                                                 results[1],\n                                                 validate=False),\n      value_offset=tf.RaggedTensor.from_nested_row_splits(\n          results[2], [results[1], results[3]], validate=False),\n      value_buffer=results[4])\n\n\ndef fill_with_offset_map(pos: tf.RaggedTensor, value: tf.Tensor,\n                         value_offset_map: tf.RaggedTensor,\n                         value_buffer: tf.Tensor, dims: List[int]) -> tf.Tensor:\n  \"\"\"Fill the |value| to |value_buffer| for each |pos| in |value_offset_map|.\n  Specifically, for each pos[i][j], we extrac value slice from value (v),\n  we got all positions for pos[i][j], which are value_offset_map.values[pos[i][j]][0],\n  value_offset_map.values[pos[i][j]][1] ...\n  And fill the value_buffer.\n\n  For example,\n  pos = [[0, 1], [2]]\n  value = [0, 1, 2, 3, 4, 5, 6]\n  value_offset_map = [[[0, 4], [2]], [[6]]]\n  dims = [2, 3]\n  =>\n  value_buffer = [0, 1, 2, 3, 0, 1, 4, 5, 6]\n  \"\"\"\n  value_offset_map_1d = value_offset_map.values\n  return gen_distribution_ops.monolith_fill_with_offset_map(\n      pos.values,\n      pos.row_splits,\n      value,\n      value_offset_map_1d.values,\n      value_offset_map_1d.row_splits,\n      value_buffer,\n      dims=dims,\n  )\n\n\ndef fill_with_offset_map_gradient(pos: tf.RaggedTensor, grad: tf.Tensor,\n                                  value_offset_map: tf.RaggedTensor,\n                                  dims: List[int]) -> tf.Tensor:\n  value_offset_map_1d = value_offset_map.values\n  return gen_distribution_ops.monolith_fill_with_offset_map_gradient(\n      pos.values,\n      pos.row_splits,\n      grad,\n      value_offset_map_1d.values,\n      value_offset_map_1d.row_splits,\n      dims=dims,\n  )\n\n\n@tf.RegisterGradient(\"MonolithFillWithOffsetMap\")\ndef _fill_with_offset_map_gradient(op: tf.Operation, grad):\n  value_offset_map = tf.RaggedTensor.from_nested_row_splits(\n      op.inputs[3], [op.inputs[1], op.inputs[4]], validate=False)\n  pos = tf.RaggedTensor.from_row_splits(op.inputs[0], op.inputs[1])\n  backprop_grad = fill_with_offset_map_gradient(pos,\n                                                grad,\n                                                value_offset_map,\n                                                dims=op.get_attr(\"dims\"))\n  return None, None, backprop_grad, None, None, None\n\n\ndef finalize_shared_tensor(shared_tensor_handles: List[tf.Tensor], dtype,\n                           shape):\n  \"\"\"Finalize a shared tensor and it won't be accessible in the future.\n  shared_tensor_handles - the *same handle* which repeats several times.\n  The reason why it is a list is to build a meaningful dependencies for output tensor,\n  which is useful for gradient calculation.\n  For example,\n  t = SharedTensor() \n  t1 = FillPart(t, data0)\n  t2 = FillPart(t, data1)\n  t = finalize_shared_tensor([t1, t2])\n  In this case, we want the grad on t can be propagated back to data0, data1.\n  \"\"\"\n  return gen_distribution_ops.monolith_finalize_shared_tensor(\n      shared_tensor_handles, dtype=dtype, shape=shape)\n\n\n@tf.RegisterGradient(\"MonolithFinalizeSharedTensor\")\ndef _finalize_shared_tensor_gradient(op: tf.Operation, grad):\n  return grad\n\n\ndef reorder_by_indices(input: tf.Tensor, shard_ids: tf.Tensor,\n                       num_of_shards: int) -> List[tf.Tensor]:\n  \"\"\"\n  Reorder the input based on precomputed shard_ids from the caller.\n  Example 1:\n    input: [1, 2, 3, 2]\n    shard_ids: [1, 0, 1, 0]\n    num_of_shards: 2\n\n    output => [2, 3, 1]\n    shard_sizes => [1, 2]\n\n  Args:\n    input: 1-D int64/2-D float tensor with shape [N,]\n    shard_ids: 1-D int32 tensor with shape [N], shard_ids[i] represents the shard is for input[i, ...]\n    num_of_shards: a int32 scalar, representing the number of shards.\n  Returns:\n    Output: reordered 1-D int64/2-D float tensor with shape [M,], M<=N.\n    Shard_sizes: 1-D int32 tensor with shape [num_of_shards].\n\n  \"\"\"\n  return gen_distribution_ops.monolith_reorder_by_indices(\n      input, shard_ids, num_of_shards)\n\n\ndef fused_reorder_by_indices(\n    inputs: List[tf.Tensor], num_of_shards: int,\n    dim_sizes: List[int]) -> Tuple[tf.Tensor, tf.Tensor, tf.Tensor, tf.Tensor, tf.Tensor]:\n  \"\"\"\n  Reorder and dedup int64 values in a list of tensors according to the sharding.\n  \n  Note that the deduplication is applied per tensor of inputs. In other words, \n    the dedup process does not check duplicated ints across tensors of inputs.\n\n  With dim_sizes of the embedding merged slot, it maps each fid to the offset \n    in the expected fused embedding to be generated based on the output fids.\n    For more intuitive cases and explanations, check out the unit test cases.\n\n  Examples:\n      inputs: [[0, 1, 0], [3, 2, 3], [5, 6, 7]]\n      num_of_shards: 2\n      dim_sizes: [1, 2, 3]\n\n      output => [0,2,6,1,3,5,7]\n      shard_sizes => [3,4]\n      sharded_slot_sizes => [1,1,1,1,1,2]\n      fused_embedding_offsets => [0,6,0,7,1,7,9,3,12]\n\n  Args:\n    inputs: List of 1-D int64 tensors.\n    num_of_shards: a int32 scalar, representing the number of shards.\n  Returns:\n    output: reordered 1-D int64 tensor.\n    shard_sizes: 1-D int32 tensor with shape (num_of_shards).\n    sharded_slot_sizes: 1-D int32 tensor with shape (num_of_shards * len(inputs)).\n    fused_embedding_offsets: 1-D int32 tensor\n  \"\"\"\n  # We only trigger this N-1 sharding scheme when it is beyond single host mode.\n  rank0_empty_shard = os.environ.get('MONOLITH_SYNC_EMPTY_RANK0_PS_SHARD',\n                                     '1') == '1' and num_of_shards > 4\n  return gen_distribution_ops.fused_reorder_by_indices(inputs,\n                                                       num_of_shards,\n                                                       dim_sizes,\n                                                       rank0_empty_shard)\n  # # An Alternative Implementation based on TensorFlow Builtin Ops:\n  # with tf.name_scope('fused_reorder_by_indicies'):\n  #   inputs = [tf.unique(ids)[0] for ids in inputs]\n  #   shard_indicies = [tf.cast(tf.math.floormod(ids, num_of_shards), dtype=tf.int32) for ids in inputs]\n  #   sharded_slot_lists = [tf.dynamic_partition(ids, indicies, num_of_shards) for ids, indicies in zip(inputs, shard_indicies)]\n  #   outputs = []\n  #   shard_sizes = []\n  #   sharded_slot_sizes = []\n  #   for i in range(num_of_shards):\n  #     sizes_per_shard = []\n  #     for m in range(len(inputs)):\n  #       outputs.append(sharded_slot_lists[m][i])\n  #       sizes_per_shard.append(tf.size(sharded_slot_lists[m][i]))\n  #     shard_sizes.append(tf.reduce_sum(sizes_per_shard))\n  #     sharded_slot_sizes.extend(sizes_per_shard)\n  #   output = tf.concat(outputs, axis=0)\n  # return output, tf.convert_to_tensor(shard_sizes), tf.convert_to_tensor(sharded_slot_sizes)\n\n\ndef map_id_to_embedding(ids: List[tf.Tensor],\n                        embeddings: List[tf.Tensor],\n                        input: tf.Tensor,\n                        use_multi_threads: bool = True) -> tf.Tensor:\n  \"\"\"\n  Map int64 in input to embedding. Output will have an extra dim at last\n  which equals to embedding dim. The length of ids and embeddings must match.\n  Args:\n    ids: a list of 1-D int64 tensor.\n    embeddings: a list of 2-D float32 tensor. Represents mapping.\n    use_multi_threads: True if the caller wants to use multi-threads.\n  \"\"\"\n  if len(ids) != len(embeddings):\n    raise ValueError(\n        \"ids length and embeddings lenght must match. {} vs {}\".format(\n            len(ids), len(embeddings)))\n\n  return gen_distribution_ops.monolith_map_id_to_embedding(\n      ids, embeddings, input, use_multi_threads=use_multi_threads)\n\n\ndef fused_embedding_to_layout(\n    embeddings_list: List[tf.Tensor],\n    fid_list_row_split: List[tf.Tensor],\n    fid_offset: tf.Tensor,\n    feature_offset: tf.Tensor,\n    nfl_offset: tf.Tensor,\n    batch_size: tf.Tensor,\n    variant_type: str,\n    feature_cfgs: FeatureConfigs,\n    ps_num: int,\n    fid_list_emb_row_lenth: tf.Tensor = None,\n    nfl_size: tf.Tensor = None,\n    feature_size: tf.Tensor = None,\n    fid_size: tf.Tensor = None,\n    emb_size: tf.Tensor = None,\n    parallel_flag: int = 0,\n    version: int = 3,\n):\n  assert variant_type in {\n      'example', 'example_batch', 'examplebatch', 'instance'\n  }\n  variant_type = 'example_batch' if variant_type == 'examplebatch' else variant_type\n  feature_cfgs_str = feature_cfgs.SerializeToString()\n  N = 0\n  for layout, conf in feature_cfgs.out_configs.items():\n    N += len(conf.shape)\n  if version != 4:\n    assert fid_list_emb_row_lenth is None\n\n  if version == 5:\n    layout_tensors = gen_distribution_ops.monolith_embedding_to_layout_v5(\n        embeddings_list=embeddings_list,\n        fid_offset=fid_offset,\n        feature_offset=feature_offset,\n        nfl_offset=nfl_offset,\n        batch_size=batch_size,\n        nfl_size=nfl_size,\n        feature_size=feature_size,\n        fid_size=fid_size,\n        emb_size=emb_size,\n        num_out=N,\n        variant_type=variant_type,\n        feature_cfgs=feature_cfgs_str,\n        ps_num=ps_num,\n        parallel_flag=parallel_flag)\n  elif version == 4:\n    assert fid_list_emb_row_lenth is not None\n    layout_tensors = gen_distribution_ops.monolith_embedding_to_layout_v4(\n        embeddings_list=embeddings_list,\n        fid_list_emb_row_lenth=fid_list_emb_row_lenth,\n        fid_offset=fid_offset,\n        feature_offset=feature_offset,\n        nfl_offset=nfl_offset,\n        batch_size=batch_size,\n        num_out=N,\n        variant_type=variant_type,\n        feature_cfgs=feature_cfgs_str,\n        ps_num=ps_num,\n        parallel_flag=parallel_flag)\n  elif version == 3:\n    layout_tensors = gen_distribution_ops.monolith_embedding_to_layout_v3(\n        embeddings_list=embeddings_list,\n        fid_offset=fid_offset,\n        feature_offset=feature_offset,\n        nfl_offset=nfl_offset,\n        batch_size=batch_size,\n        num_out=N,\n        variant_type=variant_type,\n        feature_cfgs=feature_cfgs_str,\n        ps_num=ps_num,\n        parallel_flag=parallel_flag)\n  elif version == 2:\n    layout_tensors = gen_distribution_ops.monolith_embedding_to_layout_v2(\n        embeddings_list=embeddings_list,\n        fid_list_row_split=fid_list_row_split,\n        fid_offset=fid_offset,\n        feature_offset=feature_offset,\n        nfl_offset=nfl_offset,\n        batch_size=batch_size,\n        num_out=N,\n        variant_type=variant_type,\n        feature_cfgs=feature_cfgs_str,\n        ps_num=ps_num,\n        parallel_flag=parallel_flag)\n  else:\n    layout_tensors = gen_distribution_ops.monolith_embedding_to_layout(\n        embeddings_list=embeddings_list,\n        fid_offset=fid_offset,\n        feature_offset=feature_offset,\n        nfl_offset=nfl_offset,\n        batch_size=batch_size,\n        num_out=N,\n        variant_type=variant_type,\n        feature_cfgs=feature_cfgs_str)\n  return layout_tensors\n\n\n@tf.RegisterGradient(\"MonolithEmbeddingToLayout\")\ndef _fused_embedding_to_layout_grad(op: tf.Operation, *grads):\n  M = op.get_attr(\"M\")  # fid_num\n  embeddings_list = op.inputs[0:M]\n  fid_offset, feature_offset, nfl_offset, batch_size = op.inputs[M], op.inputs[\n      M + 1], op.inputs[M + 2], op.inputs[M + 3]\n  variant_type = op.get_attr(\"variant_type\")\n  feature_cfgs_str = op.get_attr(\"feature_cfgs\")\n  embeddings_grad_list = gen_distribution_ops.monolith_embedding_to_layout_grad(\n      embeddings_list=embeddings_list,\n      fid_offset=fid_offset,\n      feature_offset=feature_offset,\n      nfl_offset=nfl_offset,\n      batch_size=batch_size,\n      tensors_grad=grads,\n      variant_type=variant_type,\n      feature_cfgs=feature_cfgs_str)\n  return embeddings_grad_list + [None] * 4\n\n\n@tf.RegisterGradient(\"MonolithEmbeddingToLayoutV2\")\ndef _fused_embedding_to_layout_grad_v2(op: tf.Operation, *grads):\n  M = op.get_attr(\"M\")  # fid_num\n  pre = 0\n  embeddings_list = op.inputs[0:M]\n  pre += M\n  fid_list_row_split = op.inputs[pre:pre + M]\n  pre += M\n  fid_offset, feature_offset, nfl_offset, batch_size = op.inputs[\n      pre], op.inputs[pre + 1], op.inputs[pre + 2], op.inputs[pre + 3]\n  variant_type = op.get_attr(\"variant_type\")\n  feature_cfgs_str = op.get_attr(\"feature_cfgs\")\n  ps_num = op.get_attr(\"ps_num\")\n  embeddings_grad_list = gen_distribution_ops.monolith_embedding_to_layout_grad_v2(\n      embeddings_list=embeddings_list,\n      fid_list_row_split=fid_list_row_split,\n      fid_offset=fid_offset,\n      feature_offset=feature_offset,\n      nfl_offset=nfl_offset,\n      batch_size=batch_size,\n      tensors_grad=grads,\n      variant_type=variant_type,\n      feature_cfgs=feature_cfgs_str,\n      ps_num=ps_num,\n      parallel_flag=0)\n  return embeddings_grad_list + [None] * (M + 4)\n\n\n@tf.RegisterGradient(\"MonolithEmbeddingToLayoutV3\")\ndef _fused_embedding_to_layout_grad_v3(op: tf.Operation, *grads):\n  M = op.get_attr(\"M\")  # fid_num\n  pre = 0\n  embeddings_list = op.inputs[0:M]\n  pre += M\n  fid_offset, feature_offset, nfl_offset, batch_size = op.inputs[\n      pre], op.inputs[pre + 1], op.inputs[pre + 2], op.inputs[pre + 3]\n  variant_type = op.get_attr(\"variant_type\")\n  feature_cfgs_str = op.get_attr(\"feature_cfgs\")\n  ps_num = op.get_attr(\"ps_num\")\n  embeddings_grad_list = gen_distribution_ops.monolith_embedding_to_layout_grad_v3(\n      embeddings_list=embeddings_list,\n      fid_offset=fid_offset,\n      feature_offset=feature_offset,\n      nfl_offset=nfl_offset,\n      batch_size=batch_size,\n      tensors_grad=grads,\n      variant_type=variant_type,\n      feature_cfgs=feature_cfgs_str,\n      ps_num=ps_num,\n      parallel_flag=0)\n  return embeddings_grad_list + [None] * 4\n\n\n@tf.RegisterGradient(\"MonolithEmbeddingToLayoutV4\")\ndef _fused_embedding_to_layout_grad_v4(op: tf.Operation, *grads):\n  M = op.get_attr(\"M\")  # fid_num\n  pre = 0\n  embeddings_list = op.inputs[0:M]\n  pre += M\n  fid_list_emb_row_lenth = op.inputs[pre]\n  pre += 1\n  fid_offset, feature_offset, nfl_offset, batch_size = op.inputs[\n      pre], op.inputs[pre + 1], op.inputs[pre + 2], op.inputs[pre + 3]\n  variant_type = op.get_attr(\"variant_type\")\n  feature_cfgs_str = op.get_attr(\"feature_cfgs\")\n  ps_num = op.get_attr(\"ps_num\")\n  embeddings_grad_list = gen_distribution_ops.monolith_embedding_to_layout_grad_v4(\n      embeddings_list=embeddings_list,\n      fid_list_emb_row_lenth=fid_list_emb_row_lenth,\n      fid_offset=fid_offset,\n      feature_offset=feature_offset,\n      nfl_offset=nfl_offset,\n      batch_size=batch_size,\n      tensors_grad=grads,\n      variant_type=variant_type,\n      feature_cfgs=feature_cfgs_str,\n      ps_num=ps_num,\n      parallel_flag=0)\n  return embeddings_grad_list + [None] * 5\n\n@tf.RegisterGradient(\"MonolithEmbeddingToLayoutV5\")\ndef _fused_embedding_to_layout_grad_v5(op: tf.Operation, *grads):\n  M = op.get_attr(\"M\")  # fid_num\n  pre = 0\n  embeddings_list = op.inputs[0:M]\n  pre += M\n  fid_offset, feature_offset, nfl_offset, batch_size = op.inputs[\n      pre], op.inputs[pre + 1], op.inputs[pre + 2], op.inputs[pre + 3]\n  variant_type = op.get_attr(\"variant_type\")\n  feature_cfgs_str = op.get_attr(\"feature_cfgs\")\n  ps_num = op.get_attr(\"ps_num\")\n  embeddings_grad_list = gen_distribution_ops.monolith_embedding_to_layout_grad_v3(\n      embeddings_list=embeddings_list,\n      fid_offset=fid_offset,\n      feature_offset=feature_offset,\n      nfl_offset=nfl_offset,\n      batch_size=batch_size,\n      tensors_grad=grads,\n      variant_type=variant_type,\n      feature_cfgs=feature_cfgs_str,\n      ps_num=ps_num,\n      parallel_flag=0)\n  return embeddings_grad_list + [None] * 8\n\n\ndef fused_embedding_to_layout_grad(\n    nfl_offset: tf.Tensor,\n    feature_offset: tf.Tensor,\n    fid_offset: tf.Tensor,\n    batch_size: tf.Tensor,\n    embeddings_list: List[tf.Tensor],\n    fid_list_row_split: List[tf.Tensor],\n    layout_tensors_grad: List[tf.Tensor],\n    variant_type: str,\n    feature_cfgs: FeatureConfigs,\n    ps_num: int,\n    fid_list_emb_row_lenth: tf.Tensor = None,\n    layout_tensors_grad_scale=None,\n    parallel_flag=0,\n    version: int = 3,\n) -> List[tf.Tensor]:\n  feature_cfgs_str = feature_cfgs.SerializeToString()\n  assert variant_type in {\n      'example', 'example_batch', 'examplebatch', 'instance'\n  }\n  variant_type = 'example_batch' if variant_type == 'examplebatch' else variant_type\n  if layout_tensors_grad_scale is not None:\n    logging.info(\n        f\"fused_embedding_to_layout_grad use layout_tensors_grad_scale\")\n    #TODO fuse layout_tensors_grad_scale to op\n    layout_tensors_grad = [\n        layout_tensors_grad_scale * grad for grad in layout_tensors_grad\n    ]\n  if version != 4:\n    assert fid_list_emb_row_lenth is None\n  if version == 4:\n    assert fid_list_emb_row_lenth is not None\n    embeddings_grad_list = gen_distribution_ops.monolith_embedding_to_layout_grad_v4(\n        embeddings_list=embeddings_list,\n        fid_list_emb_row_lenth=fid_list_emb_row_lenth,\n        fid_offset=fid_offset,\n        feature_offset=feature_offset,\n        nfl_offset=nfl_offset,\n        batch_size=batch_size,\n        tensors_grad=layout_tensors_grad,\n        variant_type=variant_type,\n        feature_cfgs=feature_cfgs_str,\n        ps_num=ps_num,\n        parallel_flag=parallel_flag)\n  elif version == 3 or version == 5:\n    embeddings_grad_list = gen_distribution_ops.monolith_embedding_to_layout_grad_v3(\n        embeddings_list=embeddings_list,\n        fid_offset=fid_offset,\n        feature_offset=feature_offset,\n        nfl_offset=nfl_offset,\n        batch_size=batch_size,\n        tensors_grad=layout_tensors_grad,\n        variant_type=variant_type,\n        feature_cfgs=feature_cfgs_str,\n        ps_num=ps_num,\n        parallel_flag=parallel_flag)\n  elif version == 2:\n    embeddings_grad_list = gen_distribution_ops.monolith_embedding_to_layout_grad_v2(\n        embeddings_list=embeddings_list,\n        fid_list_row_split=fid_list_row_split,\n        fid_offset=fid_offset,\n        feature_offset=feature_offset,\n        nfl_offset=nfl_offset,\n        batch_size=batch_size,\n        tensors_grad=layout_tensors_grad,\n        variant_type=variant_type,\n        feature_cfgs=feature_cfgs_str,\n        ps_num=ps_num,\n        parallel_flag=parallel_flag)\n  else:\n    embeddings_grad_list = gen_distribution_ops.monolith_embedding_to_layout_grad(\n        embeddings_list=embeddings_list,\n        fid_offset=fid_offset,\n        feature_offset=feature_offset,\n        nfl_offset=nfl_offset,\n        batch_size=batch_size,\n        tensors_grad=layout_tensors_grad,\n        variant_type=variant_type,\n        feature_cfgs=feature_cfgs_str)\n  return embeddings_grad_list\n\n\n@tf.RegisterGradient(\"MonolithMapIdToEmbedding\")\ndef _map_id_to_embedding_gradient(op: tf.Operation, grads: tf.Tensor):\n  num_splits = op.get_attr(\"num_splits\")\n  ids = [op.inputs[i] for i in range(num_splits)]\n  input = op.inputs[2 * num_splits]\n  embedding_grads = gen_distribution_ops.monolith_map_id_to_embedding_gradient(\n      ids, input, grads)\n  return [None] * num_splits + embedding_grads + [None]\n\n\ndef map_id_to_embedding_gradient_back_prop(ids: tf.Tensor, input: tf.Tensor,\n                                           grads: tf.Tensor):\n  \"\"\"\n  The manual back prop for MonolithMapIdToEmbedding.\n\n  Returns:\n    output: A list of 2-D tensors [K, dim], sum(K)=N\n\n  \"\"\"\n  embedding_grads = gen_distribution_ops.monolith_map_id_to_embedding_gradient(\n      ids, input, grads)\n  return embedding_grads\n\n\ndef gather_embeddings_by_input(ids: tf.Tensor,\n                               embeddings: tf.Tensor,\n                               input: tf.Tensor,\n                               use_multi_threads: bool = False) -> tf.Tensor:\n  \"\"\"\n  Gather embeddings based on input with a shape [N] and an ids:embeddings map. \n  The ids with a shape [M] is mapped element-wise to embeddings with a shape [M, dim],\n  e.g., for any index i, ids(i)'s embedding is embeddings(i).\n\n  Example:\n    ids: [1, 2, 3]\n    embeddings: [[1., 1.], [2., 2.], [3., 3.]]\n    input: [1, 3, 2, 3]\n\n    output=>[[1., 1.], [3., 3.], [2., 2.], [3., 3.]]\n    index_mapping=>[0, 2, 1, 2]\n  \n  Args:\n    ids: a 1-D int64 tensor [M].\n    embeddings: a 2-D float32 tensor [M, dim]. Mapped in order with ids.\n    input: a int32 tensor with shape [N], N >= M. Input value is range from 0 to M-1.\n\n  Returns:\n    output: a 2-D tensor [N, dim].\n    index_mapping: a 1-D tensor [N].\n\n  \"\"\"\n  return gen_distribution_ops.monolith_gather_embeddings_by_input(\n      ids, embeddings, input, use_multi_threads=use_multi_threads)\n\n\n@tf.RegisterGradient(\"MonolithGatherEmbeddingsByInput\")\ndef _gather_embeddings_by_ids_gradient(\n    op: tf.Operation, grads: tf.Tensor,\n    index_mapping_grads: Optional[tf.Tensor]):\n  ids = op.inputs[0]\n  index_mapping = op.outputs[1]\n  embedding_grads = gen_distribution_ops.monolith_gather_embeddings_by_input_gradient(\n      ids, grads, index_mapping)\n  return [None, embedding_grads, None]\n\n\ndef fused_gather_embeddings_by_input(\n    fused_embeddings: tf.Tensor, fused_embedding_offsets: List[tf.Tensor],\n    embedding_dims: List[int]) -> List[tf.Tensor]:\n  return gen_distribution_ops.monolith_fused_gather_embeddings_by_input(\n      fused_embeddings, fused_embedding_offsets, embedding_dims=embedding_dims)\n\n\ndef fused_gather_embeddings_by_input_gradient(\n    fused_embeddings: tf.Tensor,\n    grads: List[tf.Tensor],\n    embedding_offsets: List[tf.Tensor],\n    embedding_dims: List[int],\n    scale = 1\n) -> tf.Tensor:\n  return gen_distribution_ops.monolith_fused_gather_embeddings_by_input_gradient(\n      fused_embeddings, grads, embedding_offsets, embedding_dims=embedding_dims, scale=scale)\n\n\ndef reduce_mean(id_indices: tf.Tensor, id_values: tf.Tensor,\n                id_length: tf.Tensor, name: str = None):\n  \"\"\"\n  Very similar to tf.sparse.reduce_mean. The difference is now id_values is a 2-D\n  tensors instead of 1-D tensor.\n  Args:\n    id_indices: 2-D tensor represents a list of positions of id_values.\n    id_values: 2-D tensor which represents a list of actual values. (Value is 1-D tensor)\n    id_length: should be a shape which equals to [batch_size] \n  \"\"\"\n  return gen_distribution_ops.monolith_reduce_mean(id_indices, id_values,\n                                                   id_length, name=name)\n\n\ndef gather_embeddings_by_ids_gradient_back_prop(ids: tf.Tensor,\n                                                grads: tf.Tensor,\n                                                index_mapping: tf.Tensor):\n  \"\"\"\n  The manual back prop for MonolithGatherEmbeddingsByInput.\n\n  Returns:\n    output: a 2-D tensor [N, dim].\n\n  \"\"\"\n  embedding_grads = gen_distribution_ops.monolith_gather_embeddings_by_input_gradient(\n      ids, grads, index_mapping)\n  return embedding_grads\n\n\n@tf.RegisterGradient(\"MonolithReduceMean\")\ndef _reduce_mean_gradient(op: tf.Operation, grads: tf.Tensor):\n  id_indices = op.inputs[0]\n  id_value_grads = gen_distribution_ops.monolith_reduce_mean_gradient(\n      id_indices, grads)\n  return None, id_value_grads, None\n\n\ndef reduce_sum(id_indices: tf.Tensor,\n               id_values: tf.Tensor,\n               id_length: tf.Tensor,\n               name=None):\n  \"\"\"\n  Very similar to tf.sparse.reduce_sum. The difference is now id_values is a 2-D\n  tensors instead of 1-D tensor.\n  Args:\n    id_indices: 2-D tensor represents a list of positions of id_values.\n    id_values: 2-D tensor which represents a list of actual values. (Value is 1-D tensor)\n    id_length: should be a shape which equals to [batch_size] \n  \"\"\"\n  return gen_distribution_ops.monolith_reduce_sum(id_indices,\n                                                  id_values,\n                                                  id_length,\n                                                  name=name)\n\n\n@tf.RegisterGradient(\"MonolithReduceSum\")\ndef _reduce_sum_gradient(op: tf.Operation, grads: tf.Tensor):\n  id_indices = op.inputs[0]\n  id_value_grads = gen_distribution_ops.monolith_reduce_sum_gradient(\n      id_indices, grads)\n  return None, id_value_grads, None\n\n\ndef reduce_sqrtn(id_indices: tf.Tensor, id_values: tf.Tensor,\n                 id_length: tf.Tensor):\n  \"\"\"\n  Very similar to the combiner method in tf.tpu.experimental.embedding.TPUEmbedding\n  The input is a \n  Args:\n    id_indices: 2-D tensor represents a list of positions of id_values.\n    id_values: 2-D tensor which represents a list of actual values. (Value is 1-D tensor)\n    id_length: should be a shape which equals to [batch_size] \n  \"\"\"\n  return gen_distribution_ops.monolith_reduce_square_norm(\n      id_indices, id_values, id_length)\n\n\n@tf.RegisterGradient(\"MonolithReduceSquareNorm\")\ndef _reduce_sum_gradient(op: tf.Operation, grads: tf.Tensor):\n  id_indices = op.inputs[0]\n  id_values = op.inputs[1]\n  id_value_grads = gen_distribution_ops.monolith_reduce_square_norm_gradient(\n      id_indices, id_values, grads)\n  return None, id_value_grads, None\n\n\ndef fused_sorted_segment_sum(indices: List[tf.Tensor], values: List[tf.Tensor],\n                             shapes: List[tf.Tensor]):\n  \"\"\"\n  It combines multiple segment_sum into one GPU kernel.\n  \n  Args:\n    indicies: List of Indices a.k.s 1-D SORTED segment ids\n    values: List of Values to scatter into the output tensor.\n    shapes: List of Shapes that Must have the same type as indices.\n  Output:\n    reduced: output tensors, i-the tensor has a shape `shapes[i]`.\n  \"\"\"\n  return gen_distribution_ops.monolith_fused_segment_sum(\n      indices, values, shapes)\n\n\n@tf.RegisterGradient(\"MonolithFusedSegmentSum\")\ndef _FusedSegmentSumGrad(op, *grads):\n  n = len(grads)\n  updates_grads = [\n      tf.gather_nd(grad, tf.expand_dims(indices,\n                                        -1))  # Similar to fused ScatterNd\n      for grad, indices in zip(grads, op.inputs[:n])\n  ]\n  return [None] * n + updates_grads + [None] * n\n\n\ndef fused_reduce_sum_and_split(id_indices: tf.Tensor,\n                               id_values: tf.Tensor,\n                               id_length: tf.Tensor,\n                               split_dims: List[int],\n                               name: str = None):\n  \"\"\"\n  Very similar to tf.sparse.reduce_sum. It combines with a fused split op.\n  Args:\n    id_indices: 1-D tensor represents a list of positions of id_values.\n    id_values: 2-D tensor which represents a list of actual values. (Value is 1-D tensor)\n    id_length: should be a shape which equals to \"batch_size\"\n    split_dims: dimensions for the split vectors. Sum(split_dims)=id_values.dim(1)\n  Output:\n    reduced: M output tensors, and i-th tensor has a shape [bs, split_dims[i]].\n  \"\"\"\n  id_indices = tf.expand_dims(id_indices, -1)\n  id_length = tf.cast(tf.expand_dims(id_length, 0),\n                      dtype=tf.int64)  # To remove cast support int32 for cpu\n  num_of_splits = len(split_dims)\n  return gen_distribution_ops.monolith_fused_reduce_sum_and_split(id_indices,\n                                                                  id_values,\n                                                                  id_length,\n                                                                  num_of_splits,\n                                                                  split_dims,\n                                                                  name=name)\n\n\n@tf.RegisterGradient(\"MonolithFusedReduceSumAndSplit\")\ndef _fused_reduce_sum_and_split_gradient(op: tf.Operation, *grads):\n  id_indices = op.inputs[0]\n  split_dims = op.get_attr(\"split_dims\")\n  id_value_grads = gen_distribution_ops.monolith_fused_reduce_sum_and_split_gradient(\n      id_indices, grads, split_dims=split_dims)\n  return None, id_value_grads, None\n\n\ndef fused_reduce_and_split_gpu(splits: List[tf.Tensor],\n                               embeddings: List[tf.Tensor],\n                               slice_dims: List[List[int]],\n                               name: str = None) -> List[tf.Tensor]:\n  \"\"\"\n  Output:\n  Args:\n    splits: list of N 'row_splits' attribute of fid ragged tensors\n    embeddings: list of N embeddings\n    slice_dims: list of N slice_dims\n  Output:\n    reduced: M output tensors, and i-th tensor has a shape [bs, flat_slice_dims[i]].\n    where flat_slice_dims=concat(slice_dims), and M=len(flat_slice_dims)\n  \"\"\"\n  flat_slice_dims = []\n  row_split_splits = []\n  row_split_idx = 0\n  for i in range(len(slice_dims)):\n    s = slice_dims[i]\n    flat_slice_dims.extend(s)\n\n    row_split_splits.append(row_split_idx)\n    row_split_idx += splits[i].shape[0]\n\n  row_split_splits.append(row_split_idx)\n  with tf.device(\"/device:CPU:0\"):\n    fused_splits = tf.cast(tf.concat(splits, 0), tf.int32)\n  return gen_distribution_ops.monolith_fused_reduce_and_split_gpu(\n      fused_splits,\n      embeddings,\n      slice_dims=flat_slice_dims,\n      num_slices=len(flat_slice_dims),\n      row_split_splits=row_split_splits,\n      name=name)\n\n\n@tf.RegisterGradient(\"MonolithFusedReduceAndSplitGPU\")\ndef _fused_reduce_and_split_gpu_grad(op: tf.Operation, *grads):\n  row_split_splits = op.get_attr('row_split_splits')\n  slice_dims = op.get_attr('slice_dims')\n  return [None] + gen_distribution_ops.monolith_fused_reduce_and_split_gpu_grad(\n      op.inputs[0],\n      op.inputs[1:len(row_split_splits)],\n      grads,\n      row_split_splits=row_split_splits,\n      slice_dims=slice_dims\n  )\n\ndef normalize_merged_split(row_split: tf.Tensor,\n                           row_split_size: tf.Tensor) -> tf.Tensor:\n  return gen_distribution_ops.monolith_normalize_merged_split(row_split,\n                                                              row_split_size)\n"
  },
  {
    "path": "monolith/native_training/distribution_ops_benchmark.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport shutil\nimport time\nimport tensorflow as tf\n\nfrom monolith.native_training import distribution_ops\n\n\nclass DistributionOpsBenchmarkTest(tf.test.TestCase):\n\n  def map_id_to_embedding(self, use_multi_threads):\n    log_dir = \"/tmp/distribution_ops_benchmark/map_id_to_embedding{}\".format(\n        \"_multi_threads\" if use_multi_threads else \"\")\n    if os.path.exists(log_dir):\n      shutil.rmtree(log_dir)\n    options = tf.profiler.experimental.ProfilerOptions(host_tracer_level=3,\n                                                       python_tracer_level=0,\n                                                       device_tracer_level=0)\n    tf.profiler.experimental.start(log_dir, options=options)\n\n    num_elements, dim, ps_num = 1000000, 16, 10\n    ids = tf.constant([x for x in range(num_elements)], dtype=tf.int64)\n    embeddings = tf.constant(\n        [[x for x in range(dim)] for _ in range(num_elements)],\n        dtype=tf.float32)\n\n    indices = tf.math.floormod(ids, ps_num)\n    split_ids = distribution_ops.split_by_indices(indices, ids, ps_num)\n    split_embeddings = distribution_ops.split_by_indices(\n        indices, embeddings, ps_num)\n    embeddings_mapped = distribution_ops.map_id_to_embedding(\n        split_ids, split_embeddings, ids, use_multi_threads=use_multi_threads)\n\n    self.assertAllEqual(embeddings, embeddings_mapped)\n    tf.profiler.experimental.stop()\n\n  def test_gather_embeddings_by_ids_basic(self):\n    num_features = 100000\n    with tf.compat.v1.Session() as sess:\n      embeddings = tf.ones([num_features, 32])\n      id_tensor = tf.constant([x for x in range(num_features)], dtype=tf.int64)\n      input = tf.constant([[y % num_features\n                            for y in range(x, x + 4)]\n                           for x in range(num_features)],\n                          dtype=tf.int64)\n      output = distribution_ops.gather_embeddings_by_input(\n          id_tensor, embeddings, input)\n      start = time.time()\n      output = sess.run(output)\n      total_wall_time = time.time() - start\n      print('wall time: {}'.format(total_wall_time))\n    with tf.compat.v1.Session() as sess:\n      embeddings = tf.ones([num_features, 256])\n      id_tensor = tf.constant([x for x in range(num_features)], dtype=tf.int64)\n      input = tf.constant([[y % num_features\n                            for y in range(x, x + 2)]\n                           for x in range(num_features)],\n                          dtype=tf.int64)\n      output = distribution_ops.gather_embeddings_by_input(\n          id_tensor, embeddings, input)\n      start = time.time()\n      output = sess.run(output)\n      total_wall_time = time.time() - start\n      print('wall time: {}'.format(total_wall_time))\n\n  def test_gather_embeddings_by_ids_multi_threads(self):\n    num_features = 100000\n    with tf.compat.v1.Session() as sess:\n      embeddings = tf.ones([num_features, 32])\n      id_tensor = tf.constant([x for x in range(num_features)], dtype=tf.int64)\n      input = tf.constant([[y % num_features\n                            for y in range(x, x + 4)]\n                           for x in range(num_features)],\n                          dtype=tf.int64)\n      output = distribution_ops.gather_embeddings_by_input(\n          id_tensor, embeddings, input, use_multi_threads=True)\n      start = time.time()\n      output = sess.run(output)\n      total_wall_time = time.time() - start\n      print('wall time: {}'.format(total_wall_time))\n    with tf.compat.v1.Session() as sess:\n      embeddings = tf.ones([num_features, 256])\n      id_tensor = tf.constant([x for x in range(num_features)], dtype=tf.int64)\n      input = tf.constant([[y % num_features\n                            for y in range(x, x + 2)]\n                           for x in range(num_features)],\n                          dtype=tf.int64)\n      output = distribution_ops.gather_embeddings_by_input(\n          id_tensor, embeddings, input, use_multi_threads=True)\n      start = time.time()\n      output = sess.run(output)\n      total_wall_time = time.time() - start\n      print('wall time: {}'.format(total_wall_time))\n\n  def test_map_id_to_embedding(self):\n    self.map_id_to_embedding(False)\n\n  def test_map_id_to_embedding_multi_threads(self):\n    self.map_id_to_embedding(True)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distribution_ops_fused_benchmark.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport shutil\nimport time\n\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\n\nfrom monolith.native_training import distribution_ops\n\n\ndef run_fused_reorder_by_indicies(suffices=None):\n\n  # Generate num_slots of lists of int64, where number of unique ids is around num_ids\n  num_ids = int(1e6)\n  num_slots = 30\n  num_of_shards = 256\n  int64 = np.iinfo(np.int64)\n  ids = list(\n      set(np.random.randint(low=int64.min, high=int64.max + 1, size=num_ids)))\n  split_indicies = [0] + sorted(np.random.choice(num_ids, num_slots))\n  ids_list = []\n  for i in range(1, len(split_indicies)):\n    slot_ids = ids[split_indicies[i - 1]:split_indicies[i]]\n    slot_ids = np.concatenate([slot_ids, slot_ids])  # force dups\n    np.random.shuffle(slot_ids)\n    ids_list.append(slot_ids)\n\n  # input: ids_list\n  session_config = tf.compat.v1.ConfigProto()\n  session_config.graph_options.rewrite_options.disable_meta_optimizer = False\n  session_config.graph_options.rewrite_options.memory_optimization = 1\n  session_config.intra_op_parallelism_threads = 4\n  with tf.compat.v1.Session(config=session_config) as sess:\n    ids_list = [ops.convert_to_tensor(ids, dtype=tf.int64) for ids in ids_list]\n    reorder_op = distribution_ops.fused_reorder_by_indices(\n        ids_list, num_of_shards=num_of_shards)\n    start = time.time()\n    _ = sess.run(reorder_op)\n    return time.time() - start\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  # np.random.seed(1234)\n  print('> Sess.run Wall Time:',\n        np.average([run_fused_reorder_by_indicies() for _ in range(5)]))\n"
  },
  {
    "path": "monolith/native_training/distribution_ops_fused_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport tensorflow as tf\nimport itertools\n\nfrom monolith.native_training import distribution_ops\n\n\nclass DistributionOpsTest(tf.test.TestCase):\n  \n  def test_benchmark(self):\n    num_tables = 128\n    \n    with tf.compat.v1.Session() as sess:\n      ids_list = [tf.random.uniform((10240,), 0, 1<<31, dtype=tf.int64) for i in range(num_tables)]\n      reorder_op = distribution_ops.fused_reorder_by_indices(\n          ids_list, num_of_shards=8, dim_sizes=[16 for _ in range(len(ids_list))])\n      sess.run(reorder_op)\n\n  def _test_fused_reorder_by_indices(self,\n                                     ids_list,\n                                     shard_num,\n                                     expected_output,\n                                     expected_split_sizes,\n                                     expected_sharded_slot_sizes,\n                                     dim_sizes=None,\n                                     expected_embedding_offsets=None):\n    if dim_sizes is None:\n      # Fake dim_sizes for testing\n      dim_sizes = [2 for _ in range(len(ids_list))]\n    with tf.compat.v1.Session() as sess:\n      ids_list = [tf.convert_to_tensor(ids, dtype=tf.int64) for ids in ids_list]\n      reorder_op = distribution_ops.fused_reorder_by_indices(\n          ids_list, num_of_shards=shard_num, dim_sizes=dim_sizes)\n      print('>>>', reorder_op[4])\n      output, split_sizes, sharded_slot_sizes, _, embedding_offsets = sess.run(\n          reorder_op)\n\n      self.assertAllEqual(output, expected_output)\n      self.assertAllEqual(split_sizes, expected_split_sizes)\n      self.assertAllEqual(sharded_slot_sizes, expected_sharded_slot_sizes)\n\n    if expected_embedding_offsets:\n      self.assertAllEqual(embedding_offsets, list(itertools.chain(*expected_embedding_offsets)))\n\n  def test_fused_reorder_by_indices(self):\n\n    # ids_list, shard_num\n    # expected_output, expected_split_sizes, expected_sharded_slot_sizes\n    self._test_fused_reorder_by_indices(\n        # Fallback to original reorder_by_indices,\n        #  but keeping the inner-merged-slot order\n        [[0, 1, 2, 2, 3, 5]],\n        3,\n        [0, 3, 1, 2, 5],\n        [2, 1, 2],\n        [2, 1, 2])\n\n    self._test_fused_reorder_by_indices(\n        # Extra slot\n        [[0, 1, 2, 2, 3, 5], []],\n        3,\n        [0, 3, 1, 2, 5],\n        [2, 1, 2],\n        [2, 0, 1, 0, 2, 0])\n\n    self._test_fused_reorder_by_indices(\n        # plus 2*shard_num\n        [[0, 1, 2, 2, 3, 5], [6, 7, 8, 8, 9, 11]],\n        3,\n        [0, 3, 6, 9, 1, 7, 2, 5, 8, 11],\n        [4, 2, 4],\n        [2, 2, 1, 1, 2, 2])\n\n    self._test_fused_reorder_by_indices(\n        # Empty slots\n        [[], []],\n        2,\n        [],\n        [0, 0],\n        [0, 0, 0, 0])\n\n    self._test_fused_reorder_by_indices([[0, 1, 4, 5], [2, 3, 6, 7]], 2,\n                                        [0, 4, 2, 6, 1, 5, 3, 7], [4, 4],\n                                        [2, 2, 2, 2])\n\n    self._test_fused_reorder_by_indices([[0, 1, 0], [3, 2, 3], [5, 6, 7]],\n                                        2, [0, 2, 6, 1, 3, 5, 7], [3, 4],\n                                        [1, 1, 1, 1, 1, 2],\n                                        dim_sizes=[1, 2, 3],\n                                        expected_embedding_offsets=[[0, 6, 0],\n                                                                    [7, 1, 7],\n                                                                    [9, 3, 12]])\n\n    self._test_fused_reorder_by_indices(\n        # Imagine the expected fused_embeddings as follows:\n        #   [1.1, 1.2, 1.3,     # 3   # slot 0, dim 3, offset 0\n        #    2.1, 2.2,          # 6   # slot 1, dim 2, offset 3\n        #    3.1, 3.2, 3.3,     # 1   # slot 0, dim 3, offset 5\n        #    4.1, 4.2, 4.3,     # 7   # slot 0, dim 3, offset 8\n        #    5.1, 5.2,          # 4   # slot 1, dim 2, offset 11\n        #    6.1, 6.2, 6.3,     # 2   # slot 0, dim 3, offset 13\n        #    7.1, 7.2,          # 5   # slot 1, dim 2, offset 16\n        #    8.1, 8.2,          # 8   # slot 1, dim 2, offset 18\n        #    9.1, 9.2],         # 11  # slot 1, dim 2, offset 20\n        [[2, 3, 1, 2, 7, 2], [5, 8, 4, 4, 5, 11, 6]],\n        3,\n        [3, 6, 1, 7, 4, 2, 5, 8, 11],\n        [2, 3, 4],\n        [1, 1, 2, 1, 1, 3],\n        dim_sizes=[3, 2],\n        expected_embedding_offsets=[[13, 0, 5, 13, 8, 13],\n                                    [16, 18, 11, 11, 16, 20, 3]])\n\n  def test_ragged_tensor_workflow(self):\n    with tf.Graph().as_default():\n      a = tf.RaggedTensor.from_tensor(tf.constant([[0], [1]], dtype=tf.int64))\n      b = tf.RaggedTensor.from_tensor(tf.constant([[2], [3]], dtype=tf.int64))\n      c = tf.RaggedTensor.from_tensor(tf.constant([[4], [5]], dtype=tf.int64))\n      d = tf.RaggedTensor.from_tensor(tf.constant([[6], [7]], dtype=tf.int64))\n      # Currently for merged slots A, B\n      # the order ['A', 'B'] is based on merged_slot_to_config;\n      # the mapping is based on MergedMultiTypeHashTable._slot_mapping: {'a': 'A', 'b': 'B', 'c': 'A', 'd', 'B'}\n      merged_slot_values = [\n          tf.concat([a.values, c.values], 0),\n          tf.concat([b.values, d.values], 0)\n      ]\n      self._test_fused_reorder_by_indices(merged_slot_values, 2,\n                                          [0, 4, 2, 6, 1, 5, 3, 7], [4, 4],\n                                          [2, 2, 2, 2])\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distribution_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.python.framework import test_util\nfrom monolith.native_training import distribution_ops\nimport random\n\n\nclass DistributionOpsTest(tf.test.TestCase):\n\n  def test_split_by_indices(self):\n    with tf.compat.v1.Session() as sess:\n      ids = tf.constant([0, 1, 2, 2, 3], dtype=tf.int64)\n      indices = tf.math.floormod(ids, 3)\n      splits = distribution_ops.split_by_indices(indices, ids, num_splits=3)\n      splits = sess.run(splits)\n\n    expected_splits = [[0, 3], [1], [2, 2]]\n    for split, expected_split in zip(splits, expected_splits):\n      self.assertAllEqual(split, expected_split)\n\n  def test_reorder_by_indices(self):\n    with tf.compat.v1.Session() as sess:\n      ids = tf.constant([0, 1, 2, 2, 3, 5], dtype=tf.int64)\n      indices = tf.cast(tf.math.floormod(ids, 3), dtype=tf.int32)\n      reorder_op = distribution_ops.reorder_by_indices(ids,\n                                                       indices,\n                                                       num_of_shards=3)\n      output, split_sizes = sess.run(reorder_op)\n\n    expected_output = [3, 0, 1, 5, 2]\n    expected_split_sizes = [2, 1, 2]\n    self.assertAllEqual(output, expected_output)\n    self.assertAllEqual(split_sizes, expected_split_sizes)\n\n  def test_split_by_indices_gradient(self):\n    with self.session() as sess:\n      indices = tf.constant([0, 1, 0], dtype=tf.int64)\n      tensor = tf.constant([[0, 0], [1, 1], [2, 2]], dtype=tf.float32)\n      splits = distribution_ops.split_by_indices(indices, tensor, num_splits=3)\n      grad = tf.gradients(splits, tensor)[0]\n      grad = sess.run(grad)\n    self.assertAllEqual(grad, [[1, 1], [1, 1], [1, 1]])\n\n  def test_split_by_indices_empty_gradient(self):\n    with self.session() as sess:\n      indices = tf.constant([], dtype=tf.int64)\n      tensor = tf.constant([], dtype=tf.float32)\n      splits = distribution_ops.split_by_indices(indices, tensor, num_splits=3)\n      grad, = tf.gradients(splits, tensor)\n      grad = sess.run(grad)\n    self.assertAllEqual(grad, [])\n\n  def test_ragged_split_by_indices(self):\n    with self.session() as sess:\n      indices = tf.constant([0, 1, 0, 1], dtype=tf.int64)\n      num = tf.ragged.constant([[], [], [4, 3, 2], [1], [], []], dtype=tf.int64)\n      splits, pos = distribution_ops.ragged_split_by_indices(indices,\n                                                             num,\n                                                             num_splits=2)\n      splits, pos = sess.run([splits, pos])\n    expected_splits = (\n        [[], [], [4, 2], [], [], []],\n        [[], [], [3], [1], [], []],\n    )\n    for split, expected_split in zip(splits, expected_splits):\n      self.assertAllEqual(split, expected_split)\n\n    expected_pos = (\n        [[], [], [0, 2], [], [], []],\n        [[], [], [1], [3], [], []],\n    )\n    for p1, p2 in zip(pos, expected_pos):\n      self.assertAllEqual(p1, p2)\n\n  def test_unique_key_with_value_and_offset_and_fill_with_offset_map(self):\n    key = tf.ragged.constant([[], [0, 1, 2, 1, 0], [0, 1, 0], []],\n                             dtype=tf.int64)\n    dims = [1, 2, 3, 4]\n    result = distribution_ops.unique_key_with_value_and_offset(key, dims)\n    self.assertAllEqual(result.unique_key, [[], [0, 1, 2], [0, 1], []])\n    self.assertAllEqual(result.value_offset,\n                        [[], [[0, 8], [2, 6], [4]], [[10, 16], [13]], []])\n    value = tf.range(12, dtype=tf.float32)\n    filled_tensor = distribution_ops.fill_with_offset_map(\n        tf.ragged.constant([[], [0, 1, 2], [3, 4], []], dtype=tf.int64), value,\n        result.value_offset, result.value_buffer, dims)\n\n    buffer = distribution_ops.finalize_shared_tensor([filled_tensor],\n                                                     dtype=tf.float32,\n                                                     shape=[None])\n    self.assertAllEqual(\n        buffer, [0, 1, 2, 3, 4, 5, 2, 3, 0, 1, 6, 7, 8, 9, 10, 11, 6, 7, 8])\n    grad, = tf.gradients([buffer], [value], [tf.range(19, dtype=tf.float32)])\n    self.assertAllEqual(grad, [8, 10, 8, 10, 4, 5, 26, 28, 30, 13, 14, 15])\n\n  def test_fill_with_offset_map_error_case(self):\n    key = tf.ragged.constant([[], [0, 1, 2, 1, 0], [0, 1, 0], []],\n                             dtype=tf.int64)\n    dims = [1, 2, 3, 4]\n    result = distribution_ops.unique_key_with_value_and_offset(key, dims)\n    value = tf.range(10, dtype=tf.float32)  # expected size: 12\n    filled_tensor = distribution_ops.fill_with_offset_map(\n        tf.ragged.constant([[], [0, 1, 2], [3, 4], []], dtype=tf.int64), value,\n        result.value_offset, result.value_buffer, dims)\n    with self.assertRaises(tf.errors.InvalidArgumentError):\n      self.evaluate(filled_tensor)\n\n  def test_unique_key_with_value_and_offset_empty(self):\n    key = tf.ragged.constant([[], [], []], dtype=tf.int64)\n    result = distribution_ops.unique_key_with_value_and_offset(key, [1, 2, 3])\n    self.assertAllEqual(result.unique_key, [[], [], []])\n    self.assertAllEqual(result.value_offset, [[], [], []])\n\n  def test_map_id_to_embedding(self):\n    with tf.compat.v1.Session() as sess:\n      ids1 = tf.constant([1], dtype=tf.int64)\n      embeddings1 = tf.constant([[1, 1]], dtype=tf.float32)\n      ids2 = tf.constant([2], dtype=tf.int64)\n      embeddings2 = tf.constant([[2, 2]], dtype=tf.float32)\n      input = tf.constant([[1], [2]], dtype=tf.int64)\n      output = distribution_ops.map_id_to_embedding([ids1, ids2],\n                                                    [embeddings1, embeddings2],\n                                                    input,\n                                                    use_multi_threads=False)\n      output = sess.run(output)\n    self.assertAllEqual(output, [[[1, 1]], [[2, 2]]])\n\n  def test_map_id_to_embedding_multi_threads(self):\n    with tf.compat.v1.Session() as sess:\n      num_elements, dim, ps_num = 1000, 16, 10\n      ids = tf.constant([x for x in range(num_elements)], dtype=tf.int64)\n      embeddings = tf.constant(\n          [[x for x in range(dim)] for _ in range(num_elements)],\n          dtype=tf.float32)\n\n      indices = tf.math.floormod(ids, ps_num)\n      split_ids = distribution_ops.split_by_indices(indices, ids, ps_num)\n      split_embeddings = distribution_ops.split_by_indices(\n          indices, embeddings, ps_num)\n      embeddings_mapped = distribution_ops.map_id_to_embedding(\n          split_ids, split_embeddings, ids, use_multi_threads=True)\n\n      embeddings = sess.run(embeddings)\n      embeddings_mapped = sess.run(embeddings_mapped)\n\n    self.assertAllEqual(embeddings, embeddings_mapped)\n\n  def test_map_id_to_embedding_gradient(self):\n    with self.session() as sess:\n      ids1 = tf.constant([1], dtype=tf.int64)\n      embeddings1 = tf.constant([[0, 0]], dtype=tf.float32)\n      ids2 = tf.constant([2], dtype=tf.int64)\n      embeddings2 = tf.constant([[0, 0]], dtype=tf.float32)\n      input = tf.constant([1, 1, 2], dtype=tf.int64)\n      output = distribution_ops.map_id_to_embedding([ids1, ids2],\n                                                    [embeddings1, embeddings2],\n                                                    input,\n                                                    use_multi_threads=False)\n      target_output = tf.constant([[2, 2], [2, 2], [2, 2]], dtype=tf.float32)\n      loss = target_output - output\n      grads = tf.gradients(loss, [embeddings1, embeddings2])\n      grads = sess.run(grads)\n\n    expected_grads = [[[-2, -2]], [[-1, -1]]]\n    for grads_part, expexted_grads_part in zip(grads, expected_grads):\n      self.assertAllEqual(grads_part, expexted_grads_part)\n\n  def test_gather_embeddings_by_ids(self):\n    with tf.compat.v1.Session() as sess:\n      ids = tf.constant([1, 2, 3], dtype=tf.int64)\n      embeddings = tf.constant([[1, 1], [2, 2], [3, 3]], dtype=tf.float32)\n      input = tf.constant([[2], [1], [2]], dtype=tf.int64)\n      output = distribution_ops.gather_embeddings_by_input(\n          ids, embeddings, input)\n      output, index_mapping = sess.run(output)\n    self.assertAllEqual(output, [[[2, 2]], [[1, 1]], [[2, 2]]])\n    self.assertAllEqual(index_mapping, [[1], [0], [1]])\n\n  def test_gather_embeddings_by_ids_gradient(self):\n    with self.session() as sess:\n      ids = tf.constant([1, 2, 3], dtype=tf.int64)\n      embeddings = tf.constant([[1, 1], [2, 2], [3, 3]], dtype=tf.float32)\n      input = tf.constant([[1], [2], [1]], dtype=tf.int64)\n      output, index_mapping = distribution_ops.gather_embeddings_by_input(\n          ids, embeddings, input)\n\n      target_output = tf.constant([[[2, 2]], [[2, 2]], [[2, 2]]],\n                                  dtype=tf.float32)\n      loss = target_output - output\n      grads = tf.gradients(loss, embeddings)\n      grads = sess.run(grads)\n\n    expected_grads = [[-2, -2], [-1, -1], [0, 0]]\n    self.assertAllEqual(grads[0], expected_grads)\n\n  def test_gather_embeddings_by_ids_gradient_back_prop(self):\n    with self.session() as sess:\n      ids = tf.constant([2, 3, 1], dtype=tf.int64)\n      grads = tf.constant([[1, 1], [2, 2], [4, 4], [8, 8]], dtype=tf.float32)\n      # implies the input tensor with id value [3, 2, 3, 1]\n      index_mapping = tf.constant([1, 0, 1, 2], dtype=tf.int64)\n      emb_grads = distribution_ops.gather_embeddings_by_ids_gradient_back_prop(\n          ids, grads, index_mapping)\n    self.assertAllEqual(emb_grads, [[2, 2], [5, 5], [8, 8]])\n\n  @test_util.run_gpu_only\n  def test_fused_gather_embeddings_by_input(self):\n    with tf.compat.v1.Session() as sess, test_util.use_gpu():\n      # inputs = [\n      #   tf.constant([2, 3, 1, 2, 7, 2], dtype=tf.int64),\n      #   tf.constant([5, 8, 4, 4, 5, 11, 6], dtype=tf.int64)\n      # ]\n      # shard_indices: [[2, 0, 1, 2, 1, 2], [2, 2, 1, 1, 2, 2, 0]]\n      # fused_ids: [3, 6, 1, 7, 4, 2, 5, 8, 11]\n      # fused_slot_sizes: [1, 1, 2, 1, 1, 3]\n      embedding_dims = [3, 2]\n      fused_embeddings = tf.constant([\n          1.1, 1.2, 1.3, 2.1, 2.2, 3.1, 3.2, 3.3, 4.1, 4.2, 4.3, 5.1, 5.2, 6.1,\n          6.2, 6.3, 7.1, 7.2, 8.1, 8.2, 9.1, 9.2\n      ],\n                                     dtype=tf.float32)\n      SCALE = (12345, 11777\n              )  # To test the number of elements larger than GPU grid\n      fused_embedding_offsets = [\n          tf.constant([13, 0, 5, 13, 8, 13] * SCALE[0], dtype=tf.int32),\n          tf.constant([16, 18, 11, 11, 16, 20, 3] * SCALE[1], dtype=tf.int32)\n      ]\n      output = distribution_ops.fused_gather_embeddings_by_input(\n          fused_embeddings, fused_embedding_offsets, embedding_dims)\n      outputs = sess.run(output)\n\n    expected_outputs = [[[6.1, 6.2, 6.3], [1.1, 1.2, 1.3], [3.1, 3.2, 3.3],\n                         [6.1, 6.2, 6.3], [4.1, 4.2, 4.3], [6.1, 6.2, 6.3]] *\n                        SCALE[0],\n                        [[7.1, 7.2], [8.1, 8.2], [5.1, 5.2], [5.1, 5.2],\n                         [7.1, 7.2], [9.1, 9.2], [2.1, 2.2]] * SCALE[1]]\n    self.assertAllClose(outputs, expected_outputs)\n\n  def test_fused_gather_embeddings_by_input_gradient(self):\n    with tf.compat.v1.Session() as sess, test_util.use_gpu():\n      # The size of one-dimensional fused_embeddings.\n      with tf.device(\"CPU:0\"):\n        fused_embeddings_size = tf.constant(22, dtype=tf.int32)\n      embedding_dims = [3, 2]\n      SCALE = 888  # To test float sum precision loss on CPU and GPU\n      grads = [\n          tf.constant([[1.1, 1.2, 1.3], [2.1, 2.2, 2.3], [3.1, 3.2, 3.3],\n                       [4.1, 4.2, 4.3], [5.1, 5.2, 5.3], [6.1, 6.2, 6.3]] *\n                      SCALE,\n                      dtype=tf.float32),\n          tf.constant([[1.4, 1.5], [2.4, 2.5], [3.4, 3.5], [4.4, 4.5],\n                       [5.4, 5.5], [6.4, 6.5], [7.4, 7.5]] * SCALE,\n                      dtype=tf.float32)\n      ]\n      embedding_offsets = [\n          tf.constant([13, 0, 5, 13, 8, 13] * SCALE, dtype=tf.int32),\n          tf.constant([16, 18, 11, 11, 16, 20, 3] * SCALE, dtype=tf.int32)\n      ]\n      output_t = distribution_ops.fused_gather_embeddings_by_input_gradient(\n          fused_embeddings_size, grads, embedding_offsets, embedding_dims)\n      self.assertAllEqual(output_t.shape[0],\n                          22)  # shape inference when applicable\n      output = sess.run(output_t)\n    expected_output = [\n        2.1,\n        2.2,\n        2.3,  # id 3   offset 0\n        7.4,\n        7.5,  # id 6   offset 3\n        3.1,\n        3.2,\n        3.3,  # id 1   offset 5\n        5.1,\n        5.2,\n        5.3,  # id 7   offset 8\n        7.8,\n        8.0,  # id 4   offset 11\n        11.3,\n        11.6,\n        11.9,  # id 2   offset 13\n        6.8,\n        7.0,  # id 5   offset 16\n        2.4,\n        2.5,  # id 8   offset 18\n        6.4,\n        6.5,  # id 11  offset 20\n    ]\n    self.assertAllClose(output,\n                        np.asarray(expected_output) * SCALE,\n                        rtol=1e-7 * SCALE)\n\n  def test_reduce_mean(self):\n    with tf.compat.v1.Session() as sess:\n      id_indices = tf.constant([[0], [0], [1]], dtype=tf.int64)\n      id_values = tf.constant([[4, 4], [2, 2], [1, 1]], dtype=tf.float32)\n      reduced = distribution_ops.reduce_mean(id_indices, id_values, [2])\n      reduced = sess.run(reduced)\n    self.assertAllEqual(reduced, [[3, 3], [1, 1]])\n\n  def test_reduce_mean_gradient(self):\n    with self.session() as sess:\n      id_indices = tf.constant([[0], [0]], dtype=tf.int64)\n      id_values = tf.constant([[0, 0], [0, 0]], dtype=tf.float32)\n      reduced = distribution_ops.reduce_mean(id_indices, id_values, [1])\n      target = tf.constant([[-2, -4]], dtype=tf.float32)\n      loss = target - 2 * reduced\n      grads = tf.gradients(loss, id_values)[0]\n      grads = sess.run(grads)\n    self.assertAllEqual(grads, [[-1, -1], [-1, -1]])\n\n  def test_reduce_sum(self):\n    with tf.compat.v1.Session() as sess:\n      id_indices = tf.constant([[0], [0], [1]], dtype=tf.int64)\n      id_values = tf.constant([[1, 1], [2, 2], [4, 4]], dtype=tf.float32)\n      reduced = distribution_ops.reduce_sum(id_indices, id_values, [2])\n      reduced = sess.run(reduced)\n    self.assertAllEqual(reduced, [[3, 3], [4, 4]])\n\n  def test_reduce_sum_gradient(self):\n    with self.session() as sess:\n      id_indices = tf.constant([[0], [0]], dtype=tf.int64)\n      id_values = tf.constant([[0, 0], [0, 0]], dtype=tf.float32)\n      reduced = distribution_ops.reduce_sum(id_indices, id_values, [1])\n      target = tf.constant([[10, 99]], dtype=tf.float32)\n      loss = target - reduced\n      grads = tf.gradients(loss, id_values)[0]\n      grads = sess.run(grads)\n    self.assertAllEqual(grads, [[-1, -1], [-1, -1]])\n\n  def test_reduce_sqrtn(self):\n    with tf.compat.v1.Session() as sess:\n      id_indices = tf.constant([[0], [0], [1]], dtype=tf.int64)\n      id_values = tf.constant([[3, 3], [4, 4], [4, 4]], dtype=tf.float32)\n      reduced = distribution_ops.reduce_sqrtn(id_indices, id_values, [2])\n      reduced = sess.run(reduced)\n    self.assertAllClose(reduced, [[5, 5], [4, 4]])\n\n  def test_reduce_sqrtn_gradient(self):\n    with self.session() as sess:\n      id_indices = tf.constant([[0], [0]], dtype=tf.int64)\n      id_values = tf.constant([[3, 4], [4, 3]], dtype=tf.float32)\n      reduced = distribution_ops.reduce_sqrtn(id_indices, id_values, [1])\n      target = tf.constant([[10, 15]], dtype=tf.float32)\n      loss = target - reduced\n      grads = tf.gradients(loss, id_values)[0]\n      grads = sess.run(grads)\n    self.assertAllClose(grads, [[-0.6, -0.8], [-0.8, -0.6]])\n\n  def test_reduce_sqrtn_gradient_zero(self):\n    with self.session() as sess:\n      id_indices = tf.constant([[0], [0]], dtype=tf.int64)\n      id_values = tf.constant([[0, 0], [0, 0]], dtype=tf.float32)\n      reduced = distribution_ops.reduce_sqrtn(id_indices, id_values, [1])\n      target = tf.constant([[10, 15]], dtype=tf.float32)\n      loss = target - reduced\n      grads = tf.gradients(loss, id_values)[0]\n      grads = sess.run(grads)\n    self.assertAllClose(grads, [[0, 0], [0, 0]])\n\n  def test_fused_reduce_sum_and_split(self):\n    # Test split.\n    with tf.compat.v1.Session() as sess, sess.graph.device(lambda op: '/CPU:0'):\n      id_indices = tf.constant([0, 0, 1], dtype=tf.int64)\n      id_values = tf.constant([[1, 1, 1], [2, 2, 1], [4, 4, 2]],\n                              dtype=tf.float32)\n      reduced = distribution_ops.fused_reduce_sum_and_split(\n          id_indices, id_values, 2, [2, 1])\n      reduced = sess.run(reduced)\n    self.assertAllEqual(reduced[0], [[3, 3], [4, 4]])\n    self.assertAllEqual(reduced[1], [[2], [2]])\n    # Test a different split type.\n    with tf.compat.v1.Session() as sess, sess.graph.device(lambda op: '/CPU:0'):\n      id_indices = tf.constant([0, 0, 1], dtype=tf.int64)\n      id_values = tf.constant([[1, 1, 1], [2, 2, 1], [4, 4, 2]],\n                              dtype=tf.float32)\n      reduced = distribution_ops.fused_reduce_sum_and_split(\n          id_indices, id_values, 2, [1, 2])\n      reduced = sess.run(reduced)\n    self.assertAllEqual(reduced[0], [[3], [4]])\n    self.assertAllEqual(reduced[1], [[3, 2], [4, 2]])\n    # Test non-consecutive indicies\n    with tf.compat.v1.Session() as sess, sess.graph.device(lambda op: '/CPU:0'):\n      id_indices = tf.constant([0, 0, 2], dtype=tf.int64)\n      id_values = tf.constant([[1, 1, 1], [2, 2, 1], [4, 4, 2]],\n                              dtype=tf.float32)\n      reduced = distribution_ops.fused_reduce_sum_and_split(\n          id_indices, id_values, 4, [1, 2])\n      reduced = sess.run(reduced)\n    self.assertAllEqual(reduced[0], [[3], [0], [4], [0]])\n    self.assertAllEqual(reduced[1], [[3, 2], [0, 0], [4, 2], [0, 0]])\n\n  def test_fused_reduce_sum_and_split_grad(self):\n    # Test split.\n    with tf.compat.v1.Session() as sess, sess.graph.device(lambda op: '/CPU:0'):\n      id_indices = tf.constant([0, 0, 1], dtype=tf.int64)\n      id_values = tf.constant([[1, 1, 1], [2, 2, 1], [4, 4, 2]],\n                              dtype=tf.float32)\n      reduced_result = distribution_ops.fused_reduce_sum_and_split(\n          id_indices, id_values, 2, [2, 1])\n      grads = tf.gradients(reduced_result, id_values)[0]\n      grads = sess.run(grads)\n    self.assertAllEqual(grads, [[1, 1, 1], [1, 1, 1], [1, 1, 1]])\n\n  @test_util.run_gpu_only\n  def test_fused_reduce_scatter(self):\n    with tf.compat.v1.Session() as sess, test_util.use_gpu():\n      id_indices = [\n          tf.constant([0, 0, 1], dtype=tf.int32),\n          tf.constant([0, 0, 1], dtype=tf.int32),\n          tf.constant([], dtype=tf.int32, shape=[0]),\n          tf.constant([0, 0, 2, 2], dtype=tf.int32),\n      ]\n      id_values = [\n          tf.constant([[1, 1, 1], [2, 2, 1], [4, 4, 2]], dtype=tf.float32),\n          tf.constant([[1, 1, 1], [2, 2, 1], [4, 4, 2]], dtype=tf.float32),\n          tf.constant([], dtype=tf.float32, shape=[0, 3]),\n          tf.constant([[1, 1, 1, 1, 1], [2, 2, 1, 1, 1], [4, 4, 2, 2, 2],\n                       [4, 4, 2, 2, 2]],\n                      dtype=tf.float32)\n      ]\n      shapes = [(2, 3), (4, 3), (2, 3), (4, 5)]\n      reduced_tensors = distribution_ops.fused_sorted_segment_sum(\n          id_indices, id_values, shapes)\n      truth_tensors = [\n          tf.scatter_nd(tf.expand_dims(i, -1), v, s)\n          for i, v, s in zip(id_indices, id_values, shapes)\n      ]\n\n      reduced = sess.run(reduced_tensors)\n      truth = sess.run(truth_tensors)\n      expected = [[[3, 3, 2], [4, 4, 2]],\n                  [[3, 3, 2], [4, 4, 2], [0, 0, 0], [0, 0, 0]],\n                  [[0, 0, 0], [0, 0, 0]],\n                  [[3, 3, 2, 2, 2], [0, 0, 0, 0, 0], [8, 8, 4, 4, 4],\n                   [0, 0, 0, 0, 0]]]\n      for r, e, t in zip(reduced, expected, truth):\n        self.assertAllClose(r, e)\n        self.assertAllClose(e, t)\n      # Gradient Check\n      gs_expected = sess.run(tf.gradients(truth_tensors, id_values))\n      gs = sess.run(tf.gradients(reduced_tensors, id_values))\n      self.assertAllClose(gs, gs_expected)\n\n  @test_util.run_gpu_only\n  def test_fused_reduce_and_split_gpu(self):\n    num_rows = 102\n    batch_size = 256\n    emb_lens = [i * 2 - 1 for i in range(1, num_rows + 1)]\n    slice_dims = []\n    for l in emb_lens:\n      if l < 4:\n        slices = [1 for i in range(l)]\n      else:\n        slices = [l // 4] * 4\n        slices[-1] += l % 4\n      slice_dims.append(slices)\n\n    row_lens = [i for i in range(0, batch_size)]\n    random.shuffle(row_lens)\n    rows_before_reduction = sum(row_lens)\n    shapes = [\n        tf.convert_to_tensor([batch_size, emb_lens[i]], dtype=tf.int64)\n        for i in range(num_rows)\n    ]\n    ragged_tensors = [tf.ragged.range(row_lens) for j in range(num_rows)]\n    value_rowids = [t.value_rowids() for t in ragged_tensors]\n    splits = [t.row_splits for t in ragged_tensors]\n\n    with tf.compat.v1.Session() as sess, test_util.use_gpu():\n      embeddings = [\n          tf.ones((rows_before_reduction, emb_lens[i])) for i in range(num_rows)\n      ]\n\n      outputs = distribution_ops.fused_reduce_and_split_gpu(\n          splits, embeddings, slice_dims)\n      outputs2 = []\n      for i in range(num_rows):\n        temp1 = tf.scatter_nd(tf.expand_dims(value_rowids[i], -1),\n                              embeddings[i], shapes[i])\n        outputs2.extend(tf.split(temp1, slice_dims[i], axis=1))\n      self.assertEqual(len(outputs), len(outputs2))\n      for i in range(len(outputs)):\n        rand = tf.random.uniform(outputs[i].shape)\n        outputs[i] *= rand\n        outputs2[i] *= rand\n\n      grads = tf.gradients(outputs, embeddings)\n      grads2 = tf.gradients(outputs2, embeddings)\n\n      val_flags = []\n      for i in range(len(outputs)):\n        val_flags.append(tf.reduce_all(tf.equal(outputs[i], outputs2[i])))\n      val_flag = tf.reduce_all(val_flags)\n\n      self.assertEqual(len(grads), len(grads2))\n      grad_flags = []\n      for i in range(len(grads)):\n        grad_flags.append(tf.reduce_all(tf.equal(grads[i], grads2[i])))\n      grad_flag = tf.reduce_all(grad_flags)\n\n      f1, f2 = sess.run([val_flag, grad_flag])\n      self.assertTrue(f1)\n      self.assertTrue(f2)\n\n  @test_util.run_gpu_only\n  def test_aligned_concat_split(self):\n    with tf.compat.v1.Session() as sess, test_util.use_gpu():\n      arrays = []\n      num_items = 155\n      for i in range(num_items):\n        num_dims = random.randint(1, 4)\n        arrays.append(tf.random.uniform([random.randint(1, 50) for _ in range(num_dims)]))\n      concat = distribution_ops.gen_distribution_ops.monolith_aligned_flat_concat(arrays)\n      splits = distribution_ops.gen_distribution_ops.monolith_aligned_flat_split(arrays, concat)\n      arrays, splits = sess.run([arrays, splits])\n      for i in range(num_items):\n        self.assertAllEqual(arrays[i], splits[i])\n      \n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/distribution_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nfrom absl import flags, logging\nimport tensorflow as tf\nfrom monolith.native_training.metric.metric_hook import ByteCCLTelemetryHook\n\nFLAGS = flags.FLAGS\n_SYNC_TRAIN_INITED = False\n\nenable_bps = int(os.getenv(\"MONOLITH_WITH_BYTEPS\", \"0\"))\n\n\ndef bps_init(uuid: str):\n  \"\"\"\n  Initialize BytePS.\n  Args:\n    uuid: uuid of the training job, used to distinguish concurrent BytePS processes across runs.\n  \"\"\"\n  # init bps only if needed\n  if os.environ.get('BYTEPS_ALLTOALL_SESSION_SIZE') is None:\n    os.environ[\"BYTEPS_ALLTOALL_SESSION_SIZE\"] = '3'\n\n  # set size, rank based on OMPI env vars\n  if os.environ.get('BYTEPS_LOCAL_SIZE', None) is None:\n    os.environ[\"BYTEPS_LOCAL_SIZE\"] = os.environ.get(\n        'OMPI_COMM_WORLD_LOCAL_SIZE')\n  local_size = int(os.environ.get('BYTEPS_LOCAL_SIZE'))\n  rank = int(os.environ.get('OMPI_COMM_WORLD_RANK'))\n  size = int(os.environ.get('OMPI_COMM_WORLD_SIZE'))\n  local_rank = rank % local_size\n  phy_node_id = int(rank / local_size)\n  socket_path = f\"/tmp/bps_{uuid}_socket_{phy_node_id}\"\n  gdr_alltoall = os.environ.get('MONOLITH_WITH_BYTEPS_FWD_GDR', '0') == '1' or \\\n      os.environ.get('MONOLITH_WITH_BYTEPS_BWD_GDR', '0') == '1'\n\n  # gpu_nic_binding_mode: Default False, when True we bind gpu_id (0,1) to eth0, (2,3) to eth1...\n  # This is useful for A100 systems where we have topology in which some gpus are closer to some\n  # NICs.\n  gpu_nic_binding_mode = int(os.environ.get('BYTEPS_GPU_NIC_BINDING_MODE', 0))\n  if not gpu_nic_binding_mode:\n    # Constant binding mode (default), all GPUs use one NIC\n    interface = os.getenv(\"DMLC_INTERFACE\", \"eth0\")\n  else:\n    # gpu_nic_binding_mode binding mode\n    NUM_GPU_PER_NIC = 2\n    nic_id = int(local_rank // NUM_GPU_PER_NIC)\n    if gdr_alltoall:\n      os.environ[\"CUDA_VISIBLE_DEVICES\"] = str(local_rank)\n      numa_id = os.environ['BYTEPS_NUMA_ID']\n      print(\n          f\"GDR: set CUDA_VISIBLE_DEVICES={local_rank}, BYTEPS_NUMA_ID={numa_id}\"\n      )\n      os.environ['BYTEPS_PIN_MEMORY'] = \"1\"\n      os.environ['BYTEPS_PIN_MEMORY_CPU'] = os.environ.get(\n          'BYTEPS_PIN_MEMORY_CPU', '1')\n      os.environ['DMLC_NUM_CPU_DEV'] = \"0\"\n      os.environ['DMLC_NUM_GPU_DEV'] = \"1\"\n    os.environ['BYTEPS_USE_GDR_ALLREDUCE'] = os.environ.get(\n        'BYTEPS_USE_GDR_ALLREDUCE', '1')\n    interface = \"eth{}\".format(nic_id)\n\n    # Add all eth otherwise it may give out \"Destination not reachable\" error\n    # or block in some communication.\n    if os.environ.get('BYTEPS_WITH_ALL_NICS', '0') == '1':\n      os.environ[\n          \"UCX_NET_DEVICES\"] = \"mlx5_0:1,mlx5_1:1,mlx5_2:1,mlx5_3:1,eth0,eth1,eth2,eth3\"\n    else:\n      os.environ[\"UCX_NET_DEVICES\"] = \"mlx5_{}:1\".format(nic_id)\n\n  # scheduler connection info\n  cmd = f'ip addr show {interface}'\n  hostname = os.popen(\n      cmd +\n      ' | grep \"\\<inet\\>\" | awk \\'{ print $2 }\\' | awk -F \"/\" \\'{ print $1 }\\''\n  ).read().strip()\n  os.environ[\"UCX_RDMA_CM_SOURCE_ADDRESS\"] = hostname\n  os.environ[\"PSLITE_UCX_TLS\"] = os.environ.get('PSLITE_UCX_TLS',\n                                                'rc_x,tcp,self,cuda')\n  print(\n      f\"UCX: set PSLITE_UCX_TLS={os.environ['PSLITE_UCX_TLS']} {os.environ['UCX_NET_DEVICES']}\"\n  )\n  os.environ[\"DMLC_NODE_HOST\"] = hostname\n  os.environ[\"DMLC_ROLE\"] = 'joint'\n  os.environ[\"DMLC_ENABLE_UCX\"] = os.environ.get('DMLC_ENABLE_UCX', '1')\n\n  os.makedirs(socket_path, exist_ok=True)\n  os.environ[\"DMLC_WORKER_ID\"] = str(rank)\n  os.environ[\"DMLC_NUM_WORKER\"] = str(size)\n  os.environ[\"DMLC_NUM_SERVER\"] = str(size)\n  os.environ[\"BYTEPS_UUID\"] = uuid\n  os.environ[\"BYTEPS_LOCAL_RANK\"] = str(local_rank)\n  os.environ[\"BYTEPS_SOCKET_PATH\"] = socket_path\n  os.environ[\"BYTEPS_OMP_THREAD_PER_GPU\"] = os.environ.get(\n      \"BYTEPS_OMP_THREAD_PER_GPU\", \"1\")\n  os.environ[\"BYTEPS_FORCE_DISTRIBUTED\"] = '1'\n  os.environ[\"BYTEPS_TELEMETRY_ON\"] = os.environ.get(\"BYTEPS_TELEMETRY_ON\", '0')\n  os.environ[\"BYTEPS_LOG_LEVEL\"] = os.environ.get('BYTEPS_LOG_LEVEL', 'info')\n  os.environ[\"BYTEPS_SERVER_DIRECT_RESPONSE\"] = os.environ.get(\n      'BYTEPS_SERVER_DIRECT_RESPONSE', '2')\n  os.environ[\"BYTEPS_UCX_FORCE_REQ_ORDER\"] = '1'\n\n  # performance tuning knobs\n  os.environ[\"BYTEPS_KEY_HASH_FN\"] = os.environ.get('BYTEPS_KEY_HASH_FN',\n                                                    'djb2-colocate')\n  os.environ[\"BYTEPS_UCX_SHORT_THRESH\"] = os.environ.get(\n      'BYTEPS_UCX_SHORT_THRESH', '0')\n  os.environ[\"PSLITE_UCX_RNDV_THRESH\"] = os.environ.get(\n      \"PSLITE_UCX_RNDV_THRESH\", '8192')\n  os.environ[\"BYTEPS_WORKER_LOCAL_ROOT\"] = os.environ.get(\n      'BYTEPS_WORKER_LOCAL_ROOT', '-1')\n  # To enable async alltoall operations, we must reserve memory buffers on the receiver side.\n  # BYTEPS_P2P_PARTITION_BYTES sets the receive buffer size for each alltoall operation from each sender.\n  # It needs to be large enough such that the actual data sent does not exceed the buffer size, otherwise\n  # error message may occur\n  if os.environ.get(\"BYTEPS_P2P_PARTITION_BYTES\") is None:\n    alltoall_buff_size_per_rank = int(2048000 * 128 * 2 / size)\n    os.environ[\"BYTEPS_P2P_PARTITION_BYTES\"] = str(alltoall_buff_size_per_rank)\n  if os.environ.get(\"BYTEPS_PARTITION_BYTES\") is None:\n    allreduce_partition_size = 1024000 if size < 128 else 512000\n    os.environ[\"BYTEPS_PARTITION_BYTES\"] = str(allreduce_partition_size)\n\n  import byteps.tensorflow as bps\n  bps.init(lazy=False)\n\n\n# bps allreduce stress test\ndef byteps_benchmark_ar(total_len,\n                        total_niter=10000,\n                        use_cpu=False,\n                        op='pushpull'):\n  tf.compat.v1.enable_eager_execution()\n  import byteps.tensorflow as bps\n  import numpy as np\n  rank, size = bps.rank(), bps.size()\n  niter = 0\n  print(\n      f'===== start pushpull_benchmark {rank}/{size} total_len={total_len} =====',\n      flush=True)\n  device = tf.device(\"/gpu:0\" if not use_cpu else \"/cpu:0\")\n  with device:\n    tensor = tf.ones([total_len, 1], dtype=tf.float32) * (rank + 1)\n  t0 = time.time()\n  interval = 20\n  name = f'data_len_{total_len}_{op}_' + ('cpu' if use_cpu else 'gpu')\n  comm_fn = bps.push_pull\n  goodputs = []\n  while niter < total_niter:\n    with device:\n      result = comm_fn(tensor, average=True, name=name)\n    niter += 1\n    if niter % interval == 0:\n      t1 = time.time()\n      latency = (t1 - t0) / interval * 1000\n      goodput = total_len * 32 / latency / 1000000\n      goodputs.append(goodput)\n      rank == 0 and print(\n          f'DONE iter={niter}, latency={latency:.3} ms, Goodput={goodput:.5} Gb/s, is_cpu={use_cpu}',\n          flush=True)\n      t0 = time.time()\n  print(\n      f'===== end pushpull_benchmark {rank}/{size} total_len={total_len} =====',\n      flush=True)\n  return goodputs[1:]\n\n\n# bps all2all stress test\ndef byteps_benchmark_a2a(total_len,\n                         total_niter=10000,\n                         dst_gpu=True,\n                         src_gpu=True):\n  tf.compat.v1.enable_eager_execution()\n  # the CPU alltoall size is much smaller in real use cases\n  if not dst_gpu and not src_gpu:\n    total_len /= 8\n  import byteps.tensorflow as bps\n  import numpy as np\n  rank, size = bps.rank(), bps.size()\n  niter = 0\n  len_per_worker = int(total_len / size)\n  assert total_len % size == 0\n  p2p_matrix = np.array([len_per_worker] * (size * size)).reshape(size, size)\n  splits_list = list(p2p_matrix[rank])\n  recv_splits_list = list(p2p_matrix[:, rank])\n  print(\n      f'===== start all2all_benchmark {rank}/{size} total_len={total_len} =====',\n      flush=True)\n  with tf.device(\"/cpu:0\"):\n    splits = tf.constant(splits_list, dtype=tf.int32)\n    recv_splits = tf.constant(recv_splits_list, dtype=tf.int32)\n  with tf.device(\"/gpu:0\" if src_gpu else \"/cpu:0\"):\n    tensor = tf.ones([sum(splits_list), 1], dtype=tf.float32) * (rank + 1)\n  t0 = time.time()\n  interval = 20\n  name = f'data_len_{total_len}_'\n  alltoall_fn = bps.alltoall\n  if dst_gpu:\n    if src_gpu:\n      name += 'g2g'\n    else:\n      alltoall_fn = bps.alltoall_cpu2gpu\n      name += 'c2g'\n  else:\n    if src_gpu:\n      alltoall_fn = bps.alltoall_gpu2cpu\n      name += 'g2c'\n    else:\n      name += 'c2c'\n  goodputs = []\n  while niter < total_niter:\n    with tf.device(\"/gpu:0\" if src_gpu or dst_gpu else \"/cpu:0\"):\n      result = alltoall_fn(tensor,\n                           splits=splits,\n                           recv_splits=recv_splits,\n                           name=name)\n    niter += 1\n    if niter % interval == 0:\n      t1 = time.time()\n      latency = (t1 - t0) / interval * 1000\n      goodput = total_len * 32 / latency / 1000000\n      goodputs.append(goodput)\n      rank == 0 and print(\n          f'DONE iter={niter}, latency={latency:.3} ms, Goodput={goodput:.5} Gb/s',\n          flush=True)\n      t0 = time.time()\n  print(\n      f'===== end all2all_benchmark {rank}/{size} total_len={total_len} =====',\n      flush=True)\n  return goodputs[1:]\n\n\ndef bps_comm_benchmark():\n    benchmark_bps = os.environ.get(\"MONOLITH_BENCHMARK_BPS\", \"none\")\n    benchmark_iters = int(os.getenv(\"MONOLITH_BENCHMARK_ITERS\", \"200\"))\n    gpus = tf.config.experimental.list_physical_devices('GPU')\n    for gpu in gpus:\n       tf.config.experimental.set_memory_growth(gpu, True)\n    assert benchmark_bps in (\"c2g\", \"g2g\", \"c2c\", \"g2c\", \"ar\", \"all\"), benchmark_bps\n    benchmarks = [\"c2g\", \"g2g\", \"c2c\", \"g2c\", \"ar\"] if benchmark_bps == \"all\" else [benchmark_bps]\n    for benchmark in benchmarks:\n      results = []\n      dst_gpu = benchmark in (\"c2g\", \"g2g\")\n      src_gpu = benchmark in (\"g2c\", \"g2g\")\n      if benchmark == \"ar\":\n        total_len = int(os.getenv(\"MONOLITH_BENCHMARK_BPS_AR_LEN\", \"65536000\"))\n        goodputs_cpu = byteps_benchmark_ar(total_len, total_niter=benchmark_iters, use_cpu=True)\n        results.append((total_len, sum(goodputs_cpu) / len(goodputs_cpu)))\n        goodputs_gpu = byteps_benchmark_ar(total_len, total_niter=benchmark_iters, use_cpu=False)\n        results.append((total_len, sum(goodputs_gpu) / len(goodputs_gpu)))\n      else:\n        total_len = int(os.getenv(\"MONOLITH_BENCHMARK_BPS_A2A_LEN\", \"65536000\"))\n        for _ in range(3):\n          goodputs = byteps_benchmark_a2a(total_len, total_niter=benchmark_iters,\n                                          dst_gpu=dst_gpu, src_gpu=src_gpu)\n          results.append((total_len, sum(goodputs) / len(goodputs)))\n          total_len = total_len // 2\n      print(benchmark + \"_summary:\", results)\n\n\ndef init_sync_train_and_update_conf(dct_config):\n  global _SYNC_TRAIN_INITED\n  logging.info(\"Entering synchronous training.\")\n  # Import and init horovod/byteps on demand.\n  try:\n    if enable_bps:\n      if not _SYNC_TRAIN_INITED:\n        bps_init(dct_config.uuid)\n      import byteps.tensorflow as hvd\n\n      enable_bps_bcast = int(os.getenv(\"MONOLITH_WITH_BYTEPS_BCAST\", \"1\"))\n      enable_bps_allreduce = int(\n          os.getenv(\"MONOLITH_WITH_BYTEPS_ALLREDUCE\", \"1\"))\n      if enable_bps_bcast == 0 or enable_bps_allreduce == 0:\n        import horovod.tensorflow as hvd\n        if not _SYNC_TRAIN_INITED:\n          hvd.init()\n          _SYNC_TRAIN_INITED = True\n          if not dct_config.merge_sync_training_ckpt:\n            model_dir_suffix = 'index-{:04}'.format(hvd.rank())\n            model_dir = os.path.join(dct_config.model_dir, dct_config.uuid,\n                                     model_dir_suffix)\n            dct_config.model_dir = model_dir\n    else:\n      import horovod.tensorflow as hvd\n      if not _SYNC_TRAIN_INITED:\n        hvd.init()\n        _SYNC_TRAIN_INITED = True\n        if not dct_config.merge_sync_training_ckpt:\n          model_dir_suffix = 'index-{:04}'.format(hvd.rank())\n          model_dir = os.path.join(dct_config.model_dir, dct_config.uuid,\n                                   model_dir_suffix)\n          dct_config.model_dir = model_dir\n\n    dct_config.num_ps = 0\n    dct_config.reorder_fids_in_data_pipeline = True\n    dct_config.index = hvd.rank()\n    dct_config.num_workers = hvd.size()\n    dct_config.enable_variable_partition = False\n  except (ImportError, tf.errors.NotFoundError) as e:\n    logging.warning(f'init_sync_train_and_get_index error {e}')\n\n\ndef get_mpi_rank():\n  rank = 0\n  if 'OMPI_COMM_WORLD_RANK' in os.environ:\n    rank = int(os.environ.get('OMPI_COMM_WORLD_RANK'))\n  else:\n    logging.warning(f\"get_mpi_rank use default 0\")\n  return rank\n\n\ndef get_mpi_local_rank():\n  local_rank = 0\n  if 'OMPI_COMM_WORLD_LOCAL_RANK' in os.environ:\n    local_rank = int(os.environ.get('OMPI_COMM_WORLD_LOCAL_RANK'))\n  else:\n    logging.warning(f\"get_mpi_local_rank use default 0\")\n  return local_rank\n\n\ndef get_mpi_size():\n  size = 1\n  if 'OMPI_COMM_WORLD_SIZE' in os.environ:\n    size = int(os.environ.get('OMPI_COMM_WORLD_SIZE'))\n  else:\n    logging.warning(f\"get_mpi_size use default 1\")\n  return size\n\n\ndef get_mpi_local_size():\n  local_size = 1\n  if 'OMPI_COMM_WORLD_LOCAL_SIZE' in os.environ:\n    local_size = int(os.environ.get('OMPI_COMM_WORLD_LOCAL_SIZE'))\n  else:\n    logging.warning(f\"get_mpi_local_size use default 1\")\n  return local_size\n\n\ndef enable_sync_training():\n  try:\n    return FLAGS.enable_sync_training and 'OMPI_COMM_WORLD_LOCAL_RANK' in os.environ\n  except:\n    return False\n\n\ndef try_init_cuda():\n  if 'CUDA_VISIBLE_DEVICES' not in os.environ and 'OMPI_COMM_WORLD_LOCAL_RANK' in os.environ:\n    os.environ[\"CUDA_DEVICE_ORDER\"] = \"PCI_BUS_ID\"\n    os.environ['CUDA_VISIBLE_DEVICES'] = str(get_mpi_local_rank())\n  global _SYNC_TRAIN_INITED\n  if 'OMPI_COMM_WORLD_LOCAL_RANK' in os.environ:\n    if not _SYNC_TRAIN_INITED:\n      try:\n        if FLAGS.enable_sync_training:\n          enable_bps = int(os.getenv(\"MONOLITH_WITH_BYTEPS\", \"0\"))\n          enable_hvd = int(os.getenv(\"MONOLITH_WITH_HOROVOD\", \"0\"))\n          if enable_bps:\n            import byteps.tensorflow as hvd\n          elif enable_hvd:\n            import horovod.tensorflow as hvd\n          else:\n            raise Exception('no allreduce tools found!')\n          hvd.init()\n          _SYNC_TRAIN_INITED = True\n      except Exception as e:\n        logging.info(str(e))\n\n\ndef get_device_str(force_on_cpu: bool = False):\n  is_mpi_mode = True if 'OMPI_COMM_WORLD_LOCAL_RANK' in os.environ else False\n  is_ps_mode = True if FLAGS.num_ps > 0 else False\n  from monolith.native_training import device_utils\n  device = 'GPU' if FLAGS.enable_gpu_training or device_utils._GPU_PLACEMENT_ALLOWED else 'CPU'\n  device = 'CPU' if force_on_cpu else device\n  if is_mpi_mode and FLAGS.enable_sync_training:\n    if is_ps_mode:\n      rank = get_mpi_rank()\n      job = 'chief' if rank == 0 else 'worker'\n      task = rank if rank == 0 else rank - 1\n      return f'/job:{job}/replica:0/task:{task}/device:{device}:0'\n    else:\n      return ''\n  else:\n    return f'/device:{device}:0'\n\n\ndef get_sync_run_hooks(is_full_sync: bool = False):\n  if enable_sync_training():\n    enable_bps = int(os.getenv(\"MONOLITH_WITH_BYTEPS\", \"0\"))\n    enable_bps_bcast = int(os.getenv(\"MONOLITH_WITH_BYTEPS_BCAST\", \"1\"))\n    if enable_bps and enable_bps_bcast == -1:\n      run_hooks = []\n    elif enable_bps and enable_bps_bcast:\n      import byteps.tensorflow as bps\n      logging.info('Enabled BPS for bcast')\n      run_hooks = [bps.BroadcastGlobalVariablesHook(0, device=get_device_str())]\n      if is_full_sync:\n        run_hooks.append(ByteCCLTelemetryHook(50))\n    else:\n      import horovod.tensorflow as hvd\n      run_hooks = [hvd.BroadcastGlobalVariablesHook(0, device=get_device_str())]\n    return run_hooks\n  else:\n    return []\n\n\ndef update_session_config_for_gpu(session_config):\n  enable_bps = int(os.getenv(\"MONOLITH_WITH_BYTEPS\", \"0\"))\n  if enable_sync_training():\n    # It's recommended to set the visible device list in session config instead of the CUDA_VISIBLE_DEVICES environment variable. \n    # Setting the CUDA_VISIBLE_DEVICES variable may mislead NCCL as per my testing\n    # https://horovod.readthedocs.io/en/stable/tensorflow.html?highlight=visible_device_list\n    # https://horovod.readthedocs.io/en/stable/troubleshooting.html?highlight=cuda_visible_devices#running-out-of-memory\n    local_rank = os.environ.get('OMPI_COMM_WORLD_LOCAL_RANK', \"0\")\n    if os.environ.get('MONOLITH_FORCE_GPU_COMPATIBLE', '1') == '1':\n      session_config.gpu_options.force_gpu_compatible = True\n      logging.info(\"set force_gpu_compatible=True\")\n    if enable_bps and (os.environ.get('MONOLITH_WITH_BYTEPS_FWD_GDR', '0') == '1' or \\\n       os.environ.get('MONOLITH_WITH_BYTEPS_BWD_GDR', '0') == '1'):\n      # if GDR alltoall is enabled, GPU memory need to be registered for UCX\n      # ahead of time. Therefore, we disable the allow_growth option for GPU.\n      # The cuda visible devices are also limited to one device only.\n      session_config.gpu_options.allow_growth = False\n      session_config.gpu_options.per_process_gpu_memory_fraction = 0.4\n      session_config.gpu_options.visible_device_list = local_rank\n    else:\n      session_config.gpu_options.allow_growth = True\n      session_config.gpu_options.visible_device_list = local_rank\n  else:\n    session_config.gpu_options.allow_growth = True\n"
  },
  {
    "path": "monolith/native_training/embedding_combiners.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport abc\n\nimport tensorflow as tf\n\nfrom monolith.native_training import device_utils\nfrom monolith.native_training import distribution_ops\nfrom monolith.native_training import ragged_utils\n\n\nclass Combiner(abc.ABC):\n\n  def __init__(self, max_seq_length: int):\n    self._max_seq_length = max_seq_length\n\n  @property\n  def max_seq_length(self):\n    return self._max_seq_length\n\n  @abc.abstractmethod\n  def combine(self,\n              key: tf.RaggedTensor,\n              embedding: tf.Tensor,\n              name: str = None):\n    pass\n\n\nclass ReduceSum(Combiner):\n\n  def __init__(self):\n    super().__init__(0)\n\n  def combine(self,\n              key: tf.RaggedTensor,\n              embedding: tf.Tensor,\n              name: str = None):\n    return distribution_ops.reduce_sum(tf.expand_dims(\n        ragged_utils.fused_value_rowids(key), -1),\n                                       embedding,\n                                       tf.expand_dims(key.nrows(), 0),\n                                       name=name)\n\n\nclass ReduceMean(Combiner):\n\n  def __init__(self):\n    super().__init__(0)\n\n  def combine(self,\n              key: tf.RaggedTensor,\n              embedding: tf.Tensor,\n              name: str = None):\n    return distribution_ops.reduce_mean(tf.expand_dims(\n        ragged_utils.fused_value_rowids(key), -1),\n                                        embedding,\n                                        tf.expand_dims(key.nrows(), 0),\n                                        name=name)\n\n\nclass FirstN(Combiner):\n\n  def __init__(self, seq_length: int):\n    assert seq_length > 0, \"seq_length must be greater than 0\"\n    super().__init__(seq_length)\n\n  def combine(self,\n              key: tf.RaggedTensor,\n              embedding: tf.Tensor,\n              name: str = None):\n    \"\"\"For rows with smaller number of embeddings than seq_length,\n    automatically append embedding elements which are all zero (default to scatter_nd).\n    Tensor's shape is (batch, seq_length, dim) \"\"\"\n    name = name or \"FirstNCombiner\"\n    with tf.name_scope(name):\n      if not isinstance(embedding, tf.Tensor):\n        embedding = tf.convert_to_tensor(embedding)\n      batch_size_tensor = key.nrows()\n      key_sparse = key.to_sparse()\n      indices = key_sparse.indices\n\n      shape = tf.stack([\n          batch_size_tensor,\n          tf.math.reduce_max([self.max_seq_length, key_sparse.dense_shape[1]]),\n          embedding.shape.as_list()[1]\n      ])\n      with device_utils.maybe_device_if_allowed('/device:GPU:0'):\n        scattered = tf.scatter_nd(indices, embedding, shape)\n        # We use slice here instead of array composition because of the shape problem.\n        return tf.slice(scattered, [0, 0, 0], [-1, self.max_seq_length, -1])\n"
  },
  {
    "path": "monolith/native_training/embedding_combiners_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training import embedding_combiners\n\n\nclass CombinerTest(tf.test.TestCase):\n\n  def testReduceSum(self):\n    key = tf.RaggedTensor.from_row_lengths([1, 2, 3], [1, 2])\n    emb = [[1.0], [2.0], [3.0]]\n    comb = embedding_combiners.ReduceSum()\n    result = self.evaluate(comb.combine(key, emb))\n    self.assertAllClose(result, [[1.0], [5.0]])\n\n  def testFirstN(self):\n    key = tf.RaggedTensor.from_row_lengths([1, 2, 3, 4, 5, 6], [1, 2, 3])\n    emb = [[1.0], [2.0], [3.0], [4.0], [5.0], [6.0]]\n    comb = embedding_combiners.FirstN(2)\n    result = self.evaluate(comb.combine(key, emb))\n    self.assertAllClose(result,\n                        [[[1.0], [0.0]], [[2.0], [3.0]], [[4.0], [5.0]]])\n\n  def testFirstNUnknownShape(self):\n    key = tf.compat.v1.ragged.placeholder(tf.int64, 1, [])\n    emb = tf.compat.v1.placeholder(tf.float32, shape=[None, 6])\n    comb = embedding_combiners.FirstN(2)\n    result = comb.combine(key, emb)\n    self.assertAllEqual(result.shape, [None, 2, 6])\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/entry.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport abc\nimport copy\nfrom typing import Any, List, Union\n\nimport tensorflow as tf\n\nfrom monolith.native_training.monolith_export import monolith_export\n\nfrom monolith.native_training.runtime.hash_table import \\\n  embedding_hash_table_pb2\n\n\nclass Optimizer(abc.ABC):\n  \"\"\"The abstract base class for optimizer.\"\"\"\n\n  @abc.abstractmethod\n  def as_proto(self) -> embedding_hash_table_pb2.OptimizerConfig:\n    pass\n\n\ndef _convert_to_proto(obj: object, proto: object):\n  proto.SetInParent()\n  for k, v in obj.__dict__.items():\n    if v is not None:\n      setattr(proto, k, v)\n\n\nclass StochasticRoundingFloat16OptimizerWrapper(Optimizer):\n\n  def __init__(self, optimizer):\n    self._optimizer = optimizer\n\n  def as_proto(self):\n    proto = self._optimizer.as_proto()\n    proto.stochastic_rounding_float16 = True\n    return proto\n\n\n@monolith_export\nclass SgdOptimizer(Optimizer):\n  r\"\"\"随机梯度下降优化器. \n  定义参数为x, 梯度为grad, 第i次更新梯度有\n  \n  .. math::\n  \n    x_{i+1} = x_{i} - \\eta * grad\n\n  Args:\n    learning_rate (:obj:`float`): 学习率\n    \n  \"\"\"\n\n  def __init__(self, learning_rate=None):\n    self.learning_rate = learning_rate\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.sgd)\n    return opt\n\n\n@monolith_export\nclass AdagradOptimizer(Optimizer):\n  r\"\"\"Adagrad优化器, 论文可参考 http://jmlr.org/papers/v12/duchi11a.html\n  定义参数为x, 梯度为grad, 第i次更新梯度时有\n  \n  .. math::\n  \n    g_{i+1} = g_{i} + grad^2\n    \n    x_{i+1} = x_{i} - \\frac{\\eta}{\\sqrt{g_i + \\epsilon}} grad\n\n  Args:\n    learning_rate (:obj:`float`): 学习率\n    initial_accumulator_value (:obj:`float`): accmulator的起始值\n    hessian_compression_times (:obj:`float`): 在训练的时候，对accumulator使用hessian sketching算法进行压缩. 1代表没有压缩，值越大，压缩效果越好\n    warmup_steps (:obj:`int`): 已弃用\n  \n  \"\"\"\n\n  def __init__(\n      self,\n      learning_rate=None,  # alpha\n      initial_accumulator_value=None,  # beta\n      hessian_compression_times=1,\n      warmup_steps=0,\n      weight_decay_factor=0.0):\n    self.learning_rate = learning_rate\n    self.initial_accumulator_value = initial_accumulator_value\n    self.hessian_compression_times = hessian_compression_times\n    self.weight_decay_factor = weight_decay_factor\n    self.warmup_steps = warmup_steps\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.adagrad)\n    return opt\n\n\n@monolith_export\nclass AdadeltaOptimizer(Optimizer):\n\n  def __init__(self,\n               learning_rate=None,\n               weight_decay_factor=0.0,\n               averaging_ratio=0.9,\n               epsilon=0.01,\n               warmup_steps=0):\n    self.learning_rate = learning_rate\n    self.weight_decay_factor = weight_decay_factor\n    self.averaging_ratio = averaging_ratio\n    self.epsilon = epsilon\n    self.warmup_steps = warmup_steps\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.adadelta)\n    return opt\n\n\n@monolith_export\nclass AdamOptimizer(Optimizer):\n  r\"\"\"Adam优化器, 论文可参考 https://arxiv.org/abs/1412.6980\n  \n  定义参数为x, 梯度为grad, 第i次更新梯度时有\n  \n  .. math::\n  \n    m_{i+1} = \\beta_1 * m_i + (1 - \\beta_1) * grad\n    \n    v_{i+1} = \\beta_2 * v_i + (1 - \\beta_2) * grad^2\n    \n    w_{i+1} = w_i - \\eta * \\frac{m_i}{\\sqrt{v_i + \\epsilon}}\n  \n  Args:\n    learning_rate (:obj:`float`): 学习率\n    beta1 (:obj:`float`): 一阶矩估计的指数衰减率\n    beta2 (:obj:`float`): 二阶矩估计的指数衰减率\n    epsilon (:obj:`float`): 用来保证除数不为0的偏移量\n    warmup_steps (:obj:`int`): 已弃用\n  \n  \"\"\"\n\n  def __init__(self,\n               learning_rate=None,\n               beta1=0.9,\n               beta2=0.99,\n               use_beta1_warmup=False,\n               weight_decay_factor=0.0,\n               use_nesterov=False,\n               epsilon=0.01,\n               warmup_steps=0):\n    self.learning_rate = learning_rate\n    self.beta1 = beta1\n    self.beta2 = beta2\n    self.use_beta1_warmup = use_beta1_warmup\n    self.weight_decay_factor = weight_decay_factor\n    self.use_nesterov = use_nesterov\n    self.epsilon = epsilon\n    self.warmup_steps = warmup_steps\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.adam)\n    return opt\n\n\nclass AmsgradOptimizer(Optimizer):\n\n  def __init__(self,\n               learning_rate=None,\n               beta1=0.9,\n               beta2=0.99,\n               weight_decay_factor=0.0,\n               use_nesterov=False,\n               epsilon=0.01,\n               warmup_steps=0):\n    self.learning_rate = learning_rate\n    self.beta1 = beta1\n    self.beta2 = beta2\n    self.weight_decay_factor = weight_decay_factor\n    self.use_nesterov = use_nesterov\n    self.epsilon = epsilon\n    self.warmup_steps = warmup_steps\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.amsgrad)\n    return opt\n\n\n@monolith_export\nclass BatchSoftmaxOptimizer(Optimizer):\n  r\"\"\"Batch softmax优化器, 论文可参考 https://research.google/pubs/pub48840/\n\n  Args:\n    learning_rate (:obj:`float`): 学习率\n  \"\"\"\n\n  def __init__(\n      self,\n      learning_rate=None,  # alpha\n  ):\n    self.learning_rate = learning_rate\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.batch_softmax)\n    return opt\n\n\n@monolith_export\nclass MomentumOptimizer(Optimizer):\n\n  def __init__(self,\n               learning_rate=None,\n               weight_decay_factor=0.0,\n               use_nesterov=False,\n               momentum=0.9,\n               warmup_steps=0):\n    self.learning_rate = learning_rate\n    self.weight_decay_factor = weight_decay_factor\n    self.use_nesterov = use_nesterov\n    self.momentum = momentum\n    self.warmup_steps = warmup_steps\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.momentum)\n    return opt\n\n\nclass MovingAverageOptimizer(Optimizer):\n\n  def __init__(self, momentum=0.9):\n    self.momentum = momentum\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.moving_average)\n    return opt\n\n\n@monolith_export\nclass RmspropOptimizer(Optimizer):\n\n  def __init__(self, learning_rate=None, weight_decay_factor=0.0, momentum=0.9):\n    self.learning_rate = learning_rate\n    self.weight_decay_factor = weight_decay_factor\n    self.momentum = momentum\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.rmsprop)\n    return opt\n\n\n@monolith_export\nclass RmspropV2Optimizer(Optimizer):\n\n  def __init__(self, learning_rate=None, weight_decay_factor=0.0, momentum=0.9):\n    self.learning_rate = learning_rate\n    self.weight_decay_factor = weight_decay_factor\n    self.momentum = momentum\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.rmspropv2)\n    return opt\n\n\nclass FTRLWithGroupSparsityOptimizer(Optimizer):\n\n  def __init__(\n      self,\n      learning_rate=None,  # alpha\n      initial_accumulator_value=None,\n      beta=None,\n      warmup_steps=0,\n      l1_regularization=None,  # lambda1\n      l2_regularization=None):  # lambda2\n    self.learning_rate = learning_rate\n    self.initial_accumulator_value = initial_accumulator_value\n    self.beta = beta\n    self.l1_regularization_strength = l1_regularization\n    self.l2_regularization_strength = l2_regularization\n    self.warmup_steps = warmup_steps\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.group_ftrl)\n    return opt\n\n\n@monolith_export\nclass AdaGradWithGroupLassoOptimizer(Optimizer):\n\n  def __init__(self,\n               learning_rate=None,\n               beta=None,\n               initial_accumulator_value=None,\n               l2_regularization=None,\n               weight_decay_factor=0.0,\n               warmup_steps=0):\n    self.learning_rate = learning_rate\n    self.beta = beta\n    self.initial_accumulator_value = initial_accumulator_value\n    self.l2_regularization_strength = l2_regularization\n    self.weight_decay_factor = weight_decay_factor\n    self.warmup_steps = warmup_steps\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.group_adagrad)\n    return opt\n\n\n# TODO: put DcOptimizer into entry.py\n\n\nclass DynamicWdAdagradOptimizer(Optimizer):\n\n  def __init__(\n      self,\n      learning_rate=None,  # alpha\n      initial_accumulator_value=None,  # beta\n      hessian_compression_times=1,\n      warmup_steps=0,\n      weight_decay_factor=0.0,\n      decouple_weight_decay=True,\n      enable_dynamic_wd=True,\n      flip_direction=True,\n      dynamic_wd_temperature=1.0):\n    self.learning_rate = learning_rate\n    self.initial_accumulator_value = initial_accumulator_value\n    self.hessian_compression_times = hessian_compression_times\n    self.weight_decay_factor = weight_decay_factor\n    self.warmup_steps = warmup_steps\n    self.decouple_weight_decay = decouple_weight_decay\n    self.enable_dynamic_wd = enable_dynamic_wd\n    self.flip_direction = flip_direction\n    self.dynamic_wd_temperature = dynamic_wd_temperature\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.dynamic_wd_adagrad)\n    return opt\n\n\n@monolith_export\nclass FtrlOptimizer(Optimizer):\n  \"\"\"FTRL优化器, 论文可参考 https://dl.acm.org/citation.cfm?id=2488200\n  \n  Args:\n    initial_accumulator_value (:obj:`float`): accumulator的起始值\n    beta (:obj:`float`): 论文中的beta值\n  \n  \"\"\"\n\n  def __init__(\n      self,\n      learning_rate=None,  # alpha\n      initial_accumulator_value=None,\n      beta=None,\n      warmup_steps=0,\n      l1_regularization=None,  # lambda1\n      l2_regularization=None):  # lambda2\n    self.learning_rate = learning_rate\n    self.initial_accumulator_value = initial_accumulator_value\n    self.beta = beta\n    self.l1_regularization_strength = l1_regularization\n    self.l2_regularization_strength = l2_regularization\n    self.warmup_steps = warmup_steps\n\n  def as_proto(self):\n    opt = embedding_hash_table_pb2.OptimizerConfig()\n    _convert_to_proto(self, opt.ftrl)\n    return opt\n\n\nclass Initializer(abc.ABC):\n  \"\"\"The abstract base class for initializer\"\"\"\n\n  @abc.abstractmethod\n  def as_proto(self) -> embedding_hash_table_pb2.InitializerConfig:\n    pass\n\n\n@monolith_export\nclass ZerosInitializer(Initializer):\n  \"\"\"全0初始化器，将会把embedidng的初始值设为全0\"\"\"\n\n  def as_proto(self):\n    init = embedding_hash_table_pb2.InitializerConfig()\n    _convert_to_proto(self, init.zeros)\n    return init\n\n\n@monolith_export\nclass ConstantsInitializer(Initializer):\n  \"\"\"常数初始化器，将会把embedidng的初始值设为常数\"\"\"\n\n  def __init__(self, constant: float):\n    self.constant = constant\n\n  def as_proto(self):\n    init = embedding_hash_table_pb2.InitializerConfig()\n    _convert_to_proto(self, init.constants)\n    return init\n\n\nclass RandomUniformInitializer(Initializer):\n  \"\"\"随机均匀的初始化器，将会把初始化区间默认为[minval, maxval]\n  \n  Args:\n    minval, maxval (:obj:`float`): 初始化的区间\n  \n  \"\"\"\n\n  def __init__(self, minval=None, maxval=None):\n    self.minval = minval\n    self.maxval = maxval\n\n  def as_proto(self):\n    init = embedding_hash_table_pb2.InitializerConfig()\n    _convert_to_proto(self, init.random_uniform)\n    return init\n\n\nclass BatchSoftmaxInitializer(Initializer):\n\n  def __init__(self, init_step_interval: float):\n    if init_step_interval < 1:\n      raise ValueError(\"init_step_interval should be >= 1, while got {}\".format(\n          init_step_interval))\n    self.constant = init_step_interval\n\n  def as_proto(self):\n    init = embedding_hash_table_pb2.InitializerConfig()\n    _convert_to_proto(self, init.constants)\n    return init\n\n\nclass Compressor(abc.ABC):\n  \"\"\"The abstract base class for compressor\"\"\"\n\n  @abc.abstractmethod\n  def as_proto(self) -> embedding_hash_table_pb2.FloatCompressorConfig:\n    pass\n\n\n@monolith_export\nclass OneBitCompressor(Compressor):\n\n  def __init__(self, step_size: int = 200, amplitude: float = 0.05):\n    super().__init__()\n    self.step_size = step_size\n    self.amplitude = amplitude\n\n  def as_proto(self):\n    comp = embedding_hash_table_pb2.FloatCompressorConfig()\n    comp.one_bit.step_size = self.step_size\n    _convert_to_proto(self, comp.one_bit)\n    return comp\n\n\n@monolith_export\nclass FixedR8Compressor(Compressor):\n\n  def __init__(self, fixed_range=1.0):\n    super().__init__()\n    self.r = fixed_range\n\n  def as_proto(self):\n    comp = embedding_hash_table_pb2.FloatCompressorConfig()\n    _convert_to_proto(self, comp.fixed_r8)\n    return comp\n\n\n@monolith_export\nclass Fp16Compressor(Compressor):\n  \"\"\"当模型服务时，将会对embedding进行Fp16编码，从而达到在服务时节省内存的目的\"\"\"\n\n  def as_proto(self):\n    comp = embedding_hash_table_pb2.FloatCompressorConfig()\n    _convert_to_proto(self, comp.fp16)\n    return comp\n\n\n@monolith_export\nclass Fp32Compressor(Compressor):\n  \"\"\"当模型服务时，将会对embedding进行Fp32编码\"\"\"\n\n  def as_proto(self):\n    comp = embedding_hash_table_pb2.FloatCompressorConfig()\n    _convert_to_proto(self, comp.fp32)\n    return comp\n\n\ndef CombineAsSegment(\n    dim_size: int,\n    initializer: Union[Initializer, embedding_hash_table_pb2.InitializerConfig],\n    optimizer: Union[Optimizer, embedding_hash_table_pb2.OptimizerConfig],\n    compressor: Union[Compressor,\n                      embedding_hash_table_pb2.FloatCompressorConfig]\n) -> embedding_hash_table_pb2.EntryConfig.Segment:\n  segment = embedding_hash_table_pb2.EntryConfig.Segment()\n  segment.dim_size = dim_size\n  if hasattr(initializer, 'as_proto'):\n    segment.init_config.CopyFrom(initializer.as_proto())\n  else:\n    segment.init_config.CopyFrom(initializer)\n\n  if hasattr(optimizer, 'as_proto'):\n    segment.opt_config.CopyFrom(optimizer.as_proto())\n  else:\n    segment.opt_config.CopyFrom(optimizer)\n\n  if hasattr(compressor, 'as_proto'):\n    segment.comp_config.CopyFrom(compressor.as_proto())\n  else:\n    segment.comp_config.CopyFrom(compressor)\n  return segment\n\n\nclass HashTableConfig(abc.ABC):\n  \"\"\"For hash table since we are not sure which field to update, we use an update function\"\"\"\n\n  @abc.abstractmethod\n  def mutate_table(\n      self, table_config: embedding_hash_table_pb2.EmbeddingHashTableConfig):\n    pass\n\n\nclass CuckooHashTableConfig(HashTableConfig):\n\n  def __init__(self, initial_capacity=1, feature_evict_every_n_hours=0):\n    self._initial_capacity = initial_capacity\n    self._feature_evict_every_n_hours = feature_evict_every_n_hours\n\n  def mutate_table(\n      self, table_config: embedding_hash_table_pb2.EmbeddingHashTableConfig):\n    table_config.initial_capacity = self._initial_capacity\n    table_config.cuckoo.SetInParent()\n    if self._feature_evict_every_n_hours > 0:\n      table_config.enable_feature_eviction = True\n      table_config.feature_evict_every_n_hours = self._feature_evict_every_n_hours\n\n\n\n\nclass HashTableConfigInstance():\n  \"\"\"The config instance for generating HashTable\"\"\"\n\n  def __init__(self,\n               table_config: embedding_hash_table_pb2.EmbeddingHashTableConfig,\n               learning_rate_fns: List[Any],\n               extra_restore_names=None):\n    self._table_config = table_config\n    self.extra_restore_names = copy.copy(extra_restore_names) or []\n    self._learning_rate_fns = learning_rate_fns\n    self._learning_rate_tensor = None\n\n  # Used to check whether two slots share the same config.\n  def __str__(self):\n    return \"TableConfigPB: %s, LearningRateFns: [%s]\" % (\n        self._table_config.SerializeToString(), \", \".join(\n            [str(fn) for fn in self._learning_rate_fns]))\n\n  @property\n  def table_config(self):\n    return self._table_config\n\n  @property\n  def learning_rate_fns(self):\n    return self._learning_rate_fns\n\n  @property\n  def learning_rate_tensor(self):\n    return self._learning_rate_tensor\n\n  def set_learning_rate_tensor(self, learning_rate_tensor: tf.Tensor):\n    self._learning_rate_tensor = learning_rate_tensor\n\n  def call_learning_rate_fns(self) -> tf.Tensor:\n    \"\"\"Call learning rate function if callable and return a tf.Tensor\"\"\"\n    with tf.name_scope(\"learning_rate\"):\n      learning_rates = list()\n      for learning_rate_fn in self._learning_rate_fns:\n        if not callable(learning_rate_fn):\n          learning_rate = tf.cast(learning_rate_fn, dtype=tf.float32)\n        else:\n          learning_rate = learning_rate_fn()\n        learning_rates.append(learning_rate)\n\n      if len(learning_rates) > 0:\n        learning_rate_tensor = tf.stack(learning_rates)\n      else:\n        raise Exception(\"Learning_rate_fns must be not empty.\")\n\n      return learning_rate_tensor\n\n  def call_learning_rate_fns_fewer_ops(self) -> List[tf.Tensor]:\n    \"\"\"Call learning rate function if callable and return a tf.Tensor\"\"\"\n    with tf.name_scope(\"learning_rate\"):\n      learning_rates = list()\n      for learning_rate_fn in self._learning_rate_fns:\n        if not callable(learning_rate_fn):\n          learning_rate = learning_rate_fn\n        else:\n          learning_rate = learning_rate_fn()\n        learning_rates.append(learning_rate)\n\n      if len(learning_rates) == 0:\n        raise Exception(\"Learning_rate_fns must be not empty.\")\n      return learning_rates\n"
  },
  {
    "path": "monolith/native_training/entry_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport unittest\n\nfrom monolith.native_training import entry\nfrom monolith.native_training import learning_rate_functions\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\n\ndef _default_learning_rate_fn():\n  return learning_rate_functions.PolynomialDecay(initial_learning_rate=0.01,\n                                                 decay_steps=20,\n                                                 end_learning_rate=0.05)\n\n\nclass EntryTest(unittest.TestCase):\n  \"\"\"The tests here are for testing complilation.\"\"\"\n\n  def test_optimizers(self):\n    entry.SgdOptimizer(0.01).as_proto()\n    entry.AdagradOptimizer(0.01, 0.1).as_proto()\n    entry.AdagradOptimizer(0.01, 0.1, 10).as_proto()\n    entry.FtrlOptimizer(0.01, 0.1, 1).as_proto()\n    entry.DynamicWdAdagradOptimizer(0.01, 0.1, 1).as_proto()\n    entry.AdadeltaOptimizer(0.01, 0.0, 0.9, 0.01).as_proto()\n    entry.AdamOptimizer(0.01, 0.9, 0.99, False, 0.0, False, 0.01).as_proto()\n    entry.AmsgradOptimizer(0.01, 0.9, 0.99, 0.0, False, 0.01).as_proto()\n    entry.MomentumOptimizer(0.01, 0.0, False, 0.9).as_proto()\n    entry.MovingAverageOptimizer(0.9).as_proto()\n    entry.RmspropOptimizer(0.01, 0.0, 0.9).as_proto()\n    entry.RmspropV2Optimizer(0.01, 0.0, 0.9).as_proto()\n    entry.BatchSoftmaxOptimizer(0.01).as_proto()\n\n  def test_initializer(self):\n    entry.ZerosInitializer().as_proto()\n    entry.RandomUniformInitializer(-0.5, 0.5).as_proto()\n    entry.BatchSoftmaxInitializer(1.0).as_proto()\n\n  def test_compressor(self):\n    entry.Fp16Compressor().as_proto()\n    entry.Fp32Compressor().as_proto()\n    entry.FixedR8Compressor().as_proto()\n    entry.OneBitCompressor().as_proto()\n\n  def test_combine(self):\n    entry.CombineAsSegment(5, entry.ZerosInitializer(), entry.SgdOptimizer(),\n                           entry.Fp16Compressor())\n\n  def test_hashtable_config(self):\n    entry.CuckooHashTableConfig()\n\n  def test_hashtable_config_entrance(self):\n    table_config1 = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n    config1 = entry.HashTableConfigInstance(table_config1, [0.1])\n    table_config2 = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n    config2 = entry.HashTableConfigInstance(table_config2, [0.1])\n    assert (str(config1) == str(config2))\n\n    table_config3 = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n    config3 = entry.HashTableConfigInstance(table_config3,\n                                            [_default_learning_rate_fn()])\n    table_config4 = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n    config4 = entry.HashTableConfigInstance(table_config4,\n                                            [_default_learning_rate_fn()])\n    assert (str(config3) == str(config4))\n\n    assert (str(config1) != str(config3))\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/env_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport contextlib\nimport hashlib\nimport os\nimport subprocess\nimport socket\n\nfrom absl import logging\ndef setup_hdfs_env():\n  pass\n  \ndef generate_psm_from_uuid(s):\n  return s\ndef get_zk_auth_data():\n  ZK_AUTH = os.getenv('ZK_AUTH', None)\n  if ZK_AUTH:\n    print(\"ZK_AUTH\", ZK_AUTH)\n    return [(\"digest\", ZK_AUTH)]\n  return None\n"
  },
  {
    "path": "monolith/native_training/env_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport unittest\nfrom unittest import mock\n\nfrom monolith.native_training import env_utils\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/estimator.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging, flags\nfrom dataclasses import dataclass\nfrom dataclasses_json import dataclass_json\nimport os\nimport copy\nimport json\nimport numpy as np\nimport collections\nimport getpass\nfrom typing import Dict, List\nfrom kazoo.client import KazooClient\nfrom typing import Optional, Union, get_type_hints\n\nimport tensorflow as tf\n\nfrom monolith.native_training import env_utils\nfrom monolith.agent_service.utils import AgentConfig\nfrom monolith.agent_service.backends import ZKBackend\nfrom monolith.native_training.zk_utils import default_zk_servers\nfrom monolith.agent_service.replica_manager import ReplicaWatcher\nfrom monolith.native_training.cpu_training import CpuTraining, create_exporter\nfrom monolith.native_training.runner_utils import RunnerConfig, monolith_discovery\nfrom monolith.native_training.cpu_training import local_train_internal\nfrom monolith.native_training.cpu_training import distributed_train\nfrom monolith.native_training.cpu_training import distributed_sync_train\nfrom monolith.native_training.service_discovery import ServiceDiscoveryType\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.model_dump.dump_utils import DumpUtils\nfrom monolith.core.hyperparams import InstantiableParams\n\nfrom monolith.native_training.data.item_pool_hook import ItemPoolSaveRestoreHook\nfrom monolith.native_training.utils import set_metric_prefix\nfrom monolith.native_training.data.parsers import get_default_parser_ctx, ParserCtx\nfrom monolith.native_training.model_export.export_context import \\\n  is_exporting, is_exporting_distributed, ExportMode\nfrom monolith.native_training.zk_utils import MonolithKazooClient\nfrom monolith.native_training.distribution_utils import init_sync_train_and_update_conf, try_init_cuda\nfrom monolith.native_training import device_utils\n\n\n@monolith_export\nclass EstimatorSpec(\n    collections.namedtuple(\n        'EstimatorSpec',\n        ['label', 'pred', 'head_name', 'loss', 'optimizer', 'classification'])):\n  \"\"\"EstimatorSpec是model_fn返回的数据结构.\n  \n  Args:\n    label (:obj:`Union[tf.Tensor, List[tf.Tensor]]`): 样本标签, multi-head可以使用列表\n    pred (:obj:`Union[tf.Tensor, List[tf.Tensor]]`): 预测结果, multi-head可以使用列表\n    head_name (:obj:`Union[str, List[str]]`): predict名称, multi-head可以使用列表\n    loss (:obj:`tf.Tensor`): 损失\n    optimizer (:obj:`tf.Optimizer`): dense部分的优化器\n    classification (:obj:`Union[bool, List[bool]]`): 是否为分类模型, multi-head可使用列表\n  \n  \"\"\"\n\n  def __new__(cls,\n              label,\n              pred,\n              head_name=None,\n              loss=None,\n              optimizer=None,\n              classification=True):\n    return super(EstimatorSpec, cls).__new__(cls,\n                                             label=label,\n                                             pred=pred,\n                                             head_name=head_name,\n                                             loss=loss,\n                                             optimizer=optimizer,\n                                             classification=classification)\n\n  def _replace(self, **kwds):\n    \"\"\"Return a new EstimatorSpec replacing specified fields with new values.\"\"\"\n    if 'mode' in kwds:\n      if self.mode != kwds['mode']:\n        raise ValueError('mode of EstimatorSpec cannot be changed.')\n    new_fields = map(kwds.pop, self._fields, list(self))\n    return EstimatorSpec(*new_fields)\n\n\n@monolith_export\n@dataclass_json\n@dataclass\nclass RunConfig:\n  \"\"\"Estimator相关配置, 用户模型外参数统一入口\n  \n  Args: \n    is_local (:obj:`bool`): 是否为本地模式, 默认为False\n    num_ps (:obj:`int`): PS个数\n    num_workers (:obj:`int`): Woeker个数\n    chief_timeout_secs (:obj:`int`): chief 超时时长, 默认为 1800秒\n    operation_timeout_in_ms (:obj:`int`): 操作超时时长, 默认为 600000, 为600s\n    session_creation_timeout_secs (:obj:`int`): session创建超时时长, 默认为7200秒\n    enable_fused_layout (:obj:`bool`): 是否打开layout融合, 加速计算\n    partial_recovery (:obj:`bool`): 是否开启部分恢复\n    max_retry_times (:obj:`int`): 发生容错时, 最大重启次数, 默认为 6\n    retry_wait_in_secs (:obj:`int`): 发生容错时, 重启时间间隔, 默认为 5\n    bzid (:obj:`str`): serving 业务id, 用于参数同步时找到对应Online PS\n    base_name (:obj:`str`): serving 模型名, 用于参数同步时找到对应Online PS\n    ps_replica_num (:obj:`int`): serving PS 副本数\n    enable_parameter_sync (:obj:`bool`): 是否开启参数同步, 默认为False\n    model_dir (:obj:`str`): 模型dump目录\n    restore_dir (:obj:`str`): 模型加载目录, 当dump目录与加载目录不同时才需指定, 默认从model_dir中加载模型\n    restore_ckpt (:obj:`str`): 加载checkpoint版本, 默认加载最新版\n    save_checkpoints_secs (:obj:`int`): 每过多少秒存一个checkpoint\n    save_checkpoints_steps (:obj:`int`): 每过多少step存一个checkpoint\n    max_rpc_deadline_millis (:obj:`int`): prc超时时长, 默认30秒\n    dense_only_save_checkpoints_secs (:obj:`int`): 每过多少秒存一个dense部分checkpoint\n    dense_only_save_checkpoints_steps (:obj:`int`): 每过多少step存一个dense部分checkpoint\n    checkpoints_max_to_keep (:obj:`int`): 最多保存多少个checkpoint\n    warmup_file (:obj:`str`): serving warmup文件名\n    enable_local_profiling (:obj:`bool`): 是否打开本地测试 profiling\n    use_native_multi_hash_table (:obj:`bool`): 请不要指定这个变量，将于2023-1-1移除\n    clear_nn (:obj:`bool`): 是否在reload模型时将dense部分随机初始化, 默认为false.\n    continue_training (:obj:`bool`): 是clear_nn为true时, global_step是否继续保持, 默认为false.\n    reload_alias_map (:obj:`dict`): 在加载模型时, 如果由于某些原因, 变量名字不一致, 可以用reload_alias_map指定新老名字的对应关系\n    enable_alias_map_auto_gen: 是否需要自动生成 alias_map\n    save_summary_steps: 每隔多少global_step保存一次summary\n    log_step_count_steps: 每隔多少global_step打印一次loss\n    disable_native_metrics: 是否关闭 TensorFlow 的 metrics 功能，如 AUC、MSE 的计算，默认为 True\n  \"\"\"\n\n  # basic\n  is_local: bool = False\n  num_ps: int = 0\n  num_workers: int = 1\n\n  chief_timeout_secs: int = 1800\n  operation_timeout_in_ms: int = -1\n  session_creation_timeout_secs: int = 7200\n  enable_fused_layout: bool = False\n  enable_model_dump: bool = False\n\n  # failover\n  partial_recovery: bool = False\n  max_retry_times: int = 6\n  retry_wait_in_secs: int = 5\n\n  # for params sync\n  bzid: str = None\n  base_name: str = None\n  ps_replica_num: int = None\n  enable_parameter_sync: bool = False\n\n  # checkpoint and export\n  model_dir: str = \"\"\n  restore_dir: str = None\n  restore_ckpt: str = None\n  save_checkpoints_secs: int = None\n  save_checkpoints_steps: int = None\n  max_rpc_deadline_millis: int = 30000\n  dense_only_save_checkpoints_secs: int = None\n  dense_only_save_checkpoints_steps: int = None\n  checkpoints_max_to_keep: int = 10\n\n  warmup_file: str = './warmup_file'\n  enable_local_profiling: bool = False\n\n  use_native_multi_hash_table: bool = None\n\n  clear_nn: bool = False\n  continue_training: bool = False\n  reload_alias_map: Dict[str, int] = None\n  enable_alias_map_auto_gen: bool = None\n\n  kafka_topics: str = None\n  kafka_group_id: str = None\n  kafka_servers: str = None\n\n  disable_native_metrics: bool = True\n\n  save_summary_steps: int = 100\n  log_step_count_steps: int = 100\n\n  def to_runner_config(self) -> RunnerConfig:\n    conf = RunnerConfig(\n        restore_dir=self.restore_dir,\n        restore_ckpt=self.restore_ckpt,\n        model_dir=self.model_dir,\n        enable_fused_layout=self.enable_fused_layout,\n        enable_model_dump=self.enable_model_dump,\n        warmup_file=self.warmup_file,\n        enable_alias_map_auto_gen=self.enable_alias_map_auto_gen,\n        kafka_topics=self.kafka_topics,\n        kafka_group_id=self.kafka_group_id,\n        kafka_servers=self.kafka_servers,\n        save_summary_steps=self.save_summary_steps,\n        log_step_count_steps=self.log_step_count_steps,\n        disable_native_metrics=self.disable_native_metrics)\n    for name, _ in get_type_hints(RunConfig).items():\n      value = getattr(self, name)\n      if hasattr(conf, name) and value is not None:\n        default = getattr(RunConfig, name)\n        # must be and, because RunnerConfig value can be writen by command line\n        # we cannot use default value in RunConfig to overwrite command line value\n        if value != default and getattr(conf, name) != value:\n          setattr(conf, name, value)\n    # in case US tearm use CONSUL\n    if conf.discovery_type == ServiceDiscoveryType.CONSUL:\n      conf.discovery_type = ServiceDiscoveryType.ZK\n\n    if not conf.enable_gpu_training:\n      # set default value for embedding prefetch/postpush\n      if conf.embedding_prefetch_capacity <= 0:\n        conf.embedding_prefetch_capacity = 1\n      if not conf.enable_embedding_postpush:\n        conf.enable_embedding_postpush = True\n\n    # [todo] remove this when enable_realtime_training changed to enable_parameter_sync\n    if self.enable_parameter_sync:\n      if hasattr(conf, 'enable_realtime_training'):\n        conf.enable_realtime_training = True\n      elif hasattr(conf, 'enable_parameter_sync'):\n        conf.enable_parameter_sync = True\n      else:\n        raise RuntimeError(\"enable_parameter_sync not set!\")\n    return conf\n\n  def __post_init__(self):\n    ser_data = self.to_json()\n    DumpUtils().add_config(ser_data)\n    # get user params\n    params = vars(self)\n    user_params = []\n    for name, _ in get_type_hints(RunConfig).items():\n      default_value = getattr(RunConfig, name)\n      if default_value != params[name]:\n        logging.info(\"save user param {} with value {}\".format(\n            name, params[name]))\n        user_params.append(name)\n    DumpUtils().add_user_params(user_params)\n\n\n@monolith_export\nclass Estimator(object):\n  \"\"\"利用Estimator可以实现local模式与分布式模式的统一, 另外, Estimator可以帮助初始化/save/restore变量, 执行hooks, 写summary等\n  \n  Args:\n    model (:obj:`Model`): NativeModel对象\n    conf (:obj:`RunConfig`): 运行模型所要的配置\n    warm_start_from (:obj:`str`): 在保存saved_model时, 可以保存warmup文件. warm_start_from用于指定warmup文件的位置, 到目录名即可\n  \n  \"\"\"\n\n  def __init__(self,\n               model,\n               conf: Union[RunConfig, RunnerConfig],\n               warm_start_from=None):\n    self._runner_conf = conf.to_runner_config() if isinstance(\n        conf, RunConfig) else conf\n    self._model = model\n    self._task = None\n    self._warm_start_from = warm_start_from\n    self._sync_backend = None\n    self._kazoo_client = None\n\n    if isinstance(conf, RunConfig):\n      self._enable_loacl_profiling = conf.enable_local_profiling\n    else:\n      self._enable_loacl_profiling = False\n\n    logging.info(self._runner_conf)\n    if self._runner_conf.is_local:\n      # local mode cannot asscess deep_insight\n      self._model.metrics.enable_deep_insight = False\n    else:\n      self._model.metrics.enable_deep_insight = True\n      if self._runner_conf.deep_insight_name:\n        self._model.metrics.deep_insight_name = self._runner_conf.deep_insight_name\n      if self._runner_conf.deep_insight_target:\n        self._model.metrics.deep_insight_target = self._runner_conf.deep_insight_target\n      if self._runner_conf.deep_insight_sample_ratio:\n        self._model.metrics.deep_insight_sample_ratio = self._runner_conf.deep_insight_sample_ratio\n\n    if self._runner_conf.enable_realtime_training and self._runner_conf.server_type == 'ps':\n      assert self._runner_conf.bzid, \"Business id cannot be none while realtime training.\"\n      assert self._runner_conf.base_name, \"Base name cannot be none while realtime training.\"\n      zk_servers = self._runner_conf.zk_server or os.environ.get(\n          'zk_servers', default_zk_servers())\n      if self._runner_conf.unified_serving:\n        self._sync_backend = ZKBackend(self._runner_conf.bzid,\n                                       zk_servers=zk_servers)\n      else:\n        assert self._runner_conf.base_name, \"Base name cannot be none while realtime training.\"\n        self._kazoo_client = MonolithKazooClient(hosts=zk_servers)\n        self._kazoo_client.start()\n        agent_config = AgentConfig(bzid=self._runner_conf.bzid,\n                                   base_name=self._runner_conf.base_name,\n                                   deploy_type='ps',\n                                   num_ps=self._runner_conf.num_ps,\n                                   dc_aware=self._runner_conf.dc_aware)\n        replica_watcher = ReplicaWatcher(\n            self._kazoo_client,\n            agent_config,\n            zk_watch_address_family=self._runner_conf.zk_watch_address_family)\n        self._sync_backend = replica_watcher.to_sync_wrapper()\n\n    if self._runner_conf.params_override:\n      logging.info(\"Override: {}\".format(self._runner_conf.params_override))\n      params_override_dict = json.loads(self._runner_conf.params_override)\n      if hasattr(model, 'p'):\n        model.p.set(**params_override_dict)\n      elif hasattr(model, 'params'):\n        model.params.set(**params_override_dict)\n      else:\n        logging.warning('params_override error!')\n\n    try:\n      if not os.environ.get(\"HADOOP_HDFS_HOME\"):\n        env_utils.setup_hdfs_env()\n    except Exception as e:\n      logging.error('setup_hdfs_env fail {}!'.format(e))\n\n    os.environ[\"TF_GRPC_WORKER_CACHE_THREADS\"] = str(\n        self._runner_conf.tf_grpc_worker_cache_threads)\n    os.environ[\"MONOLITH_GRPC_WORKER_SERVICE_HANDLER_MULTIPLIER\"] = str(\n        self._runner_conf.monolith_grpc_worker_service_handler_multiplier)\n\n    # private attr\n    self.__est: Optional[tf.estimator.Estimator] = None\n\n  @property\n  def _sess_config(self):\n    return self._est._session_config\n\n  @property\n  def model_dir(self):\n    return self._runner_conf.model_dir\n\n  @property\n  def config(self):\n    return self._est._config\n\n  @property\n  def _est(self):\n    if self.__est is None:\n      model = copy.deepcopy(self._model)\n      model.mode = tf.estimator.ModeKeys.PREDICT\n      self._task = CpuTraining(config=self._runner_conf,\n                               task=model.instantiate())\n\n      # the default estimate for predict/export_saved_model/import_saved_model\n      if 'TF_CONF' in os.environ:\n        del os.environ['TF_CONF']\n\n      self.__est = tf.estimator.Estimator(\n          self._task.create_model_fn(),\n          model_dir=self._runner_conf.model_dir,\n          config=tf.estimator.RunConfig(\n              log_step_count_steps=self._runner_conf.log_step_count_steps),\n          warm_start_from=self._warm_start_from)\n\n    return self.__est\n\n  def _init_fountain_env(self):\n    if self._model.train.use_fountain and bool(\n        self._runner_conf.fountain_zk_host) and bool(\n            self._runner_conf.fountain_model_name):\n      logging.info(\"Override Fountain Params:{}; {}\".format(\n          self._runner_conf.fountain_model_name,\n          self._runner_conf.fountain_zk_host))\n      self._model.train.fountain_zk_host = self._runner_conf.fountain_zk_host\n      self._model.train.fountain_model_name = self._runner_conf.fountain_model_name\n\n  def close(self):\n    if self._sync_backend is not None:\n      try:\n        self._sync_backend.stop()\n      except Exception as e:\n        logging.error(e)\n\n    if self._kazoo_client is not None:\n      try:\n        self._kazoo_client.stop()\n      except Exception as e:\n        logging.info(e)\n\n      try:\n        self._kazoo_client.close()\n      except Exception as e:\n        logging.info(e)\n\n  def get_variable_value(self, name):\n    return self._est.get_variable_value(name)\n\n  def get_variable_names(self):\n    return self._est.get_variable_names()\n\n  def latest_checkpoint(self):\n    return self._est.latest_checkpoint()\n\n  def train(self, steps=None, max_steps=None, hooks=None):\n    assert hooks is None or isinstance(hooks, list) and \\\n      all(isinstance(o, tf.estimator.SessionRunHook) for o in hooks)\n    set_metric_prefix(\"monolith.training.{}\".format(\n        self._runner_conf.deep_insight_name))\n    model = copy.deepcopy(self._model)\n    if not isinstance(model, InstantiableParams):\n      model.file_name = self._model.file_name\n    model.mode = tf.estimator.ModeKeys.TRAIN\n\n    if steps is not None:\n      model.train.steps = steps\n    if max_steps is not None:\n      model.train.max_steps = max_steps\n    self._init_fountain_env()\n    if self._runner_conf.is_local:\n      if not self._runner_conf.model_dir:\n        model_dir = \"/tmp/{}/{}\".format(getpass.getuser(), model.name)\n      else:\n        model_dir = self._runner_conf.model_dir\n      DumpUtils().record_params(model)\n      self.__est = local_train_internal(model,\n                                        self._runner_conf,\n                                        model_dir=model_dir,\n                                        steps=steps,\n                                        profiling=self._enable_loacl_profiling,\n                                        user_hooks=hooks)\n      DumpUtils().dump(f'{self._runner_conf.model_dir}/model_dump')\n    else:\n      DumpUtils().enable = False\n      if self._sync_backend is not None:\n        self._sync_backend.start()\n        self._sync_backend.subscribe_model(self._runner_conf.model_name or\n                                           model.metrics.deep_insight_name)\n      logging.info(\"Environment vars: %s\", os.environ)\n      logging.info(\"Flags: %s\", flags.FLAGS.flag_values_dict())\n      logging.info(f'{model.p}')\n\n      if self._runner_conf.enable_full_sync_training:\n        init_sync_train_and_update_conf(self._runner_conf)\n        self.__est = distributed_sync_train(self._runner_conf,\n                                            params=model,\n                                            sync_backend=self._sync_backend,\n                                            user_hooks=hooks)\n      else:\n        with monolith_discovery(self._runner_conf) as discovery:\n          if self._runner_conf.enable_gpu_training:\n            device_utils.enable_gpu_training()\n            model.train.use_gpu_emb_table = False\n          if self._runner_conf.enable_partial_sync_training and self._runner_conf.server_type == \"worker\":\n            try_init_cuda()\n            self._runner_conf.device_fn = device_utils.get_device_fn()\n            model.train.slow_start_steps = 0\n          self.__est = distributed_train(config=self._runner_conf,\n                                         discovery=discovery,\n                                         params=model,\n                                         sync_backend=self._sync_backend,\n                                         user_hooks=hooks)\n    self.close()\n\n  def evaluate(self, steps=None, hooks=None):\n    assert hooks is None or isinstance(hooks, list) and \\\n      all(isinstance(o, tf.estimator.SessionRunHook) for o in hooks)\n    model = copy.deepcopy(self._model)\n    model.mode = tf.estimator.ModeKeys.EVAL\n    if not isinstance(model, InstantiableParams):\n      model.file_name = self._model.file_name\n    self._runner_conf.mode = tf.estimator.ModeKeys.EVAL\n    if steps is not None:\n      model.train.steps = steps\n    self._init_fountain_env()\n    if self._runner_conf.is_local:\n      DumpUtils().record_params(model)\n      if not self._runner_conf.model_dir:\n        model_dir = \"/tmp/{}/{}\".format(getpass.getuser(), model.name)\n      else:\n        model_dir = self._runner_conf.model_dir\n      self.__est = local_train_internal(model,\n                                        self._runner_conf,\n                                        model_dir=model_dir,\n                                        steps=steps,\n                                        profiling=self._enable_loacl_profiling,\n                                        user_hooks=hooks)\n      DumpUtils().dump(f'{self._runner_conf.model_dir}/model_dump')\n    else:\n      DumpUtils().enable = False\n      logging.info(f'{model.p}')\n      logging.info(\"Environment vars: %s\", os.environ)\n      logging.info(\"Flags: %s\", flags.FLAGS.flag_values_dict())\n\n      if self._runner_conf.enable_full_sync_training:\n        init_sync_train_and_update_conf(self._runner_conf)\n        self.__est = distributed_sync_train(self._runner_conf,\n                                            params=model,\n                                            sync_backend=self._sync_backend,\n                                            user_hooks=hooks)\n      else:\n        with monolith_discovery(self._runner_conf) as discovery:\n          if self._runner_conf.enable_gpu_training:\n            device_utils.enable_gpu_training()\n            model.train.use_gpu_emb_table = False\n          if self._runner_conf.enable_partial_sync_training and self._runner_conf.server_type == \"worker\":\n            try_init_cuda()\n            self._runner_conf.device_fn = device_utils.get_device_fn()\n          self.__est = distributed_train(config=self._runner_conf,\n                                         discovery=discovery,\n                                         params=model,\n                                         sync_backend=self._sync_backend)\n\n    self.close()\n\n  def predict(self,\n              predict_keys=None,\n              hooks=None,\n              checkpoint_path=None,\n              yield_single_examples=True):\n    est = self._est  # create tf estimator\n    input_fn = self._task.create_input_fn(tf.estimator.ModeKeys.PREDICT)\n    est.predict(input_fn, predict_keys, hooks, checkpoint_path,\n                yield_single_examples)\n    self.close()\n\n  def export_saved_model(self,\n                         batch_size=64,\n                         name=None,\n                         dense_only: bool = False,\n                         enable_fused_layout: bool = False):\n    model = copy.deepcopy(self._model)\n    runner_conf = copy.deepcopy(self._runner_conf)\n    runner_conf.enable_fused_layout = enable_fused_layout\n    model.name = name or \"demo_export\"\n    model.train.per_replica_batch_size = batch_size\n    model.mode = tf.estimator.ModeKeys.PREDICT\n\n    model_dir = runner_conf.model_dir\n    export_dir_base = os.path.join(model_dir, model.serving.export_dir_base)\n    warmup_file = runner_conf.warmup_file\n    with ParserCtx(enable_fused_layout=enable_fused_layout):\n      task = CpuTraining(config=runner_conf, task=model.instantiate())\n      exporter = create_exporter(task, model_dir, warmup_file, export_dir_base,\n                                 dense_only)\n      serving_input_receiver_fn = task.create_serving_input_receiver_fn()\n      exporter.export_saved_model(serving_input_receiver_fn)\n\n\n@monolith_export\ndef import_saved_model(saved_model_path: str,\n                       input_name: str = \"instances\",\n                       output_name: str = 'output',\n                       signature: str = None):\n  \"\"\"导出saved_model\n  \n  Args:\n    saved_model_path (:obj:`str`): saved_model路径\n  \n  \"\"\"\n\n  class saved_model(object):\n\n    def __init__(self, saved_model_path, signature, inputs, outputs):\n      basename = os.path.basename(saved_model_path)\n      if not basename.isnumeric():\n        versions = []\n        for subitem in tf.io.gfile.listdir(saved_model_path):\n          if subitem.isnumeric():\n            versions.append(int(subitem))\n\n        if versions:\n          versions.sort()\n          saved_model_path = os.path.join(saved_model_path, str(versions[-1]))\n        else:\n          raise RuntimeError(f\"no models in dir {saved_model_path}\")\n\n      self._saved_model_path = saved_model_path\n      if signature:\n        self._signature = signature\n      else:\n        self._signature = tf.compat.v1.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY\n\n      if inputs:\n        self._inputs = inputs if isinstance(inputs, (list, tuple)) else [inputs]\n      else:\n        self._inputs = None\n\n      if outputs:\n        self._outputs = outputs if isinstance(outputs,\n                                              (list, tuple)) else [outputs]\n      else:\n        self._outputs = None\n\n    def __enter__(self):\n\n      class infer(object):\n\n        def __init__(self, graph, sess, placeholders, output_dict,\n                     output_name_map):\n          self._graph = graph\n          self._sess = sess\n\n          self._placeholders = placeholders\n          self._output_dict = output_dict\n          self._output_name_map = output_name_map\n\n        def __call__(self, features: Dict[str, np.ndarray]) -> List[np.ndarray]:\n          with self._graph.as_default(), self._sess.as_default():\n            if len(self._placeholders) == 1:\n              placeholders = next(iter(self._placeholders.values()))\n              result = sess.run(self._output_dict,\n                                feed_dict={placeholders: features})\n            else:\n              result = sess.run(self._output_dict,\n                                feed_dict={\n                                    self._placeholders[name]: feature\n                                    for name, feature in features.items()\n                                })\n            return {\n                self._output_name_map[key]: tensor\n                for key, tensor in result.items()\n            }\n\n      tag = tf.compat.v1.saved_model.tag_constants.SERVING\n      graph = tf.compat.v1.Graph()\n      sess = tf.compat.v1.Session(graph=graph)\n      with graph.as_default(), sess.as_default():\n        imported = tf.compat.v1.saved_model.load(sess, {tag},\n                                                 self._saved_model_path)\n        print(imported.signature_def, flush=True)\n        signature_def = imported.signature_def[self._signature]\n\n        placeholders: Dict[str, tf.compat.v1.placeholder] = {}\n        for input_name in self._inputs:\n          input_ph_name = signature_def.inputs[input_name].name\n          input_ph = graph.get_tensor_by_name(input_ph_name)\n          placeholders[input_name] = input_ph\n\n        output_dict, output_name_map = {}, {}\n        if self._outputs:\n          for output_name in self._outputs:\n            output_tensor_name = signature_def.outputs[output_name].name\n            output_tensor = graph.get_tensor_by_name(output_tensor_name)\n            if output_tensor_name.endswith(':0'):\n              output_tensor_name = output_tensor_name[0:-2]\n            output_dict[output_tensor_name] = output_tensor\n            output_name_map[output_tensor_name] = output_name\n        else:\n          for output_name, tensor in signature_def.outputs.items():\n            output_tensor_name = tensor.name\n            output_tensor = graph.get_tensor_by_name(output_tensor_name)\n            if output_tensor_name.endswith(':0'):\n              output_tensor_name = output_tensor_name[0:-2]\n            output_dict[output_tensor_name] = output_tensor\n            output_name_map[output_tensor_name] = output_name\n\n        logging.info('import_saved_model finished')\n\n        return infer(graph, sess, placeholders, output_dict, output_name_map)\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n      logging.info('exit import_saved_model')\n\n  return saved_model(saved_model_path, signature, input_name, output_name)\n"
  },
  {
    "path": "monolith/native_training/estimator_dist_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport socket\nimport time\nimport unittest\nfrom multiprocessing import Process\n\nimport tensorflow as tf\n\nfrom monolith.native_training.runner_utils import RunnerConfig\nfrom monolith.native_training.model import TestFFMModel\nfrom monolith.native_training.service_discovery import TfConfigServiceDiscovery\nfrom monolith.native_training.estimator import Estimator\nfrom monolith.native_training.utils import get_test_tmp_dir\n\nmodel_name = 'dist_test'\nmodel_dir = \"{}/{}/ckpt\".format(get_test_tmp_dir(), model_name)\nexport_base = \"{}/{}/saved_model\".format(get_test_tmp_dir(), model_name)\n_EXIT_SUCCESS = 0\n\n\ndef get_free_port():\n  \"\"\"TODO(fitzwang) this function is not safe in preemption env\"\"\"\n  sock = socket.socket()\n  sock.bind(('', 0))\n  ip, port = sock.getsockname()\n  sock.close()\n  return port\n\n\ndef get_cluster(ps_num, worker_num):\n  cluster = {\n      'ps': ['localhost:{}'.format(get_free_port()) for _ in range(ps_num)],\n      'worker': [\n          'localhost:{}'.format(get_free_port()) for _ in range(worker_num - 1)\n      ],\n      'chief': ['localhost:{}'.format(get_free_port())]\n  }\n\n  return cluster\n\n\ndef get_saved_model_path(export_base):\n  try:\n    candidates = []\n    for f in os.listdir(export_base):\n      if not (f.startswith('temp') or f.startswith('tmp')):\n        fname = os.path.join(export_base, f)\n        if os.path.isdir(fname):\n          candidates.append(fname)\n    candidates.sort()\n    return candidates[-1]\n  except:\n    return \"\"\n\n\nclass EstimatorTrainTest(unittest.TestCase):\n  \"\"\"The tests here are for testing complilation.\"\"\"\n  params = None\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    if tf.io.gfile.exists(model_dir):\n      tf.io.gfile.rmtree(model_dir)\n\n    params = TestFFMModel.params()\n    params.metrics.enable_deep_insight = False\n    params.train.per_replica_batch_size = 64\n\n    cls.params = params\n\n  def train(self):\n    ps_num, worker_num = 2, 3\n    cluster = get_cluster(ps_num, worker_num)\n\n    def start_server(server_type, index):\n      task = {'type': server_type, 'index': index}\n      tf_confg = {'cluster': cluster, 'task': task}\n      discovery = TfConfigServiceDiscovery(tf_confg)\n\n      dct_config = RunnerConfig(index=discovery.index,\n                                model_dir=model_dir,\n                                ps_num=ps_num,\n                                worker_num=worker_num,\n                                server_type=discovery.server_type)\n      estimator = Estimator(self.params, dct_config, discovery)\n      estimator.train(steps=10)\n\n    threads = []\n    for i in range(ps_num):\n      thread = Process(target=start_server, args=('ps', i))\n      thread.start()\n      threads.append(thread)\n\n    for i in range(worker_num):\n      if i == 0:\n        thread = Process(target=start_server, args=('chief', i))\n      else:\n        thread = Process(target=start_server, args=('worker', i - 1))\n      thread.start()\n      threads.append(thread)\n      if i == 0:\n        time.sleep(1)\n\n    for thread in threads:\n      thread.join()\n      assert thread.exitcode == _EXIT_SUCCESS\n\n  def eval(self):\n    ps_num, worker_num = 2, 3\n    cluster = get_cluster(ps_num, worker_num)\n\n    def start_server(server_type, index):\n      task = {'type': server_type, 'index': index}\n      tf_confg = {'cluster': cluster, 'task': task}\n      discovery = TfConfigServiceDiscovery(tf_confg)\n\n      dct_config = RunnerConfig(index=discovery.index,\n                                model_dir=model_dir,\n                                ps_num=ps_num,\n                                worker_num=worker_num,\n                                server_type=discovery.server_type)\n\n      estimator = Estimator(self.params, dct_config, discovery)\n      estimator.evaluate(steps=10)\n\n    threads = []\n    for i in range(ps_num):\n      thread = Process(target=start_server, args=('ps', i))\n      thread.start()\n      threads.append(thread)\n\n    for i in range(worker_num):\n      if i == 0:\n        thread = Process(target=start_server, args=('chief', i))\n      else:\n        thread = Process(target=start_server, args=('worker', i - 1))\n      thread.start()\n      threads.append(thread)\n      if i == 0:\n        time.sleep(1)\n\n    for thread in threads:\n      thread.join()\n      assert thread.exitcode == _EXIT_SUCCESS\n\n  def test_dist(self):\n    self.train()\n    self.eval()\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/estimator_mode_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nimport socket\nimport unittest\nimport copy\nimport subprocess\nfrom absl import flags, logging\nfrom typing import Dict, List\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import test_util\n\nfrom monolith.native_training.runner_utils import RunnerConfig\nfrom monolith.native_training.estimator import Estimator, import_saved_model, RunConfig\nfrom monolith.native_training.utils import get_test_tmp_dir\nfrom monolith.native_training.tasks.sparse_dense_gpu.model_test import gen_input_file, MultiHeadModel\n\nFLAGS = flags.FLAGS\n\n\n#copy from monolith/native_training/cpu_training_test.py\nclass DistributedTrainTest(tf.test.TestCase):\n  _DISTRIBUTED_TRAIN_BINARY = \"monolith/native_training/tasks/sparse_dense_gpu/model\"\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    FLAGS.dataset_input_patterns = f\"/tmp/estimator_mode_test_eb.pb\"\n    gen_input_file(FLAGS.dataset_input_patterns)\n\n    # maybe file is not enough to train\n    def link_some_file(suffix):\n      if not os.path.exists(FLAGS.dataset_input_patterns + suffix):\n        os.symlink(FLAGS.dataset_input_patterns,\n                   FLAGS.dataset_input_patterns + suffix)\n\n    for i in range(10):\n      link_some_file(str(i))\n    FLAGS.dataset_input_patterns += \"{INT(0,99)}\"\n\n  def find_free_port(self, count):\n    port_list = []\n    while len(port_list) < count:\n      sock = socket.socket()\n      sock.bind(('', 0))\n      port = sock.getsockname()[1]\n      if port not in port_list:\n        port_list.append(port)\n    return port_list\n\n  def _run_test(self,\n                task_name: str,\n                other_args: List,\n                num_ps: int,\n                num_workers: int,\n                num_dsworkers: int,\n                other_env: Dict = {},\n                worker_args: List = [],\n                use_mpi_run=False):\n    cur_modir = \"{}/{}/ckpt\".format(get_test_tmp_dir(), task_name)\n    if tf.io.gfile.exists(cur_modir):\n      tf.io.gfile.rmtree(cur_modir)\n    os.makedirs(cur_modir)\n    logging.info(f\"show cur_modir: {cur_modir}\")\n    args_tmpl = [\n        self._DISTRIBUTED_TRAIN_BINARY,\n        \"--mode=train\",\n        f\"--model_dir={cur_modir}\",\n        f\"--num_ps={num_ps}\",\n        f\"--num_workers={num_workers}\",\n        f\"--uuid={self._testMethodName}\",\n        f\"--dataset_input_patterns={FLAGS.dataset_input_patterns}\",\n        f\"--dataset_input_use_snappy=False\",\n        #f\"--dataset_input_use_parquet=True\",\n        \"--lagrangex_header=True\",\n        \"--sort_id=False\",\n        \"--kafka_dump=False\",\n        \"--kafka_dump_prefix=False\",\n        \"--data_type=ExampleBatch\",\n        \"--discovery_type=mlp\",\n        \"--operation_timeout_in_ms=10000\",\n        \"--disable_native_metrics=True\",\n        \"--dataset_use_dataservice=True\"\n        if num_dsworkers else \"--dataset_use_dataservice=False\",\n        \"--cluster_type=stable\",\n    ] + other_args\n    my_env = os.environ.copy()\n    my_env.update(other_env)\n\n    def fill_host_env(role_name, num_role, cur_port_list):\n      if num_role <= 0:\n        return\n      all_host = []\n      all_addr = []\n      role_name = role_name.upper()\n      for i in range(num_role):\n        all_host.append(\"localhost\")\n        cur_port = cur_port_list[i]\n        all_addr.append(f\"localhost:{cur_port}\")\n        my_env[f\"MLP_{role_name}_{i}_PORT\"] = f\"{cur_port}\"\n        my_env[f\"MLP_{role_name}_{i}_HOST\"] = f\"localhost\"\n        my_env[f\"MLP_{role_name}_{i}_PRIMARY_HOST\"] = f\"localhost\"\n      my_env[f\"MLP_{role_name}_NUM\"] = f\"{num_role}\"\n      my_env[f\"MLP_{role_name}_ALL_HOSTS\"] = f\"{','.join(all_host)}\"\n      my_env[f\"MLP_{role_name}_ALL_PRIMARY_HOSTS\"] = my_env[\n          f\"MLP_{role_name}_ALL_HOSTS\"]\n      my_env[f\"MLP_{role_name}_ALL_ADDRS\"] = f\"{','.join(all_addr)}\"\n      my_env[f\"MLP_{role_name}_ALL_PRIMARY_ADDRS\"] = my_env[\n          f\"MLP_{role_name}_ALL_ADDRS\"]\n\n    #data_service_dispachter\n    num_dispatcher = 0\n    if num_dsworkers:\n      num_dispatcher = 1\n    all_port = self.find_free_port(num_ps + num_workers + num_dsworkers +\n                                   num_dispatcher)\n    ps_port = all_port[:num_ps]\n    worker_port = all_port[num_ps:num_ps + num_workers]\n    dsworker_port = all_port[num_ps + num_workers:-num_dispatcher]\n    dispatcher_port = all_port[-num_dispatcher:]\n\n    fill_host_env('ps', num_ps, ps_port)\n    fill_host_env('worker', num_workers, worker_port)\n    fill_host_env('dispatcher', num_dispatcher, dispatcher_port)\n    fill_host_env('dsworker', num_dsworkers, dsworker_port)\n\n    processes = {}\n    log_files = []\n\n    def start_process(role_name, num_role, cur_port_list, use_mpi_run=False):\n      if use_mpi_run:\n        hostfile = f\"{cur_modir}/../hostfile\"\n        f = open(hostfile, \"w\")\n        f.write(f\"localhost slots={num_role}\")\n        f.close()\n\n        args = copy.copy(args_tmpl)\n        args.append(f\"--server_type={role_name}\")\n        if role_name == \"worker\":\n          args += worker_args\n        cur_env = copy.deepcopy(my_env)\n        cur_env[\"MLP_ROLE\"] = role_name\n        cur_env[\"MLP_PORT\"] = f\"{cur_port_list[0]}\"\n        cur_env[\"MLP_SSH_PORT\"] = f\"{worker_port[0]}\"\n        cur_env[\"MONOLITH_WITH_HOROVOD\"] = f\"1\"\n        cur_env[\"MONOLITH_WITH_HOROVOD_FID_G2G\"] = f\"1\"\n        cur_env[\"MONOLITH_WITH_ALLREDUCE_FUSION\"] = f\"one\"\n        #cur_env[\"MONOLITH_GPU_FEATURE_FACTORY_FUSION_LEVEL\"] = f\"1\"\n        #cur_env[\"HOROVOD_MPI_THREADS_DISABLE\"] = f\"1\"\n        #cur_env[\"GPU_AFFINITY_NIC_ADDRESS\"] = f\"1\"\n        #cur_env[\"NCCL_SOCKET_IFNAME\"] = f\"eth0\"\n        #cur_env[\"NCCL_P2P_LEVEL\"] = f\"1\"\n        mpi_run_args = [\n            \"mpirun\",\n            \"--map-by\",\n            f\"ppr:{num_role}:node\",\n            \"-np\",\n            f\"{num_role}\",\n            \"--hostfile\",\n            hostfile,\n            \"--allow-run-as-root\",\n            \"-oversubscribe\",\n            \"--tag-output\",\n            \"--report-bindings\",\n            #\"--mca\", \"btl_tcp_if_include\", \"eth0\", \"--mca\", \"oob_tcp_if_include\", \"eth0\"\n        ]\n\n        for k, v in cur_env.items():\n          mpi_run_args.append(\"-x\")\n          mpi_run_args.append(f\"{k}={v}\")\n        args = mpi_run_args + args\n        process = subprocess.Popen(args)\n        logging.info(f\"start a process for {role_name}:{range(num_role)}\")\n        processes[f\"{role_name}:{0}\"] = process\n        for i in range(1, num_role):\n          processes[f\"{role_name}:{i}\"] = None\n      else:\n        for i in reversed(range(num_role)):\n          log_file = open(cur_modir + f\"/../{role_name}_{i}.log\", 'w')\n          log_files.append(log_file)\n          args = copy.copy(args_tmpl)\n          args.append(f\"--server_type={role_name}\")\n          args.append(\"--index={}\".format(i))\n          cur_env = copy.deepcopy(my_env)\n          cur_env[\"MLP_ROLE\"] = role_name\n          cur_env[\"MLP_ROLE_INDEX\"] = f\"{i}\"\n          cur_env[\"MLP_PORT\"] = f\"{cur_port_list[i]}\"\n          cur_env[\"MLP_SSH_PORT\"] = f\"{worker_port[0]}\"\n          #if i == 0 and role_name == \"worker\":\n          #  time.sleep(5)\n          '''\n          shell_commond = \"\"\n          for k, v in cur_env.items():\n            if \"BASH_FUNC_\" in k:\n              continue\n            shell_commond += f\"{k}={v} \"\n          shell_commond += f\" bazel-bin/{args[0]} \"\n          for arg in args[1:]:\n            shell_commond += f\"{arg} \"\n          logging.info(\n              f\"start a shell for {role_name}:{i} \\n {shell_commond}\")\n          '''\n          process = subprocess.Popen(args, env=cur_env)\n          logging.info(f\"start a process for {role_name}:{i}\")\n          processes[f\"{role_name}:{i}\"] = process\n\n    start_process('dispatcher', num_dispatcher, dispatcher_port)\n    start_process('dsworker', num_dsworkers, dsworker_port)\n    start_process('ps', num_ps, ps_port)\n    start_process('worker', num_workers, worker_port, use_mpi_run=use_mpi_run)\n\n    print(\" \".join(args_tmpl), num_ps, num_workers, num_dsworkers)\n\n    def wait_for_process(role_name, num_role, timeout=10, ignore_timeout=False):\n      for i in range(num_role):\n        role = f\"{role_name}:{i}\"\n        if role not in processes:\n          continue\n        process = processes[role]\n        if process is None:\n          continue\n        if not ignore_timeout:\n          self.assertEqual(process.wait(timeout=timeout), 0)\n        else:\n          try:\n            self.assertEqual(process.wait(timeout=timeout), 0)\n          except subprocess.TimeoutExpired as e:\n            logging.warning(f\"exit process for {role} timeout\")\n            process.terminate()\n        processes.pop(role)\n        logging.info(f\"exit process for {role}\")\n\n    wait_for_process('worker', 1, 250)\n    wait_for_process('worker', num_workers, timeout=10, ignore_timeout=True)\n    wait_for_process('ps', num_ps, timeout=1)\n    wait_for_process('dsworker', num_dsworkers, timeout=1,\n                     ignore_timeout=True)  #maybe chief port not free\n    wait_for_process('dispatcher',\n                     num_dispatcher,\n                     timeout=1,\n                     ignore_timeout=True)  #maybe chief port not free\n\n    for log_file in log_files:\n      log_file.flush()\n      log_file.close()\n    tf.io.gfile.rmtree(cur_modir)\n\n  def run_cpu(self, name, other_args):\n    # TODO cpu mode run gpu have error\n    if test_util.is_gpu_available(cuda_only=True):\n      return\n    args = [\n        \"--enable_gpu_training=False\",\n        \"--enable_sync_training=False\",\n        \"--embedding_prefetch_capacity=1\",\n        \"--enable_embedding_postpush=True\",\n        \"--chief_timeout_secs=20\",\n    ] + other_args\n    num_ps = 2\n    num_workers = 2\n    num_dsworkers = 0\n    self._run_test(f\"full_cpu_{name}\", args, num_ps, num_workers, num_dsworkers)\n\n  def test_cpu0(self):\n    args = [\n        \"--enable_fused_layout=False\",\n        \"--use_native_multi_hash_table=False\",\n    ]\n    self.run_cpu('0', args)\n\n  def test_cpu1(self):\n    args = [\n        \"--enable_fused_layout=False\",\n        \"--use_native_multi_hash_table=True\",\n    ]\n    self.run_cpu('1', args)\n\n  def test_cpu2(self):\n    args = [\n        \"--enable_fused_layout=True\",\n        \"--use_native_multi_hash_table=True\",\n    ]\n    self.run_cpu('2', args)\n\n  def test_cpu3(self):\n    args = [\n        \"--enable_fused_layout=True\",\n        \"--use_native_multi_hash_table=False\",\n    ]\n    self.run_cpu('3', args)\n\n  def sparse_dense_run(self, name, other_args):\n    if not test_util.is_gpu_available(cuda_only=True):\n      return\n    gpus = tf.config.list_physical_devices('GPU')\n    args = [\n        \"--enable_gpu_training=True\",\n        \"--enable_sync_training=True\",\n        \"--enable_partial_sync_training=True\",\n        \"--embedding_prefetch_capacity=1\",\n        \"--enable_embedding_postpush=True\",\n        '--params_override={\"train.max_steps\": 10}',\n    ] + other_args\n    worker_args = []\n    num_ps = 2\n    num_workers = min(2, len(gpus))\n    num_dsworkers = 1\n    other_env = {}\n    self._run_test(f\"sparse_dense_{name}\",\n                   args,\n                   num_ps,\n                   num_workers,\n                   num_dsworkers,\n                   other_env=other_env,\n                   worker_args=worker_args,\n                   use_mpi_run=True)\n\n  def test_sparse_dense0(self):\n    args = [\n        \"--enable_fused_layout=True\",\n        \"--use_native_multi_hash_table=False\",\n    ]\n    self.sparse_dense_run('0', args)\n\n  def test_sparse_dense1(self):\n    args = [\n        \"--enable_fused_layout=True\",\n        \"--use_native_multi_hash_table=True\",\n    ]\n    self.sparse_dense_run('1', args)\n\n  def test_sparse_dense2(self):\n    args = [\n        \"--enable_fused_layout=False\",\n        \"--use_native_multi_hash_table=False\",\n    ]\n    self.sparse_dense_run('2', args)\n\n  def test_sparse_dense3(self):\n    args = [\n        \"--enable_fused_layout=False\",\n        \"--use_native_multi_hash_table=True\",\n    ]\n    self.sparse_dense_run('3', args)\n\n  def full_gpu_run(self, name, other_args):\n    if not test_util.is_gpu_available(cuda_only=True):\n      return\n    gpus = tf.config.list_physical_devices('GPU')\n    args = [\n        \"--enable_gpu_training=True\",\n        \"--enable_sync_training=True\",\n        \"--reorder_fids_in_data_pipeline=True\",\n        \"--filter_type=probabilistic_filter\",\n        \"--embedding_prefetch_capacity=1\",\n        \"--enable_async_optimize=False\",\n        '--params_override={\"train.max_steps\": 10}',\n    ] + other_args\n    worker_args = []\n    num_ps = 0\n    num_workers = min(2, len(gpus))\n    num_dsworkers = 1\n    other_env = {}\n    self._run_test(f\"full_gpu_{name}\",\n                   args,\n                   num_ps,\n                   num_workers,\n                   num_dsworkers,\n                   other_env=other_env,\n                   worker_args=worker_args,\n                   use_mpi_run=True)\n\n  def test_full_gpu_0(self):\n    args = [\n        \"--enable_fused_layout=True\",\n        \"--use_native_multi_hash_table=False\",\n    ]\n    self.full_gpu_run('0', args)\n\n  def test_full_gpu_1(self):\n    args = [\n        \"--enable_fused_layout=True\",\n        \"--use_native_multi_hash_table=True\",\n    ]\n    self.full_gpu_run('1', args)\n\n  def test_full_gpu_2(self):\n    args = [\n        \"--enable_fused_layout=False\",\n        \"--use_native_multi_hash_table=False\",\n    ]\n    self.full_gpu_run('2', args)\n\n  def test_full_gpu_3(self):\n    args = [\n        \"--enable_fused_layout=False\",\n        \"--use_native_multi_hash_table=True\",\n    ]\n    self.full_gpu_run('3', args)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/estimator_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nimport unittest\n\nimport tensorflow as tf\n\nfrom monolith.native_training.runner_utils import RunnerConfig\nfrom monolith.native_training.input import generate_ffm_example\nfrom monolith.native_training.model import TestFFMModel,\\\n  _VOCAB_SIZES, _NUM_EXAMPLES\nfrom monolith.native_training.estimator import Estimator, import_saved_model\nfrom monolith.native_training.utils import get_test_tmp_dir\n\nmodel_name = 'estimator_test'\n\nmodel_dir = \"{}/{}/ckpt\".format(get_test_tmp_dir(), model_name)\nexport_base = \"{}/{}/ckpt/exported_models\".format(get_test_tmp_dir(),\n                                                  model_name)\n\ndef get_saved_model_path(export_base):\n  try:\n    candidates = []\n    for f in os.listdir(export_base):\n      if not (f.startswith('temp') or f.startswith('tmp')):\n        fname = os.path.join(export_base, f)\n        if os.path.isdir(fname):\n          candidates.append(fname)\n    candidates.sort()\n    return candidates[-1]\n  except:\n    return \"\"\n\n\nclass EstimatorTrainTest(unittest.TestCase):\n  \"\"\"The tests here are for testing complilation.\"\"\"\n  params = None\n\n  @classmethod\n  def setUpClass(cls) -> None:\n    if tf.io.gfile.exists(model_dir):\n      tf.io.gfile.rmtree(model_dir)\n\n    params = TestFFMModel.params()\n    params.metrics.enable_deep_insight = False\n    params.train.per_replica_batch_size = 64\n    params.serving.export_dir_base = export_base\n    params.serving.shared_embedding = True\n\n    cls.params = params\n    cls.conf = RunnerConfig(is_local=True,\n                            num_ps=0,\n                            model_dir=model_dir,\n                            use_native_multi_hash_table=False)\n\n  def train(self):\n    estimator = Estimator(self.params, self.conf)\n    estimator.train(steps=10)\n\n  def eval(self):\n    estimator = Estimator(self.params, self.conf)\n    estimator.evaluate(steps=10)\n\n  def predict(self):\n    estimator = Estimator(self.params, self.conf)\n    estimator.predict()\n\n  def export_saved_model(self):\n    estimator = Estimator(self.params, self.conf)\n    estimator.export_saved_model()\n\n  def import_saved_model(self):\n    saved_model_path = get_saved_model_path(export_base)\n    print('saved_model_path', saved_model_path, flush=True)\n    with import_saved_model(saved_model_path=saved_model_path) as infer:\n      # There are some bugs here since functions to restore tables are not called. Will\n      # resolve this by using resource concept in the future.\n      start = time.time()\n      num_ins = 0\n      for i in range(10):\n        features = [\n            generate_ffm_example(_VOCAB_SIZES) for _ in range(_NUM_EXAMPLES)\n        ]\n        num_ins += len(features)\n        infer(features)\n      end = time.time()\n      print(start, end, num_ins, 1000 * (end - start) / 10)\n\n  def test_local(self):\n    self.train()\n    self.eval()\n    self.predict()\n    self.export_saved_model()\n    self.import_saved_model()\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/feature.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport abc\nfrom collections import namedtuple\nimport copy\nimport enum\nfrom dataclasses import dataclass, asdict, field\nfrom typing import Callable, Dict, Iterable, List, Tuple, Set, NamedTuple, Union\nimport sys\nimport os\n\nfrom absl import logging\n\nimport tensorflow as tf\n\nfrom monolith.native_training import device_utils\nfrom monolith.native_training import distribution_ops\nfrom monolith.native_training import embedding_combiners\nfrom monolith.native_training import entry\nfrom monolith.native_training import learning_rate_functions\nfrom monolith.native_training import ragged_utils\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\nfrom monolith.native_training.model_export.export_context import is_exporting\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training import prefetch_queue\n\n_FEATURE_STRAT_END_KEY = \"{}:{}_{}\"\n\n# Default expire time is 100 years.\nDEFAULT_EXPIRE_TIME = 36500\n\n\nclass FeatureEmbTable(abc.ABC):\n  \"\"\"Used by framework. Do not use in the user code directly. Instead, using FeatureSlot.\"\"\"\n\n  def add_feature_slice(self,\n                        segment: embedding_hash_table_pb2.EntryConfig.Segment,\n                        learning_rate_fn=None):\n    \"\"\"\n    Add one feature slice for this embedding table.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def embedding_lookup(self, feature_name: str, start: int,\n                       end: int) -> tf.Tensor:\n    \"\"\"\n    Returns combined embedding tensors for the given feature name.\n    \"\"\"\n    pass\n\n  def set_feature_metadata(self, feature_name: str,\n                           combiner: embedding_combiners.Combiner):\n    pass\n\n\nclass FeatureSlice(NamedTuple):\n  \"\"\"Represents a slice of a feature slot.\"\"\"\n  feature_slot: \"FeatureSlot\"\n  start: int\n  end: int\n\n\n@dataclass\nclass FeatureSlotConfig:\n  name: str = None\n  has_bias: bool = False\n  bias_initializer: entry.Initializer = entry.ZerosInitializer()\n  bias_optimizer: entry.Optimizer = entry.FtrlOptimizer(\n      initial_accumulator_value=1e-6, beta=1.0)\n  bias_compressor: entry.Compressor = entry.Fp32Compressor()\n  bias_learning_rate_fn: Callable = None\n  default_vec_initializer: entry.Initializer = entry.RandomUniformInitializer()\n  default_vec_optimizer: entry.Optimizer = entry.AdagradOptimizer(\n      initial_accumulator_value=1.0)\n  default_vec_compressor: entry.Compressor = entry.Fp16Compressor()\n  default_vec_learning_rate_fn: Callable = None\n  hashtable_config: entry.HashTableConfig = entry.CuckooHashTableConfig()\n  slot_id: int = None\n  occurrence_threshold: int = 0\n  expire_time: int = DEFAULT_EXPIRE_TIME\n\n  def __post_init__(self):\n    if not self.name:\n      self.name = str(self.slot_id)\n\n\n@monolith_export\nclass FeatureSlot:\n  \"\"\"维护特征与HashTable的关系, 隐藏HashTable的细节. FeatureSlot可以看成是用户视角的HashTable\n  \n  Args:\n    table (:obj:`FeatureEmbTable`): 内部HashTable\n    config (:obj:`FeatureSlotConfig`): 特征配置\n  \n  \"\"\"\n\n  def __init__(self, table: FeatureEmbTable, config: FeatureSlotConfig):\n    self._table = table\n    self._config = config\n    self._current_dim_size = 0\n    self._feature_columns = set()\n\n    if self._config.has_bias:\n      self._bias_slice = self.add_feature_slice(\n          1, self._config.bias_initializer, self._config.bias_optimizer,\n          self._config.bias_compressor, self._config.bias_learning_rate_fn)\n\n  def add_feature_slice(self,\n                        dim_size: int,\n                        initializer: entry.Initializer = None,\n                        optimizer: entry.Optimizer = None,\n                        compressor: entry.Compressor = None,\n                        learning_rate_fn=None) -> FeatureSlice:\n    \"\"\"\n    在哈希表中增加一段长度为|dim_size|，并采用|initializer|作为初始化器，|optimizer|作为\n    优化器，同时在serving中使用|compressor|作为压缩器的embedding. \n    返回一个feature slice被FeatureColumn使用\n\n    Args:\n      dim_size (:obj:`float`): 这段embedding slice的长度\n      optimizer （:obj:`entry.Optimizer`): 这段embedding slice的初始化器\n      compressor （:obj:`entry.Compressor`): 这段embedding slice的初始化器\n      learning_rate_fn (:obj:`Callable`): 如果不为None，覆盖在optimizer中定义的学习率\n    \"\"\"\n    initializer = initializer or self._config.default_vec_initializer\n    optimizer = optimizer or self._config.default_vec_optimizer\n    compressor = compressor or self._config.default_vec_compressor\n    learning_rate_fn = learning_rate_fn or self._config.default_vec_learning_rate_fn\n    segment = entry.CombineAsSegment(dim_size, initializer, optimizer,\n                                     compressor)\n    self._table.add_feature_slice(segment, learning_rate_fn=learning_rate_fn)\n\n    s = FeatureSlice(self, self._current_dim_size,\n                     self._current_dim_size + dim_size)\n    self._current_dim_size = self._current_dim_size + dim_size\n    return s\n\n  def get_bias_slice(self):\n    assert self._config.has_bias\n    return self._bias_slice\n\n  def _add_feature_column(self, fc):\n    self._feature_columns.add(fc)\n    self._table.set_feature_metadata(fc.feature_name, fc.combiner)\n\n  def _fc_embedding_lookup(self, feature_name: str, s: FeatureSlice):\n    return self._table.embedding_lookup(feature_name, s.start, s.end)\n\n  def get_feature_columns(self):\n    return self._feature_columns\n\n  @property\n  def slot(self):\n    return int(self._config.name)\n\n  @property\n  def name(self):\n    return self._config.name\n\n\n@monolith_export\nclass FeatureColumn:\n  \"\"\"将FeatureColumn与输入的Feature进行链接\n  \n  Args:\n    feature_slot (:obj:`FeatureSlot`): 这个类对应的FeatureSlot\n    feature_name (:obj:`str`): 这个类对应的链接的feature_name（在input_fn返回的结果）\n  \n  \"\"\"\n\n  @classmethod\n  def reduce_sum(cls):\n    return embedding_combiners.ReduceSum()\n\n  @classmethod\n  def reduce_mean(cls):\n    return embedding_combiners.ReduceMean()\n\n  @classmethod\n  def first_n(cls, seq_length: int):\n    return embedding_combiners.FirstN(seq_length)\n\n  def __init__(self,\n               feature_slot: FeatureSlot,\n               feature_name: str,\n               combiner=None):\n    self._feature_name = feature_name\n    self._feature_slot = feature_slot\n    self._combiner = combiner or self.reduce_sum()\n    self._size_tensor = None\n    feature_slot._add_feature_column(self)\n\n  def embedding_lookup(self, s: FeatureSlice) -> tf.Tensor:\n    \"\"\"返回feature_name在feature_slot中进行查询之后的结果. \"\"\"\n    assert s.feature_slot == self._feature_slot, \"Slice must come from the dedicated feature slot.\"\n    return self._feature_slot._fc_embedding_lookup(self._feature_name, s)\n\n  def get_all_embeddings_concat(self) -> tf.Tensor:\n    \"\"\"\n    Returns concatenated all embeddings owned by this column. Used in calculate gradients\n    \"\"\"\n    return self._feature_slot._table.embedding_lookup(self._feature_name, None,\n                                                      None)\n\n  def get_all_embedding_slices(self) -> List[tf.Tensor]:\n    \"\"\"\n    Returns concatenated all embedding slices owned by this column. Used in computing gradients\n    \"\"\"\n    output_list = []\n    for k, v in self._embedding_slices.items():\n      if self._feature_name in k:\n        output_list.append(v)\n    return output_list\n\n  @property\n  def feature_name(self):\n    return self._feature_name\n\n  @property\n  def feature_slot(self) -> FeatureSlot:\n    return self._feature_slot\n\n  @property\n  def combiner(self) -> embedding_combiners.Combiner:\n    return self._combiner\n\n  def get_bias(self) -> tf.Tensor:\n    \"\"\"字节内部使用. 请勿直接使用\"\"\"\n    bias_slice = self._feature_slot.get_bias_slice()\n    return self._feature_slot._fc_embedding_lookup(self._feature_name,\n                                                   bias_slice)\n\n  def set_size_tensor(self, row_lengths: tf.Tensor):\n    assert isinstance(self._combiner, embedding_combiners.FirstN\n                     ), \"This function is only supported in a sequence feature.\"\n    seq_length = self._combiner.max_seq_length\n    # Convert row_lengths to [B, max_seq_length] Tensor, in which\n    # the first row_length elements of each row are 1, and the rest are\n    # 0. This is used as the size_tensor\n    batch_size = tf.size(row_lengths)  # 0-D Tensor\n    boolean_mask = tf.less(\n        tf.reshape(\n            tf.tile(tf.range(0, seq_length), [batch_size]),\n            [batch_size, -1],\n        ), tf.expand_dims(row_lengths, 1))  # [B, max_seq_length] Tensor\n    self._size_tensor = tf.cast(boolean_mask, tf.int32, name='size_tensor')\n\n  def get_size_tensor(self):\n    return self._size_tensor\n\n\nFeatureColumnV1 = FeatureColumn\n\nSliceConfig = namedtuple(\"SliceConfig\", [\"segment\", \"learning_rate_fn\"])\n\n\nclass TableConfig(NamedTuple):\n  slice_configs: List[SliceConfig]\n  feature_names: Set[str]\n  unmerged_slice_dims: List[int]\n  hashtable_config: entry.HashTableConfig\n  feature_to_combiners: Dict[str, embedding_combiners.Combiner]\n\n\nclass FeatureFactory(abc.ABC):\n  \"\"\"Used to get features in the model_fn.\"\"\"\n\n  def __init__(self):\n    self.slot_to_occurrence_threshold = {}\n    self.slot_to_expire_time = {}\n\n  @abc.abstractmethod\n  def create_feature_slot(self, config: FeatureSlotConfig) -> FeatureSlot:\n    \"\"\"Creates a feature slot by config.\"\"\"\n\n  def apply_gradients(self,\n                      grads_and_vars: Iterable[Tuple[tf.Tensor, tf.Tensor]],\n                      req_time: tf.Tensor = None,\n                      scale: tf.Tensor = 1) -> tf.Operation:\n    \"\"\"\n    Applies the gradients to Features owned by this factory.\n    The reason why we do not make per table based apply_gradients is because of\n    performance reason. In the runtime, we may do a batch lookup. \n    Args: \n      grads_and_vars - vars must be the all_embedding_concat from each FeatureColumn.\n    \"\"\"\n    raise NotImplementedError(\n        \"apply_gradients is not supported in this factory.\")\n\n\nclass DummyFeatureEmbTable(FeatureEmbTable):\n  \"\"\"It is used to collect config of table from model_fn.\"\"\"\n\n  def __init__(self, batch_size, hashtable_config):\n    self._batch_size = batch_size\n    self._hashtable_config = hashtable_config\n    self._slices = []\n    self._merged_slices = []\n    self._feature_names = set()\n    self._feature_to_combiner = {}\n    self._dim_size = 0\n\n  def add_feature_slice(self,\n                        segment: embedding_hash_table_pb2.EntryConfig.Segment,\n                        learning_rate_fn=None):\n    # The learning_rate_fn can be an instance of LearningRateFunction or a float\n    # value. By default, set the learning_rate_fn according to the optimizer config.\n    if learning_rate_fn is None:\n      opt_config = getattr(segment.opt_config,\n                           segment.opt_config.WhichOneof(\"type\"))\n      if hasattr(opt_config, 'warmup_steps') and opt_config.warmup_steps > 0:\n        learning_rate_fn = learning_rate_functions.PolynomialDecay(\n            initial_learning_rate=0.0,\n            decay_steps=opt_config.warmup_steps,\n            end_learning_rate=opt_config.learning_rate)\n      else:\n        learning_rate_fn = opt_config.learning_rate\n    self._dim_size += segment.dim_size\n    self._slices.append(SliceConfig(segment, learning_rate_fn))\n\n  def embedding_lookup(self, feature_name: str, start: int,\n                       end: int) -> tf.Tensor:\n    if start is None and end is None:\n      # This is the special case for gradients.\n      start = 0\n      end = self._dim_size\n\n    # TODO(leqi.zou): Maybe we should add a dict here to make sure for the\n    # same look up we should return same result.\n    emb_ph = tf.compat.v1.placeholder(tf.float32,\n                                      shape=[self._batch_size, end - start])\n    key = tf.compat.v1.ragged.placeholder(tf.int64, 1, [])\n    combiner = self._feature_to_combiner[feature_name]\n    combined = combiner.combine(\n        key,\n        emb_ph,\n        name=f'{combiner.__class__.__name__}_{feature_name}_{start}_{end}')\n    if self._batch_size:\n      shape = combined.shape.as_list()\n      shape[0] = self._batch_size\n      combined = tf.reshape(combined, shape)\n    return combined\n\n  def set_feature_metadata(self, feature_name: str,\n                           combiner: embedding_combiners.Combiner):\n    self._feature_names.add(feature_name)\n    self._feature_to_combiner[feature_name] = combiner\n\n  def get_table_config(self) -> TableConfig:\n    \"\"\"Returns merged slices of FeatureEmbTable\"\"\"\n    self._merged_slices = self._merge_slices()\n\n    # Note(youlong.cheng): This is mainly for tf.split after pooling embedding.\n    # The alternative way uses strided_slice causes duplicated backward\n    # calcualtion and unncessary memory write.\n    unmerged_slice_dims = [config.segment.dim_size for config in self._slices]\n\n    return TableConfig(self._merged_slices,\n                       [feature_name for feature_name in self._feature_names],\n                       unmerged_slice_dims, self._hashtable_config,\n                       self._feature_to_combiner)\n\n  def get_feature_names(self):\n    return self._feature_names\n\n  def _merge_slices(self):\n    \"\"\"Combines the slices which only differ in dim_size.\"\"\"\n    merged = []\n    # Using deepcopy to prevent modifing the proto in self._slices.\n    slices = copy.deepcopy(self._slices)\n    for s in slices:\n      if not merged:\n        merged.append(s)\n        continue\n      last_s = merged[-1]\n\n      last_dim_size = last_s.segment.dim_size\n      last_s.segment.ClearField(\"dim_size\")\n      dim_size = s.segment.dim_size\n      s.segment.ClearField(\"dim_size\")\n\n      if last_s.segment.SerializeToString() == s.segment.SerializeToString(\n      ) and str(last_s.learning_rate_fn) == str(s.learning_rate_fn):\n        # We can merge these two slices\n        last_s.segment.dim_size = last_dim_size + dim_size\n      else:\n        last_s.segment.dim_size = last_dim_size\n        s.segment.dim_size = dim_size\n        merged.append(s)\n    return merged\n\n\nclass DummyFeatureFactory(FeatureFactory):\n  \"\"\"Factory to collect the config.\"\"\"\n\n  def __init__(self, batch_size):\n    super().__init__()\n    self._batch_size = batch_size\n    self._tables = {}\n\n  def create_feature_slot(self, config: FeatureSlotConfig):\n    \"\"\"Creates a feature slot by config.\"\"\"\n    if config.name in self._tables:\n      raise NameError(\"Duplicate names for the table. Name: {}\".format(\n          config.name))\n    table = DummyFeatureEmbTable(self._batch_size, config.hashtable_config)\n    self._tables.update({config.name: table})\n\n    if config.slot_id is not None:\n      self.slot_to_occurrence_threshold.update(\n          {config.slot_id: config.occurrence_threshold})\n      self.slot_to_expire_time.update({config.slot_id: config.expire_time})\n    else:\n      logging.warning(\n          \"feature[{}] slot is None. pls check feature_list.conf\".format(\n              config.name))\n\n    return FeatureSlot(table, config)\n\n  def apply_gradients(self, *args, **kwargs) -> tf.Operation:\n    return tf.no_op()\n\n  def get_table_name_to_table_config(self) -> Dict[str, TableConfig]:\n    table_configs = {}\n    for k, v in self._tables.items():\n      table_config = v.get_table_config()\n      if len(table_config.slice_configs) > 0:\n        table_configs[k] = table_config\n      else:\n        raise RuntimeError(f'{k} has no slice, pls. check!')\n    return table_configs\n\n\nclass EmbeddingFeatureEmbTable(FeatureEmbTable):\n  \"\"\"Actual emb table that provides the embedding tensor from embeddings.\"\"\"\n\n  def __init__(self, embeddings: Dict[str, tf.Tensor],\n               embedding_slices: Dict[str, tf.Tensor]):\n    self._embeddings = embeddings\n    self._embedding_slices = embedding_slices\n\n  def embedding_lookup(self, feature_name: str, start: int,\n                       end: int) -> tf.Tensor:\n    if start is None and end is None:\n      # It is important to return the origin tensor since we may\n      # use this tensor as map key.\n      return self._embeddings[feature_name]\n    k = _FEATURE_STRAT_END_KEY.format(feature_name, start, end)\n    logging.vlog(1, \"_embedding_slices: {}\".format(self._embedding_slices))\n    return self._embedding_slices[k]\n\n\nclass _FeatureFactoryFusionHelper:\n  \"\"\"Only for feature to be reduced. Not for features to keep the original dim.\"\"\"\n\n  def __init__(self):\n    self._d = {}\n\n  def append(self, name, ragged_ids, embeddings, slice_dims):\n    self._d[name] = (ragged_ids.row_splits,\n                     ragged_utils.fused_value_rowids(ragged_ids), embeddings,\n                     ragged_ids.nrows(), slice_dims)\n\n  def reduce_and_split(self):\n    \"\"\"(reduce -> split) * N: BASIC for both CPU and GPU.\"\"\"\n    feature_name_to_slices = {}\n    for name, (_, value_rowids, embeddings, batch_size_tensor,\n               slice_dims) in self._d.items():\n      with tf.device(\"/device:CPU:0\"):\n        shape = tf.stack([batch_size_tensor,\n                          embeddings.shape.as_list()[1]])  # (batch_size, dim)\n      with device_utils.maybe_device_if_allowed('/device:GPU:0'):\n        # scatter_nd (a.k.a embedding_combiners.ReduceSum) + split\n        reduced_emb = tf.scatter_nd(\n            tf.expand_dims(value_rowids, -1),\n            embeddings,\n            shape,\n            name=name,\n        )\n        tf.compat.v1.add_to_collection(\"monolith_reduced_embs\", reduced_emb)\n        feature_name_to_slices[name] = tf.split(reduced_emb,\n                                                slice_dims,\n                                                axis=1,\n                                                name=name + \"_split\")\n    return feature_name_to_slices\n\n  def fused_reduce_and_split(self):\n    \"\"\"(reduce + split) * N: For CPU Performance.\"\"\"\n    feature_name_to_slices = {}\n    for name, (_, value_rowids, embeddings, batch_size_tensor,\n               slice_dims) in self._d.items():\n      # We do a simple fused operation that returns a list of tensors, split\n      # across the column dimension, so it returns a list of tensors of shapes\n      # [batch_size, split_dim[i]].\n      with tf.device(\"/device:CPU:0\"):\n        slices = distribution_ops.fused_reduce_sum_and_split(\n            value_rowids,\n            embeddings,\n            batch_size_tensor,\n            slice_dims,\n            name=f'ReduceSumAndSplit_{name}')\n        feature_name_to_slices[name] = slices\n    return feature_name_to_slices\n\n  def fused_reduce_then_split(self):\n    \"\"\"reduce * N -> split: For GPU Performance.\n    \n    Note that we don't fuse the split here, so that split + downstream model op can be fused \n    when pattern matched at graph optimization level.\n    \"\"\"\n    feature_name_to_slices = {}\n    if not self._d:\n      return feature_name_to_slices\n    es, ss, ds = [], [], []\n    for name, (row_splits, _, embeddings, _, slice_dims) in self._d.items():\n      ss.append(row_splits)\n      es.append(embeddings)\n      ds.append(slice_dims)\n\n    with device_utils.maybe_device_if_allowed('/device:GPU:0'):\n      out = distribution_ops.fused_reduce_and_split_gpu(ss, es, ds)\n      slice_idx = 0\n      for name, (_, _, _, _, slice_dims) in self._d.items():\n        feature_name_to_slices[name] = out[slice_idx:slice_idx +\n                                           len(slice_dims)]\n        slice_idx += len(slice_dims)\n    return feature_name_to_slices\n\n\ndef create_embedding_slices(\n    name_to_embeddings: Dict[str, tf.Tensor],\n    name_to_embedding_ids: Dict[str, tf.RaggedTensor],\n    feature_to_combiner: Dict[str, embedding_combiners.Combiner],\n    feature_to_unmerged_slice_dims: Dict[str,\n                                         List[int]]) -> Dict[str, tf.Tensor]:\n  embedding_slices = {}\n  feature_to_slices = {}\n  helper = _FeatureFactoryFusionHelper()\n\n  # Here we perform a fused reduce_sum+splitv operations.\n  for name, embeddings in name_to_embeddings.items():\n    ragged_ids = name_to_embedding_ids[name]\n    combiner = feature_to_combiner[name]\n    if isinstance(combiner, embedding_combiners.ReduceSum):\n      # This is for a general case, where splits and reduce_sums both happen.\n      # We do a simple fused operation that returns a list of tensors, split\n      # across the column dimension, so it returns a list of tensors of shapes\n      # [None, split_dim[i]], where None refers to the batch_size.\n      helper.append(\n          name,\n          ragged_ids,\n          embeddings,  # to combiner\n          feature_to_unmerged_slice_dims[name])  # to split\n    else:\n      combined_emb = combiner.combine(\n          ragged_ids,\n          embeddings,\n          name=f'{combiner.__class__.__name__}_{name}_vv')\n      with device_utils.maybe_device_if_allowed('/device:GPU:0'):\n        slices = tf.split(combined_emb,\n                          feature_to_unmerged_slice_dims[name],\n                          axis=-1)\n      feature_to_slices[name] = slices\n\n  with device_utils.maybe_device_if_allowed('/device:GPU:0'):\n    # In a long term, this optimization should be on graph-transform level at runtime.\n    if not is_exporting() and device_utils.within_placement_context_of(\"GPU\"):\n      if int(os.getenv(\"MONOLITH_GPU_FEATURE_FACTORY_FUSION_LEVEL\", '1')) == 1:\n        feature_to_slices.update(helper.fused_reduce_then_split())\n      else:\n        feature_to_slices.update(helper.reduce_and_split())\n    else:\n      if is_exporting():\n        feature_to_slices.update(helper.reduce_and_split())\n      else:\n        feature_to_slices.update(helper.fused_reduce_and_split())\n\n  # assign slice tensors to embedding table for lookup\n  for name, slices in feature_to_slices.items():\n    start = 0\n    for i, dim in enumerate(feature_to_unmerged_slice_dims[name]):\n      end = start + dim\n      embedding_slices[_FEATURE_STRAT_END_KEY.format(name, start,\n                                                     end)] = slices[i]\n      start = end\n\n  return embedding_slices\n\n\nclass FeatureFactoryFromEmbeddings(FeatureFactory):\n\n  def __init__(self, name_to_embeddings: Dict[str, tf.Tensor],\n               name_to_embedding_slices: Dict[str, tf.Tensor]):\n    super().__init__()\n    self._name_to_embeddings = name_to_embeddings\n    self._name_to_embedding_slices = name_to_embedding_slices\n\n  def create_feature_slot(self, config: FeatureSlotConfig) -> FeatureSlot:\n    # TODO(zouxuan): self._embeddings is actually never updated or used.\n    table = EmbeddingFeatureEmbTable(self._name_to_embeddings,\n                                     self._name_to_embedding_slices)\n    return FeatureSlot(table, config)\n\n\nclass EmbeddingLayoutFakeTable(FeatureEmbTable):\n\n  def embedding_lookup(self, feature_name: str, start: int,\n                       end: int) -> tf.Tensor:\n    return None\n\n\nclass EmbeddingLayoutFactory(object):\n\n  def __init__(self,\n               hash_table: 'PartitionedHashTable',\n               layout_embeddings: Dict[str, Union[tf.Tensor, List[tf.Tensor]]],\n               auxiliary_bundle: Dict[str, tf.Tensor] = None,\n               async_function_mgr: prefetch_queue.AsyncFunctionMgr = None,\n               async_push: bool = False):\n    self.hash_table = hash_table\n    self.layout_embeddings = layout_embeddings\n    self.auxiliary_bundle = auxiliary_bundle\n    self._async_function_mgr = async_function_mgr\n    self._async_push = async_push\n\n  def create_feature_slot(self, config: FeatureSlotConfig) -> FeatureSlot:\n    table = EmbeddingLayoutFakeTable()\n    return FeatureSlot(table, config)\n\n  def apply_gradients(self,\n                      grads_and_vars: Iterable[Tuple[tf.Tensor, tf.Tensor]],\n                      req_time: tf.Tensor = None,\n                      grad_scale: tf.Tensor = None):\n    return self.hash_table.apply_gradients(\n        layout_grads_and_vars=grads_and_vars,\n        global_step=tf.compat.v1.train.get_or_create_global_step(),\n        req_time=req_time or self.auxiliary_bundle.get(\"req_time\"),\n        auxiliary_bundle=self.auxiliary_bundle,\n        async_function_mgr=self._async_function_mgr,\n        async_push=self._async_push,\n        grad_scale=grad_scale,\n    )\n\n  def get_layout(self, layout: str) -> Union[tf.Tensor, List[tf.Tensor]]:\n    assert layout in self.layout_embeddings\n    return self.layout_embeddings[layout]\n\n  def flattened_layout(self) -> List[tf.Tensor]:\n    return self.hash_table.flatten_layout(self.layout_embeddings)\n"
  },
  {
    "path": "monolith/native_training/feature_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom copy import deepcopy\n\nimport tensorflow as tf\nfrom google.protobuf import text_format\n\nfrom monolith.native_training import entry\nfrom monolith.native_training import embedding_combiners\nfrom monolith.native_training import feature\nfrom monolith.native_training import learning_rate_functions\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\n\ndef _default_learning_rate_fn():\n  return learning_rate_functions.PolynomialDecay(initial_learning_rate=0.01,\n                                                 decay_steps=20,\n                                                 end_learning_rate=0.05)\n\n\nclass CollectingConfigTest(tf.test.TestCase):\n\n  def test_basic(self):\n    table = feature.DummyFeatureEmbTable(\n        batch_size=4, hashtable_config=entry.CuckooHashTableConfig())\n    seg = embedding_hash_table_pb2.EntryConfig.Segment()\n    seg.dim_size = 5\n    seg.opt_config.sgd.SetInParent()\n    table.add_feature_slice(seg)\n    table.set_feature_metadata(\"feature_name\",\n                               feature.FeatureColumn.reduce_sum())\n    placeholder = table.embedding_lookup(\"feature_name\", 0, 5)\n    self.assertAllEqual(placeholder.shape, [4, 5])\n\n  def test_basic_with_seq_features(self):\n    table = feature.DummyFeatureEmbTable(\n        batch_size=4, hashtable_config=entry.CuckooHashTableConfig())\n    seg = embedding_hash_table_pb2.EntryConfig.Segment()\n    seg.dim_size = 5\n    seg.opt_config.sgd.SetInParent()\n    table.add_feature_slice(seg)\n    table.set_feature_metadata(\"feature_name\",\n                               feature.FeatureColumn.first_n(10))\n    placeholder = table.embedding_lookup(\"feature_name\", 0, 5)\n    self.assertAllEqual(placeholder.shape, [4, 10, 5])\n\n  def test_info(self):\n    table = feature.DummyFeatureEmbTable(\n        batch_size=4, hashtable_config=entry.CuckooHashTableConfig())\n    entry1 = embedding_hash_table_pb2.EntryConfig.Segment()\n    text_format.Parse(\n        \"dim_size: 5 opt_config { adagrad { warmup_steps: 10 } } \", entry1)\n    table.add_feature_slice(deepcopy(entry1))\n    entry2 = embedding_hash_table_pb2.EntryConfig.Segment()\n    text_format.Parse(\"dim_size: 2 opt_config { sgd {} }\", entry2)\n    table.add_feature_slice(deepcopy(entry2),\n                            learning_rate_fn=_default_learning_rate_fn())\n    entry3 = embedding_hash_table_pb2.EntryConfig.Segment()\n    text_format.Parse(\"dim_size: 2 opt_config { sgd {} }\", entry3)\n    table.add_feature_slice(deepcopy(entry3),\n                            learning_rate_fn=_default_learning_rate_fn())\n    table.add_feature_slice(deepcopy(entry3))\n    table.set_feature_metadata(\"feature1\", embedding_combiners.ReduceSum())\n    table.embedding_lookup(\"feature1\", 0, 2)\n    config = table.get_table_config()\n    slices = config.slice_configs\n    self.assertEqual(len(slices), 3)\n    self.assertEqual(slices[0].segment.SerializeToString(),\n                     entry1.SerializeToString())\n    self.assertIsInstance(slices[0].learning_rate_fn,\n                          learning_rate_functions.LearningRateFunction)\n    merged_entry = embedding_hash_table_pb2.EntryConfig.Segment()\n    text_format.Parse(\"dim_size: 4 opt_config { sgd {} }\", merged_entry)\n    self.assertEqual(slices[1].segment.SerializeToString(),\n                     merged_entry.SerializeToString())\n    self.assertAllEqual(config.feature_names, [\"feature1\"])\n\n  def test_factory(self):\n    factory = feature.DummyFeatureFactory(5)\n    slot_config = feature.FeatureSlotConfig(name=\"table_name\")\n    slot = factory.create_feature_slot(slot_config)\n    s = slot.add_feature_slice(5)\n    fc1 = feature.FeatureColumnV1(slot, \"feature1\")\n    fc1.embedding_lookup(s)\n    fc2 = feature.FeatureColumnV1(slot, \"feature2\")\n    fc2.embedding_lookup(s)\n    table_name_to_config = factory.get_table_name_to_table_config()\n    self.assertTrue(\"table_name\" in table_name_to_config)\n    table_config = table_name_to_config[\"table_name\"]\n    self.assertSetEqual(set(table_config.feature_names),\n                        set([\"feature1\", \"feature2\"]))\n    self.assertEqual(table_config.slice_configs[0].segment.dim_size, 5)\n\n  def test_factory_with_seq_features(self):\n    factory = feature.DummyFeatureFactory(5)\n    slot_config = feature.FeatureSlotConfig(name=\"table_name\")\n    slot = factory.create_feature_slot(slot_config)\n    s = slot.add_feature_slice(5)\n    fc1 = feature.FeatureColumnV1(slot,\n                                  \"feature1\",\n                                  combiner=embedding_combiners.FirstN(5))\n    fc1.embedding_lookup(s)\n    fc2 = feature.FeatureColumnV1(slot,\n                                  \"feature2\",\n                                  combiner=embedding_combiners.FirstN(10))\n    fc2.embedding_lookup(s)\n    table_name_to_config = factory.get_table_name_to_table_config()\n    self.assertTrue(\"table_name\" in table_name_to_config)\n    table_config = table_name_to_config[\"table_name\"]\n    self.assertSetEqual(set(table_config.feature_names),\n                        set([\"feature1\", \"feature2\"]))\n    self.assertDictEqual(table_config.feature_to_combiners, {\n        \"feature1\": fc1.combiner,\n        \"feature2\": fc2.combiner\n    })\n    self.assertEqual(table_config.slice_configs[0].segment.dim_size, 5)\n\n  def test_factory_with_slot_occurrence_threshold(self):\n    factory = feature.DummyFeatureFactory(5)\n    slot_config_1 = feature.FeatureSlotConfig(name=\"table_name_1\",\n                                              slot_id=1,\n                                              occurrence_threshold=3)\n    slot_1 = factory.create_feature_slot(slot_config_1)\n    s_1 = slot_1.add_feature_slice(5)\n    fc1 = feature.FeatureColumnV1(slot_1, \"feature1\")\n    fc1.embedding_lookup(s_1)\n\n    slot_config_2 = feature.FeatureSlotConfig(name=\"table_name_2\",\n                                              slot_id=2,\n                                              occurrence_threshold=7)\n    slot_2 = factory.create_feature_slot(slot_config_2)\n    s_2 = slot_2.add_feature_slice(5)\n    fc2 = feature.FeatureColumnV1(slot_2, \"feature2\")\n    fc2.embedding_lookup(s_2)\n    self.assertEqual(factory.slot_to_occurrence_threshold[1], 3)\n    self.assertEqual(factory.slot_to_occurrence_threshold[2], 7)\n\n  def test_factory_with_applying_gradients(self):\n    factory = feature.DummyFeatureFactory(5)\n    slot_config = feature.FeatureSlotConfig(name=\"table\")\n    slot = factory.create_feature_slot(slot_config)\n    s = slot.add_feature_slice(1)\n    fc = feature.FeatureColumnV1(slot, \"feature1\")\n    concat_embedding = fc.get_all_embeddings_concat()\n    factory.apply_gradients([(tf.constant([[0.0] * 2] * 5), concat_embedding)])\n\n  def test_bias(self):\n    factory = feature.DummyFeatureFactory(5)\n    slot_config = feature.FeatureSlotConfig(name=\"table\", has_bias=True)\n    slot = factory.create_feature_slot(slot_config)\n    fc = feature.FeatureColumnV1(slot, \"feature1\")\n    fc.get_bias()\n\n\nclass EmbeddingTest(tf.test.TestCase):\n\n  def test_factory(self):\n    embeddings = {\"feature1\": tf.constant([[1, 4], [2, 3]], dtype=tf.float32)}\n    embedding_ids = {\n        \"feature1\": tf.RaggedTensor.from_row_splits([1, 2], [0, 1, 2])\n    }\n    slices = feature.create_embedding_slices(\n        embeddings, embedding_ids,\n        {\"feature1\": embedding_combiners.ReduceSum()}, {\"feature1\": [1, 1]})\n    factory = feature.FeatureFactoryFromEmbeddings(embeddings, slices)\n    slot_config = feature.FeatureSlotConfig(name=\"table_name\")\n    slot = factory.create_feature_slot(slot_config)\n    s = slot.add_feature_slice(1)\n    fc = feature.FeatureColumnV1(slot, \"feature1\")\n    tensor = fc.embedding_lookup(s)\n    with self.session() as sess:\n      tensor = sess.run(tensor)\n    self.assertAllEqual(tensor, [[1], [2]])\n\n  def test_factory_with_seq_features(self):\n    embeddings = {\n        \"feature1\":\n            tf.constant([[1, 2], [3, 4], [5, 6], [7, 8]], dtype=tf.float32)\n    }\n    embedding_ids = {\n        \"feature1\": tf.RaggedTensor.from_row_splits([1, 2, 3, 4], [0, 2, 4])\n    }\n    slices = feature.create_embedding_slices(\n        embeddings, embedding_ids, {\"feature1\": embedding_combiners.FirstN(2)},\n        {\"feature1\": [1, 1]})\n    factory = feature.FeatureFactoryFromEmbeddings(embeddings, slices)\n    slot_config = feature.FeatureSlotConfig(name=\"table_name\")\n    slot = factory.create_feature_slot(slot_config)\n    s = slot.add_feature_slice(1)\n    fc = feature.FeatureColumnV1(slot, \"feature1\")\n    tensor = fc.embedding_lookup(s)\n    with self.session() as sess:\n      tensor = sess.run(tensor)\n    self.assertAllEqual(tensor, [[[1], [3]], [[5], [7]]])\n\n  def test_fused_factory(self):\n    embeddings = {\n        \"feature1\": tf.constant([[1, 2], [2, 3], [3, 5]], dtype=tf.float32)\n    }\n    embedding_ids = {\n        \"feature1\": tf.RaggedTensor.from_row_splits([1, 2, 3], [0, 1, 1, 3])\n    }\n    slices = feature.create_embedding_slices(\n        embeddings, embedding_ids,\n        {\"feature1\": embedding_combiners.ReduceSum()}, {\"feature1\": [1, 1]})\n    factory = feature.FeatureFactoryFromEmbeddings(embeddings, slices)\n    slot_config = feature.FeatureSlotConfig(name=\"table_name\")\n    slot = factory.create_feature_slot(slot_config)\n    s = slot.add_feature_slice(1)\n    s2 = slot.add_feature_slice(1)\n    fc = feature.FeatureColumnV1(slot, \"feature1\")\n    tensor = fc.embedding_lookup(s)\n    with self.session() as sess:\n      tensor = sess.run(tensor)\n    self.assertAllClose(tensor, [[1], [0], [5]])\n    tensor = fc.embedding_lookup(s2)\n    with self.session() as sess:\n      tensor = sess.run(tensor)\n    self.assertAllClose(tensor, [[2], [0], [8]])\n\n  def test_fused_factory_with_seq_features_larger_than_max_seq_length(self):\n    # For rows with bigger number of embeddings than max_seq_length,\n    # discard the extra embedding elements.\n    embeddings = {\n        \"feature1\":\n            tf.constant([[1, 2], [2, 3], [3, 5], [10, 11]], dtype=tf.float32)\n    }\n    embedding_ids = {\n        \"feature1\": tf.RaggedTensor.from_row_splits([1, 2, 3, 4], [0, 1, 1, 4])\n    }\n    ragged_ids = embedding_ids[\"feature1\"]\n    slices = feature.create_embedding_slices(\n        embeddings, embedding_ids, {\"feature1\": embedding_combiners.FirstN(2)},\n        {\"feature1\": [1, 1]})\n    factory = feature.FeatureFactoryFromEmbeddings(embeddings, slices)\n    slot_config = feature.FeatureSlotConfig(name=\"table_name\")\n    slot = factory.create_feature_slot(slot_config)\n    s = slot.add_feature_slice(1)\n    s2 = slot.add_feature_slice(1)\n    fc = feature.FeatureColumnV1(slot, \"feature1\")\n    tensor = fc.embedding_lookup(s)\n    with self.session() as sess:\n      tensor = sess.run(tensor)\n    self.assertAllEqual(tensor, [[[1], [0]], [[0], [0]], [[2], [3]]])\n    tensor = fc.embedding_lookup(s2)\n    with self.session() as sess:\n      tensor = sess.run(tensor)\n    self.assertAllEqual(tensor, [[[2], [0]], [[0], [0]], [[3], [5]]])\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/feature_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom enum import Enum\nimport os\nfrom typing import Iterable, Dict\n\nfrom absl import logging\n\nimport tensorflow as tf\nfrom tensorflow.python.training import training_util\n\nfrom monolith.native_training import clip_ops\nfrom monolith.native_training.distribution_ops import gen_distribution_ops\nfrom monolith.native_training import device_utils\nfrom monolith.native_training import feature\nfrom monolith.native_training.native_task import NativeContext\n\nenable_hvd = os.getenv(\"MONOLITH_WITH_HOROVOD\")\nenable_bps = int(os.getenv(\"MONOLITH_WITH_BYTEPS\", '0'))\nenable_bps_allreduce = int(os.getenv(\"MONOLITH_WITH_BYTEPS_ALLREDUCE\", '1'))\nenable_allreduce_fusion = str(\n    os.getenv(\"MONOLITH_WITH_ALLREDUCE_FUSION\", 'none'))\nenable_allreduce_fp16 = int(os.getenv(\"MONOLITH_WITH_ALLREDUCE_FP16\",\n                                      '0'))  # for hvd\nskip_allreduce = int(os.getenv(\"MONOLITH_SKIP_ALLREDUCE\", '0'))\n# enable (limited) fusion functionality for byteccl where bias tensors are fused into one\n# tensor before performing allreduce.\n\nif enable_hvd != None:\n  import horovod.tensorflow as hvd\n  from horovod.tensorflow.compression import FP16Compressor, NoneCompressor\n\ncontrol_ops = []\ndense_opt_ops = []\n\ndef allreduce_cond(grads, scale = 1):\n  if enable_bps and enable_bps_allreduce:\n    import byteps.tensorflow as bps\n    from byteps.tensorflow.compression import FP16Compressor as BPSFP16Compressor, NoneCompressor as BPSNoneCompressor\n    compression = BPSFP16Compressor if enable_allreduce_fp16 else BPSNoneCompressor\n  else:\n    compression = FP16Compressor if enable_allreduce_fp16 else NoneCompressor\n\n  grads_wo_none = [grad for grad in grads if grad is not None]\n  num_grads = len(grads)\n  results = [None for _ in range(num_grads)]\n  if len(grads_wo_none) == 0:\n    return grads\n\n  def map_to_output(reduced):\n    r_idx = 0\n    for i in range(num_grads):\n      if grads[i] is not None:\n        results[i] = reduced[r_idx]\n        r_idx += 1\n    assert r_idx == len(reduced), \"Something is wrong\"\n    return results\n\n  global control_ops\n  if enable_allreduce_fusion == 'one':\n    # note: one allreduce fusion does not yet support CPU\n    # note: concat -> allreduce -> split is noticeably faster than hvd.grouped_allreduce\n    grads_fused = gen_distribution_ops.monolith_aligned_flat_concat(grads_wo_none, scale)\n    control_ops = [grads_fused]\n    if enable_bps and enable_bps_allreduce:\n      grads_fused_avg = bps.push_pull(grads_fused, average=True, compression=compression, name=\"bps_ar_fuse_one\")\n    else:\n      grads_fused_avg = hvd.allreduce(grads_fused, op=hvd.Average, compression=compression, name=\"hvd_ar_fuse_one\")\n    return map_to_output(gen_distribution_ops.monolith_aligned_flat_split(grads_wo_none, grads_fused_avg))\n  elif enable_allreduce_fusion == \"grouped\":\n    assert not enable_bps or not enable_bps_allreduce\n    return map_to_output(\n      hvd.grouped_allreduce([grad * scale for grad in grads_wo_none], op=hvd.Average, compression=compression))\n  elif enable_allreduce_fusion == 'multi':\n    raise RuntimeError(\"Support for multi is dropped. Please use 'one' as the fusion strategy\")\n  else:\n    logging.info('Enabled allreduce without fusion using Average Op!')\n    if enable_bps and enable_bps_allreduce:\n      return [\n        bps.push_pull(grad * scale, average=True, compression=compression)\n        if grad is not None else grad for grad in grads\n      ]\n    else:\n      return [\n        hvd.allreduce(grad * scale, op=hvd.Average, compression=compression) \n        if grad is not None else grad for grad in grads\n      ]\n\n\nclass GradClipType(Enum):\n  ClipByNorm = 1\n  ClipByGlobalNorm = 2\n  ClipByValue = 3\n  ClipByDenseAndSparse = 4\n  NoClip = 5\n\n\ndef _gen_norm_warmup(clip_norm: float, global_step_var: tf.Tensor,\n                     warmup_step: int):\n  if not warmup_step:\n    return clip_norm\n  return tf.cond(\n      tf.less(global_step_var, warmup_step), lambda: tf.compat.v1.div(\n          tf.cast(global_step_var, dtype=tf.float32), float(warmup_step)),\n      lambda: 1.0) * clip_norm\n\n\ndef apply_gradients_with_var_optimizer(\n    ctx: NativeContext,\n    feature_columns: Iterable[feature.FeatureColumnV1],\n    var_opt: tf.compat.v1.train.Optimizer,\n    loss: tf.Tensor,\n    clip_type: GradClipType = GradClipType.ClipByGlobalNorm,\n    clip_norm: float = None,\n    global_step=None,\n    grads_and_vars_summary: bool = False,\n    use_allreduce: bool = False,\n    ue_gradient_check: bool = False,\n    ue_fc_names: list = [],\n    ue_euclidean_norm_threshold: float = 0.0,\n    dense_weight_decay: float = 0.0,\n    features: Dict[str, tf.Tensor] = {},\n    sparse_clip_norm: float = None,\n    sparse_norm_warmup_steps: int = None,\n    dense_reduce_mean: bool = False,\n    batch_size: int = 1,\n    is_fused_layout: bool = False) -> tf.Operation:\n  \"\"\"\n  A helper function that applies gradient to both dense params and embedding params.\n  Args:\n    clip_type - clip type\n    clip_norm - norm will be used by clip\n    global_step - is not None, will be added by 1.\n    grads_and_vars_summary - when True, will print summary of grads and vars\n    dense_weight_decay - dense weight decay, l2 norm\n  \"\"\"\n  with device_utils.maybe_device_if_allowed('/device:GPU:0'):\n    assert isinstance(var_opt, tf.compat.v1.train.Optimizer)\n    feature_columns = list(feature_columns)\n    if is_fused_layout:\n      layout_factory: feature.EmbeddingLayoutFactory = ctx.layout_factory\n      all_embeddings = layout_factory.flattened_layout()\n    else:\n      all_embeddings = [fc.get_all_embeddings_concat() for fc in feature_columns]\n    variables = tf.compat.v1.trainable_variables()\n    grads_and_vars = var_opt.compute_gradients(loss,\n                                               variables + all_embeddings,\n                                               colocate_gradients_with_ops=True)\n\n    # Some variables are created but unused and we need to filter them out.\n    if is_fused_layout:\n      grads_and_vars_tmp = grads_and_vars[:len(variables)]\n      for gv in grads_and_vars[len(variables):]:\n        grads_and_vars_tmp.append((gv[0] if gv[0] is not None else tf.zeros_like(gv[1]), gv[1]))\n      grads_and_vars = grads_and_vars_tmp\n\n    dense_gvs = [gv for gv in grads_and_vars[:len(variables)] if gv[0] is not None]\n    sparse_gvs = [gv for gv in grads_and_vars[len(variables):] if gv[0] is not None]\n    if is_fused_layout:\n      feature_columns = []\n    else:\n      feature_columns = [\n          fc for fc, gv in zip(feature_columns, grads_and_vars[len(variables):]) if gv[0] is not None\n      ]\n    \n    variables = [gv[1] for gv in dense_gvs]\n    all_embeddings = [gv[1] for gv in sparse_gvs]\n    grads_and_vars = dense_gvs + sparse_gvs\n    grads = [grad_and_var[0] for grad_and_var in grads_and_vars]\n    # UE conditional gradient check\n    if ue_gradient_check:\n      grads = []\n      for grad_and_var in grads_and_vars:\n        found = False\n        for fc_name in ue_fc_names:\n          if fc_name in grad_and_var[1].name or 'uue' in grad_and_var[1].name:\n            grads.append(\n                tf.where(\n                    tf.norm(features[fc_name]) > ue_euclidean_norm_threshold,\n                    grad_and_var[0], tf.zeros_like(grad_and_var[0])))\n            logging.info(\"UE Vars: {}\".format(grad_and_var[1].name))\n            found = True\n            break\n        if not found:\n          grads.append(grad_and_var[0])\n\n    # TODO(zouxuan): this is a quick workaround to fix the empty grads issue.\n    if len(grads) == 0:\n      return tf.no_op()\n\n    dense_grads = grads[:len(variables)]\n    sparse_grads = grads[len(variables):]\n\n    if dense_reduce_mean:\n      dense_grads = [g / batch_size for g in dense_grads]\n\n    global_dense_norm = None\n    global_sparse_norm = None\n    norm_fn = clip_ops._global_norm if device_utils.within_placement_context_of(\n      \"GPU\") else tf.linalg.global_norm\n    if clip_type == GradClipType.ClipByGlobalNorm and clip_norm is not None:\n      global_dense_norm = norm_fn(grads)\n      global_sparse_norm = global_dense_norm # use the same norm for sparse and dense\n      sparse_clip_norm = sparse_clip_norm or clip_norm\n      if sparse_norm_warmup_steps is not None:\n        sparse_clip_norm = _gen_norm_warmup(sparse_clip_norm, global_step,\n                                            sparse_norm_warmup_steps)\n        logging.info('sparse_norm_warmup_steps: %s', sparse_norm_warmup_steps)\n      with tf.device('/device:CPU:0'):\n        tf.compat.v1.summary.scalar(\"global_gradient_norm\", global_dense_norm)\n    elif clip_type == GradClipType.ClipByValue and clip_norm is not None:\n      clipped_grads = [\n          tf.clip_by_value(g,\n                            clip_value_min=-clip_norm,\n                            clip_value_max=clip_norm) for g in grads\n      ]\n    elif clip_type == GradClipType.ClipByNorm and clip_norm is not None:\n      clipped_grads = [tf.clip_by_norm(g, clip_norm) for g in grads]\n    elif clip_type == GradClipType.ClipByDenseAndSparse:\n      global_dense_norm = norm_fn(dense_grads)\n      if sparse_clip_norm is not None:\n        global_sparse_norm = norm_fn(sparse_grads)\n      with tf.device('/device:CPU:0'):\n        tf.compat.v1.summary.scalar(\"global_gradient_norm/dense\",\n                                    global_dense_norm)\n        if global_sparse_norm is not None:\n          tf.compat.v1.summary.scalar(\"global_gradient_norm/sparse\",\n                                      global_sparse_norm)\n    else:\n      clipped_grads = grads\n    \n    if skip_allreduce:\n      use_allreduce = False      \n\n    # Conditionally perform clip by global norm.\n    # If we're using synchronous (allreduce=True) distributed GPU training,\n    # we defer clip and only calculate a scale factor. The scaling is fused \n    # with later concat/gather kernels for better performance\n    def cond_defer_clip(norm, clip_norm, grads):\n      defer_clip = device_utils.within_placement_context_of(\"GPU\") and \\\n        use_allreduce and not grads_and_vars_summary and not is_fused_layout\n      scale = 1\n      if norm is not None:\n        if not defer_clip:\n          grads, _ = clip_ops.clip_by_global_norm(grads, clip_norm, use_norm=norm)\n        else:\n          scale = tf.minimum(clip_norm / norm, 1)\n      return grads, scale\n\n    if clip_type in (GradClipType.ClipByGlobalNorm, GradClipType.ClipByDenseAndSparse):\n      dense_clipped_grads, dense_scale = cond_defer_clip(global_dense_norm, clip_norm, dense_grads)\n      sparse_clipped_grads, sparse_scale = cond_defer_clip(global_sparse_norm, sparse_clip_norm, sparse_grads)\n    else:\n      dense_scale = 1\n      sparse_scale = 1\n      dense_clipped_grads = clipped_grads[:len(variables)]\n      sparse_clipped_grads = clipped_grads[len(variables):]\n\n    if grads_and_vars_summary:\n      with tf.device(\"/device:CPU:0\"):\n        if len(dense_clipped_grads) > 0:\n          tf.compat.v1.summary.histogram(\n              \"variable_gradient\",\n              tf.concat(\n                  [tf.reshape(grad, [-1]) for grad in dense_clipped_grads], 0))\n          dense_grad_sizes = []\n          for grad, var in zip(dense_clipped_grads, variables):\n            summary_var_name = var.name.replace(\":\", \"_\")\n            tf.compat.v1.summary.scalar(\n                \"trainable_variable_norm/{}\".format(summary_var_name),\n                tf.norm(var))\n            tf.compat.v1.summary.histogram(\n                \"trainable_variable/{}\".format(summary_var_name), var)\n            tf.compat.v1.summary.scalar(\n                \"gradient_norm/{}\".format(summary_var_name), tf.norm(grad))\n            tf.compat.v1.summary.histogram(\n                \"gradient/{}\".format(summary_var_name), grad)\n            dense_grad_sizes.append(tf.size(grad))\n          tf.compat.v1.summary.histogram(\"dense_grad_sizes\", dense_grad_sizes)\n          tf.compat.v1.summary.scalar(\"dense_grad_total_size\",\n                                      tf.reduce_sum(dense_grad_sizes))\n          tf.compat.v1.summary.scalar(\"dense_grad_total_num\",\n                                      len(dense_grad_sizes))\n\n        for i, fc in enumerate(feature_columns):\n          tf.compat.v1.summary.histogram(\"{}_gradient\".format(fc.feature_name),\n                                         sparse_clipped_grads[i])\n\n    logging.info('use_allreduce: %s', use_allreduce)\n    dense_clipped_grads = allreduce_cond(\n        dense_clipped_grads, dense_scale\n    ) if use_allreduce and enable_hvd else dense_clipped_grads\n\n    if dense_weight_decay and variables:\n      dense_clipped_grads = [\n          g + dense_weight_decay * v\n          for g, v in zip(dense_clipped_grads, variables)\n      ]\n    logging.info('dense_weight_decay: %s', dense_weight_decay)\n    train_ops = []\n    grads_and_vars_without_optimizer = []\n    if variables:\n      global dense_opt_ops\n      for i, var in enumerate(variables):\n        if hasattr(var, 'optimizer') and var.optimizer:\n          train_ops.append(\n              ctx.add_async_function(var.optimizer.apply_gradients,\n                                     ([(dense_clipped_grads[i], var)],)))\n          logging.info(\"var {} uses a custom optimizer: {}\".format(\n              var.name, var.optimizer))\n        else:\n          grads_and_vars_without_optimizer.append((dense_clipped_grads[i], var))\n      train_ops.append(\n          ctx.add_async_function(var_opt.apply_gradients,\n                                 (grads_and_vars_without_optimizer,)))\n      dense_opt_ops = train_ops.copy()\n\n    with tf.device('/device:CPU:0'):\n      train_ops.append(\n          ctx.apply_embedding_gradients(\n              list(zip(sparse_clipped_grads, all_embeddings)), sparse_scale))\n\n    if global_step is not None:\n      # The control dependency here ensures that\n      # when the StepCounterHook tries to get the global_step\n      # from the training session at the same time of training,\n      # the read_value should be consistent (before assign_add).\n      # Also makes sure that the global step is incremented after the optimize ops, \n      # since embedding optimizer requires this global step as input\n      with tf.control_dependencies(\n          train_ops + [training_util._get_or_create_global_step_read()]):\n        train_ops.append(\n            ctx.add_async_function(tf.compat.v1.assign_add, (global_step, 1)))\n    return tf.group(*train_ops)\n\n\ndef apply_gradients(ctx: NativeContext,\n                    var_opt: tf.compat.v1.train.Optimizer,\n                    loss: tf.Tensor,\n                    clip_type: GradClipType = GradClipType.ClipByGlobalNorm,\n                    clip_norm: float = None,\n                    dense_weight_decay: float = 0.0,\n                    global_step=None,\n                    use_allreduce: bool = False):\n  layout_factory: feature.EmbeddingLayoutFactory = ctx.layout_factory\n  variables = tf.compat.v1.trainable_variables()\n  layout_embeddings = layout_factory.flattened_layout()\n  grads_and_vars = var_opt.compute_gradients(loss,\n                                             variables + layout_embeddings,\n                                             colocate_gradients_with_ops=True)\n  # clip grads\n  flag = False\n  for g, v in grads_and_vars:\n    if g is None:\n      flag = True\n      logging.info(f'grad of {v} is None, maybe it not used in the graph')\n  if flag:\n    grads_and_vars = [(g, v) for (g, v) in grads_and_vars if g is not None]\n    variables = [v for (g, v) in grads_and_vars if v in variables]\n    layout_embeddings = [\n        v for (g, v) in grads_and_vars if v in layout_embeddings\n    ]\n    assert len(grads_and_vars) == len(variables) + len(layout_embeddings)\n\n  grads = [g for (g, _) in grads_and_vars]\n  if grads and clip_norm is not None and clip_norm > 0:\n    if clip_type == GradClipType.ClipByGlobalNorm:\n      clipped_grads, global_g_norm = clip_ops.clip_by_global_norm(\n          grads, clip_norm, use_norm=tf.linalg.global_norm(grads))\n      logging.info('clip_by_global_norm: %s', clip_norm)\n      with tf.device('/device:CPU:0'):\n        tf.compat.v1.summary.scalar(\"global_gradient_norm\", global_g_norm)\n    elif clip_type == GradClipType.ClipByNorm:\n      clipped_grads = [tf.clip_by_norm(g, clip_norm) for g in grads]\n    else:\n      raise Exception(f\"{clip_type} is not supported yet!\")\n  else:\n    clipped_grads = grads\n\n  train_ops = []\n\n  # dense apply_gradients\n  if variables:\n    dense_clipped_grads = clipped_grads[:len(variables)]\n    if use_allreduce and enable_hvd:\n      dense_clipped_grads = allreduce_cond(\n        dense_clipped_grads)\n\n    if dense_weight_decay > 0:\n      grads_and_vars = [(g + dense_weight_decay * v, v)\n                        for g, v in zip(dense_clipped_grads, variables)]\n    else:\n      grads_and_vars = list(zip(dense_clipped_grads, variables))\n    train_ops.append(\n        var_opt.apply_gradients(grads_and_vars, global_step=global_step))\n  else:\n    with tf.control_dependencies(\n        [training_util._get_or_create_global_step_read()]):\n      train_ops.append(tf.compat.v1.assign_add(global_step, 1))\n\n  # sparse apply_gradients\n  if layout_embeddings:\n    sparse_clipped_grads = clipped_grads[len(variables):]\n    grads_and_vars = list(zip(sparse_clipped_grads, layout_embeddings))\n    train_ops.append(ctx.apply_embedding_gradients(grads_and_vars))\n\n  return tf.group(*train_ops)\n"
  },
  {
    "path": "monolith/native_training/feature_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom unittest import mock\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import test_util\nimport os\nos.environ['MONOLITH_WITH_ALLREDUCE_FUSION'] = 'one'\n\nfrom monolith.native_training import embedding_combiners\nfrom monolith.native_training import feature, feature_utils\nfrom monolith.native_training.native_task import NativeContext\nfrom monolith.native_training import prefetch_queue\n\n\ndef _setup_test_embedding(is_async=False):\n  \"\"\"Will create embedding with 3,1. And returns a emb with size 3.\"\"\"\n  emb_var = tf.Variable([[1.0, 1.0, 1.0, 1.0]], trainable=False)\n  emb = {\"feature1\": emb_var}\n  emb_id = tf.RaggedTensor.from_row_splits([111], [0, 1])\n  slices = feature.create_embedding_slices(\n      emb, {\"feature1\": emb_id}, {\"feature1\": embedding_combiners.ReduceSum()},\n      {\"feature1\": [3, 1]})\n  feature_factory = feature.FeatureFactoryFromEmbeddings(emb, slices)\n\n  def apply_emb_gradients(grads_and_vars, scale=1):\n    return tf.group([var.assign_sub(grad * scale) for grad, var in grads_and_vars])\n\n  feature_factory.apply_gradients = mock.MagicMock(\n      side_effect=apply_emb_gradients)\n  ctx = NativeContext(\n      feature_factory=feature_factory,\n      async_function_mgr=prefetch_queue.AsyncFunctionMgr(is_async))\n  slot = ctx.create_feature_slot(feature.FeatureSlotConfig(name=\"Slot\"))\n  s = slot.add_feature_slice(3)\n  fc = feature.FeatureColumnV1(slot, \"feature1\")\n  emb = fc.embedding_lookup(s)\n  return ctx, fc, emb_var, emb\n\n\nclass FeatureUtilsTest(tf.test.TestCase):\n\n  def test_apply_gradients_with_dense_optimizer(self):\n    ctx, fc, emb_var, emb = _setup_test_embedding()\n    emb_loss = tf.reduce_sum(tf.reduce_sum(emb))\n    var = tf.Variable(1.0)\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    loss = emb_loss + var\n    opt = tf.compat.v1.train.GradientDescentOptimizer(1.0)\n    # norm is 2, will be clipped by 1\n    op = feature_utils.apply_gradients_with_var_optimizer(\n        ctx, [fc],\n        opt,\n        loss,\n        clip_norm=1.0,\n        global_step=global_step,\n        grads_and_vars_summary=True)\n\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(op)\n      self.assertAllEqual(sess.run(var), 0.5)\n      self.assertAllEqual(sess.run(emb_var), [[0.5, 0.5, 0.5, 1.0]])\n      self.assertAllEqual(sess.run(global_step), 1)\n\n  @test_util.run_gpu_only\n  def test_apply_gradients_with_dense_optimizer_gpu(self):\n    # this test tests the fusion of clip_by_global_norm with later kernels\n    with test_util.use_gpu():\n      ctx, fc, emb_var, emb = _setup_test_embedding()\n      emb_loss = tf.reduce_sum(tf.reduce_sum(emb))\n      var = tf.Variable(1.0)\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      loss = emb_loss + var\n      opt = tf.compat.v1.train.GradientDescentOptimizer(1.0)\n      # norm is 2, will be clipped by 1\n      op = feature_utils.apply_gradients_with_var_optimizer(\n          ctx, [fc],\n          opt,\n          loss,\n          clip_norm=1.0,\n          global_step=global_step,\n          grads_and_vars_summary=False,\n          use_allreduce=True)\n\n      with self.session() as sess:\n        sess.run(tf.compat.v1.global_variables_initializer())\n        sess.run(op)\n        self.assertAllEqual(sess.run(var), 0.5)\n        self.assertAllEqual(sess.run(emb_var), [[0.5, 0.5, 0.5, 1.0]])\n        self.assertAllEqual(sess.run(global_step), 1)\n\n  def test_apply_gradients_with_dense_optimizer_post_push(self):\n    ctx, fc, emb_var, emb = _setup_test_embedding(is_async=True)\n    emb_loss = tf.reduce_sum(tf.reduce_sum(emb))\n    var = tf.Variable(1.0)\n    opt = tf.compat.v1.train.GradientDescentOptimizer(1.0)\n    loss = emb_loss + var\n    op = feature_utils.apply_gradients_with_var_optimizer(ctx, [fc], opt, loss)\n\n    with tf.compat.v1.train.SingularMonitoredSession(\n        hooks=ctx.async_function_mgr.hooks) as sess:\n      sess.run(op)\n      sess.run(op)\n      sess.run(op)\n      # Since it is async pushed, the push should happen twice.\n      var_value, emb_var_value = sess.run([var, emb_var])\n      # Run op three times will trigger two optimization\n      self.assertAllEqual(var_value, -1.0)\n      # But emb is not affected. Optimized by 3 times.\n      self.assertAllEqual(emb_var_value, [[-2.0, -2.0, -2.0, 1.0]])\n\n  def test_apply_gradients_without_dense_optimizer(self):\n    ctx, fc, emb_var, emb = _setup_test_embedding()\n\n    emb_loss = tf.reduce_sum(tf.reduce_sum(emb))\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    loss = emb_loss\n    opt = tf.compat.v1.train.GradientDescentOptimizer(1.0)\n    op = feature_utils.apply_gradients_with_var_optimizer(\n        ctx, [fc], opt, loss, global_step=global_step)\n\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(op)\n      self.assertAllEqual(sess.run(emb_var), [[0.0, 0.0, 0.0, 1.0]])\n      self.assertAllEqual(sess.run(global_step), 1)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/file_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\nfile_ops = gen_monolith_ops\n\n\nclass WritableFile:\n  \"\"\"A gfile wrapper used in the graph execution.\"\"\"\n\n  def __init__(self, filename):\n    self._handle = file_ops.monolith_writable_file(filename)\n\n  def append(self, content):\n    \"\"\"Append the content into the file.\n    Args:\n      content - a 0-D string tensor.\n    \"\"\"\n    return file_ops.monolith_writable_file_append(self._handle, content)\n\n  def append_entry_dump(self, item_id, bias, embedding):\n    return file_ops.monolith_entry_dump_file_append(self._handle, item_id, bias, embedding)\n\n  def close(self):\n    return file_ops.monolith_writable_file_close(self._handle)\n\n\nclass FileCloseHook(tf.estimator.SessionRunHook):\n  \"\"\"A hook that will close WritableFiles at the end of session.\"\"\"\n\n  def __init__(self, files):\n    assert isinstance(files, list)\n    self._files = files\n    self._close_ops = [f.close() for f in files]\n\n  def end(self, session):\n    session.run(self._close_ops)\n"
  },
  {
    "path": "monolith/native_training/file_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nimport tensorflow as tf\n\nfrom monolith.native_training import file_ops\n\n\nclass WritableFileTest(tf.test.TestCase):\n\n  def test_basic(self):\n    filename = os.environ[\"TEST_TMPDIR\"] + \"/test_basic/test_name\"\n\n    times = 1000\n\n    @tf.function\n    def write():\n      f = file_ops.WritableFile(filename)\n      for i in tf.range(times):\n        f.append(\"1234\")\n      f.close()\n\n    self.evaluate(write())\n\n    with tf.io.gfile.GFile(filename) as f:\n      self.assertAllEqual(f.read(), \"1234\" * times)\n\n  def test_hook(self):\n    filename = os.environ[\"TEST_TMPDIR\"] + \"/test_hook/test_name\"\n    f = file_ops.WritableFile(filename)\n    write_op = f.append(\"1234\")\n\n    with tf.compat.v1.train.MonitoredSession(\n        hooks=[file_ops.FileCloseHook([f])]) as sess:\n      sess.run(write_op)\n\n    with tf.io.gfile.GFile(filename) as f:\n      self.assertAllEqual(f.read(), \"1234\")\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/fountain/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\ncc_library(\n    name = \"fountain_dataset_ops\",\n    visibility = [\"//visibility:public\"],\n)\n\npy_library(\n    name = \"fountain_lib\",\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "monolith/native_training/fountain/README.md",
    "content": "Dummy implementation of fountain."
  },
  {
    "path": "monolith/native_training/fused_embedding_to_layout_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom contextlib import nullcontext\nimport logging\nimport string\nimport numpy as np\n\nnp.random.seed(2)\nfrom random import randint\nimport tensorflow as tf\n# tf.compat.v1.disable_eager_execution()\ntf.compat.v1.disable_v2_behavior()\n\nfrom collections import defaultdict\nfrom tensorflow.python.framework import test_util\nfrom monolith.native_training import distribution_ops\nfrom idl.matrix.proto.example_pb2 import ExampleBatch, Example, FeatureListType, \\\n  SliceConfig, PoolingType, OutType, OutConfig, FeatureConfig, FeatureConfigs, TensorShape\nfrom monolith.native_training.data.parsers import parse_instances, parse_examples, parse_example_batch, \\\n    sharding_sparse_fids, get_default_parser_ctx, ParserCtx\n\nSHARD_BIT = 0x80000000\n\n\ndef infer_shape(out_conf: OutConfig,\n                out_type: OutType,\n                max_sequence_length: int = 0):\n  out_conf.out_type = out_type\n  if out_type == OutType.NONE:\n    for sc in out_conf.slice_configs:\n      shape = out_conf.shape.add()\n      if max_sequence_length > 0:\n        shape.dims.extend([-1, max_sequence_length, sc.end - sc.start])\n      else:\n        shape.dims.extend([-1, sc.end - sc.start])\n  elif out_type == OutType.CONCAT:\n    shape = out_conf.shape.add()\n    last_dim = 0\n    for sc in out_conf.slice_configs:\n      last_dim += sc.end - sc.start\n    if max_sequence_length > 0:\n      shape.dims.extend([-1, max_sequence_length, last_dim])\n    else:\n      shape.dims.extend([-1, last_dim])\n  elif out_type == OutType.STACK:\n    shape = out_conf.shape.add()\n    last_dim = None\n    for sc in out_conf.slice_configs:\n      if last_dim is None:\n        last_dim = sc.end - sc.start\n      else:\n        assert last_dim == sc.end - sc.start\n    if max_sequence_length > 0:\n      shape.dims.extend(\n          [-1, len(out_conf.slice_configs), max_sequence_length, last_dim])\n    else:\n      shape.dims.extend([-1, len(out_conf.slice_configs), last_dim])\n  elif out_type == OutType.ADDN:\n    shape = out_conf.shape.add()\n    last_dim = None\n    for sc in out_conf.slice_configs:\n      if last_dim is None:\n        last_dim = sc.end - sc.start\n      else:\n        assert last_dim == sc.end - sc.start\n\n    if max_sequence_length > 0:\n      shape.dims.extend([-1, max_sequence_length, last_dim])\n    else:\n      shape.dims.extend([-1, last_dim])\n  else:\n    raise ValueError('out_type error')\n\n\ndef get_key(ln: str, sc: SliceConfig) -> str:\n  return f\"{ln}_{sc.feature_name}_{sc.start}_{sc.end}\"\n\n\ndef pooling(pooling_type, in_data, max_length):\n  if max_length and len(in_data) > max_length:\n    data = in_data[0:max_length]\n  else:\n    data = in_data\n  if pooling_type == PoolingType.SUM:\n    result = np.zeros_like(data[0])\n    for d in data:\n      result += d\n    return result\n  if pooling_type == PoolingType.MEAN:\n    result = np.zeros_like(data[0])\n    for d in data:\n      result += d\n    result /= len(data)\n    return result\n  else:\n    last_dim = int(data[0].shape[-1])\n    result = np.zeros(shape=(max_length, last_dim), dtype=np.float32)\n    for i, d in enumerate(data):\n      result[i, :] = d\n      if i < max_length:\n        result[i, :] = d\n      else:\n        break\n    return result\n\n\nclass FusedEmbeddingToLayoutTest(tf.test.TestCase):\n\n  def get_pre_output_offset(self, shard, f_cfg):\n    return f_cfg[\"pre_output_index\"] + shard * f_cfg[\n        \"table_feature_count\"] + f_cfg[\"feature_in_table_index\"]\n\n  def get_feature_cfg(self, raw_feature_cfgs, ps_num):\n    feature_cfg = defaultdict(dict)\n    table_cfg = defaultdict(dict)\n    for feature_name, cfg in raw_feature_cfgs.feature_configs.items():\n      dim = 0\n      for slice_dim in cfg.slice_dims:\n        dim += slice_dim\n      feature_cfg[feature_name] = {\n          \"feature_name\": feature_name,\n          \"feature_index\": -1,\n          \"table_name\": cfg.table,\n          \"table_index\": -1,\n          \"feature_in_table_index\": -1,\n          \"table_feature_count\": 0,\n          \"pre_output_index\": 0,\n          \"dim_sum\": dim,\n      }\n      if cfg.table not in table_cfg:\n        table_cfg[cfg.table] = {\n            \"table_name\": cfg.table,\n            \"feature_list\": [],\n            \"table_index\": -1,\n            \"feature_count\": 0,\n        }\n\n    table_name_sort = sorted(table_cfg.keys())\n    for idx, name in enumerate(table_name_sort):\n      table_cfg[name][\"table_index\"] = idx\n\n    feature_name_sort = sorted(feature_cfg.keys())\n    for idx, name in enumerate(feature_name_sort):\n      f_cfg = feature_cfg[name]\n      t_cfg = table_cfg[f_cfg[\"table_name\"]]\n\n      f_cfg[\"feature_index\"] = idx\n      f_cfg[\"table_index\"] = t_cfg[\"table_index\"]\n      f_cfg[\"feature_in_table_index\"] = len(t_cfg[\"feature_list\"])\n\n      t_cfg[\"feature_list\"].append(name)\n\n    pre_index = 0\n    for idx, name in enumerate(table_name_sort):\n      t_cfg = table_cfg[name]\n      t_cfg[\"feature_count\"] = len(t_cfg[\"feature_list\"])\n      for feature_name in t_cfg[\"feature_list\"]:\n        f_cfg = feature_cfg[feature_name]\n        f_cfg[\"pre_output_index\"] = pre_index\n        f_cfg[\"table_feature_count\"] = t_cfg[\"feature_count\"]\n      pre_index += max(t_cfg[\"feature_count\"], 1) * ps_num\n    return feature_cfg, table_cfg, feature_name_sort, table_name_sort\n\n  def test_fused_embedding_to_layout(self,\n                                     shard_op_version=None,\n                                     op_version=2,\n                                     parallel_flag=1,\n                                     use_gpu=False):\n    batch_size = 256\n    num_ps = 5\n    slot_count = 200\n    slot_table_split = [50, 100\n                       ]  #slot split for [table_one, table_two, table_three]\n    max_sequence_length = 3\n    feature_cfgs = FeatureConfigs()\n    bias = OutConfig()\n    vec = OutConfig()\n    ffm1 = OutConfig()\n    ffm2 = OutConfig()\n    firstN = OutConfig()\n\n    for slot in range(1, slot_count):\n      feature_name = f\"fc_slot_{slot}\"\n      fconf = FeatureConfig()\n      if slot >= slot_table_split[1]:\n        table_name = \"table_one\"  #table_three, but now test for table with different dim\n        slice_dims = [1, 4, 16]\n        sequence_length = max_sequence_length\n        pooling_type = PoolingType.FIRSTN\n        slice_config = firstN.slice_configs.add()\n        slice_config.feature_name = feature_name\n        slice_config.start = 1\n        slice_config.end = 21\n      else:\n        sequence_length = 0\n        if slot < slot_table_split[0]:\n          table_name = \"table_one\"\n          slice_dims = [1, 4, 8]\n          pooling_type = PoolingType.SUM\n          slice_config = ffm1.slice_configs.add()\n          slice_config.feature_name = feature_name\n          slice_config.start = 5\n          slice_config.end = 13\n        else:\n          table_name = \"table_two\"\n          slice_dims = [1, 4, 16]\n          pooling_type = PoolingType.MEAN\n          slice_config = ffm2.slice_configs.add()\n          slice_config.feature_name = feature_name\n          slice_config.start = 5\n          slice_config.end = 21\n\n        slice_config = bias.slice_configs.add()\n        slice_config.feature_name = feature_name\n        slice_config.start = 0\n        slice_config.end = 1\n        slice_config = vec.slice_configs.add()\n        slice_config.feature_name = feature_name\n        slice_config.start = 1\n        slice_config.end = 5\n\n      fconf.table = table_name\n      fconf.slice_dims.extend(slice_dims)\n      fconf.max_sequence_length = sequence_length\n      fconf.pooling_type = pooling_type\n      feature_cfgs.feature_configs[feature_name].CopyFrom(fconf)\n\n    infer_shape(bias, OutType.ADDN)\n    feature_cfgs.out_configs['bias'].CopyFrom(bias)\n    infer_shape(vec, OutType.CONCAT)\n    feature_cfgs.out_configs['vec'].CopyFrom(vec)\n    infer_shape(ffm1, OutType.STACK)\n    feature_cfgs.out_configs['ffm1'].CopyFrom(ffm1)\n    infer_shape(ffm2, OutType.NONE)\n    feature_cfgs.out_configs['ffm2'].CopyFrom(ffm2)\n    infer_shape(firstN, OutType.NONE, max_sequence_length)\n    feature_cfgs.out_configs['firstN'].CopyFrom(firstN)\n\n    logging.info(f\"feature_cfgs : {feature_cfgs} \")\n    feature_cfg, table_cfg, feature_name_sort, table_name_sort = self.get_feature_cfg(\n        feature_cfgs, num_ps)\n\n    fid_offset_list = list()\n    feature_offset_list = [0]\n    nfl_offset_list = [0]\n    nfl_offset_list2 = [0]\n\n    sparse_features = ExampleBatch(batch_size=batch_size)\n    std_features = defaultdict(list)\n    fids_dict = {}\n    fid_row_split_list = [[0] for _ in range(num_ps * len(table_name_sort))]\n\n    for feature_name in feature_name_sort:\n      slot = int(feature_name.split(\"fc_slot_\")[-1])\n      named_feature_list = sparse_features.named_feature_list.add()\n      named_feature_list.id = slot\n      named_feature_list.name = feature_name\n      is_shared = True if slot % 2 == 0 else False\n      logging.info(f\"show shared {named_feature_list.name} {is_shared}\")\n      named_feature_list.type = FeatureListType.SHARED if is_shared else FeatureListType.INDIVIDUAL\n      f_cfg = feature_cfg[feature_name]\n      table_name = f_cfg[\"table_name\"]\n      t_cfg = table_cfg[table_name]\n      table_index = t_cfg[\"table_index\"]\n      dim_sum = f_cfg[\"dim_sum\"]\n      if table_name not in fids_dict:\n        fids_dict[table_name] = defaultdict(list)\n      index2 = [0] * num_ps * len(table_cfg)\n\n      def make_fids(feature):\n        std_features[named_feature_list.name].append(feature)\n        '''\n        fids = list(\n            set([(slot << 48) + randint(100, 1000000)\n                 for _ in range(randint(1, 5))]))\n        '''\n        fids = list(\n            set([(slot * 10000) + (i + 1) * 1000 + randint(1, 9) * 100\n                 for i in range(randint(1, max_sequence_length * 2))]))\n        logging.info(f\"show fids {fids}\")\n        feature.fid_v2_list.value.extend(fids)\n        for fid in fids:\n          idx = fid % num_ps\n          full_index = self.get_pre_output_offset(idx, f_cfg)\n          index1 = table_index * num_ps + idx\n          fid_offset = full_index << 32 | index2[index1]\n          index2[index1] += 1\n\n          fid_offset_list.append(fid_offset)\n          fids_dict[table_name][idx].append((dim_sum, fid))\n        feature_offset_list.append(len(fid_offset_list))\n\n      if is_shared:\n        feature = named_feature_list.feature.add()\n        make_fids(feature)\n      else:\n        for _ in range(batch_size):\n          feature = named_feature_list.feature.add()\n          make_fids(feature)\n\n      for ps_i in range(num_ps):\n        fid_row_split_list[table_index * num_ps + ps_i].append(\n            len(fids_dict[table_name][ps_i]))\n\n      nfl_index = len(feature_offset_list) - 1\n      if is_shared:  # add shared encode, 向前一位\n        nfl_offset_list[-1] |= SHARD_BIT\n      nfl_offset_list.append(nfl_index)\n    nfl_size_list = [len(nfl_offset_list)]\n    feature_size_list = [len(feature_offset_list)]\n    fid_size_list = [len(fid_offset_list)]\n\n    logging.info(f\"show fid_row_split_list: {fid_row_split_list}\")\n\n    logging.info(f\"sparse_features : {sparse_features} \")\n\n    fid_to_emb = {}\n    embeddings_list = []\n    emb_size_list = []\n    for table_name, table in fids_dict.items():\n      for idx in sorted(table):\n        values = table[idx]\n        #emb = np.random.uniform(size=size)\n        #logging.info(f\"show emb {emb}\")\n        emb = []\n        for i, (dim, fid) in enumerate(values):\n          fid_emb = []\n          for j in range(dim):\n            fid_emb.append(fid + j)\n          fid_to_emb[fid] = np.array(fid_emb, dtype=float)\n          emb.extend(fid_emb)\n        emb_size_list.append(len(emb))\n        emb = np.array(emb, dtype=float)\n        logging.info(f\"show emb2 {emb}\")\n        embeddings_list.append(\n            tf.reshape(tf.constant(value=emb, dtype=tf.float32), [-1]))\n    #sparse_features_str = tf.constant(value=sparse_features.SerializeToString(),\n    #                                  dtype=tf.string)\n\n    if shard_op_version:\n      get_default_parser_ctx().enable_fused_layout = True\n      parsed_results = parse_example_batch(sparse_features.SerializeToString(),\n                                           sparse_features=[],\n                                           dense_features=[],\n                                           dense_feature_shapes=[],\n                                           dense_feature_types=[],\n                                           extra_features=[],\n                                           extra_feature_shapes=[])\n      sparse_varint = parsed_results.pop(\n          ParserCtx.sharding_sparse_fids_sparse_features_key)\n      fid_list, fid_offset_list_ts, feature_offset_list_ts, nfl_offset_list_ts, batch_size_ts, nfl_size_list_ts, feature_size_list_ts, \\\n      fid_size_list_ts, emb_size_list_ts, fid_row_split_list_ts, fid_row_split_size_list_ts, fid_list_emb_row_lenth, \\\n      fid_list_table_row_length, fid_list_shard_row_lenth = sharding_sparse_fids(\n          sparse_varint,\n          num_ps,\n          feature_cfgs,\n          False,\n          \"examplebatch\",\n          parallel_flag=0,\n          fid_list_ret_list=True,\n          version=shard_op_version)\n    else:\n      fid_row_split_list_ts = fid_row_split_list\n      fid_offset_list_ts = tf.constant(fid_offset_list, dtype=tf.uint64)\n      feature_offset_list_ts = tf.constant(feature_offset_list,\n                                           dtype=tf.int32)\n      nfl_offset_list_ts = tf.constant(nfl_offset_list, dtype=tf.uint32)\n      fid_list_emb_row_lenth = None\n      if op_version >= 3:\n        raise TypeError('Not imple')\n        batch_size_ts = tf.constant([0] * batch_size, dtype=tf.int32)\n      else:\n        batch_size_ts = tf.constant([batch_size], dtype=tf.int32)\n      nfl_size_list_ts = tf.constant(nfl_size_list, dtype=tf.int32)\n      feature_size_list_ts = tf.constant(feature_size_list, dtype=tf.int32)\n      fid_size_list_ts = tf.constant(fid_size_list, dtype=tf.int32)\n      emb_size_list_ts = tf.constant(emb_size_list, dtype=tf.int32)\n\n    variant_type = 'example_batch'\n    if use_gpu:\n      assert op_version >= 3\n    if op_version == 4:\n      embeddings_list_new = []\n      for ps_i in range(num_ps):\n        for table_i in range(len(table_name_sort)):\n          embeddings_list_new.append(embeddings_list[table_i * num_ps + ps_i])\n      embeddings_list = [tf.concat(embeddings_list_new, axis=-1)]\n\n    with test_util.use_gpu() if use_gpu else tf.device(\"CPU:0\"):\n      layouts_op = distribution_ops.fused_embedding_to_layout(\n          embeddings_list,\n          fid_row_split_list_ts,\n          fid_offset_list_ts,\n          feature_offset_list_ts,\n          nfl_offset_list_ts,\n          batch_size_ts,\n          variant_type,\n          feature_cfgs,\n          num_ps,\n          fid_list_emb_row_lenth=fid_list_emb_row_lenth,\n          nfl_size=nfl_size_list_ts,\n          feature_size=feature_size_list_ts,\n          fid_size=fid_size_list_ts,\n          emb_size=emb_size_list_ts,\n          parallel_flag=parallel_flag,\n          version=op_version)\n    with self.session() as sess:\n      layouts = sess.run(layouts_op)\n    #logging.info(f\"show layouts: {layouts}\")\n\n    layout_names = sorted([x for x in feature_cfgs.out_configs.keys()])\n    out_tensors = {}\n    layout_info = {}\n    out_tensor_list = []\n    out_tensor_name_list = []\n\n    # get layout configs.\n    for ln in layout_names:\n      out_config = feature_cfgs.out_configs[ln]\n      out_tensors[ln] = []\n      info = {}\n      if len(out_config.shape) == 1:\n        for shape in out_config.shape:\n          real_shape = list(shape.dims)\n          real_shape[0] = batch_size\n          ts = np.zeros(shape=real_shape, dtype=np.float32)\n          #logging.info(f\" {ln} {ts} \")\n          out_tensors[ln].append(ts)\n          out_tensor_list.append(ts)\n          out_tensor_name_list.append(ln + \":\" + str(len(out_tensors[ln])))\n\n          offset = 0\n          for i, sc in enumerate(out_config.slice_configs):\n            key = get_key(ln, sc)\n            dim = sc.end - sc.start\n            if out_config.out_type == OutType.CONCAT:\n              info[key] = (ts, offset)\n              offset += dim\n            elif out_config.out_type == OutType.STACK:\n              info[key] = (ts, i)\n            elif out_config.out_type == OutType.ADDN:\n              info[key] = (ts, 0)\n            else:\n              raise Exception(\"error\")\n      else:\n        for sc, shape in zip(out_config.slice_configs, out_config.shape):\n          real_shape = list(shape.dims)\n          real_shape[0] = batch_size\n          ts = np.zeros(shape=real_shape, dtype=np.float32)\n          out_tensors[ln].append(ts)\n          out_tensor_list.append(ts)\n          out_tensor_name_list.append(ln + \":\" + str(len(out_tensors[ln])))\n\n          key = get_key(ln, sc)\n          info[key] = (ts, 0)\n\n      layout_info[ln] = info\n    # {name: (out, offset)}\n\n    for ln in layout_names:\n      out_config = feature_cfgs.out_configs[ln]\n      out_type = out_config.out_type\n      for slice_conf in out_config.slice_configs:\n        name = slice_conf.feature_name\n        features = std_features[name]\n        feature_config = feature_cfgs.feature_configs[name]\n        pooling_type = feature_config.pooling_type\n        max_length = feature_config.max_sequence_length\n        key = get_key(ln, slice_conf)\n        dim = slice_conf.end - slice_conf.start\n        (ts, offset) = layout_info[ln][key]\n        if out_type == OutType.ADDN:\n          tmp_addn = np.zeros(ts.shape)  # per slice tmp out\n\n        #logging.info(f\" {ln} {ts} \")\n        for i in range(batch_size):\n          if i < len(features):\n            tmp = []\n            for fid in features[i].fid_v2_list.value:\n              fid_emb = fid_to_emb[fid]\n              emb_slice = fid_emb[slice_conf.start:slice_conf.end]\n              tmp.append(emb_slice)\n\n            if out_type == OutType.CONCAT:\n              ts[i, offset:offset + dim] = pooling(pooling_type, tmp,\n                                                   max_length)\n            elif out_type == OutType.STACK:\n              ts[i, offset, :] = pooling(pooling_type, tmp, max_length)\n            elif out_type == OutType.ADDN:\n              ret = pooling(pooling_type, tmp, max_length)\n              tmp_addn[i, :] = ret\n            else:\n              ts[i, :] = pooling(pooling_type, tmp, max_length)\n          else:\n            # shared & copy\n            if out_type == OutType.CONCAT:\n              ts[i, offset:offset + dim] = ts[i - 1, offset:offset + dim]\n            elif out_type == OutType.STACK:\n              ts[i, offset, :] = ts[i - 1, offset, :]\n            elif out_type == OutType.ADDN:\n              tmp_addn[i, :] = tmp_addn[i - 1, :]\n            else:\n              ts[i, :] = ts[i - 1, :]\n\n        if out_type == OutType.ADDN:\n          ts += tmp_addn\n\n        #logging.info(f\" {ln} {ts} \")\n\n    #logging.info(f\"xxx out_tensor_list: {out_tensor_list}\")\n    for name, t, p in zip(out_tensor_name_list, out_tensor_list, layouts):\n      #logging.info(f\"xxx show result: {name} \\n ans:{t} \\n res:{p}\")\n      flag = np.allclose(t, p, rtol=1e-04, atol=1e-07, equal_nan=False)\n      if not flag:\n        logging.error(f\"xxx show result: {name} \\n ans:{t} \\n res:{p}\")\n      else:\n        logging.info(f\"show result: {name} \\n ans:{t} \\n res:{p}\")\n      assert flag\n\n  def test_fused_embedding_to_layout_use_shard_op(self):\n    self.test_fused_embedding_to_layout(shard_op_version=2)\n\n  def test_fused_embedding_to_layout_use_shard_op3(self):\n    self.test_fused_embedding_to_layout(shard_op_version=3, op_version=3)\n\n  def test_fused_embedding_to_layout_use_shard_op3_gpu(self):\n    self.test_fused_embedding_to_layout(shard_op_version=3,\n                                        op_version=3,\n                                        use_gpu=True)\n\n  def test_fused_embedding_to_layout_use_shard_op4(self):\n    self.test_fused_embedding_to_layout(shard_op_version=4, op_version=4)\n\n  def test_fused_embedding_to_layout_use_shard_op4_gpu(self):\n    self.test_fused_embedding_to_layout(shard_op_version=4,\n                                        op_version=4,\n                                        use_gpu=True)\n\n  def test_fused_embedding_to_layout_parallel(self):\n    self.test_fused_embedding_to_layout(parallel_flag=0)\n\n  def test_fused_embedding_to_layout_grad(self,\n                                          shard_op_version=None,\n                                          op_version=2,\n                                          parallel_flag=1,\n                                          use_gpu=False):\n    batch_size = 256\n    num_ps = 3\n    slot_num = 30\n    slot_table_split = [10,\n                        20]  #slot split for [table_one, table_two, table_three]\n    max_sequence_length = 3\n    alphabet_name = list(string.ascii_lowercase) + ['za', 'zb', 'zc', 'zd']\n    feature_cfgs = FeatureConfigs()\n    #sparse_features = list()\n\n    bias = OutConfig()\n    vec = OutConfig()\n    ffm1 = OutConfig()\n    ffm2 = OutConfig()\n    firstN = OutConfig()\n\n    for slot in range(1, slot_num):\n      feature_name = f\"fc_slot_{alphabet_name[slot - 1]}\"\n      fconf = FeatureConfig()\n      if slot >= slot_table_split[1]:\n        table_name = \"table_one\"  #table_three, but now test for table with different dim\n        slice_dims = [1, 4, 16]\n        sequence_length = max_sequence_length\n        pooling_type = PoolingType.FIRSTN\n        slice_config = firstN.slice_configs.add()\n        slice_config.feature_name = feature_name\n        slice_config.start = 0\n        slice_config.end = 21\n      else:\n        sequence_length = 0\n        if slot < slot_table_split[0]:\n          table_name = \"table_one\"\n          slice_dims = [1, 4, 8]\n          pooling_type = PoolingType.SUM\n          slice_config = ffm1.slice_configs.add()\n          slice_config.feature_name = feature_name\n          slice_config.start = 5\n          slice_config.end = 13\n        else:\n          table_name = \"table_two\"\n          slice_dims = [1, 4, 16]\n          pooling_type = PoolingType.MEAN\n          slice_config = ffm2.slice_configs.add()\n          slice_config.feature_name = feature_name\n          slice_config.start = 5\n          slice_config.end = 21\n        slice_config = bias.slice_configs.add()\n        slice_config.feature_name = feature_name\n        slice_config.start = 0\n        slice_config.end = 1\n        slice_config = vec.slice_configs.add()\n        slice_config.feature_name = feature_name\n        slice_config.start = 1\n        slice_config.end = 5\n\n      fconf.table = table_name\n      fconf.slice_dims.extend(slice_dims)\n      fconf.max_sequence_length = sequence_length\n      fconf.pooling_type = pooling_type\n      feature_cfgs.feature_configs[feature_name].CopyFrom(fconf)\n\n    infer_shape(bias, OutType.ADDN)\n    feature_cfgs.out_configs['bias'].CopyFrom(bias)\n    infer_shape(vec, OutType.CONCAT)\n    feature_cfgs.out_configs['vec'].CopyFrom(vec)\n    infer_shape(ffm1, OutType.STACK)\n    feature_cfgs.out_configs['ffm1'].CopyFrom(ffm1)\n    infer_shape(ffm2, OutType.NONE)\n    feature_cfgs.out_configs['ffm2'].CopyFrom(ffm2)\n    infer_shape(firstN, OutType.NONE, max_sequence_length)\n    feature_cfgs.out_configs['firstN'].CopyFrom(firstN)\n\n    logging.info(f\"feature_cfgs : {feature_cfgs} \")\n    feature_cfg, table_cfg, feature_name_sort, table_name_sort = self.get_feature_cfg(\n        feature_cfgs, num_ps)\n\n    # gen all fids\n    slot2fid = defaultdict(list)\n    sparse_features = [Example() for i in range(batch_size)]\n    fid_idx_list_batch = defaultdict(lambda: [[] for i in range(batch_size)])\n    for slot in range(1, slot_num):\n      feature_name = f\"fc_slot_{alphabet_name[slot - 1]}\"\n      fids = list(\n          set([(slot << 48) + randint(100, 1000000)\n               for _ in range(randint(batch_size + 1, batch_size + 10))]))\n      slot2fid[slot] = fids\n\n      for bi in range(batch_size):\n        sparse_feature = sparse_features[bi]\n        named_feature = sparse_feature.named_feature.add()\n        named_feature.name = feature_name\n        fid_idx_list = [i for i in range(bi, len(fids) - batch_size + 1 + bi)]\n        fid_idx_list_batch[slot][bi] = fid_idx_list\n\n        fid_list = [fids[idx] for idx in fid_idx_list]\n        named_feature.feature.fid_v2_list.value.extend(fid_list)\n\n    # gen offset\n    slot2fid_offset = defaultdict(list)\n    embedding_fid_list = [[] for _ in range(num_ps * len(table_cfg))]\n    emb_size_list = [0 for _ in range(num_ps * len(table_cfg))]\n    fid_row_split_list = [[0] for _ in range(num_ps * len(table_cfg))]\n    # record the truth\n    truth = defaultdict(lambda: defaultdict(list))\n    for slot in range(1, slot_num):\n      feature_name = f\"fc_slot_{alphabet_name[slot - 1]}\"\n      f_cfg = feature_cfg[feature_name]\n      dim_sum = f_cfg[\"dim_sum\"]\n      table_idx = f_cfg[\"table_index\"]\n      fids = slot2fid[slot]\n      embedding_fid_list_tmp = [[] for _ in range(num_ps)]\n      for fid in fids:\n        ps_index = fid % num_ps\n        index1 = table_idx * num_ps + ps_index\n        embedding_fid_list[index1].append((fid, dim_sum))\n        emb_size_list[index1] += dim_sum\n        index2 = len(embedding_fid_list[index1]) - 1\n        embedding_fid_list_tmp[ps_index].append(fid)\n        feature_index = len(embedding_fid_list_tmp[ps_index]) - 1\n        full_index = self.get_pre_output_offset(ps_index, f_cfg)\n        #[index1(table_index), index2(fid in table index)\n        # , full_index(all_feature_index), feature_index(fid in all_feature index)]\n        slot2fid_offset[slot].append(\n            [index1, index2, full_index, feature_index])\n        truth[index1][index2] = [0, dim_sum]\n      for ps_i in range(num_ps):\n        index1 = table_idx * num_ps + ps_i\n        fid_row_split_list[index1].append(len(embedding_fid_list[index1]))\n\n    # gen offset\n    fid_offset_list = list()\n    feature_offset_list = [0]\n    nfl_offset_list = [0]\n    for slot in range(1, slot_num):\n      feature_name = f\"fc_slot_{alphabet_name[slot - 1]}\"\n      feature_config = feature_cfgs.feature_configs[feature_name]\n      pooling_type = feature_config.pooling_type\n      max_length = feature_config.max_sequence_length\n      for bi in range(batch_size):\n        fid_idx_list = fid_idx_list_batch[slot][bi]\n\n        for i, idx in enumerate(fid_idx_list):\n          index1, index2, full_index, feature_index = slot2fid_offset[slot][idx]\n          fid_offset = full_index << 32 | feature_index\n          fid_offset_list.append(fid_offset)\n          if pooling_type == PoolingType.FIRSTN and i >= max_length:\n            pass\n          elif pooling_type == PoolingType.MEAN:\n            truth[index1][index2][0] += 1 / len(fid_idx_list)\n          else:\n            truth[index1][index2][0] += 1\n\n        feature_offset_list.append(len(fid_offset_list))\n      nfl_index = len(feature_offset_list) - 1\n      nfl_offset_list.append(nfl_index)\n    nfl_size_list = [len(nfl_offset_list)]\n    feature_size_list = [len(feature_offset_list)]\n    fid_size_list = [len(fid_offset_list)]\n\n    # gen emb\n    embeddings_list = list()\n    for idx, embedding_fid in enumerate(embedding_fid_list):\n      dim_sum = 0\n      for fid, dim in embedding_fid:\n        dim_sum += dim\n      size = (dim_sum, 1)\n      emb = np.random.uniform(size=size)\n      embeddings_list.append(\n          tf.reshape(tf.constant(value=emb, dtype=tf.float32), [-1]))\n\n    with self.session() as sess:\n      if shard_op_version:\n        get_default_parser_ctx().enable_fused_layout = True\n        parsed_results = parse_examples(\n            [sparse.SerializeToString() for sparse in sparse_features],\n            sparse_features=[],\n            dense_features=[],\n            dense_feature_shapes=[],\n            dense_feature_types=[],\n            extra_features=[],\n            extra_feature_shapes=[])\n        sparse_varint = parsed_results.pop(\n            ParserCtx.sharding_sparse_fids_sparse_features_key)\n        fid_list, fid_offset_list_ts, feature_offset_list_ts, nfl_offset_list_ts, batch_size_ts, nfl_size_list_ts, feature_size_list_ts, \\\n        fid_size_list_ts, emb_size_list_ts, fid_row_split_list, fid_row_split_size_list, fid_list_emb_row_lenth, \\\n        fid_list_table_row_length, fid_list_shard_row_lenth = sharding_sparse_fids(\n            sparse_varint,\n            num_ps,\n            feature_cfgs,\n            True,\n            \"example\",\n            parallel_flag=0,\n            fid_list_ret_list=True,\n            version=shard_op_version)\n\n        assert op_version == shard_op_version\n      else:\n        fid_offset_list_ts = tf.constant(fid_offset_list, dtype=tf.uint64)\n        feature_offset_list_ts = tf.constant(feature_offset_list,\n                                             dtype=tf.int32)\n        nfl_offset_list_ts = tf.constant(nfl_offset_list, dtype=tf.uint32)\n        batch_size_ts = tf.constant([batch_size], dtype=tf.int32)\n        nfl_size_list_ts = tf.constant(nfl_size_list, dtype=tf.int32)\n        feature_size_list_ts = tf.constant(feature_size_list, dtype=tf.int32)\n        fid_size_list_ts = tf.constant(fid_size_list, dtype=tf.int32)\n        emb_size_list_ts = tf.constant(emb_size_list, dtype=tf.int32)\n\n        fid_list_emb_row_lenth = None\n        if op_version >= 3:\n          raise TypeError('Not imple')\n\n      if use_gpu:\n        assert op_version >= 3\n      if op_version == 4:\n        embeddings_list_new = []\n        for ps_i in range(num_ps):\n          for table_i in range(len(table_name_sort)):\n            embeddings_list_new.append(embeddings_list[table_i * num_ps + ps_i])\n        embeddings_list = [tf.concat(embeddings_list_new, axis=-1)]\n\n      variant_type = 'example'\n      with test_util.use_gpu() if use_gpu else tf.device(\"CPU:0\"):\n        layouts = distribution_ops.fused_embedding_to_layout(\n            embeddings_list,\n            fid_row_split_list,\n            fid_offset_list_ts,\n            feature_offset_list_ts,\n            nfl_offset_list_ts,\n            batch_size_ts,\n            variant_type,\n            feature_cfgs,\n            num_ps,\n            fid_list_emb_row_lenth=fid_list_emb_row_lenth,\n            nfl_size=nfl_size_list_ts,\n            feature_size=feature_size_list_ts,\n            fid_size=fid_size_list_ts,\n            emb_size=emb_size_list_ts,\n            parallel_flag=parallel_flag,\n            version=op_version)\n        #layouts_ret = sess.run(layouts)\n        #logging.info(f\"show result: {layouts_ret}\")\n        test_grads = tf.gradients(layouts, embeddings_list)\n        if op_version == 4:\n          recv_embeddings_split = tf.split(test_grads[0],\n                                           fid_list_emb_row_lenth)\n\n          test_grads = [None] * (num_ps * len(table_name_sort))\n          recv_embeddings_split_index = 0\n          for ps_index in range(num_ps):\n            for table_idx in range(len(table_name_sort)):\n              test_grads[\n                  table_idx * num_ps +\n                  ps_index] = recv_embeddings_split[recv_embeddings_split_index]\n              recv_embeddings_split_index += 1\n      ''' TODO\n      test_grads = distribution_ops.fused_embedding_to_layout_grad(\n          nfl_offset_list_ts,\n          feature_offset_list_ts,\n          fid_offset_list_ts,\n          batch_size_ts,\n          embeddings_list,\n          fid_row_split_list,\n          layouts,\n          variant_type,\n          feature_cfgs,\n          num_ps,\n          parallel_flag=parallel_flag,\n          version=2,\n      )\n      '''\n\n      grads = sess.run(test_grads)\n      logging.info(f\"show result: {grads}\")\n      logging.info(f\"show truth: {truth}\")\n      assert len(grads) == len(truth)\n      for i in range(len(truth)):\n        part_truth = truth[i]\n        grad = grads[i]\n        offset = 0\n        for j in range(len(part_truth)):\n          t, dim = part_truth[j]\n          # There is no slice use twice in the UT data, so the grads of one fid embedding should be the same\n          assert len(np.unique(grad[offset: offset + dim])) == 1, \\\n                    f\"Alert All The Same! [{i}, {j}] [{(t, dim)}, {grad[offset: offset + dim]}]\"\n          # The gound truth should be the fid used times\n          assert np.allclose(t, grad[offset], rtol=1e-04, atol=1e-07, equal_nan=False), \\\n            f\"Alert Equal! [{i}, {j}] [{t} {grad[offset]}]\"\n          offset += dim\n\n  def test_fused_embedding_to_layout_grad_no_parallel(self):\n    self.test_fused_embedding_to_layout_grad(parallel_flag=0)\n\n  def test_fused_embedding_to_layout_grad_use_shard_op(self):\n    self.test_fused_embedding_to_layout_grad(shard_op_version=2, op_version=2)\n\n  def test_fused_embedding_to_layout_grad_use_shard_op3(self):\n    self.test_fused_embedding_to_layout_grad(shard_op_version=3, op_version=3)\n\n  def test_fused_embedding_to_layout_grad_use_shard_op3_gpu(self):\n    self.test_fused_embedding_to_layout_grad(shard_op_version=3,\n                                             op_version=3,\n                                             use_gpu=True)\n\n  def test_fused_embedding_to_layout_grad_use_shard_op4(self):\n    self.test_fused_embedding_to_layout_grad(shard_op_version=4, op_version=4)\n\n  def test_fused_embedding_to_layout_grad_use_shard_op4_gpu(self):\n    self.test_fused_embedding_to_layout_grad(shard_op_version=4,\n                                             op_version=4,\n                                             use_gpu=True)\n\n\nclass FusedEmbeddingToLayoutFitPreTest(tf.test.TestCase):\n\n  def test_fused_embedding_to_layout(self):\n    batch_size = 10\n    feature_cfgs = FeatureConfigs()\n    sparse_features = ExampleBatch(batch_size=batch_size)\n    std_features = defaultdict(list)\n    fids_dict = {}\n    bias = OutConfig()\n    vec = OutConfig()\n    ffm1 = OutConfig()\n    ffm2 = OutConfig()\n\n    index1 = 0\n    index2 = [0] * 10\n    fid_offset_list = list()\n    feature_offset_list = [0]\n    nfl_offset_list = [0]\n    feature_names_list = list()\n    slot_to_nfl_map = dict()\n\n    for slot in range(1, 50):\n      named_feature_list = sparse_features.named_feature_list.add()\n      named_feature_list.id = slot\n      named_feature_list.name = f\"fc_slot_{slot}\"\n      feature_names_list.append(named_feature_list.name)\n      is_shared = True if slot % 2 == 0 else False\n      named_feature_list.type = FeatureListType.SHARED if is_shared else FeatureListType.INDIVIDUAL\n      slot_to_nfl_map[slot] = named_feature_list\n\n    # set all offset_list by sorted sorted_feature_names_list\n    sorted_feature_names_list = sorted(feature_names_list)\n\n    for feature_name in sorted_feature_names_list:\n      slot = int(feature_name.split(\"fc_slot_\")[-1])\n      named_feature_list = slot_to_nfl_map[slot]\n      fconf = FeatureConfig()\n      if slot < 25:\n        table_name = \"table_one\"\n        slice_dims = [1, 4, 8]\n        pooling_type = PoolingType.SUM\n        slice_config = ffm1.slice_configs.add()\n        slice_config.feature_name = f\"fc_slot_{slot}\"\n        slice_config.start = 5\n        slice_config.end = 13\n      else:\n        table_name = \"table_two\"\n        slice_dims = [1, 4, 16]\n        pooling_type = PoolingType.MEAN\n        slice_config = ffm2.slice_configs.add()\n        slice_config.feature_name = f\"fc_slot_{slot}\"\n        slice_config.start = 5\n        slice_config.end = 21\n\n      slice_config = bias.slice_configs.add()\n      slice_config.feature_name = f\"fc_slot_{slot}\"\n      slice_config.start = 0\n      slice_config.end = 1\n      slice_config = vec.slice_configs.add()\n      slice_config.feature_name = f\"fc_slot_{slot}\"\n      slice_config.start = 1\n      slice_config.end = 5\n\n      fconf.table = table_name\n      fconf.slice_dims.extend(slice_dims)\n      fconf.pooling_type = pooling_type\n      feature_cfgs.feature_configs[f\"fc_slot_{slot}\"].CopyFrom(fconf)\n      table_name = \"table_one\" if slot < 25 else \"table_two\"\n      if table_name not in fids_dict:\n        fids_dict[table_name] = defaultdict(list)\n      if named_feature_list.type == FeatureListType.SHARED:\n        feature = named_feature_list.feature.add()\n        std_features[named_feature_list.name].append(feature)\n        fids = list(\n            set([(slot << 48) + randint(100, 1000000)\n                 for _ in range(randint(1, 5))]))\n        feature.fid_v2_list.value.extend(fids)\n        for fid in fids:\n          idx = fid % 5\n          if table_name == \"table_one\":\n            index1 = 0 + idx\n            fid_offset = index1 << 32 | index2[index1]\n            index2[index1] += 1\n          else:\n            index1 = 5 + idx\n            fid_offset = index1 << 32 | index2[index1]\n            index2[index1] += 1\n\n          fid_offset_list.append(fid_offset)\n          fids_dict[table_name][idx].append(fid)\n        feature_offset_list.append(len(fid_offset_list))\n\n      else:\n        for _ in range(batch_size):\n          feature = named_feature_list.feature.add()\n          fids = list(\n              set([(slot << 48) + randint(100, 1000000)\n                   for _ in range(randint(1, 5))]))\n          feature.fid_v2_list.value.extend(fids)\n          for fid in fids:\n            idx = fid % 5\n            if table_name == \"table_one\":\n              index1 = 0 + idx\n              fid_offset = index1 << 32 | index2[index1]\n              index2[index1] += 1\n            else:\n              index1 = 5 + idx\n              fid_offset = index1 << 32 | index2[index1]\n              index2[index1] += 1\n\n            fid_offset_list.append(fid_offset)\n            fids_dict[table_name][idx].append(fid)\n          feature_offset_list.append(len(fid_offset_list))\n          std_features[named_feature_list.name].append(feature)\n\n      nfl_index = len(feature_offset_list) - 1\n      nfl_offset_list.append(nfl_index)\n    nfl_size_list = [len(nfl_offset_list)]\n    feature_size_list = [len(feature_offset_list)]\n    fid_size_list = [len(fid_offset_list)]\n\n    # add shared encode\n    nfl_idx = 0\n    for feature_name in sorted_feature_names_list:\n      slot = int(feature_name.split(\"fc_slot_\")[-1])\n      is_shared = True if slot % 2 == 0 else False\n      head_bit = SHARD_BIT if is_shared else 0\n      nfl_offset_list[nfl_idx] |= head_bit\n      nfl_idx += 1\n\n    infer_shape(bias, OutType.ADDN)\n    feature_cfgs.out_configs['bias'].CopyFrom(bias)\n    infer_shape(vec, OutType.CONCAT)\n    feature_cfgs.out_configs['vec'].CopyFrom(vec)\n    infer_shape(ffm1, OutType.STACK)\n    feature_cfgs.out_configs['ffm1'].CopyFrom(ffm1)\n    infer_shape(ffm2, OutType.NONE)\n    feature_cfgs.out_configs['ffm2'].CopyFrom(ffm2)\n\n    embeddings_dict, fid_to_emb = {}, {}\n    emb_size_list = []\n    fids_list, embeddings_list, embeddings_np_list = [], [], []\n    for table_name, table in fids_dict.items():\n      embeddings_dict[table_name] = {}\n      for idx in sorted(table):\n        values = table[idx]\n        fids_list.append(tf.constant(value=values, dtype=tf.int64))\n        if table_name == \"table_one\":\n          length = 13\n        else:\n          length = 21\n\n        size = (len(values), length)\n        emb = np.random.uniform(size=size)\n        emb_size_list.append(len(values) * length)\n        embeddings_dict[table_name][idx] = emb\n        embeddings_list.append(\n            tf.constant(value=emb, shape=size, dtype=tf.float32))\n        embeddings_np_list.append(emb)\n\n        for i, fid in enumerate(values):\n          fid_to_emb[fid] = (len(embeddings_np_list) - 1, i)\n    sparse_features_str = tf.constant(value=sparse_features.SerializeToString(),\n                                      dtype=tf.string)\n    variant_type = 'example_batch'\n    # layouts = distribution_ops.fused_embedding_to_layout(sparse_features_str, fids_list, embeddings_list, variant_type, feature_cfgs)\n\n    fid_offset_list_ts = tf.constant(fid_offset_list, dtype=tf.uint64)\n    feature_offset_list_ts = tf.constant(feature_offset_list,\n                                         dtype=tf.int32)\n    nfl_offset_list_ts = tf.constant(nfl_offset_list, dtype=tf.uint32)\n    batch_size_ts = tf.constant([batch_size], dtype=tf.int32)\n    nfl_size_list_ts = tf.constant(nfl_size_list, dtype=tf.int32)\n    feature_size_list_ts = tf.constant(feature_size_list, dtype=tf.int32)\n    fid_size_list_ts = tf.constant(fid_size_list, dtype=tf.int32)\n    emb_size_list_ts = tf.constant(emb_size_list, dtype=tf.int32)\n\n    layouts_op = distribution_ops.fused_embedding_to_layout(\n        embeddings_list,\n        None,\n        fid_offset_list_ts,\n        feature_offset_list_ts,\n        nfl_offset_list_ts,\n        batch_size_ts,\n        variant_type,\n        feature_cfgs,\n        -1,\n        nfl_size=nfl_size_list_ts,\n        feature_size=feature_size_list_ts,\n        fid_size=fid_size_list_ts,\n        emb_size=emb_size_list_ts,\n        version=1)\n    with self.session() as sess:\n      layouts = sess.run(layouts_op)\n\n    layout_names = sorted(['bias', 'vec', 'ffm1', 'ffm2'])\n    out_tensors = {}\n    layout_info = {}\n    out_tensor_list = []\n\n    # get layout configs.\n    for ln in layout_names:\n      out_config = feature_cfgs.out_configs[ln]\n      out_tensors[ln] = []\n      info = {}\n      if len(out_config.shape) == 1:\n        for shape in out_config.shape:\n          real_shape = list(shape.dims)\n          real_shape[0] = batch_size\n          ts = np.zeros(shape=real_shape, dtype=np.float32)\n          out_tensors[ln].append(ts)\n          out_tensor_list.append(ts)\n\n          offset = 0\n          for i, sc in enumerate(out_config.slice_configs):\n            key = get_key(ln, sc)\n            dim = sc.end - sc.start\n            if out_config.out_type == OutType.CONCAT:\n              info[key] = (ts, offset)\n              offset += dim\n            elif out_config.out_type == OutType.STACK:\n              info[key] = (ts, i)\n            elif out_config.out_type == OutType.ADDN:\n              info[key] = (ts, 0)\n            else:\n              raise Exception(\"error\")\n      else:\n        for sc, shape in zip(out_config.slice_configs, out_config.shape):\n          real_shape = list(shape.dims)\n          real_shape[0] = batch_size\n          ts = np.zeros(shape=real_shape, dtype=np.float32)\n          out_tensors[ln].append(ts)\n          out_tensor_list.append(ts)\n\n          key = get_key(ln, sc)\n          info[key] = (ts, 0)\n\n      layout_info[ln] = info\n    # {name: (out, offset)}\n\n    for ln in layout_names:\n      out_config = feature_cfgs.out_configs[ln]\n      out_type = out_config.out_type\n      for slice_conf in out_config.slice_configs:\n        name = slice_conf.feature_name\n        features = std_features[name]\n        feature_config = feature_cfgs.feature_configs[name]\n        pooling_type = feature_config.pooling_type\n        max_length = feature_config.max_sequence_length\n        key = get_key(ln, slice_conf)\n        dim = slice_conf.end - slice_conf.start\n        (ts, offset) = layout_info[ln][key]\n        if out_type == OutType.ADDN:\n          tmp_addn = np.zeros(ts.shape)  # per slice tmp out\n\n        for i in range(batch_size):\n          if i < len(features):\n            tmp = []\n            for fid in features[i].fid_v2_list.value:\n              (idx, row) = fid_to_emb[fid]\n              emb_slice = embeddings_np_list[idx][\n                  row, slice_conf.start:slice_conf.end]\n              tmp.append(emb_slice)\n\n            if out_type == OutType.CONCAT:\n              ts[i, offset:offset + dim] = pooling(pooling_type, tmp,\n                                                   max_length)\n            elif out_type == OutType.STACK:\n              ts[i, offset, :] = pooling(pooling_type, tmp, max_length)\n            elif out_type == OutType.ADDN:\n              ret = pooling(pooling_type, tmp, max_length)\n              tmp_addn[i, :] = ret\n            else:\n              ts[i, :] = pooling(pooling_type, tmp, max_length)\n          else:\n            # shared & copy\n            if out_type == OutType.CONCAT:\n              ts[i, offset:offset + dim] = ts[i - 1, offset:offset + dim]\n            elif out_type == OutType.STACK:\n              ts[i, offset, :] = ts[i - 1, offset, :]\n            elif out_type == OutType.ADDN:\n              tmp_addn[i, :] = tmp_addn[i - 1, :]\n            else:\n              ts[i, :] = ts[i - 1, :]\n\n        if out_type == OutType.ADDN:\n          ts += tmp_addn\n\n    for t, p in zip(out_tensor_list, layouts):\n      logging.info(f\"fused_embedding_to_layout show {t} {p}\")\n      assert np.allclose(t, p, rtol=1e-04, atol=1e-07, equal_nan=False)\n\n  def test_fused_embedding_to_layout_grad(self):\n    batch_size = 4\n    slot_num = 30\n    alphabet_name = list(string.ascii_lowercase) + ['za', 'zb', 'zc', 'zd']\n    feature_cfgs = FeatureConfigs()\n    sparse_features = list()\n    # 13, 13, 21, 21\n    embedding_fid_list = [[], [], [], []]\n\n    bias = OutConfig()\n    vec = OutConfig()\n    ffm1 = OutConfig()\n    ffm2 = OutConfig()\n\n    slot2fid = dict()\n    slot2fid_offset = defaultdict(list)\n    for slot in range(1, slot_num):\n      table_idx = 0 if slot < slot_num / 2 else 2\n      fids = list(\n          set([(slot << 48) + randint(100, 1000000)\n               for _ in range(randint(2, 10))]))\n      slot2fid[slot] = fids\n      for fid in fids:\n        if fid % 2:\n          index1 = table_idx\n        else:\n          index1 = table_idx + 1\n        embedding_fid_list[index1].append(fid)\n        index2 = len(embedding_fid_list[index1]) - 1\n        slot2fid_offset[slot].append([index1, index2])\n\n      fconf = FeatureConfig()\n      table_name = \"table_one\" if slot < slot_num / 2 else \"table_two\"\n      if slot < slot_num / 2:\n        slice_dims = [1, 4, 8]\n        pooling_type = PoolingType.SUM\n        slice_config = ffm1.slice_configs.add()\n        slice_config.feature_name = f\"fc_slot_{alphabet_name[slot - 1]}\"\n        slice_config.start = 5\n        slice_config.end = 13\n      else:\n        slice_dims = [1, 4, 16]\n        pooling_type = PoolingType.MEAN\n        slice_config = ffm2.slice_configs.add()\n        slice_config.feature_name = f\"fc_slot_{alphabet_name[slot - 1]}\"\n        slice_config.start = 5\n        slice_config.end = 21\n      slice_config = bias.slice_configs.add()\n      slice_config.feature_name = f\"fc_slot_{alphabet_name[slot - 1]}\"\n      slice_config.start = 0\n      slice_config.end = 1\n      slice_config = vec.slice_configs.add()\n      slice_config.feature_name = f\"fc_slot_{alphabet_name[slot - 1]}\"\n      slice_config.start = 1\n      slice_config.end = 5\n      fconf.table = table_name\n      fconf.slice_dims.extend(slice_dims)\n      fconf.pooling_type = pooling_type\n      feature_cfgs.feature_configs[\n          f\"fc_slot_{alphabet_name[slot - 1]}\"].CopyFrom(fconf)\n\n    infer_shape(bias, OutType.ADDN)\n    feature_cfgs.out_configs['bias'].CopyFrom(bias)\n    infer_shape(vec, OutType.CONCAT)\n    feature_cfgs.out_configs['vec'].CopyFrom(vec)\n    infer_shape(ffm1, OutType.STACK)\n    feature_cfgs.out_configs['ffm1'].CopyFrom(ffm1)\n    infer_shape(ffm2, OutType.NONE)\n    feature_cfgs.out_configs['ffm2'].CopyFrom(ffm2)\n\n    # record the truth\n    truth = defaultdict(lambda: defaultdict(int))\n\n    fid_offset_list = list()\n    feature_offset_list = [0]\n    nfl_offset_list = [0]\n    embeddings_list = list()\n    sparse_features = [Example() for i in range(batch_size)]\n\n    for slot in range(1, slot_num):\n      feature_name = f\"fc_slot_{alphabet_name[slot - 1]}\"\n      feature_config = feature_cfgs.feature_configs[feature_name]\n      pooling_type = feature_config.pooling_type\n      max_length = feature_config.max_sequence_length\n      for bi in range(batch_size):\n        sparse_feature = sparse_features[bi]\n        named_feature = sparse_feature.named_feature.add()\n        named_feature.name = f\"fc_slot_{alphabet_name[slot - 1]}\"\n        all_fids = slot2fid[slot]\n        fid_num = randint(0, len(all_fids))\n        fid_idx_list = [randint(1, len(all_fids) - 1) for i in range(fid_num)]\n        fid_list = [all_fids[idx] for idx in fid_idx_list]\n        named_feature.feature.fid_v2_list.value.extend(fid_list)\n\n        for idx in fid_idx_list:\n          index1, index2 = slot2fid_offset[slot][idx]\n          fid_offset = index1 << 32 | index2\n          fid_offset_list.append(fid_offset)\n          if pooling_type == PoolingType.FIRSTN and i >= max_length:\n            pass\n          elif pooling_type == PoolingType.MEAN:\n            truth[index1][index2] += 1 / len(fid_idx_list)\n          else:\n            truth[index1][index2] += 1\n\n        feature_offset_list.append(len(fid_offset_list))\n      nfl_index = len(feature_offset_list) - 1\n      nfl_offset_list.append(nfl_index)\n    nfl_size_list = [len(nfl_offset_list)]\n    feature_size_list = [len(feature_offset_list)]\n    fid_size_list = [len(fid_offset_list)]\n\n    idx = 0\n    emb_size_list = []\n    for embedding_fid in embedding_fid_list:\n      if idx < 2:\n        embeddings_dim = [13 for i in embedding_fid]\n      else:\n        embeddings_dim = [21 for i in embedding_fid]\n      size = (len(embeddings_dim), embeddings_dim[0])\n      emb = np.random.uniform(size=size)\n      emb_size_list.append(size[0] * size[1])\n      embeddings_list.append(\n          tf.constant(value=emb, shape=size, dtype=tf.float32))\n      idx += 1\n    variant_type = 'example'\n\n    with self.session() as sess:\n      fid_offset_list_ts = tf.constant(fid_offset_list, dtype=tf.uint64)\n      feature_offset_list_ts = tf.constant(feature_offset_list,\n                                           dtype=tf.int32)\n      nfl_offset_list_ts = tf.constant(nfl_offset_list, dtype=tf.uint32)\n      batch_size_ts = tf.constant([batch_size], dtype=tf.int32)\n      nfl_size_list_ts = tf.constant(nfl_size_list, dtype=tf.int32)\n      feature_size_list_ts = tf.constant(feature_size_list, dtype=tf.int32)\n      fid_size_list_ts = tf.constant(fid_size_list, dtype=tf.int32)\n      emb_size_list_ts = tf.constant(emb_size_list, dtype=tf.int32)\n\n      layouts = distribution_ops.fused_embedding_to_layout(\n          embeddings_list,\n          None,\n          fid_offset_list_ts,\n          feature_offset_list_ts,\n          nfl_offset_list_ts,\n          batch_size_ts,\n          variant_type,\n          feature_cfgs,\n          -1,\n          nfl_size=nfl_size_list_ts,\n          feature_size=feature_size_list_ts,\n          fid_size=fid_size_list_ts,\n          emb_size=emb_size_list_ts,\n          version=1)\n      test_grads = tf.gradients(layouts, embeddings_list)\n      grads = sess.run(test_grads)\n      for i in range(len(grads)):\n        grad = grads[i]\n        for j in range(grad.shape[0]):\n          # There is no slice use twice in the UT data, so the grads of one fid embedding should be the same\n          assert len(np.unique(\n              grad[j, :])) == 1, \"Alert All The Same! [{}, {}]\".format(i, j)\n          # The gound truth should be the fid used times\n          logging.info(\n              f\"fused_embedding_to_layout grad show {truth[i][j]} {grad[j, 0]}\")\n          assert np.allclose(truth[i][j],grad[j, 0], rtol=1e-04, atol=1e-07, equal_nan=False), \\\n            \"Alert Equal! [{}, {}]\".format(i, j)\n\n\nif __name__ == \"__main__\":\n  # tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/gen_seq_mask.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom typing import Union\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\nops = gen_monolith_ops\n\n\ndef gen_seq_mask(splits: Union[tf.Tensor, tf.RaggedTensor],\n                 max_seq_length: int) -> tf.Tensor:\n  if isinstance(splits, tf.RaggedTensor):\n    splits = splits.row_splits()\n  return ops.gen_seq_mask(splits=splits, max_seq_length=max_seq_length)\n"
  },
  {
    "path": "monolith/native_training/gen_seq_mask_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nimport tensorflow as tf\n\nfrom monolith.native_training.gen_seq_mask import gen_seq_mask\n\n\nclass GenSeqMaskTest(tf.test.TestCase):\n\n  def test_gen_seq_mask_int32(self):\n    split = tf.constant([0, 5, 7, 9, 13], dtype=tf.int32)\n    mask = gen_seq_mask(split, 6)\n    result = tf.constant([[1, 1, 1, 1, 1, 0], [1, 1, 0, 0, 0, 0],\n                          [1, 1, 0, 0, 0, 0], [1, 1, 1, 1, 0, 0]],\n                         dtype=tf.int32)\n    self.assertAllEqual(mask, result)\n\n  def test_gen_seq_mask_int64(self):\n    split = tf.constant([0, 5, 7, 9, 13], dtype=tf.int64)\n    mask = gen_seq_mask(split, 6)\n    result = tf.constant([[1, 1, 1, 1, 1, 0], [1, 1, 0, 0, 0, 0],\n                          [1, 1, 0, 0, 0, 0], [1, 1, 1, 1, 0, 0]],\n                         dtype=tf.int64)\n    self.assertAllEqual(mask, result)\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/gflags_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl.flags import FlagValues\nfrom absl import logging, flags\nimport dataclasses\nfrom dataclasses import Field\nfrom enum import Enum\nimport inspect\nimport re\nimport sys\nfrom typing import get_type_hints, Iterable, Tuple, Dict\n\nFLAGS = flags.FLAGS\n\n_SPACE = re.compile(r\"\\s+\")\n_PARAM = re.compile(r\"^:param\\s+([a-zA-Z0-9._-]+)\\s*:\\s*(.*)\")\n\n\nclass Status(Enum):\n  Init = 1\n  Open = 2\n  Extend = 3\n  Closed = 4\n\n\ndef _extract_help_info(cls, help_info, is_nested):\n  if is_nested:\n    for base in cls.__bases__:\n      assert _extract_help_info(base, help_info, is_nested) == Status.Closed\n\n  doc = [\n      \" \".join(re.split(_SPACE, line.strip()))\n      for line in cls.__doc__.split('\\n')\n      if len(line.strip()) > 0\n  ]\n\n  key_stack = []\n  status = Status.Init\n  for i, line in enumerate(doc):\n    matched = _PARAM.match(line)\n    if matched:\n      new_key, info = matched.groups()\n      if status == Status.Init:\n        help_info[new_key] = [info]\n        key_stack.append(new_key)\n      elif status == Status.Open or status == Status.Extend:\n        old_key = key_stack.pop()\n        assert old_key != new_key\n        help_info[new_key] = [info]\n        key_stack.append(new_key)\n      else:\n        assert status == Status.Closed\n        break\n\n      # trans status\n      status = Status.Open\n    else:\n      if status == Status.Init:\n        pass\n      elif status == Status.Open:\n        key = key_stack[-1]\n        help_info[key].append(line)\n        status = Status.Extend\n      elif status == Status.Extend:\n        key = key_stack[-1]\n        help_info[key].append(line)\n      else:\n        assert status == Status.Closed\n        break\n\n    if i + 1 == len(doc):\n      status = Status.Closed\n\n  return status\n\n\ndef extract_help_info(cls, is_nested=True):\n  help_info = {}\n  status = _extract_help_info(cls, help_info, is_nested)\n  assert status == Status.Closed\n\n  return {key: \" \".join(value) for key, value in help_info.items()}\n\n\ndef extract_flags_decorator(remove_flags=None, is_nested=True):\n\n  def decorator(cls):\n    extract_flags(flags, cls, is_nested, remove_flags)\n    return cls\n\n  return decorator\n\n\ndef extract_flags(gflags, dcls, is_nested=True, skip_flags=None) -> FlagValues:\n  FLAGS = gflags.FLAGS\n  help_info = extract_help_info(dcls, is_nested)\n  skip_flags = set() if skip_flags is None else set(skip_flags)\n  for key, dtype in get_type_hints(dcls).items():\n    if key not in help_info.keys() or key in skip_flags:\n      continue\n    default = getattr(dcls, key)\n    help_str = \"default={}, {}\".format(default, help_info.get(key, \"\"))\n    try:\n      if dtype == int:\n        gflags.DEFINE_integer(key, default, \"{}, {}\".format('int', help_str))\n      elif dtype == bool:\n        gflags.DEFINE_bool(key, default, \"{}, {}\".format('bool', help_str))\n      elif dtype == str:\n        gflags.DEFINE_string(key, default, \"{}, {}\".format('string', help_str))\n      elif dtype == float:\n        gflags.DEFINE_float(key, default, \"{}, {}\".format('float', help_str))\n      elif issubclass(dtype, Enum):\n        default_value = default.name.lower()\n        enum_values = [name.lower() for name in dtype._member_names_]\n        gflags.DEFINE_enum(key, default_value, enum_values,\n                           \"{}, {}\".format('enum', help_str))\n      else:\n        raise ValueError(\"only <int|bool|str|float|enum> is support!\")\n    except:\n      pass\n\n  return FLAGS\n\n\ndef get_flags_parser(flags, FLAGS):\n\n  def flags_parser(args):\n    try:\n      return FLAGS(args)\n    except flags.Error as error:\n      logging.error('FATAL Flags parsing error: {}\\n{}'.format(\n          error, FLAGS.get_help(include_special_flags=False)))\n      logging.error('Pass --helpshort or --helpfull to see help on flags.\\n')\n      sys.exit(1)\n\n  return flags_parser\n\n\ndef update(config):\n  \"\"\"\n    update config's attr value using flags.FLAGS\n    if config's attr value is default value and FLAGS' attr value is not default\n\n    config: any type of Config like CpuTraingingConfig, DistributedCpuTrainingConfig\n    example: see gflags_utils_test.py test_update()\n  \"\"\"\n  FLAGS = flags.FLAGS\n  cls = config.__class__\n  for key, dtype in get_type_hints(cls).items():\n    tmp = getattr(cls, key)\n    if isinstance(tmp, Field):\n      field = tmp\n      default = field.default if field.default is not None else field.default_factory(\n      )\n    else:\n      default = tmp\n\n    from_code = config.__dict__.get(key, default)\n\n    try:\n      if not hasattr(FLAGS, key):\n        continue\n    except:\n      continue\n\n    if issubclass(dtype, Enum):\n      from_cmd = dtype[getattr(FLAGS, key).upper()]\n    else:\n      from_cmd = getattr(FLAGS, key)\n\n    if from_code == default and from_cmd != default:\n      # user has not set this field, it should not overwrite by cmd\n      config.__dict__[key] = from_cmd\n    else:\n      continue\n\n  return config\n\n\n@dataclasses.dataclass\nclass _MonolithGflagMeta:\n  # Link a field `name` to `flag`\n  linked_map: Dict[str, str] = dataclasses.field(default_factory=dict)\n\n\ndef _get_flag_obj(cls, set_if_not_exists=False) -> _MonolithGflagMeta:\n  attr = \"_monolith_gflag_meta\"\n  if not hasattr(cls, attr):\n    if set_if_not_exists:\n      setattr(cls, attr, _MonolithGflagMeta())\n    else:\n      return _MonolithGflagMeta()\n  return getattr(cls, attr)\n\n\nclass LinkDataclassToFlags:\n  \"\"\"Links a field's default value to a flag. Example:\n  \n  flags.DEFINE_int(\"c_value\", 0, \"\")\n\n  @LinkDataclassToFlags(linked_map={\"v\": \"v_value\"})\n  @dataclass.dataclasses\n  class C:\n    v: int = 0\n\n  When we instantiate C as c, if v_value is not 0, and c.v is the default value (which is 0 in this case),\n  c.v = FLAGS.v_value\n  \"\"\"\n\n  def __init__(self, linked_list=None, linked_map=None):\n    \"\"\"Elements `e` in linked_list is equivalent to `e:e` in the linekd_map \"\"\"\n    self._m = {}\n    self._m.update(linked_map or {})\n    linked_list = linked_list or []\n    for name in linked_list:\n      self._m[name] = name\n\n  def __call__(self, cls):\n\n    assert dataclasses.is_dataclass(\n        cls), \"LinkDataclassToFlag should be used on dataclasses\"\n\n    fields = dataclasses.fields(cls)\n    named_fields = {field.name: field for field in fields}\n\n    for name, flag in self._m.items():\n      if name not in named_fields:\n        raise ValueError(f\"{name} is not a valid attribute of {type(cls)}\")\n      if flag not in FLAGS:\n        raise ValueError(f\"{flag} is not defined in gflags\")\n\n    obj = _get_flag_obj(cls, set_if_not_exists=True)\n    for name, flag in self._m.items():\n      obj.linked_map[name] = flag\n\n    return cls\n\n\ndef _get_merged_meta(cls):\n  meta = _MonolithGflagMeta()\n  classes = inspect.getmro(cls)\n  for c in classes:\n    obj = _get_flag_obj(c)\n    meta.linked_map.update(obj.linked_map)\n\n  return meta\n\n\ndef update_by_flags(cls):\n  assert dataclasses.is_dataclass(\n      cls), \"update_by_flag should be used on dataclasses\"\n  orig_init = cls.__init__\n  fields = dataclasses.fields(cls)\n  named_fields = {field.name: field for field in fields}\n  del fields\n  meta = _get_merged_meta(cls)\n\n  def create_init(orig_init, named_fields, meta):\n\n    def __init__(self, *args, **kwargs):\n      orig_init(self, *args, **kwargs)\n      for name, flag in meta.linked_map.items():\n        if getattr(self, name) == named_fields[name].default and getattr(\n            FLAGS, flag) != FLAGS[flag].default:\n          setattr(self, name, getattr(FLAGS, flag))\n\n    return __init__\n\n  cls.__init__ = create_init(orig_init, named_fields, meta)\n  return cls\n"
  },
  {
    "path": "monolith/native_training/gflags_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import flags\nfrom absl.testing import absltest\nfrom typing import get_type_hints\nfrom enum import Enum\nimport dataclasses\n\nfrom monolith.native_training import gflags_utils as utils\n\nFLAGS = flags.FLAGS\n\n\n@dataclasses.dataclass\nclass TestConfig:\n  \"\"\"\n    :param test_int1: integer 1 for test\n    :param test_int2: integer 2 for test\n    :param test_str: string for test\n                        and test another line\n  \"\"\"\n  test_int1: int = 0\n  test_int2: int = 0\n  test_str: str = None\n\n\n@utils.extract_flags_decorator()\n@dataclasses.dataclass\nclass TestConfig2:\n  \"\"\"\n    :param testconfig2_int1: integer 1 for TestConfig2\n    :param testconfig2_str1: str 1 for TestConfig2\n  \"\"\"\n  testconfig2_int1: int = 1\n  testconfig2_str1: str = \"str1\"\n\n\n@utils.extract_flags_decorator({\"testconfig3_int1\"})\n@dataclasses.dataclass\nclass TestConfig3:\n  \"\"\"\n    :param testconfig3_int1: testconfig3_int1\n    :param testconfig3_int2: testconfig3_int2\n    :param testconfig3_str1: testconfig3_str1\n  \"\"\"\n  testconfig3_int1: int = 1\n  testconfig3_int2: int = 2\n  testconfig3_str1: str = \"str1\"\n\n\n@dataclasses.dataclass\nclass TestConfig4(TestConfig):\n  \"\"\"\n    :param testconfig4_int1: testconfig4_int1\n    :param testconfig4_str1: testconfig4_str1\n  \"\"\"\n  testconfig4_int1: int = 4\n  testconfig4_str1: str = \"testconfig4_str1\"\n\n\n@dataclasses.dataclass\nclass TestConfig5Base:\n  \"\"\"\n    :param testconfig5base_int1: testconfig5base_int1\n    :param testconfig5base_int2: testconfig5base_int2\n    :param testconfig5base_str: testconfig5base_str\n  \"\"\"\n  testconfig5base_int1: int = 0\n  testconfig5base_int2: int = 0\n  testconfig5base_str: str = None\n\n\n@utils.extract_flags_decorator(is_nested=False)\n@dataclasses.dataclass\nclass TestConfig5(TestConfig5Base):\n  \"\"\"\n    :param testconfig5_int1: testconfig5_int1\n    :param testconfig5_str1: testconfig5_str1\n  \"\"\"\n  testconfig5_int1: int = 5\n  testconfig5_str1: str = \"testconfig5_str1\"\n\n\nclass GflagUtilsTest(absltest.TestCase):\n\n  def _check_help_info(self, cls, skip_flags=set()):\n    help_info = utils.extract_help_info(cls)\n    for key, _ in get_type_hints(cls).items():\n      if key in skip_flags:\n        continue\n      self.assertIn(key, help_info,\n                    '{} is not in {}, please add a help info'.format(key, cls))\n\n  def test_extract_help_info(self):\n\n    res = utils.extract_help_info(TestConfig)\n    self.assertIn(\"test_int1\", res)\n    self.assertIn(\"test_int2\", res)\n    self.assertIn(\"test_str\", res)\n    self.assertEqual(\"integer 1 for test\", res[\"test_int1\"])\n    self.assertEqual(\"integer 2 for test\", res[\"test_int2\"])\n    self.assertEqual(\"string for test and test another line\", res[\"test_str\"])\n\n    res2 = utils.extract_help_info(TestConfig4, is_nested=False)\n    self.assertIn(\"testconfig4_int1\", res2)\n    self.assertIn(\"testconfig4_str1\", res2)\n    self.assertNotIn(\"test_int1\", res2)\n    self.assertNotIn(\"test_int2\", res2)\n    self.assertNotIn(\"test_str\", res2)\n\n  def test_update(self):\n    FLAGS = flags.FLAGS\n    flags.DEFINE_integer(\"test_int1\", 2, \"test int 1\")\n    flags.DEFINE_integer(\"test_int2\", 3, \"test int 2\")\n\n    config = TestConfig(\n        test_int1=1,\n        test_int2=0,\n    )\n    utils.update(config)\n    # will not update test_int1 because test_int1 in config is not default value.\n    # will update test_int2 because test_int2 in config is default value\n    #      and FLAGS.test_int2 is not default value.\n    self.assertEqual(config.test_int1, 1)  #not updated\n    self.assertEqual(config.test_int2, 3)  #updated\n    self.assertEqual(config.test_str, None)\n    # for test_str attr, no FLAGS is define, so nothing will happend\n\n  def test_extract_gflags_decorator(self):\n    FLAGS = flags.FLAGS\n    conf = TestConfig2(testconfig2_int1=2, testconfig2_str1=\"newstr1\")\n    self.assertEqual(FLAGS.testconfig2_int1, 1)\n    self.assertEqual(FLAGS.testconfig2_str1, \"str1\")\n    self.assertEqual(conf.testconfig2_int1, 2)\n    self.assertEqual(conf.testconfig2_str1, \"newstr1\")\n\n    conf3 = TestConfig3()\n    self.assertEqual(hasattr(FLAGS, \"testconfig3_int1\"), False)\n    self.assertEqual(hasattr(FLAGS, \"testconfig3_int2\"), True)\n    self.assertEqual(hasattr(FLAGS, \"testconfig3_str1\"), True)\n\n    self.assertEqual(hasattr(conf3, \"testconfig3_int1\"), True)\n    self.assertEqual(hasattr(conf3, \"testconfig3_int2\"), True)\n    self.assertEqual(hasattr(conf3, \"testconfig3_str1\"), True)\n\n    conf5 = TestConfig5()\n    self.assertEqual(hasattr(FLAGS, \"testconfig5_int1\"), True)\n    self.assertEqual(hasattr(FLAGS, \"testconfig5_str1\"), True)\n    self.assertEqual(hasattr(FLAGS, \"testconfig5base_int1\"), False)\n    self.assertEqual(hasattr(FLAGS, \"testconfig5base_int2\"), False)\n    self.assertEqual(hasattr(FLAGS, \"testconfig5base_str\"), False)\n\n    self.assertEqual(hasattr(conf5, \"testconfig5_int1\"), True)\n    self.assertEqual(hasattr(conf5, \"testconfig5_str1\"), True)\n    self.assertEqual(hasattr(conf5, \"testconfig5base_int1\"), True)\n    self.assertEqual(hasattr(conf5, \"testconfig5base_int2\"), True)\n    self.assertEqual(hasattr(conf5, \"testconfig5base_str\"), True)\n\n  flags.DEFINE_string(\"testflag6\", \"\", \"\")\n\n  @utils.update_by_flags\n  @utils.LinkDataclassToFlags(linked_list=[\"testflag6\"],\n                              linked_map={\"v\": \"testflag6\"})\n  @dataclasses.dataclass\n  class TestClass6:\n    v: str = None\n    testflag6: str = None\n\n  def test_link_flag(self):\n    FLAGS.testflag6 = \"\"\n    c = self.TestClass6()\n    self.assertEqual(c.v, None)\n    self.assertEqual(c.testflag6, None)\n\n    FLAGS.testflag6 = \"a\"\n    c = self.TestClass6()\n    self.assertEqual(c.v, \"a\")\n    self.assertEqual(c.testflag6, \"a\")\n\n    FLAGS.testflag6 = \"b\"\n    c = self.TestClass6(v=\"v\")\n    self.assertEqual(c.v, \"v\")\n    self.assertEqual(c.testflag6, \"b\")\n\n  flags.DEFINE_string(\"testflag7\", \"\", \"\")\n\n  @utils.LinkDataclassToFlags(linked_map={\"v\": \"testflag7\"})\n  @dataclasses.dataclass\n  class TestClass7Base:\n    v: str = None\n\n  @utils.update_by_flags\n  @dataclasses.dataclass\n  class TestClass7Inherit(TestClass7Base):\n    v2: str = \"v2\"\n\n  def test_link_flag_inheritance(self):\n    FLAGS.testflag7 = \"a\"\n    c = self.TestClass7Inherit()\n    self.assertEqual(c.v, \"a\")\n    self.assertEqual(c.v2, \"v2\")\n\n\nif __name__ == \"__main__\":\n  absltest.main()\n"
  },
  {
    "path": "monolith/native_training/graph_meta.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom typing import Callable\n\nimport tensorflow as tf\n\n_MONOLITH_GRAPH_META = \"monolith_graph_meta\"\n\n\ndef get_meta(key: str, MetaFactory: Callable):\n  g = tf.compat.v1.get_default_graph()\n  l = g.get_collection_ref(_MONOLITH_GRAPH_META)\n  if not l:\n    l.append({})\n  meta_dict = l[0]\n  if key not in meta_dict:\n    meta_dict[key] = MetaFactory()\n  return meta_dict[key]\n"
  },
  {
    "path": "monolith/native_training/graph_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\n\ndef add_batch_norm_into_update_ops():\n  ops = tf.compat.v1.get_default_graph().get_operations()\n  update_ops = [\n      op for op in ops\n      if 'AssignMovingAvg' in op.name and op.type == \"AssignSubVariableOp\"\n  ]\n\n  for update_op in update_ops:\n    tf.compat.v1.add_to_collection(tf.compat.v1.GraphKeys.UPDATE_OPS, update_op)\n"
  },
  {
    "path": "monolith/native_training/hash_filter_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nfrom contextlib import nullcontext\nimport os\nfrom typing import List\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\n\nfrom monolith.native_training import basic_restore_hook\nfrom monolith.native_training import save_utils\nfrom monolith.native_training import utils\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\nfrom monolith.utils import get_libops_path\nfrom monolith.native_training.model_export.export_context import is_exporting_standalone\n\nHASH_FILTER_CAPACITY = 300000000\nHASH_FILTER_SPLIT_NUM = 7\n\nfilter_ops = gen_monolith_ops\n\nfilter_save_op = gen_monolith_ops\n\nfilter_restore_op = gen_monolith_ops\n\n_TIMEOUT_IN_MS = 30 * 60 * 1000\n\n\nclass FilterType(object):\n\n  SLIDING_HASH_FILTER = 'sliding_hash_filter'\n  PROBABILISTIC_FILTER = 'probabilistic_filter'\n  NO_FILTER = 'no_filter'\n\n\ndef create_hash_filter(capacity: int,\n                       split_num: int,\n                       config: bytes = b\"\",\n                       name_suffix: str = \"\") -> tf.Tensor:\n  \"\"\"Creates a hash filter\"\"\"\n  return filter_ops.MonolithHashFilter(capacity=capacity,\n                                       split_num=split_num,\n                                       config=config,\n                                       shared_name=\"MonolithHashFilter\" +\n                                       name_suffix)\n\n\ndef create_probabilistic_filter(equal_probability,\n                                config: bytes = b\"\",\n                                name_suffix: str = \"\") -> tf.Tensor:\n  \"\"\"Creates a probabilistic filter\"\"\"\n  return filter_ops.MonolithProbabilisticFilter(\n      equal_probability=equal_probability,\n      config=config,\n      shared_name=\"MonolithProbabilisticFilter\" + name_suffix)\n\n\ndef create_dummy_hash_filter(name_suffix: str = \"0\") -> tf.Tensor:\n  \"\"\"Creates a dummy hash filter\"\"\"\n  return filter_ops.MonolithDummyHashFilter(shared_name=\"DummyHashFilter\" +\n                                            name_suffix)\n\n\ndef _create_hash_filter(\n    enable_hash_filter: bool,\n    config: bytes = b\"\",\n    name_suffix: str = \"\",\n    filter_capacity: int = HASH_FILTER_CAPACITY,\n    filter_split_num: int = HASH_FILTER_SPLIT_NUM,\n    filter_equal_probability: bool = False,\n    filter_type: FilterType = FilterType.SLIDING_HASH_FILTER) -> tf.Tensor:\n  if enable_hash_filter is True:\n    if filter_type == FilterType.SLIDING_HASH_FILTER:\n      return create_hash_filter(filter_capacity, filter_split_num, config,\n                                name_suffix)\n    elif filter_type == FilterType.PROBABILISTIC_FILTER:\n      return create_probabilistic_filter(filter_equal_probability, config,\n                                         name_suffix)\n    elif filter_type == FilterType.NO_FILTER:\n      return create_dummy_hash_filter(name_suffix)\n    else:\n      raise ValueError(\"Invalid filter type, please investigate and retry!\")\n  else:\n    return create_dummy_hash_filter(name_suffix)\n\n\ndef create_hash_filters(\n    ps_num: int,\n    enable_hash_filter: bool,\n    config: bytes = b\"\",\n    filter_capacity: int = HASH_FILTER_CAPACITY,\n    filter_split_num: int = HASH_FILTER_SPLIT_NUM,\n    filter_equal_probability: bool = False,\n    filter_type: FilterType = FilterType.SLIDING_HASH_FILTER\n) -> List[tf.Tensor]:\n  logging.info(\n      \"Create hash fitlers, enable_hash_filter:{}.\".format(enable_hash_filter))\n  if ps_num == 0:\n    return [\n        _create_hash_filter(enable_hash_filter,\n                            config,\n                            \"\",\n                            filter_capacity,\n                            filter_split_num,\n                            filter_equal_probability=filter_equal_probability,\n                            filter_type=filter_type)\n    ]\n  else:\n    hash_filters = []\n    for i in range(ps_num):\n      ps_device_name = utils.ps_device(i)\n      with nullcontext() if is_exporting_standalone() else tf.device(\n          ps_device_name):\n        hash_filters.append(\n            _create_hash_filter(\n                enable_hash_filter,\n                config,\n                \"_\" + str(i),\n                filter_capacity,\n                filter_split_num,\n                filter_equal_probability=filter_equal_probability,\n                filter_type=filter_type))\n    return hash_filters\n\n\ndef save_hash_filter(hash_filter: tf.Tensor,\n                     hash_filter_basename: tf.Tensor,\n                     enable_hash_filter: bool = False) -> tf.Operation:\n  if enable_hash_filter is True:\n    return filter_save_op.monolith_hash_filter_save(hash_filter,\n                                                    hash_filter_basename)\n  else:\n    return tf.no_op()\n\n\ndef restore_hash_filter(hash_filter: tf.Tensor,\n                        hash_filter_base_name: tf.Tensor,\n                        enable_hash_filter: bool = False) -> tf.Operation:\n  if enable_hash_filter is True:\n    return filter_restore_op.monolith_hash_filter_restore(\n        hash_filter, hash_filter_base_name)\n  else:\n    return tf.no_op()\n\n\ndef intercept_gradient(filter_tensor: tf.Tensor, ids: tf.Tensor,\n                       embeddings: tf.Tensor):\n  \"\"\"\n  If id is supposed to be filtered, the gradient will be intercepted. Output the same \n  embeddings.\n  Args:\n    ids - a 1-D int64 tensor.\n    embeddings - a N-d embedding tensor whose the first dimension is corresponding to ids. \n  \"\"\"\n  return filter_ops.MonolithHashFilterInterceptGradient(\n      filter_handle=filter_tensor, ids=ids, embeddings=embeddings)\n\n\nclass HashFilterCheckpointSaverListener(tf.estimator.CheckpointSaverListener):\n  \"\"\"\n  Saves the hash filters when saver is run.\n  \"\"\"\n\n  def __init__(self,\n               basename: str,\n               hash_filters: [tf.Tensor],\n               enable_hash_filter: bool = False,\n               enable_save_restore: bool = True):\n    \"\"\"\n    |basename| should be a file name which is same as what is passed to saver.\n    |hash_filters| hash filters to save in checkpoint.\n    |enable_hash_filter| whether use real hash filters. If true, will save\n                         hash filters in checkpoint. If false, will skip save\n                         logic internally.\n    enable_hash_filter: TODO(zouxuan) Whether to use save and restore on the\n                        hash filter.\n                        Hash filter is broken for save restore during sync\n                        training right now.\n    \"\"\"\n    super().__init__()\n    self._helper = save_utils.SaveHelper(basename)\n    self._hash_filters = hash_filters\n    self._enable_hash_filter = enable_hash_filter\n    self._enable_save_restore = enable_save_restore\n    self._hash_filter_id_to_placeholder = {}\n    self._save_op = self._build_save_graph()\n\n  def before_save(self, sess, global_step_value):\n    \"\"\"\n    We use before save so the checkpoint file is updated after we successfully\n    save the hash filter.\n    \"\"\"\n    if self._enable_hash_filter is False or self._enable_save_restore is False:\n      return\n\n    feed_dict = {}\n    hash_filter_names = []\n    asset_dir = self._helper.get_ckpt_asset_dir(\n        self._helper.get_ckpt_prefix(global_step_value))\n    tf.io.gfile.makedirs(asset_dir)\n    for ps_idx, hash_filter in enumerate(self._hash_filters):\n      hash_filter_basename = asset_dir + \"hash_filter_{}\".format(ps_idx)\n      hash_filter_names.append(hash_filter_basename)\n      feed_dict.update({\n          self._hash_filter_id_to_placeholder[id(hash_filter)]:\n              hash_filter_basename\n      })\n\n    sess.run(self._save_op,\n             feed_dict=feed_dict,\n             options=tf.compat.v1.RunOptions(timeout_in_ms=_TIMEOUT_IN_MS))\n    logging.info(\"Finished saving hash filters.\")\n\n  def _build_save_graph(self) -> tf.Operation:\n    if self._enable_hash_filter is False or self._enable_save_restore is False:\n      return tf.no_op()\n\n    last_op = tf.no_op()\n    for ps_idx, hash_filter in enumerate(self._hash_filters):\n      hash_filter_basename = tf.compat.v1.placeholder(tf.string, shape=[])\n      self._hash_filter_id_to_placeholder.update(\n          {id(hash_filter): hash_filter_basename})\n      with tf.control_dependencies([last_op]):\n        last_op = save_hash_filter(hash_filter, hash_filter_basename, True)\n    return last_op\n\n\nclass HashFilterCheckpointRestorerListener(\n    basic_restore_hook.CheckpointRestorerListener):\n  \"\"\"Restores the hash filters from basename\"\"\"\n\n  def __init__(self,\n               basename: str,\n               hash_filters: [tf.Tensor],\n               enable_hash_filter: bool = False,\n               enable_save_restore: bool = True):\n    \"\"\"\n    |basename| should be a file name which is same as what is passed to saver.\n    |hash_filters| hash filters to save in checkpoint.\n    |enable_hash_filter| whether use real hash filters. If true, will save\n                         hash filters in checkpoint. If false, will skip save\n                         logic internally.\n    enable_hash_filter: TODO(zouxuan) Whether to use save and restore on the\n                        hash filter.\n                        Hash filter is broken for save restore during sync\n                        training right now.\n    \"\"\"\n    super().__init__()\n    self._basename = basename\n    self._helper = save_utils.SaveHelper(self._basename)\n    self._hash_filters = hash_filters\n    self._enable_hash_filter = enable_hash_filter\n    self._enable_save_restore = enable_save_restore\n    self._hash_filter_id_to_placeholder = {}\n    self._restore_op = self._build_restore_graph()\n\n  def before_restore(self, session):\n    \"\"\"\n    We use before restore so as to strictly control the order of restorer listeners.\n\n    \"\"\"\n    ckpt_prefix = tf.train.latest_checkpoint(os.path.dirname(self._basename))\n    if not ckpt_prefix:\n      logging.info(\"No checkpoint found in %s. Skip the hash filters restore.\",\n                   self._basename)\n      return\n    logging.info(\"Restore hash filter from %s\", ckpt_prefix)\n    asset_dir = self._helper.get_ckpt_asset_dir(ckpt_prefix)\n    if tf.io.gfile.exists(asset_dir):\n      self._restore_from_path_prefix(session, asset_dir)\n    else:\n      # This is the legacy behavior and should be removed soon.\n      self._restore_from_path_prefix(session, ckpt_prefix)\n\n  def _restore_from_path_prefix(self, sess, path_prefix):\n    if self._enable_hash_filter is False or self._enable_save_restore is False:\n      return\n\n    feed_dict = {}\n    hash_filter_names = []\n    for ps_idx, hash_filter in enumerate(self._hash_filters):\n      hash_filter_basename = path_prefix + \"hash_filter_{}\".format(ps_idx)\n      hash_filter_names.append(hash_filter_basename)\n      feed_dict.update({\n          self._hash_filter_id_to_placeholder[id(hash_filter)]:\n              hash_filter_basename\n      })\n\n    sess.run(self._restore_op,\n             feed_dict=feed_dict,\n             options=tf.compat.v1.RunOptions(timeout_in_ms=_TIMEOUT_IN_MS))\n\n  def _build_restore_graph(self) -> tf.Operation:\n    if self._enable_hash_filter is False or self._enable_save_restore is False:\n      return tf.no_op()\n\n    restore_ops = []\n    for ps_idx, hash_filter in enumerate(self._hash_filters):\n      hash_filter_basename = tf.compat.v1.placeholder(tf.string, shape=[])\n      self._hash_filter_id_to_placeholder.update(\n          {id(hash_filter): hash_filter_basename})\n      restore_ops.append(\n          restore_hash_filter(hash_filter, hash_filter_basename, True))\n    return tf.group(restore_ops)\n\n\n@ops.RegisterGradient(\"MonolithHashFilterInterceptGradient\")\ndef _intercept_gradient_gradient(op: tf.Operation, grad: tf.Tensor):\n  filter_tensor = op.inputs[0]\n  ids = op.inputs[1]\n  filtered_grad = filter_ops.MonolithHashFilterInterceptGradientGradient(\n      filter_handle=filter_tensor, ids=ids, grad=grad)\n  return None, None, filtered_grad\n"
  },
  {
    "path": "monolith/native_training/hash_filter_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nfrom typing import List\n\nimport tensorflow as tf\nfrom tensorflow.python.lib.io import tf_record\n\nimport monolith.native_training.hash_filter_ops as ops\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\n\ndef get_config_str(occurrence_threshold=0):\n  config = embedding_hash_table_pb2.SlotOccurrenceThresholdConfig()\n  config.default_occurrence_threshold = occurrence_threshold\n  return config.SerializeToString()\n\n\nclass HashFilterOpsTest(tf.test.TestCase):\n\n  def _count_files(self, basename: str):\n    return len(tf.io.gfile.glob(basename + \"*\"))\n\n  def _GetHashFilterSplitMetaDump(self, ckpt_file: str):\n    for record in tf_record.tf_record_iterator(ckpt_file):\n      return embedding_hash_table_pb2.HashFilterSplitMetaDump.FromString(record)\n    return None\n\n  def test_hash_filter_basic(self):\n    config = get_config_str(3)\n    hash_filter = ops.create_hash_filter(100, 7, config)\n    # we choose a key that is unique enough so they won't collide with each other.\n    ids = tf.constant([1, 3 << 17, 1], dtype=tf.int64)\n    embedding = tf.zeros([3, 2])\n    loss = ops.intercept_gradient(hash_filter, ids, embedding)\n    grad = tf.gradients(loss, embedding)[0]\n    with self.session() as sess:\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[0, 0], [0, 0], [0, 0]])\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[0, 0], [0, 0], [1, 1]])\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[1, 1], [0, 0], [1, 1]])\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[1, 1], [1, 1], [1, 1]])\n\n  def test_hash_filter_save_restore(self):\n    config = get_config_str(3)\n    hash_filter = ops.create_hash_filter(100, 7, config)\n    # we choose a key that is unique enough so they won't collide with each other.\n    ids = tf.constant([1, 3 << 17, 1], dtype=tf.int64)\n    embedding = tf.zeros([3, 2])\n    loss = ops.intercept_gradient(hash_filter, ids, embedding)\n    grad = tf.gradients(loss, embedding)[0]\n    base_folder = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                               \"test_hash_filter_save_restore\")\n    with self.session() as sess:\n      # save checkpoint 0\n      ckpt_basename_0 = os.path.join(base_folder, \"hash_filter_test_0\")\n      hash_filter_save_op = ops.save_hash_filter(hash_filter, ckpt_basename_0,\n                                                 True)\n      sess.run(hash_filter_save_op)\n      self.assertEqual(self._count_files(ckpt_basename_0), 7)\n\n      # restore checkpoint 0\n      hash_filter_restore_op = ops.restore_hash_filter(hash_filter,\n                                                       ckpt_basename_0, True)\n      sess.run(hash_filter_restore_op)\n\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[0, 0], [0, 0], [0, 0]])\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[0, 0], [0, 0], [1, 1]])\n\n      # save checkpoint 1\n      ckpt_basename_1 = os.path.join(base_folder, \"hash_filter_test_1\")\n      hash_filter_save_op = ops.save_hash_filter(hash_filter, ckpt_basename_1,\n                                                 True)\n      sess.run(hash_filter_save_op)\n      files = sorted(tf.io.gfile.glob(ckpt_basename_1 + \"*\"))\n      self.assertEqual(self._count_files(ckpt_basename_1), 7)\n      # restore checkpoint 1\n      hash_filter_restore_op = ops.restore_hash_filter(hash_filter,\n                                                       ckpt_basename_1, True)\n      sess.run(hash_filter_restore_op)\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[1, 1], [0, 0], [1, 1]])\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[1, 1], [1, 1], [1, 1]])\n      ckpt_basename = os.path.join(base_folder, \"hash_filter_test\")\n\n  def test_hash_filter_save_restore_across_multiple_filters(self):\n    config = get_config_str(2)\n\n    # Each hash filter contains up to 2 elements.\n    hash_filter = ops.create_hash_filter(300, 100, config)\n    # we choose a key that is unique enough so they won't collide with each other.\n    ids = tf.constant([1, 1 << 17, 2 << 17, 1], dtype=tf.int64)\n    embedding = tf.zeros([4, 2])\n    loss = ops.intercept_gradient(hash_filter, ids, embedding)\n    grad = tf.gradients(loss, embedding)[0]\n    base_folder = os.path.join(\n        os.environ[\"TEST_TMPDIR\"],\n        \"test_hash_filter_save_restore_across_multiple_filters\")\n    with self.session() as sess:\n      # save checkpoint 0\n      ckpt_basename_0 = os.path.join(base_folder, \"hash_filter_test_0\")\n      hash_filter_save_op = ops.save_hash_filter(hash_filter, ckpt_basename_0,\n                                                 True)\n      sess.run(hash_filter_save_op)\n\n      # Verify checkpoint content\n      ckpt_0_files = sorted(tf.io.gfile.glob(ckpt_basename_0 + \"*\"))\n      self.assertEqual(len(ckpt_0_files), 100)\n      for file in ckpt_0_files:\n        dump = self._GetHashFilterSplitMetaDump(file)\n        self.assertEqual(dump.total_size, 3)\n        self.assertEqual(dump.num_elements, 0)\n        self.assertEqual(dump.sliding_hash_filter_meta.split_num, 100)\n        self.assertEqual(dump.sliding_hash_filter_meta.head, 0)\n        self.assertEqual(dump.sliding_hash_filter_meta.head_increment, 0)\n\n      # restore checkpoint 0\n      hash_filter_restore_op = ops.restore_hash_filter(hash_filter,\n                                                       ckpt_basename_0, True)\n      sess.run(hash_filter_restore_op)\n\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[0, 0], [0, 0], [0, 0], [0, 0]])\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[1, 1], [0, 0], [0, 0], [1, 1]])\n\n      # save checkpoint 1\n      ckpt_basename_1 = os.path.join(base_folder, \"hash_filter_test_1\")\n      hash_filter_save_op = ops.save_hash_filter(hash_filter, ckpt_basename_1,\n                                                 True)\n      sess.run(hash_filter_save_op)\n\n      # verify checkpoint 1\n      ckpt_1_files = sorted(tf.io.gfile.glob(ckpt_basename_1 + \"*\"))\n      self.assertEqual(len(ckpt_1_files), 100)\n      for file in ckpt_1_files[:4]:\n        dump = self._GetHashFilterSplitMetaDump(file)\n        self.assertEqual(dump.total_size, 3)\n        self.assertEqual(dump.num_elements, 2)\n        self.assertEqual(dump.sliding_hash_filter_meta.split_num, 100)\n        self.assertEqual(dump.sliding_hash_filter_meta.head, 4)\n        self.assertEqual(dump.sliding_hash_filter_meta.head_increment, 4)\n      for file in ckpt_1_files[4:]:\n        dump = self._GetHashFilterSplitMetaDump(file)\n        self.assertEqual(dump.total_size, 3)\n        self.assertEqual(dump.num_elements, 0)\n        self.assertEqual(dump.sliding_hash_filter_meta.split_num, 100)\n        self.assertEqual(dump.sliding_hash_filter_meta.head, 4)\n        self.assertEqual(dump.sliding_hash_filter_meta.head_increment, 4)\n\n      # restore checkpoint 1\n      hash_filter_restore_op = ops.restore_hash_filter(hash_filter,\n                                                       ckpt_basename_1, True)\n      sess.run(hash_filter_restore_op)\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[1, 1], [1, 1], [1, 1], [1, 1]])\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[1, 1], [1, 1], [1, 1], [1, 1]])\n\n  def test_dummy_hash_filter_basic(self):\n    hash_filter = ops.create_dummy_hash_filter()\n    # we choose a key that is unique enough so they won't collide with each other.\n    ids = tf.constant([1, 3 << 17, 1], dtype=tf.int64)\n    embedding = tf.zeros([3, 2])\n    loss = ops.intercept_gradient(hash_filter, ids, embedding)\n    grad = tf.gradients(loss, embedding)[0]\n    with self.session() as sess:\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[1, 1], [1, 1], [1, 1]])\n\n  def test_dummy_hash_filter_save_restore(self):\n    basename = \"dummy_hash_filter\"\n    hash_filter = ops.create_dummy_hash_filter()\n    # we choose a key that is unique enough so they won't collide with each other.\n    ids = tf.constant([1, 3 << 17, 1], dtype=tf.int64)\n    embedding = tf.zeros([3, 2])\n    loss = ops.intercept_gradient(hash_filter, ids, embedding)\n    grad = tf.gradients(loss, embedding)[0]\n    with self.session() as sess:\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[1, 1], [1, 1], [1, 1]])\n      hash_filter_save_op = ops.save_hash_filter(hash_filter, basename, False)\n      sess.run(hash_filter_save_op)\n      self.assertEqual(self._count_files(basename), 0)\n      grad_value = sess.run(grad)\n      self.assertAllEqual(grad_value, [[1, 1], [1, 1], [1, 1]])\n      hash_filter_restore_op = ops.restore_hash_filter(hash_filter, basename,\n                                                       False)\n      hash_filter_restore_op = ops.restore_hash_filter(hash_filter, basename,\n                                                       False)\n      self.assertAllEqual(grad_value, [[1, 1], [1, 1], [1, 1]])\n      self.assertEqual(self._count_files(basename), 0)\n\n  def test_restore_not_found(self):\n    with self.session() as sess:\n      non_existent_files = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                                        \"test_restore_not_found\",\n                                        \"hash_filters\")\n      config = get_config_str(2)\n      hash_filter = ops.create_hash_filter(300, 7, config)\n      restore_op = ops.restore_hash_filter(hash_filter, non_existent_files,\n                                           True)\n      with self.assertRaises(Exception):\n        sess.run(restore_op)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hash_table_ops.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax=\"proto2\";\n\npackage monolith;\n\nmessage HashTableProto {\n  enum OptionalBool {\n    kBoolNone = -1;\n    kFalse = 0;\n    kTrue = 1;\n  };\n  optional string table_tensor = 1;\n  optional int32 dim_size = 2;\n  optional string shared_name = 3;\n  optional bytes slot_expire_time_config = 4;\n  optional string learning_rate_tensor = 5;\n  optional int32 saver_parallel = 6;\n  repeated string extra_restore_names = 7;\n  optional OptionalBool export_share_embedding = 8;\n}\n"
  },
  {
    "path": "monolith/native_training/hash_table_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport abc\nimport copy\nimport concurrent.futures\nimport dataclasses\nimport hashlib\nimport os\nimport threading\nfrom typing import Tuple, Union, Dict, List\nfrom collections import defaultdict\n\nfrom absl import logging\nfrom google.protobuf import text_format\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\n\nfrom monolith.native_training import basic_restore_hook\nfrom monolith.native_training import entry\nfrom monolith.native_training import hash_filter_ops\nfrom monolith.native_training import distributed_serving_ops\nfrom monolith.native_training import graph_meta\nfrom monolith.native_training import hash_table_ops_pb2\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\nfrom monolith.native_training import save_utils\nfrom monolith.native_training.hash_table_utils import infer_dim_size\nfrom monolith.utils import get_libops_path\nfrom monolith.native_training.model_export.export_context import \\\n    is_exporting\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\nhash_table_ops = gen_monolith_ops\n\n_TIMEOUT_IN_MS = 60 * 60 * 1000\n\n\nclass BaseHashTable(abc.ABC):\n  \"\"\"\n  The base class for the hash table.\n\n  For the write operation, it will return a new HashTable. This makes it easier\n  to chain operations that need to use the updated tables. User can use this\n  behavior to balance the parallelism and the data freshness.\n  \"\"\"\n\n  @abc.abstractmethod\n  def assign(self,\n             ids: tf.Tensor,\n             values: tf.Tensor,\n             req_time: tf.Tensor = None) -> \"BaseHashTable\":\n    \"\"\"\n    Assign values to |id| entry in hash table.\n    ids - a 1D tensor represents which entry should be added by value\n    values - a 2D tensor. The first dim should equal to ids's length, the second dim should\n    equal to hash_table's dim size.\n\n    Returns updated hash table.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def assign_add(self,\n                 ids: tf.Tensor,\n                 values: tf.Tensor,\n                 req_time: tf.Tensor = None) -> \"BaseHashTable\":\n    \"\"\"\n    Assign add values to |id| entry in hash table.\n    ids - a 1D tensor represents which entry should be added by value\n    values - a 2D tensor. The first dim should equal to ids's length, the second dim should\n    equal to hash_table's dim size.\n\n    Returns updated hash table.\n    \"\"\"\n    pass\n\n  @abc.abstractmethod\n  def lookup(self,\n             ids: tf.Tensor,\n             use_multi_threads=False,\n             enable_dedup=False) -> tf.Tensor:\n    \"\"\"\n    Look up the embeddings in hash table. The embedding will be summed up in the same batch.\n\n    ids - a 1D int64 tensor\n\n    use_multi_threads - True if the caller wants to lookup using multi-threads.\n\n    enable_dedup - True if the caller wants to lookup without duplicate ids\n\n    Returns a 2-D tensor which maps id to embeddings.\n    \"\"\"\n    pass\n\n  @property\n  @abc.abstractmethod\n  def dim_size(self):\n    pass\n\n  @abc.abstractmethod\n  def apply_gradients(self,\n                      ids: tf.Tensor,\n                      grads: tf.Tensor,\n                      global_step: tf.Tensor,\n                      use_multi_threads=False,\n                      enable_dedup=False,\n                      req_time: tf.Tensor = None) -> \"BaseHashTable\":\n    \"\"\"Applies the gradients with respect to the ids.\"\"\"\n    pass\n\n  @abc.abstractmethod\n  def as_op(self) -> Union[tf.Tensor, tf.Operation]:\n    \"\"\"\n    Convert hash table to an op or tensor. Useful to do the dependency control.\n    \"\"\"\n    pass\n\n\n_HASH_TABLE_GRAPH_KEY = \"monolith_hash_tables\"\n\n\n@dataclasses.dataclass\nclass HashTableMetadata:\n  name_set: set = dataclasses.field(default_factory=set)\n  tensor_table_to_obj_dict: Dict = dataclasses.field(default_factory=dict)\n\n\n_BOOL_MAP = {\n    None: hash_table_ops_pb2.HashTableProto.kBoolNone,\n    False: hash_table_ops_pb2.HashTableProto.kFalse,\n    True: hash_table_ops_pb2.HashTableProto.kTrue,\n}\n\n_BOOL_REVERSE_MAP = {v: k for k, v in _BOOL_MAP.items()}\n\n\nclass HashTable(BaseHashTable):\n  \"\"\"\n  It maps a int64 to a float32 embedding.\n  \"\"\"\n\n  def __init__(self,\n               table: tf.Tensor = None,\n               shared_name: str = None,\n               dim_size: int = None,\n               slot_expire_time_config: bytes = None,\n               learning_rate_tensor: tf.Tensor = None,\n               saver_parallel: int = -1,\n               extra_restore_names=None,\n               table_proto=None,\n               import_scope=None):\n    if table_proto is not None:\n      self._init_from_proto(table_proto, import_scope)\n      return\n    self._table = table\n    self._dim_size = dim_size\n    self._init_table_name = shared_name\n    self._check_and_insert_name(shared_name)\n    self._slot_expire_time_config = slot_expire_time_config\n    self._learning_rate_tensor = learning_rate_tensor\n    self._saver_parallel = saver_parallel\n    self._extra_restore_names = extra_restore_names or []\n    self.export_share_embedding = None\n    ops.get_collection_ref(_HASH_TABLE_GRAPH_KEY).append(self)\n\n  def _init_from_proto(self,\n                       proto: hash_table_ops_pb2.HashTableProto = None,\n                       import_scope: str = None):\n    g = tf.compat.v1.get_default_graph()\n    self._table = g.as_graph_element(\n        ops.prepend_name_scope(proto.table_tensor, import_scope))\n    self._dim_size = proto.dim_size\n    self._init_table_name = proto.shared_name\n    self._slot_expire_time_config = proto.slot_expire_time_config\n    self._learning_rate_tensor = g.as_graph_element(\n        ops.prepend_name_scope(proto.learning_rate_tensor, import_scope))\n    self._saver_parallel = proto.saver_parallel\n    self._extra_restore_names = tuple(proto.extra_restore_names)\n    self.export_share_embedding = _BOOL_REVERSE_MAP[\n        proto.export_share_embedding]\n\n  def to_proto(self, export_scope=None):\n    if (export_scope is not None and\n        not self._table.name.startswith(export_scope)):\n      return None\n    proto = hash_table_ops_pb2.HashTableProto()\n    proto.table_tensor = ops.strip_name_scope(self._table.name, export_scope)\n    proto.dim_size = self._dim_size\n    proto.shared_name = self._init_table_name\n    proto.slot_expire_time_config = self._slot_expire_time_config\n    proto.learning_rate_tensor = ops.strip_name_scope(\n        self._learning_rate_tensor.name, export_scope)\n    proto.saver_parallel = self._saver_parallel\n    proto.extra_restore_names.extend(self._extra_restore_names)\n    proto.export_share_embedding = _BOOL_MAP[self.export_share_embedding]\n    return proto\n\n  @staticmethod\n  def from_proto(table_proto, import_scope=None):\n    return HashTable(table_proto=table_proto, import_scope=import_scope)\n\n  @classmethod\n  def get_metadata(cls) -> HashTableMetadata:\n    return graph_meta.get_meta(\"hash_table_metadata\", HashTableMetadata)\n\n  @classmethod\n  def _check_and_insert_name(cls, name):\n    meta = cls.get_metadata()\n    if name in meta.name_set:\n      raise ValueError(\"shared_name {} has already been used.\".format(name))\n    meta.name_set.add(name)\n\n  @property\n  def table(self):\n    \"\"\"Returns table tensor.\"\"\"\n    return self._table\n\n  @property\n  def name(self):\n    \"\"\"Return table name.\"\"\"\n    return self._init_table_name\n\n  @property\n  def extra_restore_names(self):\n    \"\"\"Returns other possible original table names.\"\"\"\n    return self._extra_restore_names\n\n  @property\n  def dim_size(self):\n    \"\"\"Return dim size.\"\"\"\n    return self._dim_size\n\n  \"\"\"Implements BaseHashTable\"\"\"\n\n  def assign(self,\n             ids: tf.Tensor,\n             values: tf.Tensor,\n             req_time: tf.Tensor = None) -> \"HashTable\":\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    # Makes test easier\n    ids = tf.convert_to_tensor(ids, tf.int64)\n    values = tf.convert_to_tensor(values, tf.float32)\n    return self._copy_with_new_table(\n        hash_table_ops.monolith_hash_table_assign(self._table, ids, values,\n                                                  req_time))\n\n  def assign_add(self,\n                 ids: tf.Tensor,\n                 values: tf.Tensor,\n                 req_time: tf.Tensor = None) -> \"HashTable\":\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    return self._copy_with_new_table(\n        hash_table_ops.monolith_hash_table_assign_add(self._table, ids, values,\n                                                      req_time))\n\n  def lookup(self,\n             ids: tf.Tensor,\n             use_multi_threads=False,\n             enable_dedup=False) -> tf.Tensor:\n    lookup_tensor = hash_table_ops.monolith_hash_table_lookup(\n        self._table, ids, self._dim_size, use_multi_threads=use_multi_threads)\n    return lookup_tensor\n\n  def lookup_entry(self, ids: tf.Tensor) -> tf.Tensor:\n    lookup_tensor = hash_table_ops.monolith_hash_table_lookup_entry(\n        self._table, ids)\n    return lookup_tensor\n\n  def apply_gradients(self,\n                      ids: tf.Tensor,\n                      grads: tf.Tensor,\n                      global_step: tf.Tensor,\n                      use_multi_threads=False,\n                      enable_dedup=False,\n                      req_time: tf.Tensor = None) -> \"HashTable\":\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    updated_op = hash_table_ops.monolith_hash_table_optimize(\n        self._table,\n        ids,\n        grads,\n        self._learning_rate_tensor,\n        req_time,\n        global_step,\n        use_multi_threads=use_multi_threads,\n        enable_dedup=enable_dedup)\n    with tf.control_dependencies([updated_op]):\n      new_table = self._copy_with_new_table(tf.identity(self._table))\n    return new_table\n\n  def as_op(self):\n    return self._table\n\n  def table_update(self, update_op: tf.Tensor) -> \"HashTable\":\n    with tf.control_dependencies([update_op]):\n      new_table = self._copy_with_new_table(tf.identity(self._table))\n    return new_table\n\n  def save(self, basename: tf.Tensor, random_sleep_ms: int = 0) -> \"HashTable\":\n    new_table = hash_table_ops.monolith_hash_table_save(\n        self._table,\n        basename,\n        slot_expire_time_config=self._slot_expire_time_config,\n        nshards=self._saver_parallel,\n        random_sleep_ms=random_sleep_ms)\n    return self._copy_with_new_table(new_table)\n\n  def restore(self, basename: tf.Tensor) -> \"HashTable\":\n    new_table = hash_table_ops.monolith_hash_table_restore(\n        self._table, basename)\n    return self._copy_with_new_table(new_table)\n\n  def _copy_with_new_table(self, new_table: tf.Tensor):\n    copied = copy.copy(self)\n    copied.__dict__[\"_table\"] = new_table\n    return copied\n\n  def size(self) -> tf.Tensor:\n    return hash_table_ops.monolith_hash_table_size(self._table)\n\n  def save_as_tensor(self, shard_idx, num_shards, limit,\n                     offset) -> Tuple[tf.Tensor, tf.Tensor]:\n    \"\"\"\n    Dumps the hash table as tensors.\n    Args:\n    shard_idx - the idx of shard, should be within [0, num_shards)\n    num_shards - the number of shards we want to have. This is helpful for dumping tensor\n    in parallel.\n    limit - at most, how many tensors will be output. If the output dump tensor's size is\n    less than limit, it means we finish the current shard.\n    offset - the offset from current shard. If we want to start from begining, set it to 0.\n\n    Returns 2 tensors:\n    1 A 0-D int64 tensor represents the new offset.\n    2. A 1-D string tensor which is serialized format of monolith::EntryDump.\n    \"\"\"\n    shard_idx = tf.convert_to_tensor(shard_idx, tf.int32)\n    num_shards = tf.convert_to_tensor(num_shards, tf.int32)\n    limit = tf.convert_to_tensor(limit, tf.int64)\n    offset = tf.convert_to_tensor(offset, tf.int64)\n    return hash_table_ops.monolith_hash_table_save_as_tensor(\n        self._table,\n        shard_idx,\n        num_shards,\n        limit,\n        offset,\n        name=\"monolith_hash_table_save_as_tensor\")\n\n\ndef fused_lookup(tables: tf.Tensor,\n                 ids: tf.Tensor,\n                 fused_slot_size: tf.Tensor,\n                 num_of_shards: int,\n                 req_time: tf.Tensor = None) -> Tuple[tf.Tensor]:\n  \"\"\" A fused operation for lookup.\n\n  This op takes a fused_ids, and fused_slot_sizes,\n  lookup via a list of tables, and return a concatenated embedding. Several\n  auxiluary results are also returned to simplify processing at later stages.\n\n  Example: \n    tables = [{1: [1], 2: [2]}, {3: [3, 3], 4: [4, 4]}]\n    ids = [1, 3, 2, 4]\n    fused_slot_size = [1, 1, 1, 1]\n    num_of_shards = 2\n\n  After the op, the outputs are:\n    embeddings = [1, 3, 3, 2, 4, 4]\n    recv_splits = [3, 3]\n    id_offsets = [0, 1, 2, 3]\n    emb_offsets = [0, 1, 3, 4]\n  For a setup of K tables, N shards:\n  Args:\n    tables:  A list of tables with shape [K], it is ordered by the tables' hashed_keys.\n    ids:  A flattened IDs with shape [M], M=sum(fused_slot_size[i]).\n    fused_slot_size: A list with shape [K*N].\n    num_of_shards: a integer N.\n  Returns:\n    embeddings: A 1-D flattened embeddings with shape [L], L=sum(embedding_sizes[i])\n    recv_splits: A 1-D flattened tensor with shape [N].\n    id_offsets: A 1-D flattened tensor wih shape [K*N], and it is an artifact used by apply_gradients.\n    emb_offsets: A 1-D flattened tensor with shape [K*N], and it is an artifact used by apply_gradients.\n  \"\"\"\n  if req_time is None:\n    req_time = tf.constant(0, tf.int64)\n  return hash_table_ops.monolith_hash_table_fused_lookup(\n      tables, ids, fused_slot_size, req_time, num_of_shards)\n\n\ndef fused_apply_gradient(\n    tables: List[tf.Tensor],\n    ids: tf.Tensor,\n    indices: tf.Tensor,\n    fused_slot_size: tf.Tensor,\n    id_grads: tf.Tensor,\n    id_offsets: tf.Tensor,\n    grad_offsets: tf.Tensor,\n    learning_rate_tensors: tf.Tensor,\n    req_time: tf.Tensor,\n    global_step: tf.Tensor,\n    num_of_shards: int,\n    enable_grad_accumulation: bool = False,\n):\n  \"\"\"A fused operation for applying gradients.\n  \n  This op takes fused ids and fused\n  gradients, and several other positional information, and applies the gradient\n  updates to the list of tables.\n\n  Example:\n    tables = [{1: [1], 2: [2]}, {3: [3, 3], 4: [4, 4]}]\n    ids = [1, 3, 2, 4]\n    fused_slot_size = [1, 1, 1, 1]\n    id_grads = [1, 2, 2, 1, 2, 2]\n    id_offsets = [0, 1, 2, 3]\n    grad_offsets = [0, 1, 3, 4]\n    learning_rate_tensors = [1, 1]\n    req_time = time_in_seconds\n    global_step = 1\n    num_of_shards = 2\n\n  After calling the op, with SGD, the output is the updated table:\n    tables = [{1: [0], 2: [1]}, {3: [1, 1], 4: [2, 2]}]\n\n  For a setup of K tables, N shards:\n  Args:\n    tables:  A list of tables with shape [K], it is ordered by the tables' hashed_keys.\n    ids:  A flattened IDs with shape [M], M=sum(fused_slot_size[i]).\n    fused_slot_size: A list with shape [K*N].\n    id_offsets: A 1-D flattened tensor wih shape [K*N], it is an intermediate artifact from fused_lookup.\n    grad_offsets: A 1-D flattened tensor with shape [K*N], it is an intermediate artifact from fused_lookup.\n    learning_rate_tensors: A 1-D flattened tensor wih shape [L], L=sum(learning_rate_lengths).\n    req_time: A scalar tensor with type tf.int64.\n    global_step: A scalar tensor with type tf.int64.\n    num_of_shards: a integer N.\n    enable_grad_accumulation: if enabled, the gradient accumulation is activated from the PS side for cross-shard gradients.\n  Returns:\n    An updated tables tensor.\n  \"\"\"\n  return hash_table_ops.monolith_hash_table_fused_optimize(\n      tables, ids, indices, fused_slot_size, id_grads, id_offsets, grad_offsets,\n      learning_rate_tensors, req_time, global_step, num_of_shards,\n      enable_grad_accumulation)\n\n\ndef hash_table_from_config(config: entry.HashTableConfigInstance,\n                           hash_filter: tf.Tensor = None,\n                           name_suffix=\"\",\n                           sync_client: tf.Tensor = None,\n                           saver_parallel: int = -1) -> HashTable:\n  table_config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n  table_config.CopyFrom(config.table_config)\n  assert table_config.HasField(\"type\")\n  table_type = table_config.WhichOneof(\"type\")\n  logging.info(\"Hash table type: {}\".format(table_type))\n  use_gpu = table_type == \"gpucuco\"\n  d = \"/device:GPU:0\" if use_gpu else \"/device:CPU:0\"\n\n  if is_exporting():\n    table_config.entry_config.entry_type = embedding_hash_table_pb2.EntryConfig.EntryType.SERVING\n  dim_size = infer_dim_size(config.table_config)\n  table_config_str = table_config.SerializeToString()\n  slot_expire_time_config = config.table_config.slot_expire_time_config.SerializeToString(\n  )\n  hash_table_name = \"MonolithHashTable_\" + name_suffix\n  if len(config.learning_rate_fns) != len(\n      config.table_config.entry_config.segments):\n    raise ValueError(\n        \"Size of learning_rate_fns and size of segments must be equal.\")\n  if hash_filter is None:\n    with tf.device(d):\n      hash_filter = hash_filter_ops.create_dummy_hash_filter(\n          name_suffix=name_suffix)\n  if sync_client is None or use_gpu:  # We don't have gpu sync for now, get rid of or use_gpu if added one\n    with tf.device(d):\n      sync_client = distributed_serving_ops.create_dummy_sync_client()\n  with tf.device(\n      d\n  ):  # Merged Device is essential here to avoid affecting job task placement\n    table_op = hash_table_ops.monolith_hash_table(\n        name=hash_table_name,\n        filter_handle=hash_filter,\n        sync_client_handle=sync_client,\n        config=table_config_str,\n        shared_name=hash_table_name)\n  return HashTable(table_op,\n                   shared_name=hash_table_name,\n                   dim_size=dim_size,\n                   slot_expire_time_config=slot_expire_time_config,\n                   learning_rate_tensor=config.call_learning_rate_fns(),\n                   saver_parallel=saver_parallel,\n                   extra_restore_names=config.extra_restore_names)\n\n\ndef test_hash_table(\n    dim_size,\n    enable_hash_filter=False,\n    name_suffix=None,\n    learning_rate=1.0,\n    occurrence_threshold=0,\n    use_adagrad=False,\n    expire_time=36500,  # For testing, the Default expire time is 100 years.\n    sync_client: tf.Tensor = None,\n    extra_restore_names=None,\n    use_gpu=False,\n) -> HashTable:\n  \"\"\"\n  Returns a hash table which essentially is a |dim_size| float\n  table with sgd optimizer.\n  \"\"\"\n  table_config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n  if use_gpu:\n    table_config.gpucuco.SetInParent()\n  else:\n    table_config.cuckoo.SetInParent()\n  segment = table_config.entry_config.segments.add()\n  segment.dim_size = dim_size\n  if use_adagrad:\n    segment.opt_config.adagrad.SetInParent()  # use adagrad for gpu hash table\n  else:\n    segment.opt_config.sgd.SetInParent()\n\n  if use_gpu:\n    segment.init_config.ones.SetInParent()  # check ones\n  else:\n    segment.init_config.zeros.SetInParent()\n\n  segment.comp_config.fp32.SetInParent()\n\n  slot_occurrence_threshold_config = embedding_hash_table_pb2.SlotOccurrenceThresholdConfig(\n  )\n  slot_occurrence_threshold_config.default_occurrence_threshold = occurrence_threshold\n\n  table_config.slot_expire_time_config.default_expire_time = expire_time\n  config = entry.HashTableConfigInstance(\n      table_config, [learning_rate], extra_restore_names=extra_restore_names)\n  if not use_gpu:\n    hash_filters = hash_filter_ops.create_hash_filters(\n        0, enable_hash_filter,\n        slot_occurrence_threshold_config.SerializeToString())\n  if not name_suffix:\n    name_suffix = tf.compat.v1.get_default_graph().unique_name(\"test\")\n\n  if not use_gpu:\n    return hash_table_from_config(config=config,\n                                  hash_filter=hash_filters[0],\n                                  name_suffix=name_suffix,\n                                  sync_client=sync_client)\n  return hash_table_from_config(config=config,\n                                name_suffix=name_suffix,\n                                sync_client=sync_client)\n\n\ndef vocab_hash_table(vocab_size: int,\n                     dim_size: int,\n                     enable_hash_filter=False,\n                     learning_rate=1.0) -> HashTable:\n  \"\"\"\n  Returns a hash table which essentially is a [vocab_size, dim_size] float\n  table with sgd optimizer.\n  \"\"\"\n  # Here we use a hash table which is more powerful than vocab table.\n  return test_hash_table(dim_size,\n                         enable_hash_filter,\n                         learning_rate=learning_rate)\n\n\ndef _all_table_tensor_prefix(table: HashTable) -> List[str]:\n  all_names = [table.name] + table._extra_restore_names\n  return [name.replace(\":\", \"-\").replace(\"/\", \"-\") for name in all_names]\n\n\ndef _table_tensor_prefix(table: HashTable) -> str:\n  return _all_table_tensor_prefix(table)[0]\n\n\nclass HashTableCheckpointSaverListener(tf.estimator.CheckpointSaverListener):\n  \"\"\"Saves the hash tables when saver is run.\"\"\"\n\n  def __init__(self, basename: str):\n    \"\"\"|basename| should be a file name which is same as what is passed to saver.\"\"\"\n    super().__init__()\n    self._helper = save_utils.SaveHelper(basename)\n    self._table_id_to_placeholder = {}\n    self._save_op = self._build_save_graph()\n\n  def before_save(self, sess, global_step_value):\n    \"\"\"\n    We use before save so the checkpoint file is updated after we successfully\n    save the hash table.\n    \"\"\"\n    logging.info(\"Starting saving hash tables.\")\n    feed_dict = {}\n    base_dir = self._helper.get_ckpt_asset_dir(\n        self._helper.get_ckpt_prefix(global_step_value))\n    tf.io.gfile.makedirs(base_dir)\n    for table in ops.get_collection(_HASH_TABLE_GRAPH_KEY):\n      table_basename = base_dir + _table_tensor_prefix(table)\n      feed_dict.update(\n          {self._table_id_to_placeholder[table.name]: table_basename})\n    sess.run(self._save_op,\n             feed_dict=feed_dict,\n             options=tf.compat.v1.RunOptions(timeout_in_ms=_TIMEOUT_IN_MS))\n    logging.info(\"Finished saving hash tables.\")\n\n  def _build_save_graph(self) -> tf.Operation:\n    save_tensors = []\n    # This reduces disk metadata modification pressure.\n    random_sleep_ms = 15 * len(ops.get_collection(_HASH_TABLE_GRAPH_KEY))\n    for table in ops.get_collection(_HASH_TABLE_GRAPH_KEY):\n      table_basename = tf.compat.v1.placeholder(tf.string, shape=[])\n      self._table_id_to_placeholder.update({table.name: table_basename})\n      save_tensors.append(\n          table.save(table_basename, random_sleep_ms=random_sleep_ms).table)\n    with tf.control_dependencies(save_tensors):\n      return tf.no_op()\n\n\nclass HashTableCheckpointRestorerListener(\n    basic_restore_hook.CheckpointRestorerListener):\n  \"\"\"Restores the hash tables from basename\"\"\"\n\n  def __init__(self, basename: str, ps_monitor=None):\n    super().__init__()\n    self._basename = basename\n    self._helper = save_utils.SaveHelper(basename)\n    self._table_id_to_placeholder = {}\n    self._restore_ops_per_device = self._build_restore_graph()\n    self._ps_monitor = ps_monitor\n\n  def before_restore(self, session):\n    \"\"\"\n    We use before restore so as to strictly control the order of restorer listeners.\n\n    \"\"\"\n    ckpt_prefix = tf.train.latest_checkpoint(os.path.dirname(self._basename))\n    if not ckpt_prefix:\n      logging.info(\n          \"No checkpoint found in %s. Looking for assets(sparse only).\",\n          self._basename)\n      # for sparse only ckpt converted from sail\n      assets_list = tf.io.gfile.glob(\n          os.path.join(os.path.dirname(self._basename), \"*.assets\"))\n      if len(assets_list) == 0:\n        logging.info(\"No assets(sparse only) found, skipping.\")\n        return\n      elif len(assets_list) > 1:\n        logging.info(\n            f\"Found {len(assets_list)} sparse assets of value {assets_list}, skipping.\"\n        )\n        return\n      asset_dir = assets_list[0] + \"/\"\n    else:\n      asset_dir = self._helper.get_ckpt_asset_dir(ckpt_prefix)\n    logging.info(\"Restore hash tables from %s.\", asset_dir)\n    self._restore_from_path_prefix(session, asset_dir)\n    logging.info(\"Finished restore.\")\n\n  def _restore_from_path_prefix(self, sess, path_prefix):\n\n    def get_restore_prefix(prefixes: List[str]):\n      for prefix in prefixes:\n        if len(tf.io.gfile.glob(path_prefix + prefix + \"*\")):\n          return prefix\n      raise ValueError(\n          (\"Unable to find table checkpoint in '%s' for table: %s. \"\n           \"Maybe the model structure has been changed.\"), path_prefix,\n          repr(prefixes))\n\n    tables = tf.compat.v1.get_collection(_HASH_TABLE_GRAPH_KEY)\n    with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:\n      table_to_prefix = {\n          table.name: executor.submit(get_restore_prefix,\n                                      _all_table_tensor_prefix(table))\n          for table in tables\n      }\n      for table in tables:\n        table_to_prefix[table.name] = table_to_prefix[table.name].result()\n\n    feed_dict = {}\n    for table in tables:\n      table_basename = path_prefix + table_to_prefix[table.name]\n      feed_dict.update(\n          {self._table_id_to_placeholder[table.name]: table_basename})\n\n    restore_ops_all = []\n    for device, restore_ops in self._restore_ops_per_device.items():\n      if not self._ps_monitor or self._ps_monitor.is_ps_uninitialized(\n          sess, device):\n        restore_ops_all.extend(restore_ops)\n    sess.run(restore_ops_all,\n             feed_dict=feed_dict,\n             options=tf.compat.v1.RunOptions(timeout_in_ms=_TIMEOUT_IN_MS))\n\n  def _build_restore_graph(self):\n    restore_ops_per_device = defaultdict(list)\n    for table in ops.get_collection(_HASH_TABLE_GRAPH_KEY):\n      table_basename = tf.compat.v1.placeholder(tf.string, shape=[])\n      self._table_id_to_placeholder.update({table.name: table_basename})\n      restore_op = table.restore(table_basename).as_op()\n      restore_ops_per_device[table.table.device].append(restore_op)\n    return restore_ops_per_device\n\n\n# This is for ByteDance internal use only\ndef extract_slot_from_entry(entry: tf.Tensor, fid_v2=True):\n  return hash_table_ops.monolith_extract_slot_from_entry(entry, fid_v2=fid_v2)\n\n\nclass HashTableRestorerSaverListener(tf.estimator.CheckpointSaverListener):\n  \"\"\"Since we use restore to remove stale entries,\n  we create a saver listener here.\"\"\"\n\n  def __init__(self, ckpt_prefix: str):\n    self._l = HashTableCheckpointRestorerListener(ckpt_prefix)\n\n  def after_save(self, session, global_step_value):\n    self._l.before_restore(session)\n\n\nops.register_proto_function(_HASH_TABLE_GRAPH_KEY,\n                            proto_type=hash_table_ops_pb2.HashTableProto,\n                            to_proto=HashTable.to_proto,\n                            from_proto=HashTable.from_proto)\n"
  },
  {
    "path": "monolith/native_training/hash_table_ops_benchmark.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nimport numpy as np\nimport tensorflow as tf\nimport sys\n\nfrom monolith.native_training import hash_filter_ops\nimport monolith.native_training.hash_table_ops as ops\n\n\ndef _get_id_tensor(x):\n  return tf.constant(x, dtype=tf.int64)\n\n\n# TODO: use tf.test.Benchmark\nclass HashTableOpsBenchmark(tf.test.TestCase):\n\n  def test_lookup(self):\n    with tf.compat.v1.Session() as sess:\n      len, dim_size = (10000, 32)\n      id_tensor = _get_id_tensor([x for x in range(len)])\n      hash_table = ops.test_hash_table(dim_size)\n      hash_table = hash_table.assign_add(id_tensor[:-5],\n                                         tf.ones([len, dim_size]))\n      hash_table = hash_table.assign_add(id_tensor[-5:],\n                                         tf.zeros([len, dim_size]))\n      iters = 100\n      embedding_one = [float(1) * iters for _ in range(32)]\n      embedding_zero = [0 for _ in range(32)]\n      start = time.time()\n      _embeddings = hash_table.lookup(id_tensor)\n      for _ in range(iters):\n        embeddings = sess.run(_embeddings)\n      total_wall_time = time.time() - start\n      print('wall time: {}'.format(total_wall_time / iters))\n    self.assertAllClose(embeddings[:-5],\n                        [embedding_one for _ in range(len - 5)])\n    self.assertAllClose(embeddings[-5:], [embedding_zero for _ in range(5)])\n\n  def test_lookup_multi_thread(self):\n    with tf.compat.v1.Session() as sess:\n      len, dim_size = (10000, 32)\n      id_tensor = _get_id_tensor([x for x in range(len)])\n      hash_table = ops.test_hash_table(dim_size)\n      hash_table = hash_table.assign_add(id_tensor[:-5],\n                                         tf.ones([len, dim_size]))\n      hash_table = hash_table.assign_add(id_tensor[-5:],\n                                         tf.zeros([len, dim_size]))\n      iters = 100\n      embedding_one = [float(1) * iters for _ in range(32)]\n      embedding_zero = [0 for _ in range(32)]\n      start = time.time()\n      _embeddings = hash_table.lookup(id_tensor, use_multi_threads=True)\n      for _ in range(iters):\n        embeddings = sess.run(_embeddings)\n      total_wall_time = time.time() - start\n      print('wall time(MT): {}'.format(total_wall_time / iters))\n    self.assertAllClose(embeddings[:-5],\n                        [embedding_one for _ in range(len - 5)])\n    self.assertAllClose(embeddings[-5:], [embedding_zero for _ in range(5)])\n\n  def test_basic_optimize(self):\n    with tf.compat.v1.Session() as sess:\n      len, dim_size = (1000000, 32)\n      # We assume each ID is appeared 4 times.\n      id_tensor = _get_id_tensor([x // 4 for x in range(len)])\n      hash_table = ops.test_hash_table(dim_size,\n                                       learning_rate=0.001,\n                                       use_adagrad=True)\n      hash_table = hash_table.assign_add(id_tensor[:-5],\n                                         tf.ones([len, dim_size]))\n      hash_table = hash_table.assign_add(id_tensor[-5:],\n                                         tf.zeros([len, dim_size]))\n      start = time.time()\n      embeddings = hash_table.lookup(id_tensor)\n      loss = -embeddings\n      grads = tf.gradients(loss, embeddings)\n      hash_table = hash_table.apply_gradients(zip(grads, [embeddings]))\n      embeddings = hash_table.lookup(id_tensor)\n      embeddings = sess.run(embeddings)\n      total_wall_time = time.time() - start\n      print('wall time: {}'.format(total_wall_time))\n\n  def test_multi_threads_optimize(self):\n    with tf.compat.v1.Session() as sess:\n      len, dim_size = (1000000, 32)\n      # We assume each ID is appeared 4 times.\n      id_tensor = _get_id_tensor([x // 4 for x in range(len)])\n      hash_table = ops.test_hash_table(dim_size,\n                                       learning_rate=0.001,\n                                       use_adagrad=True)\n      hash_table = hash_table.assign_add(id_tensor[:-5],\n                                         tf.ones([len, dim_size]))\n      hash_table = hash_table.assign_add(id_tensor[-5:],\n                                         tf.zeros([len, dim_size]))\n      start = time.time()\n      embeddings = hash_table.lookup(id_tensor)\n      loss = -embeddings\n      grads = tf.gradients(loss, embeddings)\n      hash_table = hash_table.apply_gradients(zip(grads, [embeddings]),\n                                              use_multi_threads=True)\n      embeddings = hash_table.lookup(id_tensor)\n      embeddings = sess.run(embeddings)\n      total_wall_time = time.time() - start\n      print('wall time: {}'.format(total_wall_time))\n\n  def test_multi_threads_optimize_with_dedup(self):\n    with tf.compat.v1.Session() as sess:\n      len, dim_size = (1000000, 32)\n      # We assume each ID is appeared 4 times.\n      id_tensor = _get_id_tensor([x // 4 for x in range(len)])\n      hash_table = ops.test_hash_table(dim_size,\n                                       learning_rate=0.001,\n                                       use_adagrad=True)\n      hash_table = hash_table.assign_add(id_tensor[:-5],\n                                         tf.ones([len, dim_size]))\n      hash_table = hash_table.assign_add(id_tensor[-5:],\n                                         tf.zeros([len, dim_size]))\n      start = time.time()\n      embeddings = hash_table.lookup(id_tensor)\n      loss = -embeddings\n      grads = tf.gradients(loss, embeddings)\n      hash_table = hash_table.apply_gradients(zip(grads, [embeddings]),\n                                              use_multi_threads=True,\n                                              enable_dedup=True)\n      embeddings = hash_table.lookup(id_tensor)\n      embeddings = sess.run(embeddings)\n      total_wall_time = time.time() - start\n      print('wall time: {}'.format(total_wall_time))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hash_table_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport datetime\nimport os\nimport random\nfrom typing import Dict, List\n\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.python.training import monitored_session\n\nfrom monolith.native_training import basic_restore_hook\nfrom monolith.native_training import entry\nfrom monolith.native_training import hash_filter_ops\nfrom monolith.native_training import learning_rate_functions\nfrom monolith.native_training import save_utils\nimport monolith.native_training.hash_table_ops as ops\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\n\ndef _get_id_tensor(x):\n  return tf.constant(x, dtype=tf.int64)\n\n\ndef test_hash_table_with_hash_filters(dim_size,\n                                      hash_filters,\n                                      name_suffix=\"0\",\n                                      learning_rate=1.0) -> ops.HashTable:\n  \"\"\"\n  Returns a hash table which essentially is a |dim_size| float \n  table with sgd optimizer.\n  \"\"\"\n  table_config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n  table_config.cuckoo.SetInParent()\n  segment = table_config.entry_config.segments.add()\n  segment.dim_size = dim_size\n  segment.opt_config.sgd.SetInParent()\n  segment.init_config.zeros.SetInParent()\n  config = entry.HashTableConfigInstance(table_config, [learning_rate])\n  return ops.hash_table_from_config(config=config,\n                                    hash_filter=hash_filters[0],\n                                    name_suffix=name_suffix)\n\n\ndef test_hash_table(*args, **kwargs):\n  \"\"\"Serialize and deserialize hash table to make sure this process works fine\"\"\"\n  with tf.name_scope(\"scope\") as scope:\n    h = ops.test_hash_table(*args, **kwargs)\n    proto = h.to_proto(export_scope=scope)\n    return ops.HashTable.from_proto(proto, import_scope=scope)\n\n\nclass HashTableOpsTest(tf.test.TestCase):\n\n  def test_basic(self):\n    with tf.compat.v1.Session() as sess:\n      dim_size = 1\n      hash_table = ops.vocab_hash_table(3, dim_size)\n      hash_table = hash_table.assign_add(_get_id_tensor([0, 1]),\n                                         tf.ones([2, dim_size]))\n      embeddings = hash_table.lookup(_get_id_tensor([0, 1, 2]))\n      size = hash_table.size()\n      embeddings, size = sess.run([embeddings, size])\n    self.assertAllEqual(embeddings, [[1], [1], [0]])\n    self.assertAllEqual(size, 2)\n    self.assertNotEqual(hash_table.name, \"MonolithHashTable\")\n\n  def test_assign(self):\n    with tf.compat.v1.Session() as sess:\n      dim_size = 1\n      hash_table = ops.vocab_hash_table(3, dim_size)\n      hash_table = hash_table.assign(_get_id_tensor([0, 1]),\n                                     tf.ones([2, dim_size]))\n      embeddings1 = hash_table.lookup(_get_id_tensor([0, 1, 2]))\n\n      # Ensure the second assign happens after the first lookup\n      with tf.control_dependencies([embeddings1]):\n        hash_table = hash_table.assign(\n            _get_id_tensor([1]),\n            tf.constant([5 for _ in range(dim_size)], dtype=tf.float32))\n        embeddings2 = hash_table.lookup(_get_id_tensor([0, 1, 2]))\n      embeddings1, embeddings2 = sess.run([embeddings1, embeddings2])\n    self.assertAllEqual(embeddings1, [[1], [1], [0]])\n    self.assertAllEqual(embeddings2, [[1], [5], [0]])\n    self.assertNotEqual(hash_table.name, \"MonolithHashTable\")\n\n  def test_lookup_entry(self):\n    table = test_hash_table(1)\n    updated_table = table.assign(_get_id_tensor([0, 1, 2]),\n                                 [[0.1], [0.2], [0.3]])\n    self.evaluate(updated_table.as_op())\n    entry_strs = table.lookup_entry(_get_id_tensor([0, 1, 2, 3]))\n    entry_strs = self.evaluate(entry_strs)\n    nums = list()\n    for i in range(3):\n      # OK to parse\n      dump = embedding_hash_table_pb2.EntryDump()\n      dump.ParseFromString(entry_strs[i])\n      nums.append(dump.num)\n    self.assertAllClose(nums, [[0.1], [0.2], [0.3]])\n    self.assertEqual(entry_strs[3], b\"\")\n\n  def test_save_as_tensor(self):\n    table = test_hash_table(1)\n    updated_table = table.assign(_get_id_tensor([0, 1, 2]),\n                                 [[0.1], [0.2], [0.3]])\n    self.evaluate(updated_table.as_op())\n    _, dump_str = table.save_as_tensor(0, 1, 1000, 0)\n    dump_str = self.evaluate(dump_str)\n    for i in range(len(dump_str)):\n      # OK to parse\n      dump = embedding_hash_table_pb2.EntryDump()\n      dump.ParseFromString(dump_str[i])\n\n  def testNameConflict(self):\n    with self.session() as sess:\n      hash_table = test_hash_table(1, name_suffix=\"same_suffix\")\n      with self.assertRaises(ValueError):\n        test_hash_table(1, name_suffix=\"same_suffix\")\n\n  def test_gradients(self):\n    with tf.compat.v1.Session() as sess:\n      hash_table = test_hash_table(1, learning_rate=0.1)\n      id_tensor = _get_id_tensor([0, 0, 1])\n      embeddings = hash_table.lookup(id_tensor)\n      loss = -embeddings\n      grads = tf.gradients(loss, embeddings)\n      global_step = _get_id_tensor(0)\n      hash_table = hash_table.apply_gradients(id_tensor,\n                                              grads[0],\n                                              global_step=global_step)\n\n      new_embeddings = hash_table.lookup(_get_id_tensor([0, 1]))\n      new_embeddings = sess.run(new_embeddings)\n    self.assertAllClose(new_embeddings, [[0.2], [0.1]])\n\n  def test_gradients_with_learning_rate_fn(self):\n    with tf.compat.v1.Session() as sess:\n      hash_table = test_hash_table(1, learning_rate=lambda: 0.1)\n      id_tensor = _get_id_tensor([0, 0, 1])\n      embeddings = hash_table.lookup(id_tensor)\n      loss = -embeddings\n      grads = tf.gradients(loss, embeddings)\n      global_step = _get_id_tensor(0)\n      hash_table = hash_table.apply_gradients(id_tensor,\n                                              grads[0],\n                                              global_step=global_step)\n      new_embeddings = hash_table.lookup(_get_id_tensor([0, 1]))\n      new_embeddings = sess.run(new_embeddings)\n    self.assertAllClose(new_embeddings, [[0.2], [0.1]])\n\n  def test_gradients_with_learning_rate_decay(self):\n    with tf.compat.v1.Session() as sess:\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      self.evaluate(tf.compat.v1.assign_add(global_step, 1))\n      hash_table = test_hash_table(\n          1,\n          learning_rate=learning_rate_functions.PolynomialDecay(\n              initial_learning_rate=0.01,\n              decay_steps=10,\n              end_learning_rate=0.11))\n      id_tensor = _get_id_tensor([0, 0, 1])\n      embeddings = hash_table.lookup(id_tensor)\n      loss = -embeddings\n      grads = tf.gradients(loss, embeddings)\n      hash_table = hash_table.apply_gradients(id_tensor,\n                                              grads[0],\n                                              global_step=global_step)\n\n      new_embeddings = hash_table.lookup(_get_id_tensor([0, 1]))\n      new_embeddings = sess.run(new_embeddings)\n    self.assertAllClose(new_embeddings, [[0.04], [0.02]])\n\n  def test_gradients_with_dedup(self):\n    vec_dim = 10\n    with tf.compat.v1.Session() as sess:\n      hash_table = test_hash_table(vec_dim, learning_rate=0.1)\n      id_tensor = _get_id_tensor([0, 1, 0, 1, 0])\n      embeddings = hash_table.lookup(id_tensor)\n      loss = -embeddings\n      grads = tf.gradients(loss, embeddings)\n      global_step = _get_id_tensor(0)\n      hash_table = hash_table.apply_gradients(id_tensor,\n                                              grads[0],\n                                              global_step=global_step,\n                                              enable_dedup=True)\n\n      new_embeddings = hash_table.lookup(_get_id_tensor([0, 1]))\n      new_embeddings = sess.run(new_embeddings)\n    expected_output = [[0.3 for _ in range(vec_dim)],\n                       [0.2 for _ in range(vec_dim)]]\n    self.assertAllClose(new_embeddings, expected_output)\n\n  def test_gradients_with_different_ids(self):\n    with tf.compat.v1.Session() as sess:\n      hash_table = test_hash_table(1, learning_rate=0.1)\n      embeddings = hash_table.lookup(_get_id_tensor([0, 0, 1]))\n      loss = -embeddings\n      grads = tf.gradients(loss, embeddings)\n      global_step = _get_id_tensor(0)\n      hash_table = hash_table.apply_gradients(_get_id_tensor([1, 0, 1]),\n                                              grads[0],\n                                              global_step=global_step)\n\n      new_embeddings = hash_table.lookup(_get_id_tensor([0, 1]))\n      new_embeddings = sess.run(new_embeddings)\n    self.assertAllClose(new_embeddings, [[0.1], [0.2]])\n\n  def test_gradients_with_hash_filter(self):\n    with tf.compat.v1.Session() as sess:\n      hash_table = test_hash_table(1,\n                                   enable_hash_filter=True,\n                                   learning_rate=0.1,\n                                   occurrence_threshold=3)\n      id_tensor = _get_id_tensor([0, 0, 1])\n      embeddings = hash_table.lookup(id_tensor)\n      loss = -embeddings\n      grads = tf.gradients(loss, embeddings)\n      global_step = _get_id_tensor(0)\n      hash_table = hash_table.apply_gradients(id_tensor,\n                                              grads[0],\n                                              global_step=global_step)\n\n      expected_results = [\n          # occurrence_threshold=3\n          # id 0, first apply gradient changes count=1, first apply gradient changes count=2\n          # both <=3, no real update.\n          # id 1, first apply gradient changes count=1 <= 3, no real update\n          [[0], [0]],\n          # id 0, first apply gradient changes count=3, first apply gradient changes count=4\n          # first update <= 3, second update > 3, update once\n          # id 1, first apply gradient changes count=2 <= 3, no real update\n          [[0.1], [0]],\n          # id 0, first apply gradient changes count=5, first apply gradient changes count=6\n          # both update count > 3, update twice\n          # id 1, first apply gradient changes count=3 <= 3, no real update\n          [[0.3], [0.0]],\n          # id 0, first apply gradient changes count=7, first apply gradient changes count=8\n          # both update count > 3, update twice\n          # id 1, first apply gradient changes count=4 > 3, update once\n          [[0.5], [0.1]]\n      ]\n      for i in range(0, 4):\n        new_embeddings = hash_table.lookup(_get_id_tensor([0, 1]))\n        new_embeddings = sess.run(new_embeddings)\n        self.assertAllClose(new_embeddings, expected_results[i])\n\n  def test_save_restore(self):\n    with self.session() as sess:\n      hash_table = test_hash_table(1)\n      hash_table = hash_table.assign_add(\n          _get_id_tensor([-1, 1]), tf.constant([[1], [2]], dtype=tf.float32))\n      base_name = os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_save_restore\",\n                               \"table\")\n      hash_table = hash_table.save(base_name)\n      sess.run(hash_table.as_op())\n\n    with self.session() as sess:\n      hash_table2 = test_hash_table(1, False)\n      hash_table2 = hash_table2.restore(base_name)\n      embedding = hash_table2.lookup(_get_id_tensor([-1, 1]))\n      embedding = sess.run(embedding)\n    self.assertAllEqual(embedding, [[1], [2]])\n\n  def test_restore_from_another_table(self):\n    with self.session() as sess:\n      hash_table1 = test_hash_table(1)\n      hash_table1 = hash_table1.assign(_get_id_tensor([1]),\n                                       tf.constant([[1]], dtype=tf.float32))\n      base_name = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                               \"test_restore_from_another_table\", \"table\")\n      hash_table1 = hash_table1.save(base_name)\n      sess.run(hash_table1.as_op())\n      hash_table2 = test_hash_table(1, extra_restore_names=[hash_table1.name])\n      hash_table2 = hash_table2.restore(base_name)\n      embedding = hash_table2.lookup(_get_id_tensor([1]))\n      embedding = sess.run(embedding)\n    self.assertAllEqual(embedding, [[1]])\n\n  def test_save_restore_with_feature_eviction_assign_add(self):\n    with self.session() as sess:\n      # Default feature eviction time is expire_time.\n      # Feature with ts older than expire_time will be evicted.\n      expire_time = 1\n      hash_table = test_hash_table(dim_size=1, expire_time=expire_time)\n      max_ts = 10000000\n      expire_time_in_sec = expire_time * 24 * 3600\n      evict_ts = max_ts - expire_time_in_sec - 1\n      hash_table = hash_table.assign_add(_get_id_tensor([1]),\n                                         tf.constant([[1]], dtype=tf.float32),\n                                         tf.constant(evict_ts, dtype=tf.int64))\n\n      # Feature with keep_ts which is newer than expire_time.\n      # It will not be evicted after save.\n      keep_ts = max_ts - expire_time_in_sec + 1\n      hash_table = hash_table.assign_add(_get_id_tensor([2]),\n                                         tf.constant([[2]], dtype=tf.float32),\n                                         tf.constant(keep_ts, dtype=tf.int64))\n\n      # Feature with max_ts date will be kept and also it will update the internal max_req_time.\n      hash_table = hash_table.assign_add(_get_id_tensor([3]),\n                                         tf.constant([[3]], dtype=tf.float32),\n                                         tf.constant(max_ts, dtype=tf.int64))\n      base_name = os.path.join(\n          os.environ[\"TEST_TMPDIR\"],\n          \"test_save_restore_with_feature_eviction_assign_add\", \"table\")\n      hash_table = hash_table.save(base_name)\n      sess.run(hash_table.as_op())\n\n    with self.session() as sess:\n      hash_table2 = test_hash_table(1, False)\n      hash_table2 = hash_table2.restore(base_name)\n      embedding = hash_table2.lookup(_get_id_tensor([1, 2, 3]))\n      embedding = sess.run(embedding)\n    self.assertAllEqual(embedding, [[0], [2], [3]])\n\n  def test_save_restore_with_feature_eviction_apply_gradients(self):\n    with self.session() as sess:\n      # Default feature eviction time is expire_time.\n      # Feature with evic_ts older than expire_time will be evicted.\n      expire_time = 1\n      hash_table = test_hash_table(dim_size=1, expire_time=expire_time)\n      max_ts = 10000000\n      expire_time_in_sec = expire_time * 24 * 3600\n      evict_ts = max_ts - expire_time_in_sec - 1\n      global_step = _get_id_tensor(0)\n      hash_table = hash_table.apply_gradients(_get_id_tensor([1]),\n                                              tf.constant([[1]],\n                                                          dtype=tf.float32),\n                                              global_step,\n                                              req_time=tf.constant(\n                                                  evict_ts, dtype=tf.int64))\n\n      # Feature with keep_ts which is newer than expire_time.\n      # It will not be evicted after save.\n      keep_ts = max_ts - expire_time_in_sec + 1\n      global_step = _get_id_tensor(0)\n      hash_table = hash_table.apply_gradients(_get_id_tensor([2]),\n                                              tf.constant([[2]],\n                                                          dtype=tf.float32),\n                                              global_step,\n                                              req_time=tf.constant(\n                                                  keep_ts, dtype=tf.int64))\n\n      # Feature with max_ts will be kept and also it will update the internal max_req_time.\n      global_step = _get_id_tensor(0)\n      hash_table = hash_table.apply_gradients(_get_id_tensor([3]),\n                                              tf.constant([[3]],\n                                                          dtype=tf.float32),\n                                              global_step,\n                                              req_time=tf.constant(\n                                                  max_ts, dtype=tf.int64))\n\n      base_name = os.path.join(\n          os.environ[\"TEST_TMPDIR\"],\n          \"test_save_restore_with_feature_eviction_apply_gradients\", \"table\")\n      hash_table = hash_table.save(base_name)\n      sess.run(hash_table.as_op())\n\n    with self.session() as sess:\n      hash_table2 = test_hash_table(1, False)\n      hash_table2 = hash_table2.restore(base_name)\n      embedding = hash_table2.lookup(_get_id_tensor([1, 2, 3]))\n      embedding = sess.run(embedding)\n    self.assertAllEqual(embedding, [[0], [-2], [-3]])\n\n  def test_entry_ttl_zero(self):\n    basename = os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_entry_ttl\",\n                            \"table\")\n    with self.session() as sess:\n      hash_table = test_hash_table(1, expire_time=0)\n      hash_table = hash_table.assign_add(\n          _get_id_tensor([-1, 1]), tf.constant([[1], [2]], dtype=tf.float32))\n      hash_table = hash_table.save(basename)\n      sess.run(hash_table.as_op())\n    with self.session() as sess:\n      hash_table2 = test_hash_table(1)\n      hash_table2 = hash_table2.restore(basename)\n      embedding = hash_table2.lookup(_get_id_tensor([-1, 1]))\n      embedding = sess.run(embedding)\n    self.assertAllEqual(embedding, [[0], [0]])\n\n  def test_entry_ttl_not_zero(self):\n    basename = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                            \"test_entry_ttl_not_zero\", \"table\")\n    with self.session() as sess:\n      hash_table = test_hash_table(1, expire_time=60 * 60)\n      hash_table = hash_table.assign_add(\n          _get_id_tensor([-1, 1]), tf.constant([[1], [2]], dtype=tf.float32))\n      hash_table = hash_table.save(basename)\n      sess.run(hash_table.as_op())\n    with self.session() as sess:\n      hash_table2 = test_hash_table(1)\n      hash_table2 = hash_table2.restore(basename)\n      embedding = hash_table2.lookup(_get_id_tensor([-1, 1]))\n      embedding = sess.run(embedding)\n    self.assertAllEqual(embedding, [[1], [2]])\n\n  def test_entry_ttl_by_slots(self):\n    basename = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                            \"test_entry_ttl_by_slots\", \"table\")\n    table_config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n    table_config.cuckoo.SetInParent()\n    segment = table_config.entry_config.segments.add()\n    segment.dim_size = 1\n    segment.opt_config.sgd.SetInParent()\n    segment.init_config.zeros.SetInParent()\n    table_config.slot_expire_time_config.default_expire_time = 60 * 60\n    slot_expire_time_1 = table_config.slot_expire_time_config.slot_expire_times.add(\n    )\n    slot_expire_time_1.slot = 1\n    slot_expire_time_1.expire_time = 0\n    slot_expire_time_2 = table_config.slot_expire_time_config.slot_expire_times.add(\n    )\n    slot_expire_time_2.slot = 2\n    slot_expire_time_2.expire_time = 1\n    hash_filters = hash_filter_ops.create_hash_filters(0, False)\n    config = entry.HashTableConfigInstance(table_config, [1.0])\n\n    with self.session() as sess:\n      id_1 = (1 << 48)\n      id_2 = (2 << 48)\n      name_suffix = tf.compat.v1.get_default_graph().unique_name(\"\")\n      hash_table = ops.hash_table_from_config(config,\n                                              hash_filter=hash_filters[0],\n                                              name_suffix=name_suffix)\n      hash_table = hash_table.assign_add(\n          _get_id_tensor([id_1, id_2]), tf.constant([[1], [2]],\n                                                    dtype=tf.float32),\n          tf.constant(100, dtype=tf.int64))\n      hash_table = hash_table.save(basename)\n      sess.run(hash_table.as_op())\n\n    basename_new = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                                \"test_entry_ttl_by_slots\", \"table_new\")\n    with self.session() as sess:\n      name_suffix = tf.compat.v1.get_default_graph().unique_name(\"\")\n      hash_table2 = ops.hash_table_from_config(config,\n                                               hash_filter=hash_filters[0],\n                                               name_suffix=name_suffix)\n      hash_table2 = hash_table2.restore(basename)\n      embedding_2 = hash_table2.lookup(_get_id_tensor([id_1, id_2]))\n      embedding_2 = sess.run(embedding_2)\n      hash_table2 = hash_table2.save(basename_new)\n      sess.run(hash_table2.as_op())\n    self.assertAllEqual(embedding_2, [[0], [2]])\n\n    with self.session() as sess:\n      hash_table3 = test_hash_table(1)\n      hash_table3 = hash_table3.restore(basename_new)\n      embedding_3 = hash_table3.lookup(_get_id_tensor([id_1, id_2]))\n    self.assertAllEqual(embedding_3, [[0], [2]])\n\n  def test_restore_not_found(self):\n    with self.session() as sess:\n      non_existent_files = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                                        \"test_restore_not_found\", \"table\")\n      hash_table2 = test_hash_table(1)\n      hash_table2 = hash_table2.restore(non_existent_files)\n      with self.assertRaises(Exception):\n        sess.run(hash_table2.as_op())\n\n  def test_save_restore_hook(self):\n    basename = os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_save_restore_hook\",\n                            \"model.ckpt\")\n    hash_filter = hash_filter_ops.create_dummy_hash_filter()\n    hash_table = test_hash_table(1)\n    add_op = hash_table.assign_add(_get_id_tensor([0]),\n                                   tf.constant([[1]],\n                                               dtype=tf.float32)).as_op()\n    sub_op = hash_table.assign_add(_get_id_tensor([0]),\n                                   tf.constant([[-1]],\n                                               dtype=tf.float32)).as_op()\n    embedding = hash_table.lookup(_get_id_tensor([0]))\n    saver_listener = ops.HashTableCheckpointSaverListener(basename)\n    # We need to create some variables to make saver happy.\n    tf.compat.v1.train.create_global_step()\n    saver = tf.compat.v1.train.Saver(tf.compat.v1.global_variables(),\n                                     sharded=True,\n                                     max_to_keep=10,\n                                     keep_checkpoint_every_n_hours=2)\n    saver_hook = tf.estimator.CheckpointSaverHook(os.path.dirname(basename),\n                                                  save_steps=1000,\n                                                  saver=saver,\n                                                  listeners=[saver_listener])\n    restorer_listener = ops.HashTableCheckpointRestorerListener(basename)\n    restore_hook = basic_restore_hook.CheckpointRestorerHook(\n        listeners=[restorer_listener])\n\n    with self.session() as sess:\n      saver_hook.begin()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      # In the estimator API, graph will be finalized before calling hook\n      g = tf.compat.v1.get_default_graph()\n      g.finalize()\n      sess.run(add_op)\n      saver_hook.after_create_session(sess, None)\n      sess.run(sub_op)\n      # restore will override sub_op\n      restore_hook.after_create_session(sess, None)\n      embedding = sess.run(embedding)\n    self.assertAllEqual(embedding, [[1]])\n\n  def test_restore_after_save(self):\n    ckpt_prefix = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                               \"test_restore_after_save\", \"model.ckpt\")\n    hash_table = test_hash_table(1)\n    assign_1_op = hash_table.assign(_get_id_tensor([0]),\n                                    tf.constant([[1]],\n                                                dtype=tf.float32)).as_op()\n    assign_2_op = hash_table.assign(_get_id_tensor([0]),\n                                    tf.constant([[2]],\n                                                dtype=tf.float32)).as_op()\n    emb = hash_table.lookup(_get_id_tensor([0]))\n\n    class AssignSaverListener(tf.estimator.CheckpointSaverListener):\n\n      def after_save(self, session, global_step_value):\n        session.run(assign_2_op)\n\n    # We need to create some variables to make saver happy.\n    tf.compat.v1.train.create_global_step()\n    saver = tf.compat.v1.train.Saver()\n    saver_hook = tf.estimator.CheckpointSaverHook(\n        os.path.dirname(ckpt_prefix),\n        save_steps=100,\n        saver=saver,\n        listeners=[\n            ops.HashTableCheckpointSaverListener(ckpt_prefix),\n            AssignSaverListener(),\n            ops.HashTableRestorerSaverListener(ckpt_prefix)\n        ])\n\n    with self.session() as sess:\n      saver_hook.begin()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(assign_1_op)\n      saver_hook.after_create_session(sess, None)\n      self.assertAllEqual([[1]], sess.run(emb))\n\n  def test_save_restore_hook_with_feature_eviction_assign_add(self):\n    basename = os.path.join(\n        os.environ[\"TEST_TMPDIR\"],\n        \"test_save_restore_hook_with_feature_eviction_assign_add\", \"model.ckpt\")\n    hash_filter = hash_filter_ops.create_dummy_hash_filter()\n    # Default feature eviction time is expire_time.\n    # Feature with ts older than expire_time will be evicted.\n    expire_time = 1\n    hash_table = test_hash_table(dim_size=1, expire_time=expire_time)\n    max_ts = 10000000\n    expire_time_in_sec = expire_time * 24 * 3600\n    evict_ts = max_ts - expire_time_in_sec - 1\n    assign_op_1 = hash_table.assign_add(_get_id_tensor([1]),\n                                        tf.constant([[1]], dtype=tf.float32),\n                                        tf.constant(evict_ts,\n                                                    dtype=tf.int64)).as_op()\n\n    keep_ts = max_ts - expire_time_in_sec + 1\n    assign_op_2 = hash_table.assign_add(_get_id_tensor([2]),\n                                        tf.constant([[2]], dtype=tf.float32),\n                                        tf.constant(keep_ts,\n                                                    dtype=tf.int64)).as_op()\n\n    assign_op_3 = hash_table.assign_add(_get_id_tensor([3]),\n                                        tf.constant([[3]], dtype=tf.float32),\n                                        tf.constant(max_ts,\n                                                    dtype=tf.int64)).as_op()\n\n    embedding = hash_table.lookup(_get_id_tensor([1, 2, 3]))\n    saver_listener = ops.HashTableCheckpointSaverListener(basename)\n    # We need to create some variables to make saver happy.\n    tf.compat.v1.train.create_global_step()\n    saver = tf.compat.v1.train.Saver(tf.compat.v1.global_variables(),\n                                     sharded=True,\n                                     max_to_keep=10,\n                                     keep_checkpoint_every_n_hours=2)\n    saver_hook = tf.estimator.CheckpointSaverHook(os.path.dirname(basename),\n                                                  save_steps=1000,\n                                                  saver=saver,\n                                                  listeners=[saver_listener])\n    restorer_listener = ops.HashTableCheckpointRestorerListener(basename)\n    restore_hook = basic_restore_hook.CheckpointRestorerHook(\n        listeners=[restorer_listener])\n\n    with self.session() as sess:\n      saver_hook.begin()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      # In the estimator API, graph will be finalized before calling hook\n      g = tf.compat.v1.get_default_graph()\n      g.finalize()\n      sess.run(assign_op_1)\n      sess.run(assign_op_2)\n      sess.run(assign_op_3)\n      embedding_values = sess.run(embedding)\n      self.assertAllEqual(embedding_values, [[1], [2], [3]])\n\n      saver_hook.after_create_session(sess, None)\n      restore_hook.after_create_session(sess, None)\n      embedding_values = sess.run(embedding)\n      self.assertAllEqual(embedding, [[0], [2], [3]])\n\n  def test_save_restore_hook_with_feature_eviction_apply_gradients(self):\n    basename = os.path.join(\n        os.environ[\"TEST_TMPDIR\"],\n        \"test_save_restore_hook_with_feature_eviction_apply_gradients\",\n        \"model.ckpt\")\n    hash_filter = hash_filter_ops.create_dummy_hash_filter()\n    # Default feature eviction time is expire_time.\n    # Feature with ts older than expire_time will be evicted.\n    expire_time = 1\n    hash_table = test_hash_table(dim_size=1, expire_time=expire_time)\n    max_ts = 10000000\n    expire_time_in_sec = expire_time * 24 * 3600\n    evict_ts = max_ts - expire_time_in_sec - 1\n    global_step = _get_id_tensor(0)\n    assign_op_1 = hash_table.apply_gradients(\n        _get_id_tensor([1]),\n        tf.constant([[1]], dtype=tf.float32),\n        global_step,\n        req_time=tf.constant(evict_ts, dtype=tf.int64)).as_op()\n\n    ts_to_keep = max_ts - expire_time_in_sec + 1\n    global_step = _get_id_tensor(0)\n    assign_op_2 = hash_table.apply_gradients(\n        _get_id_tensor([2]),\n        tf.constant([[2]], dtype=tf.float32),\n        global_step,\n        req_time=tf.constant(ts_to_keep, dtype=tf.int64)).as_op()\n\n    global_step = _get_id_tensor(0)\n    assign_op_3 = hash_table.apply_gradients(\n        _get_id_tensor([3]),\n        tf.constant([[3]], dtype=tf.float32),\n        global_step,\n        req_time=tf.constant(max_ts, dtype=tf.int64)).as_op()\n\n    embedding = hash_table.lookup(_get_id_tensor([1, 2, 3]))\n    saver_listener = ops.HashTableCheckpointSaverListener(basename)\n    # We need to create some variables to make saver happy.\n    tf.compat.v1.train.create_global_step()\n    saver = tf.compat.v1.train.Saver(tf.compat.v1.global_variables(),\n                                     sharded=True,\n                                     max_to_keep=10,\n                                     keep_checkpoint_every_n_hours=2)\n    saver_hook = tf.estimator.CheckpointSaverHook(os.path.dirname(basename),\n                                                  save_steps=1000,\n                                                  saver=saver,\n                                                  listeners=[saver_listener])\n    restorer_listener = ops.HashTableCheckpointRestorerListener(basename)\n    restore_hook = basic_restore_hook.CheckpointRestorerHook(\n        listeners=[restorer_listener])\n\n    with self.session() as sess:\n      saver_hook.begin()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      # In the estimator API, graph will be finalized before calling hook\n      g = tf.compat.v1.get_default_graph()\n      g.finalize()\n      sess.run(assign_op_1)\n      sess.run(assign_op_2)\n      sess.run(assign_op_3)\n      embedding_values = sess.run(embedding)\n      self.assertAllEqual(embedding_values, [[-1], [-2], [-3]])\n\n      saver_hook.after_create_session(sess, None)\n      restore_hook.after_create_session(sess, None)\n      embedding_values = sess.run(embedding)\n      self.assertAllEqual(embedding, [[0], [-2], [-3]])\n\n  def test_save_restore_hook_with_no_req_time_feature_eviction_apply_gradients(\n      self):\n    basename = os.path.join(\n        os.environ[\"TEST_TMPDIR\"],\n        \"test_save_restore_hook_with_no_req_time_feature_eviction_apply_gradients\",\n        \"model.ckpt\")\n    hash_filter = hash_filter_ops.create_dummy_hash_filter()\n    hash_table = test_hash_table(dim_size=1, expire_time=1)\n    global_step = _get_id_tensor(0)\n    assign_op_1 = hash_table.apply_gradients(\n        _get_id_tensor([1]), tf.constant([[1]], dtype=tf.float32),\n        global_step).as_op()\n\n    assign_op_2 = hash_table.apply_gradients(\n        _get_id_tensor([2]), tf.constant([[2]], dtype=tf.float32),\n        global_step).as_op()\n\n    embedding = hash_table.lookup(_get_id_tensor([1, 2]))\n    saver_listener = ops.HashTableCheckpointSaverListener(basename)\n    # We need to create some variables to make saver happy.\n    tf.compat.v1.train.create_global_step()\n    saver = tf.compat.v1.train.Saver(tf.compat.v1.global_variables(),\n                                     sharded=True,\n                                     max_to_keep=10,\n                                     keep_checkpoint_every_n_hours=2)\n    saver_hook = tf.estimator.CheckpointSaverHook(os.path.dirname(basename),\n                                                  save_steps=1000,\n                                                  saver=saver,\n                                                  listeners=[saver_listener])\n    restorer_listener = ops.HashTableCheckpointRestorerListener(basename)\n    restore_hook = basic_restore_hook.CheckpointRestorerHook(\n        listeners=[restorer_listener])\n\n    with self.session() as sess:\n      saver_hook.begin()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      # In the estimator API, graph will be finalized before calling hook\n      g = tf.compat.v1.get_default_graph()\n      g.finalize()\n      sess.run(assign_op_1)\n      sess.run(assign_op_2)\n      embedding_values = sess.run(embedding)\n      self.assertAllEqual(embedding_values, [[-1], [-2]])\n\n      saver_hook.after_create_session(sess, None)\n      restore_hook.after_create_session(sess, None)\n      embedding_values = sess.run(embedding)\n      self.assertAllEqual(embedding, [[-1], [-2]])\n\n  def test_save_restore_hook_with_zero_req_time_feature_eviction_apply_gradients(\n      self):\n    basename = os.path.join(\n        os.environ[\"TEST_TMPDIR\"],\n        \"test_save_restore_hook_with_zero_req_time_feature_eviction_apply_gradients\",\n        \"model.ckpt\")\n    hash_filter = hash_filter_ops.create_dummy_hash_filter()\n    hash_table = test_hash_table(dim_size=1, expire_time=1)\n    global_step = _get_id_tensor(0)\n    assign_op_1 = hash_table.apply_gradients(_get_id_tensor([1]),\n                                             tf.constant([[1]],\n                                                         dtype=tf.float32),\n                                             global_step,\n                                             req_time=tf.constant(\n                                                 0, dtype=tf.int64)).as_op()\n\n    global_step = _get_id_tensor(0)\n    assign_op_2 = hash_table.apply_gradients(_get_id_tensor([2]),\n                                             tf.constant([[2]],\n                                                         dtype=tf.float32),\n                                             global_step,\n                                             req_time=tf.constant(\n                                                 0, dtype=tf.int64)).as_op()\n\n    embedding = hash_table.lookup(_get_id_tensor([1, 2]))\n    saver_listener = ops.HashTableCheckpointSaverListener(basename)\n    # We need to create some variables to make saver happy.\n    tf.compat.v1.train.create_global_step()\n    saver = tf.compat.v1.train.Saver(tf.compat.v1.global_variables(),\n                                     sharded=True,\n                                     max_to_keep=10,\n                                     keep_checkpoint_every_n_hours=2)\n    saver_hook = tf.estimator.CheckpointSaverHook(os.path.dirname(basename),\n                                                  save_steps=1000,\n                                                  saver=saver,\n                                                  listeners=[saver_listener])\n    restorer_listener = ops.HashTableCheckpointRestorerListener(basename)\n    restore_hook = basic_restore_hook.CheckpointRestorerHook(\n        listeners=[restorer_listener])\n\n    with self.session() as sess:\n      saver_hook.begin()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      # In the estimator API, graph will be finalized before calling hook\n      g = tf.compat.v1.get_default_graph()\n      g.finalize()\n      sess.run(assign_op_1)\n      sess.run(assign_op_2)\n      embedding_values = sess.run(embedding)\n      self.assertAllEqual(embedding_values, [[-1], [-2]])\n\n      saver_hook.after_create_session(sess, None)\n      restore_hook.after_create_session(sess, None)\n      embedding_values = sess.run(embedding)\n      self.assertAllEqual(embedding, [[-1], [-2]])\n\n  def test_save_restore_hook_with_same_req_time_feature_eviction_apply_gradients(\n      self):\n    basename = os.path.join(\n        os.environ[\"TEST_TMPDIR\"],\n        \"test_save_restore_hook_with_same_req_time_feature_eviction_apply_gradients\",\n        \"model.ckpt\")\n    hash_filter = hash_filter_ops.create_dummy_hash_filter()\n    hash_table = test_hash_table(dim_size=1, expire_time=1)\n    global_step = _get_id_tensor(0)\n    assign_op_1 = hash_table.apply_gradients(_get_id_tensor([1]),\n                                             tf.constant([[1]],\n                                                         dtype=tf.float32),\n                                             global_step,\n                                             req_time=tf.constant(\n                                                 100, dtype=tf.int64)).as_op()\n\n    global_step = _get_id_tensor(0)\n    assign_op_2 = hash_table.apply_gradients(_get_id_tensor([2]),\n                                             tf.constant([[2]],\n                                                         dtype=tf.float32),\n                                             global_step,\n                                             req_time=tf.constant(\n                                                 100, dtype=tf.int64)).as_op()\n\n    embedding = hash_table.lookup(_get_id_tensor([1, 2]))\n    saver_listener = ops.HashTableCheckpointSaverListener(basename)\n    # We need to create some variables to make saver happy.\n    tf.compat.v1.train.create_global_step()\n    saver = tf.compat.v1.train.Saver(tf.compat.v1.global_variables(),\n                                     sharded=True,\n                                     max_to_keep=10,\n                                     keep_checkpoint_every_n_hours=2)\n    saver_hook = tf.estimator.CheckpointSaverHook(os.path.dirname(basename),\n                                                  save_steps=1000,\n                                                  saver=saver,\n                                                  listeners=[saver_listener])\n    restorer_listener = ops.HashTableCheckpointRestorerListener(basename)\n    restore_hook = basic_restore_hook.CheckpointRestorerHook(\n        listeners=[restorer_listener])\n\n    with self.session() as sess:\n      saver_hook.begin()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      # In the estimator API, graph will be finalized before calling hook\n      g = tf.compat.v1.get_default_graph()\n      g.finalize()\n      sess.run(assign_op_1)\n      sess.run(assign_op_2)\n      embedding_values = sess.run(embedding)\n      self.assertAllEqual(embedding_values, [[-1], [-2]])\n\n      saver_hook.after_create_session(sess, None)\n      restore_hook.after_create_session(sess, None)\n      embedding_values = sess.run(embedding)\n      self.assertAllEqual(embedding, [[-1], [-2]])\n\n  def test_delete_save_path(self):\n    basename = os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_delete_save_path\",\n                            \"model.ckpt\")\n    helper = save_utils.SaveHelper(basename)\n\n    class HashTableCheckpointRestore(ops.HashTableCheckpointRestorerListener):\n\n      def restore_checkpoint(self, sess, global_step_value):\n        path_prefix = helper.get_ckpt_asset_dir(\n            helper.get_ckpt_prefix(global_step_value))\n        self._restore_from_path_prefix(sess, path_prefix)\n\n    class HashFilterCheckpointRestore(\n        hash_filter_ops.HashFilterCheckpointRestorerListener):\n\n      def restore_checkpoint(self, sess, global_step_value):\n        path_prefix = helper.get_ckpt_asset_dir(\n            helper.get_ckpt_prefix(global_step_value))\n        self._restore_from_path_prefix(sess, path_prefix)\n\n    config = embedding_hash_table_pb2.SlotOccurrenceThresholdConfig()\n    config.default_occurrence_threshold = 0\n    enable_hash_filter = True\n    hash_filters = hash_filter_ops.create_hash_filters(\n        0, enable_hash_filter, config.SerializeToString())\n    hash_table = test_hash_table_with_hash_filters(dim_size=1,\n                                                   hash_filters=hash_filters)\n    add_op = hash_table.assign_add(_get_id_tensor([0]),\n                                   tf.constant([[1]],\n                                               dtype=tf.float32)).as_op()\n    sub_op = hash_table.assign_add(_get_id_tensor([0]),\n                                   tf.constant([[-1]],\n                                               dtype=tf.float32)).as_op()\n    lookup_op = hash_table.lookup(_get_id_tensor([0]))\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    train_op = tf.compat.v1.assign_add(global_step, 1)\n\n    hash_table_saver_listener = ops.HashTableCheckpointSaverListener(basename)\n    hash_filter_saver_listener = hash_filter_ops.HashFilterCheckpointSaverListener(\n        basename, hash_filters, True)\n    saver = save_utils.PartialRecoverySaver(tf.compat.v1.global_variables(),\n                                            sharded=True,\n                                            max_to_keep=1,\n                                            keep_checkpoint_every_n_hours=2)\n    saver_hook = save_utils.NoFirstSaveCheckpointSaverHook(\n        os.path.dirname(basename),\n        save_steps=1,\n        saver=saver,\n        listeners=[hash_table_saver_listener, hash_filter_saver_listener])\n\n    hash_table_restorer_listener = HashTableCheckpointRestore(basename)\n    hash_filter_restorer_listener = HashFilterCheckpointRestore(\n        basename, hash_filters, True)\n\n    with tf.compat.v1.train.SingularMonitoredSession(\n        hooks=[saver_hook],\n        checkpoint_dir=os.path.dirname(basename)) as mon_sess:\n      sess = mon_sess.raw_session()\n      sess.run(add_op)\n      # let saving happen in step 1 and step 10.\n      mon_sess.run(train_op)\n      for _ in range(8):\n        sess.run(train_op)\n      mon_sess.run(train_op)\n\n      # hash table checkpoint 1 is deleted.\n      with self.assertRaises(Exception):\n        hash_table_restorer_listener.restore_checkpoint(sess, 1)\n      # hash filter checkpoint 1 is deleted.\n      with self.assertRaises(Exception):\n        hash_filter_restorer_listener.restore_checkpoint(sess, 1)\n      sess.run(sub_op)\n      # checkpoint 10 is OK.\n      hash_table_restorer_listener.restore_checkpoint(sess, 10)\n      hash_filter_restorer_listener.restore_checkpoint(sess, 10)\n      embedding = sess.run(lookup_op)\n      self.assertAllEqual(embedding, [[1]])\n\n  def test_save_restore_with_hash_table_clear_logic(self):\n    basename = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                            \"test_save_restore_with_hash_table_clear_logic\",\n                            \"model.ckpt\")\n    hash_filter = hash_filter_ops.create_dummy_hash_filter()\n    hash_table = test_hash_table(1)\n    add_op_0 = hash_table.assign_add(_get_id_tensor([0]),\n                                     tf.constant([[1]],\n                                                 dtype=tf.float32)).as_op()\n    add_op_1 = hash_table.assign_add(_get_id_tensor([1]),\n                                     tf.constant([[1]],\n                                                 dtype=tf.float32)).as_op()\n    embedding_0 = hash_table.lookup(_get_id_tensor([0]))\n    embedding_1 = hash_table.lookup(_get_id_tensor([1]))\n    saver_listener = ops.HashTableCheckpointSaverListener(basename)\n    # We need to create some variables to make saver happy.\n    tf.compat.v1.train.create_global_step()\n    saver = tf.compat.v1.train.Saver(tf.compat.v1.global_variables(),\n                                     sharded=True,\n                                     max_to_keep=10,\n                                     keep_checkpoint_every_n_hours=2)\n    saver_hook = tf.estimator.CheckpointSaverHook(os.path.dirname(basename),\n                                                  save_steps=1000,\n                                                  saver=saver,\n                                                  listeners=[saver_listener])\n    restorer_listener = ops.HashTableCheckpointRestorerListener(basename)\n    restore_hook = basic_restore_hook.CheckpointRestorerHook(\n        listeners=[restorer_listener])\n\n    with self.session() as sess:\n      saver_hook.begin()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      # In the estimator API, graph will be finalized before calling hook\n      g = tf.compat.v1.get_default_graph()\n      g.finalize()\n      sess.run(add_op_0)\n      saver_hook.after_create_session(sess, None)\n\n      sess.run(add_op_1)\n\n      embedding_value = sess.run(embedding_1)\n      self.assertAllEqual(embedding_value, [[1]])\n\n      restore_hook.after_create_session(sess, None)\n\n      # update before save will be restored from checkpoint.\n      embedding_value = sess.run(embedding_0)\n      self.assertAllEqual(embedding_value, [[1]])\n\n      # update after save will not be restored from checkpoint.\n      embedding_value = sess.run(embedding_1)\n      self.assertAllEqual(embedding_value, [[0]])\n\n  def test_hash_table_and_hash_filter_save_restore_hook_together(self):\n    basename = os.path.join(\n        os.environ[\"TEST_TMPDIR\"],\n        \"test_hash_table_and_hash_filter_save_restore_hook_together\",\n        \"model.ckpt\")\n\n    config = embedding_hash_table_pb2.SlotOccurrenceThresholdConfig()\n    config.default_occurrence_threshold = 2\n    enable_hash_filter = True\n    hash_filters = hash_filter_ops.create_hash_filters(\n        0, enable_hash_filter, config.SerializeToString())\n    hash_table = test_hash_table_with_hash_filters(dim_size=1,\n                                                   hash_filters=hash_filters)\n    add_op = hash_table.assign_add(_get_id_tensor([0]),\n                                   tf.constant([[1]],\n                                               dtype=tf.float32)).as_op()\n    embedding = hash_table.lookup(_get_id_tensor([0]))\n    hash_table_saver_listener = ops.HashTableCheckpointSaverListener(basename)\n    hash_filter_saver_listener = hash_filter_ops.HashFilterCheckpointSaverListener(\n        basename, hash_filters, enable_hash_filter)\n\n    # We need to create some variables to make saver happy.\n    tf.compat.v1.train.create_global_step()\n    saver = tf.compat.v1.train.Saver(tf.compat.v1.global_variables(),\n                                     sharded=True,\n                                     max_to_keep=10,\n                                     keep_checkpoint_every_n_hours=2)\n    saver_hook = tf.estimator.CheckpointSaverHook(\n        os.path.dirname(basename),\n        save_steps=1000,\n        saver=saver,\n        listeners=[hash_table_saver_listener, hash_filter_saver_listener])\n\n    hash_table_restorer_listener = ops.HashTableCheckpointRestorerListener(\n        basename)\n    hash_filter_restorer_listener = hash_filter_ops.HashFilterCheckpointRestorerListener(\n        basename, hash_filters, True)\n    restore_hook = basic_restore_hook.CheckpointRestorerHook(\n        listeners=[hash_table_restorer_listener, hash_filter_restorer_listener])\n\n    with self.session() as sess:\n      saver_hook.begin()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      # In the estimator API, graph will be finalized before calling hook\n      g = tf.compat.v1.get_default_graph()\n      g.finalize()\n      # add_op not actually works as count after adding in hash filter is 1.\n      sess.run(add_op)\n      embedding_value = sess.run(embedding)\n      self.assertAllEqual(embedding_value, [[0]])\n      # save hash filter ckpt with count is 1.\n      saver_hook.after_create_session(sess, None)\n\n      embedding_value = sess.run(embedding)\n\n      # add_op not actually works as count after adding in hash filter is 2.\n      sess.run(add_op)\n      embedding_value = sess.run(embedding)\n      self.assertAllEqual(embedding_value, [[0]])\n\n      # add_op works as count after adding in hash filter is 3.\n      sess.run(add_op)\n      embedding_value = sess.run(embedding)\n      self.assertAllEqual(embedding_value, [[1]])\n\n      # restore hash table ckpt (embedding value is 0)\n      # and hash filter ckpt (count is 1)\n      restore_hook.after_create_session(sess, None)\n      embedding_value = sess.run(embedding)\n      self.assertAllEqual(embedding_value, [[0]])\n      #add_op not works as count in hash filter is 2 after it restored from ckpt.\n      sess.run(add_op)\n      embedding_value = sess.run(embedding)\n      self.assertAllEqual(embedding_value, [[0]])\n\n      # add_op works as count after adding in hash filter is 3.\n      sess.run(add_op)\n      embedding_value = sess.run(embedding)\n      self.assertAllEqual(embedding_value, [[1]])\n\n      # restore again to test everything is good.\n      # restore hash table ckpt (embedding value is 0)\n      # and hash filter ckpt (count is 1)\n      restore_hook.after_create_session(sess, None)\n      embedding_value = sess.run(embedding)\n      self.assertAllEqual(embedding_value, [[0]])\n      #add_op not works as count in hash filter is 2 after it restored from ckpt.\n      sess.run(add_op)\n      embedding_value = sess.run(embedding)\n      self.assertAllEqual(embedding_value, [[0]])\n\n      # add_op works as count after adding in hash filter is 3.\n      sess.run(add_op)\n      embedding_value = sess.run(embedding)\n      self.assertAllEqual(embedding_value, [[1]])\n\n  def test_two_hash_table_whose_name_is_prefix(self):\n    with tf.compat.v1.Session() as sess:\n      dim_size = 1\n      hash_table1 = test_hash_table(dim_size)\n      hash_table2 = test_hash_table(dim_size)\n      basename = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                              \"test_two_hash_table_whose_name_is_prefix\")\n      hash_table1 = hash_table1.save(basename + \"/table1\")\n      hash_table2 = hash_table2.save(basename + \"/table10\")\n      sess.run([hash_table1.as_op(), hash_table2.as_op()])\n      hash_table1 = hash_table1.restore(basename + \"/table1\")\n      hash_table2 = hash_table2.restore(basename + \"/table10\")\n      sess.run([hash_table1.as_op(), hash_table2.as_op()])\n\n  def test_fused_lookup(self):\n    with tf.compat.v1.Session() as sess:\n      hash_tables = []\n      dim_sizes = [1, 1, 2]\n      for x in range(len(dim_sizes)):\n        dim_size = dim_sizes[x]\n        hash_table = ops.vocab_hash_table(9, dim_size)\n        hash_table = hash_table.assign(\n            _get_id_tensor([0 + 3 * x, 1 + 3 * x]),\n            tf.ones([2, dim_size]) if x % 2 == 0 else tf.zeros([2, dim_size]))\n        hash_tables.append(hash_table)\n      embeddings = ops.fused_lookup(\n          [hash_table.table for hash_table in hash_tables],\n          _get_id_tensor([0, 4, 6, 1, 3, 7]),\n          fused_slot_size=tf.constant([1, 1, 1, 1, 1, 1]),\n          num_of_shards=2)\n      embeddings, recv_splits, id_offsets, emb_offsets, indices = sess.run(\n          embeddings)\n    self.assertAllEqual(embeddings, [1, 0, 1, 1, 1, 0, 1, 1])\n    self.assertAllEqual(recv_splits, [4, 4])\n    self.assertAllEqual(id_offsets, [0, 1, 2, 3, 4, 5, 6])\n    self.assertAllEqual(emb_offsets, [0, 1, 2, 4, 5, 6, 8])\n\n  def test_fused_optimize(self):\n    with tf.compat.v1.Session() as sess:\n      hash_tables = []\n      dim_sizes = [1, 2]\n      fused_slot_size = tf.constant([1, 1, 1, 1])\n      ids = _get_id_tensor([0, 4, 1, 3])\n      for x in range(len(dim_sizes)):\n        dim_size = dim_sizes[x]\n        hash_table = ops.vocab_hash_table(6, dim_size)\n        hash_table = hash_table.assign(\n            _get_id_tensor([0 + 3 * x, 1 + 3 * x]),\n            tf.ones([2, dim_size]) if x == 0 else tf.zeros([2, dim_size]))\n        hash_tables.append(hash_table)\n      hash_table_resource = [hash_table.table for hash_table in hash_tables]\n      #embeddings=[1, 0, 0, 1, 0, 0]\n      embeddings, recv_splits, id_offsets, emb_offsets, indices = ops.fused_lookup(\n          hash_table_resource, ids, fused_slot_size, num_of_shards=2)\n      new_tables = ops.fused_apply_gradient(hash_table_resource,\n                                            ids,\n                                            indices,\n                                            fused_slot_size,\n                                            tf.constant(\n                                                [-1, -2, -2, -1, -2, -2],\n                                                dtype=tf.float32),\n                                            id_offsets,\n                                            emb_offsets,\n                                            tf.constant([0.1, 0.1],\n                                                        dtype=tf.float32),\n                                            tf.constant(0, dtype=tf.int64),\n                                            tf.constant(0, dtype=tf.int64),\n                                            num_of_shards=2)\n      with tf.control_dependencies(new_tables):\n        lookup_op = ops.fused_lookup(hash_table_resource,\n                                     ids,\n                                     fused_slot_size,\n                                     num_of_shards=2)\n      embeddings, recv_splits, id_offsets, emb_offsets, indices = sess.run(\n          lookup_op)\n    self.assertAllClose(embeddings, [1.1, 0.2, 0.2, 1.1, 0.2, 0.2])\n    self.assertAllEqual(recv_splits, [3, 3])\n    self.assertAllEqual(id_offsets, [0, 1, 2, 3, 4])\n    self.assertAllEqual(emb_offsets, [0, 1, 3, 4, 6])\n\n  def test_batch_softmax_optimizer(self):\n    table_config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n    table_config.cuckoo.SetInParent()\n    segment = table_config.entry_config.segments.add()\n    segment.dim_size = 1\n    segment.opt_config.batch_softmax.SetInParent()\n    segment.init_config.zeros.SetInParent()\n    segment.comp_config.fp32.SetInParent()\n    learning_rate = 0.1\n    config = entry.HashTableConfigInstance(table_config, [learning_rate])\n    with self.session() as sess:\n      hash_table = ops.hash_table_from_config(config=config,\n                                              name_suffix='batch_softmax')\n      for global_step in range(1000):\n        fids = list()\n        if global_step % 5 == 0:\n          fids.append(0)\n        if global_step % 10 == 0:\n          fids.append(1)\n        if len(fids) == 0:\n          continue\n        id_tensor = _get_id_tensor(fids)\n        global_step = _get_id_tensor(global_step)\n        hash_table = hash_table.apply_gradients(id_tensor,\n                                                tf.constant([0.1 for _ in fids],\n                                                            dtype=tf.float32),\n                                                global_step=global_step)\n      item_step_interval = hash_table.lookup(_get_id_tensor([0, 1]))\n      item_step_interval = tf.math.maximum(item_step_interval,\n                                           tf.constant([1.0], dtype=tf.float32))\n      item_step_interval = sess.run(item_step_interval)\n    self.assertAllClose([1 / val for val in item_step_interval], [[0.2], [0.1]],\n                        atol=0.01)\n\n  def test_extract_fid(self):\n    entry = embedding_hash_table_pb2.EntryDump()\n    entry.id = 1 << 48\n    slot_tensor = ops.extract_slot_from_entry([entry.SerializeToString()])\n    self.assertAllEqual(self.evaluate(slot_tensor), [1])\n\n  def test_meta_graph_export(self):\n    table = test_hash_table(2)\n    meta = tf.compat.v1.train.export_meta_graph()\n    self.assertIn(ops._HASH_TABLE_GRAPH_KEY, meta.collection_def)\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hash_table_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom typing import Callable\n\nimport tensorflow as tf\nfrom monolith.native_training.runtime.hash_table import embedding_hash_table_pb2\n\n\n@tf.function\ndef iterate_table_and_apply(table: \"HashTable\",\n                            apply_fn: Callable[[tf.Tensor], None],\n                            limit=1000,\n                            nshards=4,\n                            name=\"IterateTable\"):\n  \"\"\"Iterate the hash table, and call apply_fn for each slice.\n  Args:\n    apply_fn - a fn that accepts a 1-D tf string which is serialized EntryDump.\n    limit - the maximum number of strings that will be fed into apply_fn (to save the memory usage).\n    nshards - the parallelism of calling apply_fn.\n  \"\"\"\n  for i in tf.range(nshards):\n    offset = tf.constant(0, dtype=tf.int64)\n    dump = tf.constant([], dtype=tf.string)\n    while tf.math.equal(tf.size(dump), limit) or tf.math.equal(offset, 0):\n      tf.autograph.experimental.set_loop_options(\n          parallel_iterations=1,\n          shape_invariants=[(dump, tf.TensorShape([None])),\n                            (offset, tf.TensorShape([]))])\n      offset, dump = table.save_as_tensor(i, nshards, limit, offset)\n      apply_fn(dump)\n\n\ndef infer_dim_size(\n    config: embedding_hash_table_pb2.EmbeddingHashTableConfig) -> int:\n  dim_size = 0\n  for segment in config.entry_config.segments:\n    dim_size += segment.dim_size\n  return dim_size\n"
  },
  {
    "path": "monolith/native_training/hash_table_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training import hash_table_utils\nfrom monolith.native_training import hash_table_ops\n\n\nclass HashTableUtilsTest(tf.test.TestCase):\n\n  def test_iterate_table_and_apply(self):\n    with self.session() as sess:\n      table = hash_table_ops.test_hash_table(1)\n      sess.run(\n          table.assign(tf.range(100, dtype=tf.int64), [[0.0]] * 100).as_op())\n      count_var = tf.Variable(0)\n      sess.run(count_var.initializer)\n\n      def count_fn(dump: tf.Tensor):\n        return count_var.assign_add(tf.size(dump), use_locking=True)\n\n      sess.run(\n          hash_table_utils.iterate_table_and_apply(table,\n                                                   count_fn,\n                                                   limit=2,\n                                                   nshards=10))\n      count = sess.run(count_var)\n      self.assertEqual(count, 100)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hooks/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_library\", \"py_test\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\nload(\"@com_github_grpc_grpc//bazel:python_rules.bzl\", \"py_proto_library\")\n\npackage(\n    default_visibility = [\"//visibility:public\"],\n)\n\npy_library(\n    name = \"session_hooks\",\n    srcs = [\"session_hooks.py\"],\n)\n\npy_test(\n    name = \"session_hooks_test\",\n    srcs = [\"session_hooks_test.py\"],\n    deps = [\n        \":session_hooks\",\n    ],\n)\n\nproto_library(\n    name = \"ckpt_hooks_proto\",\n    srcs = [\"ckpt_hooks.proto\"],\n)\n\npy_proto_library(\n    name = \"ckpt_hooks_py_proto\",\n    deps = [\n        \":ckpt_hooks_proto\",\n    ],\n)\n\npy_library(\n    name = \"ckpt_hooks\",\n    srcs = [\"ckpt_hooks.py\"],\n    deps = [\n        \":ckpt_hooks_py_proto\",\n        \"//monolith/native_training:barrier_ops\",\n        \"//monolith/native_training:graph_meta\",\n    ],\n)\n\npy_test(\n    name = \"ckpt_hooks_test\",\n    srcs = [\"ckpt_hooks_test.py\"],\n    deps = [\n        \":ckpt_hooks\",\n        \"//monolith/native_training:save_utils\",\n    ],\n)\n\nproto_library(\n    name = \"controller_hooks_proto\",\n    srcs = [\"controller_hooks.proto\"],\n)\n\npy_proto_library(\n    name = \"controller_hooks_py_proto\",\n    deps = [\n        \":controller_hooks_proto\",\n    ],\n)\n\npy_library(\n    name = \"controller_hooks\",\n    srcs = [\"controller_hooks.py\"],\n    deps = [\n        \":controller_hooks_py_proto\",\n        \"//monolith/native_training:barrier_ops\",\n        \"//monolith/native_training:utils\",\n    ],\n)\n\npy_test(\n    name = \"controller_hooks_test\",\n    srcs = [\"controller_hooks_test.py\"],\n    deps = [\n        \":controller_hooks\",\n    ],\n)\n\npy_library(\n    name = \"ckpt_info\",\n    srcs = [\"ckpt_info.py\"],\n    deps = [\n        \"//monolith/native_training:hash_table_ops\",\n        \"//monolith/native_training:hash_table_utils\",\n        \"//monolith/native_training:multi_hash_table_ops\",\n        \"//monolith/native_training/proto:ckpt_info_py_proto\",\n    ],\n)\n\npy_test(\n    name = \"ckpt_info_test\",\n    srcs = [\"ckpt_info_test.py\"],\n    deps = [\n        \":ckpt_info\",\n    ],\n)\n\npy_library(\n    name = \"hook_utils\",\n    srcs = [\"hook_utils.py\"],\n)\n\npy_test(\n    name = \"hook_utils_test\",\n    srcs = [\"hook_utils_test.py\"],\n    deps = [\n        \":hook_utils\",\n    ],\n)\n\npy_library(\n    name = \"ps_check_hooks\",\n    srcs = [\"ps_check_hooks.py\"],\n    deps = [\n        \"//monolith/native_training:barrier_ops\",\n        \"//monolith/native_training:logging_ops\",\n        \"//monolith/native_training:utils\",\n        \"//monolith/native_training/runtime/ops:logging_ops_py_proto\",\n    ],\n)\n\npy_test(\n    name = \"ps_check_hooks_test\",\n    srcs = [\"ps_check_hooks_test.py\"],\n    deps = [\n        \":ps_check_hooks\",\n    ],\n)\n\npy_library(\n    name = \"feature_engineering_hooks\",\n    srcs = [\"feature_engineering_hooks.py\"],\n    deps = [\n        \"//idl:example_py_proto\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/hooks/ckpt_hooks.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax=\"proto2\";\n\npackage monolith.hooks;\n\nmessage WorkerCkptInfo {\n  optional int64 global_step = 1;\n}\n\n"
  },
  {
    "path": "monolith/native_training/hooks/ckpt_hooks.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport dataclasses\nimport os\nimport time\n\nfrom absl import logging\nimport tensorflow as tf\nfrom tensorflow.python.data.ops import iterator_ops\nfrom tensorflow.python.data.experimental.ops import distribute_options  # Should be removed after tf2.5\n\nfrom monolith.native_training.hooks import ckpt_hooks_pb2\nfrom monolith.native_training import basic_restore_hook\nfrom monolith.native_training import barrier_ops\nfrom monolith.native_training import graph_meta\n\n\n@dataclasses.dataclass\nclass Meta:\n  info_var: tf.Variable\n  info_var_placeholder: tf.compat.v1.placeholder\n  info_var_assign_op: tf.Operation\n  enable_iter_save_restore: bool = True\n\n\nSAVE_ACTION = \"Save\"\n\n\ndef _get_meta() -> Meta:\n\n  def factory():\n    info_var = tf.compat.v1.get_local_variable(\"WorkerCkptMetaInfo\",\n                                               dtype=tf.string,\n                                               initializer=\"\")\n    info_var_placeholder = tf.compat.v1.placeholder(tf.string, [])\n    info_var_assign_op = info_var.assign(info_var_placeholder)\n    return Meta(info_var=info_var,\n                info_var_placeholder=info_var_placeholder,\n                info_var_assign_op=info_var_assign_op)\n\n  return graph_meta.get_meta(\"worker_ckpt_meta\", factory)\n\n\ndef assign_ckpt_info(session: tf.compat.v1.Session,\n                     info: ckpt_hooks_pb2.WorkerCkptInfo):\n  meta = _get_meta()\n  session.run(meta.info_var_assign_op,\n              feed_dict={meta.info_var_placeholder: info.SerializeToString()})\n\n\ndef get_ckpt_info(\n    session: tf.compat.v1.Session) -> ckpt_hooks_pb2.WorkerCkptInfo:\n  ckpt_info = ckpt_hooks_pb2.WorkerCkptInfo()\n  ckpt_info.ParseFromString(session.run(_get_meta().info_var))\n  return ckpt_info\n\n\nclass BarrierSaverListener(tf.estimator.CheckpointSaverListener):\n  \"\"\"During saving, set up barrier condition to block worker for chief.\"\"\"\n\n  def __init__(self,\n               barrier_op: barrier_ops.BarrierOp,\n               wait_seconds=1,\n               max_pending_seconds=30):\n    self._barrier_op = barrier_op\n    self._wait_seconds = wait_seconds\n    self._max_pending_seconds = max_pending_seconds\n    # Make sure meta is created.\n    self._meta = _get_meta()\n    self._release_barrier = False\n\n  def before_save(self, session, global_step_value):\n    assign_ckpt_info(\n        session, ckpt_hooks_pb2.WorkerCkptInfo(global_step=global_step_value))\n    logging.info(\"Place barrier for saving.\")\n    start_time = time.time()\n    try:\n      self._barrier_op.place_barrier(session, action=SAVE_ACTION)\n      self._release_barrier = True\n    except barrier_ops.BarrierAlreadyPlacedError:\n      logging.info(\"Barrier is placed by someone else already.\")\n\n    while not self._barrier_op.is_all_blocked(session):\n      time.sleep(self._wait_seconds)\n      if time.time() - start_time > self._max_pending_seconds:\n        break\n\n    unblocked_indices = self._barrier_op.get_unblocked_indices(session)\n    if unblocked_indices:\n      logging.info(\"Unblocked worker indices: {}.\".format(\n          str(unblocked_indices)))\n    else:\n      logging.info(\"All workers have been blocked.\")\n\n  def after_save(self, session, global_step_value):\n    start_time = time.time()\n    if self._release_barrier:\n      logging.info(\"Remove barrier for saving.\")\n      self._barrier_op.remove_barrier(session)\n      self._release_barrier = False\n\n\nclass _WorkerCkptRestorerHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, saver: tf.compat.v1.train.Saver, model_dir: str,\n               latest_filename: str):\n    self._saver = saver\n    self._model_dir = model_dir\n    self._latest_filename = latest_filename\n\n  def after_create_session(self, session, coord):\n    latest_ckpt = tf.train.latest_checkpoint(self._model_dir,\n                                             self._latest_filename)\n    if latest_ckpt is not None and self._saver:\n      self._saver.restore(session, latest_ckpt)\n    else:\n      logging.info(\"Skipped worker ckpt restore.\")\n\n\nclass WorkerCkptHelper:\n\n  def __init__(self, model_dir: str, index: int):\n    # Here we try to keep them as similar as tf.data.experimental.CheckpointInputPipelineHook\n    self._model_dir = model_dir\n    self._index = index\n    checkpoint_prefix = \"input_worker_{}\".format(index)\n    self._checkpoint_basename = checkpoint_prefix + \".ckpt\"\n    self._latest_filename = \"checkpoint_\" + checkpoint_prefix\n    iterators = tf.compat.v1.get_collection(iterator_ops.GLOBAL_ITERATORS)\n    saveables = []\n    if _get_meta().enable_iter_save_restore:\n      saveables.extend([\n          iterator_ops._IteratorSaveable(\n              i,\n              i.name,\n              external_state_policy=distribute_options.ExternalStatePolicy.\n              IGNORE) for i in iterators\n      ])\n    else:\n      logging.info(\"The iterator save is disabled.\")\n    if saveables:\n      self._saver = tf.compat.v1.train.Saver(var_list=saveables, sharded=True)\n    else:\n      # Saver will throw error if we try to saveables is an empty list.\n      self._saver = None\n\n  def create_save_iterator_callback(self):\n\n    def callback(action: str, sess: tf.compat.v1.Session):\n      if not action == SAVE_ACTION:\n        return\n      ckpt_info = get_ckpt_info(sess)\n      try:\n        if self._saver:\n          self._saver.save(sess,\n                           os.path.join(self._model_dir,\n                                        self._checkpoint_basename),\n                           global_step=ckpt_info.global_step,\n                           latest_filename=self._latest_filename,\n                           write_meta_graph=False)\n      except tf.errors.UnimplementedError as e:\n        logging.warning(\n            \"Current dataset iterators don't support save. This might be expected. %s\",\n            str(e))\n\n    return callback\n\n  def create_restorer_hook(self):\n    return _WorkerCkptRestorerHook(self._saver, self._model_dir,\n                                   self._latest_filename)\n\n\ndef disable_iterator_save_restore():\n  \"\"\"In some situations (like in ByteDance we feed data via stdin), the \n  input progress is not trackable by tensorflow. In this case, we should\n  disable iterator restore since its state is inaccurate.\n  \n  NOTICE: this function should be called before any creation of classes\n  in this module.\n  \"\"\"\n  _get_meta().enable_iter_save_restore = False\n"
  },
  {
    "path": "monolith/native_training/hooks/ckpt_hooks_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nimport threading\n\nfrom absl import logging\n\nimport tensorflow as tf\n\nfrom monolith.native_training.hooks import ckpt_hooks\nfrom monolith.native_training.hooks import ckpt_hooks_pb2\nfrom monolith.native_training import barrier_ops\nfrom monolith.native_training import save_utils\n\n\nclass CountCheckpointSaverListener(tf.estimator.CheckpointSaverListener):\n\n  def __init__(self):\n    self.begin_count = 0\n    self.before_save_count = 0\n    self.after_save_count = 0\n\n  def begin(self):\n    self.begin_count += 1\n\n  def before_save(self, session, global_step):\n    self.before_save_count += 1\n\n  def after_save(self, session, global_step):\n    self.after_save_count += 1\n\n  def get_counts(self):\n    return {\n        'begin': self.begin_count,\n        'before_save': self.before_save_count,\n        'after_save': self.after_save_count\n    }\n\n\nclass FixedSessionCreator(tf.compat.v1.train.SessionCreator):\n\n  def __init__(self, fixed_sess):\n    self._sess = fixed_sess\n\n  def create_session(self):\n    return self._sess\n\n\nclass WorkerCkptHooksTest(tf.test.TestCase):\n\n  def testIteratorSaveRestore(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"iterator_save\")\n    tf.io.gfile.makedirs(model_dir)\n    ds = tf.data.Dataset.from_tensor_slices([0, 1, 2, 3])\n    it = tf.compat.v1.data.make_one_shot_iterator(ds)\n    next_ele = it.get_next()\n    helper = ckpt_hooks.WorkerCkptHelper(model_dir, 0)\n    with self.session() as sess:\n      sess.run(next_ele)\n      ckpt_hooks.assign_ckpt_info(sess,\n                                  ckpt_hooks_pb2.WorkerCkptInfo(global_step=10))\n      save_callback = helper.create_save_iterator_callback()\n      save_callback(ckpt_hooks.SAVE_ACTION, sess)\n\n      self.assertAllEqual(sess.run(next_ele), 1)\n\n    with tf.compat.v1.train.MonitoredSession(\n        hooks=[helper.create_restorer_hook()]) as sess:\n      # Restore happens\n      self.assertAllEqual(sess.run(next_ele), 1)\n\n  def testNoCkpt(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"no_ckpt\")\n    helper = ckpt_hooks.WorkerCkptHelper(model_dir, 0)\n    with tf.compat.v1.train.MonitoredSession(\n        hooks=[helper.create_restorer_hook()]) as sess:\n      pass\n\n  def testNoSaveables(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"no_saveables\")\n    tf.io.gfile.makedirs(model_dir)\n    helper = ckpt_hooks.WorkerCkptHelper(model_dir, 0)\n    with self.session() as sess:\n      ckpt_hooks.assign_ckpt_info(sess,\n                                  ckpt_hooks_pb2.WorkerCkptInfo(global_step=10))\n      save_callback = helper.create_save_iterator_callback()\n      save_callback(ckpt_hooks.SAVE_ACTION, sess)\n\n  def testCkptDisabled(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"ckpt_disabled\")\n    tf.io.gfile.makedirs(model_dir)\n    ds = tf.data.Dataset.from_tensor_slices([0, 1, 2, 3])\n    it = tf.compat.v1.data.make_one_shot_iterator(ds)\n    next_ele = it.get_next()\n    ckpt_hooks.disable_iterator_save_restore()\n    helper = ckpt_hooks.WorkerCkptHelper(model_dir, 0)\n    with self.session() as sess:\n      sess.run(next_ele)\n      ckpt_hooks.assign_ckpt_info(sess,\n                                  ckpt_hooks_pb2.WorkerCkptInfo(global_step=10))\n      save_callback = helper.create_save_iterator_callback()\n      save_callback(ckpt_hooks.SAVE_ACTION, sess)\n\n      self.assertAllEqual(sess.run(next_ele), 1)\n\n    with tf.compat.v1.train.MonitoredSession(\n        hooks=[helper.create_restorer_hook()]) as sess:\n      # Restore should not happen\n      self.assertAllEqual(sess.run(next_ele), 0)\n\n  def test_saver_with_barrier(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"saver_with_barrier\")\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    train_op = tf.compat.v1.assign_add(global_step, 1)\n    barrier_op = barrier_ops.BarrierOp(2, False)\n    listener1 = ckpt_hooks.BarrierSaverListener(barrier_op)\n    listener2 = CountCheckpointSaverListener()\n    hook = save_utils.NoFirstSaveCheckpointSaverHook(\n        model_dir,\n        save_steps=1,\n        listeners=[listener1, listener2],\n        saver=tf.compat.v1.train.Saver())\n\n    class WaitAllWorkersHook(tf.estimator.SessionRunHook):\n\n      def end(self, session):\n        nonlocal barrier_op\n        while not barrier_op.is_none_blocked(session):\n          time.sleep(0.1)\n\n    with tf.compat.v1.Session() as sess:\n\n      g = tf.compat.v1.get_default_graph()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(tf.compat.v1.local_variables_initializer())\n\n      def run():\n        with g.as_default(), tf.compat.v1.train.MonitoredSession(\n            session_creator=FixedSessionCreator(sess),\n            hooks=[hook, WaitAllWorkersHook()]) as mon_sess:\n          mon_sess.run(train_op)\n\n      worker = threading.Thread(target=run)\n      worker.daemon = True\n      worker.start()\n      while not barrier_op.is_barrier_placed(sess):\n        time.sleep(0.1)\n      # Barrier is placed by save listener.\n      self.assertEqual(1, sess.run(global_step))\n      self.assertEqual({\n          'begin': 1,\n          'before_save': 0,\n          'after_save': 0,\n      }, listener2.get_counts())\n\n      print(\"Start to wait\")\n      barrier_op.wait_until_barrier_removed(sess, 1)\n      worker.join()\n      self.assertEqual({\n          'begin': 1,\n          'before_save': 1,\n          'after_save': 1,\n      }, listener2.get_counts())\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hooks/ckpt_info.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nfrom typing import DefaultDict\n\nimport numpy as np\nimport tensorflow as tf\n\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import hash_table_utils\nfrom monolith.native_training import multi_hash_table_ops\nfrom monolith.native_training.proto import ckpt_info_pb2\n\n_MAX_SLOT = 102400\n\n\nclass FidSlotCountSaverListener(tf.estimator.CheckpointSaverListener):\n\n  def __init__(self, model_dir: str):\n    self._model_dir = model_dir\n    all_tables = tf.compat.v1.get_collection(\n        hash_table_ops._HASH_TABLE_GRAPH_KEY)\n    self.all_multi_hash_tables = tf.compat.v1.get_collection(\n        multi_hash_table_ops._MULTI_HASH_TABLE_GRAPH_KEY)\n    if not all_tables and not self.all_multi_hash_tables:\n      # MultiHashTable info is collected in a different way.\n      # This usually means the listener is created before hash table is created\n      # Throws an error here\n      raise ValueError(\n          (\"Unable to find hash tables. \"\n           \"It may be caused by creating the listener before calling model_fn\"))\n\n    device_to_tables = DefaultDict(list)\n    for table in all_tables:\n      device_to_tables[table.table.device].append(table)\n\n    self._count_vars = {}\n    count_ops = []\n\n    for device, tables in device_to_tables.items():\n      with tf.device(device):\n        device_unique_str = str(device).replace(\":\", \"_\")\n        count_var = tf.compat.v1.get_variable(\n            f\"monolith_fid_slot_count/{device_unique_str}\",\n            shape=[_MAX_SLOT],\n            dtype=tf.int64,\n            initializer=tf.compat.v1.zeros_initializer(tf.int64),\n            collections=[])\n\n        self._count_vars[device] = count_var\n\n        def apply_fn(entry):\n          slot = hash_table_ops.extract_slot_from_entry(entry)\n          slot = tf.math.minimum(slot, _MAX_SLOT - 1)\n          update = tf.ones_like(slot, dtype=tf.int64)\n          index = tf.reshape(slot, [-1, 1])\n          scattered = tf.scatter_nd(index, update, [_MAX_SLOT])\n          count_var.assign_add(scattered, use_locking=True)\n\n        for table in tables:\n          count_ops.append(\n              hash_table_utils.iterate_table_and_apply(table, apply_fn))\n\n    self._count_op = tf.group(count_ops)\n\n    init_ops = []\n    for count_var in self._count_vars.values():\n      init_ops.append(count_var.initializer)\n    self._init_op = tf.group(init_ops)\n\n  def before_save(self, session, global_step_value):\n    if self.all_multi_hash_tables:\n      return\n    session.run(self._init_op)\n    session.run(self._count_op)\n    counts = session.run(list(self._count_vars.values()))\n    counts = np.sum(counts, axis=0)\n    info = ckpt_info_pb2.CkptInfo()\n    for slot, count in enumerate(counts):\n      if count:\n        info.slot_counts[slot] = count\n    tf.io.gfile.makedirs(self._model_dir)\n    with tf.io.gfile.GFile(\n        os.path.join(self._model_dir, f\"ckpt.info-{global_step_value}\"),\n        \"w\") as f:\n      f.write(str(info))\n"
  },
  {
    "path": "monolith/native_training/hooks/ckpt_info_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nfrom google.protobuf import text_format\nimport tensorflow as tf\n\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training.hooks import ckpt_info\nfrom monolith.native_training.proto import ckpt_info_pb2\n\n\nclass FidCountListener(tf.test.TestCase):\n\n  def test_basic(self):\n    h = hash_table_ops.test_hash_table(1)\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"basic\")\n    h = h.assign(tf.constant([1], dtype=tf.int64), [[0.0]])\n    l = ckpt_info.FidSlotCountSaverListener(model_dir)\n    with self.session() as sess:\n      sess.run(h.as_op())\n      l.before_save(sess, 0)\n    with tf.io.gfile.GFile(os.path.join(model_dir, \"ckpt.info-0\")) as f:\n      text = f.read()\n\n    ckpt = ckpt_info_pb2.CkptInfo()\n    text_format.Parse(text, ckpt)\n    self.assertEqual(ckpt.slot_counts[0], 1)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hooks/controller_hooks.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\n\npackage monolith;\n\nmessage ControllerHooksProto {\n  enum Action {\n    UNKNOWN = 0;\n    TRIGGER_SAVE = 1;\n    STOP = 2;\n  }\n  optional Action action = 1;\n}\n"
  },
  {
    "path": "monolith/native_training/hooks/controller_hooks.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport contextlib\nimport os\nimport threading\nimport time\nimport traceback\nfrom typing import Callable\n\nfrom absl import logging\nfrom google.protobuf import text_format\nimport tensorflow as tf\n\nfrom monolith.native_training.hooks import controller_hooks_pb2\nfrom monolith.native_training import barrier_ops\nfrom monolith.native_training import utils\n\nSTOP_ACTION = \"Stop\"\n\n\nclass ControllerHook(tf.estimator.SessionRunHook):\n\n  def __init__(self,\n               num_ps=0,\n               barrier_op: barrier_ops.BarrierOp = None,\n               trigger_save: Callable = None):\n    self._barrier_op = barrier_op\n    self._trigger_save = trigger_save\n    device_ctx = tf.device(\n        utils.ps_device(0)) if num_ps > 0 else contextlib.nullcontext()\n    with tf.name_scope(\"monolith_controller_hook\"), device_ctx:\n      self._control_var = tf.compat.v1.get_local_variable(\n          \"control_var\", initializer=[False, False], trainable=False)\n      self._stop_op = self._control_var[0].assign(True)\n      self._trigger_save_op = self._control_var[1].assign(True)\n      self._reset_trigger_save_op = self._control_var[1].assign(False)\n\n  @property\n  def stop_op(self):\n    return self._stop_op\n\n  @property\n  def trigger_save_op(self):\n    return self._trigger_save_op\n\n  def before_run(self, run_context):\n    return tf.estimator.SessionRunArgs(self._control_var)\n\n  def after_run(self, run_context, run_values):\n    if run_values.results[0]:\n      if self._barrier_op:\n        self._barrier_op.place_barrier(run_context.session, action=STOP_ACTION)\n        logging.info(\"Trying to stop all workers.\")\n        start_time = time.time()\n        while time.time(\n        ) - start_time < 30 and not self._barrier_op.is_all_blocked(\n            run_context.session):\n          time.sleep(2)\n        self._barrier_op.remove_barrier(run_context.session)\n    elif run_values.results[1]:\n      run_context.session.run(self._reset_trigger_save_op)\n      if self._trigger_save:\n        self._trigger_save()\n\n\nclass _StopHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, should_stop_fn):\n    self._should_stop_fn = should_stop_fn\n\n  def after_run(self, run_context, run_values):\n    if self._should_stop_fn():\n      run_context.request_stop()\n\n\nclass StopHelper:\n\n  def __init__(self):\n    self._should_stop = False\n\n  def create_barrier_callback(self):\n\n    def callback(action: str, sess: tf.compat.v1.Session):\n      if action != STOP_ACTION:\n        return\n      self._should_stop = True\n      logging.info(\"Receive the request to stop the training.\")\n\n    return callback\n\n  def create_stop_hook(self):\n\n    def should_stop():\n      return self._should_stop\n\n    return _StopHook(should_stop)\n\n\nQUERY_INTERVAL = 60\n\n\nclass QueryActionHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, model_dir: str, hook: ControllerHook):\n    self._query_path = os.path.join(model_dir, \"monolith_action\")\n    self._resp_path = os.path.join(model_dir, \"monolith_action_response\")\n    self._hook = hook\n    self._session = None\n    self._th = None\n    self._close = threading.Event()\n\n  def after_create_session(self, session, coord):\n    self._session = session\n    self._th = threading.Thread(name=\"QuertActionHookThread\",\n                                target=self._query_loop,\n                                daemon=True)\n    self._th.start()\n\n  def end(self, session):\n    self._close.set()\n    if self._th:\n      self._th.join()\n\n  def _query_loop(self):\n    while True:\n      if self._close.wait(timeout=QUERY_INTERVAL):\n        break\n      try:\n        self._query()\n      except:\n        logging.error(traceback.format_exc())\n\n  def _query(self):\n    if not tf.io.gfile.exists(self._query_path):\n      return\n    with tf.io.gfile.GFile(self._query_path, \"r\") as f:\n      text_proto = f.read()\n    try:\n      proto = controller_hooks_pb2.ControllerHooksProto()\n      try:\n        text_format.Parse(text_proto, proto)\n      except text_format.ParseError as e:\n        self._write_resp(str(e))\n        return\n      if proto.action == controller_hooks_pb2.ControllerHooksProto.TRIGGER_SAVE:\n        self._session.run(self._hook.trigger_save_op)\n      elif proto.action == controller_hooks_pb2.ControllerHooksProto.STOP:\n        self._session.run(self._hook.stop_op)\n      else:\n        self._write_resp(\"Unknown action: \", text_proto)\n        return\n      self._write_resp(\"OK\")\n    finally:\n      tf.io.gfile.remove(self._query_path)\n\n  def _write_resp(self, content: str):\n    with tf.io.gfile.GFile(self._resp_path, \"w\") as f:\n      f.write(content)\n"
  },
  {
    "path": "monolith/native_training/hooks/controller_hooks_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nfrom unittest import mock\n\nimport tensorflow as tf\n\nfrom monolith.native_training import barrier_ops\nfrom monolith.native_training.hooks import controller_hooks\n\n\nclass ControllerHookTest(tf.test.TestCase):\n\n  def testStop(self):\n    helper = controller_hooks.StopHelper()\n    op = barrier_ops.BarrierOp(\n        1, barrier_callbacks=[helper.create_barrier_callback()])\n    h1 = controller_hooks.ControllerHook(barrier_op=op)\n    h2 = helper.create_stop_hook()\n    dummy = tf.Variable(1)\n    with tf.compat.v1.train.SingularMonitoredSession(hooks=[h1, h2]) as sess:\n      sess.run(dummy)\n      self.assertFalse(sess.should_stop())\n      sess.run(h1.stop_op)\n      # Session might be stopped. But request_stop might be fetched before\n      # we run stop_op, so it is possible that it is not stopped yet.\n      if not sess.should_stop():\n        # Do a dummy run again. Session must be stopped after this.\n        sess.run(dummy)\n        self.assertTrue(sess.should_stop())\n\n  def testSave(self):\n    trigger_save = mock.MagicMock()\n    h = controller_hooks.ControllerHook(trigger_save=trigger_save)\n    dummy = tf.Variable(1)\n    with tf.compat.v1.train.SingularMonitoredSession(hooks=[h]) as sess:\n      sess.run(h.trigger_save_op)\n      sess.run(dummy)\n      sess.run(dummy)\n      trigger_save.assert_called_once()\n\n\nclass QueryActionHookTest(tf.test.TestCase):\n\n  @mock.patch(\"monolith.native_training.hooks.controller_hooks.QUERY_INTERVAL\",\n              0.1)\n  def testStop(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                             \"QueryActionHookTest_testStop\")\n    trigger_save = mock.MagicMock()\n    h = controller_hooks.ControllerHook(trigger_save=trigger_save)\n    qh = controller_hooks.QueryActionHook(model_dir, h)\n    dummy = tf.constant(0)\n    with tf.compat.v1.train.SingularMonitoredSession(hooks=[h, qh]) as sess:\n      tf.io.gfile.makedirs(model_dir)\n      query_path = os.path.join(model_dir, \"monolith_action\")\n      with tf.io.gfile.GFile(query_path, \"w\") as f:\n        f.write(\"action: TRIGGER_SAVE\")\n      now = time.time()\n      while time.time() - now < 60 and tf.io.gfile.exists(query_path):\n        time.sleep(0.1)\n      sess.run(dummy)\n      sess.run(dummy)\n      trigger_save.assert_called_once()\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hooks/feature_engineering_hooks.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nimport uuid as gen_uid\nimport os\nfrom struct import pack\nfrom absl import logging\n\nfrom idl.matrix.proto.example_pb2 import ExampleBatch, FeatureListType\n\n\nclass FeatureEngineeringSaveHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, config, nxt_elem, cap=100):\n    self._config = config\n    self._nxt_elem = nxt_elem\n    self._cap = cap\n\n  def begin(self):\n    self._batch_list = []  # List[Dict[str, tf.Tensor]]\n    self._steps = 0\n\n  def before_run(self, run_context):\n    self._steps += 1\n    # skip iter init\n    if self._steps > 1:\n      return tf.compat.v1.train.SessionRunArgs(self._nxt_elem)\n\n  def _save_features(self):\n    base_dir = os.path.join(self._config.model_dir, \"features\")\n    try:\n      tf.io.gfile.makedirs(base_dir)\n    except tf.errors.OpError:\n      pass\n    file_path = \"\"\n    if self._config.server_type == \"worker\" and self._config.index == 0:\n      file_path = os.path.join(base_dir,\n                               \"chief_\" + str(gen_uid.uuid1()) + \".pb\")\n    else:\n      file_path = os.path.join(\n          base_dir, \"worker\" + str(self._config.index) + \"_\" +\n          str(gen_uid.uuid1()) + \".pb\")\n\n    results = []\n    for batch in self._batch_list:\n      # batch to ExampleBatch\n      example_batch = ExampleBatch()\n      for k, v in batch.items():\n        named_feature_list = example_batch.named_feature_list.add()\n        named_feature_list.name = k\n        named_feature_list.type = FeatureListType.INDIVIDUAL\n\n        if isinstance(v, tf.compat.v1.ragged.RaggedTensorValue):\n          lv = v.to_list()\n        else:  # np.ndarray\n          lv = v.tolist()\n\n        for fids in lv:\n          feature = named_feature_list.feature.add()\n          if len(fids) > 0 and isinstance(fids[0], float):\n            feature.float_list.value.extend(fids)\n          else:\n            feature.fid_v2_list.value.extend(fids)\n\n        example_batch.batch_size = len(lv)\n      results.append(example_batch)\n\n    with tf.io.gfile.GFile(file_path, \"w\") as f:\n      for example_batch in results:\n        ss = example_batch.SerializeToString()\n        sz = len(ss)\n        f.write(pack('<Q', 0))  # lagrange header\n        f.write(pack('<Q', sz))\n        f.write(ss)\n    logging.info(\"save to %s\", file_path)\n\n  def after_run(self, run_context, run_values):\n    if self._steps > 1:\n      self._batch_list.append(run_values.results)\n      if len(self._batch_list) >= self._cap:\n        self._save_features()\n        self._batch_list.clear()\n\n  def end(self, session):\n    if len(self._batch_list) >= 0:\n      self._save_features()\n      self._batch_list.clear()\n"
  },
  {
    "path": "monolith/native_training/hooks/hook_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\n\nclass BeforeSaveListener(tf.estimator.CheckpointSaverListener):\n  \"\"\"Only calls before save in the listener\"\"\"\n\n  def __init__(self, listener: tf.estimator.CheckpointSaverListener):\n    self._listener = listener\n\n  def before_save(self, session, global_step_value):\n    self._listener.before_save(session, global_step_value)\n\n  def __repr__(self):\n    return super().__repr__() + repr(self._listener)\n\n\nclass AfterSaveListener(tf.estimator.CheckpointSaverListener):\n  \"\"\"Only calls after save in the listener\"\"\"\n\n  def __init__(self, listener: tf.estimator.CheckpointSaverListener):\n    self._listener = listener\n\n  def after_save(self, session, global_step_value):\n    self._listener.after_save(session, global_step_value)\n\n  def __repr__(self):\n    return super().__repr__() + repr(self._listener)\n"
  },
  {
    "path": "monolith/native_training/hooks/hook_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training.hooks import hook_utils\n\n\nclass HookUtilsTest(tf.test.TestCase):\n\n  def testBeforeAfterSaverListener(self):\n    # This is mainly for testing compiling\n    base_l = tf.estimator.CheckpointSaverListener()\n    l1 = hook_utils.BeforeSaveListener(base_l)\n    l2 = hook_utils.AfterSaveListener(base_l)\n    with self.session() as sess:\n      l1.before_save(sess, 0)\n      l1.after_save(sess, 0)\n      l2.before_save(sess, 0)\n      l2.after_save(sess, 0)\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hooks/ps_check_hooks.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport threading\nfrom typing import Callable, Dict, NamedTuple\n\nfrom absl import logging\nfrom google.protobuf import text_format\nimport tensorflow as tf\n\nfrom monolith.native_training import barrier_ops\nfrom monolith.native_training import logging_ops\nfrom monolith.native_training import utils\nfrom monolith.native_training.runtime.ops import logging_ops_pb2\n\n\ndef get_ps_machine_info_shared_name(index: int):\n  return f\"ps_machine_info_{index}\"\n\n\ndef _default_report(results: Dict[int, logging_ops_pb2.MachineHealthResult]):\n  debugging_strs = []\n  for idx, result in results.items():\n    debugging_strs.append(\n        f\"PS {idx}: {text_format.MessageToString(result, as_one_line=True)}\")\n  logging.error(\"PS are not healthy:\\n%s\", \"\\n\".join(debugging_strs))\n  # TODO(leqi.zou): Give some alerts\n\n\nclass Config(NamedTuple):\n  barrier_op: barrier_ops.BarrierOp\n  num_ps: int\n  ps_device_fn: Callable[[int], str] = utils.ps_device\n  report_fn: Callable[[Dict[int, str]], None] = _default_report\n\n\nclass _PsHealthChecker:\n\n  def __init__(self, config: Config):\n    self._config = config\n    # self._cancel = threading.Event()\n    self._machine_status_tensors = []\n    for i in range(config.num_ps):\n      with tf.device(config.ps_device_fn(i)):\n        handle = logging_ops.machine_info(\n            shared_name=get_ps_machine_info_shared_name(i))\n        self._machine_status_tensors.append(\n            logging_ops.check_machine_health(handle))\n\n  def create_threads(self, sess, coord: tf.train.Coordinator):\n    # Daemon is important. It seems that if we have the error in the\n    # after_create_session phase, the coordinator will never stop so\n    # the process will be stuck forever.\n    th = threading.Thread(target=self._run, args=(sess, coord), daemon=True)\n    coord.register_thread(th)\n    th.start()\n\n  def _run(self, sess, coord: tf.train.Coordinator):\n    while not coord.should_stop():\n      status_list = sess.run(self._machine_status_tensors)\n      results = {}\n      should_stop = False\n      for idx, status in enumerate(status_list):\n        if len(status) > 0:\n          should_stop = True\n          result = logging_ops_pb2.MachineHealthResult()\n          result.ParseFromString(status)\n          results[idx] = result\n      if should_stop:\n        self._config.report_fn(results)\n        self._config.barrier_op.place_barrier(sess)\n        coord.wait_for_stop()\n      coord.wait_for_stop(timeout=30.0)\n\n\nclass PsHealthCheckerHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, config: Config):\n    self._config = config\n    self._checker = None\n\n  def begin(self):\n    self._checker = _PsHealthChecker(self._config)\n\n  def after_create_session(self, session, coord):\n    self._checker.create_threads(session, coord)\n"
  },
  {
    "path": "monolith/native_training/hooks/ps_check_hooks_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport time\nfrom unittest import mock\n\nimport tensorflow as tf\n\nfrom monolith.native_training import barrier_ops\nfrom monolith.native_training import logging_ops\nfrom monolith.native_training.hooks import ps_check_hooks\nfrom monolith.native_training.runtime.ops import logging_ops_pb2\n\n\nclass PrepareMachineInfoHook(tf.estimator.SessionRunHook):\n  \"\"\"Used to create machine info after session creation\"\"\"\n\n  def __init__(self, machine_info):\n    self._machine_info = machine_info\n\n  def after_create_session(self, session, coord):\n    session.run(self._machine_info)\n\n\nclass RaiseErrorHook(tf.estimator.SessionRunHook):\n\n  def __init__(self,\n               raise_in_after_create_session=False,\n               raise_in_before_run=False):\n    self.raise_in_after_create_session = raise_in_after_create_session\n    self.raise_in_before_run = raise_in_before_run\n    self.exc = tf.errors.DeadlineExceededError(None, None, \"Test exception\")\n\n  def after_create_session(self, session, coord):\n    if self.raise_in_after_create_session:\n      raise self.exc\n\n  def before_run(self, run_context):\n    if self.raise_in_before_run:\n      print(\"RAISEd\")\n      raise self.exc\n\n\nclass PsCheckHooksTest(tf.test.TestCase):\n\n  def _set_up_hook(self, report_fn=None, mem_limit=1 << 60):\n    op = barrier_ops.BarrierOp(1)\n    report_fn = report_fn or ps_check_hooks._default_report\n    config = ps_check_hooks.Config(barrier_op=op,\n                                   num_ps=1,\n                                   ps_device_fn=lambda idx: None,\n                                   report_fn=report_fn)\n    machine_info = logging_ops.machine_info(\n        mem_limit=mem_limit,\n        shared_name=ps_check_hooks.get_ps_machine_info_shared_name(0))\n    return [\n        PrepareMachineInfoHook(machine_info),\n        ps_check_hooks.PsHealthCheckerHook(config)\n    ]\n\n  def test_basic(self):\n    hooks = self._set_up_hook()\n    with tf.compat.v1.train.SingularMonitoredSession(hooks=hooks):\n      time.sleep(1)\n\n  def test_oom(self):\n    report_fn = mock.MagicMock()\n    hooks = self._set_up_hook(report_fn, mem_limit=0)\n    with tf.compat.v1.train.SingularMonitoredSession(hooks=hooks):\n      time.sleep(1)\n    report_fn.assert_called_once()\n\n  def test_raise_in_after_create_session(self):\n    hooks = self._set_up_hook()\n\n    def run():\n      with tf.compat.v1.train.SingularMonitoredSession(\n          hooks=hooks + [RaiseErrorHook(raise_in_after_create_session=True)]):\n        pass\n\n    self.assertRaises(tf.errors.DeadlineExceededError, run)\n\n  def test_raise_in_before_run(self):\n    hooks = self._set_up_hook()\n\n    def run():\n      t = tf.constant(1.0)\n      with tf.compat.v1.train.SingularMonitoredSession(\n          hooks=hooks + [RaiseErrorHook(raise_in_before_run=True)]) as sess:\n        sess.run(t)\n\n    self.assertRaises(tf.errors.DeadlineExceededError, run)\n\n  def test_default_report(self):\n    # This mainly for grammar check\n    ps_check_hooks._default_report({1: logging_ops_pb2.MachineHealthResult()})\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hooks/server/BUILD",
    "content": "load(\"@pip_deps//:requirements.bzl\", \"requirement\")\nload(\"@rules_python//python:defs.bzl\", \"py_library\", \"py_test\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\nload(\"@com_github_grpc_grpc//bazel:python_rules.bzl\", \"py_grpc_library\", \"py_proto_library\")\n\npackage(\n    default_visibility = [\"//visibility:public\"],\n)\n\nproto_library(\n    name = \"service_proto\",\n    srcs = [\"service.proto\"],\n)\n\npy_proto_library(\n    name = \"service_py_proto\",\n    deps = [\":service_proto\"],\n)\n\npy_grpc_library(\n    name = \"service_py_grpc\",\n    srcs = [\":service_proto\"],\n    deps = [\":service_py_proto\"],\n)\n\npy_library(\n    name = \"constants\",\n    srcs = [\"constants.py\"],\n)\n\npy_library(\n    name = \"server_lib\",\n    srcs = [\"server_lib.py\"],\n    deps = [\n        \":constants\",\n        \":service_py_grpc\",\n        \"//monolith/native_training:barrier_ops\",\n        \"//monolith/native_training:net_utils\",\n        \"//monolith/native_training:save_utils\",\n        requirement(\"grpcio\"),\n    ],\n)\n\npy_library(\n    name = \"client_lib\",\n    srcs = [\"client_lib.py\"],\n    deps = [\n        \":constants\",\n        \":service_py_grpc\",\n        requirement(\"grpcio\"),\n    ],\n)\n\npy_test(\n    name = \"server_lib_test\",\n    srcs = [\"server_lib_test.py\"],\n    deps = [\n        \":client_lib\",\n        \":server_lib\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/hooks/server/client_lib.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nimport grpc\nimport tensorflow as tf\n\nfrom monolith.native_training.hooks.server import service_pb2\nfrom monolith.native_training.hooks.server import service_pb2_grpc\nfrom monolith.native_training.hooks.server import constants\n\n\ndef get_stub_from_model_dir(model_dir: str):\n  with tf.io.gfile.GFile(\n      os.path.join(model_dir, constants.SERVER_ADDR_FILENAME), \"r\") as f:\n    addr = f.read()\n  channel = grpc.insecure_channel(addr)\n  return service_pb2_grpc.ControllerStub(channel)\n"
  },
  {
    "path": "monolith/native_training/hooks/server/constants.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nSERVER_ADDR_FILENAME = \"controller_server_addr.txt\""
  },
  {
    "path": "monolith/native_training/hooks/server/server_lib.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport concurrent.futures\nimport os\nimport socket\nimport time\n\nimport grpc\nimport tensorflow as tf\nfrom tensorflow.python.lib.io import file_io\n\nfrom monolith.native_training import barrier_ops\nfrom monolith.native_training import net_utils\nfrom monolith.native_training import save_utils\nfrom monolith.native_training.hooks.server import constants\nfrom monolith.native_training.hooks.server import service_pb2\nfrom monolith.native_training.hooks.server import service_pb2_grpc\n\n\nclass ControllerServicer(service_pb2_grpc.ControllerServicer):\n\n  def __init__(self, sess: tf.compat.v1.Session,\n               barrier_op: barrier_ops.BarrierOp,\n               saver_hook: save_utils.NoFirstSaveCheckpointSaverHook):\n    self._sess = sess\n    self._saver_hook = saver_hook\n    self._barrier_op = barrier_op\n\n  def StopTraining(self, req, ctx):\n    try:\n      self._barrier_op.place_barrier(self._sess)\n    except barrier_ops.BarrierAlreadyPlacedError:\n      ctx.abort(grpc.StatusCode.ALREADY_EXISTS, \"Barrier is placed already.\")\n    return service_pb2.StopTrainingResponse()\n\n  def ResumeTraining(self, req, ctx):\n    self._barrier_op.remove_barrier(self._sess)\n    return service_pb2.ResumeTrainingResponse()\n\n  def GetBlockStatus(self, req, ctx):\n    resp = service_pb2.GetBlockStatusResponse()\n    blocked_indices = self._barrier_op.get_blocked_indices(self._sess)\n    unblocked_indices = list(\n        set(range(self._barrier_op.capacity)) - set(blocked_indices))\n    resp.blocked_indices.extend(blocked_indices)\n    resp.unblocked_indices.extend(unblocked_indices)\n    return resp\n\n  def SaveCheckpoint(self, req, ctx):\n    resp = service_pb2.SaveCheckpointResponse()\n    self._saver_hook.trigger_save(self._sess)\n    return resp\n\n  def GetTrainingStatus(self, req, ctx):\n    resp = service_pb2.GetTrainingStatusResponse()\n    with self._sess.graph.as_default():\n      resp.global_step = self._sess.run(tf.compat.v1.train.get_global_step())\n    return resp\n\n\nclass ServerHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, model_dir: str, barrier_op: barrier_ops.BarrierOp,\n               saver_hook: save_utils.NoFirstSaveCheckpointSaverHook):\n    self._model_dir = model_dir\n    self._barrier_op = barrier_op\n    self._saver_hook = saver_hook\n    self._server = None\n\n  def after_create_session(self, session, coord):\n    servicer = ControllerServicer(session, self._barrier_op, self._saver_hook)\n    self._server = grpc.server(\n        concurrent.futures.ThreadPoolExecutor(max_workers=2))\n    service_pb2_grpc.add_ControllerServicer_to_server(servicer, self._server)\n    port = self._server.add_insecure_port(\"[::]:0\")\n    addr = net_utils.get_local_server_addr(port)\n    self._server.start()\n    tf.io.gfile.makedirs(self._model_dir)\n    file_io.atomic_write_string_to_file(\n        os.path.join(self._model_dir, constants.SERVER_ADDR_FILENAME), addr)\n\n  def end(self, session):\n    self._server.stop(20)\n"
  },
  {
    "path": "monolith/native_training/hooks/server/server_lib_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nimport grpc\nimport tensorflow as tf\n\nfrom monolith.native_training import barrier_ops\nfrom monolith.native_training import save_utils\nfrom monolith.native_training.hooks.server import client_lib\nfrom monolith.native_training.hooks.server import server_lib\nfrom monolith.native_training.hooks.server import service_pb2\nfrom monolith.native_training.hooks.server import service_pb2_grpc\n\n\nclass ServerTest(tf.test.TestCase):\n\n  def test_basic(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"basic\")\n    barrier = barrier_ops.BarrierOp(1)\n    saver_hook = save_utils.NoFirstSaveCheckpointSaverHook(model_dir,\n                                                           save_secs=10000)\n    server_hook = server_lib.ServerHook(model_dir, barrier, saver_hook)\n    tf.compat.v1.train.create_global_step()\n    with tf.compat.v1.train.SingularMonitoredSession(\n        hooks=[server_hook, saver_hook]) as sess:\n      stub = client_lib.get_stub_from_model_dir(model_dir)\n      stub.StopTraining(service_pb2.StopTrainingRequest())\n      with self.assertRaises(grpc.RpcError):\n        stub.StopTraining(service_pb2.StopTrainingRequest())\n      resp = stub.GetBlockStatus(service_pb2.GetBlockStatusRequest())\n      self.assertAllEqual(resp.blocked_indices, [0])\n      stub.ResumeTraining(service_pb2.ResumeTrainingRequest())\n      resp = stub.GetBlockStatus(service_pb2.GetBlockStatusRequest())\n      self.assertAllEqual(resp.blocked_indices, [])\n      stub.SaveCheckpoint(service_pb2.SaveCheckpointRequest())\n      stub.GetTrainingStatus(service_pb2.GetTrainingStatusRequest())\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hooks/server/service.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto3\";\n\nmessage StopTrainingRequest {\n}\n\nmessage StopTrainingResponse {\n}\n\nmessage ResumeTrainingRequest {\n}\n\nmessage ResumeTrainingResponse {\n}\n\nmessage GetBlockStatusRequest {\n}\n\nmessage GetBlockStatusResponse {\n  repeated int32 blocked_indices = 1;\n  repeated int32 unblocked_indices = 2;\n}\n\nmessage SaveCheckpointRequest {\n}\n\nmessage SaveCheckpointResponse {\n}\n\nmessage GetTrainingStatusRequest {\n}\n\nmessage GetTrainingStatusResponse {\n  int64 global_step = 1;\n}\n\nservice Controller {\n  // Requests stopping the training. All workers will be stopped gradually.\n  rpc StopTraining(StopTrainingRequest) returns (StopTrainingResponse) {\n  }\n  // Requests resuming the training. All workers will be resumed gradually.\n  rpc ResumeTraining(ResumeTrainingRequest) returns (ResumeTrainingResponse) {\n  }\n  // Checks the current block/unblock status.\n  rpc GetBlockStatus(GetBlockStatusRequest) returns (GetBlockStatusResponse) {\n  }\n  // Triggers a on-demand checkpoint save. Can be called in any cases. For\n  // example,\n  // can be called immediately after StopTraining is returned.\n  // When rpc returned successfully, a checkpoint is saved successfully.\n  rpc SaveCheckpoint(SaveCheckpointRequest) returns (SaveCheckpointResponse) {\n  }\n  rpc GetTrainingStatus(GetTrainingStatusRequest)\n      returns (GetTrainingStatusResponse) {\n  }\n}"
  },
  {
    "path": "monolith/native_training/hooks/session_hooks.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport dataclasses\n\nimport tensorflow as tf\n\n\n@dataclasses.dataclass\nclass _Info:\n  session: tf.compat.v1.Session = None\n\n\n_INFO = _Info()\n\n\nclass SetCurrentSessionHook(tf.estimator.SessionRunHook):\n\n  def after_create_session(self, session, coord):\n    _INFO.session = session\n\n  def end(self, session):\n    _INFO.session = None\n\n\ndef get_current_session():\n  \"\"\"Returns the current session. If hook was added,\n  it will return session in hook. Otherwise, it will\n  return default session.\n  \"\"\"\n  if _INFO.session:\n    return _INFO.session\n  return tf.compat.v1.get_default_session()\n"
  },
  {
    "path": "monolith/native_training/hooks/session_hooks_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training.hooks import session_hooks\n\n\nclass SessionHooksTest(tf.test.TestCase):\n\n  def testBasic(self):\n    self.assertTrue(session_hooks.get_current_session() is None)\n    with tf.compat.v1.train.MonitoredSession(\n        hooks=[session_hooks.SetCurrentSessionHook()]) as sess:\n      sess.run([])\n      session_hooks.get_current_session()\n      self.assertTrue(session_hooks.get_current_session() is not None)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/hvd_lib.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport importlib\nimport os\nimport threading\n\n\nclass _Lib:\n  \"\"\"A lib that will delay import when used.\"\"\"\n\n  def __init__(self):\n    self._lib = None\n    self._lock = threading.Lock()\n\n  @property\n  def lib(self):\n    with self._lock:\n      if self._lib is None:\n        if self.enable_bps:\n          self._lib = importlib.import_module(\"byteps.tensorflow\")\n        else:\n          self._lib = importlib.import_module(\"horovod.tensorflow\")\n    return self._lib\n\n  @property\n  def enable_bps(self):\n    return int(os.getenv(\"MONOLITH_WITH_BYTEPS\", \"0\"))\n\n  def init(self):\n    return self.lib.init()\n\n  def rank(self):\n    return self.lib.rank()\n\n  def size(self):\n    return self.lib.size()\n\n  def allgather(self, *args, **kwargs):\n    return self.lib.allgather(*args, **kwargs)\n\n  def broadcast(self, *args, **kwargs):\n    return self.lib.broadcast(*args, **kwargs)\n\n  def BroadcastGlobalVariablesHook(self, *args, **kwargs):\n    return self.lib.BroadcastGlobalVariablesHook(*args, **kwargs)\n\n\n_lib = _Lib()\n\n\ndef __getattr__(name):\n  \"\"\"Export all method in _Lib class\"\"\"\n  return getattr(_lib, name)\n"
  },
  {
    "path": "monolith/native_training/input.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nfrom typing import List\nimport tensorflow as tf\n\n\ndef slot_to_key(slot: int):\n  return \"feature_{}\".format(slot)\n\n\ndef generate_ffm_example(vocab_sizes: List[int], length=5) -> str:\n  \"\"\"Generate a random training example.\"\"\"\n\n  def _int64_feature(values):\n    return tf.train.Feature(int64_list=tf.train.Int64List(value=values))\n\n  def _float32_feature(values):\n    return tf.train.Feature(float_list=tf.train.FloatList(value=values))\n\n  feature = {}\n  feature[\"label\"] = _float32_feature([np.random.randint(low=0, high=1)])\n  max_vocab = max(vocab_sizes)\n\n  for i, vocab_size in enumerate(vocab_sizes):\n    num_ids = np.random.randint(length) + 1\n    ids = np.random.randint(max_vocab * i,\n                            max_vocab * i + vocab_size,\n                            size=num_ids).tolist()\n    feature[slot_to_key(i)] = _int64_feature(ids)\n\n  example_proto = tf.train.Example(features=tf.train.Features(feature=feature))\n  return example_proto.SerializeToString()\n"
  },
  {
    "path": "monolith/native_training/layers/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_cc_binary\", \"tf_cc_test\", \"tf_kernel_library\")\n\npackage(\n    default_visibility = [\"//visibility:public\"],\n)\n\npy_library(\n    name = \"utils\",\n    srcs = [\"utils.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \"//monolith:utils\",\n        \"//monolith/native_training:monolith_export\",\n        \"//monolith/native_training:utils\",\n        \"//monolith/native_training/summary:summary_ops\",\n    ],\n)\n\npy_library(\n    name = \"add_bias\",\n    srcs = [\"add_bias.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"add_bias_test\",\n    srcs = [\"add_bias_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":add_bias\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"dense\",\n    srcs = [\"dense.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"dense_test\",\n    srcs = [\"dense_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":dense\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"advanced_activations\",\n    srcs = [\"advanced_activations.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"advanced_activations_test\",\n    srcs = [\"advanced_activations_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":advanced_activations\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"agru\",\n    srcs = [\"agru.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"agru_test\",\n    srcs = [\"agru_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":agru\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"norms\",\n    srcs = [\n        \"norms.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"norms_test\",\n    srcs = [\"norms_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":norms\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"mlp\",\n    srcs = [\n        \"mlp.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":advanced_activations\",\n        \":dense\",\n        \":norms\",\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"mlp_test\",\n    srcs = [\"mlp_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":mlp\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"feature_cross\",\n    srcs = [\n        \"feature_cross.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":agru\",\n        \":layer_ops\",\n        \":mlp\",\n        \":utils\",\n        \"//monolith/core:base_layer\",\n    ],\n)\n\npy_test(\n    name = \"feature_cross_test\",\n    srcs = [\"feature_cross_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":feature_cross\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"feature_trans\",\n    srcs = [\n        \"feature_trans.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":agru\",\n        \":mlp\",\n        \":utils\",\n        \"//monolith/core:base_layer\",\n    ],\n)\n\npy_test(\n    name = \"feature_trans_test\",\n    srcs = [\"feature_trans_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":feature_trans\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"feature_seq\",\n    srcs = [\n        \"feature_seq.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":agru\",\n        \":mlp\",\n        \":utils\",\n        \"//monolith/core:base_layer\",\n    ],\n)\n\npy_test(\n    name = \"feature_seq_test\",\n    srcs = [\"feature_seq_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":feature_seq\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"pooling\",\n    srcs = [\n        \"pooling.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"pooling_test\",\n    srcs = [\"pooling_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":pooling\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"logit_correction\",\n    srcs = [\n        \"logit_correction.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":mlp\",\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"logit_correction_test\",\n    srcs = [\"logit_correction_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":logit_correction\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"lhuc\",\n    srcs = [\n        \"lhuc.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":advanced_activations\",\n        \":dense\",\n        \":mlp\",\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"lhuc_test\",\n    srcs = [\"lhuc_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":lhuc\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"multi_task\",\n    srcs = [\n        \"multi_task.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":advanced_activations\",\n        \":dense\",\n        \":mlp\",\n        \":utils\",\n    ],\n)\n\npy_test(\n    name = \"multi_task_test\",\n    srcs = [\"multi_task_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":multi_task\",\n        \"//monolith/core:testing_utils\",\n    ],\n)\n\npy_library(\n    name = \"layers\",\n    srcs = [\n        \"__init__.py\",\n    ],\n    srcs_version = \"PY3\",\n    deps = [\n        \":add_bias\",\n        \":advanced_activations\",\n        \":agru\",\n        \":dense\",\n        \":feature_cross\",\n        \":feature_seq\",\n        \":feature_trans\",\n        \":lhuc\",\n        \":logit_correction\",\n        \":mlp\",\n        \":multi_task\",\n        \":norms\",\n        \":pooling\",\n        \":sparse_nas\",\n        \"//monolith/native_training:utils\",\n    ],\n)\n\ncc_library(\n    name = \"internal_kernels\",\n    alwayslink = 1,\n)\n\ntf_kernel_library(\n    name = \"layer_tf_ops\",\n    srcs = [\n        \"kernels/ffm_kernels.cc\",\n        \"kernels/ffm_kernels.h\",\n        \"kernels/feature_insight_kernels.cc\",\n        \"kernels/fid_counter_kernel.cc\",\n        \"ops/ffm_ops.cc\",\n        \"ops/nas_ops.cc\",\n        \"ops/feature_insight_ops.cc\",\n        \"ops/fid_counter_op.cc\",\n    ],\n    copts = [\"-DNDEBUG\"],\n    gpu_srcs = [\n        \"kernels/ffm_kernels.h\",\n        \"kernels/ffm_kernels.cu.cc\",\n    ],\n    deps = [\n        \":internal_kernels\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:gpu_device_array_for_custom_op\",\n    ],\n)\n\npy_library(\n    name = \"layer_ops\",\n    srcs = [\"layer_ops.py\"],\n    deps = [\n        \"//monolith:utils\",\n        \"//monolith/core:testing_utils\",\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"layer_ops_test\",\n    srcs = [\"layer_ops_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":layer_ops\",\n    ],\n)\n\npy_library(\n    name = \"sparse_nas\",\n    srcs = [\"sparse_nas.py\"],\n    deps = [\n        \":layer_ops\",\n        \":utils\",\n        \"//monolith/native_training/data:feature_list\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\n\nexports_files([\n    \"kernels/ffm_kernels.cc\",\n    \"kernels/ffm_kernels.cu.cc\",\n    \"kernels/feature_insight_kernels.cc\",\n    \"ops/ffm_ops.cc\",\n    \"kernels/nas_kernels.cc\",\n    \"ops/nas_ops.cc\",\n    \"ops/feature_insight_ops.cc\",\n    \"ops/fid_counter_ops.cc\",\n])\n"
  },
  {
    "path": "monolith/native_training/layers/README.md",
    "content": "The layers in Monolith are a super set of tensorflow keras layers. \nMonolith adds/enhances the following layers:\n- Dense\n- MLP\n- AddBias\n- LayerNorm/GradNorm\n- GroupInt/AllInt/CDot/CAN/DCN/CIN/AutoInt/SeNet/iRazor/DIN/DIEN/DMR_U2I\n- LogitCorrection\n- SumPooling/AvgPooling/MaxPooling\n\nMonolith layers are compatible with keras layers, that means you can mix usage of keras layers and monolith layers. \nHere is an example of creating monolith layer:\n```python\nimport tensorflow as tf\nfrom monolith.native_training.layers import Dense\n\n# the first method to new a monolith layer, which is the same as keras\ndense = Dense(units=100, activation=tf.keras.activations.relu)\n\n# the second method to new a monlith layer\ndense_p = Dense.params()\ndense_p.units=100\ndense_p.activation=tf.keras.activations.relu\ndense2 = dense_p.instantiate()\n\nmodel = tf.keras.Sequential([\n  dense,    # create from constructor\n  dense2,   # create from new_instance\n  tf.keras.layers.Dense(units=100, activation=tf.keras.activations.relu)  # mix use\n])\n```\nAs show above, there is two methods to create a layer, one is using constructor, \nthe other employ `new_instance` method of `InstantiableParams`. \n\nIn most case, just replace:\n```python\nfrom tensorflow.keras import layers\n```\nwith \n```python\nfrom monolith.native_training import layers\n```\n\nwe prefer monolith layers. \n"
  },
  {
    "path": "monolith/native_training/layers/__init__.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport types\nimport tensorflow as tf\nfrom tensorflow.keras.layers import *\n\nfrom monolith.native_training.layers.mlp import MLP\nfrom monolith.native_training.layers.feature_cross import *\nfrom monolith.native_training.layers.feature_trans import *\nfrom monolith.native_training.layers.feature_seq import *\nfrom monolith.native_training.layers.advanced_activations import *\nfrom monolith.native_training.layers.add_bias import AddBias\nfrom monolith.native_training.layers.lhuc import LHUCTower\nfrom monolith.native_training.layers.logit_correction import LogitCorrection\nfrom monolith.native_training.layers.norms import LayerNorm, GradNorm\nfrom monolith.native_training.layers.pooling import SumPooling, AvgPooling, MaxPooling\nfrom monolith.native_training.layers.utils import MergeType, DCNType\nfrom monolith.native_training.layers.multi_task import MMoE, SNR\nfrom monolith.native_training.utils import params as _params\n\ndel globals()['Dense']\nfrom monolith.native_training.layers.dense import Dense\n\nkeras_layers = {}\nfor name in dir(tf.keras.layers):\n  if name.startswith(\"_\") or name == \"Layer\":\n    continue\n  cls = getattr(tf.keras.layers, name)\n  try:\n    if issubclass(cls, Layer) and not hasattr(cls, 'params'):\n      cls.params = types.MethodType(_params, cls)\n      keras_layers[name] = cls\n  except:\n    pass\n"
  },
  {
    "path": "monolith/native_training/layers/add_bias.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom tensorflow.keras.layers import Layer, InputSpec\nfrom tensorflow.keras import initializers\nfrom tensorflow.python.keras import regularizers\n\nfrom monolith.native_training.utils import get_ndim, int_shape, with_params\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.layers.utils import check_dim, dim_size\n\n\n@monolith_export\n@with_params\nclass AddBias(Layer):\n  r\"\"\"AddBias 执行 :math:`y = x + b`, 与直接用`+`相比, AddBias处理了更多的shape问题\n  \n  例如image有两种表示方式NWHC, NCWH, 对于时间序列也有类似的问题. AddBias可以让用户透明增加Bias\n  \n  >>> add_bias = AddBias(initializer=tf.initializers.Zeros())\n  >>> y = add_bias(x, data_format='channels_first')\n\n  Args:\n    initializer (:obj:`tf.initializer`): bias的初始化器\n    regularizer (:obj:`tf.regularizer`): bias的正则化器\n  \n  \"\"\"\n\n  def __init__(self, initializer=None, regularizer=None, **kwargs):\n    super(AddBias, self).__init__(**kwargs)\n    self.initializer = initializers.get(initializer) or tf.initializers.Zeros()\n    self.regularizer = regularizers.get(regularizer)\n\n    # allowed input specification\n    self.input_spec = InputSpec(min_ndim=2)\n    self.bias = None\n\n  def build(self, input_shape):\n    shape = list(map(check_dim, input_shape[1:]))\n    self.bias = self.add_weight(name='bias',\n                                shape=shape,\n                                dtype=tf.float32,\n                                initializer=self.initializer,\n                                regularizer=self.regularizer)\n\n  def call(self, inputs, **kwargs):\n    data_format = kwargs.get('data_format', 'channels_last')\n    if data_format not in {'channels_first', 'channels_last'}:\n      raise ValueError('Unknown data_format: ' + str(data_format))\n    bias_shape = int_shape(self.bias)\n    if len(bias_shape) != 1 and len(bias_shape) != get_ndim(inputs) - 1:\n      raise ValueError(\n          'Unexpected bias dimensions %d, expect to be 1 or %d dimensions' %\n          (len(bias_shape), get_ndim(inputs)))\n    if get_ndim(inputs) == 5:\n      if data_format == 'channels_first':\n        if len(bias_shape) == 1:\n          inputs += tf.reshape(self.bias, (1, bias_shape[0], 1, 1, 1))\n        else:\n          inputs += tf.reshape(self.bias, (1, bias_shape[3]) + bias_shape[:3])\n      elif data_format == 'channels_last':\n        if len(bias_shape) == 1:\n          inputs += tf.reshape(self.bias, (1, 1, 1, bias_shape[0]))\n        else:\n          inputs += tf.reshape(self.bias, (1,) + bias_shape)\n    elif get_ndim(inputs) == 4:\n      if data_format == 'channels_first':\n        if len(bias_shape) == 1:\n          inputs += tf.reshape(self.bias, (1, bias_shape[0], 1, 1))\n        else:\n          inputs += tf.reshape(self.bias, (1, bias_shape[2]) + bias_shape[:2])\n      elif data_format == 'channels_last':\n        if len(bias_shape) == 1:\n          inputs = tf.nn.bias_add(inputs, self.bias, data_format='NHWC')\n        else:\n          inputs += tf.reshape(self.bias, (1,) + bias_shape)\n    elif get_ndim(inputs) == 3:\n      if data_format == 'channels_first':\n        if len(bias_shape) == 1:\n          inputs += tf.reshape(self.bias, (1, bias_shape[0], 1))\n        else:\n          inputs += tf.reshape(self.bias, (1, bias_shape[1], bias_shape[0]))\n      elif data_format == 'channels_last':\n        if len(bias_shape) == 1:\n          inputs += tf.reshape(self.bias, (1, 1, bias_shape[0]))\n        else:\n          inputs += tf.reshape(self.bias, (1,) + bias_shape)\n    else:\n      inputs = tf.nn.bias_add(inputs, self.bias)\n    return inputs\n\n  def get_config(self):\n    config = {\n        'initializer': tf.keras.initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n    }\n    base_config = super(AddBias, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n"
  },
  {
    "path": "monolith/native_training/layers/add_bias_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\n\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.add_bias import AddBias\n\n\nclass AddBiasTest(tf.test.TestCase):\n\n  def test_ab_instantiate(self):\n    layer_template = AddBias.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.initializer = tf.initializers.Zeros()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = AddBias(initializer=tf.initializers.Zeros())\n    print(ins2)\n\n  def test_ab_serde(self):\n    layer_template = AddBias.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.initializer = tf.initializers.Zeros()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    cfg = ins1.get_config()\n    ins2 = AddBias.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_ab_call(self):\n    layer_template = AddBias.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.initializer = tf.initializers.Zeros()\n    layer = test_params0.instantiate()\n\n    data = tf.keras.backend.variable(np.random.uniform(size=(100, 10)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/advanced_activations.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport types\n\nimport tensorflow.keras.initializers as initializers\nimport tensorflow.keras.constraints as constraints\nfrom tensorflow.python.keras.activations import exponential\nfrom tensorflow.python.keras.activations import gelu\nfrom tensorflow.python.keras.activations import hard_sigmoid\nfrom tensorflow.python.keras.activations import linear\nfrom tensorflow.python.keras.activations import selu\nfrom tensorflow.python.keras.activations import sigmoid\nfrom tensorflow.python.keras.activations import softplus\nfrom tensorflow.python.keras.activations import softsign\nfrom tensorflow.python.keras.activations import swish\nfrom tensorflow.python.keras.activations import tanh\nfrom tensorflow.python.keras.layers import Layer\nfrom tensorflow.python.keras.layers.advanced_activations import ReLU\nfrom tensorflow.python.keras.layers.advanced_activations import LeakyReLU\nfrom tensorflow.python.keras.layers.advanced_activations import ELU\nfrom tensorflow.python.keras.layers.advanced_activations import Softmax\nfrom tensorflow.python.keras.layers.advanced_activations import ThresholdedReLU\nfrom tensorflow.python.keras.layers.advanced_activations import PReLU\n\nfrom monolith.native_training.utils import params as _params\nfrom monolith.native_training.monolith_export import monolith_export\n\n__all__ = [\n    'ReLU', 'LeakyReLU', 'ELU', 'Softmax', 'ThresholdedReLU', 'PReLU',\n    'Exponential', 'Gelu', 'HardSigmoid', 'Linear', 'Selu', 'Sigmoid',\n    'Sigmoid2', 'Softplus', 'Softsign', 'Swish', 'Tanh'\n]\n\nTanh = type('Tanh', (Layer,), {'call': lambda self, x: tanh(x)})\nSigmoid = type('Sigmoid', (Layer,), {'call': lambda self, x: sigmoid(x)})\nSigmoid2 = type('Sigmoid2', (Layer,), {'call': lambda self, x: sigmoid(x) * 2})\nLinear = type('Linear', (Layer,), {'call': lambda self, x: linear(x)})\nGelu = type('Gelu', (Layer,), {'call': lambda self, x: gelu(x)})\nSelu = type('Selu', (Layer,), {'call': lambda self, x: selu(x)})\nSoftsign = type('Softsign', (Layer,), {'call': lambda self, x: softsign(x)})\nSoftplus = type('Softplus', (Layer,), {'call': lambda self, x: softplus(x)})\nExponential = type('Exponential', (Layer,),\n                   {'call': lambda self, x: exponential(x)})\nHardSigmoid = type('HardSigmoid', (Layer,),\n                   {'call': lambda self, x: hard_sigmoid(x)})\nSwish = type('Swish', (Layer,), {'call': lambda self, x: swish(x)})\n\nReLU.params = types.MethodType(_params, ReLU)\nPReLU.params = types.MethodType(_params, PReLU)\nLeakyReLU.params = types.MethodType(_params, LeakyReLU)\nELU.params = types.MethodType(_params, ELU)\nSoftmax.params = types.MethodType(_params, Softmax)\nThresholdedReLU.params = types.MethodType(_params, ThresholdedReLU)\nTanh.params = types.MethodType(_params, Tanh)\nSigmoid.params = types.MethodType(_params, Sigmoid)\nSigmoid2.params = types.MethodType(_params, Sigmoid2)\nLinear.params = types.MethodType(_params, Linear)\nGelu.params = types.MethodType(_params, Gelu)\nSelu.params = types.MethodType(_params, Selu)\nSoftsign.params = types.MethodType(_params, Softsign)\nSoftplus.params = types.MethodType(_params, Softplus)\nExponential.params = types.MethodType(_params, Exponential)\nHardSigmoid.params = types.MethodType(_params, HardSigmoid)\nSwish.params = types.MethodType(_params, Swish)\n\n__all_activations = {\n    'exponential': Exponential,\n    'gelu': Gelu,\n    'hard_sigmoid': HardSigmoid,\n    'hardsigmoid': HardSigmoid,\n    'linear': Linear,\n    'selu': Selu,\n    'sigmoid': Sigmoid,\n    'sigmoid2': Sigmoid2,\n    'softplus': Softplus,\n    'softsign': Softsign,\n    'swish': Swish,\n    'tanh': Tanh,\n    'leakyrelu': LeakyReLU,\n    'relu': ReLU,\n    'elu': ELU,\n    'softmax': Softmax,\n    'thresholdedrelu': ThresholdedReLU,\n    'prelu': PReLU\n}\nALL_ACTIVATION_NAMES = set(__all_activations.keys())\n\n\n@monolith_export\ndef get(identifier):\n  \"\"\"获取函数, 可以用名字获取, 也可以用序列化的Json获取\n\n  Args:\n    identifier (:obj:`Any`): 标识, 可以是name, 获序列化的Json, None等\n  \n  Returns:\n    激活函数\n  \"\"\"\n\n  if identifier is None:\n    return None\n  if isinstance(identifier, str):\n    if identifier.lower() in __all_activations:\n      return __all_activations[identifier.lower()]()\n    else:\n      evaled = eval(identifier)\n      if isinstance(evaled, dict):\n        return deserialize(evaled)\n      raise TypeError(\n          'Could not interpret activation function identifier: {}'.format(\n              identifier))\n  elif isinstance(identifier, dict):\n    return deserialize(identifier)\n  elif callable(identifier):\n    if hasattr(identifier, 'params'):\n      try:\n        if issubclass(identifier, Layer):\n          return identifier()\n        else:\n          return identifier\n      except:\n        return identifier\n    elif isinstance(identifier, Layer):\n      name = identifier.__class__.__name__.lower()\n      return __all_activations[name]()\n    else:\n      try:\n        name = identifier.__name__\n        return __all_activations[name]()\n      except:\n        return identifier\n  else:\n    raise TypeError(\n        'Could not interpret activation function identifier: {}'.format(\n            identifier))\n\n\n@monolith_export\ndef serialize(activation):\n  \"\"\"序列化激活函数\n  \n  Args:\n    activation (:obj:`tf.activation`): keras激活函数\n  \n  Returns:\n    Dict/Json 获序列化的激活函数\n  \"\"\"\n\n  if isinstance(activation, (Linear, Exponential, Selu, HardSigmoid, Gelu,\n                             Sigmoid, Softplus, Softsign, Swish, Tanh)):\n    return repr({'name': activation.__class__.__name__})\n  elif isinstance(activation, (LeakyReLU, ELU)):\n    return repr({\n        'name': activation.__class__.__name__,\n        'alpha': float(activation.alpha)\n    })\n  elif isinstance(activation, ReLU):\n    return repr({\n        'name': 'ReLU',\n        'max_value': activation.max_value,\n        'negative_slope': float(activation.negative_slope),\n        'threshold': float(activation.threshold)\n    })\n  elif isinstance(activation, PReLU):\n    return repr({\n        'name':\n            'PReLU',\n        'alpha_initializer':\n            initializers.serialize(activation.alpha_initializer),\n        'alpha_regularizer':\n            initializers.serialize(activation.alpha_regularizer),\n        'alpha_constraint':\n            constraints.serialize(activation.alpha_constraint),\n        'shared_axes':\n            activation.shared_axes\n    })\n  elif isinstance(activation, Softmax):\n    return repr({'name': 'Softmax', 'axis': activation.axis})\n  elif isinstance(activation, ThresholdedReLU):\n    return repr({'name': 'ThresholdedReLU', 'theta': float(activation.theta)})\n  else:\n    return None\n\n\n@monolith_export\ndef deserialize(identifier):\n  \"\"\"反序列化激活函数\n  \n  Args:\n    identifier (:obj:`Any`): 标识, 可以是name, 获序列化的Json, None等\n  \n  Returns:\n    激活函数\n  \"\"\"\n\n  if identifier is None:\n    return None\n  else:\n    if not isinstance(identifier, dict):\n      identifier = eval(identifier)\n    assert isinstance(identifier, dict)\n    name = identifier['name'].lower()\n    assert name in __all_activations\n    identifier.pop('name')\n    return __all_activations[name](**identifier)\n"
  },
  {
    "path": "monolith/native_training/layers/advanced_activations_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nimport tensorflow.keras.activations as acts\nimport tensorflow.keras.layers as lyacts\n\nfrom monolith.native_training.layers.advanced_activations import get, serialize\n\n\ndef serde(act):\n  _act = get(act)\n  sered_act = serialize(_act)\n  get(sered_act)\n\n\nall_acts = [\n    'relu', 'leakyrelu', 'elu', 'softmax', 'thresholdedrelu', 'prelu',\n    'exponential', 'gelu', 'hardsigmoid', 'linear', 'selu', 'sigmoid',\n    'softplus', 'softsign', 'swish', 'tanh'\n]\n\nraw_acts = [\n    acts.tanh, acts.sigmoid, acts.softsign, acts.softplus, acts.softmax,\n    acts.exponential, acts.elu, acts.gelu, acts.hard_sigmoid, acts.selu,\n    acts.swish, acts.relu, acts.linear\n]\n\nlay_acts = [\n    lyacts.ReLU(),\n    lyacts.PReLU(),\n    lyacts.ThresholdedReLU(),\n    lyacts.ELU(),\n    lyacts.Softmax(),\n    lyacts.LeakyReLU()\n]\n\n\nclass ActivationsTest(tf.test.TestCase):\n\n  def test_get_from_str(self):\n    for act in all_acts:\n      serde(act)\n\n  def test_get_from_layers(self):\n    for act in lay_acts:\n      serde(act)\n\n  def test_get_from_func(self):\n    for act in lay_acts:\n      serde(act)\n\n  def test_params(self):\n    for act in all_acts:\n      cls = get(act).__class__\n      p = cls.params()\n      # print(p.new_instance())\n\n  def test_call(self):\n    inp = tf.random.uniform(shape=(100, 200))\n    out = []\n    for act in all_acts:\n      out.append(get(act)(inp))\n\n    sum_out = tf.reduce_sum(tf.add_n(out))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/agru.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom tensorflow.python.eager import context\nfrom tensorflow.python.keras import backend\nfrom tensorflow.python.keras import activations\nfrom tensorflow.python.keras import initializers\nfrom tensorflow.python.keras import regularizers\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.ops import array_ops\nfrom tensorflow.python.ops import math_ops\nfrom tensorflow.python.ops import nn_ops\nfrom tensorflow.python.ops import rnn_cell_impl\nfrom tensorflow.python.ops import tensor_array_ops\nfrom tensorflow.python.ops import control_flow_ops\nfrom tensorflow.python.keras.engine.base_layer import Layer\nfrom tensorflow.python.keras.engine.input_spec import InputSpec\nimport tensorflow.python.keras.layers.legacy_rnn.rnn_cell_impl as rnn_impl\n\nfrom monolith.native_training.utils import with_params\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.layers.utils import check_dim, dim_size\n\n_hasattr = rnn_impl._hasattr\n_concat = rnn_cell_impl._concat\n_zero_state_tensors = rnn_cell_impl._zero_state_tensors\n\n_BIAS_VARIABLE_NAME = \"bias\"\n_WEIGHTS_VARIABLE_NAME = \"kernel\"\n\n# This can be used with self.assertRaisesRegexp for assert_like_rnncell.\nASSERT_LIKE_RNNCELL_ERROR_REGEXP = \"is not an RNNCell\"\n\n__all__ = ['AGRUCell', 'dynamic_rnn_with_attention']\n\n\n@monolith_export\n@with_params\nclass AGRUCell(Layer):\n  \"\"\"带attention的GRU单元, 用于DIEN中.\n  \n  Args:\n    units (:obj:`int`): GRU隐含层大小\n    att_type (:obj:`str`): attention方式, 支持两种AGRU/AUGRU\n    activation (:obj:`tf.activation`): 激活函数\n    initializer (:obj:`tf.initializer`): kernel初始化器\n    regularizer (:obj:`tf.regularizer`): kernel正则化\n  \n  \"\"\"\n\n  def __init__(self,\n               units,\n               att_type='AGRU',\n               activation=None,\n               initializer=None,\n               regularizer=None,\n               **kwargs):\n    super(AGRUCell, self).__init__(**kwargs)\n\n    # Inputs must be 2-dimensional.\n    assert att_type.upper() in {'AGRU', 'AUGRU'}\n    self.input_spec = [\n        InputSpec(ndim=2),\n        InputSpec(ndim=2),\n        InputSpec(max_ndim=2)\n    ]\n    self.units = units\n    self.att_type = att_type\n    self.activation = activations.get(activation or math_ops.tanh)\n    self.initializer = tf.initializers.get(\n        initializer) or tf.initializers.HeNormal()\n    self.regularizer = regularizers.get(regularizer)\n\n  @property\n  def state_size(self):\n    return self.units\n\n  @property\n  def output_size(self):\n    return self.units\n\n  def build(self, inputs_shape):\n    input_shape, state_shape, att_shape = inputs_shape\n    assert check_dim(state_shape[-1]) == self.units\n    input_depth = check_dim(input_shape[-1])\n    if input_shape[-1] == -1:\n      raise ValueError(\"Expected inputs.shape[-1] to be known, saw shape: %s\" %\n                       str(inputs_shape))\n    self._gate_kernel = self.add_weight(\n        name=\"gates/{}\".format(_WEIGHTS_VARIABLE_NAME),\n        dtype=self.dtype,\n        shape=[input_depth + self.units, 2 * self.units],\n        initializer=self.initializer,\n        regularizer=self.regularizer)\n    self._gate_bias = self.add_weight(\n        name=\"gates/{}\".format(_BIAS_VARIABLE_NAME),\n        dtype=self.dtype,\n        shape=[2 * self.units],\n        initializer=initializers.Ones(),\n        regularizer=self.regularizer)\n\n    self._candidate_kernel = self.add_weight(\n        name=\"candidate/{}\".format(_WEIGHTS_VARIABLE_NAME),\n        dtype=self.dtype,\n        shape=[input_depth + self.units, self.units],\n        initializer=self.initializer,\n        regularizer=self.regularizer)\n    self._candidate_bias = self.add_weight(\n        name=\"candidate/{}\".format(_BIAS_VARIABLE_NAME),\n        dtype=self.dtype,\n        shape=[self.units],\n        initializer=initializers.Ones(),\n        regularizer=self.regularizer)\n\n    super(AGRUCell, self).build(inputs_shape)\n\n  def call(self, inputs, **kwargs):\n    x, state, att_score = inputs\n    gate_inputs = math_ops.matmul(array_ops.concat([x, state], 1),\n                                  self._gate_kernel)\n    gate_inputs = nn_ops.bias_add(gate_inputs, self._gate_bias)\n\n    value = math_ops.sigmoid(gate_inputs)\n    r, u = array_ops.split(value=value, num_or_size_splits=2, axis=1)\n\n    r_state = r * state\n    candidate = math_ops.matmul(array_ops.concat([x, r_state], 1),\n                                self._candidate_kernel)\n    candidate = nn_ops.bias_add(candidate, self._candidate_bias)\n\n    c = self.activation(candidate)\n    if att_score is None:\n      new_h = (1.0 - u) * state + u * c\n    else:\n      if self.att_type.upper() == 'AUGRU':\n        # GRU with attentional update gate（AUGRU）\n        u = (1.0 - att_score) * u\n        new_h = u * state + (1 - u) * c\n      else:  # self.att_type.upper() == 'AGRU':\n        # Attention based GRU（AGRU）\n        new_h = (1. - att_score) * state + att_score * c\n    return new_h, new_h\n\n  def zero_state(self, batch_size, dtype):\n    # Try to use the last cached zero_state. This is done to avoid recreating\n    # zeros, especially when eager execution is enabled.\n    state_size = self.state_size\n    is_eager = context.executing_eagerly()\n    if is_eager and _hasattr(self, \"_last_zero_state\"):\n      (last_state_size, last_batch_size, last_dtype,\n       last_output) = getattr(self, \"_last_zero_state\")\n      if (last_batch_size == batch_size and last_dtype == dtype and\n          last_state_size == state_size):\n        return last_output\n    with backend.name_scope(type(self).__name__ + \"ZeroState\"):\n      output = _zero_state_tensors(state_size, batch_size, dtype)\n    if is_eager:\n      self._last_zero_state = (state_size, batch_size, dtype, output)\n    return output\n\n  def get_config(self):\n    config = {\n        \"units\": self.units,\n        \"att_type\": self.att_type,\n        \"initializer\": initializers.serialize(self.initializer),\n        \"activation\": activations.serialize(self.activation),\n        'regularizer': regularizers.serialize(self.regularizer)\n    }\n    base_config = super(AGRUCell, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@monolith_export\ndef create_ta(name, size, dtype):\n  \"\"\"创建Tensor Array, 一般用于while循环中存放中间结果.\n\n  Args:\n    name (:obj:`str`): Array名称\n    size (:obj:`int`): Array大小\n    dtype (:obj:`tf.DType`): 数据类型\n    \n  \"\"\"\n\n  return tensor_array_ops.TensorArray(dtype=dtype,\n                                      size=size,\n                                      tensor_array_name=name)\n\n\n@monolith_export\ndef static_rnn_with_attention(cell, inputs, att_scores, init_state=None):\n  \"\"\"带Attention的静态RNN, 利用python for循环直接将时间维度静态展开, 模型大小会增大\n\n  Args:\n    cell (:obj:`RNNCell`): RNN单元\n    inputs (:obj:`tf.Tensor`): 输入数据, shape为(batch_size, seq_len, emb_size)\n    att_scores (:obj:`tf.Tensor`): attention权重, shape为(batch_size, seq_len)\n    init_state (:obj:`tf.Tensor`): 初始化状态\n  \n  \"\"\"\n\n  assert isinstance(cell, AGRUCell)\n  if init_state is None:\n    batch_size = dim_size(inputs, 0)\n    if getattr(cell, \"get_initial_state\", None) is not None:\n      state = cell.get_initial_state(inputs=None,\n                                     batch_size=batch_size,\n                                     dtype=dtype)\n    else:\n      state = cell.zero_state(batch_size, inputs.dtype)\n  else:\n    state = init_state\n\n  inputs, outputs = tf.unstack(tf.transpose(inputs, [1, 0, 2])), []\n  for time, inp in enumerate(inputs):\n    attr = tf.reshape(att_scores[:, time], shape=(-1, 1))\n    cell_out, new_state = cell((inp, state, attr))\n    state = new_state\n    outputs.append(state)\n\n  outputs = tf.transpose(tf.stack(outputs), [1, 0, 2])\n\n  return outputs, state\n\n\n@monolith_export\ndef dynamic_rnn_with_attention(cell,\n                               inputs,\n                               att_scores,\n                               parallel_iterations=1,\n                               swap_memory=True,\n                               init_state=None):\n  \"\"\"带Attention的动态RNN, 得用tf.while实现, 模型大小不会增大\n\n  Args:\n    cell (:obj:`RNNCell`): RNN单元\n    inputs (:obj:`tf.Tensor`): 输入数据, shape为(batch_size, seq_len, emb_size)\n    att_scores (:obj:`tf.Tensor`): attention权重, shape为(batch_size, seq_len)\n    parallel_iterations (:obj:`int`): 并行迭代次数, 具体请参考`control_flow_ops.while_loop`\n    swap_memory (:obj:`bool`): 是否swap内存, 具体请参考`control_flow_ops.while_loop`\n    init_state (:obj:`tf.Tensor`): 初始化状态\n  \n  \"\"\"\n\n  assert isinstance(cell, AGRUCell)\n  batch_size, time_steps = dim_size(inputs, 0), dim_size(inputs, 1)\n  time = array_ops.constant(0, dtype=tf.dtypes.int32, name=\"time\")\n\n  if init_state is None:\n    if getattr(cell, \"get_initial_state\", None) is not None:\n      state = cell.get_initial_state(inputs=None,\n                                     batch_size=batch_size,\n                                     dtype=dtype)\n    else:\n      state = cell.zero_state(batch_size, inputs.dtype)\n  else:\n    state = init_state\n\n  with ops.name_scope(\"dynamic_rnn\"):\n    output_ta = create_ta(\"output_ta\", time_steps, inputs.dtype)\n    input_ta = create_ta(\"input_ta\", time_steps, inputs.dtype)\n\n  # [batch_size, time, emb_dim] -> [time, batch_size, emb_dim]\n  input_ta = input_ta.unstack(tf.transpose(inputs, [1, 0, 2]))\n\n  def _body(time, output_ta, state, att_scores):\n    att_score = tf.reshape(att_scores[:, time], shape=(-1, 1))  # [bz, 1]\n    cell_out, new_state = cell((input_ta.read(time), state, att_score))\n    output_ta = output_ta.write(time, cell_out)\n\n    return (time + 1, output_ta, new_state, att_scores)\n\n  _, output_final, final_state, _ = control_flow_ops.while_loop(\n      cond=lambda time, *_: time < time_steps,\n      body=_body,\n      loop_vars=(time, output_ta, state, att_scores),\n      parallel_iterations=parallel_iterations,\n      swap_memory=swap_memory)\n\n  outputs = output_final.stack()\n  outputs = tf.transpose(outputs, [1, 0, 2])\n  outputs.set_shape([None, time_steps, dim_size(outputs, -1)])\n\n  return outputs, final_state\n"
  },
  {
    "path": "monolith/native_training/layers/agru_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.agru import AGRUCell, \\\n  dynamic_rnn_with_attention, static_rnn_with_attention\n\n\nclass AGRUTest(tf.test.TestCase):\n\n  def test_agru_instantiate(self):\n    dense_layer_template = AGRUCell.params()\n\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.units = 10\n    test_params0.activation = tf.keras.activations.sigmoid\n    test_params0.initializer = tf.keras.initializers.GlorotNormal()\n    mlp1 = test_params0.instantiate()\n    print(mlp1)\n\n    mlp2 = AGRUCell(units=10,\n                    activation=tf.keras.activations.sigmoid,\n                    initializer=tf.keras.initializers.HeUniform())\n    print(mlp2)\n\n  def test_agru_serde(self):\n    mlp1 = AGRUCell(units=10,\n                    activation=tf.keras.activations.sigmoid,\n                    initializer=tf.keras.initializers.HeUniform())\n\n    cfg = mlp1.get_config()\n    mlp2 = AGRUCell.from_config(cfg)\n\n    print(mlp1, mlp2)\n\n  def test_agru_call(self):\n    dense_layer_template = AGRUCell.params()\n\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.units = 10\n    test_params0.activation = tf.keras.activations.sigmoid\n    test_params0.initializer = tf.keras.initializers.GlorotNormal()\n    layer = test_params0.instantiate()\n    print(layer)\n\n    data = tf.keras.backend.variable(np.ones((100, 100)))\n    state = tf.keras.backend.variable(np.ones((100, 10)))\n    attr = tf.keras.backend.variable(np.ones((100, 1)))\n    _, out = layer((data, state, attr))\n    sum_out = tf.reduce_sum(out)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_agru_static_rnn_call(self):\n    dense_layer_template = AGRUCell.params()\n\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.units = 10\n    test_params0.activation = tf.keras.activations.sigmoid\n    test_params0.initializer = tf.keras.initializers.GlorotNormal()\n    cell = test_params0.instantiate()\n    print(cell)\n\n    data = tf.keras.backend.variable(np.ones((100, 20, 10)))\n    attr = tf.keras.backend.variable(np.ones((100, 20)))\n    _, out = static_rnn_with_attention(cell, inputs=data, att_scores=attr)\n    sum_out = tf.reduce_sum(out)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_agru_dynamic_rnn_call(self):\n    dense_layer_template = AGRUCell.params()\n\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.units = 10\n    test_params0.activation = tf.keras.activations.sigmoid\n    test_params0.initializer = tf.keras.initializers.GlorotNormal()\n    cell = test_params0.instantiate()\n    print(cell)\n\n    data = tf.random.uniform(shape=(100, 20, 10))\n    attr = tf.random.uniform(shape=(100, 20))\n    _, out = dynamic_rnn_with_attention(cell, inputs=data, att_scores=attr)\n    sum_out = tf.reduce_sum(out)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.compat.v1.disable_v2_behavior()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/dense.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom tensorflow.python.ops import variables as variable_ops\nfrom tensorflow.python.keras.layers import Layer\nfrom tensorflow.python.framework import tensor_shape\nfrom tensorflow.python.keras import activations\nfrom tensorflow.python.keras import regularizers\nfrom tensorflow.python.keras import backend as K\nfrom tensorflow.python.keras import initializers\nfrom tensorflow.python.keras.engine import base_layer_utils\nfrom tensorflow.python.keras.engine.input_spec import InputSpec\nfrom tensorflow.python.keras.layers.ops import core as core_ops\n\nfrom monolith.native_training.utils import with_params, get_uname\nfrom monolith.native_training.monolith_export import monolith_export\n\n\n@monolith_export\n@with_params\nclass Dense(Layer):\n  \"\"\"Dense Layer实现 :math:`y = active(wx + b)`.\n\n  之所以要重新实现一个Dense Layer, 是因为增加的了些额外的操作, 如kernel_norm, 论文可参考 https://arxiv.org/pdf/1602.07868.pdf\n  kernel_norm的计算方式为: \n  \n  .. math::\n  \n    y = active( norm_{kernel} * l2_{normalize}(W) x + b)\n  \n  先对W求 :math:`l2_{normalize}`, 将其取值限制在[-1, 1]之间, 然后乘以 :math:`norm_{kernel}`, 这样 :math:`norm_{kernel} * l2_{normalize}(W)` \n  的取值在 [-kernel_norm, kernel_norm]之间, 可以有效地防止梯度爆炸. :math:`norm_{kernel}` 一般由W的初值决定, 有 :math:`norm_{kernel} = morm(W_{init})`. \n  也可让 :math:`norm_{kernel}` 成为trainable, 让算法自已调节.\n  \n  Args:\n    units (:obj:`tf.Tensor`): 输入, 也就是x\n    activation (:obj:`tf.activation`, `str`): 激活函数, 可以用str表示, 也可以用TF中的activation\n    use_bias (:obj:`bool`): 是否使用bias\n    kernel_initializer (:obj:`tf.initializer`): kernel, 也就是W的初始化器\n    bias_initializer (:obj:`tf.initializer`): bias, 也就是b的初始化器\n    bias_regularizer (:obj:`tf.regularizer`): bias正侧化\n    allow_kernel_norm (:obj:`bool`): 是否开启kernel_norm\n    kernel_norm_trainable (:obj:`bool`):  是否让kernel_norm可训练\n    partitioner (:obj:`tf.partitioner`, optional): 分区器, 可以将一个大变量分到不同的PS机器上\n    inactive_relu_monitor (:obj:`bool`): 是否开启relu_monitor\n    inactive_relu_monitor_decay (:obj:`float`): 因为relu的非0率是用指数平均来计算的, decay就是衰减因子\n    optimizer (:obj:`tf.optimizer`): 优化器, 请参考TF\n\n  >>> dense = Dense(units=100,\n  >>>               activation=tf.keras.activations.sigmoid,\n  >>>               kernel_initializer=tf.keras.initializers.GlorotNormal())\n  >>> y = dense(x)\n  \n  \"\"\"\n\n  def __init__(self,\n               units,\n               activation=None,\n               use_bias=True,\n               kernel_initializer='glorot_uniform',\n               bias_initializer='zeros',\n               kernel_regularizer=None,\n               bias_regularizer=None,\n               allow_kernel_norm=False,\n               kernel_norm_trainable=False,\n               partitioner=None,\n               inactive_relu_monitor=False,\n               inactive_relu_monitor_decay=0.1,\n               optimizer=None,\n               **kwargs):\n    if 'input_shape' not in kwargs and 'input_dim' in kwargs:\n      kwargs['input_shape'] = (kwargs.pop('input_dim'),)\n    # Call the _init__() function for tf.keras.layers.Dense\n    super(Dense, self).__init__(**kwargs)\n\n    # Change/Add some class properties to the tf.keras.layers.Dense\n    # properties. Note that this Dense layer does not support regularizers\n    # and constraints.\n    self.units = units\n    self.activation = activations.get(activation)\n    self.use_bias = use_bias\n    self.kernel_initializer = initializers.get(kernel_initializer or\n                                               'glorot_uniform')\n    self.bias_initializer = initializers.get(bias_initializer)\n    self.kernel_var = None\n\n    self.supports_masking = True\n    self.input_spec = InputSpec(min_ndim=2)\n    self.kernel_regularizer = regularizers.get(kernel_regularizer)\n    self.bias_regularizer = regularizers.get(bias_regularizer)\n    self.allow_kernel_norm = allow_kernel_norm\n    self.kernel_norm_trainable = kernel_norm_trainable\n    self.partitioner = partitioner\n    self.inactive_relu_monitor = inactive_relu_monitor\n    self.inactive_relu_monitor_decay = inactive_relu_monitor_decay\n    self.optimizer = optimizer\n\n  def add_weight(self,\n                 name=None,\n                 shape=None,\n                 dtype=None,\n                 initializer=None,\n                 regularizer=None,\n                 trainable=None,\n                 constraint=None,\n                 use_resource=None,\n                 synchronization=tf.VariableSynchronization.AUTO,\n                 aggregation=tf.VariableAggregation.NONE,\n                 **kwargs):\n    var = super().add_weight(name=name,\n                             shape=shape,\n                             dtype=dtype,\n                             initializer=initializer,\n                             regularizer=regularizer,\n                             trainable=trainable,\n                             constraint=constraint,\n                             use_resource=use_resource,\n                             synchronization=synchronization,\n                             aggregation=aggregation,\n                             **kwargs)\n    if isinstance(var, tf.Variable):\n      var.optimizer = self.optimizer\n    elif isinstance(var, variable_ops.PartitionedVariable):\n      for var_p in var:\n        var_p.optimizer = self.optimizer\n    return var\n\n  def get_variable(self,\n                   name,\n                   shape=None,\n                   dtype=None,\n                   initializer=None,\n                   regularizer=None,\n                   trainable=None,\n                   collections=None,\n                   caching_device=None,\n                   partitioner=None,\n                   validate_shape=True,\n                   use_resource=None,\n                   custom_getter=None,\n                   constraint=None,\n                   synchronization=tf.VariableSynchronization.AUTO,\n                   aggregation=tf.VariableAggregation.NONE):\n    cur_name_scope = tf.compat.v1.get_default_graph().get_name_scope()\n    with tf.compat.v1.variable_scope(cur_name_scope,\n                                     reuse=tf.compat.v1.AUTO_REUSE):\n      var = tf.compat.v1.get_variable(name=name,\n                                      shape=shape,\n                                      dtype=dtype,\n                                      initializer=initializer,\n                                      regularizer=regularizer,\n                                      trainable=trainable,\n                                      collections=collections,\n                                      caching_device=caching_device,\n                                      partitioner=partitioner,\n                                      validate_shape=validate_shape,\n                                      use_resource=use_resource,\n                                      custom_getter=custom_getter,\n                                      constraint=constraint,\n                                      synchronization=synchronization,\n                                      aggregation=aggregation)\n    if isinstance(var, tf.Variable):\n      var.optimizer = self.optimizer\n    elif isinstance(var, variable_ops.PartitionedVariable):\n      for var_p in var:\n        var_p.optimizer = self.optimizer\n\n    if base_layer_utils.is_split_variable(var) or isinstance(\n        var, variable_ops.PartitionedVariable):\n      for v in var:\n        K.track_variable(v)\n        if trainable:\n          self._trainable_weights.append(v)\n        else:\n          self._non_trainable_weights.append(v)\n    else:\n      K.track_variable(var)\n      if trainable:\n        self._trainable_weights.append(var)\n      else:\n        self._non_trainable_weights.append(var)\n    return var\n\n  def build(self, input_shape):\n    dtype = tf.dtypes.as_dtype(self.dtype or K.floatx())\n    if not (dtype.is_floating or dtype.is_complex):\n      raise TypeError('Unable to build `Dense` layer with non-floating point '\n                      'dtype %s' % (dtype,))\n    input_shape = tensor_shape.TensorShape(input_shape)\n    if tensor_shape.dimension_value(input_shape[-1]) is None:\n      raise ValueError('The last dimension of the inputs to `Dense` '\n                       'should be defined. Found `None`.')\n    last_dim = tensor_shape.dimension_value(input_shape[-1])\n    self.input_spec = InputSpec(min_ndim=2, axes={-1: last_dim})\n\n    kernel_shape = [last_dim, self.units]\n    init_kernel = self.kernel_initializer(shape=kernel_shape, dtype=self.dtype)\n\n    self.kernel_var = self.get_variable(initializer=init_kernel,\n                                        trainable=True,\n                                        name='kernel',\n                                        shape=None,\n                                        dtype=dtype,\n                                        regularizer=self.kernel_regularizer,\n                                        partitioner=self.partitioner)\n    self.kernel = self.kernel_var\n\n    # Add the option for allow_kernel_norm\n    if self.allow_kernel_norm:\n      self.kernel = tf.nn.l2_normalize(self.kernel,\n                                       axis=0,\n                                       epsilon=1e-6,\n                                       name='normalized_kernel')\n      if self.kernel_norm_trainable:\n        init_trainable_kernel_norm = tf.linalg.norm(init_kernel, axis=0)\n        self.trainable_kernel_norm = self.get_variable(\n            initializer=init_trainable_kernel_norm,\n            shape=None,\n            trainable=True,\n            name='trainable_kernel_norm',\n            dtype=dtype,\n            partitioner=self.partitioner)\n        self.kernel = tf.multiply(self.kernel,\n                                  self.trainable_kernel_norm,\n                                  name='mul_of_kernel_and_trainable_norm')\n\n    if self.use_bias:\n      self.bias = self.add_weight(name='bias',\n                                  shape=[self.units],\n                                  initializer=self.bias_initializer,\n                                  regularizer=self.bias_regularizer,\n                                  dtype=dtype,\n                                  trainable=True)\n    else:\n      self.bias = None\n\n    if self.inactive_relu_monitor and self.activation.__name__ == 'relu':\n      self.inactive_relu_count_moving_avg = self.get_variable(\n          initializer=tf.keras.initializers.zeros,\n          trainable=False,\n          name='inactive_relu_count_moving_avg',\n          shape=[self.units],\n          dtype=tf.float32,\n          collections=[\n              tf.compat.v1.GraphKeys.METRIC_VARIABLES,\n              tf.compat.v1.GraphKeys.GLOBAL_VARIABLES\n          ])\n\n    super(Dense, self).build(input_shape)\n\n  def call(self, inputs, **kwargs):\n    output = core_ops.dense(inputs,\n                            self.kernel,\n                            self.bias,\n                            self.activation,\n                            dtype=self._compute_dtype_object)\n    if self.inactive_relu_monitor:\n      inactive_relu_count = self.units - tf.math.count_nonzero(output, axis=0)\n      tf.compat.v1.summary.histogram('inactive_relu_count_moving_avg',\n                                     self.inactive_relu_count_moving_avg)\n      update_op = tf.compat.v1.assign(\n          self.inactive_relu_count_moving_avg,\n          (1. - self.inactive_relu_monitor_decay) *\n          self.inactive_relu_count_moving_avg +\n          self.inactive_relu_monitor_decay *\n          tf.cast(inactive_relu_count, dtype=tf.float32),\n      )\n      with tf.control_dependencies([update_op]):\n        output = tf.identity(output)\n    return output\n\n  def compute_output_shape(self, input_shape):\n    input_shape = tensor_shape.TensorShape(input_shape)\n    input_shape = input_shape.with_rank_at_least(2)\n    if tensor_shape.dimension_value(input_shape[-1]) is None:\n      raise ValueError(\n          'The innermost dimension of input_shape must be defined, but saw: %s'\n          % input_shape)\n    return input_shape[:-1].concatenate(self.units)\n\n  def get_config(self):\n    config = {\n        'units': self.units,\n        'activation': activations.serialize(self.activation),\n        'use_bias': self.use_bias,\n        'kernel_initializer': initializers.serialize(self.kernel_initializer),\n        'bias_initializer': initializers.serialize(self.bias_initializer),\n        'kernel_regularizer': regularizers.serialize(self.kernel_regularizer),\n        'bias_regularizer': regularizers.serialize(self.bias_regularizer),\n        'allow_kernel_norm': self.allow_kernel_norm,\n        'kernel_norm_trainable': self.kernel_norm_trainable,\n        'partitioner': self.partitioner,\n    }\n    base_config = super(Dense, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n"
  },
  {
    "path": "monolith/native_training/layers/dense_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport os\n\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.dense import Dense\n\n\nclass DenseTest(tf.test.TestCase):\n\n  def test_dense_instantiate(self):\n    dense_layer_template = Dense.params()\n\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.units = 100\n    test_params0.activation = tf.keras.activations.sigmoid\n    test_params0.kernel_initializer = tf.keras.initializers.GlorotNormal()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = Dense(units=100,\n                 activation=tf.keras.activations.sigmoid,\n                 kernel_initializer=tf.keras.initializers.GlorotNormal())\n    print(ins2)\n\n  def test_dense_serde(self):\n    dense_layer_template = Dense.params()\n\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.units = 100\n    test_params0.activation = tf.keras.activations.sigmoid\n    test_params0.kernel_initializer = tf.keras.initializers.GlorotNormal()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    cfg = ins1.get_config()\n    ins2 = Dense.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_dense_call(self):\n    layer = Dense(units=100,\n                  activation=tf.keras.activations.sigmoid,\n                  kernel_initializer=tf.keras.initializers.GlorotNormal())\n\n    data = tf.keras.backend.variable(np.ones((100, 100)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_dense_kernel_norm_call(self):\n    layer = Dense(units=100,\n                  allow_kernel_norm=True,\n                  kernel_norm_trainable=True,\n                  activation=tf.keras.activations.sigmoid,\n                  kernel_initializer=tf.keras.initializers.GlorotNormal())\n\n    data = tf.keras.backend.variable(np.ones((100, 100)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_inactive_relu_monitor(self):\n    dense_layer_template = Dense.params()\n\n    test_params = dense_layer_template.copy()\n    test_params.units = 10\n    test_params.activation = tf.keras.activations.relu\n    test_params.inactive_relu_monitor = True\n    layer = test_params.instantiate()\n\n    with tf.Graph().as_default():\n      x = tf.constant([[1., 1., 1., 1., 1.]])\n      _ = layer(x)\n      graph = tf.compat.v1.get_default_graph()\n      self.assertIn('Dense/inactive_relu_count_moving_avg_1',\n                    [node.name for node in graph.as_graph_def().node])\n\n  def test_dense_with_explicit_partition(self):\n    layer = Dense(units=1024,\n                  allow_kernel_norm=True,\n                  kernel_norm_trainable=True,\n                  activation=tf.keras.activations.sigmoid,\n                  kernel_initializer=tf.keras.initializers.GlorotNormal(),\n                  partitioner=tf.compat.v1.variable_axis_size_partitioner(\n                      max_shard_bytes=1 << 17, max_shards=5))\n\n    data = tf.keras.backend.variable(np.ones((100, 294)))\n    sum_out = layer(data)\n    partition_dims = []\n    expected_dims = [59, 59, 59, 59, 58]\n    for var in layer.kernel_var:\n      partition_dims.append(var.shape[0])\n\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sum_out = sess.run(sum_out)\n      self.assertEqual(sum_out.shape, (100, 1024))\n\n  def test_dense_with_implicit_partition(self):\n    with tf.compat.v1.variable_scope(\n        \"\",\n        partitioner=tf.compat.v1.variable_axis_size_partitioner(\n            max_shard_bytes=1 << 17, max_shards=5)):\n      # The dense kernel's shape is [294, 1024] and will be\n      # partitioned into five shards(unevenly)\n      layer = Dense(units=1024,\n                    allow_kernel_norm=True,\n                    kernel_norm_trainable=True,\n                    activation=tf.keras.activations.sigmoid,\n                    kernel_initializer=tf.keras.initializers.GlorotNormal(),\n                    partitioner=None)\n      data = tf.keras.backend.variable(np.ones((100, 294)))\n      sum_out = layer(data)\n      partition_dims = []\n      expected_dims = [59, 59, 59, 59, 58]\n      for var in layer.kernel_var:\n        partition_dims.append(var.shape[0])\n\n      self.assertEqual(partition_dims, expected_dims)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sum_out = sess.run(sum_out)\n      self.assertEqual(sum_out.shape, (100, 1024))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/feature_cross.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom typing import List\nfrom absl import logging\n\nimport tensorflow as tf\nfrom tensorflow.keras.layers import Layer, Conv1D\nfrom tensorflow.python.keras import activations\nimport tensorflow.keras.initializers as initializers\nfrom tensorflow.python.keras import regularizers\nfrom monolith.native_training.layers.mlp import MLP\nfrom monolith.native_training.utils import with_params, get_uname\nfrom monolith.native_training.layers.utils import merge_tensor_list, DCNType\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.layers.layer_ops import ffm\nfrom tensorflow.python.ops import variables as variable_ops\nfrom tensorflow.python.keras.engine import base_layer_utils\nfrom tensorflow.python.keras import backend as K\n\nfrom monolith.native_training.layers.utils import check_dim, dim_size\n\n\n@monolith_export\n@with_params\nclass GroupInt(Layer):\n  \"\"\"Group Interaction的缩写, 一种简单的特征交叉方式, 同时支持attention. 论文可参考 https://www.csie.ntu.edu.tw/~b97053/paper/Rendle2010FM.pdf\n\n  特征交叉可以在多个层面做, 一种方法是在特征工程中做, 即在特征工程阶段直接生成一个新特征, 这个特征是由多个原始征特拼接起来的, 然后再做Embedding. \n  这样做的好处是记忆性较好, 但由于稀疏性, 有时训练不够充分, 也存在过拟合的风险. 另一种是在模型层面做, 代表算法为FM, DeepFM等\n  \n  在模型中做二阶特征交叉存在如下问题:\n    - 输出维度高: FM用点积表示特征交叉, 如果输入有n个特征, 输出有 n(n-1)/2 维, 当特征较多时, 给训练/推理带来很大的负担\n    - 重复交叉: 特征交叉可以在两个地方做, 现实中往往同时做. FM等算法并不区分参与交叉的是原始特征还是交叉特征. 所以存在重复交叉. 不过, 也有人认为\n      重复交叉会生成更高阶的特征, 不是重复\n  \n  为了克服FM等算法的不足, 可以使用GroupInt. 它先将特征分组(Group), 哪些特征属于一个组由算法开发人员确定. 然后用sumpooling来将特征聚合\n  得到group embedding. 最后用group embedding做两两交叉输出\n  \n  GroupInt输出有如下几种形式:\n    - 交叉用dot, 直接输出. 此时输出的大小远小于原始FM, 而且, 人工确定group, 减少了重复交叉\n    - 交叉用multiply, 输出有两种选择:\n        - 直接concat输出\n        - 用attention, 将所以结果线性组合后输出(与AFM一样, 论文可参考 https://www.ijcai.org/proceedings/2017/0435.pdf)\n\n  Args:\n    interaction_type (:obj:`str`): Interaction的方式有两种, dot和multiply\n    use_attention (:obj:`bool`): 是否使用attention, 当interaction_type为'multiply'时才可用\n    attention_units (:obj:`List[int]`): 使用一个MLP生成attention, attention_units表示MLP每一层的dim, 最后一维必须是1\n    activation (:obj:`tf.activation`): MLP的激活函数\n    initializer (:obj:`tf.initializer`): MLP的初始化器\n    regularizer (:obj:`tf.regularizer`): MLP的正则化器\n    out_type (:obj:`str`): 输出类型, 可以为stack, concat, None\n    keep_list (:obj:`bool`): 输出是否保持list\n  \n  \"\"\"\n\n  def __init__(self,\n               interaction_type='multiply',\n               use_attention: bool = False,\n               attention_units: List[int] = None,\n               activation='relu',\n               initializer=None,\n               regularizer=None,\n               out_type='concat',\n               keep_list: bool = False,\n               **kwargs):\n    super(GroupInt, self).__init__(**kwargs)\n    assert interaction_type in ['multiply', 'dot']\n    self.interaction_type = interaction_type\n\n    self.use_attention = use_attention\n    if use_attention:\n      assert interaction_type == 'multiply'\n\n    self.attention_units = attention_units\n    self.activation = activations.get(activation)\n    self.initializer = initializers.get(\n        initializer) or initializers.GlorotNormal()\n    self.regularizer = regularizers.get(regularizer)\n\n    self.out_type = out_type\n    self.keep_list = keep_list\n\n  def build(self, input_shape):\n    if self.use_attention:\n      assert self.attention_units[-1] == 1\n      self.mlp = MLP(name='groupint_attention_mlp',\n                     output_dims=self.attention_units,\n                     activations=self.activation,\n                     initializers=self.initializer,\n                     kernel_regularizer=self.regularizer)\n    else:\n      self.mlp = None\n\n    return super().build(input_shape)\n\n  def call(self, inputs, **kwargs):\n    left_fields, right_fields = inputs\n    left, right = tf.concat(left_fields, axis=1), tf.concat(right_fields,\n                                                            axis=1)\n    last_dim_size = dim_size(left_fields[0], -1)\n    ffm_embeddings = ffm(left=left,\n                         right=right,\n                         dim_size=last_dim_size,\n                         int_type=self.interaction_type)\n\n    if self.interaction_type == 'multiply':\n      if self.use_attention:\n        num_feature = len(left_fields) * len(\n            right_fields\n        )  #int(dim_size(left, 1) * dim_size(right, 1) / last_dim_size)\n        stacked = tf.reshape(ffm_embeddings,\n                             shape=(-1, num_feature, last_dim_size))\n        attention = self.mlp(stacked)  # (bs, num_feature, 1)\n        ffm_embeddings = tf.reshape(stacked * attention,\n                                    shape=(-1, num_feature * last_dim_size))\n    return [ffm_embeddings] if self.keep_list else ffm_embeddings\n\n  def get_config(self):\n    config = {\n        'interaction_type': self.interaction_type,\n        'use_attention': self.use_attention,\n        'attention_units': self.attention_units,\n        'activation': activations.serialize(self.activation),\n        'initializer': initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n        'out_type': self.out_type,\n        'keep_list': self.keep_list\n    }\n    base_config = super(GroupInt, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\nFFM = GroupInt\n\n\n@monolith_export\n@with_params\nclass AllInt(Layer):\n  r\"\"\"AllInt是All Interaction的缩写, 是一种简单的特征交叉方式, 通过引入压缩矩阵, 减少输出大小. 论文可参考 https://www.csie.ntu.edu.tw/~b97053/paper/Rendle2010FM.pdf\n\n  GroupInt虽然能克服FM带来的输出膨胀的问题, 但也有其它问题, 如Group要人工决定, 给算法开发人员带来较大的负担. AllInt将所有特征都做交叉, 不用人工选择, \n  同时引入压缩矩阵来减少输出大小\n  \n  All Interaction中引入压缩矩阵. 如下:\n  \n  .. math::\n  \n    O_{n, c} = X_{n, k} * X_{n, k}^T * C_{n, c}\n  \n  为了避免生成(n, n)的大中间矩阵, 在计算上进行了一些优化, 即先算 :math:`X_{n, k}^T * C_{n, c}`, 这样得到的(k, c)矩阵小很多, 计算效率高\n\n  Args:\n    cmp_dim (:obj:`int`): 压缩维的维度\n    initializer (:obj:`tf.initializer`): 初始化器\n    regularizer (:obj:`tf.regularizer`): kernel正则化器\n    use_bias (:obj:`bool`) 是否启用bias\n    out_type (:obj:`str`): 输出类型, 可以为stack, concat, None\n    keep_list (:obj:`bool`): 输出是否保持list\n    \n  \"\"\"\n\n  def __init__(self,\n               cmp_dim,\n               initializer=None,\n               regularizer=None,\n               use_bias=True,\n               out_type='concat',\n               keep_list=False,\n               **kwargs):\n    super(AllInt, self).__init__(**kwargs)\n    self.cmp_dim = cmp_dim\n    self.initializer = initializers.get(\n        initializer) or initializers.GlorotNormal()\n    self.regularizer = regularizers.get(regularizer)\n    self.use_bias = use_bias\n\n    self.out_type = out_type\n    self.keep_list = keep_list\n\n  def build(self, input_shape):\n    num_feat = check_dim(input_shape[1])\n    self.kernel = self.add_weight(name='allint_kernel',\n                                  shape=(num_feat, self.cmp_dim),\n                                  dtype=tf.float32,\n                                  initializer=self.initializer,\n                                  regularizer=self.regularizer,\n                                  trainable=True)\n    if self.use_bias:\n      self.bias = self.add_weight(name='allint_bias',\n                                  shape=(self.cmp_dim,),\n                                  dtype=tf.float32,\n                                  initializer=initializers.Zeros(),\n                                  trainable=True)\n    return super(AllInt, self).build(input_shape)\n\n  def call(self, embeddings, **kwargs):\n    # embeddings: [batch_size, num_feat, emb_size]\n    transposed = tf.transpose(embeddings,\n                              perm=[0, 2,\n                                    1])  # [batch_size, emb_size, num_feat]\n    feature_comp = tf.matmul(transposed,\n                             self.kernel)  # [batch_size, emb_size, cmp_dim]\n    if self.use_bias:\n      feature_comp += self.bias\n    # [batch_size, num_feat, emb_size] * [batch_size, emb_size, cmp_dim] -> [batch_size, num_feat, cmp_dim]\n    interaction = tf.matmul(embeddings,\n                            feature_comp)  # [batch_size, num_feat, cmp_dim]\n\n    return merge_tensor_list(interaction,\n                             merge_type=self.out_type,\n                             keep_list=self.keep_list)\n\n  def get_config(self):\n    config = {\n        'cmp_dim': self.cmp_dim,\n        'initializer': initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n        'use_bias': self.use_bias,\n        'out_type': self.out_type,\n        'keep_list': self.keep_list\n    }\n\n    base_config = super(AllInt, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@monolith_export\n@with_params\nclass CDot(Layer):\n  \"\"\"Compression and Dot Interaction, CDot. 可以看成是Allint的升级版, 也是一种自动做特征交叉的方法. 论文可参考 https://arxiv.org/pdf/1803.05170.pdf\n  \n  Allint通过引入压缩矩阵, 减少相对FM的输出大小, 同时移除了GroupInt中人工定义Group的不足, CDot与Allint十分相似\n  \n  CDot相对Allint的改进在于:\n    - AllInt引入的压缩矩阵与输入无关, 在CDot中, 压缩矩阵是与输入数据相关, 可以根据输入, 自适应地调节压缩矩阵. \n    - CDot输出时, 会将压缩后的中间特征也输出, 作为上层MLP的输入, Allint不会做这一步\n  \n  一般提取高阶特征交叉时使用MLP, MLP的输入是直接接拼起来的Embedding. 一些实验表明, 可以先用CDot提取二阶特征, 再在二阶特征基础上提取高阶\n  特征效果更好. 所以CDot也可以与MLP联用, 用于高阶特征提取\n  \n  Args:\n    project_dim (:obj:`int`): 投影dim\n    compress_units (:obj:`List[int]`): 用一个MLP来压缩, 压缩MLP的各层dims\n    activation (:obj:`tf.activation`): MLP的激活函数\n    initializer (:obj:`tf.initializer`): 初始化器\n    regularizer (:obj:`tf.regularizer`): kernel正则化器\n    \n  \"\"\"\n\n  def __init__(self,\n               project_dim,\n               compress_units,\n               activation='relu',\n               initializer=None,\n               regularizer=None,\n               **kwargs):\n    super(CDot, self).__init__(**kwargs)\n    self.activation = activations.get(activation)\n    self.initializer = initializers.get(\n        initializer) or initializers.GlorotNormal()\n    self.regularizer = regularizers.get(regularizer)\n\n    self.project_dim = project_dim\n    self.compress_units = compress_units\n\n  def build(self, input_shape):\n    (_, num_feature, emd_size) = input_shape\n    self._num_feature = check_dim(num_feature)\n    self._emd_size = check_dim(emd_size)\n\n    self.project_weight = self.add_weight(name=\"project_weight\",\n                                          shape=(num_feature, self.project_dim),\n                                          dtype=tf.float32,\n                                          initializer=self.initializer,\n                                          regularizer=self.regularizer)\n\n    self.compress_tower = MLP(output_dims=self.compress_units +\n                              [emd_size * self.project_dim],\n                              activations=self.activation,\n                              initializers=self.initializer,\n                              kernel_regularizer=self.regularizer,\n                              name=\"compress_tower\")\n    self._trainable_weights.extend(self.compress_tower.trainable_weights)\n    self._non_trainable_weights.extend(\n        self.compress_tower.non_trainable_weights)\n    return super(CDot, self).build(input_shape)\n\n  def call(self, inputs, **kwargs):\n    # 1) project the origin feature into raw compressed space\n    transed_input = tf.transpose(inputs,\n                                 perm=[0, 2, 1\n                                      ])  # (batch_size, emd_size, num_feature)\n    # (batch_size, emd_size, num_feature) * (num_feature, project_dim) -> (batch_size, emd_size, project_dim)\n    projected = tf.matmul(transed_input, self.project_weight)\n\n    # 2) concat the raw compressed features, and go through mlp to cast to compressed space\n    concated = tf.reshape(\n        projected,\n        shape=(-1, self._emd_size *\n               self.project_dim))  # (batch_size, emd_size * project_dim)\n    compressed = self.compress_tower(\n        concated)  # (batch_size, emd_size * project_dim)\n\n    # 3) feature cross\n    # (batch_size, num_feature, emd_size) * (batch_size, emd_size, project_dim)  -> (batch_size, num_feature, project_dim)\n    crossed = tf.matmul(\n        inputs,\n        tf.reshape(compressed, shape=(-1, self._emd_size, self.project_dim)))\n    crossed = tf.reshape(\n        crossed,\n        shape=(-1, self._num_feature *\n               self.project_dim))  # (batch_size, num_feature * project_dim)\n\n    # 4) concat the compressed features and crossed features\n    return tf.concat([crossed, compressed], axis=1)\n\n  def get_config(self):\n    config = {\n        'project_dim': self.project_dim,\n        'compress_units': self.compress_units,\n        'activation': activations.serialize(self.activation),\n        'initializer': initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n    }\n\n    base_config = super(CDot, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@monolith_export\n@with_params\nclass CAN(Layer):\n  \"\"\"Co-action Network, CAN, 协同作用网络 论文可参考 https://arxiv.org/pdf/2011.05625.pdf\n  \n  在模型中做特征交叉, 同一份Embedding, 同时要拟合原始特征/交叉特征, 容易两个都拟合不好. CAN是为了改善这种情况提出的, 通过拓展参数, 使得交叉特征与原始特征的学习相对独立\n  \n  CAN Unit将要建模的”特征对”分为weight side(item)和input side(user):\n    - weight side可以reshape成MLP的参数\n    - input side作为MLP的输入，通过多层MLP来建模co-action\n  \n  Args:\n    layer_num (:obj:`int`): Layer的层数\n    activation (:obj:`tf.activation`): 激活函数\n    is_seq (:obj:`bool`): 是否为序列特征\n    is_stacked (:obj:`bool`): User侧是否是多个特征stack起来的\n    \n  \"\"\"\n\n  def __init__(self,\n               layer_num: int = 2,\n               activation='relu',\n               is_seq: bool = False,\n               is_stacked: bool = True,\n               **kwargs):\n    super(CAN, self).__init__(**kwargs)\n    self.layer_num = layer_num\n    self.activation = activations.get(activation)\n    self.is_seq = is_seq\n    self.is_stacked = is_stacked\n\n  def build(self, input_shape):\n    user_emb_sh, item_emb_sh = input_shape\n    self._batch_size = check_dim(user_emb_sh[0])\n    assert user_emb_sh[0] == item_emb_sh[0]\n    u_emb_size = check_dim(user_emb_sh[-1])\n    iemb_size = check_dim(item_emb_sh[-1])\n\n    assert iemb_size == (u_emb_size * (u_emb_size + 1)) * self.layer_num\n    self._splits = [u_emb_size * u_emb_size, u_emb_size] * self.layer_num\n\n    return super(CAN, self).build(input_shape)\n\n  def call(self, inputs, **kwargs):\n    user_emb, item_emb = inputs\n    if self._batch_size == -1:\n      self._batch_size = dim_size(user_emb, 0)\n\n    dims = self._splits[1]\n    if self.is_seq and self.is_stacked:\n      # user_emb shape: (bs, num_feat, seq_len, u_emb_size)\n      weight_shape = (self._batch_size, 1, dims, dims)\n      bias_shape = (self._batch_size, 1, 1, dims)\n    elif not self.is_seq and self.is_stacked:\n      # user_emb shape: (bs, num_feat, u_emb_size)\n      weight_shape = (self._batch_size, dims, dims)\n      bias_shape = (self._batch_size, 1, dims)\n    elif self.is_seq and not self.is_stacked:\n      # user_emb shape: (bs, seq_len, u_emb_size)\n      weight_shape = (self._batch_size, dims, dims)\n      bias_shape = (self._batch_size, 1, dims)\n    else:\n      # user_emb shape: (bs, u_emb_size)\n      user_emb = tf.expand_dims(user_emb, axis=1)  # (bs, 1, u_emb_size)\n      weight_shape = (self._batch_size, dims, dims)\n      bias_shape = (self._batch_size, 1, dims)\n\n    params = tf.split(item_emb, num_or_size_splits=self._splits, axis=1)\n    for i in range(self.layer_num):\n      weight = tf.reshape(params[2 * i], shape=weight_shape)\n      bias = tf.reshape(params[2 * i + 1], shape=bias_shape)\n      if self.activation is not None:\n        user_emb = self.activation(tf.matmul(user_emb, weight) + bias)\n      else:\n        user_emb = tf.matmul(user_emb, weight) + bias\n\n    if self.is_seq and self.is_stacked:\n      # user_emb shape: (bs, num_feat, seq_len, u_emb_size)\n      return tf.reduce_sum(user_emb, axis=2)  # (bs, num_feat, u_emb_size)\n    elif not self.is_seq and self.is_stacked:\n      # user_emb shape: (bs, num_feat, u_emb_size)\n      return user_emb  # (bs, num_feat, u_emb_size)\n    elif self.is_seq and not self.is_stacked:\n      # user_emb shape: (bs, seq_len, u_emb_size)\n      return tf.reduce_sum(user_emb, axis=1)  # (bs, u_emb_size)\n    else:\n      # user_emb shape: (bs, 1, u_emb_size)\n      return tf.squeeze(user_emb)  # (bs, u_emb_size)\n\n  def get_config(self):\n    config = {\n        'layer_num': self.layer_num,\n        'activation': activations.serialize(self.activation),\n        \"is_seq\": self.is_seq,\n        \"is_stacked\": self.is_stacked\n    }\n    base_config = super(CAN, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@monolith_export\n@with_params\nclass DCN(Layer):\n  r\"\"\"二阶特征交叉可用FM等方法显式提取, 更高阶的交叉用MLP隐式提取. Deep & Cross Network (DCN)可替代MLP做高阶特征交叉, \n  通过加入残差联接, 达到比MLP更好的效果\n  \n  DCN现在有三个版本(论文可参考 https://arxiv.org/pdf/1708.05123.pdf):\n    - vector, :math:`x_{l+1} = x_0 * x_l w + b + x_l`, 其中w的shape为(dim, 1)\n    - matrix, :math:`x_{l+1} = x_0 * (x_l w + b) + x_l`, 其中w的shape为(dim, dim)\n    - mixed, :math:`x_{l+1} = \\sum_i x_0 * (x_l V C U^T + b) * softmax(x_l g) + x_l`\n  \n  Args:\n    layer_num (:obj:`int`): DCN的层数\n    dcn_type (:obj:`str`): DCN类型, 目前支持三种vector/matrix/mixed\n    initializer (:obj:`tf.initializer`): 初始化器\n    regularizer (:obj:`tf.regularizer`): 正则化器\n    num_experts (:obj:`int`): 只在mixed模式下有用, 用于指定expert个数\n    low_rank (:obj:`int`): 只在mixed模式下有用, 用于指定低秩\n    use_dropout (:obj:`bool`): 只否使用dropout\n    keep_prob (:obj:`float`): dropout的保留概率\n    mode (:obj:`str`): 运行模式, 可以是train/eval/predict\n  \n  \"\"\"\n\n  def __init__(self,\n               layer_num: int = 1,\n               dcn_type: str = DCNType.Matrix,\n               initializer=None,\n               regularizer=None,\n               num_experts: int = 1,\n               low_rank: int = 0,\n               allow_kernel_norm: bool = False,\n               use_dropout=False,\n               keep_prob=0.95,\n               mode: str = tf.estimator.ModeKeys.TRAIN,\n               **kwargs):\n    super(DCN, self).__init__(**kwargs)\n    self.layer_num = layer_num\n    self.dcn_type = dcn_type\n    self.num_experts = num_experts\n    self.low_rank = low_rank\n    self.initializer = initializers.get(\n        initializer) or initializers.GlorotNormal()\n    self.regularizer = regularizers.get(regularizer)\n    self.allow_kernel_norm = allow_kernel_norm\n    self.use_dropout = use_dropout\n    self.keep_prob = keep_prob\n    self.mode = mode\n\n  def build(self, input_shape):\n    dims = check_dim(input_shape[-1])\n    if self.dcn_type == DCNType.Vector:\n      self.kernel = [\n          self.get_variable(name='kernel_{}'.format(i),\n                            shape=[dims, 1],\n                            dtype=tf.float32,\n                            initializer=self.initializer,\n                            regularizer=self.regularizer,\n                            trainable=True) for i in range(self.layer_num)\n      ]\n    elif self.dcn_type == DCNType.Matrix:\n      self.kernel = [\n          self.get_variable(name='kernel_{}'.format(i),\n                            shape=[dims, dims],\n                            dtype=tf.float32,\n                            initializer=self.initializer,\n                            regularizer=self.regularizer,\n                            trainable=True) for i in range(self.layer_num)\n      ]\n    else:\n      self.U = [[\n          self.get_variable(name='U_{}_{}'.format(i, j),\n                            shape=[dims, self.low_rank],\n                            dtype=tf.float32,\n                            initializer=self.initializer,\n                            regularizer=self.regularizer,\n                            trainable=True) for j in range(self.num_experts)\n      ] for i in range(self.layer_num)]\n\n      self.V = [[\n          self.get_variable(name='V_{}_{}'.format(i, j),\n                            shape=[dims, self.low_rank],\n                            dtype=tf.float32,\n                            initializer=self.initializer,\n                            regularizer=self.regularizer,\n                            trainable=True) for j in range(self.num_experts)\n      ] for i in range(self.layer_num)]\n\n      self.C = [[\n          self.get_variable(name='C_{}_{}'.format(i, j),\n                            shape=[self.low_rank, self.low_rank],\n                            dtype=tf.float32,\n                            initializer=self.initializer,\n                            regularizer=self.regularizer,\n                            trainable=True) for j in range(self.num_experts)\n      ] for i in range(self.layer_num)]\n\n      self.G = [[\n          self.get_variable(name='G_{}_{}'.format(i, j),\n                            shape=[dims, 1],\n                            dtype=tf.float32,\n                            initializer=self.initializer,\n                            regularizer=self.regularizer,\n                            trainable=True) for j in range(self.num_experts)\n      ] for i in range(self.layer_num)]\n\n    self.bias = [\n        self.get_variable(name='bias_{}'.format(i),\n                          shape=[1, dims],\n                          dtype=tf.float32,\n                          initializer=initializers.Zeros(),\n                          regularizer=None,\n                          trainable=True) for i in range(self.layer_num)\n    ]\n\n    return super(DCN, self).build(input_shape)\n\n  def call(self, inputs, **kwargs):\n    x0 = inputs\n    xl = x0\n\n    for i in range(self.layer_num):\n      if self.dcn_type == DCNType.Vector:\n        xl = x0 * tf.matmul(xl, self.kernel[i]) + self.bias[i] + xl\n      elif self.dcn_type == DCNType.Matrix:\n        xl = x0 * (tf.matmul(xl, self.kernel[i]) + self.bias[i]) + xl\n      else:\n        output_of_experts = []\n        gating_score_of_experts = []\n        for expert_id in range(self.num_experts):\n          # (1) G(x_l)\n          # compute the gating score by x_l: (batch_size, 1)\n          gating_score_of_experts.append(tf.matmul(xl, self.G[i][expert_id]))\n\n          # (2) E(x_l)\n          # project the input x_l to $\\mathbb{R}^{r}$\n          v_x = tf.matmul(xl, self.V[i][expert_id])  # (batch_size, low_rank)\n          v_x = tf.tanh(v_x)\n\n          # nonlinear activation in low rank space\n          cv_x = tf.matmul(v_x, self.C[i][expert_id])  # (batch_size, low_rank)\n          cv_x = tf.tanh(cv_x)\n\n          # project back to $\\mathbb{R}^{d}$\n          ucv_x = tf.matmul(cv_x, self.U[i][expert_id],\n                            transpose_b=True)  # (batch_size, num_feat)\n\n          out = x0 * (ucv_x + self.bias[i])\n          output_of_experts.append(out)\n\n        # (3) mixture of low-rank experts\n        output_of_experts = tf.stack(output_of_experts,\n                                     -1)  # (batch_size, num_feat, num_experts)\n        gating_score_of_experts = tf.stack(gating_score_of_experts,\n                                           -2)  # (bs, num_experts, 1)\n        gating_score_of_experts = tf.nn.softmax(gating_score_of_experts,\n                                                axis=-1)\n        moe_out = tf.matmul(output_of_experts, gating_score_of_experts)\n        xl = tf.squeeze(moe_out, -1) + xl\n\n      if self.use_dropout and self.mode == tf.estimator.ModeKeys.TRAIN:\n        xl = tf.nn.dropout(xl, rate=1 - self.keep_prob)\n\n    return xl\n\n  def get_variable(self, name, shape, dtype, initializer, regularizer,\n                   trainable):\n    # ref https://arxiv.org/pdf/1602.07868.pdf\n    if self.allow_kernel_norm:\n      upper_ns = tf.compat.v1.get_default_graph().get_name_scope()\n      var_init = initializer(shape, dtype)\n      with tf.compat.v1.name_scope(f'{upper_ns}/{name}/') as name_scope:\n        var_name = name_scope.strip('/')\n        with tf.compat.v1.variable_scope('', reuse=tf.compat.v1.AUTO_REUSE):\n          var = tf.compat.v1.get_variable(initializer=var_init,\n                                          name=var_name,\n                                          dtype=dtype,\n                                          regularizer=regularizer,\n                                          trainable=trainable)\n\n        normalized = tf.nn.l2_normalize(var,\n                                        axis=0,\n                                        epsilon=1e-6,\n                                        name='normalized_var')\n        var_norm_init = tf.norm(var_init, axis=0, name='init_trainable_norm')\n        if base_layer_utils.is_split_variable(var) or isinstance(\n            var, variable_ops.PartitionedVariable):\n          for v in var:\n            K.track_variable(v)\n            if trainable:\n              self._trainable_weights.append(v)\n            else:\n              self._non_trainable_weights.append(v)\n        else:\n          K.track_variable(var)\n          if trainable:\n            self._trainable_weights.append(var)\n          else:\n            self._non_trainable_weights.append(var)\n\n        with tf.compat.v1.variable_scope('', reuse=tf.compat.v1.AUTO_REUSE):\n          trainable_var_norm = tf.compat.v1.get_variable(\n              initializer=var_norm_init,\n              name=f'{var_name}/trainable_norm',\n              dtype=dtype)\n\n        if base_layer_utils.is_split_variable(trainable_var_norm) or isinstance(\n            trainable_var_norm, variable_ops.PartitionedVariable):\n          for v in trainable_var_norm:\n            K.track_variable(v)\n            if trainable:\n              self._trainable_weights.append(v)\n            else:\n              self._non_trainable_weights.append(v)\n        else:\n          K.track_variable(trainable_var_norm)\n          if trainable:\n            self._trainable_weights.append(trainable_var_norm)\n          else:\n            self._non_trainable_weights.append(trainable_var_norm)\n        var = tf.multiply(normalized, trainable_var_norm, name='mul_var_norm')\n    else:\n      var = self.add_weight(initializer=initializer,\n                            shape=shape,\n                            name=name,\n                            dtype=dtype,\n                            regularizer=regularizer,\n                            trainable=trainable)\n\n    return var\n\n  def get_config(self):\n    config = {\n        'layer_num': self.layer_num,\n        'dcn_type': self.dcn_type,\n        'initializer': initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n        'num_experts': self.num_experts,\n        'low_rank': self.low_rank,\n        'allow_kernel_norm': self.allow_kernel_norm,\n        'use_dropout': self.use_dropout,\n        'keep_prob': self.keep_prob,\n        'mode': self.mode\n    }\n\n    base_config = super(DCN, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@monolith_export\n@with_params\nclass CIN(Layer):\n  r\"\"\"Compressed Interaction Network, CIN, 压缩相互作用网络. 它是高阶(二阶以上)特征提取方法, 形式上是DCN与FM的结合体, 也是xDeepFM的核心. 论文可参考 https://arxiv.org/pdf/1703.04247.pdf\n  \n  DCN的计算: \n    - :math:`x_{l+1} = f_{\\theta}(x_0, x_l) + x_l`, 即它是一个残差网络, 并且每一层的计算都与 :math:`x_0` 有关\n  \n  FM的计算: \n    - 相对于LR, 增加了二阶交叉项, 并且用embedding的形式压缩表达, 计算特征交叉的方式是点积\n    \n  CIN的计算: \n    - 与DCN一样, 并且每一层的计算都与 :math:`x_0` 有关, 但是并不使用残差, :math:`f_{\\theta}(x,y)` 不是线性的, 而是与FM类似, 用embedding计算得到, \n      但使用的不是点积(bit-wise), 而是对应元素相乘, 然后线性组合(vector-wise). :math:`f_{\\theta}(x,y)` 是类似于FM的方法显式交叉, 所以它是一种显式高阶特征交叉方法\n    - 计算上, CIN还有一个特点是它可以转化成CNN高效计算\n\n  .. math::\n\n    X_{h,*}^k = \\sum_{i=1}^{H_{k-1}} \\sum_{j=1}^m W_{ij}^{k,k} (x_{i,*}^{k-1} \\circ x_{j,*}^0)\n  \n  CIN的主要特点是:\n    - 相互作用在vector-wise level, 而不是在bit-wise level \n    - 高阶特征交叉是显性的, 而非隐性的\n    - 模型大小并不会随因交叉度增加而指数增加\n  \n  Args:\n    hidden_uints (:obj:`List[int]`): CIN隐含层uints个数\n    activation (:obj:`tf.activation`): 激活函数\n    initializer (:obj:`tf.initializer`): 初始化器\n    regularizer (:obj:`tf.regularizer`): 正则化器\n  \n  \"\"\"\n\n  def __init__(self,\n               hidden_uints,\n               activation=None,\n               initializer='glorot_uniform',\n               regularizer=None,\n               **kwargs):\n    super(CIN, self).__init__(**kwargs)\n    self.hidden_uints = hidden_uints\n    self.activation = activations.get(activation)\n    self.initializer = initializers.get(initializer)\n    self.regularizer = regularizers.get(regularizer)\n\n    self._layer_num = len(self.hidden_uints)\n    self._batch_size = None\n    self._num_feat = None\n    self._emb_size = None\n\n  def build(self, input_shape):\n    assert len(input_shape) == 3\n    (batch_size, num_feat, emb_size) = input_shape\n    self._batch_size = check_dim(batch_size)\n    self._num_feat = check_dim(num_feat)\n    self._emb_size = check_dim(emb_size)\n\n    self._conv1d = []\n    for i, uints in enumerate(self.hidden_uints):\n      if i == 0:\n        last_hidden_dim = num_feat\n      else:\n        last_hidden_dim = self.hidden_uints[i - 1]\n\n      if i != self._layer_num - 1:\n        self._conv1d.append(\n            Conv1D(filters=uints,\n                   kernel_size=1,\n                   strides=1,\n                   activation=self.activation,\n                   kernel_initializer=self.initializer,\n                   kernel_regularizer=self.regularizer,\n                   input_shape=(emb_size, last_hidden_dim * num_feat)))\n      else:\n        self._conv1d.append(\n            Conv1D(filters=uints,\n                   kernel_size=1,\n                   strides=1,\n                   kernel_initializer=self.initializer,\n                   kernel_regularizer=self.regularizer,\n                   input_shape=(emb_size, last_hidden_dim * num_feat)))\n\n      self._trainable_weights.extend(self._conv1d[-1].trainable_weights)\n      self._non_trainable_weights.extend(self._conv1d[-1].non_trainable_weights)\n    return super(CIN, self).build(input_shape)\n\n  def call(self, inputs, **kwargs):\n    x0 = tf.transpose(inputs, perm=[0, 2,\n                                    1])  # (batch_size, emb_size, num_feat)\n    xl = x0\n\n    final_result = []\n    for i in range(self._layer_num):\n      # (batch_size, emb_size, -1)\n      xl_last_dim = dim_size(xl, -1)\n      zl = tf.reshape(tf.einsum('bdh,bdm->bdhm', xl, x0),\n                      shape=(self._batch_size, self._emb_size,\n                             xl_last_dim * self._num_feat))\n      xl = self._conv1d[i](zl)  # (batch_size, emb_size, num_hidden)\n\n      final_result.append(xl)\n\n    return tf.concat([tf.reduce_sum(hi, axis=1) for hi in final_result], axis=1)\n\n  def get_config(self):\n    config = {\n        'hidden_uints': self.hidden_uints,\n        'activation': activations.serialize(self.activation),\n        \"initializer\": initializers.serialize(self.initializer),\n        \"regularizer\": regularizers.serialize(self.regularizer)\n    }\n\n    base_config = super(CIN, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n"
  },
  {
    "path": "monolith/native_training/layers/feature_cross_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\n\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.feature_cross import GroupInt, AllInt, CDot, CAN, CIN, DCN\n\n\nclass FeatureCrossTest(tf.test.TestCase):\n\n  def test_groupint_instantiate(self):\n    layer_template = GroupInt.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.interaction_type = 'dot'\n    test_params0.use_attention = False\n    test_params0.attention_units = [128, 256, 1]\n    test_params0.activation = 'relu'\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = GroupInt(interaction_type='multiply',\n                    use_attention=True,\n                    attention_units=[128, 256, 1],\n                    activation='relu')\n    print(ins2)\n\n  def test_groupint_serde(self):\n    ins1 = GroupInt(interaction_type='multiply',\n                    use_attention=True,\n                    attention_units=[128, 256, 1],\n                    activation='relu')\n\n    cfg = ins1.get_config()\n    ins2 = GroupInt.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_groupint_call(self):\n    layer_template = GroupInt.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.out_type = 'concat'\n    layer = test_params0.instantiate()\n\n    left = [tf.keras.backend.variable(np.ones((100, 10))) for _ in range(5)]\n    right = [tf.keras.backend.variable(np.ones((100, 10))) for _ in range(3)]\n    sum_out = tf.reduce_sum(layer((left, right)))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_groupint_attention_call(self):\n    layer = GroupInt(interaction_type='multiply',\n                     use_attention=True,\n                     attention_units=[15, 10, 1],\n                     activation='relu')\n\n    left = [tf.keras.backend.variable(np.ones((100, 10))) for _ in range(5)]\n    right = [tf.keras.backend.variable(np.ones((100, 10))) for _ in range(3)]\n    sum_out = tf.reduce_sum(layer((left, right)))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_allint_instantiate(self):\n    layer_template = AllInt.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.cmp_dim = 4\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = AllInt(cmp_dim=4)\n    print(ins2)\n\n  def test_allint_serde(self):\n    layer_template = AllInt.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.cmp_dim = 4\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    cfg = ins1.get_config()\n    ins2 = AllInt.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_allint_call(self):\n    layer_template = AllInt.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.cmp_dim = 4\n    layer = test_params0.instantiate()\n\n    data = tf.keras.backend.variable(np.ones((100, 10, 10)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_cdot_instantiate(self):\n    layer_template = CDot.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.project_dim = 8\n    test_params0.compress_units = [128, 256]\n    test_params0.activation = 'tanh'\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = CDot(project_dim=8, compress_units=[128, 256], activation='tanh')\n    print(ins2)\n\n  def test_cdot_serde(self):\n    ins1 = CDot(project_dim=8, compress_units=[128, 256], activation='tanh')\n\n    cfg = ins1.get_config()\n    ins2 = CDot.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_cdot_call(self):\n    layer = CDot(project_dim=8, compress_units=[128, 256], activation='tanh')\n\n    data = tf.keras.backend.variable(np.ones((100, 10, 10)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_can_instantiate(self):\n    layer_template = CAN.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.layer_num = 8\n    test_params0.activation = 'sigmoid'\n    test_params0.is_seq = False\n    test_params0.is_stacked = True\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = CAN(layer_num=8, activation='tanh', is_seq=False, is_stacked=True)\n    print(ins2)\n\n  def test_can_serde(self):\n    ins1 = CAN(layer_num=8, activation='tanh', is_seq=False, is_stacked=True)\n\n    cfg = ins1.get_config()\n    ins2 = CAN.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_can_seq_call(self):\n    layer = CAN(layer_num=2, activation='relu', is_seq=True, is_stacked=True)\n\n    user = tf.keras.backend.variable(np.ones((128, 10, 12, 10)))\n    item = tf.keras.backend.variable(np.ones((128, 220)))\n    sum_out = tf.reduce_sum(layer((user, item)))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_can_call(self):\n    layer = CAN(layer_num=2, activation='relu', is_seq=False, is_stacked=True)\n\n    user = tf.keras.backend.variable(np.ones((128, 10, 10)))\n    item = tf.keras.backend.variable(np.ones((128, 220)))\n    sum_out = tf.reduce_sum(layer((user, item)))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_dcn_instantiate(self):\n    layer_template = DCN.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.layer_num = 8\n    test_params0.dcn_type = 'matrix'\n    test_params0.use_dropout = True\n    test_params0.keep_prob = 0.5\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = DCN(layer_num=8, dcn_type='matrix', use_dropout=True, keep_prob=0.5)\n    print(ins2)\n\n  def test_dcn_serde(self):\n    ins1 = DCN(layer_num=8, dcn_type='matrix', use_dropout=True, keep_prob=0.5)\n\n    cfg = ins1.get_config()\n    ins2 = DCN.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_dcn_vector_call(self):\n    layer = DCN(layer_num=2,\n                dcn_type='vector',\n                allow_kernel_norm=True,\n                use_dropout=True,\n                keep_prob=0.5)\n\n    data = tf.keras.backend.variable(np.ones((128, 10, 10)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_dcn_matrix_call(self):\n    layer = DCN(layer_num=2,\n                dcn_type='matrix',\n                allow_kernel_norm=True,\n                use_dropout=True,\n                keep_prob=0.5)\n\n    data = tf.keras.backend.variable(np.ones((128, 10, 10)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_dcn_mixed_call(self):\n    layer = DCN(layer_num=2,\n                dcn_type='mixed',\n                num_experts=2,\n                low_rank=5,\n                allow_kernel_norm=True,\n                use_dropout=True,\n                keep_prob=0.5)\n\n    data = tf.keras.backend.variable(np.ones((128, 10, 10)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_cin_instantiate(self):\n    layer_template = CIN.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.hidden_uints = [10, 5]\n    test_params0.activation = 'sigmoid'\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = CIN(hidden_uints=[10, 5], activation='tanh')\n    print(ins2)\n\n  def test_cin_serde(self):\n    ins1 = CIN(hidden_uints=[10, 5], activation='tanh')\n\n    cfg = ins1.get_config()\n    ins2 = CIN.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_cin_call(self):\n    layer = CIN(hidden_uints=[10, 5], activation='relu')\n\n    data = tf.keras.backend.variable(np.ones((128, 10, 10)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.compat.v1.disable_v2_behavior()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/feature_seq.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom monolith.native_training.layers.advanced_activations import serialize\nimport numpy as np\n\nimport tensorflow as tf\nfrom tensorflow.keras.layers import Dense, Layer, InputSpec\nfrom tensorflow.python.keras import activations\nimport tensorflow.keras.initializers as initializers\nfrom tensorflow.python.keras import regularizers\nfrom monolith.core.base_layer import add_layer_loss\nfrom monolith.native_training.layers.mlp import MLP\nfrom monolith.native_training.layers.agru import AGRUCell, dynamic_rnn_with_attention\nfrom monolith.native_training.utils import with_params\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.layers.utils import check_dim, dim_size\n\n\n@monolith_export\n@with_params\nclass DIN(Layer):\n  \"\"\"Deep Interest Network, 是阿里的原创, 基于兴趣序列特征聚合, 论文可参考 https://arxiv.org/pdf/1706.06978.pdf\n\n  为了更好地描述用户, 仅用静态特征是不够的, 需要加入行为特征. 行为特征往往是一个序列, 如点击过的app, 购买过的商品等等. \n  一方面, 用户的行为是由内在兴趣(Interest)与外部条件(Target)一起促成的. 用户行为是用户兴趣的体现, 简单起见, 用户行为表示兴趣\n  \n  DIN的三个假设:\n    - Behavior/Interest: 将用户行为序列表示为embedding序列, 这个序列同时也表示用户兴趣\n    - Target Representation: 将用户物品(Target)表示为embedding, 它与行为/兴趣处于同一空间, 因为它能满足用户的兴趣, 促进行为的发生\n    - Interest Match: 用户对物品发生行为, 是因为物品满足了用户的`某些`兴趣, 用Attention来表达\n  \n  为了简单, 以单个特征为例:\n    - queries: 表示召回的物品(Target), emb_size为k, shape为(k, )\n    - keys   : 表示用户序列特征(Interest), emb_size为k, 序列长长度为t, shape为(t, k)\n  \n  先将queries tile成shape为(t, k), 即将数据copy t次, 使queries, key同shape. 然后作如下操作\n    din_all = concat([queries, keys, queries - keys, queries * keys])\n  \n  也就是将queries, keys及其差值, 乘值等concat起来, 然后输入MLP, 得到attention weight(即物品对兴趣的满足程度)\n    attention_weight = mlp(din_all)\n  \n  最后, 线性组合, 实现attention (兴趣汇总), 如下:\n    output = matmul(attention_weight * keys)\n  \n  结果的shape为(k, ), 与原始queries同shape. \n\n  Args:\n    hidden_units (:obj:`list`): DIN中MLP layers 的hidden_units, 最后一维为1\n    activation (:obj:`tf.activation`): 激活函数\n    initializer (:obj:`tf.initializer`): kernel/bias初始化器\n    regularizer (:obj:`tf.regularizer`): kernel正则化\n    mode (:obj:`str`): 输出模式, 如果为 `sum`, 则会进行线性组合, 反回的shape与queries一样, 否则只相乘不组合, 返架的shape与keys一样\n    decay (:obj:`bool`): 是否在attention weight上做decay, 默认为False\n    \n  \"\"\"\n\n  def __init__(self,\n               hidden_units,\n               activation=None,\n               initializer=None,\n               regularizer=None,\n               mode: str = 'sum',\n               decay: bool = False,\n               **kwargs):\n    super(DIN, self).__init__(**kwargs)\n    assert hidden_units[-1] == 1\n    self.input_spec = [InputSpec(ndim=2), InputSpec(ndim=3)]\n    self.hidden_units = hidden_units\n    self.activation = activations.get(activation)\n    self.initializer = initializers.get(\n        initializer) or initializers.GlorotNormal()\n    self.regularizer = regularizers.get(regularizer)\n    self.dense_tower = None\n    self.mode = mode\n    self.decay = decay\n\n  def build(self, input_shape):\n    self.dense_tower = MLP(name='compress_tower',\n                           activations=self.activation,\n                           output_dims=self.hidden_units,\n                           initializers=self.initializer,\n                           kernel_regularizer=self.regularizer)\n    self._trainable_weights.extend(self.dense_tower.trainable_weights)\n    self._non_trainable_weights.extend(self.dense_tower.non_trainable_weights)\n    self.add_loss(self.dense_tower.losses)\n    super(DIN, self).build(input_shape)\n\n  def call(self, inputs, **kwargs):\n    queries, keys = inputs\n    mask = kwargs.get('mask', None)\n\n    T, H = dim_size(keys, 1), dim_size(keys, 2)\n    if self.hidden_units is None:\n      self.hidden_units = [T, 1]\n\n    # tf.tile(input, multiples, name=None), creates a new tensor by replicating `input` `multiples` times\n    # The output tensor's i'th dimension has input.dims(i) * multiples[i] elements,\n    # and the values of input are replicated multiples[i] times along the 'i'th dimension\n    queries = tf.reshape(tf.tile(queries, [1, T]),\n                         [-1, T, H])  # [B, H] -> [B, T * H] --> [B, T, H]\n\n    # DIN\n    din_all = tf.concat([queries, keys, queries - keys, queries * keys],\n                        axis=-1)  # [B, T, 4 * H]\n    # dense_tower on the last dim, [B, T, 4 * H] -> [B, T, 1]\n    attention_weight = self.dense_tower(din_all)\n    if self.decay:\n      attention_weight /= (H**0.5)\n\n    # Mask\n    if mask is not None:\n      mask = tf.greater_equal(mask, tf.ones_like(mask))\n      key_masks = tf.expand_dims(mask, 2)  # [B, T, 1]\n      attention_weight = tf.where(key_masks, attention_weight,\n                                  tf.zeros_like(attention_weight))  # [B, 1, T]\n      tf.compat.v1.summary.histogram(\n          '{name}_attention_outputs'.format(name=self.name), attention_weight)\n\n    if self.mode == 'sum':\n      # Weighted sum\n      # [B, T, 1]^T * [B, T, H] -> [B, 1, H]\n      attention_out = tf.matmul(attention_weight, keys, transpose_a=True)\n      outputs = tf.squeeze(attention_out, [1])  # [B, 1, H] -> [B, H]\n    else:\n      # [B, T, H] * [B, T, 1] -> [B, T, H]\n      outputs = keys * attention_weight\n    return outputs\n\n  def get_config(self):\n    config = {\n        'hidden_units': self.hidden_units,\n        'activation': activations.serialize(self.activation),\n        'initializer': initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n    }\n    base_config = super(DIN, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@monolith_export\n@with_params\nclass DIEN(Layer):\n  \"\"\"DIN的升级版, Deep Interest Evolution Network, 阿里原创, 基于兴趣演进的序列特征聚合, 论文可参考 https://arxiv.org/pdf/1809.03672.pdf\n  \n  在推荐场景，用户无需输入搜索关键词来表达意图，这种情况下捕捉用户兴趣并考虑兴趣的动态变化将是提升模型效果的关键.\n  大多该类模型将用户的行为直接看做兴趣，而用户的潜在兴趣往往很难通过行为来完全表示, 需要挖掘行为背后的用户真实兴趣，并考虑用户兴趣的动态变化\n  \n  DIEN的假设:\n    - Behavior Layer: 也就是将用户行为序列表示为embedding序列, embedding表达的意义是行为本身, 不再直接代表兴趣, 这与DIN不同\n    - Interest Extractor Layer: 用GRU从用户行为中提取兴趣(Interest), 兴趣是随时间演变的, DIN没有考虑这一点\n    - Interest Evolving Layer: 随着外部环境(Target attention)和内部认知(Interest)的变化，用户兴趣也不断变化, 最终兴起促使行为发生\n        - 物品表示与DIN一样, 它与兴趣处于同一空间, 因为它能满足用户的兴趣, 促进行为的发生\n        - 物品与兴趣的关系建模与DIN不一样, DIN是静态地看物品能否满足用户兴趣; DIEN中, 用户兴趣是演进的(Evolving), 物品会诱导/挖掘用户兴趣\n          在网络结构上表示为AGRU, 即attention + GRU\n\n  Args:\n    num_units (:obj:`int`): GRU隐含层的大小\n    att_type (:obj:`str`): attention的类型, 目前支持AGRU/AUGRU两种\n    activation (:obj:`tf.activation`): 激活函数\n    initializer (:obj:`tf.initializer`): kernel/bias初始化器\n    regularizer (:obj:`tf.regularizer`): kernel正则化\n\n  \"\"\"\n\n  def __init__(self,\n               num_units,\n               att_type='AGRU',\n               activation=tf.keras.activations.relu,\n               initializer=tf.initializers.HeUniform,\n               regularizer=None,\n               **kwargs):\n    super(DIEN, self).__init__(**kwargs)\n    self.num_units, self.att_type = num_units, att_type\n    self.activation = tf.keras.activations.get(activation)\n    self.initializer = initializers.get(\n        initializer) or initializers.GlorotNormal()\n    self.regularizer = regularizers.get(regularizer)\n\n  def build(self, input_shape):\n    self.gru_cell = tf.keras.layers.GRUCell(\n        name='gru_cell',\n        units=self.num_units,\n        activation=self.activation,\n        kernel_initializer=self.initializer,\n        bias_initializer=tf.initializers.Zeros(),\n        kernel_regularizer=self.regularizer)\n    self._trainable_weights.extend(self.gru_cell.trainable_weights)\n    self._non_trainable_weights.extend(self.gru_cell.non_trainable_weights)\n    self.add_loss(self.gru_cell.losses)\n\n    self.augru_cell = AGRUCell(name='augru_cell',\n                               units=self.num_units,\n                               activation=self.activation,\n                               att_type='AGRU',\n                               initializer=self.initializer,\n                               regularizer=self.regularizer)\n    self._trainable_weights.extend(self.augru_cell.trainable_weights)\n    self._non_trainable_weights.extend(self.augru_cell.non_trainable_weights)\n    self.add_loss(self.augru_cell.losses)\n\n    self.weight = self.add_weight(name='attention_weight',\n                                  dtype=tf.float32,\n                                  shape=(self.num_units, self.num_units),\n                                  initializer=self.initializer,\n                                  regularizer=self.regularizer)\n    super(DIEN, self).build(input_shape)\n\n  def _attention(self, queries, keys):\n    emb_size = dim_size(keys, 2)\n    query_weight = tf.reshape(tf.matmul(queries, self.weight, transpose_b=True),\n                              [-1, emb_size, 1])\n    logit = tf.squeeze(tf.matmul(keys, query_weight), [2])\n\n    return tf.nn.softmax(logit)\n\n  def call(self, inputs, **kwargs):\n    if isinstance(inputs, (list, tuple)):\n      if len(inputs) == 3:\n        queries, keys, mask = inputs[:]\n      elif len(inputs) == 2:\n        queries, keys = inputs[:]\n      else:\n        queries = inputs[0]\n        keys = kwargs['keys']\n    else:\n      queries = inputs\n      keys = kwargs['keys']\n\n    # interest extractor layer to capture temporal interests\n    outputs, _ = tf.compat.v1.nn.dynamic_rnn(self.gru_cell,\n                                             keys,\n                                             dtype=tf.float32)  # [B, T, H]\n\n    # interest evolving layer to capture interest evolving process that is relative to the target item\n    attn_scores = self._attention(queries, outputs)  # [B, T]\n    _, final_state = dynamic_rnn_with_attention(self.augru_cell, outputs,\n                                                attn_scores)  # [B, T, H]\n\n    return final_state\n\n  def get_config(self):\n    config = {\n        'num_units': self.num_units,\n        'att_type': self.att_type,\n        'activation': activations.serialize(self.activation),\n        'initializer': initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n    }\n    base_config = super(DIEN, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@monolith_export\n@with_params\nclass DMR_U2I(Layer):\n  \"\"\"Deep Match to Rank, DMR, 深度配匹排序, 与RNN不同, 主要考虑序列顺序 \n  \n  与DIN一样, 还DMR还是用attention的方式来聚合序列特征. 不同的是MR考虑了序列顺序, 即增加了位置embedding来处理用户序列的选后顺序\n  由于原始论文中最后的输出是点积, 梯度回传时只有一个值, 会导致训练不充分, 所以引入辅助loss, 但是辅助loss要用到负采样, 系统实现上比较\n  麻烦, 这里用element wise乘积代替点积, 去除辅助loss. 论文可参考 https://ojs.aaai.org/index.php/AAAI/article/view/5346/5202\n  \n  Args:\n    cmp_dim (:obj:`int`): 压缩维度\n    activation (:obj:`tf.activation`): 激活函数\n    initializer (:obj:`tf.initializer`): kernel/bias初始化器\n    regularizer (:obj:`tf.regularizer`): kernel正则化\n  \n  \"\"\"\n\n  def __init__(self,\n               cmp_dim: int,\n               activation=\"PReLU\",\n               initializer=\"glorot_uniform\",\n               regularizer=None,\n               **kwargs):\n    super(DMR_U2I, self).__init__(**kwargs)\n    self.cmp_dim = cmp_dim\n    self.activation = activations.get(activation)\n    self.initializer = initializers.get(\n        initializer) or initializers.GlorotNormal()\n    self.regularizer = regularizers.get(regularizer)\n\n  def build(self, input_shape):\n    item_sh, user_seq_sh = input_shape\n    (bs1, seq_length, ue_size) = tuple(map(check_dim, user_seq_sh))\n    (bs2, ie_size) = tuple(map(check_dim, item_sh))\n    assert bs1 == bs2\n\n    # position embedding\n    self.pos_emb = self.add_weight(name=\"pos_emb\",\n                                   shape=(seq_length, self.cmp_dim),\n                                   initializer=self.initializer,\n                                   regularizer=self.regularizer)\n\n    self.emb_weight = self.add_weight(name=\"emb_weight\",\n                                      shape=(ue_size, self.cmp_dim),\n                                      initializer=self.initializer,\n                                      regularizer=self.regularizer)\n\n    self.z_weight = self.add_weight(name=\"z_weight\",\n                                    shape=(self.cmp_dim, 1),\n                                    initializer=initializers.Ones())\n\n    self.bias = self.add_weight(name=\"bias\",\n                                shape=(self.cmp_dim,),\n                                initializer=initializers.Zeros())\n\n    self.linear = Dense(name=\"dense\",\n                        units=ie_size,\n                        activation=self.activation,\n                        kernel_initializer=self.initializer,\n                        kernel_regularizer=self.regularizer,\n                        use_bias=True)\n    self._trainable_weights.extend(self.linear.trainable_weights)\n    self._non_trainable_weights.extend(self.linear.non_trainable_weights)\n\n  def call(self, inputs, **kwargs):\n    items, user_seq = inputs\n\n    # 1) calculate compressed represention\n    emb_cmp = tf.matmul(user_seq, self.emb_weight)  # (bs, seq_length, cmp_dim)\n    comped = self.pos_emb + emb_cmp + self.bias  # (bs, seq_length, cmp_dim)\n\n    # 2) prepare attention weight\n    # (bs, seq_length, cmp_dim) * (cmp_dim, 1)  -> (bs, seq_length, 1)\n    alpha = tf.matmul(comped, self.z_weight)  # (bs, seq_length, 1)\n    alpha = tf.nn.softmax(alpha, axis=1)  # (bs, seq_length, 1)\n\n    # 3) execute attention\n    user_seq_trans = tf.transpose(user_seq,\n                                  perm=(0, 2, 1))  # (bs, ue_size, seq_length)\n    # (bs, ue_size, seq_length) * (bs, seq_length, 1) -> (bs, ue_size, 1) -> (bs, ue_size)\n    user_seq_merged = tf.squeeze(tf.matmul(user_seq_trans, alpha),\n                                 axis=-1)  # (bs, ue_size)\n\n    # 4) linear transform\n    user_seq_merged = self.linear(user_seq_merged)  # (bs, ie_size)\n\n    return user_seq_merged * items\n\n  def get_config(self):\n    config = {\n        'cmp_dim': self.cmp_dim,\n        'activation': activations.serialize(self.activation),\n        'initializer': initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n    }\n    base_config = super(DMR_U2I, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n"
  },
  {
    "path": "monolith/native_training/layers/feature_seq_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.feature_seq import DIN, DIEN, DMR_U2I\n\n\nclass FeatureSeqTest(tf.test.TestCase):\n\n  def test_din_instantiate(self):\n    layer_template = DIN.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.hidden_units = [10, 1]\n    test_params0.initializer = tf.initializers.GlorotNormal()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = DIN(hidden_units=[10, 1], initializer=tf.initializers.HeUniform())\n    print(ins2)\n\n  def test_din_serde(self):\n    ins1 = DIN(hidden_units=[10, 1], initializer=tf.initializers.HeUniform())\n\n    cfg = ins1.get_config()\n    ins2 = DIN.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_din_call(self):\n    layer = DIN(hidden_units=[10, 1], initializer=tf.initializers.HeUniform())\n\n    query = tf.keras.backend.variable(np.ones((100, 10)))\n    keys = tf.keras.backend.variable(np.ones((100, 15, 10)))\n    out = layer((query, keys))\n    sum_out = tf.reduce_sum(out)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_dien_instantiate(self):\n    layer_template = DIEN.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.num_units = 10\n    test_params0.initializer = tf.initializers.GlorotNormal()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = DIEN(num_units=10, initializer=tf.initializers.HeUniform())\n    print(ins2)\n\n  def test_dien_serde(self):\n    ins1 = DIEN(num_units=10, initializer=tf.initializers.HeUniform())\n\n    cfg = ins1.get_config()\n    ins2 = DIEN.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_dien_call(self):\n    layer = DIEN(num_units=10, initializer=tf.initializers.HeUniform())\n\n    query = tf.keras.backend.variable(np.ones((100, 10)))\n    keys = tf.keras.backend.variable(np.ones((100, 15, 10)))\n    out = layer((query, keys))\n    sum_out = tf.reduce_sum(out)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_dmr_instantiate(self):\n    layer_template = DMR_U2I.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.cmp_dim = 10\n    test_params0.activation = 'relu'\n    test_params0.initializer = tf.initializers.GlorotNormal()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = DMR_U2I(cmp_dim=10,\n                   activation='relu',\n                   initializer=tf.initializers.HeUniform())\n    print(ins2)\n\n  def test_dmr_serde(self):\n    ins1 = DMR_U2I(cmp_dim=10,\n                   activation='relu',\n                   initializer=tf.initializers.HeUniform())\n\n    cfg = ins1.get_config()\n    ins2 = DMR_U2I.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_dmr_call(self):\n    layer = DMR_U2I(cmp_dim=5,\n                    activation='relu',\n                    initializer=tf.initializers.HeUniform())\n\n    query = tf.keras.backend.variable(np.ones((100, 10)))\n    keys = tf.keras.backend.variable(np.ones((100, 15, 10)))\n    out = layer((query, keys))\n    sum_out = tf.reduce_sum(out)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/feature_trans.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\n\nimport tensorflow as tf\nfrom tensorflow.keras.layers import Layer, InputSpec\nimport tensorflow.keras.initializers as initializers\nfrom tensorflow.python.keras import regularizers\nfrom monolith.core.base_layer import add_layer_loss\nfrom monolith.native_training.layers.mlp import MLP\nfrom monolith.native_training.layers.utils import merge_tensor_list\nfrom monolith.native_training.utils import with_params\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.layers.utils import check_dim, dim_size\n\n\n@monolith_export\n@with_params\nclass AutoInt(Layer):\n  r\"\"\"Auto-Interaction的缩写, 基于Self-attention的特征变换. 论文可参考 https://arxiv.org/pdf/1810.11921.pdf\n\n  一个样本有n个特征, 每个特征用一个k维的embedding表示, 则样本可以表示为(n, k)的矩阵. 所谓attention, 本质上是一种线性组合, 关键是确定组合系数\n  \n  AutoInt中确定组合系数的方式为: \n  \n  .. math::\n  \n    coeff_{n, n} = softmax( X_{n, k} * X_{n, k}^T )\n  \n  即先计算自相关, 确定特征与其它特征的`相似性`, 然后用softmax的方式归一化, 得到组合系数. 最后是组性组合, 计算attention: \n  \n  .. math::\n  \n    O_{n, k} = coeff_{n, n} * X_{n, k}\n  \n  在AutoInt中, 上述过程可以迭代进行多次, 一次为一个layer\n\n  Args:\n    layer_num (:obj:`int`): auto int layer的层数, 一层为一个完整的auto int\n    out_type (:obj:`str`): 输出类型, 可以为stack, concat, None\n    keep_list (:obj:`bool`): 输出是否保持list\n    \n  \"\"\"\n\n  def __init__(self,\n               layer_num=1,\n               out_type='concat',\n               keep_list: bool = False,\n               **kwargs):\n    super(AutoInt, self).__init__(**kwargs)\n    self.layer_num = layer_num\n    self.out_type = out_type\n    self.keep_list = keep_list\n\n  def call(self, embeds, **kwargs):\n    assert len(embeds.shape) == 3\n\n    autoint_input = embeds\n    for i in range(self.layer_num):\n      layer_name = '{name}_{idx}'.format(name=self.name, idx=i)\n      with tf.name_scope(layer_name):\n        # [batch_size, num_feat, emb_dim] -> [batch_size, num_feat, num_feat]\n        attn = tf.nn.softmax(tf.matmul(autoint_input,\n                                       autoint_input,\n                                       transpose_b=True),\n                             axis=-1)\n        autoint_input = tf.matmul(attn,\n                                  autoint_input)  # [batch, num_feats, emb_dim]\n\n    return merge_tensor_list(autoint_input,\n                             merge_type=self.out_type,\n                             keep_list=self.keep_list)\n\n  def get_config(self):\n    config = {\n        'layer_num': self.layer_num,\n        'out_type': self.out_type,\n        'keep_list': self.keep_list\n    }\n    base_config = super(AutoInt, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@with_params\nclass iRazor(Layer):\n  \"\"\"特征选择和Embedding维度搜索\n\n  一个样本有n个特征, 每个特征用一个k维的embedding表示. 可以为每一个Embedding分配一个先择概率(一个0~1之间的数), 训练出完成后, 如果概率较大, 则保留,\n  否则去除, 从而实现Embedding维度搜索. 也可以给特征分配一个移除概率, 这个概率与Embedding分配的概率可以归一化, 如果概率越大, 则将特征移除. 当训练完成\n  后, 可以用后剪枝算法CPT(cumulative probability threshold, 累积概率阈值)来对网络裁剪, 从而达到特征选择和Embedding维度搜索的目的\n  \n  .. note::\n  \n    从另一个角度看, 不同的特征, 重要程度不一样, 同一个特征Embedding, 不同的维度重要程度也不一样. 常用内积, 在Euclid空间中计算内积就是点乘, \n    因此每个维度的重要性一样. 所以可以引入一个`度量空间`, 在这个空间算内积. 为了简单, 度量矩阵用对角阵(半正定), 此时, 直观理解就是每个embedding维度权重不\n    一样, 而且权重匀为正数, 是可以学习的. 前面的分析是假设`度量空间`存在, 可以为`度量空间`情况也分配权重, 而且这个权重与Embedding权重是归一化的, \n    从而实现不同特征重要性不一样. 此时, iRazor的目的是做特征变换\n  \n  给定一个 nas_space, 假设emb_size=8, 则nas_space=[0, 1, 3, 5, 8], 是对embedding的一个划分:{}, {0}, {1, 2}, {3, 4}, {5, 6, 7}, 共5段, 每段出现的概率为p_i\n  \n  .. code-block:: text\n  \n    rigid_masks = [\n      [0, 0, 0, 0, 0, 0, 0, 0],   -> p_0, 表示`度量空间`不存在的概率\n      [1, 0, 0, 0, 0, 0, 0, 0],   -> p_1, 表示0号位置的概率/重要性\n      [0, 1, 1, 0, 0, 0, 0, 0],   -> p_2, 表示1-2号位置的概率/重要性\n      [0, 0, 0, 1, 1, 0, 0, 0],   -> p_3, 表示3-4号位置的概率/重要性\n      [0, 0, 0, 0, 0, 1, 1, 1]    -> p_4, 表示5-8号位置的概率/重要性\n    ]\n    P = (p_0, p_1, p_2, p_3, p_4), 且有 p_0 + ... + p_4 = 1\n    soft_masks = P * rigid_masks = (p_1, p_2, p_2, p_3, p_3, p_4, p_4, p_4)\n  \n  从上面可以看出, nas_space是对测度空间的限制, 强制某几个维度(分组)有相同的权重. 如果 nas_space = [0,1,2,3,4,5,6,7,8], 可以去除这种强制. \n  rigid_masks中第一行全为0, 表示表示`度量空间`不存在. 可以加一个辅助loss, 强制`度量空间`不存在, 因为可以减少参数/省内存/评估特征重要性\n  \n  .. code-block:: text\n  \n    loss = feature_weight * sum(soft_masks)\n  \n  Args:\n    nas_space (:obj:`list`): 用于定义embedding特征分组, 第一个元素是0, 最后一个元素是emb_size, 元素是有序的. \n                            0表示`度量空间`不存在, nas_space[i-1]:nas_space[i] 表示一个分组, 位于同一组内的元素有相同的权重\n    t (:obj:`float`): softmax平滑因子\n    initializer (:obj:`tf.initializer`): kernel/bias初始化器\n    regularizer (:obj:`tf.regularizer`): kernel正则化\n    feature_weight (:obj:`tf.Tensor`): 特征权重, 用于计算辅助loss\n    out_type (:obj:`str`): 输出类型, 可以为stack, concat, None\n    keep_list (:obj:`bool`): 输出是否保持list\n\n  \"\"\"\n\n  def __init__(self,\n               nas_space,\n               t=0.05,\n               initializer=None,\n               regularizer=None,\n               feature_weight=None,\n               out_type='concat',\n               keep_list=False,\n               **kwargs):\n    super(iRazor, self).__init__(**kwargs)\n    self.out_type = out_type\n    self.keep_list = keep_list\n    self.nas_space = nas_space\n    self.t = t\n\n    self.nas_logits = None\n    self.emb_size = max(self.nas_space)\n    self.nas_len = len(self.nas_space)\n    self.initializer = initializers.get(initializer)\n    self.regularizer = regularizers.get(regularizer)\n\n    if feature_weight is not None:\n      if isinstance(feature_weight, (tf.Tensor, tf.Variable)):\n        self.feature_weight = tf.reshape(feature_weight, shape=(1, -1))\n      else:\n        self.feature_weight = tf.constant(feature_weight,\n                                          shape=(1, -1),\n                                          dtype=tf.float32)\n    else:\n      self.feature_weight = feature_weight\n\n  @property\n  def rigid_masks(self):\n    masks = np.zeros(shape=(self.nas_len, self.emb_size), dtype=np.float32)\n    for i, j in enumerate(self.nas_space):\n      if i > 0:\n        masks[i, self.nas_space[i - 1]:j] = 1.0\n    return tf.constant(masks, name=\"masks\", dtype=tf.float32)\n\n  def build(self, input_shape):\n    # input_shape: [bath_size, num_feat, emb_dim]\n    shape = (check_dim(input_shape[1]), self.nas_len)\n    self.nas_logits = self.add_weight(name=\"nas_weight\",\n                                      shape=shape,\n                                      dtype=tf.float32,\n                                      initializer=self.initializer,\n                                      regularizer=self.regularizer)\n    super(iRazor, self).build(input_shape)\n\n  def call(self, embeds, **kwargs):\n    assert check_dim(embeds.shape[-1]) == max(self.nas_space)\n\n    nas_weight = tf.nn.softmax(self.nas_logits / self.t,\n                               axis=1,\n                               name=\"nas_concat_choice_probs\")\n    tf.compat.v1.summary.histogram(name='nas_weight', values=nas_weight)\n\n    # create soft mask for each embedding dim with nas\n    soft_masks = tf.matmul(nas_weight, self.rigid_masks, name=\"choice_matrix\")\n\n    if self.feature_weight is not None:\n      nas_loss = tf.matmul(self.feature_weight,\n                           tf.reduce_sum(soft_masks, axis=1, keepdims=True))\n      add_layer_loss(self.name, tf.reduce_sum(nas_loss))\n\n    # re-weight embeds\n    out_embeds = embeds * soft_masks\n\n    return merge_tensor_list(out_embeds,\n                             merge_type=self.out_type,\n                             keep_list=self.keep_list)\n\n  def get_config(self):\n    config = {\n        'nas_space': self.nas_space,\n        't': self.t,\n        'initializer': initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n        'feature_weight': self.feature_weight,\n        'out_type': self.out_type,\n        'keep_list': self.keep_list,\n    }\n    base_config = super(iRazor, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@monolith_export\n@with_params\nclass SeNet(Layer):\n  \"\"\"SeNet最早用于图像中, 这里是借用其概念, 不同特征具有不同重要性. 论文可参考 https://arxiv.org/pdf/1709.01507.pdf\n\n  一个样本有n个特征, 每个特征用一个k维的embedding表示. 但是并不是每个特征都一样重要, 所以想给每个特征一个权重, 以调整其重要性.\n  权重计算是用一个MLP完成的, 一般有三层input - cmp_layer - output. 其中input/output是同shape的, input是通过 reduce_mean输入矩阵(n, k)的最后一维得到.\n  最后用 weight(n) * (n, k) 为特征加权\n\n  Args:\n    num_feature (:obj:`int`): 输入特征数\n    cmp_dim (:obj:`int`): 压缩维的维度\n    initializer (:obj:`tf.initializer`): kernel/bias初始化器\n    kernel_regularizer (:obj:`tf.regularizer`): kernel正则化\n    bias_regularizer (:obj:`tf.regularizer`): bias正则化\n    on_gpu: 计算是否发生在GPU上, 如果是, 则用GPU优化版本\n    out_type (:obj:`str`): 输出类型, 可以为stack, concat, None\n    keep_list (:obj:`bool`): 输出是否保持list\n    \n  \"\"\"\n\n  def __init__(self,\n               num_feature,\n               cmp_dim,\n               initializer=None,\n               regularizer=None,\n               on_gpu=False,\n               out_type='concat',\n               keep_list=False,\n               **kwargs):\n    super(SeNet, self).__init__(**kwargs)\n    self.num_feat = num_feature\n    self.cmp_dim = cmp_dim\n    self.initializer = initializers.get(initializer)\n    self.regularizer = regularizers.get(regularizer)\n    self.on_gpu = on_gpu\n    self.out_type = out_type\n    self.keep_list = keep_list\n\n  def build(self, input_shape):\n    if self.cmp_dim is None:\n      self.cmp_tower = lambda x: x\n    else:\n      self.cmp_tower = MLP(name='cmp_tower',\n                           output_dims=[self.cmp_dim, self.num_feat],\n                           activations=['relu', 'sigmoid'],\n                           initializers=self.initializer,\n                           kernel_regularizer=self.regularizer)\n      self._trainable_weights.extend(self.cmp_tower.trainable_weights)\n      self._non_trainable_weights.extend(self.cmp_tower.non_trainable_weights)\n      self.add_loss(self.cmp_tower.losses)\n    super(SeNet, self).build(input_shape)\n\n  def call(self, inputs, **kwargs):\n    senet_input_concat, emb_dim = None, None\n    if isinstance(inputs, (tf.Tensor, tf.Variable)):\n      # [batch_size, slots_num, emb_dim]\n      num_feat, emb_dim = dim_size(inputs, 1), dim_size(inputs, 2)\n      senet_input_concat = tf.reshape(inputs, [-1, num_feat, emb_dim])\n      sequeeze_embedding = tf.reduce_mean(senet_input_concat,\n                                          axis=2)  # [batch, slots_num]\n    else:  # isinstance(inputs, (list, tuple))\n      num_feat = len(inputs)\n      if self.on_gpu:\n        slots_lens = [dim_size(embed, 1) for embed in inputs]\n        ids = tf.constant(\n            np.concatenate([[i] * length for i, length in enumerate(slots_lens)\n                           ]))\n        lens = tf.constant([1.0 / slot_len for slot_len in slots_lens])\n\n        concat_trans = tf.transpose(tf.concat(inputs, axis=1))\n        sequeeze_embedding = tf.compat.v1.segment_sum(concat_trans, ids)\n        sequeeze_embedding = tf.transpose(sequeeze_embedding)\n        sequeeze_embedding = tf.reshape(sequeeze_embedding,\n                                        shape=(-1, num_feat))\n        sequeeze_embedding = tf.multiply(sequeeze_embedding, lens)\n      else:\n        sequeeze_embedding = tf.concat(\n            [tf.reduce_mean(embed, axis=1, keepdims=True) for embed in inputs],\n            axis=1,\n            name='concat')\n\n    weight_out = self.cmp_tower(sequeeze_embedding)\n    if isinstance(inputs, (tf.Tensor, tf.Variable)):\n      # [batch, num_feat] -> # [batch, num_feat, 1]\n      weight_out = tf.reshape(weight_out, [-1, num_feat, 1])\n      senet_weighted = tf.multiply(weight_out, senet_input_concat)\n    else:\n      weight_out = tf.split(weight_out, num_feat, axis=1)\n      senet_weighted = [\n          tf.multiply(embed, weight)\n          for embed, weight in zip(inputs, weight_out)\n      ]\n\n    return merge_tensor_list(senet_weighted,\n                             merge_type=self.out_type,\n                             keep_list=self.keep_list,\n                             num_feature=num_feat)\n\n  def get_config(self):\n    config = {\n        'num_feature': self.num_feat,\n        'cmp_dim': self.cmp_dim,\n        'initializer': initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n        'on_gpu': self.on_gpu,\n        'out_type': self.out_type,\n        'keep_list': self.keep_list\n    }\n    base_config = super(SeNet, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n"
  },
  {
    "path": "monolith/native_training/layers/feature_trans_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.feature_trans import AutoInt, iRazor, SeNet\n\n\nclass FeatureTransTest(tf.test.TestCase):\n\n  def test_autoint_instantiate(self):\n    layer_template = AutoInt.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.layer_num = 1\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = AutoInt(layer_num=1)\n    print(ins2)\n\n  def test_autoint_serde(self):\n    layer_template = AutoInt.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.layer_num = 1\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    cfg = ins1.get_config()\n    ins2 = AutoInt.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_autoint_call(self):\n    layer_template = AutoInt.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.layer_num = 2\n    layer = test_params0.instantiate()\n\n    data = tf.keras.backend.variable(np.ones((100, 10, 10)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_senet_instantiate(self):\n    layer_template = SeNet.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.num_feature = 10\n    test_params0.cmp_dim = 4\n    test_params0.initializer = tf.initializers.GlorotNormal()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = SeNet(num_feature=10,\n                 cmp_dim=4,\n                 initializer=tf.initializers.HeUniform())\n    print(ins2)\n\n  def test_senet_serde(self):\n    ins1 = SeNet(num_feature=10,\n                 cmp_dim=4,\n                 initializer=tf.initializers.HeUniform())\n\n    cfg = ins1.get_config()\n    ins2 = SeNet.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_senet_call(self):\n    layer_template = SeNet.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.num_feature = 10\n    test_params0.cmp_dim = 4\n    test_params0.initializer = tf.initializers.GlorotNormal()\n    layer = test_params0.instantiate()\n\n    data = tf.keras.backend.variable(np.ones((100, 10, 10)))\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_irazor_instantiate(self):\n    layer_template = iRazor.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.nas_space = [0, 2, 5, 7, 10]\n    test_params0.initializer = tf.initializers.GlorotNormal()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = iRazor(nas_space=[0, 2, 5, 7, 10],\n                  t=0.08,\n                  initializer=tf.initializers.HeUniform())\n    print(ins2)\n\n  def test_irazor_serde(self):\n    ins1 = iRazor(nas_space=[0, 2, 5, 7, 10],\n                  t=0.08,\n                  initializer=tf.initializers.HeUniform())\n\n    cfg = ins1.get_config()\n    ins2 = iRazor.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_irazor_call(self):\n    layer = iRazor(nas_space=[0, 2, 5, 7, 10],\n                   t=0.08,\n                   initializer=tf.initializers.HeUniform())\n\n    data = tf.keras.backend.variable(np.ones((100, 10, 10)))\n    out = layer(data)\n    sum_out = tf.reduce_sum(out)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/kernels/feature_insight_kernels.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <algorithm>\n#include <cmath>\n#include <cstdio>\n#include <ctime>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass FeatureInsightOp : public OpKernel {\n public:\n  explicit FeatureInsightOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    std::vector<int32> segment_sizes;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"segment_sizes\", &segment_sizes));\n\n    int32 idx = 0;\n    num_feature_ = segment_sizes.size();\n    for (int32 size : segment_sizes) {\n      for (int i = 0; i < size; ++i) {\n        segment_id_map_.push_back(idx);\n      }\n      idx++;\n    }\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    const Tensor *input_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"input\", &input_tensor));\n    auto input_mat = input_tensor->matrix<float>();\n    const Tensor *weight_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"weight\", &weight_tensor));\n    auto weight_mat = weight_tensor->matrix<float>();\n\n    int64 batch_size = input_tensor->dim_size(0);\n    int64 out_dim = weight_tensor->dim_size(1);\n\n    Tensor *out_tensor;\n    OP_REQUIRES_OK(\n        ctx, ctx->allocate_output(\n                 \"output\", {batch_size, num_feature_ * out_dim}, &out_tensor));\n    auto out_mat = out_tensor->matrix<float>();\n    out_mat.setZero();\n\n    Tensor tmp_tensor;\n    OP_REQUIRES_OK(ctx, ctx->allocate_temp(out_tensor->dtype(), {num_feature_},\n                                           &tmp_tensor));\n    auto tmp_mat = tmp_tensor.flat<float>();\n\n    for (size_t i = 0; i < batch_size; ++i) {  // batch_size\n      for (size_t k = 0; k < out_dim; ++k) {   // out_size\n        tmp_mat.setZero();\n        for (size_t j = 0; j < input_tensor->dim_size(1);\n             ++j) {  // total_embedding_size\n          int32 idx = segment_id_map_[j];\n          tmp_mat(idx) += input_mat(i, j) * weight_mat(j, k);\n        }\n\n        for (size_t idx = 0; idx < num_feature_; ++idx) {\n          out_mat(i, idx * out_dim + k) += tmp_mat(idx);\n        }\n      }\n    }\n  }\n\n private:\n  int64 num_feature_;\n  std::vector<int32> segment_id_map_;\n};\n\nclass FeatureInsightGradOp : public OpKernel {\n public:\n  explicit FeatureInsightGradOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"segment_sizes\", &segment_sizes_));\n\n    int K;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"K\", &K));\n\n    num_feature_ = segment_sizes_.size();\n    int grad_dim = num_feature_ * K;\n    grad_dim_to_k_.reserve(grad_dim);\n    grad_dim_to_feature_idx_.reserve(grad_dim);\n    feature_idx_to_embedding_start_.reserve(num_feature_);\n    for (int i = 0; i < num_feature_; ++i) {\n      for (int j = 0; j < K; ++j) {\n        grad_dim_to_feature_idx_.push_back(i);\n        grad_dim_to_k_.push_back(j);\n      }\n\n      if (i == 0) {\n        feature_idx_to_embedding_start_.push_back(0);\n      } else {\n        feature_idx_to_embedding_start_.push_back(\n            feature_idx_to_embedding_start_[i - 1] + segment_sizes_[i - 1]);\n      }\n    }\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    const Tensor *grad_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"grad\", &grad_tensor));\n    auto grad_mat = grad_tensor->matrix<float>();\n    const Tensor *input_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"input\", &input_tensor));\n    auto input_mat = input_tensor->matrix<float>();\n    const Tensor *weight_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"weight\", &weight_tensor));\n    auto weight_mat = weight_tensor->matrix<float>();\n\n    Tensor *input_grad_tensor;\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(\"input_grad\", input_tensor->shape(),\n                                        &input_grad_tensor));\n    auto input_grad_mat = input_grad_tensor->matrix<float>();\n    input_grad_mat.setZero();\n\n    Tensor *weight_grad_tensor;\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(\"weight_grad\", weight_tensor->shape(),\n                                        &weight_grad_tensor));\n    auto weight_grad_mat = weight_grad_tensor->matrix<float>();\n    weight_grad_mat.setZero();\n\n    int64 batch_size = input_tensor->dim_size(0);\n    int64 grad_dim = grad_tensor->dim_size(1);\n    LOG(INFO) << \"get data done! batch_size=\" << batch_size\n              << \", grad_dim=\" << grad_dim;\n    for (size_t i = 0; i < batch_size; ++i) {  // batch_size\n      for (size_t g = 0; g < grad_dim; ++g) {\n        int k = grad_dim_to_k_[g];\n        int feature_idx = grad_dim_to_feature_idx_[g];\n        int start = feature_idx_to_embedding_start_[feature_idx];\n        int end = start + segment_sizes_[feature_idx];\n        float grad_val = grad_mat(i, g);\n        for (int j = start; j < end; ++j) {\n          weight_grad_mat(j, k) += grad_val * input_mat(i, j);\n          input_grad_mat(i, j) += grad_val * weight_mat(j, k);\n        }\n      }\n    }\n  }\n\n private:\n  int64 num_feature_;\n  std::vector<int32> segment_sizes_;\n  std::vector<int32> grad_dim_to_k_;\n  std::vector<int32> grad_dim_to_feature_idx_;\n  std::vector<int32> feature_idx_to_embedding_start_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"FeatureInsight\").Device(DEVICE_CPU),\n                        FeatureInsightOp)\n\nREGISTER_KERNEL_BUILDER(Name(\"FeatureInsightGrad\").Device(DEVICE_CPU),\n                        FeatureInsightGradOp)\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/layers/kernels/ffm_kernels.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/layers/kernels/ffm_kernels.h\"\n#include <string>\n#include <vector>\n\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing CPUDevice = Eigen::ThreadPoolDevice;\n\ntemplate <>\nstruct FFMImpl<CPUDevice> {\n  static void Compute(OpKernelContext *ctx, const std::string &int_type,\n                      TTypes<float>::ConstMatrix left_matrix, int left_feat_num,\n                      TTypes<float>::ConstMatrix right_matrix,\n                      int right_feat_num, int batch_size, int dim_size,\n                      TTypes<float>::Matrix output) {\n    output.setZero();\n\n    for (int l = 0; l < left_feat_num; ++l) {\n      int l_idx = l * dim_size;\n      for (int r = 0; r < right_feat_num; ++r) {\n        int r_idx = r * dim_size;\n        if (int_type == \"dot\") {\n          int o_idx = l * right_feat_num + r;\n          for (int b = 0; b < batch_size; ++b) {\n            for (int k = 0; k < dim_size; ++k) {\n              output(b, o_idx) +=\n                  left_matrix(b, l_idx + k) * right_matrix(b, r_idx + k);\n            }\n          }\n        } else {\n          int o_idx = (l * right_feat_num + r) * dim_size;\n          for (int b = 0; b < batch_size; ++b) {\n            for (int k = 0; k < dim_size; ++k) {\n              output(b, o_idx + k) =\n                  left_matrix(b, l_idx + k) * right_matrix(b, r_idx + k);\n            }\n          }\n        }\n      }\n    }\n  }\n};\n\ntemplate <>\nstruct FFMGradImpl<CPUDevice> {\n  static void Compute(OpKernelContext *ctx, const std::string &int_type,\n                      TTypes<float>::ConstMatrix grad_matrix, int grad_feat_num,\n                      TTypes<float>::ConstMatrix left_matrix, int left_feat_num,\n                      TTypes<float>::ConstMatrix right_matrix,\n                      int right_feat_num, int batch_size, int dim_size,\n                      TTypes<float>::Matrix left_grad_matrix,\n                      TTypes<float>::Matrix right_grad_matrix) {\n    left_grad_matrix.setZero();\n    right_grad_matrix.setZero();\n\n    for (int g = 0; g < grad_feat_num; ++g) {\n      int l_idx = (g / right_feat_num) * dim_size;\n      int r_idx = (g % right_feat_num) * dim_size;\n\n      if (int_type == \"dot\") {\n        for (int b = 0; b < batch_size; ++b) {\n          for (int k = 0; k < dim_size; ++k) {\n            left_grad_matrix(b, l_idx + k) +=\n                grad_matrix(b, g) * right_matrix(b, r_idx + k);\n\n            right_grad_matrix(b, r_idx + k) +=\n                grad_matrix(b, g) * left_matrix(b, l_idx + k);\n          }\n        }\n      } else {\n        int g_idx = g * dim_size;\n        for (int b = 0; b < batch_size; ++b) {\n          for (int k = 0; k < dim_size; ++k) {\n            left_grad_matrix(b, l_idx + k) +=\n                grad_matrix(b, g_idx + k) * right_matrix(b, r_idx + k);\n\n            right_grad_matrix(b, r_idx + k) +=\n                grad_matrix(b, g_idx + k) * left_matrix(b, l_idx + k);\n          }\n        }\n      }\n    }\n  }\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"FFM\").Device(DEVICE_CPU), FFMOp<CPUDevice>)\n\nREGISTER_KERNEL_BUILDER(Name(\"FFMGrad\").Device(DEVICE_CPU),\n                        FFMGradOp<CPUDevice>)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/layers/kernels/ffm_kernels.cu.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#if GOOGLE_CUDA\n#define EIGEN_USE_GPU\n#include \"monolith/native_training/layers/kernels/ffm_kernels.h\"\n#include <string>\n#include <vector>\n\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/kernels/gpu_device_array.h\"\n#include \"tensorflow/core/kernels/gpu_device_array_gpu.h\"\n#include \"tensorflow/core/util/gpu_kernel_helper.h\"\n#include \"tensorflow/core/util/gpu_launch_config.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing GPUDevice = Eigen::GpuDevice;\n\n__global__ void FFMKernelMultiply(TTypes<float>::ConstMatrix left_matrix,\n                                  int left_feat_num,\n                                  TTypes<float>::ConstMatrix right_matrix,\n                                  int right_feat_num, int batch_size,\n                                  int dim_size, TTypes<float>::Matrix output) {\n  GPU_1D_KERNEL_LOOP(b, batch_size) {\n    for (int l = 0; l < left_feat_num; ++l) {\n      int l_idx = l * dim_size;\n      for (int r = 0; r < right_feat_num; ++r) {\n        int r_idx = r * dim_size;\n        int o_idx = (l * right_feat_num + r) * dim_size;\n        for (int k = 0; k < dim_size; ++k) {\n          output(b, o_idx + k) =\n              left_matrix(b, l_idx + k) * right_matrix(b, r_idx + k);\n        }\n      }\n    }\n  }\n}\n\n__global__ void FFMKernelDot(TTypes<float>::ConstMatrix left_matrix,\n                             int left_feat_num,\n                             TTypes<float>::ConstMatrix right_matrix,\n                             int right_feat_num, int batch_size, int dim_size,\n                             TTypes<float>::Matrix output) {\n  GPU_1D_KERNEL_LOOP(b, batch_size) {\n    for (int j = 0; j < output.dimension(1); ++j) {\n      output(b, j) = 0;\n    }\n  }\n  __syncthreads();\n\n  GPU_1D_KERNEL_LOOP(b, batch_size) {\n    for (int l = 0; l < left_feat_num; ++l) {\n      int l_idx = l * dim_size;\n      for (int r = 0; r < right_feat_num; ++r) {\n        int r_idx = r * dim_size;\n        int o_idx = l * right_feat_num + r;\n        for (int k = 0; k < dim_size; ++k) {\n          output(b, o_idx) +=\n              left_matrix(b, l_idx + k) * right_matrix(b, r_idx + k);\n        }\n      }\n    }\n  }\n}\n\ntemplate <>\nstruct FFMImpl<GPUDevice> {\n  static void Compute(OpKernelContext *ctx, const std::string &int_type,\n                      TTypes<float>::ConstMatrix left_matrix, int left_feat_num,\n                      TTypes<float>::ConstMatrix right_matrix,\n                      int right_feat_num, int batch_size, int dim_size,\n                      TTypes<float>::Matrix output) {\n    Eigen::GpuDevice gpu_device = ctx->eigen_device<Eigen::GpuDevice>();\n    auto config = GetGpuLaunchConfig(batch_size, gpu_device);\n\n    if (int_type == \"dot\") {\n      TF_CHECK_OK(GpuLaunchKernel(\n          FFMKernelDot, config.block_count, config.thread_per_block, 0,\n          gpu_device.stream(), left_matrix, left_feat_num, right_matrix,\n          right_feat_num, batch_size, dim_size, output));\n    } else {\n      TF_CHECK_OK(GpuLaunchKernel(\n          FFMKernelMultiply, config.block_count, config.thread_per_block, 0,\n          gpu_device.stream(), left_matrix, left_feat_num, right_matrix,\n          right_feat_num, batch_size, dim_size, output));\n    }\n  }\n};\n\n__global__ void FFMGradKernelMultiply(\n    TTypes<float>::ConstMatrix grad_matrix, int grad_feat_num,\n    TTypes<float>::ConstMatrix left_matrix, int left_feat_num,\n    TTypes<float>::ConstMatrix right_matrix, int right_feat_num, int batch_size,\n    int dim_size, TTypes<float>::Matrix left_grad_matrix,\n    TTypes<float>::Matrix right_grad_matrix) {\n  GPU_1D_KERNEL_LOOP(b, batch_size) {\n    for (int g = 0; g < left_feat_num * dim_size; ++g) {\n      left_grad_matrix(b, g) = 0;\n    }\n    for (int g = 0; g < right_feat_num * dim_size; ++g) {\n      right_grad_matrix(b, g) = 0;\n    }\n  }\n  __syncthreads();\n\n  GPU_1D_KERNEL_LOOP(b, batch_size) {\n    for (int g = 0; g < grad_feat_num; ++g) {\n      int l_idx = (g / right_feat_num) * dim_size;\n      int r_idx = (g % right_feat_num) * dim_size;\n\n      int g_idx = g * dim_size;\n      for (int k = 0; k < dim_size; ++k) {\n        left_grad_matrix(b, l_idx + k) +=\n            grad_matrix(b, g_idx + k) * right_matrix(b, r_idx + k);\n\n        right_grad_matrix(b, r_idx + k) +=\n            grad_matrix(b, g_idx + k) * left_matrix(b, l_idx + k);\n      }\n    }\n  }\n}\n\n__global__ void FFMGradKernelDot(\n    TTypes<float>::ConstMatrix grad_matrix, int grad_feat_num,\n    TTypes<float>::ConstMatrix left_matrix, int left_feat_num,\n    TTypes<float>::ConstMatrix right_matrix, int right_feat_num, int batch_size,\n    int dim_size, TTypes<float>::Matrix left_grad_matrix,\n    TTypes<float>::Matrix right_grad_matrix) {\n  GPU_1D_KERNEL_LOOP(b, batch_size) {\n    for (int g = 0; g < left_feat_num * dim_size; ++g) {\n      left_grad_matrix(b, g) = 0;\n    }\n    for (int g = 0; g < right_feat_num * dim_size; ++g) {\n      right_grad_matrix(b, g) = 0;\n    }\n  }\n  __syncthreads();\n\n  GPU_1D_KERNEL_LOOP(b, batch_size) {\n    for (int g = 0; g < grad_feat_num; ++g) {\n      int l_idx = (g / right_feat_num) * dim_size;\n      int r_idx = (g % right_feat_num) * dim_size;\n\n      for (int k = 0; k < dim_size; ++k) {\n        left_grad_matrix(b, l_idx + k) +=\n            grad_matrix(b, g) * right_matrix(b, r_idx + k);\n\n        right_grad_matrix(b, r_idx + k) +=\n            grad_matrix(b, g) * left_matrix(b, l_idx + k);\n      }\n    }\n  }\n}\n\ntemplate <>\nstruct FFMGradImpl<GPUDevice> {\n  static void Compute(OpKernelContext *ctx, const std::string &int_type,\n                      TTypes<float>::ConstMatrix grad_matrix, int grad_feat_num,\n                      TTypes<float>::ConstMatrix left_matrix, int left_feat_num,\n                      TTypes<float>::ConstMatrix right_matrix,\n                      int right_feat_num, int batch_size, int dim_size,\n                      TTypes<float>::Matrix left_grad_matrix,\n                      TTypes<float>::Matrix right_grad_matrix) {\n    Eigen::GpuDevice gpu_device = ctx->eigen_device<Eigen::GpuDevice>();\n    auto config = GetGpuLaunchConfig(batch_size, gpu_device);\n\n    if (int_type == \"dot\") {\n      TF_CHECK_OK(GpuLaunchKernel(\n          FFMGradKernelDot, config.block_count, config.thread_per_block, 0,\n          gpu_device.stream(), grad_matrix, grad_feat_num, left_matrix,\n          left_feat_num, right_matrix, right_feat_num, batch_size, dim_size,\n          left_grad_matrix, right_grad_matrix));\n    } else {\n      TF_CHECK_OK(GpuLaunchKernel(\n          FFMGradKernelMultiply, config.block_count, config.thread_per_block, 0,\n          gpu_device.stream(), grad_matrix, grad_feat_num, left_matrix,\n          left_feat_num, right_matrix, right_feat_num, batch_size, dim_size,\n          left_grad_matrix, right_grad_matrix));\n    }\n  }\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"FFM\").Device(DEVICE_GPU), FFMOp<GPUDevice>)\n\nREGISTER_KERNEL_BUILDER(Name(\"FFMGrad\").Device(DEVICE_GPU),\n                        FFMGradOp<GPUDevice>)\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // GOOGLE_CUDA\n"
  },
  {
    "path": "monolith/native_training/layers/kernels/ffm_kernels.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_LAYERS_KERNELS_FFM_KERNELS_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_LAYERS_KERNELS_FFM_KERNELS_H_\n\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntemplate <typename Device>\nstruct FFMImpl {\n  static void Compute(OpKernelContext *ctx, const std::string &int_type,\n                      TTypes<float>::ConstMatrix left_matrix, int left_feat_num,\n                      TTypes<float>::ConstMatrix right_matrix,\n                      int right_feat_num, int batch_size, int dim_size,\n                      TTypes<float>::Matrix output);\n};\n\ntemplate <typename Device>\nstruct FFMGradImpl {\n  static void Compute(OpKernelContext *ctx, const std::string &int_type,\n                      TTypes<float>::ConstMatrix grad_matrix, int grad_feat_num,\n                      TTypes<float>::ConstMatrix left_matrix, int left_feat_num,\n                      TTypes<float>::ConstMatrix right_matrix,\n                      int right_feat_num, int batch_size, int dim_size,\n                      TTypes<float>::Matrix left_grad_matrix,\n                      TTypes<float>::Matrix right_grad_matrix);\n};\n\ntemplate <typename Device>\nclass FFMOp : public OpKernel {\n public:\n  explicit FFMOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"dim_size\", &dim_size_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"int_type\", &int_type_));\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    const Tensor *left_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"left\", &left_tensor));\n    OP_REQUIRES(\n        ctx, left_tensor->dims() == 2,\n        errors::InvalidArgument(\"the left input tensor of ffm is not 2D\"));\n    int64 batch_size = left_tensor->dim_size(0);\n    int64 left_feat_num = left_tensor->dim_size(1) / dim_size_;\n    auto left_matrix = left_tensor->matrix<float>();\n\n    const Tensor *right_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"right\", &right_tensor));\n    OP_REQUIRES(\n        ctx, left_tensor->dims() == 2,\n        errors::InvalidArgument(\"the right input tensor of ffm is not 2D\"));\n    OP_REQUIRES(ctx, batch_size == right_tensor->dim_size(0),\n                errors::InvalidArgument(\n                    \"the batch size of left and right tensor are not match\"));\n    int64 right_feat_num = right_tensor->dim_size(1) / dim_size_;\n    auto right_matrix = right_tensor->matrix<float>();\n\n    Tensor *output_tensor = nullptr;\n    int out_last_dim = 0;\n    if (int_type_ == \"dot\") {\n      out_last_dim = left_feat_num * right_feat_num;\n    } else {\n      out_last_dim = left_feat_num * right_feat_num * dim_size_;\n    }\n\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {batch_size, out_last_dim},\n                                             &output_tensor));\n    auto output_matrix = output_tensor->matrix<float>();\n    FFMImpl<Device>::Compute(ctx, int_type_, left_matrix, left_feat_num,\n                             right_matrix, right_feat_num, batch_size,\n                             dim_size_, output_matrix);\n  }\n\n private:\n  int dim_size_;\n  std::string int_type_;\n};\n\ntemplate <typename Device>\nclass FFMGradOp : public OpKernel {\n public:\n  explicit FFMGradOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"dim_size\", &dim_size_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"int_type\", &int_type_));\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    const Tensor *grad_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"grad\", &grad_tensor));\n    OP_REQUIRES(ctx, grad_tensor->dims() == 2,\n                errors::InvalidArgument(\"the grad tensor of ffm is not 2D\"));\n    int batch_size = grad_tensor->dim_size(0);\n    int grad_feat_num = 0;\n    if (int_type_ == \"dot\") {\n      grad_feat_num = grad_tensor->dim_size(1);\n    } else {\n      grad_feat_num = grad_tensor->dim_size(1) / dim_size_;\n    }\n\n    auto grad_matrix = grad_tensor->matrix<float>();\n\n    const Tensor *left_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"left\", &left_tensor));\n    OP_REQUIRES(\n        ctx, left_tensor->dims() == 2,\n        errors::InvalidArgument(\"the left input tensor of ffm is not 2D\"));\n    int64 left_feat_num = left_tensor->dim_size(1) / dim_size_;\n    auto left_matrix = left_tensor->matrix<float>();\n\n    const Tensor *right_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"right\", &right_tensor));\n    OP_REQUIRES(\n        ctx, left_tensor->dims() == 2,\n        errors::InvalidArgument(\"the right input tensor of ffm is not 2D\"));\n    int64 right_feat_num = right_tensor->dim_size(1) / dim_size_;\n    auto right_matrix = right_tensor->matrix<float>();\n\n    OP_REQUIRES(ctx, grad_feat_num == left_feat_num * right_feat_num,\n                errors::InvalidArgument(\"the in/out shape not match\"));\n\n    Tensor *left_grad_tensor = nullptr;\n    OP_REQUIRES_OK(\n        ctx, ctx->allocate_output(0, left_tensor->shape(), &left_grad_tensor));\n    auto left_grad_matrix = left_grad_tensor->matrix<float>();\n\n    Tensor *right_grad_tensor = nullptr;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(1, right_tensor->shape(),\n                                             &right_grad_tensor));\n    auto right_grad_matrix = right_grad_tensor->matrix<float>();\n\n    FFMGradImpl<Device>::Compute(ctx, int_type_, grad_matrix, grad_feat_num,\n                                 left_matrix, left_feat_num, right_matrix,\n                                 right_feat_num, batch_size, dim_size_,\n                                 left_grad_matrix, right_grad_matrix);\n  }\n\n private:\n  int dim_size_;\n  std::string int_type_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_LAYERS_KERNELS_FFM_KERNELS_H_\n"
  },
  {
    "path": "monolith/native_training/layers/kernels/fid_counter_kernel.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass MonolithFidCounterOp : public OpKernel {\n public:\n  explicit MonolithFidCounterOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"step\", &step_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"counter_threshold\", &counter_threshold_));\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    ctx->set_output(0, ctx->input(0));\n  }\n\n private:\n  float step_;\n  int counter_threshold_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithFidCounter\").Device(DEVICE_CPU), MonolithFidCounterOp)\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/layers/layer_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom absl import logging\nfrom typing import Tuple\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\nlayer_ops_lib = gen_monolith_ops\n\n\ndef ffm(left: tf.Tensor,\n        right: tf.Tensor,\n        dim_size: int,\n        int_type: str = 'multiply') -> tf.Tensor:\n  output = layer_ops_lib.FFM(left=left,\n                             right=right,\n                             dim_size=dim_size,\n                             int_type=int_type)\n  return output\n\n\n@tf.RegisterGradient('FFM')\ndef _ffm_grad(op, grad: tf.Tensor) -> tf.Tensor:\n  left, right = op.inputs[0], op.inputs[1]\n  dim_size = op.get_attr('dim_size')\n  int_type = op.get_attr('int_type')\n\n  (left_grad, right_grad) = layer_ops_lib.FFMGrad(grad=grad,\n                                                  left=left,\n                                                  right=right,\n                                                  dim_size=dim_size,\n                                                  int_type=int_type)\n  return left_grad, right_grad\n\n\ndef feature_insight(input_embedding,\n                    weight,\n                    segment_sizes,\n                    aggregate: bool = False) -> tf.Tensor:\n  assert segment_sizes\n  assert input_embedding.shape.as_list()[-1] == weight.shape.as_list()[0]\n  out = layer_ops_lib.FeatureInsight(input=input_embedding,\n                                     weight=weight,\n                                     segment_sizes=segment_sizes)\n  if aggregate:\n    k, num_feature = weight.shape.as_list()[-1], len(segment_sizes)\n    segment_ids = []\n    for i in range(num_feature):\n      segment_ids.extend([i] * k)\n    segment_ids_tensor = tf.constant(value=segment_ids,\n                                     shape=(k * num_feature,),\n                                     dtype=tf.int32)\n    return tf.transpose(\n        tf.math.segment_sum(tf.transpose(out * out),\n                            segment_ids=segment_ids_tensor))\n    pass\n  else:\n    return out\n\n\n@tf.RegisterGradient('FeatureInsight')\ndef _feature_insight(op, grad: tf.Tensor) -> Tuple[tf.Tensor, tf.Tensor]:\n  input_embedding, weight = op.inputs[0], op.inputs[1]\n  segment_sizes = op.get_attr('segment_sizes')\n  k = weight.shape.as_list()[-1]\n  input_embedding_grad, weight_grad = layer_ops_lib.FeatureInsightGrad(\n      grad=grad,\n      input=input_embedding,\n      weight=weight,\n      segment_sizes=segment_sizes,\n      K=k)\n  return input_embedding_grad, weight_grad\n\n\n\n\ndef fid_counter(counter: tf.Tensor, counter_threshold: int, step: float = 1.0):\n  \"\"\"Count element value(e.g. embedding/label), will consume 1-size vector as counter\n  Args:\n    counter(Tensor): feature slice to store counter\n    counter_threshold(int): threshold set step to 0\n    step(Tensor): value add to counter\n  Returns:\n    counter: counter value\n  Attention:\n    1. fid_counter's input embedding MUST use SgdOptimizer(1.0).\n    2. We recommend using Fp32Compressor() for counter slice.\n    3. If you use Fp16Compressor(), for precision reason, we recommend setting counter_threshold to 60000.\n\n  Example::\n      >>> item_count = self.embedding_lookup(slice_name='item_count',\n                                             slots=[534],\n                                             dim=1,\n                                             initializer= ConstantsInitializer(1.0),\n                                             optimizer= SgdOptimizer(1.0),\n                                             compressor= Fp32Compressor())\n      >>> item_count = layer_ops.fid_counter(item_count, step=1)\n      >>> item_count = tf.reshape(item_count, shape=(-1, ))\n      >>> item_weights = 1 / (1 + tf.math.exp(4 - 0.03 * item_count))\n  \"\"\"\n  counter = layer_ops_lib.MonolithFidCounter(\n      counter=counter, step=step, counter_threshold=counter_threshold)\n  counter = counter + tf.cast(step, counter.dtype)\n  counter = tf.where(\n      counter > counter_threshold,\n      tf.ones_like(counter) * tf.cast(counter_threshold, counter.dtype),\n      counter)\n  return counter\n\n\n@tf.RegisterGradient('MonolithFidCounter')\ndef _fid_counter_grad(op, grad: tf.Tensor) -> tf.Tensor:\n  counter = op.inputs[0]\n  step = op.get_attr('step')\n  grad = tf.ones_like(counter) * tf.cast(-step, counter.dtype)\n  counter_threshold = op.get_attr('counter_threshold')\n  grad = tf.where(counter >= counter_threshold, tf.zeros_like(grad), grad)\n  return grad\n"
  },
  {
    "path": "monolith/native_training/layers/layer_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport numpy as np\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import test_util\n\nfrom monolith.native_training.layers.layer_ops import ffm\nfrom monolith.native_training.layers import layer_ops\n\ntf.random.set_seed(0)\n\n\nclass LayerOpsTest(tf.test.TestCase):\n\n  def test_ffm_mul(self):\n    with test_util.use_gpu():\n      left = tf.random.uniform(shape=(8, 10 * 4), minval=0, maxval=10)\n      right = tf.random.uniform(shape=(8, 12 * 4), minval=0, maxval=10)\n      output_maybe_on_gpu = ffm(left=left, right=right, dim_size=4)\n      if tf.test.is_gpu_available():\n        self.assertEqual(output_maybe_on_gpu.device,\n                         '/job:localhost/replica:0/task:0/device:GPU:0')\n      with tf.device(\"/device:CPU:0\"):\n        output_on_cpu = ffm(left=left, right=right, dim_size=4)\n        self.assertEqual(output_on_cpu.device,\n                         '/job:localhost/replica:0/task:0/device:CPU:0')\n      self.assertTrue(output_maybe_on_gpu.shape == (8, 480))\n      self.assertAllEqual(output_maybe_on_gpu, output_on_cpu)\n\n  def test_ffm_mul_grad(self):\n    with test_util.use_gpu():\n      left = tf.random.uniform(shape=(8, 10 * 4), minval=0, maxval=10)\n      right = tf.random.uniform(shape=(8, 12 * 4), minval=0, maxval=10)\n      with tf.GradientTape() as g:\n        g.watch(left)\n        g.watch(right)\n        out = ffm(left=left, right=right, dim_size=4)\n        loss = tf.reduce_sum(out)\n        left_grad_maybe_on_gpu, right_grad_maybe_on_gpu = g.gradient(\n            loss, [left, right])\n        self.assertTrue(left_grad_maybe_on_gpu.shape == (8, 40))\n        self.assertTrue(right_grad_maybe_on_gpu.shape == (8, 48))\n\n      with tf.device(\"/device:CPU:0\"), tf.GradientTape() as g:\n        g.watch(left)\n        g.watch(right)\n        out = ffm(left=left, right=right, dim_size=4)\n        loss = tf.reduce_sum(out)\n        left_grad_on_cpu, right_grad_on_cpu = g.gradient(loss, [left, right])\n        self.assertEqual(left_grad_on_cpu.device,\n                         '/job:localhost/replica:0/task:0/device:CPU:0')\n        self.assertEqual(right_grad_on_cpu.device,\n                         '/job:localhost/replica:0/task:0/device:CPU:0')\n        self.assertAllEqual(left_grad_maybe_on_gpu, left_grad_on_cpu)\n        self.assertAllEqual(right_grad_maybe_on_gpu, right_grad_on_cpu)\n\n  def test_ffm_dot(self):\n    with test_util.use_gpu():\n      left = tf.random.uniform(shape=(8, 10 * 4), minval=0, maxval=10)\n      right = tf.random.uniform(shape=(8, 12 * 4), minval=0, maxval=10)\n      output_maybe_on_gpu = ffm(left=left,\n                                right=right,\n                                dim_size=4,\n                                int_type='dot')\n      if tf.test.is_gpu_available():\n        self.assertEqual(output_maybe_on_gpu.device,\n                         '/job:localhost/replica:0/task:0/device:GPU:0')\n      with tf.device(\"/device:CPU:0\"):\n        output_on_cpu = ffm(left=left, right=right, dim_size=4, int_type='dot')\n        self.assertEqual(output_on_cpu.device,\n                         '/job:localhost/replica:0/task:0/device:CPU:0')\n      self.assertTrue(output_maybe_on_gpu.shape == (8, 120))\n      self.assertAllEqual(output_maybe_on_gpu, output_on_cpu)\n\n  def test_ffm_dot_grad(self):\n    with test_util.use_gpu():\n      left = tf.random.uniform(shape=(8, 10 * 4), minval=0, maxval=10)\n      right = tf.random.uniform(shape=(8, 12 * 4), minval=0, maxval=10)\n      with tf.GradientTape() as g:\n        g.watch(left)\n        g.watch(right)\n        out = ffm(left=left, right=right, dim_size=4, int_type='dot')\n        loss = tf.reduce_sum(out)\n        left_grad_maybe_on_gpu, right_grad_maybe_on_gpu = g.gradient(\n            loss, [left, right])\n\n        self.assertTrue(left_grad_maybe_on_gpu.shape == (8, 40))\n        self.assertTrue(right_grad_maybe_on_gpu.shape == (8, 48))\n\n      with tf.device(\"/device:CPU:0\"), tf.GradientTape() as g:\n        g.watch(left)\n        g.watch(right)\n        out = ffm(left=left, right=right, dim_size=4, int_type='dot')\n        loss = tf.reduce_sum(out)\n        left_grad_on_cpu, right_grad_on_cpu = g.gradient(loss, [left, right])\n        self.assertEqual(left_grad_on_cpu.device,\n                         '/job:localhost/replica:0/task:0/device:CPU:0')\n        self.assertEqual(right_grad_on_cpu.device,\n                         '/job:localhost/replica:0/task:0/device:CPU:0')\n        self.assertAllEqual(left_grad_maybe_on_gpu, left_grad_on_cpu)\n        self.assertAllEqual(right_grad_maybe_on_gpu, right_grad_on_cpu)\n\n  def test_feature_insight(self):\n    segment_sizes = [3, 2, 4]\n    input_embedding = [\n        0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.1, 1.2, 1.3, 1.4, 1.5,\n        1.6, 1.7, 1.8, 1.9, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9\n    ]\n    input_embedding_tensor = tf.constant(value=input_embedding,\n                                         shape=(3, 9),\n                                         dtype=tf.float32)\n    weight = [\n        0.1, 0.2, 0.3, 0.4, 0.5, 0.4, 0.3, 0.2, 0.1, 0.9, 0.8, 0.7, 0.6, 0.5,\n        0.6, 0.7, 0.8, 0.9\n    ]\n    weight_tensor = tf.constant(value=weight, shape=(9, 2), dtype=tf.float32)\n\n    input_embedding_splits = tf.split(input_embedding_tensor,\n                                      num_or_size_splits=segment_sizes,\n                                      axis=1)\n    weight_splits = tf.split(weight_tensor,\n                             num_or_size_splits=segment_sizes,\n                             axis=0)\n    concatenated = tf.concat([\n        tf.matmul(ip, w) for ip, w in zip(input_embedding_splits, weight_splits)\n    ],\n                             axis=1)\n    k, num_feature = 2, 3\n    segment_ids = []\n    for i in range(num_feature):\n      segment_ids.extend([i] * k)\n    segment_ids_tensor = tf.constant(value=segment_ids,\n                                     shape=(k * num_feature,),\n                                     dtype=tf.int32)\n    res_exp = tf.transpose(\n        tf.math.segment_sum(tf.transpose(concatenated * concatenated),\n                            segment_ids=segment_ids_tensor))\n    out = layer_ops.feature_insight(input_embedding_tensor,\n                                    weight_tensor,\n                                    segment_sizes,\n                                    aggregate=True)\n    self.assertAllClose(out, res_exp)\n\n  def test_feature_insight_grad(self):\n    segment_sizes = [3, 2, 4]\n    input_embedding = [\n        0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.1, 1.2, 1.3, 1.4, 1.5,\n        1.6, 1.7, 1.8, 1.9, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9\n    ]\n    input_embedding_tensor = tf.constant(value=input_embedding,\n                                         shape=(3, 9),\n                                         dtype=tf.float32)\n    weight = [\n        0.1, 0.2, 0.3, 0.4, 0.5, 0.4, 0.3, 0.2, 0.1, 0.9, 0.8, 0.7, 0.6, 0.5,\n        0.6, 0.7, 0.8, 0.9\n    ]\n    weight_tensor = tf.constant(value=weight, shape=(9, 2), dtype=tf.float32)\n\n    with tf.GradientTape(persistent=True) as g:\n      g.watch(input_embedding_tensor)\n      g.watch(weight_tensor)\n      input_embedding_splits = tf.split(input_embedding_tensor,\n                                        num_or_size_splits=segment_sizes,\n                                        axis=1)\n      weight_splits = tf.split(weight_tensor,\n                               num_or_size_splits=segment_sizes,\n                               axis=0)\n      res_exp = tf.concat([\n          tf.matmul(ip, w)\n          for ip, w in zip(input_embedding_splits, weight_splits)\n      ],\n                          axis=1)\n      out = layer_ops.feature_insight(input_embedding_tensor, weight_tensor,\n                                      segment_sizes)\n\n    input_embedding_grad_exp = g.gradient(res_exp, input_embedding_tensor)\n    weight_grad_exp = g.gradient(res_exp, weight_tensor)\n    input_embedding_grad = g.gradient(out, input_embedding_tensor)\n    weight_grad = g.gradient(out, weight_tensor)\n\n    self.assertAllClose(out, res_exp)\n    self.assertAllClose(input_embedding_grad, input_embedding_grad_exp)\n    self.assertAllClose(weight_grad, weight_grad_exp)\n\n\n  def test_fid_counter_grad(self):\n    alpha = tf.constant([1.0])\n    with tf.GradientTape() as g:\n      g.watch(alpha)\n      counter = layer_ops.fid_counter(alpha, counter_threshold=1000, step=1)\n      counter_loss = tf.reduce_sum(counter)\n      var_grad = g.gradient(counter_loss, alpha)\n      self.assertAllClose(counter, [2.0])\n      self.assertAllClose(var_grad, [-1.0])\n      print(f\"The grad {list(var_grad.numpy())}\", flush=True)\n\n    with tf.GradientTape() as g:\n      g.watch(alpha)\n      counter = layer_ops.fid_counter(alpha, counter_threshold=1000, step=0.01)\n      counter_loss = tf.reduce_sum(counter)\n      var_grad = g.gradient(counter_loss, alpha)\n      self.assertAllClose(counter, [1.01])\n      self.assertAllClose(var_grad, [-0.01])\n      print(f\"The grad {list(var_grad.numpy())}\", flush=True)\n\n    alpha = tf.constant([1000.0])\n    with tf.GradientTape() as g:\n      g.watch(alpha)\n      counter = layer_ops.fid_counter(alpha, counter_threshold=1000, step=1)\n      counter_loss = tf.reduce_sum(counter)\n      var_grad = g.gradient(counter_loss, alpha)\n      self.assertAllClose(counter, [1000])\n      self.assertAllClose(var_grad, [0])\n      print(f\"The grad {list(var_grad.numpy())}\", flush=True)\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/lhuc.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# -*- encoding=utf-8 -*-\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow.keras.layers import Layer\nfrom tensorflow.keras import regularizers\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import BatchNormalization as BatchNorm\n\nfrom monolith.native_training.layers.mlp import MLP\nfrom monolith.native_training.layers.dense import Dense\nfrom monolith.native_training.utils import extend_as_list, with_params\nimport monolith.native_training.layers.advanced_activations as ad_acts\nfrom monolith.native_training.monolith_export import monolith_export\n\n\n@monolith_export\n@with_params\nclass LHUCTower(Layer):\n  \"\"\"LHUCTower, 对MLP的改进, 在MLP的基础上增加了一系列的 LHUC MLP 当作Gate. 论文可参考 https://arxiv.org/abs/1601.02828\n\n  Args:\n    output_dims (:obj:`List[int]`): 主Tower的每一层的输出神经元个数\n    lhuc_output_dims (:obj:`List[int]`, `List[List[int]]`): 每个LHUC MLP的output_dims, 其长度与output_dims相同, 可以有两种方式指定, \n                                                            1) 用`List[int]`指定, 此时, 除最上层外, 所有LHUC MLP结构相同, 最上层的Dense会在内部自动加上\n                                                            并处理shape; 2) 用`List[List[int]]`, 此时, 每个LHUC MLP结构都可以不同, 内部不会处理最上层Dense\n                                                            层, 所以用户必须确保shape是正确的. lhuc_output_dims默认为None, 等价于[]. \n    activations (:obj:`List[tf.activation]`, `List[str]`, `tf.activation`, `str`): 激活函数, 可以用str表示, 也可以用TF中的activation\n    initializers (:obj:`List[tf.initializer]`): kernel, 也就是W的初始化器, 是一个列表\n    kernel_regularizer (:obj:`tf.regularizer`): kernel正侧化器\n    use_weight_norm (:obj:`bool`): 是否开启kernel_norm \n    use_learnable_weight_norm (:obj:`bool`): 是否让kernel_norm可训练\n    use_bias (:obj:`bool`): 是否使用bias, 默认为True\n    bias_regularizer (:obj:`tf.regularizer`): bias正侧化\n    enable_batch_normalization (:obj:`bool`): 是否开启batch normalization, 如果开启, 会对输入数据, 及每个Dense Layer的输出匀做\n                                              BatchNorm (最后一个Dense Layer除外).\n    batch_normalization_momentum (:obj:`float`): BatchNorm中的动量因子\n    batch_normalization_renorm (:obj:`bool`): 是否使用renorm, (论文可参考 https://arxiv.org/abs/1702.03275)\n    batch_normalization_renorm_clipping (:obj:`bool`): renorm中的clipping, 具体请参考TF中的 `BatchNormalization`_\n    batch_normalization_renorm_momentum (:obj:`float`): renorm中的momentum, 具体请参考TF中的 `BatchNormalization`_\n  \n  此外, 对于 weight_norm, batch_normalization 相关参数, 主MLP与LHUC MLP共用, 如果要为LHUC MLP指定不同的参数, 可用 \"lhuc_{params_name}\" 来指定\n\n  .. _BatchNormalization: https://www.tensorflow.org/api_docs/python/tf/keras/layers/BatchNormalization\n  \n  \"\"\"\n\n  def __init__(self,\n               output_dims,\n               lhuc_output_dims=None,\n               activations='relu',\n               initializers=None,\n               use_bias=True,\n               use_weight_norm=True,\n               use_learnable_weight_norm=True,\n               kernel_regularizer=None,\n               bias_regularizer=None,\n               enable_batch_normalization=False,\n               batch_normalization_momentum=0.99,\n               batch_normalization_renorm=False,\n               batch_normalization_renorm_clipping=None,\n               batch_normalization_renorm_momentum=0.99,\n               **kwargs):\n    self._lhuc_kwargs = {\n        k: v for k, v in kwargs.items() if k.startswith('lhuc_')\n    }\n    for lhuc_key in self._lhuc_kwargs:\n      del kwargs[lhuc_key]\n\n    super(LHUCTower, self).__init__(**kwargs)\n    self.output_dims = output_dims\n    self.n_layers = len(output_dims)\n\n    if activations is None:\n      self.activations = [ad_acts.get('relu')] * (self.n_layers - 1) + [None]\n    elif isinstance(activations, (list, tuple)):\n      assert len(activations) == self.n_layers\n      self.activations = [ad_acts.get(act) for act in activations]\n    else:\n      self.activations = [\n          ad_acts.get(activations) if i != self.n_layers - 1 else None\n          for i in range(self.n_layers)\n      ]\n\n    self.initializers = extend_as_list(initializers, self.n_layers)\n\n    self.use_bias = use_bias\n    self.use_weight_norm = use_weight_norm\n    self.use_learnable_weight_norm = use_learnable_weight_norm\n    self.kernel_regularizer = kernel_regularizer\n    self.bias_regularizer = bias_regularizer\n\n    self.enable_batch_normalization = enable_batch_normalization\n    self.batch_normalization_momentum = batch_normalization_momentum\n    self.batch_normalization_renorm = batch_normalization_renorm\n    self.batch_normalization_renorm_clipping = batch_normalization_renorm_clipping\n    self.batch_normalization_renorm_momentum = batch_normalization_renorm_momentum\n\n    if lhuc_output_dims:\n      assert isinstance(lhuc_output_dims, (list, tuple))\n      if all(isinstance(dims, (list, tuple)) for dims in lhuc_output_dims):\n        for i, dims in enumerate(lhuc_output_dims):\n          assert dims[-1] == output_dims[\n              i], \"the last dim of lhuc must be identity with dense output\"\n        self.lhuc_output_dims = lhuc_output_dims\n      elif all(isinstance(dims, int) for dims in lhuc_output_dims):\n        self.lhuc_output_dims = []\n        for dim in self.output_dims:\n          self.lhuc_output_dims.append(lhuc_output_dims + [dim])\n      else:\n        raise Exception(\"lhuc_output_dims is error\")\n    else:\n      self.lhuc_output_dims = [[i] for i in self.output_dims]\n    self.lhuc_activations = [[\n        ad_acts.get('relu') if i != len(dims) - 1 else ad_acts.get('sigmoid2')\n        for i in range(len(dims))\n    ]\n                             for dims in self.lhuc_output_dims]\n\n    self.layers = []\n    self.lhuc_layers = []\n    self.extra_layers = []\n\n  def lhuc_params(self, name):\n    params = self._lhuc_kwargs.get(f\"lhuc_{name}\")\n    if params is None and hasattr(self, name):\n      params = getattr(self, name)\n    return params\n\n  def build(self, input_shape):\n    if self.enable_batch_normalization:\n      bn_layer = BatchNorm(\n          name='batch_norm',\n          momentum=self.batch_normalization_momentum,\n          renorm=self.batch_normalization_renorm,\n          renorm_clipping=self.batch_normalization_renorm_clipping,\n          renorm_momentum=self.batch_normalization_renorm_momentum)\n      self._trainable_weights.extend(bn_layer.trainable_weights)\n      self._non_trainable_weights.extend(bn_layer.non_trainable_weights)\n      self.extra_layers.append(bn_layer)\n\n    for i, dim in enumerate(self.output_dims):\n      layer_name = f'layer_{i + 1}'\n      sequential = Sequential(name=layer_name)  # one block in dense tower\n      dense = Dense(name=f'{layer_name}/dense',\n                    units=dim,\n                    activation=None,\n                    use_bias=self.use_bias,\n                    kernel_initializer=self.initializers[i],\n                    bias_initializer=tf.initializers.zeros(),\n                    allow_kernel_norm=self.use_weight_norm,\n                    kernel_norm_trainable=self.use_learnable_weight_norm,\n                    kernel_regularizer=regularizers.get(\n                        self.kernel_regularizer),\n                    bias_regularizer=regularizers.get(self.bias_regularizer))\n      self._trainable_weights.extend(dense.trainable_weights)\n      self._non_trainable_weights.extend(dense.non_trainable_weights)\n      sequential.add(dense)\n\n      if i != (self.n_layers - 1) and self.enable_batch_normalization:\n        bn_layer = BatchNorm(\n            name=f'{layer_name}/batch_norm',\n            momentum=self.batch_normalization_momentum,\n            renorm=self.batch_normalization_renorm,\n            renorm_clipping=self.batch_normalization_renorm_clipping,\n            renorm_momentum=self.batch_normalization_renorm_momentum)\n        self._trainable_weights.extend(bn_layer.trainable_weights)\n        self._non_trainable_weights.extend(bn_layer.non_trainable_weights)\n        sequential.add(bn_layer)\n\n      if self.activations[i] is not None:\n        sequential.add(self.activations[i])\n\n      self.layers.append(sequential)\n\n      # for lhuc tower\n      mlp = MLP(name=f'{layer_name}/lhuc',\n                output_dims=self.lhuc_output_dims[i],\n                activations=self.lhuc_activations[i],\n                initializers=self.initializers[i],\n                kernel_regularizer=self.lhuc_params('kernel_regularizer'),\n                use_weight_norm=self.lhuc_params('use_weight_norm'),\n                use_learnable_weight_norm=self.lhuc_params(\n                    'use_learnable_weight_norm'),\n                use_bias=self.lhuc_params('use_bias'),\n                bias_regularizer=self.lhuc_params('bias_regularizer'),\n                enable_batch_normalization=self.lhuc_params(\n                    'enable_batch_normalization'),\n                batch_normalization_momentum=self.lhuc_params(\n                    'batch_normalization_momentum'),\n                batch_normalization_renorm=self.lhuc_params(\n                    'batch_normalization_renorm'),\n                batch_normalization_renorm_clipping=self.lhuc_params(\n                    'batch_normalization_renorm_clipping'),\n                batch_normalization_renorm_momentum=self.lhuc_params(\n                    'batch_normalization_renorm_momentum'))\n      self._trainable_weights.extend(mlp.trainable_weights)\n      self._non_trainable_weights.extend(mlp.non_trainable_weights)\n      self.lhuc_layers.append(mlp)\n\n      super(LHUCTower, self).build(input_shape)\n\n  def call(self, inputs, **kwargs):\n    if isinstance(inputs, (list, tuple)):\n      assert len(inputs) == 2\n      dense_input, lhuc_input = inputs\n    else:\n      inputs = tf.convert_to_tensor(inputs)\n      dense_input = inputs\n      lhuc_input = inputs\n\n    input_t = dense_input\n    for layer in self.extra_layers:\n      input_t = layer(input_t)\n\n    for layer, lhuc_layer in zip(self.layers, self.lhuc_layers):\n      output_t = layer(input_t) * lhuc_layer(lhuc_input)\n      input_t = output_t\n\n    return output_t\n\n  def get_config(self):\n    config = {\n        \"output_dims\":\n            self.output_dims,\n        \"lhuc_output_dims\":\n            self.lhuc_output_dims,\n        \"activations\": [ad_acts.serialize(act) for act in self.activations],\n        \"initializers\": [\n            tf.initializers.serialize(init) for init in self.initializers\n        ],\n        \"use_bias\":\n            self.use_bias,\n        \"use_weight_norm\":\n            self.use_weight_norm,\n        \"use_learnable_weight_norm\":\n            self.use_learnable_weight_norm,\n        'kernel_regularizer':\n            regularizers.serialize(self.kernel_regularizer),\n        'bias_regularizer':\n            regularizers.serialize(self.bias_regularizer),\n        \"enable_batch_normalization\":\n            self.enable_batch_normalization,\n        \"batch_normalization_momentum\":\n            self.batch_normalization_momentum,\n        'batch_normalization_renorm':\n            self.batch_normalization_renorm,\n        'batch_normalization_renorm_clipping':\n            self.batch_normalization_renorm_clipping,\n        'batch_normalization_renorm_momentum':\n            self.batch_normalization_renorm_momentum\n    }\n\n    config.update(self._lhuc_kwargs)\n    base_config = super(LHUCTower, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n  @classmethod\n  def from_config(cls, config):\n    p = cls.params().copy()\n    need_pop = []\n    for key, value in config.items():\n      if key in p:\n        if key == 'initializers':\n          p[key] = [tf.initializers.deserialize(init) for init in config[key]]\n        elif key == 'activations':\n          p[key] = [ad_acts.deserialize(act) for act in config[key]]\n        elif key == 'kernel_regularizer':\n          regularizers.deserialize(value),\n        elif key == 'bias_regularizer':\n          regularizers.deserialize(value),\n        else:\n          p[key] = value\n        need_pop.append(key)\n\n    for key in need_pop:\n      config.pop(key)\n    return p.instantiate()\n"
  },
  {
    "path": "monolith/native_training/layers/lhuc_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\n\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.lhuc import LHUCTower\n\n\nclass LHUCTowerTest(tf.test.TestCase):\n\n  def test_lhuc_instantiate(self):\n    lhuc_layer_template = LHUCTower.params()\n\n    test_params0 = lhuc_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.output_dims = [1, 3, 4, 5]\n    test_params0.activations = None\n    test_params0.initializers = tf.keras.initializers.GlorotNormal()\n    lhuc1 = test_params0.instantiate()\n    print(lhuc1)\n\n    lhuc2 = LHUCTower(output_dims=[1, 3, 4, 5],\n                      activations=None,\n                      initializers=tf.keras.initializers.HeUniform())\n    print(lhuc2)\n\n  def test_lhuc_serde(self):\n    lhuc_layer_template = LHUCTower.params()\n\n    test_params0 = lhuc_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.output_dims = [1, 3, 4, 5]\n    test_params0.activations = None\n    test_params0.initializers = tf.keras.initializers.GlorotNormal()\n    lhuc1 = test_params0.instantiate()\n\n    cfg = lhuc1.get_config()\n    lhuc2 = LHUCTower.from_config(cfg)\n\n    print(lhuc1, lhuc2)\n\n  def test_lhuc_call(self):\n    layer = LHUCTower(output_dims=[50, 20, 1],\n                      activations=None,\n                      lhuc_output_dims=[[50, 50], [50, 50, 20], [100, 1]],\n                      use_bias=True,\n                      lhuc_use_bias=False,\n                      initializers=tf.keras.initializers.HeUniform())\n\n    dense_data = tf.keras.backend.variable(np.ones((100, 100)))\n    lhuc_data = tf.keras.backend.variable(np.ones((100, 50)))\n    sum_out = tf.reduce_sum(layer([dense_data, lhuc_data]))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/logit_correction.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom tensorflow.keras.layers import Layer, InputSpec\nfrom tensorflow.python.ops import math_ops\nfrom tensorflow.keras import activations\nfrom tensorflow.keras import initializers\nfrom tensorflow.python.keras import regularizers\n\nfrom monolith.native_training.utils import with_params\nfrom monolith.native_training.layers.mlp import MLP\nfrom monolith.native_training.monolith_export import monolith_export\n\n\n@monolith_export\n@with_params\nclass LogitCorrection(Layer):\n  \"\"\"Logit校正, 由于采样等原因, 会使得CTR/CVR的预测与后验均值有偏差, 需要对这种偏差进行校正\n\n  Logit校正可以在训练时进行, 也可以在推理时进行, 为了减轻推理时负担, 一般选择训练时进行, LogitCorrection就是用于训练时校正的\n  \n  Args:\n    activation (:obj:`tf.activation`): 激活函数, 默认为None\n    sample_bias (:obj:`bool`): 是否校正样本采样偏差\n\n  \"\"\"\n\n  def __init__(self, activation=None, sample_bias: bool = False, **kwargs):\n    super(LogitCorrection, self).__init__(**kwargs)\n    # compatible with older version forced sumpooling\n    # self.input_spec = InputSpec(shape=[None, None, 1])\n    self.input_spec = [InputSpec(max_ndim=2), InputSpec(max_ndim=2)]\n    self.activation = activations.get(activation)\n    self.sample_bias = sample_bias\n\n  def call(self, inputs, **kwargs):\n    # tensor with tf.shape([None,])\n    logits, sample_rate = inputs\n    corrected = self.get_sample_logits(logits, sample_rate, self.sample_bias)\n    if self.activation is not None:\n      corrected = self.activation(corrected)\n    return corrected\n\n  @staticmethod\n  def safe_log_sigmoid(logits):\n    zeros = tf.zeros_like(logits, dtype=logits.dtype)\n    cond = (logits >= zeros)\n    relu_logits = tf.where(cond, logits, zeros)\n    neg_abs_logits = tf.where(cond, -logits, logits)\n    return tf.negative(relu_logits - logits +\n                       tf.compat.v1.log1p(tf.exp(neg_abs_logits)))\n\n  @staticmethod\n  def get_sample_logits(logits, sample_rate, sample_bias):\n    if sample_rate is None and sample_bias:\n      return LogitCorrection.safe_log_sigmoid(logits)\n    elif sample_rate is not None and not sample_bias:\n      return tf.add(logits, tf.negative(tf.compat.v1.log(sample_rate)))\n    elif sample_rate is not None and sample_bias:\n      return tf.add(LogitCorrection.safe_log_sigmoid(logits),\n                    tf.negative(tf.compat.v1.log(sample_rate)))\n    else:\n      return logits\n\n  def compute_output_shape(self, input_shape):\n    return tuple(tf.shape([\n        None,\n    ]))\n\n  def get_config(self):\n    config = {\n        'activation': activations.serialize(self.activation),\n        'sample_bias': self.sample_bias\n    }\n    base_config = super(LogitCorrection, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n"
  },
  {
    "path": "monolith/native_training/layers/logit_correction_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.logit_correction import *\n\n\nclass SailSpecialTest(tf.test.TestCase):\n\n  def test_sr_instantiate(self):\n    layer_template = LogitCorrection.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.activation = tf.keras.activations.relu\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = LogitCorrection(activation=tf.keras.activations.relu)\n    print(ins2)\n\n  def test_sr_serde(self):\n    layer_template = LogitCorrection.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.activation = tf.keras.activations.sigmoid\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    cfg = ins1.get_config()\n    ins2 = LogitCorrection.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_sr_call(self):\n    layer_template = LogitCorrection.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.activation = tf.keras.activations.tanh\n    layer = test_params0.instantiate()\n\n    x = tf.keras.backend.variable(np.random.uniform(size=(100, 10)))\n    sr = tf.keras.backend.variable(np.random.uniform(low=1e-10, size=(100, 1)))\n    sum_out = tf.reduce_sum(layer((x, sr)))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/mlp.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom tensorflow.keras.layers import Layer\nfrom tensorflow.keras.layers import BatchNormalization as BatchNorm\nfrom tensorflow.python.keras import regularizers\n\nfrom monolith.native_training.layers.dense import Dense\nfrom monolith.native_training.utils import extend_as_list, with_params\nimport monolith.native_training.layers.advanced_activations as ad_acts\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.summary.summary_ops import feature_insight_data\n\n\n@monolith_export\n@with_params\nclass MLP(Layer):\n  \"\"\"多层感知器(Multilayer Perceptron), 最经典的人工神经网络, 由一系列层叠起来的Dense层组成\n\n  Args:\n    output_dims (:obj:`List[int]`): 每一层的输出神经元个数\n    activations (:obj:`List[tf.activation]`, `List[str]`, `tf.activation`, `str`): 激活函数, 可以用str表示, 也可以用TF中的activation\n    initializers (:obj:`List[tf.initializer]`): kernel, 也就是W的初始化器, 是一个列表\n    kernel_regularizer (:obj:`tf.regularizer`): kernel正侧化器\n    use_weight_norm (:obj:`bool`): 是否开启kernel_norm \n    use_learnable_weight_norm (:obj:`bool`): 是否让kernel_norm可训练\n    use_bias (:obj:`bool`): 是否使用bias, 默认为True\n    bias_regularizer (:obj:`tf.regularizer`): bias正侧化\n    enable_batch_normalization (:obj:`bool`): 是否开启batch normalization, 如果开启, 会对输入数据, 及每个Dense Layer的输出匀做\n                                              BatchNorm (最后一个Dense Layer除外).\n    batch_normalization_momentum (:obj:`float`): BatchNorm中的动量因子\n    batch_normalization_renorm (:obj:`bool`): 是否使用renorm, (论文可参考 https://arxiv.org/abs/1702.03275)\n    batch_normalization_renorm_clipping (:obj:`bool`): renorm中的clipping, 具体请参考TF中的 `BatchNormalization`_\n    batch_normalization_renorm_momentum (:obj:`float`): renorm中的momentum, 具体请参考TF中的 `BatchNormalization`_\n  \n  .. _BatchNormalization: https://www.tensorflow.org/api_docs/python/tf/keras/layers/BatchNormalization\n  \n  \"\"\"\n\n  def __init__(self,\n               output_dims,\n               activations=None,\n               initializers=None,\n               kernel_regularizer=None,\n               use_weight_norm=True,\n               use_learnable_weight_norm=True,\n               use_bias=True,\n               bias_regularizer=None,\n               enable_batch_normalization=False,\n               batch_normalization_momentum=0.99,\n               batch_normalization_renorm=False,\n               batch_normalization_renorm_clipping=None,\n               batch_normalization_renorm_momentum=0.99,\n               **kwargs):\n    super(MLP, self).__init__(**kwargs)\n    self.output_dims = output_dims\n    self.use_weight_norm = use_weight_norm\n    self.use_learnable_weight_norm = use_learnable_weight_norm\n    self.use_bias = use_bias\n    self.kernel_regularizer = regularizers.get(kernel_regularizer)\n    self.bias_regularizer = regularizers.get(bias_regularizer)\n    self.enable_batch_normalization = enable_batch_normalization\n    self.batch_normalization_momentum = batch_normalization_momentum\n    self.batch_normalization_renorm = batch_normalization_renorm\n    self.batch_normalization_renorm_clipping = batch_normalization_renorm_clipping\n    self.batch_normalization_renorm_momentum = batch_normalization_renorm_momentum\n    self._stacked_layers = []\n    self._n_layers = len(self.output_dims)\n    self._activations = None\n    self._initializers = [\n        tf.initializers.get(init)\n        for init in extend_as_list(initializers, self._n_layers)\n    ]\n\n    if activations is None:\n      self._activations = [ad_acts.get('relu')] * (self._n_layers - 1) + [None]\n    elif isinstance(activations, (list, tuple)):\n      assert len(activations) == self._n_layers\n      self._activations = [ad_acts.get(act) for act in activations]\n    else:\n      self._activations = [\n          ad_acts.get(activations) if i != self._n_layers - 1 else None\n          for i in range(self._n_layers)\n      ]\n\n  def build(self, input_shape):\n    if self.enable_batch_normalization:\n      bn = BatchNorm(momentum=self.batch_normalization_momentum,\n                     renorm=self.batch_normalization_renorm,\n                     renorm_clipping=self.batch_normalization_renorm_clipping,\n                     renorm_momentum=self.batch_normalization_renorm_momentum,\n                     name=f\"BatchNorm/in\")\n      self._trainable_weights.extend(bn.trainable_weights)\n      self._non_trainable_weights.extend(bn.non_trainable_weights)\n      self.add_loss(bn.losses)\n      self._stacked_layers.append(bn)\n\n    for i, dim in enumerate(self.output_dims):\n      is_final_layer = (i == (self._n_layers - 1))\n      dense = Dense(name=f\"dense_{i}\",\n                    units=dim,\n                    activation=None,\n                    use_bias=self.use_bias,\n                    kernel_initializer=self._initializers[i],\n                    bias_initializer=tf.initializers.zeros(),\n                    allow_kernel_norm=self.use_weight_norm,\n                    kernel_norm_trainable=self.use_learnable_weight_norm,\n                    kernel_regularizer=self.kernel_regularizer,\n                    bias_regularizer=self.bias_regularizer)\n      self._trainable_weights.extend(dense.trainable_weights)\n      self._non_trainable_weights.extend(dense.non_trainable_weights)\n      self.add_loss(dense.losses)\n      self._stacked_layers.append(dense)\n\n      if not is_final_layer and self.enable_batch_normalization:\n        bn = BatchNorm(momentum=self.batch_normalization_momentum,\n                       renorm=self.batch_normalization_renorm,\n                       renorm_clipping=self.batch_normalization_renorm_clipping,\n                       renorm_momentum=self.batch_normalization_renorm_momentum,\n                       name=f\"BatchNorm/out\")\n        self._trainable_weights.extend(bn.trainable_weights)\n        self._non_trainable_weights.extend(bn.non_trainable_weights)\n        self.add_loss(bn.losses)\n        self._stacked_layers.append(bn)\n\n      if self._activations[i] is not None:\n        self._stacked_layers.append(self._activations[i])\n\n    super(MLP, self).build(input_shape)\n\n  def call(self, input, **kwargs):\n    input_t, output_t = input, None\n    for layer in self._stacked_layers:\n      output_t = layer(input_t)\n      if layer.name.endswith('dense_0') and len(kwargs) > 0:\n        segment_names = kwargs.get('segment_names')\n        segment_sizes = kwargs.get('segment_sizes')\n        group_info = kwargs.get('group_info')\n        label = kwargs.get('label')\n        if segment_names is not None and segment_sizes is not None:\n          feature_insight_data(input_t, segment_names, segment_sizes,\n                               weight=layer.kernel, group_info=group_info, label=label)\n      input_t = output_t\n    return output_t\n\n  def get_config(self):\n    config = {\n        'output_dims':\n            self.output_dims,\n        \"activations\": [ad_acts.serialize(act) for act in self._activations],\n        \"initializers\": [\n            tf.initializers.serialize(init) for init in self._initializers\n        ],\n        \"use_weight_norm\":\n            self.use_weight_norm,\n        \"use_learnable_weight_norm\":\n            self.use_learnable_weight_norm,\n        \"enable_batch_normalization\":\n            self.enable_batch_normalization,\n        \"batch_normalization_momentum\":\n            self.batch_normalization_momentum,\n        \"use_bias\":\n            self.use_bias,\n        'kernel_regularizer':\n            regularizers.serialize(self.kernel_regularizer),\n        'bias_regularizer':\n            regularizers.serialize(self.bias_regularizer),\n        'batch_normalization_renorm':\n            self.batch_normalization_renorm,\n        'batch_normalization_renorm_clipping':\n            self.batch_normalization_renorm_clipping,\n        'batch_normalization_renorm_momentum':\n            self.batch_normalization_renorm_momentum\n    }\n    base_config = super(MLP, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n  @classmethod\n  def from_config(cls, config):\n    p = cls.params().copy()\n    need_pop = []\n    for key, value in config.items():\n      if key in p:\n        if key == 'initializers':\n          p[key] = [tf.initializers.deserialize(init) for init in config[key]]\n        elif key == 'activations':\n          p[key] = [ad_acts.deserialize(act) for act in config[key]]\n        else:\n          p[key] = value\n        need_pop.append(key)\n\n    for key in need_pop:\n      config.pop(key)\n    return p.instantiate()\n\n  def get_layer(self, index: int):\n    assert index < len(self._stacked_layers)\n    return self._stacked_layers[index]\n"
  },
  {
    "path": "monolith/native_training/layers/mlp_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\n\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.mlp import MLP\n\n\nclass MLPTest(tf.test.TestCase):\n\n  def test_mlp_instantiate(self):\n    dense_layer_template = MLP.params()\n\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.output_dims = [1, 3, 4, 5]\n    test_params0.activations = None\n    test_params0.initializers = tf.keras.initializers.GlorotNormal()\n    mlp1 = test_params0.instantiate()\n    print(mlp1)\n\n    mlp2 = MLP(output_dims=[1, 3, 4, 5],\n               activations=None,\n               initializers=tf.keras.initializers.HeUniform())\n    print(mlp2)\n\n  def test_mlp_serde(self):\n    dense_layer_template = MLP.params()\n\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.output_dims = [1, 3, 4, 5]\n    test_params0.activations = None\n    test_params0.initializers = tf.keras.initializers.GlorotNormal()\n    mlp1 = test_params0.instantiate()\n\n    cfg = mlp1.get_config()\n    mlp2 = MLP.from_config(cfg)\n\n    print(mlp1, mlp2)\n\n  def test_mlp_call(self):\n    dense_layer_template = MLP.params()\n\n    test_params0 = dense_layer_template.copy()\n    test_params0.name = 'test_dense0'\n    test_params0.output_dims = [100, 50, 10, 1]\n    test_params0.enable_batch_normalization = True\n    test_params0.activations = [\n        'relu', tf.keras.activations.tanh, tf.keras.layers.PReLU, None\n    ]\n    test_params0.initializers = tf.keras.initializers.GlorotNormal()\n    layer = test_params0.instantiate()\n\n    data = tf.keras.backend.variable(np.ones((100, 100)))\n    sum_out = tf.reduce_sum(layer(data))\n    self.assertEqual(len(layer._stacked_layers), 11)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/multi_task.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport math\nimport numpy as np\nfrom typing import Union, List, Optional, Any\n\nimport tensorflow as tf\nfrom tensorflow.keras import backend as K\nfrom tensorflow.keras import activations, initializers, regularizers, constraints\nfrom tensorflow.keras.layers import Layer, InputSpec\n\nfrom monolith.core.base_layer import add_layer_loss\nfrom monolith.native_training.utils import with_params\nimport monolith.native_training.layers.advanced_activations as ad_acts\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.layers.mlp import MLP\nfrom monolith.native_training.layers.dense import Dense\n\n\n@monolith_export\n@with_params\nclass MMoE(Layer):\n  \"\"\"MMoE (Multi-gate Mixture of Experts) 是 MTL (Multi-task Training) 多任务学习的一种结构。通过引入\n  Multi-gate 来描述任务之间相关性以及每个任务对底层共享参数的依赖程度。\n  论文可参考: https://www.kdd.org/kdd2018/accepted-papers/view/modeling-task-relationships-in-multi-task-learning-with-multi-gate-mixture-\n\n  Args: \n    num_tasks (:obj:`int`): 任务训练的数量\n    expert_output_dims (:obj:`List[int]`, `List[List[int]]`): 每个Expert MLP的output_dims, 可以通过两种方法来定义\n                                                          1) 用`List[int]`指定, 此时, 每个Expert的结构是相同的;\n                                                          2) 用`List[List[int]]`, 此时, 每个Expert MLP结构都可以不同, 内部不会处理最上层Dense\n                                                          层, 所以用户必须确保每个Expert最上层的shape是相同的\n    expert_activations (:obj:`List[Any]`, `str`): 每个Expert激活函数, 可以用str表示, 也可以用TF中的activation\n    expert_initializers (:obj:`List[Any]`, `str`): W的初始化器, 可以是 str 也可以用户定义使用列表，默认使用 Glorot_uniform 初始化\n    gate_type (:obj:`str`): 每个gate所使用的计算方式, 可以在 (softmax, topk, noise_topk) 。默认使用的是 softmax\n    topk (:obj:`int`): 定义gate使用(topk, noise_topk)计算后保留最大的k个Expert, 默认是1\n    num_experts (:obj:`int`): 定义 Expert 的个数, 默认会根据 Expert 的其他参数生成个数\n    kernel_regularizer (:obj:`tf.regularizer`): kernel正侧化器\n    use_weight_norm (:obj:`bool`): 是否开启kernel_norm, 默认为True\n    use_learnable_weight_norm (:obj:`bool`): 是否让kernel_norm可训练, 默认为True\n    use_bias (:obj:`bool`): 是否使用bias, 默认为True\n    bias_regularizer (:obj:`tf.regularizer`): bias正侧化\n    enable_batch_normalization (:obj:`bool`): 是否开启batch normalization, 如果开启, 会对输入数据, 及每个Dense Layer的输出匀做\n                                              BatchNorm (最后一个Dense Layer除外).\n    batch_normalization_momentum (:obj:`float`): BatchNorm中的动量因子\n    batch_normalization_renorm (:obj:`bool`): 是否使用renorm, (论文可参考 https://arxiv.org/abs/1702.03275)\n    batch_normalization_renorm_clipping (:obj:`bool`): renorm中的clipping, 具体请参考TF中的 `BatchNormalization`_\n    batch_normalization_renorm_momentum (:obj:`float`): renorm中的momentum, 具体请参考TF中的 `BatchNormalization`_\n\n    .. _BatchNormalization: https://www.tensorflow.org/api_docs/python/tf/keras/layers/BatchNormalization\n  \"\"\"\n\n  def __init__(self,\n               num_tasks: int,\n               expert_output_dims: Union[List[int], List[List[int]]],\n               expert_activations: Union[str, List[Any]],\n               expert_initializers: Union[str, List[Any]] = 'glorot_uniform',\n               gate_type: str = 'softmax',\n               top_k: int = 1,\n               num_experts: Optional[int] = None,\n               kernel_regularizer=None,\n               use_weight_norm=True,\n               use_learnable_weight_norm=True,\n               use_bias=True,\n               bias_regularizer=None,\n               enable_batch_normalization=False,\n               batch_normalization_momentum=0.99,\n               batch_normalization_renorm=False,\n               batch_normalization_renorm_clipping=None,\n               batch_normalization_renorm_momentum=0.99,\n               **kwargs):\n    super(MMoE, self).__init__(**kwargs)\n\n    assert gate_type in {'softmax', 'topk', 'noise_topk'}\n    self._gate_type = gate_type\n\n    if num_experts is None:\n      if all(isinstance(dims, (list, tuple)) for dims in expert_output_dims):\n        self._num_experts = len(expert_output_dims)\n      elif isinstance(expert_activations, (list, tuple)):\n        self._num_experts = len(expert_activations)\n      elif isinstance(expert_initializers, (list, tuple)):\n        self._num_experts = len(expert_initializers)\n      else:\n        raise Exception('num_experts not set')\n    else:\n      self._num_experts = num_experts\n\n    if all(isinstance(dims, (list, tuple)) for dims in expert_output_dims):\n      last_dim = expert_output_dims[0][-1]\n      for dims in expert_output_dims:\n        assert last_dim == dims[-1]\n      self._expert_output_dims = expert_output_dims\n    else:\n      self._expert_output_dims = [expert_output_dims] * self._num_experts\n\n    if isinstance(expert_activations, (tuple, list)):\n      assert len(expert_activations) == self._num_experts\n      self._expert_activations = [\n          activations.get(act) for act in expert_activations\n      ]\n    else:\n      self._expert_activations = [\n          activations.get(expert_activations) for _ in range(self._num_experts)\n      ]\n\n    if isinstance(expert_initializers, (tuple, list)):\n      assert len(expert_initializers) == self._num_experts\n      self._expert_initializers = [\n          initializers.get(init) for init in expert_initializers\n      ]\n    else:\n      self._expert_initializers = [\n          initializers.get(expert_initializers)\n          for _ in range(self._num_experts)\n      ]\n\n    self._top_k = top_k\n    self._num_tasks = num_tasks\n    self.use_weight_norm = use_weight_norm\n    self.use_learnable_weight_norm = use_learnable_weight_norm\n    self.kernel_regularizer = regularizers.get(kernel_regularizer)\n    self.use_bias = use_bias\n    self.bias_regularizer = regularizers.get(bias_regularizer)\n    self.enable_batch_normalization = enable_batch_normalization\n    self.batch_normalization_momentum = batch_normalization_momentum\n    self.batch_normalization_renorm = batch_normalization_renorm\n    self.batch_normalization_renorm_clipping = batch_normalization_renorm_clipping\n    self.batch_normalization_renorm_momentum = batch_normalization_renorm_momentum\n\n  def build(self, input_shape):\n    self.experts = []\n    for i in range(self._num_experts):\n      mlp = MLP(name=f'expert_{i}',\n                output_dims=self._expert_output_dims[i],\n                activations=self._expert_activations[i],\n                initializers=self._expert_initializers[i],\n                kernel_regularizer=self.kernel_regularizer,\n                use_weight_norm=self.use_weight_norm,\n                use_learnable_weight_norm=self.use_learnable_weight_norm,\n                use_bias=self.use_bias,\n                bias_regularizer=self.bias_regularizer,\n                enable_batch_normalization=self.enable_batch_normalization,\n                batch_normalization_momentum=self.batch_normalization_momentum,\n                batch_normalization_renorm=self.batch_normalization_renorm,\n                batch_normalization_renorm_clipping=self.\n                batch_normalization_renorm_clipping,\n                batch_normalization_renorm_momentum=self.\n                batch_normalization_renorm_momentum)\n      self._trainable_weights.extend(mlp.trainable_weights)\n      self._non_trainable_weights.extend(mlp.non_trainable_weights)\n      self.experts.append(mlp)\n\n    # input_shape: [TensorShape([bz, dim1]), TensorShape([bz, dim2])]\n    if all(isinstance(shape, tf.TensorShape) for shape in input_shape):\n      gate_input_dim = input_shape[-1].as_list()[-1]\n    elif all(\n        isinstance(shape, tf.compat.v1.Dimension) for shape in input_shape):\n      gate_input_dim = input_shape[-1].value\n    else:\n      assert isinstance(input_shape[-1], int)\n      gate_input_dim = input_shape[-1]\n\n    gate_shape = (gate_input_dim, self._num_experts * self._num_tasks)\n    self._gate_weight = self.add_weight(name=\"gate_weight\",\n                                        shape=gate_shape,\n                                        dtype=tf.float32,\n                                        initializer=initializers.Zeros(),\n                                        trainable=True)\n    if self._gate_type == 'noise_topk':\n      self._gate_noise = self.add_weight(\n          name=\"gate_noise\",\n          shape=gate_shape,\n          dtype=tf.float32,\n          initializer=initializers.GlorotNormal(),\n          trainable=True)\n    else:\n      self._gate_noise = None\n\n    super(MMoE, self).build(input_shape)\n\n  def calc_gate(self, gate_input: tf.Tensor):\n    # (batch, num_tasks * num_experts)\n    gete_logit = tf.matmul(gate_input, self._gate_weight)\n\n    if self._gate_type == 'noise_topk':\n      noise = tf.random.normal(shape=tf.shape(gete_logit))\n      noise = noise * tf.nn.softplus(tf.matmul(gate_input, self._gate_noise))\n      gete_logit = gete_logit + noise\n\n    # (batch, num_tasks, num_experts)\n    gete_logit = tf.reshape(gete_logit,\n                            shape=(-1, self._num_tasks, self._num_experts))\n    gates = tf.nn.softmax(gete_logit, axis=2)\n\n    if self._gate_type in {'topk', 'noise_topk'}:\n      # (batch, num_tasks, top_k)\n      top_gates, _ = tf.nn.top_k(gates, self._top_k)\n      # (batch, num_tasks, 1)\n      threshold = tf.reduce_min(top_gates, axis=2, keepdims=True)\n      # (batch, num_tasks, num_experts)\n      gates = tf.where(gates >= threshold, gates,\n                       tf.zeros_like(gates, dtype=gates.dtype))\n      gates /= tf.reduce_sum(gates, axis=2, keepdims=True)  # normalize\n\n    # (batch, num_experts, num_tasks)\n    return tf.transpose(gates, perm=[0, 2, 1])\n\n  def call(self, inputs, **kwargs):\n    if isinstance(inputs, (list, tuple)):\n      assert len(inputs) == 2\n      expert_input, gate_input = inputs\n    else:\n      inputs = tf.convert_to_tensor(inputs)\n      expert_input = inputs\n      gate_input = inputs\n\n    # (batch, output_dim, num_experts)\n    expert_outputs = tf.stack([expert(expert_input) for expert in self.experts],\n                              axis=2)\n\n    # (batch, num_experts, num_tasks)\n    gates = self.calc_gate(gate_input)\n    if self._gate_type != 'softmax':  # add layer loss\n      # (num_experts, num_tasks)\n      importance = tf.reduce_sum(gates, axis=0)\n      # (num_tasks, )\n      mean, variance = tf.nn.moments(importance, [0])\n      cv_square = variance / tf.square(mean)\n      self.add_loss(cv_square)\n\n    # (batch, output_dim, num_tasks)\n    mmoe_output = tf.matmul(expert_outputs, gates)\n\n    # (batch, output_dim) * num_tasks\n    final_outputs = tf.unstack(mmoe_output, axis=2)\n\n    return final_outputs\n\n  def get_config(self):\n    config = {\n        'num_tasks':\n            self._num_tasks,\n        'num_experts':\n            self._num_experts,\n        'expert_output_dims':\n            self._expert_output_dims,\n        \"expert_activations\": [\n            ad_acts.serialize(act) for act in self._expert_activations\n        ],\n        \"expert_initializers\": [\n            tf.initializers.serialize(init)\n            for init in self._expert_initializers\n        ],\n        'gate_type':\n            self._gate_type,\n        'top_k':\n            self._top_k,\n        \"use_weight_norm\":\n            self.use_weight_norm,\n        \"use_learnable_weight_norm\":\n            self.use_learnable_weight_norm,\n        \"enable_batch_normalization\":\n            self.enable_batch_normalization,\n        \"batch_normalization_momentum\":\n            self.batch_normalization_momentum,\n        \"use_bias\":\n            self.use_bias,\n        'kernel_regularizer':\n            regularizers.serialize(self.kernel_regularizer),\n        'bias_regularizer':\n            regularizers.serialize(self.bias_regularizer),\n        'batch_normalization_renorm':\n            self.batch_normalization_renorm,\n        'batch_normalization_renorm_clipping':\n            self.batch_normalization_renorm_clipping,\n        'batch_normalization_renorm_momentum':\n            self.batch_normalization_renorm_momentum\n    }\n    base_config = super(MMoE, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@tf.custom_gradient\ndef hard_concrete_ste(x):\n  out = tf.minimum(1.0, tf.maximum(x, 0.0))\n\n  def grad(dy):\n    return dy\n\n  return out, grad\n\n\n@monolith_export\n@with_params\nclass SNR(Layer):\n  \"\"\"SNR (Sub-Network Routing) 是为了解决多任务学习(MTL)中任务之间相关性不大导致出现训练效果不好(Negative Transfer)而提出的\n      一种灵活共享参数的方法, 论文可参考: https://ojs.aaai.org/index.php/AAAI/article/view/3788\n  \n  Args:\n    num_out_subnet (:obj:`int`): 表示Sub_Network (Expert) 输出的个数\n    out_subnet_dim (:obj:`int`): 表示Sub_Network (Expert) 输出的维度\n    snr_type (:obj:`str`): 表示Sub_Networks之前的连接的结构, 可以在 ('trans', 'aver'), 默认使用 'trans'\n    zeta (:obj:`float`): 表示改变Conrete分布范围的上界\n    gamma (:obj:`float`): 表示改变Conrete分布范围的下界\n    beta (:obj:`float`): 表示Concrete分布的温度因子, 用于决定分布的平滑程度\n    use_ste: (:obj:`bool`): 表示是否使用STE (Straight-Through Estimator), 默认为False\n    mode (:obj:`str`): 表示tf.esitimator.Estimator的模式, 默认是训练模式\n    initializer (:obj:`str`): 表示参数W的初始化器, 配合 'trans' 结构默认使用glorot_uniform\n    regularizer (:obj:`tf.regularizer`): 表示参数W的正则化\n  \"\"\"\n\n  def __init__(self,\n               num_out_subnet: int,\n               out_subnet_dim: int,\n               snr_type: str = 'trans',\n               zeta: float = 1.1,\n               gamma: float = -0.1,\n               beta: float = 0.5,\n               use_ste: bool = False,\n               mode: str = tf.estimator.ModeKeys.TRAIN,\n               initializer='glorot_uniform',\n               regularizer=None,\n               **kwargs):\n    assert snr_type in {'trans', 'aver'}\n    self._mode = mode\n    self._num_out_subnet = num_out_subnet\n    self._out_subnet_dim = out_subnet_dim\n    self._num_in_subnet = None\n    self._in_subnet_dim = None\n\n    self._snr_type = snr_type\n    self._weight = None\n    self._log_alpha = None\n    self._beta = beta\n    self._zeta = zeta\n    self._gamma = gamma\n    self._use_ste = use_ste\n    self._mode = mode\n\n    self._initializer = initializers.get(initializer)\n    self._regularizer = regularizers.get(regularizer)\n    super(SNR, self).__init__(**kwargs)\n\n  def build(self, input_shape):\n    assert isinstance(input_shape, (list, tuple))\n    self._num_in_subnet = len(input_shape)\n    in_subnet_dim = 0\n    for i, shape in enumerate(input_shape):\n      last_dim = shape[-1]\n      if not isinstance(last_dim, int):\n        last_dim = last_dim.value\n\n      if i == 0:\n        in_subnet_dim = last_dim\n      else:\n        assert in_subnet_dim == last_dim\n    self._in_subnet_dim = in_subnet_dim\n\n    num_route = self._num_in_subnet * self._num_out_subnet\n    block_size = self._in_subnet_dim * self._out_subnet_dim\n\n    self._log_alpha = self.add_weight(name='log_alpha',\n                                      shape=(num_route, 1),\n                                      initializer=initializers.Zeros(),\n                                      trainable=True)\n\n    factor = self._beta * math.log(-self._gamma / self._zeta)\n    l0_loss = tf.reduce_sum(tf.sigmoid(self._log_alpha - factor))\n    self.add_loss(l0_loss)\n\n    if self._snr_type == 'trans':\n      self._weight = self.add_weight(name='weight',\n                                     dtype=tf.float32,\n                                     shape=(num_route, block_size),\n                                     initializer=self._initializer,\n                                     regularizer=self._regularizer,\n                                     trainable=True)\n    else:\n      assert self._snr_type == 'aver' and self._in_subnet_dim == self._out_subnet_dim\n      self._weight = tf.tile(tf.reshape(tf.eye(self._in_subnet_dim),\n                                        shape=(1, block_size)),\n                             multiples=(num_route, 1))\n\n    super(SNR, self).build(input_shape)\n\n  def sample(self):\n    if self._mode != tf.estimator.ModeKeys.PREDICT:\n      num_route = self._num_in_subnet * self._num_out_subnet\n\n      u = tf.random.uniform(shape=(num_route, 1), minval=0, maxval=1)\n      s = tf.sigmoid((tf.math.log(u) - tf.math.log(1.0 - u) + self._log_alpha) /\n                     self._beta)\n    else:\n      s = tf.sigmoid(self._log_alpha)\n\n    s_ = s * (self._zeta - self._gamma) + self._gamma\n\n    if self._use_ste:\n      z = hard_concrete_ste(s_)\n    else:\n      z = tf.minimum(1.0, tf.maximum(s_, 0.0))\n\n    return z\n\n  def call(self, inputs, **kwargs):\n    z = self.sample()\n    weight = tf.multiply(self._weight, z)\n\n    shape1 = (self._num_in_subnet, self._num_out_subnet, self._in_subnet_dim,\n              self._out_subnet_dim)\n    shape2 = (self._num_in_subnet * self._in_subnet_dim,\n              self._num_out_subnet * self._out_subnet_dim)\n    weight = tf.reshape(\n        tf.transpose(tf.reshape(weight, shape1), perm=[0, 2, 1, 3]), shape2)\n\n    return tf.split(tf.matmul(tf.concat(inputs, axis=1), weight),\n                    num_or_size_splits=self._num_out_subnet,\n                    axis=1)\n\n  def get_config(self):\n    config = {\n        'num_out_subnet': self._num_out_subnet,\n        'out_subnet_dim': self._out_subnet_dim,\n        'snr_type': self._snr_type,\n        'zeta': self._zeta,\n        'gamma': self._gamma,\n        'beta': self._beta,\n        'use_ste': self._use_ste,\n        'mode': self._mode,\n        'initializer': initializers.serialize(self._initializer),\n        'regularizer': regularizers.serialize(self._regularizer)\n    }\n\n    base_config = super(SNR, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n"
  },
  {
    "path": "monolith/native_training/layers/multi_task_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\n\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.multi_task import MMoE, SNR\n\n\nclass MultiTaskTest(tf.test.TestCase):\n\n  def test_mmoe_instantiate(self):\n    mmoe_layer_template = MMoE.params()\n\n    test_params0 = mmoe_layer_template.copy()\n    test_params0.name = 'test_mmoe'\n    test_params0.num_tasks = 2\n    test_params0.num_experts = 3\n    test_params0.expert_output_dims = [128, 64, 64]\n    test_params0.expert_activations = 'relu'\n    test_params0.expert_initializers = tf.keras.initializers.GlorotNormal()\n    mmoe1 = test_params0.instantiate()\n    print(mmoe1)\n\n    mmoe2 = MMoE(num_tasks=2,\n                 num_experts=3,\n                 expert_output_dims=[128, 64, 64],\n                 expert_activations='relu',\n                 expert_initializers=tf.keras.initializers.GlorotNormal())\n    print(mmoe2)\n\n  def test_mmoe_serde(self):\n    mmoe_layer_template = MMoE.params()\n\n    test_params0 = mmoe_layer_template.copy()\n    test_params0.name = 'test_mmoe'\n    test_params0.num_tasks = 2\n    test_params0.num_experts = 3\n    test_params0.expert_output_dims = [128, 64, 64]\n    test_params0.expert_activations = 'relu'\n    test_params0.expert_initializers = tf.keras.initializers.GlorotNormal()\n    mmoe1 = test_params0.instantiate()\n\n    cfg = mmoe1.get_config()\n    mmoe2 = MMoE.from_config(cfg)\n\n    print(mmoe1, mmoe2)\n\n  def test_mmoe_call(self):\n    layer = MMoE(num_tasks=2,\n                 num_experts=3,\n                 gate_type='topk',\n                 top_k=2,\n                 expert_output_dims=[[128, 64, 64], [64, 64], [128, 64]],\n                 expert_activations='relu',\n                 expert_initializers=tf.keras.initializers.GlorotNormal())\n\n    dense_data = tf.keras.backend.variable(np.ones((100, 128)))\n    # mmoe_data = tf.keras.backend.variable(np.ones((100, 64)))\n    # sum_out = tf.reduce_sum(layer([dense_data, mmoe_data]))\n    sum_out = tf.reduce_sum(layer(dense_data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_snr_instantiate(self):\n    snr_layer_template = SNR.params()\n\n    test_params0 = snr_layer_template.copy()\n    test_params0.name = 'test_snr'\n    test_params0.num_out_subnet = 3\n    test_params0.out_subnet_dim = 128\n    test_params0.use_ste = False\n    snr1 = test_params0.instantiate()\n    print(snr1)\n\n    snr2 = SNR(num_out_subnet=3, out_subnet_dim=128, use_ste=False)\n    print(snr2)\n\n  def test_snr_serde(self):\n    snr_layer_template = SNR.params()\n\n    test_params0 = snr_layer_template.copy()\n    test_params0.name = 'test_snr'\n    test_params0.num_out_subnet = 3\n    test_params0.out_subnet_dim = 128\n    test_params0.use_ste = False\n    snr1 = test_params0.instantiate()\n    print(snr1)\n\n    cfg = snr1.get_config()\n    snr2 = SNR.from_config(cfg)\n\n    print(snr1, snr2)\n\n  def test_snr_call(self):\n    layer = SNR(num_out_subnet=3,\n                out_subnet_dim=128,\n                snr_type='aver',\n                use_ste=False,\n                mode=tf.estimator.ModeKeys.PREDICT)\n\n    snr_data1 = tf.keras.backend.variable(np.ones((100, 128)))\n    snr_data2 = tf.keras.backend.variable(np.ones((100, 128)))\n    snr_data3 = tf.keras.backend.variable(np.ones((100, 128)))\n    snr_data4 = tf.keras.backend.variable(np.ones((100, 128)))\n\n    sum_out = tf.reduce_sum(layer([snr_data1, snr_data2, snr_data3, snr_data4]))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/norms.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nimport tensorflow.keras.initializers as initializers\nfrom tensorflow.python.keras import regularizers\nfrom tensorflow.keras.layers import Layer, InputSpec\n\nfrom monolith.core.base_layer import add_layer_loss\nfrom monolith.native_training.utils import with_params\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.layers.utils import check_dim, dim_size\n\n\n@with_params\nclass BatchNorm(Layer):\n\n  def __init__(self,\n               momentum=0.99,\n               center=True,\n               scale=True,\n               moving_mean_initializer=initializers.Zeros(),\n               moving_variance_initializer=initializers.Ones(),\n               beta_initializer=initializers.Zeros(),\n               gamma_initializer=initializers.Ones(),\n               regularizer=None,\n               training_use_global_dist=False,\n               global_dist_momentum=1.0,\n               stop_grad_of_var_mean=False,\n               epsilon=1e-6,\n               mode=tf.estimator.ModeKeys.TRAIN,\n               **kwargs):\n    super(BatchNorm, self).__init__(**kwargs)\n\n    self.momentum = momentum\n    self.epsilon = epsilon\n    self.center = center\n    self.scale = scale\n    self.beta_initializer = initializers.get(beta_initializer)\n    self.gamma_initializer = initializers.get(gamma_initializer)\n    self.moving_mean_initializer = initializers.get(moving_mean_initializer)\n    self.moving_variance_initializer = initializers.get(\n        moving_variance_initializer)\n    self.training_use_global_dist = training_use_global_dist\n    self.global_dist_momentum = global_dist_momentum\n    self.stop_grad_of_var_mean = stop_grad_of_var_mean\n    self.mode = mode\n    self.regularizer = regularizers.get(regularizer)\n\n    self.input_spec = InputSpec(min_ndim=2)\n\n  def build(self, input_shape):\n    assert len(input_shape) >= 2\n    self.input_dim = check_dim(input_shape[-1])\n\n    self.moving_mean = self.add_weight(name='moving_mean',\n                                       shape=[self.input_dim],\n                                       dtype=self.dtype,\n                                       initializer=self.moving_mean_initializer)\n    self.moving_variance = self.add_weight(\n        name='moving_variance',\n        dtype=self.dtype,\n        shape=[self.input_dim],\n        initializer=self.moving_variance_initializer)\n\n    if self.center:\n      self.beta_offset = self.add_weight(name='beta_offset',\n                                         dtype=self.dtype,\n                                         shape=[self.input_dim],\n                                         initializer=self.beta_initializer,\n                                         regularizer=self.regularizer)\n    else:\n      self.beta_offset = tf.constant(0.0, dtype=tf.float32)\n    if self.scale:\n      self.gamma_scale = self.add_weight(name='gamma_scale',\n                                         dtype=self.dtype,\n                                         shape=[self.input_dim],\n                                         initializer=self.gamma_initializer,\n                                         regularizer=self.regularizer)\n    else:\n      self.gamma_scale = tf.constant(1.0, dtype=tf.float32)\n\n    self.input_spec = InputSpec(min_ndim=2, axes={-1: self.input_dim})\n    super(BatchNorm, self).build(input_shape)\n\n  def call(self, inputs, **kwargs):\n\n    @tf.custom_gradient\n    def replace_gradient(original_moving_average, gradient):\n\n      def grad(dy):\n        return gradient, None\n\n      return tf.identity(original_moving_average), grad\n\n    if self.mode == tf.estimator.ModeKeys.TRAIN:\n      if self.stop_grad_of_var_mean:\n        self.mean, self.variance = tf.nn.moments(tf.stop_gradient(inputs),\n                                                 axes=[0])\n      else:\n        self.mean, self.variance = tf.nn.moments(inputs, axes=[0])\n      # replace moving average gradient by mean & variance in current minibatch\n\n      moving_mean = replace_gradient(self.moving_mean, self.mean)\n      moving_variance = replace_gradient(self.moving_variance, self.variance)\n      moving_variance = tf.maximum(moving_variance,\n                                   tf.constant(0, dtype=tf.float32))\n      add_layer_loss('{}_moving_mean'.format(self.name),\n                     tf.reduce_sum(moving_mean))\n      add_layer_loss('{}_moving_variance'.format(self.name),\n                     tf.reduce_sum(moving_variance))\n\n      if self.training_use_global_dist:\n        mean = self.global_dist_momentum * moving_mean + \\\n               (1.0 - self.global_dist_momentum) * self.mean\n        variance = self.global_dist_momentum * moving_variance + \\\n                   (1.0 - self.global_dist_momentum) * self.variance\n      else:\n        mean, variance = self.mean, self.variance\n\n      tf.compat.v1.summary.histogram(self.name + '/mean_train', mean)\n      tf.compat.v1.summary.scalar(self.name + '/mean_train',\n                                  tf.reduce_mean(mean))\n      tf.compat.v1.summary.histogram(self.name + '/var_train', variance)\n      tf.compat.v1.summary.scalar(self.name + '/var_train',\n                                  tf.reduce_mean(variance))\n\n    else:\n      moving_variance = tf.maximum(self.moving_variance,\n                                   tf.constant(0, dtype=tf.float32))\n      mean, variance = tf.stop_gradient(\n          self.moving_mean), tf.stop_gradient(moving_variance)\n      tf.compat.v1.summary.histogram(self.name + '/mean_test', mean)\n      tf.compat.v1.summary.scalar(self.name + '/mean_test',\n                                  tf.reduce_mean(mean))\n      tf.compat.v1.summary.histogram(self.name + '/var_test', variance)\n      tf.compat.v1.summary.scalar(self.name + '/var_test',\n                                  tf.reduce_mean(variance))\n\n    output = tf.nn.batch_normalization(inputs, mean, variance, self.beta_offset,\n                                       self.gamma_scale, self.epsilon)\n    return output\n\n  def set_use_global_dist(self, training_use_global_dist):\n    assert type(training_use_global_dist) is bool\n    self.training_use_global_dist = training_use_global_dist\n\n  def get_config(self):\n    config = {\n        'momentum':\n            self.momentum,\n        'epsilon':\n            self.epsilon,\n        'center':\n            self.center,\n        'scale':\n            self.scale,\n        'beta_initializer':\n            initializers.serialize(self.beta_initializer),\n        'gamma_initializer':\n            initializers.serialize(self.gamma_initializer),\n        'moving_mean_initializer':\n            initializers.serialize(self.moving_mean_initializer),\n        'moving_variance_initializer':\n            initializers.serialize(self.moving_variance_initializer),\n        'training_use_global_dist':\n            self.training_use_global_dist,\n        'global_dist_momentum':\n            self.global_dist_momentum,\n        'stop_grad_of_var_mean':\n            self.stop_grad_of_var_mean,\n        'mode':\n            self.mode,\n        'regularizer':\n            regularizers.serialize(self.regularizer),\n    }\n    base_config = super(BatchNorm, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@monolith_export\n@with_params\nclass LayerNorm(Layer):\n  \"\"\"与BatchNorm类似, 但是是在样本内做归一化. \n\n  与BatchNorm不同的是LayerNorm在训练与推理时, 使用同一套逻辑, 不用分别处理\n\n  Args:\n    initializer (:obj:`tf.initializer`): gamma的初始化器\n    regularizer (:obj:`tf.regularizer`): beta/gamma的变量的正则化\n\n  \"\"\"\n\n  def __init__(self, initializer, regularizer=None, **kwargs):\n    super(LayerNorm, self).__init__(**kwargs)\n    self.beta, self.gamma = None, None\n    self.initializer = initializers.get(initializer) or initializers.Ones()\n    self.regularizer = regularizers.get(regularizer)\n\n  def build(self, input_shape):\n    params_shape = [check_dim(input_shape[-1])]\n    self.beta = self.add_weight(name='beta',\n                                dtype=tf.float32,\n                                shape=params_shape,\n                                initializer=initializers.Zeros(),\n                                regularizer=self.regularizer)\n    self.gamma = self.add_weight(name='gamma',\n                                 dtype=tf.float32,\n                                 shape=params_shape,\n                                 initializer=self.initializer,\n                                 regularizer=self.regularizer)\n    super(LayerNorm, self).build(input_shape)\n\n  def call(self, inputs, **kwargs):\n    mean, variance = tf.nn.moments(inputs, [-1], keepdims=True)\n    output = tf.nn.batch_normalization(inputs,\n                                       mean,\n                                       variance,\n                                       self.beta,\n                                       self.gamma,\n                                       variance_epsilon=1e-6)\n\n    return output\n\n  def get_config(self):\n    config = {\n        'initializer': initializers.serialize(self.initializer),\n        'regularizer': regularizers.serialize(self.regularizer),\n    }\n    base_config = super(LayerNorm, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n\n\n@monolith_export\n@with_params\nclass GradNorm(Layer):\n  \"\"\"GradNorm提出通过将不同任务的梯度控制在一定的范围来进行多任务学习, 论文可参考 https://arxiv.org/abs/1711.02257\n\n  GradNorm是通过构造辅助loss实现, 辅助的构造过程如下:\n    - 选择shared bottom的最顶层变量W, 然后计算每个head对它的梯度 (如果顶层有多个W, 则分别计算梯度, 再concat起来)\n    - 对上一步得到的梯度取L2范数, 得到gnorms, gnorms是一个n维向量, 长度与task的个数相同\n    - gnorms加权weight, 得到wgnorms,  wgnorms平均, 得到avgnorm\n    - gnorm_loss = scale * sum([(wgnorms - avgnorm) / (avgnorm + epsilon)]^loss_pow), relative_diff = True\n    - gnorm_loss = scale * sum([wgnorms - avgnorm]^loss_pow), relative_diff = False\n    - weighted_loss = sum(weight * losses)\n\n  Args:\n    loss_names (:obj:`str`): loss名称, 用于确定loss的个数, 写相关日志\n    scale (:obj:`float`): 缩放因子, 用于缩放\n    loss_pow (:obj:`float`): gnorm diff的指数因子\n    relative_diff (:obj:`bool`): gnorm diff的计算方式, 如果为True, 会计算相对值\n    epsilon (:obj:`float`): 一个非常小的常数, 防止除以0\n  \n  \"\"\"\n\n  def __init__(self,\n               loss_names,\n               scale=1.0,\n               loss_pow=2.0,\n               relative_diff=False,\n               epsilon=1e-6,\n               **kwargs):\n    super(GradNorm, self).__init__(**kwargs)\n    self.loss_names = loss_names\n    self.scale = scale\n    self.loss_pow = loss_pow\n    self.relative_diff = relative_diff\n    self.epsilon = epsilon\n\n  def build(self, input_shape):\n    n = len(self.loss_names)\n    self.weight = self.add_weight(name='grad_norm_weights',\n                                  shape=[n],\n                                  dtype=tf.float32,\n                                  initializer=tf.initializers.Zeros())\n    self._weights = tf.nn.softmax(self.weight)\n\n    for i in range(n):\n      tf.compat.v1.summary.scalar(\n          'gradnorm_weight/{}'.format(self.loss_names[i]), self._weights[i])\n\n    super(GradNorm, self).build(input_shape)\n\n  def _get_norm(self, grad):\n    return (tf.reduce_sum(tf.multiply(grad, grad)))**0.5\n\n  def get_weights(self):\n    return self._weights\n\n  def call(self, inputs, **kwargs):\n    losses, shared_inputs = inputs\n    if not isinstance(shared_inputs, list):\n      shared_inputs = [shared_inputs]\n\n    grads = [tf.gradients(loss, shared_inputs) for loss in losses]\n    grads = [tf.concat(gs, axis=1) for gs in grads]\n\n    gnorms = [self._get_norm(g) for g in grads]\n    gnorms = tf.stop_gradient(tf.stack(gnorms, axis=0))\n\n    weights = self._weights\n    n = len(self.loss_names)\n\n    avgnorm = tf.reduce_sum(gnorms * weights) / n\n    wgnorms = gnorms * weights\n\n    grad_diff = tf.abs(wgnorms - avgnorm)\n    if self.relative_diff:\n      grad_diff = grad_diff / (avgnorm + self.epsilon)\n\n    gnorm_loss = tf.reduce_sum(grad_diff**self.loss_pow) * self.scale\n    weighted_loss = tf.reduce_sum(\n        tf.stack(losses, axis=0) * tf.stop_gradient(weights))\n\n    for i in range(n):\n      tf.compat.v1.summary.scalar(\n          'gradnorm_gnorm/{}'.format(self.loss_names[i]), gnorms[i])\n      tf.compat.v1.summary.scalar(\n          'gradnorm_wgnorm/{}'.format(self.loss_names[i]), wgnorms[i])\n\n    return gnorm_loss, weighted_loss\n\n  def get_config(self):\n    config = {\n        'loss_names': self.loss_names,\n        'scale': self.scale,\n        'loss_pow': self.loss_pow,\n        'relative_diff': self.relative_diff,\n        'epsilon': self.epsilon\n    }\n    base_config = super(GradNorm, self).get_config()\n    return dict(list(base_config.items()) + list(config.items()))\n"
  },
  {
    "path": "monolith/native_training/layers/norms_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\n\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.norms import LayerNorm, GradNorm\n\n\nclass NormTest(tf.test.TestCase):\n\n  def test_ln_instantiate(self):\n    layer_template = LayerNorm.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.initializer = tf.keras.initializers.GlorotNormal()\n    bn1 = test_params0.instantiate()\n    print(bn1)\n\n    bn2 = LayerNorm(initializer=tf.keras.initializers.HeUniform())\n    print(bn2)\n\n  def test_ln_serde(self):\n    layer_template = LayerNorm.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.initializer = tf.keras.initializers.GlorotNormal()\n    bn1 = test_params0.instantiate()\n    print(bn1)\n\n    cfg = bn1.get_config()\n    bn2 = LayerNorm.from_config(cfg)\n\n    print(bn1, bn2)\n\n  def test_ln_call(self):\n    bn = LayerNorm(initializer=tf.keras.initializers.HeUniform())\n\n    data = tf.keras.backend.variable(np.ones((100, 100)))\n    sum_out = tf.reduce_sum(bn(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_gn_instantiate(self):\n    layer_template = GradNorm.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.loss_names = [\"abc\", 'defg']\n    bn1 = test_params0.instantiate()\n    print(bn1)\n\n    bn2 = GradNorm(loss_names=[\"abc\", 'defg'], relative_diff=True)\n    print(bn2)\n\n  def test_gn_serde(self):\n    layer_template = GradNorm.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.loss_names = [\"abc\", 'defg']\n    bn1 = test_params0.instantiate()\n    print(bn1)\n\n    cfg = bn1.get_config()\n    bn2 = GradNorm.from_config(cfg)\n\n    print(bn1, bn2)\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/ops/feature_insight_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\n\nREGISTER_OP(\"FeatureInsight\")\n    .Input(\"input: float\")\n    .Input(\"weight: float\")\n    .Output(\"output: float\")\n    .Attr(\"segment_sizes: list(int)\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      std::vector<int32> segment_sizes;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"segment_sizes\", &segment_sizes));\n      auto batch_size = ctx->Dim(ctx->input(0), 0);\n      shape_inference::DimensionHandle out_dims;\n      TF_RETURN_IF_ERROR(ctx->Multiply(ctx->MakeDim(segment_sizes.size()),\n                                       ctx->Dim(ctx->input(1), 1), &out_dims));\n\n      ctx->set_output(0, ctx->Matrix(batch_size, out_dims));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"FeatureInsightGrad\")\n    .Input(\"grad: float\")\n    .Input(\"input: float\")\n    .Input(\"weight: float\")\n    .Output(\"input_grad: float\")\n    .Output(\"weight_grad: float\")\n    .Attr(\"segment_sizes: list(int)\")\n    .Attr(\"K: int\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(1));\n      ctx->set_output(1, ctx->input(2));\n      return Status::OK();\n    });\n\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/layers/ops/ffm_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\n\nREGISTER_OP(\"FFM\")\n    .Input(\"left: float\")\n    .Input(\"right: float\")\n    .Output(\"output: float\")\n    .Attr(\"dim_size: int\")\n    .Attr(\"int_type: string\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      int dim_size;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"dim_size\", &dim_size));\n      auto batch_size = ctx->Dim(ctx->input(0), 0);\n\n      std::string int_type;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"int_type\", &int_type));\n\n      shape_inference::DimensionHandle tmp_dims;\n      ctx->Multiply(ctx->DimKnownRank(ctx->input(0), 1),\n                    ctx->DimKnownRank(ctx->input(1), 1), &tmp_dims);\n\n      shape_inference::DimensionHandle out_dims;\n      if (int_type == \"dot\") {\n        ctx->Divide(tmp_dims, dim_size * dim_size, true, &out_dims);\n      } else {\n        ctx->Divide(tmp_dims, dim_size, true, &out_dims);\n      }\n\n      ctx->set_output(0, ctx->Matrix(batch_size, out_dims));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"FFMGrad\")\n    .Input(\"grad: float\")\n    .Input(\"left: float\")\n    .Input(\"right: float\")\n    .Output(\"left_grad: float\")\n    .Output(\"right_grad: float\")\n    .Attr(\"dim_size: int\")\n    .Attr(\"int_type: string\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(1));\n      ctx->set_output(1, ctx->input(2));\n      return Status::OK();\n    });\n\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/layers/ops/fid_counter_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\n\nREGISTER_OP(\"MonolithFidCounter\")\n    .Input(\"counter: float\")\n    .Output(\"output: float\")\n    .Attr(\"step: float\")\n    .Attr(\"counter_threshold: int\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/layers/ops/nas_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\n\nREGISTER_OP(\"BernoulliGate\")\n    .Input(\"alpha: float\")\n    .Output(\"sampled: float\")\n    .Output(\"proba: float\")\n    .Attr(\"ste_type: string\")\n    .Attr(\"use_logistic: bool\")\n    .Attr(\"temperature: float\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"BernoulliGateGrad\")\n    .Input(\"grad: float\")\n    .Input(\"alpha: float\")\n    .Input(\"proba: float\")\n    .Output(\"output: float\")\n    .Attr(\"ste_type: string\")\n    .Attr(\"use_logistic: bool\")\n    .Attr(\"temperature: float\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"DiscreteGate\")\n    .Input(\"alpha: float\")\n    .Output(\"sampled: float\")\n    .Output(\"proba: float\")\n    .Attr(\"is_one_hot: bool\")\n    .Attr(\"use_gumbel: bool\")\n    .Attr(\"temperature: float\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      ctx->set_output(1, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"DiscreteGateGrad\")\n    .Input(\"grad: float\")\n    .Input(\"sampled: float\")\n    .Input(\"proba: float\")\n    .Output(\"output: float\")\n    .Attr(\"is_one_hot: bool\")\n    .Attr(\"temperature: float\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"DiscreteTruncatedGate\")\n    .Input(\"alpha: float\")\n    .Output(\"sampled: float\")\n    .Output(\"proba: float\")\n    .Attr(\"threshold: float\")\n    .Attr(\"drop_first_dim: bool\")\n    .Attr(\"use_gumbel: bool\")\n    .Attr(\"temperature: float\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      bool drop_first_dim;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"drop_first_dim\", &drop_first_dim));\n\n      if (drop_first_dim) {\n        shape_inference::DimensionHandle alpha_dim = ctx->Dim(ctx->input(0), 0);\n        shape_inference::DimensionHandle sampled_dim;\n        ctx->Subtract(alpha_dim, 1, &sampled_dim);\n\n        ctx->set_output(0, ctx->Vector(sampled_dim));\n      } else {\n        ctx->set_output(0, ctx->input(0));\n      }\n\n      ctx->set_output(1, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"DiscreteTruncatedGateGrad\")\n    .Input(\"grad: float\")\n    .Input(\"sampled: float\")\n    .Input(\"proba: float\")\n    .Output(\"output: float\")\n    .Attr(\"threshold: float\")\n    .Attr(\"drop_first_dim: bool\")\n    .Attr(\"temperature: float\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(2));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"NasArchWeight\")\n    .Input(\"arch_weight: float\")\n    .Input(\"global_step: float\")\n    .Output(\"arch_output: float\")\n    .Attr(\"update_rate: float\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"BinaryMaskToSlotWeight\")\n    .Input(\"weight: float\")\n    .Input(\"mask: int32\")\n    .Input(\"shape: int32\")\n    .Output(\"output: float\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->Vector(ctx->UnknownDim()));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"BinaryMaskToSlotWeightGrad\")\n    .Input(\"grad: float\")\n    .Input(\"mask: int32\")\n    .Input(\"shape: int32\")\n    .Output(\"output: float\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->Vector(ctx->UnknownDim()));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"SegmentDiscreteGate\")\n    .Input(\"proba: float\")\n    .Input(\"segment_sizes: int32\")\n    .Output(\"output: float\")\n    .Attr(\"use_gumbel: bool\")\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/layers/pooling.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom tensorflow.keras.layers import Layer\nfrom tensorflow.python.ops import math_ops\nfrom tensorflow.python.ops import array_ops\n\nfrom monolith.native_training.utils import with_params, check_list\nfrom monolith.native_training.monolith_export import monolith_export\n\n\n@monolith_export\nclass Pooling(Layer):\n  \"\"\"Pooling基类\n\n  Args:\n    kwargs (:obj:`dict`): 其它位置参数, 详情请参考 `TF Layer`_\n    \n  .. _TF Layer: https://www.tensorflow.org/api_docs/python/tf/keras/layers/Layer\n    \n  \"\"\"\n\n  def __init__(self, **kwargs):\n    super(Pooling, self).__init__(**kwargs)\n\n  def pool(self, vec_list):\n    raise NotImplementedError\n\n  def call(self, vec_list, **kwargs):\n    check_list(vec_list, lambda x: x > 0)\n    if len(vec_list) == 1:\n      return vec_list[0]\n    return self.pool(vec_list)\n\n\n@monolith_export\n@with_params\nclass SumPooling(Pooling):\n  \"\"\"Sum pooling, 加法池化\n\n  Args:\n    kwargs (:obj:`dict`): 其它位置参数, 详情请参考 `TF Layer`_\n    \n  .. _TF Layer: https://www.tensorflow.org/api_docs/python/tf/keras/layers/Layer\n  \n  \"\"\"\n\n  def __init__(self, **kwargs):\n    super(SumPooling, self).__init__(**kwargs)\n\n  def pool(self, vec_list):\n    return math_ops.add_n(vec_list)\n\n\n@monolith_export\n@with_params\nclass AvgPooling(Pooling):\n  \"\"\"Average pooling, 平匀池化\n\n  Args:\n    kwargs (:obj:`dict`): 其它位置参数, 详情请参考 `TF Layer`_\n    \n  .. _TF Layer: https://www.tensorflow.org/api_docs/python/tf/keras/layers/Layer\n  \n  \"\"\"\n\n  def __init__(self, **kwargs):\n    super(AvgPooling, self).__init__(**kwargs)\n\n  def pool(self, vec_list):\n    return math_ops.add_n(vec_list) / len(vec_list)\n\n\n@monolith_export\n@with_params\nclass MaxPooling(Pooling):\n  \"\"\"Max pooling, 最大池化\n\n  Args:\n    kwargs (:obj:`dict`): 其它位置参数, 详情请参考 `TF Layer`_\n    \n  .. _TF Layer: https://www.tensorflow.org/api_docs/python/tf/keras/layers/Layer\n  \n  \"\"\"\n\n  def __init__(self, **kwargs):\n    super(MaxPooling, self).__init__(**kwargs)\n\n  def pool(self, vec_list):\n    return math_ops.reduce_max(array_ops.stack(vec_list), axis=0)\n"
  },
  {
    "path": "monolith/native_training/layers/pooling_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\n\nimport tensorflow as tf\n\nfrom monolith.native_training.layers.pooling import *\n\n\nclass PoolingTest(tf.test.TestCase):\n\n  def test_sp_instantiate(self):\n    layer_template = SumPooling.params()\n\n    test_params0 = layer_template.copy()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = SumPooling()\n    print(ins2)\n\n  def test_sp_serde(self):\n    layer_template = SumPooling.params()\n\n    test_params0 = layer_template.copy()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    cfg = ins1.get_config()\n    ins2 = SumPooling.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_sp_call(self):\n    layer_template = SumPooling.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.name = 'test_dense0'\n    layer = test_params0.instantiate()\n\n    data = [\n        tf.keras.backend.variable(np.random.uniform(size=(100, 10)))\n        for _ in range(5)\n    ]\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_mp_instantiate(self):\n    layer_template = MaxPooling.params()\n\n    test_params0 = layer_template.copy()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = MaxPooling()\n    print(ins2)\n\n  def test_mp_serde(self):\n    layer_template = MaxPooling.params()\n\n    test_params0 = layer_template.copy()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    cfg = ins1.get_config()\n    ins2 = MaxPooling.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_mp_call(self):\n    layer_template = MaxPooling.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.name = 'test_dense0'\n    layer = test_params0.instantiate()\n\n    data = [\n        tf.keras.backend.variable(np.random.uniform(size=(100, 10)))\n        for _ in range(5)\n    ]\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n  def test_ap_instantiate(self):\n    layer_template = AvgPooling.params()\n\n    test_params0 = layer_template.copy()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    ins2 = AvgPooling()\n    print(ins2)\n\n  def test_ap_serde(self):\n    layer_template = AvgPooling.params()\n\n    test_params0 = layer_template.copy()\n    ins1 = test_params0.instantiate()\n    print(ins1)\n\n    cfg = ins1.get_config()\n    ins2 = AvgPooling.from_config(cfg)\n\n    print(ins1, ins2)\n\n  def test_ap_call(self):\n    layer_template = AvgPooling.params()\n\n    test_params0 = layer_template.copy()\n    test_params0.name = 'test_dense0'\n    layer = test_params0.instantiate()\n\n    data = [\n        tf.keras.backend.variable(np.random.uniform(size=(100, 10)))\n        for _ in range(5)\n    ]\n    sum_out = tf.reduce_sum(layer(data))\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      print(sess.run(sum_out))\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/sparse_nas.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom glob import glob\nimport os, re\nfrom absl import logging, flags\nimport numpy as np\nfrom typing import List\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.keras.layers import Layer\nfrom tensorflow.python.keras import initializers\nfrom tensorflow.python.keras.engine.input_spec import InputSpec\n\nfrom monolith.native_training.utils import with_params\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.layers.utils import check_dim, dim_size\nfrom monolith.native_training.data.feature_list import FeatureList\nfrom monolith.native_training.summary.utils import SummaryType\n"
  },
  {
    "path": "monolith/native_training/layers/sparse_nas_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport numpy as np\n\nimport tensorflow as tf\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/layers/utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.python.ops import array_ops\n\nfrom monolith.native_training.monolith_export import monolith_export\n\n\n@monolith_export\nclass MergeType:\n  CONCAT = 'concat'\n  STACK = 'stack'\n  NONE = None\n\n\n@monolith_export\nclass DCNType:\n  Vector = \"vector\"\n  Matrix = \"matrix\"\n  Mixed = \"mixed\"\n\n\ndef check_dim(dim):\n  if dim is None:\n    return -1\n  elif isinstance(dim, int):\n    return dim\n  elif isinstance(dim, tf.compat.v1.Dimension):\n    return dim.value\n  else:\n    raise Exception(f'dim {dim} is error')\n\n\ndef dim_size(inputs, axis: int):\n  shape = inputs.get_shape().as_list()\n  assert len(shape) > axis\n  dim = check_dim(shape[axis])\n  if dim == -1:\n    return array_ops.shape(inputs)[axis]\n  else:\n    return dim\n\n\n@monolith_export\ndef merge_tensor_list(tensor_list,\n                      merge_type: str = 'concat',\n                      num_feature: int = None,\n                      axis: int = 1,\n                      keep_list: bool = False):\n  \"\"\"将Tensor列表合并\n  \n  Args:\n    tensor_list (:obj:`List[tf.Tensor]`): 输入的Tensor列表\n    merge_type (:obj:`str`): 合并类型, 支持stack/concat两种, 如果设为None, 则不做任何处理\n    num_feature (:obj:`int`): 特征个数\n    axis (:obj:`int`): merge延哪个轴进行\n    keep_list (:obj:`bool`): 输出结果是否保持list\n  \n  \"\"\"\n\n  if isinstance(tensor_list, tf.Tensor):\n    tensor_list = [tensor_list]\n  assert merge_type in {'stack', 'concat', None}\n\n  if len(tensor_list) == 1:\n    shapes = [check_dim(dim) for dim in tensor_list[0].get_shape().as_list()]\n\n    if len(shapes) == 3:\n      (batch_size, num_feat, emb_size) = shapes\n      if merge_type == MergeType.STACK:\n        output = tensor_list if keep_list else tensor_list[0]\n      elif merge_type == MergeType.CONCAT:\n        tensor_list[0] = tf.reshape(tensor_list[0],\n                                    shape=(batch_size, num_feat * emb_size))\n        output = tensor_list if keep_list else tensor_list[0]\n      else:\n        output = tf.unstack(tensor_list[0], axis=axis)\n    elif len(shapes) == 2 and num_feature is not None and num_feature > 1:\n      (batch_size, emb_size) = shapes\n      emb_size = int(emb_size / num_feature)\n      if merge_type == MergeType.STACK:\n        tensor_list[0] = tf.reshape(tensor_list[0],\n                                    shape=(batch_size, num_feature, emb_size))\n        output = tensor_list if keep_list else tensor_list[0]\n      elif merge_type == MergeType.CONCAT:\n        output = tensor_list if keep_list else tensor_list[0]\n      else:\n        tensor_list[0] = tf.reshape(tensor_list[0],\n                                    shape=(batch_size, num_feature, emb_size))\n        output = tf.unstack(tensor_list[0], axis=axis)\n    elif len(shapes) == 2:\n      output = tensor_list if keep_list else tensor_list[0]\n    else:\n      raise Exception(\"shape error: ({})\".format(', '.join(map(str, shapes))))\n  elif merge_type == 'stack':\n    stacked = tf.stack(tensor_list, axis=axis)\n    output = [stacked] if keep_list else stacked\n  elif merge_type == 'concat':\n    concated = tf.concat(tensor_list, axis=axis)\n    output = [concated] if keep_list else concated\n  else:\n    output = tensor_list\n\n  return output\n\n\nEPSILON = np.finfo(tf.float32.as_numpy_dtype).tiny\n\n# Copy from: https://github.com/ermongroup/subsets/blob/master/subsets/sample_subsets.py\n# Reparameterizable Subset Sampling via Continuous Relaxations (https://arxiv.org/pdf/1901.10517.pdf)\n# [code](https://github.com/ermongroup/subsets)\ndef gumbel_keys(w):\n    # sample some gumbels\n    uniform = tf.random_uniform(\n        tf.shape(w),\n        minval=EPSILON,\n        maxval=1.0)\n    z = -tf.log(-tf.log(uniform))\n    w = w + z\n    return w\n\n\ndef continuous_topk(w, k, t, separate=False):\n    khot_list = []\n    onehot_approx = tf.zeros_like(w, dtype=tf.float32)\n    for i in range(k):\n        khot_mask = tf.maximum(1.0 - onehot_approx, EPSILON)\n        w += tf.log(khot_mask)\n        onehot_approx = tf.nn.softmax(w / t, axis=-1)\n        khot_list.append(onehot_approx)\n    if separate:\n        return khot_list\n    else:\n        return tf.reduce_sum(khot_list, 0)\n\n\ndef sample_subset(w, k, t=0.1):\n    '''\n    Args:\n        w (Tensor): Float Tensor of weights for each element. In gumbel mode\n            these are interpreted as log probabilities\n        k (int): number of elements in the subset sample\n        t (float): temperature of the softmax\n    '''\n    w = gumbel_keys(w)\n    return continuous_topk(w, k, t)\n"
  },
  {
    "path": "monolith/native_training/learning_rate_functions.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\nimport abc\nimport tensorflow as tf\n\n\nclass LearningRateFunction():\n  \"\"\"The learning rate function base class.\n\n  You can use a learning rate function to modulate how the learning rate\n  of your optimizer changes over time.\n\n  A `LearningRateFunction` instance can be passed in as the `learning_rate`\n  argument of any dense optimizer or as `learning_rate_fn` argument for adding\n  feature slice of embedding table.\n\n  To implement your own function object, you should implement the `__call__`\n  method.\n\n  \"\"\"\n\n  @abc.abstractmethod\n  def __call__(self):\n    raise NotImplementedError(\"Learning rate function must override __call__\")\n\n  # Used to check whether two LearningRateFunctions have the same feature.\n  def __str__(self):\n    return \"LearningRateFunction(\\\"%s\\\",Params:%s)\" % (\n        self.__class__.__name__, \",\".join([\n            \"%s=%s\" % (key, self.__dict__[key]) for key in sorted(self.__dict__)\n        ]))\n\n\nclass PolynomialDecay(LearningRateFunction):\n  \"\"\"A LearningRateFunction that uses an polynomial decay schedule.\n\n  This function applies a polynomial decay function to a provided\n  `initial_learning_rate` to reach an `end_learning_rate` in the given `decay_steps`.\n\n  \"\"\"\n\n  def __init__(self,\n               initial_learning_rate,\n               decay_steps,\n               end_learning_rate=0.0001,\n               power=1.0,\n               cycle=False,\n               name=None):\n    \"\"\"Applies polynomial decay to the learning rate.\n\n    Args:\n      initial_learning_rate: A scalar `float32` or `float64` `Tensor` or a Python\n        number. The initial learning rate.\n      decay_steps: A scalar `int32` or `int64` `Tensor` or a Python number. Must\n        be positive.  See the decay computation above.\n      end_learning_rate: A scalar `float32` or `float64` `Tensor` or a Python\n        number.  The minimal end learning rate.\n      power: A scalar `float32` or `float64` `Tensor` or a Python number.  The\n        power of the polynomial. Defaults to linear, 1.0.\n      cycle: A boolean, whether or not it should cycle beyond decay_steps.\n      name: String.  Optional name of the operation. Defaults to\n        'PolynomialDecay'.\n\n    Returns:\n      A scalar `Tensor` of the same type as `initial_learning_rate`.  The decayed\n      learning rate.\n\n    \"\"\"\n    super(PolynomialDecay, self).__init__()\n    self.initial_learning_rate = initial_learning_rate\n    self.decay_steps = decay_steps\n    self.end_learning_rate = end_learning_rate\n    self.power = power\n    self.cycle = cycle\n    self.name = name\n\n  def __call__(self):\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    return tf.compat.v1.train.polynomial_decay(\n        learning_rate=self.initial_learning_rate,\n        global_step=global_step,\n        decay_steps=self.decay_steps,\n        end_learning_rate=self.end_learning_rate,\n        power=self.power,\n        cycle=self.cycle)\n"
  },
  {
    "path": "monolith/native_training/learning_rate_functions_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport tensorflow as tf\n\nfrom monolith.native_training import learning_rate_functions\n\n\nclass PolynomialDecayTest(tf.test.TestCase):\n\n  def test_basic(self):\n    with tf.compat.v1.Session() as sess:\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      self.evaluate(tf.compat.v1.assign_add(global_step, 1))\n      learning_rate_fn = learning_rate_functions.PolynomialDecay(\n          initial_learning_rate=0.01, decay_steps=10, end_learning_rate=0.11)\n      learning_rate = self.evaluate(learning_rate_fn())\n      self.assertAllClose(learning_rate, 0.02, 1e-6)\n\n      self.evaluate(tf.compat.v1.assign_add(global_step, 1))\n      learning_rate = self.evaluate(learning_rate_fn())\n      self.assertAllClose(learning_rate, 0.03, 1e-6)\n\n      learning_rate_fn2 = learning_rate_functions.PolynomialDecay(\n          initial_learning_rate=0.01, decay_steps=10, end_learning_rate=0.11)\n      self.assertEqual(str(learning_rate_fn), str(learning_rate_fn2))\n\n  def test_dense_optimizer(self):\n    with tf.compat.v1.Session() as sess:\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      learning_rate_fn = learning_rate_functions.PolynomialDecay(\n          initial_learning_rate=3.0, decay_steps=10, end_learning_rate=11.0)\n      var0 = tf.Variable([1.0, 2.0], dtype=tf.float32)\n      var1 = tf.Variable([3.0, 4.0], dtype=tf.float32)\n      grads0 = tf.constant([0.1, 0.1], dtype=tf.float32)\n      grads1 = tf.constant([0.01, 0.01], dtype=tf.float32)\n\n      ada_opt = tf.compat.v1.train.AdagradOptimizer(\n          learning_rate_fn, initial_accumulator_value=0.1)\n\n      ada_update = ada_opt.apply_gradients(zip([grads0, grads1], [var0, var1]))\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n\n      # Fetch params to validate initial values\n      v0_val, v1_val = self.evaluate([var0, var1])\n      self.assertAllClose([1.0, 2.0], v0_val)\n      self.assertAllClose([3.0, 4.0], v1_val)\n\n      # Run 3 steps of adagrad\n      for _ in range(3):\n        self.evaluate(ada_update)\n\n      # Validate updated params\n      v0_val, v1_val = self.evaluate([var0, var1])\n      self.assertAllCloseAccordingToType(\n          np.array([-1.6026098728179932, -0.6026098728179932]), v0_val)\n      self.assertAllCloseAccordingToType(\n          np.array([2.715679168701172, 3.715679168701172]), v1_val)\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/logging_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom typing import Dict, List, Callable, Tuple\n\nimport tensorflow as tf\n\nfrom absl import flags\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\nflags.DEFINE_integer(\n    \"monolith_default_machine_info_mem_limit\", 1 << 62,\n    \"The default value for mem_limit in machine info. (Bytes)\")\nFLAGS = flags.FLAGS\n\nlogging_ops = gen_monolith_ops\n\n\ndef tensors_timestamp(\n    tensors: List[tf.Tensor]) -> Tuple[List[tf.Tensor], tf.Tensor]:\n  \"\"\"Gets the timestamp when the tensors are ready.\"\"\"\n  return logging_ops.monolith_tensors_timestamp(tensors)\n\n\ndef emit_timer(key: str,\n               value: tf.Tensor,\n               tags: Dict[str, str] = None) -> tf.Operation:\n  tags = tags or {}\n  tag_str = \"|\".join([f\"{k}={v}\" for k, v in tags.items()])\n  return logging_ops.monolith_metric_v2(value, key=key, tags=tag_str)\n\n\ndef machine_info(mem_limit=None, shared_name=None) -> tf.Tensor:\n  \"\"\"Returns a MachineInfo tensor which contains a MachineInfo resource.\"\"\"\n  if mem_limit is None:\n    mem_limit = FLAGS.monolith_default_machine_info_mem_limit\n  return logging_ops.monolith_machine_info(mem_limit=mem_limit,\n                                           name=shared_name,\n                                           shared_name=shared_name)\n\n\ndef check_machine_health(machine_info_tensor: tf.Tensor) -> tf.Tensor:\n  \"\"\"Returns a scalar string tensor, which is serialized version of MachineHealthResult.\"\"\"\n  return logging_ops.monolith_check_machine_health(machine_info_tensor)\n"
  },
  {
    "path": "monolith/native_training/logging_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom absl import flags\n\nfrom monolith.native_training import logging_ops\nfrom monolith.native_training.runtime.ops import logging_ops_pb2\n\nFLAGS = flags.FLAGS\n\n\nclass LoggingOpsTest(tf.test.TestCase):\n\n  def test_tensors_timestamp(self):\n    tensor = [tf.constant(0)]\n    tensor, ts = logging_ops.tensors_timestamp(tensor)\n    tensor, new_ts = logging_ops.tensors_timestamp(tensor)\n\n    with self.session() as sess:\n      new_ts_value, ts_value = sess.run([new_ts, ts])\n      self.assertGreaterEqual(new_ts_value, ts_value)\n\n  def test_emit_timer(self):\n    op = logging_ops.emit_timer(\"test\", 0.0)\n    self.evaluate(op)\n\n  def test_machine_health(self):\n    FLAGS.monolith_default_machine_info_mem_limit = 1 << 62\n    info = logging_ops.machine_info()\n    self.assertEqual(self.evaluate(logging_ops.check_machine_health(info)), b\"\")\n\n  def test_machine_health_oom(self):\n    FLAGS.monolith_default_machine_info_mem_limit = 0\n    info = logging_ops.machine_info()\n    serialized_result = self.evaluate(logging_ops.check_machine_health(info))\n    result = logging_ops_pb2.MachineHealthResult()\n    result.ParseFromString(serialized_result)\n    self.assertEqual(result.status,\n                     logging_ops_pb2.MachineHealthResult.OUT_OF_MEMORY)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/losses/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\n\npackage(\n    default_visibility = [\"//visibility:public\"],\n)\n\npy_library(\n    name = \"batch_softmax_loss\",\n    srcs = [\"batch_softmax_loss.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_test(\n    name = \"batch_softmax_loss_test\",\n    srcs = [\"batch_softmax_loss_test.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":batch_softmax_loss\",\n    ],\n)\n\npy_binary(\n    name = \"inbatch_auc_loss\",\n    srcs = [\"inbatch_auc_loss.py\"],\n    deps = [\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"inbatch_auc_loss_test\",\n    srcs = [\"inbatch_auc_loss_test.py\"],\n    deps = [\n        \":inbatch_auc_loss\",\n    ],\n)\n\npy_binary(\n    name = \"ltr_losses\",\n    srcs = [\"ltr_losses.py\"],\n    deps = [\n        \"//monolith:utils\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_library(\n    name = \"losses\",\n    srcs = [],\n    srcs_version = \"PY3\",\n    deps = [\n        \":batch_softmax_loss\",\n        \":inbatch_auc_loss\",\n        \":ltr_losses\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/losses/batch_softmax_loss.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\n\ndef batch_softmax_loss(query: tf.Tensor,\n                       item: tf.Tensor,\n                       item_step_interval: tf.Tensor,\n                       r: tf.Tensor,\n                       normalize: bool = True,\n                       temperature: float = 1.0) -> tf.Tensor:\n  \"\"\"\n  Batch Softmax Loss\n\n  Args:\n    query (:obj:`tf.Tensor`): query 向量, shape=(batch_size, k)\n    item (:obj:`tf.Tensor`): item 向量, shape=(batch_size, k)\n    item_step_interval (:obj:`tf.Tensor`): item 出现的平均 global step 间隔, shape=(batch_size,)\n    r (:obj:`tf.Tensor`): query 对 item 感兴趣程度权重\n    normalize (:obj:`bool`): 是否对 query/item 向量归一化\n    temperature (:obj:`float`): hyper-parameter tuned to maximize retrieval metrics such as recall or precision\n  \"\"\"\n\n  if temperature <= 0:\n    raise ValueError(\n        \"temperature should be positive, while got {}\".format(temperature))\n  if normalize:\n    query = tf.linalg.l2_normalize(query, axis=1)\n    item = tf.linalg.l2_normalize(item, axis=1)\n\n  # (batch_size, batch_size)\n  similarity = tf.matmul(query, item, transpose_b=True) / temperature\n  # The first looked-up item_step_interval is filled by zeros\n  item_step_interval = tf.math.maximum(item_step_interval,\n                                       tf.constant([1.0], dtype=tf.float32))\n  item_frequency = 1 / item_step_interval\n  similarity = tf.math.exp(similarity - tf.math.log(item_frequency))\n  loss = -tf.reduce_sum(\n      tf.multiply(\n          r,\n          tf.math.log(\n              tf.linalg.tensor_diag_part(similarity) /\n              tf.reduce_sum(similarity, axis=1))))\n\n  return loss\n"
  },
  {
    "path": "monolith/native_training/losses/batch_softmax_loss_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport tensorflow as tf\n\nfrom monolith.native_training.losses.batch_softmax_loss import batch_softmax_loss\n\n\nclass BatchSoftmaxLossTest(tf.test.TestCase):\n\n  def test_batch_softmax_loss(self):\n    batch_size, dim = 4, 3\n    query = tf.constant(np.random.random([batch_size, dim]), dtype=tf.float32)\n    item = tf.constant(np.random.random([batch_size, dim]), dtype=tf.float32)\n    item_step_interval = tf.constant(\n        [np.random.randint(1, 10) for _ in range(batch_size)], dtype=tf.float32)\n    r = tf.ones((batch_size,), dtype=tf.float32)\n    loss = batch_softmax_loss(query, item, item_step_interval, r)\n    self.assertAllClose([loss], [6.5931373])\n\n\nif __name__ == '__main__':\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/losses/inbatch_auc_loss.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nfrom typing import List, Tuple, Optional, NamedTuple\n\nimport tensorflow as tf\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\ninbatch_auc_loss_ops = gen_monolith_ops\n\n\ndef inbatch_auc_loss(label: tf.Tensor,\n                     logit: tf.Tensor,\n                     neg_weight=1.0) -> tf.Tensor:\n  return inbatch_auc_loss_ops.inbatch_auc_loss(label=label,\n                                               logit=logit,\n                                               neg_weight=neg_weight)\n\n\n@tf.RegisterGradient(op_type='InbatchAucLoss')\ndef _inbatch_auc_loss_grad(op: tf.Operation, grad: tf.Tensor):\n  label, logit = op.inputs[0], op.inputs[1]\n  neg_weight = op.get_attr(name='neg_weight')\n  logit_grad = inbatch_auc_loss_ops.inbatch_auc_loss_grad(label=label,\n                                                          logit=logit,\n                                                          grad=grad,\n                                                          neg_weight=neg_weight)\n  return None, logit_grad\n"
  },
  {
    "path": "monolith/native_training/losses/inbatch_auc_loss_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nfrom math import log, exp\nimport tensorflow as tf\nfrom monolith.native_training.losses import inbatch_auc_loss\n\n\nclass InbatchAucLossTest(tf.test.TestCase):\n\n  def test_inbatch_auc_loss(self):\n    label = [1, 0, 0, 1]\n    logit = [0.5, -0.2, -0.4, 0.8]\n    loss = inbatch_auc_loss.inbatch_auc_loss(label=label, logit=logit)\n\n    loss_truth = 0\n    pos, neg = [], []\n    for i, l in enumerate(label):\n      if l > 0:\n        pos.append(i)\n      else:\n        neg.append(i)\n\n    for i in pos:\n      for j in neg:\n        diff = logit[i] - logit[j]\n        loss_truth += log(1 / (1 + exp(-diff)))\n\n    self.assertAlmostEqual(loss, tf.constant(loss_truth), delta=0.000001)\n\n  def test_inbatch_auc_loss_grad(self):\n    label = [1, 0, 0, 1]\n    logit = [0.5, -0.2, -0.4, 0.8]\n    logit_grad = inbatch_auc_loss.inbatch_auc_loss_ops.inbatch_auc_loss_grad(\n        label=label, logit=logit, grad=2, neg_weight=1.0)\n\n    pos, neg = [], []\n    for i, l in enumerate(label):\n      if l > 0:\n        pos.append(i)\n      else:\n        neg.append(i)\n\n    logit_grad_truth = [0] * len(logit)\n    for i in pos:\n      for j in neg:\n        diff = logit[i] - logit[j]\n        grad_ij = 1 - 1 / (1 + exp(-diff))\n\n        logit_grad_truth[i] += grad_ij\n        logit_grad_truth[j] -= grad_ij\n\n    logit_grad_truth = [2 * x for x in logit_grad_truth]\n    self.assertAllClose(logit_grad, tf.constant(logit_grad_truth))\n\n\nif __name__ == \"__main__\":\n  # tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/losses/ltr_losses.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport abc\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.ops import array_ops\nfrom tensorflow.python.ops import math_ops\nfrom tensorflow.python.ops import nn_ops\nfrom tensorflow.python.framework import dtypes\nfrom tensorflow.python.framework import sparse_tensor\nfrom tensorflow.python.ops import random_ops\nfrom tensorflow.python.ops import sparse_ops\nfrom tensorflow.python.ops.losses import losses as core_losses\n\n# The smallest probability that is used to derive smallest logit for invalid or\n# padding entries.\n_EPSILON = 1e-10\n\n\ndef label_valid_fn(labels):\n  \"\"\"Returns a boolean `Tensor` for label validity.\"\"\"\n  labels = ops.convert_to_tensor(labels)\n  return math_ops.greater_equal(labels, 0.)\n\n\ndef sort_by_scores(scores, features_list, topn=None):\n  \"\"\"Sorts example features according to per-example scores.\n\n  Args:\n    scores: A `Tensor` of shape [batch_size, list_size] representing the\n      per-example scores.\n    features_list: A list of `Tensor`s with the same shape as scores to be\n      sorted.\n    topn: An integer as the cutoff of examples in the sorted list.\n\n  Returns:\n    A list of `Tensor`s as the list of sorted features by `scores`.\n  \"\"\"\n  scores = ops.convert_to_tensor(scores)\n  scores.get_shape().assert_has_rank(2)\n  batch_size, list_size = array_ops.unstack(array_ops.shape(scores))\n  if topn is None:\n    topn = list_size\n  topn = math_ops.minimum(topn, list_size)\n  _, indices = nn_ops.top_k(scores, topn, sorted=True)\n  list_offsets = array_ops.expand_dims(\n      math_ops.range(batch_size) * list_size, 1)\n  # The shape of `indices` is [batch_size, topn] and the shape of\n  # `list_offsets` is [batch_size, 1]. Broadcasting is used here.\n  gather_indices = array_ops.reshape(indices + list_offsets, [-1])\n  output_shape = array_ops.stack([batch_size, topn])\n  # Each feature is first flattened to a 1-D vector and then gathered by the\n  # indices from sorted scores and then re-shaped.\n  return [\n      array_ops.reshape(\n          array_ops.gather(array_ops.reshape(feature, [-1]), gather_indices),\n          output_shape) for feature in features_list\n  ]\n\n\ndef organize_valid_indices(is_valid, shuffle=True, seed=None):\n  \"\"\"Organizes indices in such a way that valid items appear first.\n\n  Args:\n    is_valid: A boolen `Tensor` for entry validity with shape [batch_size,\n      list_size].\n    shuffle: A boolean indicating whether valid items should be shuffled.\n    seed: An int for random seed at the op level. It works together with the\n      seed at global graph level together to determine the random number\n      generation. See `tf.set_random_seed`.\n\n  Returns:\n    A tensor of indices with shape [batch_size, list_size, 2]. The returned\n    tensor can be used with `tf.gather_nd` and `tf.scatter_nd` to compose a new\n    [batch_size, list_size] tensor. The values in the last dimension are the\n    indices for an element in the input tensor.\n  \"\"\"\n  is_valid = ops.convert_to_tensor(is_valid)\n  is_valid.get_shape().assert_has_rank(2)\n  output_shape = array_ops.shape(is_valid)\n\n  if shuffle:\n    values = random_ops.random_uniform(output_shape, seed=seed)\n  else:\n    values = (array_ops.ones_like(is_valid, dtypes.float32) * array_ops.reverse(\n        math_ops.to_float(math_ops.range(output_shape[1])), [-1]))\n\n  rand = array_ops.where(is_valid, values, array_ops.ones(output_shape) * -1e-6)\n  # shape(indices) = [batch_size, list_size]\n  _, indices = nn_ops.top_k(rand, output_shape[1], sorted=True)\n  # shape(batch_ids) = [batch_size, list_size]\n  batch_ids = array_ops.ones_like(indices) * array_ops.expand_dims(\n      math_ops.range(output_shape[0]), 1)\n  return array_ops.concat(\n      [\n          array_ops.expand_dims(batch_ids, 2),  #[[0,...0], [1, ..., 1]]\n          array_ops.expand_dims(indices, 2)\n      ],  # shuffle之后的indices，dim0=batch  dim1=shuffle后的index,例如[0, 1, 2, 3] 变为[2,3,0,1]，为list的长度\n      axis=2)\n\n\ndef shuffle_valid_indices(is_valid, seed=None):\n  \"\"\"Returns a shuffle of indices with valid ones on top.\"\"\"\n  return organize_valid_indices(is_valid, shuffle=True, seed=seed)\n\n\ndef reshape_first_ndims(tensor, first_ndims, new_shape):\n  \"\"\"Reshapes the first n dims of the input `tensor` to `new shape`.\n\n  Args:\n    tensor: The input `Tensor`.\n    first_ndims: A int denoting the first n dims.\n    new_shape: A list of int representing the new shape.\n\n  Returns:\n    A reshaped `Tensor`.\n  \"\"\"\n  assert tensor.get_shape().ndims is None or tensor.get_shape(\n  ).ndims >= first_ndims, (\n      'Tensor shape is less than {} dims.'.format(first_ndims))\n  new_shape = array_ops.concat(\n      [new_shape, array_ops.shape(tensor)[first_ndims:]], 0)\n  if isinstance(tensor, sparse_tensor.SparseTensor):\n    return sparse_ops.sparse_reshape(tensor, new_shape)\n\n  return array_ops.reshape(tensor, new_shape)\n\n\ndef approx_ranks(logits, alpha=10.):\n  r\"\"\"Computes approximate ranks given a list of logits.\n\n  Given a list of logits, the rank of an item in the list is simply\n  one plus the total number of items with a larger logit. In other words,\n\n    rank_i = 1 + \\sum_{j \\neq i} I_{s_j > s_i},\n\n  where \"I\" is the indicator function. The indicator function can be\n  approximated by a generalized sigmoid:\n\n    I_{s_j < s_i} \\approx 1/(1 + exp(-\\alpha * (s_j - s_i))).\n\n  This function approximates the rank of an item using this sigmoid\n  approximation to the indicator function. This technique is at the core\n  of \"A general approximation framework for direct optimization of\n  information retrieval measures\" by Qin et al.\n\n  Args:\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    alpha: Exponent of the generalized sigmoid function.\n\n  Returns:\n    A `Tensor` of ranks with the same shape as logits.\n  \"\"\"\n  list_size = array_ops.shape(logits)[1]\n  x = array_ops.tile(array_ops.expand_dims(logits, 2), [1, 1, list_size])\n  y = array_ops.tile(array_ops.expand_dims(logits, 1), [1, list_size, 1])\n  pairs = math_ops.sigmoid(alpha * (y - x))\n  return math_ops.reduce_sum(pairs, -1) + .5\n\n\ndef inverse_max_dcg(labels,\n                    gain_fn=lambda labels: math_ops.pow(2.0, labels) - 1.,\n                    rank_discount_fn=lambda rank: 1. / math_ops.log1p(rank),\n                    topn=None):\n  \"\"\"Computes the inverse of max DCG.\n\n  Args:\n    labels: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      graded relevance of the corresponding item.\n    gain_fn: A gain function. By default this is set to: 2^label - 1.\n    rank_discount_fn: A discount function. By default this is set to:\n      1/log(1+rank).\n    topn: An integer as the cutoff of examples in the sorted list.\n  Returns:\n    A `Tensor` with shape [batch_size, 1].\n  \"\"\"\n  ideal_sorted_labels, = sort_by_scores(labels, [labels], topn=topn)\n  rank = math_ops.range(array_ops.shape(ideal_sorted_labels)[1]) + 1\n  discounted_gain = gain_fn(ideal_sorted_labels) * rank_discount_fn(\n      math_ops.to_float(rank))\n  discounted_gain = math_ops.reduce_sum(discounted_gain, 1, keepdims=True)\n  return array_ops.where(math_ops.greater(discounted_gain, 0.),\n                         1. / discounted_gain,\n                         array_ops.zeros_like(discounted_gain))\n\n\ndef get_batch_idx_size(logits, labels, rank_id, name_prefix):\n  batch_size = tf.shape(logits)[0]\n  rank_key, rank_idx, count = tf.unique_with_counts(rank_id)\n  max_count = tf.reduce_max(count)\n  unique_rank_id_num = tf.shape(count)[0]\n  rank_idx_tile = tf.tile(tf.expand_dims(rank_idx, 0), [unique_rank_id_num, 1])\n  range_count_tile = tf.tile(tf.expand_dims(tf.range(unique_rank_id_num), 1),\n                             [1, batch_size])\n  list_id_mask = tf.cast(tf.equal(rank_idx_tile, range_count_tile), tf.int32)\n  cum_mask = tf.cumsum(list_id_mask, axis=1, exclusive=True)\n  masked_list_id = list_id_mask * cum_mask\n  col_cor = tf.reduce_sum(masked_list_id, axis=0)\n  row_cor = rank_idx\n  batch_idx = tf.concat(\n      [tf.expand_dims(row_cor, 1),\n       tf.expand_dims(col_cor, 1)], axis=1)\n  output_shape = [tf.shape(count)[0], max_count]\n\n  logits_idx = tf.scatter_nd(updates=logits,\n                             indices=batch_idx,\n                             shape=output_shape,\n                             name=name_prefix + \"logits_idx\")\n  label_idx = tf.scatter_nd(updates=labels,\n                            indices=batch_idx,\n                            shape=output_shape,\n                            name=name_prefix + \"label_idx\") - 1e-6\n  mask_idx = tf.scatter_nd(updates=tf.ones_like(logits),\n                           indices=batch_idx,\n                           shape=[batch_size, batch_size],\n                           name=name_prefix + 'mask_idx')\n  unique_idx = tf.argmax(list_id_mask, axis=1)\n  return logits_idx, label_idx, mask_idx, unique_idx\n\n\nclass RankingLossKey(object):\n  \"\"\"Ranking loss key strings.\"\"\"\n  # Names for the ranking based loss functions.\n  PAIRWISE_HINGE_LOSS = 'pairwise_hinge_loss'\n  PAIRWISE_LOGISTIC_LOSS = 'pairwise_logistic_loss'\n  PAIRWISE_SOFT_ZERO_ONE_LOSS = 'pairwise_soft_zero_one_loss'\n  SOFTMAX_LOSS = 'softmax_loss'\n  SIGMOID_CROSS_ENTROPY_LOSS = 'sigmoid_cross_entropy_loss'\n  MEAN_SQUARED_LOSS = 'mean_squared_loss'\n  LIST_MLE_LOSS = 'list_mle_loss'\n  APPROX_NDCG_LOSS = 'approx_ndcg_loss'\n\n\ndef make_loss_fn(loss_keys,\n                 loss_weights=None,\n                 weights_feature_name=None,\n                 lambda_weight=None,\n                 reduction=core_losses.Reduction.SUM_BY_NONZERO_WEIGHTS,\n                 name=None,\n                 seed=None,\n                 extra_args=None):\n  \"\"\"Makes a loss function using a single loss or multiple losses.\n\n  Args:\n    loss_keys: A string or list of strings representing loss keys defined in\n      `RankingLossKey`. Listed loss functions will be combined in a weighted\n      manner, with weights specified by `loss_weights`. If `loss_weights` is\n      None, default weight of 1 will be used.\n    loss_weights: List of weights, same length as `loss_keys`. Used when merging\n      losses to calculate the weighted sum of losses. If `None`, all losses are\n      weighted equally with weight being 1.\n    weights_feature_name: A string specifying the name of the weights feature in\n      `features` dict.\n    lambda_weight: A `_LambdaWeight` object created by factory methods like\n      `create_ndcg_lambda_weight()`.\n    reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to\n      reduce training loss over batch.\n    name: A string used as the name for this loss.\n    seed: A randomization seed used in computation of some loss functions such\n      as ListMLE and pListMLE.\n    extra_args: A string-keyed dictionary that contains any other loss-specific\n      arguments.\n\n  Returns:\n    A function _loss_fn(). See `_loss_fn()` for its signature.\n\n  Raises:\n    ValueError: If `reduction` is invalid.\n    ValueError: If `loss_keys` is None or empty.\n    ValueError: If `loss_keys` and `loss_weights` have different sizes.\n  \"\"\"\n  if (reduction not in core_losses.Reduction.all() or\n      reduction == core_losses.Reduction.NONE):\n    raise ValueError('Invalid reduction: {}'.format(reduction))\n\n  if not loss_keys:\n    raise ValueError('loss_keys cannot be None or empty.')\n\n  if loss_weights:\n    if len(loss_keys) != len(loss_weights):\n      raise ValueError('loss_keys and loss_weights must have the same size.')\n\n  if not isinstance(loss_keys, list):\n    loss_keys = [loss_keys]\n\n  def _loss_fn(labels, logits, features):\n    \"\"\"Computes a single loss or weighted combination of losses.\n\n    Args:\n      labels: A `Tensor` of the same shape as `logits` representing relevance.\n      logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n        ranking score of the corresponding item.\n      features: Dict of Tensors of shape [batch_size, list_size, ...] for\n        per-example features and shape [batch_size, ...] for non-example context\n        features.\n\n    Returns:\n      An op for a single loss or weighted combination of multiple losses.\n\n    Raises:\n      ValueError: If `loss_keys` is invalid.\n    \"\"\"\n    weights = features[weights_feature_name] if weights_feature_name else None\n    loss_kwargs = {\n        'labels': labels,\n        'logits': logits,\n        'weights': weights,\n        'reduction': reduction,\n        'name': name,\n    }\n    if extra_args is not None:\n      loss_kwargs.update(extra_args)\n\n    loss_kwargs_with_lambda_weight = loss_kwargs.copy()\n    loss_kwargs_with_lambda_weight['lambda_weight'] = lambda_weight\n\n    loss_kwargs_with_lambda_weight_and_seed = (\n        loss_kwargs_with_lambda_weight.copy())\n    loss_kwargs_with_lambda_weight_and_seed['seed'] = seed\n\n    key_to_fn = {\n        RankingLossKey.PAIRWISE_HINGE_LOSS:\n            (_pairwise_hinge_loss, loss_kwargs_with_lambda_weight),\n        RankingLossKey.PAIRWISE_LOGISTIC_LOSS:\n            (_pairwise_logistic_loss, loss_kwargs_with_lambda_weight),\n        RankingLossKey.PAIRWISE_SOFT_ZERO_ONE_LOSS:\n            (_pairwise_soft_zero_one_loss, loss_kwargs_with_lambda_weight),\n        RankingLossKey.SOFTMAX_LOSS:\n            (_softmax_loss, loss_kwargs_with_lambda_weight),\n        RankingLossKey.SIGMOID_CROSS_ENTROPY_LOSS:\n            (_sigmoid_cross_entropy_loss, loss_kwargs),\n        RankingLossKey.MEAN_SQUARED_LOSS: (_mean_squared_loss, loss_kwargs),\n        RankingLossKey.LIST_MLE_LOSS:\n            (_list_mle_loss, loss_kwargs_with_lambda_weight_and_seed),\n        RankingLossKey.APPROX_NDCG_LOSS: (_approx_ndcg_loss, loss_kwargs),\n    }\n\n    # Obtain the list of loss ops.\n    loss_ops = []\n    for loss_key in loss_keys:\n      if loss_key not in key_to_fn:\n        raise ValueError('Invalid loss_key: {}.'.format(loss_key))\n      loss_fn, kwargs = key_to_fn[loss_key]\n      loss_ops.append(loss_fn(**kwargs))\n\n    # Compute weighted combination of losses.\n    if loss_weights:\n      weighted_losses = []\n      for loss_op, loss_weight in zip(loss_ops, loss_weights):\n        weighted_losses.append(math_ops.multiply(loss_op, loss_weight))\n    else:\n      weighted_losses = loss_ops\n\n    return math_ops.add_n(weighted_losses)\n\n  return _loss_fn\n\n\ndef create_ndcg_lambda_weight(topn=None, smooth_fraction=0.):\n  \"\"\"Creates _LambdaWeight for NDCG metric.\"\"\"\n  return DCGLambdaWeight(\n      topn,\n      gain_fn=lambda labels: math_ops.pow(2.0, labels) - 1.,\n      rank_discount_fn=lambda rank: 1. / math_ops.log1p(rank),\n      normalized=True,\n      smooth_fraction=smooth_fraction)\n\n\ndef create_reciprocal_rank_lambda_weight(topn=None, smooth_fraction=0.):\n  \"\"\"Creates _LambdaWeight for MRR-like metric.\"\"\"\n  return DCGLambdaWeight(topn,\n                         gain_fn=lambda labels: labels,\n                         rank_discount_fn=lambda rank: 1. / rank,\n                         normalized=True,\n                         smooth_fraction=smooth_fraction)\n\n\ndef create_p_list_mle_lambda_weight(list_size):\n  \"\"\"Creates _LambdaWeight based on Position-Aware ListMLE paper.\n\n  Produces a weight based on the formulation presented in the\n  \"Position-Aware ListMLE\" paper (Lan et al.) and available using\n  create_p_list_mle_lambda_weight() factory function above.\n\n  Args:\n    list_size: Size of the input list.\n\n  Returns:\n    A _LambdaWeight for Position-Aware ListMLE.\n  \"\"\"\n  return ListMLELambdaWeight(\n      rank_discount_fn=lambda rank: math_ops.pow(2., list_size - rank) - 1.)\n\n\nclass _LambdaWeight(object):\n  \"\"\"Interface for ranking metric optimization.\n\n  This class wraps weights used in the LambdaLoss framework for ranking metric\n  optimization (https://ai.google/research/pubs/pub47258). Such an interface is\n  to be instantiated by concrete lambda weight models. The instance is used\n  together with standard loss such as logistic loss and softmax loss.\n  \"\"\"\n\n  __metaclass__ = abc.ABCMeta\n\n  def _get_valid_pairs_and_clean_labels(self, sorted_labels):\n    \"\"\"Returns a boolean Tensor for valid pairs and cleaned labels.\"\"\"\n    sorted_labels = ops.convert_to_tensor(sorted_labels)\n    sorted_labels.get_shape().assert_has_rank(2)\n    is_label_valid = label_valid_fn(sorted_labels)\n    valid_pairs = math_ops.logical_and(array_ops.expand_dims(is_label_valid, 2),\n                                       array_ops.expand_dims(is_label_valid, 1))\n    sorted_labels = array_ops.where(is_label_valid, sorted_labels,\n                                    array_ops.zeros_like(sorted_labels))\n    return valid_pairs, sorted_labels\n\n  @abc.abstractmethod\n  def pair_weights(self, sorted_labels):\n    \"\"\"Returns the weight adjustment `Tensor` for example pairs.\n\n    Args:\n      sorted_labels: A dense `Tensor` of labels with shape [batch_size,\n        list_size] that are sorted by logits.\n\n    Returns:\n      A `Tensor` that can weight example pairs.\n    \"\"\"\n    raise NotImplementedError('Calling an abstract method.')\n\n  def individual_weights(self, sorted_labels):\n    \"\"\"Returns the weight `Tensor` for individual examples.\n\n    Args:\n      sorted_labels: A dense `Tensor` of labels with shape [batch_size,\n        list_size] that are sorted by logits.\n\n    Returns:\n      A `Tensor` that can weight individual examples.\n    \"\"\"\n    return sorted_labels\n\n\nclass IdentityLambdaWeight(_LambdaWeight):\n\n  def __init__(self,):\n    pass\n\n  def pair_weights(self, sorted_labels):\n    return 1.0\n\n\nclass DCGLambdaWeight(_LambdaWeight):\n  \"\"\"LambdaWeight for Discounted Cumulative Gain metric.\"\"\"\n\n  def __init__(self,\n               topn=None,\n               gain_fn=lambda label: label,\n               rank_discount_fn=lambda rank: 1. / rank,\n               normalized=False,\n               smooth_fraction=0.):\n    \"\"\"Constructor.\n\n    Ranks are 1-based, not 0-based. Given rank i and j, there are two types of\n    pair weights:\n      u = |rank_discount_fn(|i-j|) - rank_discount_fn(|i-j| + 1)|\n      v = |rank_discount_fn(i) - rank_discount_fn(j)|\n    where u is the newly introduced one in LambdaLoss paper\n    (https://ai.google/research/pubs/pub47258) and v is the original one in the\n    LambdaMART paper \"From RankNet to LambdaRank to LambdaMART: An Overview\".\n    The final pair weight contribution of ranks is\n      (1-smooth_fraction) * u + smooth_fraction * v.\n\n    Args:\n      topn: (int) The topn for the DCG metric.\n      gain_fn: (function) Tranforms labels.\n      rank_discount_fn: (function) The rank discount function.\n      normalized: (bool) If True, normalize weight by the max DCG.\n      smooth_fraction: (float) parameter to control the contribution from\n        LambdaMART.\n    \"\"\"\n    self._topn = topn\n    self._gain_fn = gain_fn\n    self._rank_discount_fn = rank_discount_fn\n    self._normalized = normalized\n    assert 0. <= smooth_fraction and smooth_fraction <= 1., (\n        'smooth_fraction %s should be in range [0, 1].' % smooth_fraction)\n    self._smooth_fraction = smooth_fraction\n\n  def pair_weights(self, sorted_labels):\n    \"\"\"See `_LambdaWeight`.\"\"\"\n    with ops.name_scope(None, 'dcg_lambda_weight', (sorted_labels,)):\n      valid_pair, sorted_labels = self._get_valid_pairs_and_clean_labels(\n          sorted_labels)\n      gain = self._gain_fn(sorted_labels)\n      if self._normalized:\n        gain *= inverse_max_dcg(sorted_labels,\n                                gain_fn=self._gain_fn,\n                                rank_discount_fn=self._rank_discount_fn,\n                                topn=self._topn)\n      pair_gain = array_ops.expand_dims(gain, 2) - array_ops.expand_dims(\n          gain, 1)\n      pair_gain *= math_ops.to_float(valid_pair)\n\n      list_size = array_ops.shape(sorted_labels)[1]\n      topn = self._topn or list_size\n      rank = math_ops.range(list_size) + 1\n\n      def _discount_for_relative_rank_diff():\n        \"\"\"Rank-based discount in the LambdaLoss paper.\"\"\"\n        # The LambdaLoss is not well defined when topn is active and topn <\n        # list_size. We cap the rank of examples to topn + 1 so that the rank\n        # differene is capped to topn. This is just a convenient upperbound\n        # when topn is active. We need to revisit this.\n        capped_rank = array_ops.where(math_ops.greater(rank, topn),\n                                      array_ops.ones_like(rank) * (topn + 1),\n                                      rank)\n        rank_diff = math_ops.to_float(\n            math_ops.abs(\n                array_ops.expand_dims(capped_rank, 1) -\n                array_ops.expand_dims(capped_rank, 0)))\n        pair_discount = array_ops.where(\n            math_ops.greater(rank_diff, 0),\n            math_ops.abs(\n                self._rank_discount_fn(rank_diff) -\n                self._rank_discount_fn(rank_diff + 1)),\n            array_ops.zeros_like(rank_diff))\n        return pair_discount\n\n      def _discount_for_absolute_rank():\n        \"\"\"Standard discount in the LambdaMART paper.\"\"\"\n        # When the rank discount is (1 / rank) for example, the discount is\n        # |1 / r_i - 1 / r_j|. When i or j > topn, the discount becomes 0.\n        rank_discount = array_ops.where(\n            math_ops.greater(rank, topn),\n            array_ops.zeros_like(math_ops.to_float(rank)),\n            self._rank_discount_fn(math_ops.to_float(rank)))\n        pair_discount = math_ops.abs(\n            array_ops.expand_dims(rank_discount, 1) -\n            array_ops.expand_dims(rank_discount, 0))\n        return pair_discount\n\n      u = _discount_for_relative_rank_diff()\n      v = _discount_for_absolute_rank()\n      pair_discount = (1. -\n                       self._smooth_fraction) * u + self._smooth_fraction * v\n      pair_weight = math_ops.abs(pair_gain) * pair_discount\n      if self._topn is None:\n        return pair_weight\n      pair_mask = math_ops.logical_or(\n          array_ops.expand_dims(math_ops.less_equal(rank, self._topn), 1),\n          array_ops.expand_dims(math_ops.less_equal(rank, self._topn), 0))\n      return pair_weight * math_ops.to_float(pair_mask)\n\n  def individual_weights(self, sorted_labels):\n    \"\"\"See `_LambdaWeight`.\"\"\"\n    with ops.name_scope(None, 'dcg_lambda_weight', (sorted_labels,)):\n      sorted_labels = ops.convert_to_tensor(sorted_labels)\n      sorted_labels = array_ops.where(label_valid_fn(sorted_labels),\n                                      sorted_labels,\n                                      array_ops.zeros_like(sorted_labels))\n      gain = self._gain_fn(sorted_labels)\n      if self._normalized:\n        gain *= inverse_max_dcg(sorted_labels,\n                                gain_fn=self._gain_fn,\n                                rank_discount_fn=self._rank_discount_fn,\n                                topn=self._topn)\n      rank_discount = self._rank_discount_fn(\n          math_ops.to_float(\n              math_ops.range(array_ops.shape(sorted_labels)[1]) + 1))\n      return gain * rank_discount\n\n\nclass PrecisionLambdaWeight(_LambdaWeight):\n  \"\"\"LambdaWeight for Precision metric.\"\"\"\n\n  def __init__(self,\n               topn,\n               positive_fn=lambda label: math_ops.greater_equal(label, 1.0)):\n    \"\"\"Constructor.\n\n    Args:\n      topn: (int) The K in Precision@K metric.\n      positive_fn: (function): A function on `Tensor` that output boolean True\n        for positive examples. The rest are negative examples.\n    \"\"\"\n    self._topn = topn\n    self._positive_fn = positive_fn\n\n  def pair_weights(self, sorted_labels):\n    \"\"\"See `_LambdaWeight`.\n\n    The current implementation here is that for any pairs of documents i and j,\n    we set the weight to be 1 if\n      - i and j have different labels.\n      - i <= topn and j > topn or i > topn and j <= topn.\n    This is exactly the same as the original LambdaRank method. The weight is\n    the gain of swapping a pair of documents.\n\n    Args:\n      sorted_labels: A dense `Tensor` of labels with shape [batch_size,\n        list_size] that are sorted by logits.\n\n    Returns:\n      A `Tensor` that can weight example pairs.\n    \"\"\"\n    with ops.name_scope(None, 'precision_lambda_weight', (sorted_labels,)):\n      valid_pair, sorted_labels = self._get_valid_pairs_and_clean_labels(\n          sorted_labels)\n      binary_labels = math_ops.to_float(self._positive_fn(sorted_labels))\n      label_diff = math_ops.abs(\n          array_ops.expand_dims(binary_labels, 2) -\n          array_ops.expand_dims(binary_labels, 1))\n      label_diff *= math_ops.to_float(valid_pair)\n      # i <= topn and j > topn or i > topn and j <= topn, i.e., xor(i <= topn, j\n      # <= topn).\n      list_size = array_ops.shape(sorted_labels)[1]\n      rank = math_ops.range(list_size) + 1\n      rank_mask = math_ops.logical_xor(\n          array_ops.expand_dims(math_ops.less_equal(rank, self._topn), 1),\n          array_ops.expand_dims(math_ops.less_equal(rank, self._topn), 0))\n      return label_diff * math_ops.to_float(rank_mask)\n\n\nclass ListMLELambdaWeight(_LambdaWeight):\n  \"\"\"LambdaWeight for ListMLE cost function.\"\"\"\n\n  def __init__(self, rank_discount_fn):\n    \"\"\"Constructor.\n\n    Ranks are 1-based, not 0-based.\n\n    Args:\n      rank_discount_fn: (function) The rank discount function.\n    \"\"\"\n    self._rank_discount_fn = rank_discount_fn\n\n  def pair_weights(self, sorted_labels):\n    \"\"\"See `_LambdaWeight`.\"\"\"\n    return sorted_labels\n\n  def individual_weights(self, sorted_labels):\n    \"\"\"See `_LambdaWeight`.\"\"\"\n    with ops.name_scope(None, 'p_list_mle_lambda_weight', (sorted_labels,)):\n      sorted_labels = ops.convert_to_tensor(sorted_labels)\n      rank_discount = self._rank_discount_fn(\n          math_ops.to_float(\n              math_ops.range(array_ops.shape(sorted_labels)[1]) + 1))\n      return array_ops.ones_like(sorted_labels) * rank_discount\n\n\ndef _sort_and_normalize(labels, logits, weights=None):\n  \"\"\"Sorts `labels` and `logits` and normalize `weights`.\n\n  Args:\n    labels: A `Tensor` of the same shape as `logits` representing graded\n      relevance.\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    weights: A scalar, a `Tensor` with shape [batch_size, 1], or a `Tensor` with\n      the same shape as `labels`.\n\n  Returns:\n    A tuple of (sorted_labels, sorted_logits, sorted_weights).\n  \"\"\"\n  labels = ops.convert_to_tensor(labels)\n  logits = ops.convert_to_tensor(logits)\n  logits.get_shape().assert_has_rank(2)\n  logits.get_shape().assert_is_compatible_with(labels.get_shape())\n  weights = 1.0 if weights is None else ops.convert_to_tensor(weights)\n  weights = array_ops.ones_like(labels) * weights\n  _, topn = array_ops.unstack(array_ops.shape(logits))\n\n  # Only sort entries with valid labels that are >= 0.\n  scores = array_ops.where(\n      math_ops.greater_equal(labels,\n                             0.), logits, -1e-6 * array_ops.ones_like(logits) +\n      math_ops.reduce_min(logits, axis=1, keepdims=True))\n  sorted_labels, sorted_logits, sorted_weights = sort_by_scores(\n      scores, [labels, logits, weights], topn=topn)\n  return sorted_labels, sorted_logits, sorted_weights\n\n\ndef _pairwise_comparison(sorted_labels,\n                         sorted_logits,\n                         sorted_weights,\n                         lambda_weight=None):\n  r\"\"\"Returns pairwise comparison `Tensor`s.\n\n  Given a list of n items, the labels of graded relevance l_i and the logits\n  s_i, we sort the items in a list based on s_i and obtain ranks r_i. We form\n  n^2 pairs of items. For each pair, we have the following:\n\n                        /\n                        | 1   if l_i > l_j\n  * `pairwise_labels` = |\n                        | 0   if l_i <= l_j\n                        \\\n  * `pairwise_logits` = s_i - s_j\n                         /\n                         | 0              if l_i <= l_j,\n  * `pairwise_weights` = | |l_i - l_j|    if lambda_weight is None,\n                         | lambda_weight  otherwise.\n                         \\\n\n  The `sorted_weights` is item-wise and is applied non-symmetrically to update\n  pairwise_weights as\n    pairwise_weights(i, j) = w_i * pairwise_weights(i, j).\n  This effectively applies to all pairs with l_i > l_j. Note that it is actually\n  symmetric when `sorted_weights` are constant per list, i.e., listwise weights.\n\n  Args:\n    sorted_labels: A `Tensor` with shape [batch_size, list_size] of labels\n      sorted.\n    sorted_logits: A `Tensor` with shape [batch_size, list_size] of logits\n      sorted.\n    sorted_weights: A `Tensor` with shape [batch_size, list_size] of item-wise\n      weights sorted.\n    lambda_weight: A `_LambdaWeight` object.\n\n  Returns:\n    A tuple of (pairwise_labels, pairwise_logits, pairwise_weights) with each\n    having the shape [batch_size, list_size, list_size].\n  \"\"\"\n  # Compute the difference for all pairs in a list. The output is a Tensor with\n  # shape [batch_size, list_size, list_size] where the entry [-1, i, j] stores\n  # the information for pair (i, j).\n  pairwise_label_diff = array_ops.expand_dims(\n      sorted_labels, 2) - array_ops.expand_dims(sorted_labels, 1)\n  pairwise_logits = array_ops.expand_dims(\n      sorted_logits, 2) - array_ops.expand_dims(sorted_logits, 1)\n  pairwise_labels = math_ops.to_float(math_ops.greater(pairwise_label_diff, 0))\n  is_label_valid = label_valid_fn(sorted_labels)\n  valid_pair = math_ops.logical_and(array_ops.expand_dims(is_label_valid, 2),\n                                    array_ops.expand_dims(is_label_valid, 1))\n  # Only keep the case when l_i > l_j.\n  pairwise_weights = pairwise_labels * math_ops.to_float(valid_pair)\n  # Apply the item-wise weights along l_i.\n  pairwise_weights *= tf.cast(array_ops.expand_dims(sorted_weights, 2),\n                              tf.float32)\n  if lambda_weight is not None:\n    pairwise_weights *= lambda_weight.pair_weights(sorted_labels)\n  else:\n    pairwise_weights *= math_ops.abs(pairwise_label_diff)\n  pairwise_weights = array_ops.stop_gradient(pairwise_weights,\n                                             name='weights_stop_gradient')\n  return pairwise_labels, pairwise_logits, pairwise_weights\n\n\ndef _pairwise_loss(loss_fn,\n                   labels,\n                   logits,\n                   weights=None,\n                   lambda_weight=None,\n                   lambda_scale=True,\n                   reduction=core_losses.Reduction.SUM_BY_NONZERO_WEIGHTS):\n  \"\"\"Template to compute pairwise loss.\n\n  Args:\n    loss_fn: A function that computes loss from the pairwise logits with l_i >\n      l_j.\n    labels: A `Tensor` of the same shape as `logits` representing graded\n      relevance.\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise\n      weights, or a `Tensor` with shape [batch_size, list_size] for item-wise\n      weights.\n    lambda_weight: A `_LambdaWeight` object.\n    reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to\n      reduce training loss over batch.\n\n  Returns:\n    An op for the pairwise loss.\n  \"\"\"\n  sorted_labels, sorted_logits, sorted_weights = _sort_and_normalize(\n      labels, logits, weights)\n  _, pairwise_logits, pairwise_weights = _pairwise_comparison(\n      sorted_labels, sorted_logits, sorted_weights, lambda_weight)\n\n  if lambda_weight is not None and lambda_scale:\n    # For LambdaLoss with relative rank difference, the scale of loss becomes\n    # much smaller when applying LambdaWeight. This affects the training can\n    # make the optimal learning rate become much larger. We use a heuristic to\n    # scale it up to the same magnitude as standard pairwise loss.\n    pairwise_weights *= math_ops.to_float(array_ops.shape(sorted_labels)[1])\n  return core_losses.compute_weighted_loss(loss_fn(pairwise_logits),\n                                           weights=pairwise_weights,\n                                           reduction=reduction)\n\n\ndef _pairwise_hinge_loss(labels,\n                         logits,\n                         weights=None,\n                         lambda_weight=None,\n                         lambda_scale=True,\n                         reduction=core_losses.Reduction.SUM_BY_NONZERO_WEIGHTS,\n                         name=None):\n  \"\"\"Computes the pairwise hinge loss for a list.\n\n  The hinge loss is defined as Hinge(l_i > l_j) = max(0, 1 - (s_i - s_j)). So a\n  correctly ordered pair has 0 loss if (s_i - s_j >= 1). Otherwise the loss\n  increases linearly with s_i - s_j. When the list_size is 2, this reduces to\n  the standard hinge loss.\n\n  Args:\n    labels: A `Tensor` of the same shape as `logits` representing graded\n      relevance.\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise\n      weights, or a `Tensor` with shape [batch_size, list_size] for item-wise\n      weights.\n    lambda_weight: A `_LambdaWeight` object.\n    reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to\n      reduce training loss over batch.\n    name: A string used as the name for this loss.\n\n  Returns:\n    An op for the pairwise hinge loss.\n  \"\"\"\n\n  def _loss(logits):\n    \"\"\"The loss of pairwise logits with l_i > l_j.\"\"\"\n    # TODO(xuanhui, pointer-team): Consider pass params object into the loss and\n    # put a margin here.\n    return nn_ops.relu(1. - logits)\n\n  with ops.name_scope(name, 'pairwise_hinge_loss', (labels, logits, weights)):\n    return _pairwise_loss(_loss,\n                          labels,\n                          logits,\n                          weights,\n                          lambda_weight,\n                          lambda_scale,\n                          reduction=reduction)\n\n\ndef _pairwise_logistic_loss(\n    labels,\n    logits,\n    weights=None,\n    lambda_weight=None,\n    lambda_scale=True,\n    reduction=core_losses.Reduction.SUM_BY_NONZERO_WEIGHTS,\n    name=None):\n  \"\"\"Computes the pairwise logistic loss for a list.\n\n  The preference probability of each pair is computed as the sigmoid function:\n  P(l_i > l_j) = 1 / (1 + exp(s_j - s_i)) and the logistic loss is log(P(l_i >\n  l_j)) if l_i > l_j and 0 otherwise.\n\n  Args:\n    labels: A `Tensor` of the same shape as `logits` representing graded\n      relevance.\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise\n      weights, or a `Tensor` with shape [batch_size, list_size] for item-wise\n      weights.\n    lambda_weight: A `_LambdaWeight` object.\n    reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to\n      reduce training loss over batch.\n    name: A string used as the name for this loss.\n\n  Returns:\n    An op for the pairwise logistic loss.\n  \"\"\"\n\n  def _loss(logits):\n    \"\"\"The loss of pairwise logits with l_i > l_j.\"\"\"\n    # The following is the same as log(1 + exp(-pairwise_logits)).\n    return nn_ops.relu(-logits) + math_ops.log1p(\n        math_ops.exp(-math_ops.abs(logits)))\n\n  with ops.name_scope(name, 'pairwise_logistic_loss',\n                      (labels, logits, weights)):\n    return _pairwise_loss(_loss,\n                          labels,\n                          logits,\n                          weights,\n                          lambda_weight,\n                          lambda_scale,\n                          reduction=reduction)\n\n\ndef _pairwise_soft_zero_one_loss(\n    labels,\n    logits,\n    weights=None,\n    lambda_weight=None,\n    reduction=core_losses.Reduction.SUM_BY_NONZERO_WEIGHTS,\n    name=None):\n  \"\"\"Computes the pairwise soft zero-one loss.\n\n  Note this is different from sigmoid cross entropy in that soft zero-one loss\n  is a smooth but non-convex approximation of zero-one loss. The preference\n  probability of each pair is computed as the sigmoid function: P(l_i > l_j) = 1\n  / (1 + exp(s_j - s_i)). Then 1 - P(l_i > l_j) is directly used as the loss.\n  So a correctly ordered pair has a loss close to 0, while an incorrectly\n  ordered pair has a loss bounded by 1.\n\n  Args:\n    labels: A `Tensor` of the same shape as `logits` representing graded\n      relevance.\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise\n      weights, or a `Tensor` with shape [batch_size, list_size] for item-wise\n      weights.\n    lambda_weight: A `_LambdaWeight` object.\n    reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to\n      reduce training loss over batch.\n    name: A string used as the name for this loss.\n\n  Returns:\n    An op for the pairwise soft zero one loss.\n  \"\"\"\n\n  def _loss(logits):\n    \"\"\"The loss of pairwise logits with l_i > l_j.\"\"\"\n    return array_ops.where(math_ops.greater(logits, 0),\n                           1. - math_ops.sigmoid(logits),\n                           math_ops.sigmoid(-logits))\n\n  with ops.name_scope(name, 'pairwise_soft_zero_one_loss',\n                      (labels, logits, weights)):\n    return _pairwise_loss(_loss,\n                          labels,\n                          logits,\n                          weights,\n                          lambda_weight,\n                          reduction=reduction)\n\n\ndef _softmax_loss(labels,\n                  logits,\n                  weights=None,\n                  lambda_weight=None,\n                  reduction=core_losses.Reduction.SUM_BY_NONZERO_WEIGHTS,\n                  name=None):\n  \"\"\"Computes the softmax cross entropy for a list.\n\n  Given the labels l_i and the logits s_i, we sort the examples and obtain ranks\n  r_i. The standard softmax loss doesn't need r_i and is defined as\n      -sum_i l_i * log(exp(s_i) / (exp(s_1) + ... + exp(s_n))).\n  The `lambda_weight` re-weight examples based on l_i and r_i.\n      -sum_i w(l_i, r_i) * log(exp(s_i) / (exp(s_1) + ... + exp(s_n))).abc\n  See 'individual_weights' in 'DCGLambdaWeight' for how w(l_i, r_i) is computed.\n\n  Args:\n    labels: A `Tensor` of the same shape as `logits` representing graded\n      relevance.\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise\n      weights, or a `Tensor` with shape [batch_size, list_size] for item-wise\n      weights.\n    lambda_weight: A `DCGLambdaWeight` instance.\n    reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to\n      reduce training loss over batch.\n    name: A string used as the name for this loss.\n\n  Returns:\n    An op for the softmax cross entropy as a loss.\n  \"\"\"\n  with ops.name_scope(name, 'softmax_loss', (labels, logits, weights)):\n    sorted_labels, sorted_logits, sorted_weights = _sort_and_normalize(\n        labels, logits, weights)\n    is_label_valid = label_valid_fn(sorted_labels)\n    # Reset the invalid labels to 0 and reset the invalid logits to a logit with\n    # ~= 0 contribution in softmax.\n    sorted_labels = array_ops.where(is_label_valid, sorted_labels,\n                                    array_ops.zeros_like(sorted_labels))\n    sorted_logits = array_ops.where(\n        is_label_valid, sorted_logits,\n        math_ops.log(_EPSILON) * array_ops.ones_like(sorted_logits))\n    if lambda_weight is not None and isinstance(lambda_weight, DCGLambdaWeight):\n      sorted_labels = lambda_weight.individual_weights(sorted_labels)\n    sorted_labels *= sorted_weights\n    label_sum = math_ops.reduce_sum(sorted_labels, 1, keepdims=True)\n    nonzero_mask = math_ops.greater(array_ops.reshape(label_sum, [-1]), 0.0)\n    label_sum, sorted_labels, sorted_logits = [\n        array_ops.boolean_mask(x, nonzero_mask)\n        for x in [label_sum, sorted_labels, sorted_logits]\n    ]\n    return core_losses.softmax_cross_entropy(sorted_labels / label_sum,\n                                             sorted_logits,\n                                             weights=array_ops.reshape(\n                                                 label_sum, [-1]),\n                                             reduction=reduction)\n\n\ndef _sigmoid_cross_entropy_loss(\n    labels,\n    logits,\n    weights=None,\n    reduction=core_losses.Reduction.SUM_BY_NONZERO_WEIGHTS,\n    name=None):\n  \"\"\"Computes the sigmoid_cross_entropy loss for a list.\n\n  Given the labels of graded relevance l_i and the logits s_i, we calculate\n  the sigmoid cross entropy for each ith position and aggregate the per position\n  losses.\n\n  Args:\n    labels: A `Tensor` of the same shape as `logits` representing graded\n      relevance.\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise\n      weights, or a `Tensor` with shape [batch_size, list_size] for item-wise\n      weights.\n    reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to\n      reduce training loss over batch.\n    name: A string used as the name for this loss.\n\n  Returns:\n    An op for the sigmoid cross entropy as a loss.\n  \"\"\"\n  with ops.name_scope(name, 'sigmoid_cross_entropy_loss',\n                      (labels, logits, weights)):\n    is_label_valid = array_ops.reshape(label_valid_fn(labels), [-1])\n    weights = 1.0 if weights is None else ops.convert_to_tensor(weights)\n    weights = array_ops.ones_like(labels) * weights\n    label_vector, logit_vector, weight_vector = [\n        array_ops.boolean_mask(array_ops.reshape(x, [-1]), is_label_valid)\n        for x in [labels, logits, weights]\n    ]\n    return core_losses.sigmoid_cross_entropy(label_vector,\n                                             logit_vector,\n                                             weights=weight_vector,\n                                             reduction=reduction)\n\n\ndef _mean_squared_loss(labels,\n                       logits,\n                       weights=None,\n                       reduction=core_losses.Reduction.SUM_BY_NONZERO_WEIGHTS,\n                       name=None):\n  \"\"\"Computes the mean squared loss for a list.\n\n  Given the labels of graded relevance l_i and the logits s_i, we calculate\n  the squared error for each ith position and aggregate the per position\n  losses.\n\n  Args:\n    labels: A `Tensor` of the same shape as `logits` representing graded\n      relevance.\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise\n      weights, or a `Tensor` with shape [batch_size, list_size] for item-wise\n      weights.\n    reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to\n      reduce training loss over batch.\n    name: A string used as the name for this loss.\n\n  Returns:\n    An op for the mean squared error as a loss.\n  \"\"\"\n  with ops.name_scope(name, 'mean_squared_loss', (labels, logits, weights)):\n    is_label_valid = array_ops.reshape(label_valid_fn(labels), [-1])\n    weights = 1.0 if weights is None else ops.convert_to_tensor(weights)\n    weights = array_ops.ones_like(labels) * weights\n    label_vector, logit_vector, weight_vector = [\n        array_ops.boolean_mask(array_ops.reshape(x, [-1]), is_label_valid)\n        for x in [labels, logits, weights]\n    ]\n    return core_losses.mean_squared_error(label_vector,\n                                          logit_vector,\n                                          weights=weight_vector,\n                                          reduction=reduction)\n\n\ndef _list_mle_loss(labels,\n                   logits,\n                   weights=None,\n                   lambda_weight=None,\n                   reduction=core_losses.Reduction.SUM_BY_NONZERO_WEIGHTS,\n                   name=None,\n                   seed=None):\n  \"\"\"Computes the ListMLE loss [Xia et al. 2008] for a list.\n\n  Given the labels of graded relevance l_i and the logits s_i, we calculate\n  the ListMLE loss for the given list.\n\n  The `lambda_weight` re-weights examples based on l_i and r_i.\n  The recommended weighting scheme is the formulation presented in the\n  \"Position-Aware ListMLE\" paper (Lan et al.) and available using\n  create_p_list_mle_lambda_weight() factory function above.\n\n  Args:\n    labels: A `Tensor` of the same shape as `logits` representing graded\n      relevance.\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise\n      weights, or a `Tensor` with shape [batch_size, list_size] for item-wise\n      weights.\n    lambda_weight: A `DCGLambdaWeight` instance.\n    reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to\n      reduce training loss over batch.\n    name: A string used as the name for this loss.\n    seed: A randomization seed used when shuffling ground truth permutations.\n\n  Returns:\n    An op for the ListMLE loss.\n  \"\"\"\n  with ops.name_scope(name, 'list_mle_loss', (labels, logits, weights)):\n    is_label_valid = label_valid_fn(labels)\n    # Reset the invalid labels to 0 and reset the invalid logits to a logit with\n    # ~= 0 contribution.\n    labels = array_ops.where(is_label_valid, labels,\n                             array_ops.zeros_like(labels))\n    logits = array_ops.where(\n        is_label_valid, logits,\n        math_ops.log(_EPSILON) * array_ops.ones_like(logits))\n    weights = 1.0 if weights is None else ops.convert_to_tensor(weights)\n    weights = array_ops.squeeze(weights)\n\n    # Shuffle labels and logits to add randomness to sort.\n    shuffled_indices = shuffle_valid_indices(is_label_valid, seed)\n    shuffled_labels = array_ops.gather_nd(labels, shuffled_indices)\n    shuffled_logits = array_ops.gather_nd(logits, shuffled_indices)\n\n    sorted_labels, sorted_logits = sort_by_scores(\n        shuffled_labels, [shuffled_labels, shuffled_logits])\n\n    raw_max = math_ops.reduce_max(sorted_logits, axis=1, keepdims=True)\n    sorted_logits = sorted_logits - raw_max\n    sums = math_ops.cumsum(math_ops.exp(sorted_logits), axis=1, reverse=True)\n    sums = math_ops.log(sums) - sorted_logits\n\n    if lambda_weight is not None and isinstance(lambda_weight,\n                                                ListMLELambdaWeight):\n      sums *= lambda_weight.individual_weights(sorted_labels)\n\n    negative_log_likelihood = math_ops.reduce_sum(sums, 1)\n\n    return core_losses.compute_weighted_loss(negative_log_likelihood,\n                                             weights=weights,\n                                             reduction=reduction)\n\n\ndef _approx_ndcg_loss(labels,\n                      logits,\n                      weights=None,\n                      reduction=core_losses.Reduction.SUM,\n                      name=None,\n                      alpha=10.):\n  \"\"\"Computes ApproxNDCG loss.\n\n  ApproxNDCG [\"A general approximation framework for direct optimization of\n  information retrieval measures\" by Qin et al.] is a smooth approximation\n  to NDCG.\n\n  Args:\n    labels: A `Tensor` of the same shape as `logits` representing graded\n      relevance.\n    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the\n      ranking score of the corresponding item.\n    weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise\n      weights, or a `Tensor` with shape [batch_size, list_size] for item-wise\n      weights. If None, the weight of a list in the mini-batch is set to\n      the sum of the labels of the items in that list.\n    reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to\n      reduce training loss over batch.\n    name: A string used as the name for this loss.\n    alpha: The exponent in the generalized sigmoid function.\n\n  Returns:\n    An op for the ApproxNDCG loss.\n  \"\"\"\n  with ops.name_scope(name, 'approx_ndcg_loss', (labels, logits, weights)):\n    is_label_valid = label_valid_fn(labels)\n    labels = array_ops.where(is_label_valid, labels,\n                             array_ops.zeros_like(labels))\n    logits = array_ops.where(\n        is_label_valid, logits, -1e3 * array_ops.ones_like(logits) +\n        math_ops.reduce_min(logits, axis=-1, keepdims=True))\n\n    label_sum = math_ops.reduce_sum(labels, 1, keepdims=True)\n    if weights is None:\n      weights = array_ops.ones_like(label_sum)\n    weights = array_ops.squeeze(weights)\n\n    nonzero_mask = math_ops.greater(array_ops.reshape(label_sum, [-1]), 0.0)\n    labels, logits, weights = [\n        array_ops.boolean_mask(x, nonzero_mask)\n        for x in [labels, logits, weights]\n    ]\n\n    gains = math_ops.pow(2., math_ops.to_float(labels)) - 1.\n    ranks = approx_ranks(logits, alpha=alpha)\n    discounts = 1. / math_ops.log1p(ranks)\n    dcg = math_ops.reduce_sum(gains * discounts, -1)\n    cost = -dcg * array_ops.squeeze(inverse_max_dcg(labels))\n\n    return core_losses.compute_weighted_loss(cost,\n                                             weights=weights,\n                                             reduction=reduction)\n"
  },
  {
    "path": "monolith/native_training/metric/BUILD",
    "content": "load(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_cc_binary\")\nload(\"@rules_python//python:defs.bzl\", \"py_library\", \"py_test\")\nload(\"@pip_deps//:requirements.bzl\", \"requirement\")\n\npackage(\n    default_visibility = [\n        \"//monolith/native_training:__subpackages__\",\n        \"//monolith/sail:__subpackages__\",\n    ],\n)\n\npy_library(\n    name = \"deep_insight_ops\",\n    srcs = [\"deep_insight_ops.py\"],\n    deps = [\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"deep_insight_ops_test\",\n    srcs = [\"deep_insight_ops_test.py\"],\n    deps = [\":deep_insight_ops\"],\n)\n\npy_library(\n    name = \"cli\",\n    srcs = [\"cli.py\"],\n    visibility = [\"//visibility:public\"],\n)\n\npy_library(\n    name = \"exit_hook\",\n    srcs = [\"exit_hook.py\"],\n    deps = [\n        \":cli\",\n        \"//monolith/native_training:native_task_context\",\n    ],\n)\n\npy_library(\n    name = \"metric_hook\",\n    srcs = [\n        \"kafka_utils.py\",\n        \"metric_hook.py\",\n    ],\n    deps = [\n        \":cli\",\n        \":exit_hook\",\n        \"//monolith/native_training:utils\",\n        \"//monolith/native_training/alert\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n        requirement(\"kafka-python\"),\n    ],\n)\n\npy_test(\n    name = \"metric_hook_test\",\n    srcs = [\"metric_hook_test.py\"],\n    deps = [\n        \":metric_hook\",\n    ],\n)\n\npy_library(\n    name = \"utils\",\n    srcs = [\"utils.py\"],\n    deps = [\n        \":deep_insight_ops\",\n    ],\n)\n\npy_test(\n    name = \"utils_test\",\n    srcs = [\"utils_test.py\"],\n    deps = [\n        \":utils\",\n    ],\n)\n\n"
  },
  {
    "path": "monolith/native_training/metric/cli.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport threading\nfrom absl import logging\nclass Client:\n\n  def __init__(self, *args, **kwargs):\n    pass\n\n  def __getattr__(self, name):\n    def method(*args, **kwargs):\n      pass\n    return method\n\ndef get_cli(*args, **kwargs):\n  return Client()\n"
  },
  {
    "path": "monolith/native_training/metric/deep_insight_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport socket\nfrom typing import List\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\ndeep_insight_ops = gen_monolith_ops\n\n_FEATURE_REQ_TIME = \"req_time\"\n_SAMPLE_RATE = \"sample_rate\"\n_UID = \"uid\"\n\ndef deep_insight_client(enable_metrics_counter: bool=False, is_fake: bool = False, dump_filename=None, container: str=socket.gethostname()) \\\n  -> tf.Tensor:\n  \"\"\"\n  Create a deep insight client\n  Args:\n    enable_metrics_counter - whether enable metrics counter for using deepinsight.\n    container - Use host name as the container name. So that each container will\n                create and use a seperate deepinsight resource.\n  \"\"\"\n  return deep_insight_ops.monolith_create_deep_insight_client(\n      enable_metrics_counter, is_fake, dump_filename, container)\n\n\ndef write_deep_insight(deep_insight_client_tensor: tf.Tensor,\n                       uids: tf.Tensor,\n                       req_times: tf.Tensor,\n                       labels: tf.Tensor,\n                       preds: tf.Tensor,\n                       sample_rates: tf.Tensor,\n                       model_name: str,\n                       target: str = \"ctr_head\",\n                       sample_ratio: float = 0.01,\n                       return_msgs: bool = False,\n                       use_zero_train_time=False) -> tf.Tensor:\n  \"\"\"\n  Write one instance's metrics to deep insight. Internal it includes parse and \n  build JSON format deep insight message. And send to databus channel using \n  unblock API.\n  Args:\n    uid - a 1-D int64 tensor.\n    req_time - a 1-D int64 tensor.\n    labels - a 1-D float tensor.\n    preds - a 1-D float tensor.\n    sample_rates - a 1-D float tensor.\n    model_name - model name of string type.\n    target - target of string type.\n    sample_ratio - sample ratio of float type.\n    return_msg - whether return the msg sent to deepinsight for debugging.\n    use_zero_train_time - Use True if you want to use training time (0) in deepinsight.\n                          this is actually used only in test. Use false if\n                          you want to use real training time to write to deepinsight.\n  Returns:\n    1-D string tensor.\n  \"\"\"\n  return deep_insight_ops.monolith_write_deep_insight(\n      deep_insight_client_handle=deep_insight_client_tensor,\n      uids=uids,\n      req_times=req_times,\n      labels=labels,\n      preds=preds,\n      sample_rates=sample_rates,\n      model_name=model_name,\n      target=target,\n      sample_ratio=sample_ratio,\n      return_msgs=return_msgs,\n      use_zero_train_time=use_zero_train_time)\n\n\ndef write_deep_insight_v2(deep_insight_client_tensor: tf.Tensor,\n                          req_times: tf.Tensor,\n                          labels: tf.Tensor,\n                          preds: tf.Tensor,\n                          sample_rates: tf.Tensor,\n                          extra_fields_values: List[tf.Tensor],\n                          extra_fields_keys: List[str],\n                          model_name: str,\n                          targets: List[str],\n                          sample_ratio: float = 0.01,\n                          return_msgs: bool = False,\n                          use_zero_train_time=False) -> tf.Tensor:\n  \"\"\"\n  Write one instance's metrics to deep insight. Internal it includes parse and \n  build JSON format deep insight message. And send to databus channel using \n  unblock API.\n  Args:\n    deep_insight_client_tensor: MonolithCreateDeepInsightClient\n    req_times: 1-D int64 tensor, shape = (batch_size,)\n    labels: 2-D float tensor, shape = (num_targets, batch_size)\n    preds: 2-D float tensor, shape = (num_targets, batch_size)\n    sample_rates: 2-D float tensor, shape = (num_targets, batch_size)\n    extra_fields_values: List of 1-D tensors, each shape = (batch_size,)\n    extra_fields_keys: List of strings.\n    model_name: model name of string type.\n    targets: List of target names.\n    sample_ratio: sample ratio of float type.\n    return_msgs: whether return the msg sent to deepinsight for debugging.\n    use_zero_train_time: Use True if you want to use training time (0) in deepinsight.\n                         this is actually used only in test. Use false if\n                         you want to use real training time to write to deepinsight.\n  Returns:\n    1-D string tensor.\n  \"\"\"\n  return deep_insight_ops.monolith_write_deep_insight_v2(\n      deep_insight_client_handle=deep_insight_client_tensor,\n      req_times=req_times,\n      labels=labels,\n      preds=preds,\n      sample_rates=sample_rates,\n      extra_fields_values=extra_fields_values,\n      extra_fields_keys=extra_fields_keys,\n      model_name=model_name,\n      targets=targets,\n      sample_ratio=sample_ratio,\n      return_msgs=return_msgs,\n      use_zero_train_time=use_zero_train_time)\n"
  },
  {
    "path": "monolith/native_training/metric/deep_insight_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport time\n\nfrom absl import logging\nimport json\nimport tensorflow as tf\n\nimport monolith.native_training.metric.deep_insight_ops as ops\n\n\nclass DeepInsightOpsTest(tf.test.TestCase):\n\n  def dummy_test(self):\n    pass\n\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()"
  },
  {
    "path": "monolith/native_training/metric/exit_hook.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport atexit\nimport sys\nimport signal\n\nfrom monolith.native_training import utils\nfrom monolith.native_training import native_task_context\nfrom monolith.native_training.metric import cli\n\nsig_no = None\n\n\ndef sig_handler(signo, frame):\n  global sig_no\n  sig_no = signo\n  sys.exit(signo)\n\n\nsignal.signal(signal.SIGHUP, sig_handler)\nsignal.signal(signal.SIGINT, sig_handler)\nsignal.signal(signal.SIGTERM, sig_handler)\n\n\n@atexit.register\ndef exit_hook():\n  ctx = native_task_context.get()\n  mcli = cli.get_cli(utils.get_metric_prefix())\n  index = ctx.worker_index if ctx.server_type == 'worker' else ctx.ps_index\n  tags = {\n      'server_type': ctx.server_type,\n      'index': str(index),\n      'sig': str(sig_no),\n  }\n  if sig_no is not None:\n    mcli.emit_counter(\"exit_hook\", 1, tags)\n"
  },
  {
    "path": "monolith/native_training/metric/kafka_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport time\nfrom queue import Queue\nfrom absl import logging\nfrom threading import Thread, RLock\nfrom kafka import KafkaProducer\n\n\nclass KProducer(object):\n\n  def __init__(self, brokers, topic) -> None:\n    self.brokers = brokers\n    self.topic = topic\n    self._producer = KafkaProducer(bootstrap_servers=brokers)\n\n    self._lock = RLock()\n    self._has_stopped = False\n    self._msg_queue = Queue()  # thread safe\n\n    self._total = 0\n    self._success = 0\n    self._failed = 0\n\n    self._thread = Thread(target=self._poll)\n    self._thread.start()\n\n  def send(self, msgs):\n    if msgs is None or len(msgs) == 0:\n      return\n    elif isinstance(msgs, (str, bytes)):\n      msgs = [msgs]\n    else:\n      msgs = [msg for msg in msgs if msg is not None and len(msg) > 0]\n\n    if len(msgs) > 0:\n      logging.log_first_n(level=logging.INFO, msg=msgs[0], n=10)\n      self._total += len(msgs)\n      self._msg_queue.put(msgs)\n\n  def _poll(self):\n    while True:\n      try:\n        msg_batch = self._msg_queue.get(timeout=1)\n      except:\n        with self._lock:\n          if self._has_stopped:\n            break\n          else:\n            continue\n\n      if msg_batch is not None and len(msg_batch) > 0:\n        for msg in msg_batch:\n          future = self._producer.send(self.topic, msg)\n          future.add_callback(self._send_success).add_errback(self._send_failed)\n\n      with self._lock:\n        if self._has_stopped:\n          break\n\n  def total(self):\n    return self._total\n\n  def success(self):\n    return self._success\n\n  def failed(self):\n    return self._failed\n\n  def _flush(self):\n    with self._lock:\n      assert self._has_stopped\n\n    while True:\n      try:\n        msg_batch = self._msg_queue.get(timeout=1)\n      except:\n        break\n\n      if not msg_batch:\n        break\n\n      for msg in msg_batch:\n        future = self._producer.send(self.topic, msg)\n        future.add_callback(self._send_success).add_errback(self._send_failed)\n\n  def close(self):\n    try:\n      logging.info('set stopped')\n      with self._lock:\n        self._has_stopped = True\n      logging.info('wait for thread join')\n      self._thread.join()\n      logging.info('flush queue')\n      self._flush()\n      logging.info('close kafka producer')\n      self._producer.close(timeout=1)\n    except Exception as e:\n      logging.warning(str(e))\n\n  def _send_success(self, *args, **kwargs):\n    self._success += 1\n\n  def _send_failed(self, *args, **kwargs):\n    time.sleep(2)  # if failed, sleep two second\n    logging.warning('send metric to kafka error')\n    self._failed += 1\n"
  },
  {
    "path": "monolith/native_training/metric/metric_hook.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Metrics codes are ported from Lagrange Lite: lagrange_lite/tensorflow/train.py\n#coding:utf-8\nimport json\nimport numpy as np\nimport os\nimport tensorflow as tf\nimport time\nfrom typing import Any, Tuple, Callable\nfrom queue import Queue, Empty\nfrom threading import Thread, RLock\n\nfrom absl import logging, flags\nfrom datetime import datetime\nfrom tensorflow.python.profiler.internal import _pywrap_traceme\nfrom tensorflow.python.training import basic_session_run_hooks\nfrom tensorflow.python.training import session_run_hook\nfrom tensorflow.python.training import training_util\n\nfrom monolith.native_training.alert import alert_manager\nfrom monolith.native_training.alert import alert_pb2\nfrom monolith.native_training.metric import cli\nfrom monolith.native_training import utils\nfrom monolith.native_training.metric.kafka_utils import KProducer\nfrom monolith.native_training.metric.exit_hook import exit_hook\n\n\nFLAGS = flags.FLAGS\n\n\nclass ThroughputMetricHook(tf.estimator.SessionRunHook):\n  \"\"\" Log accumulated steps and time elapsed per step. \"\"\"\n\n  def __init__(self,\n               model_name,\n               start_time_secs,\n               cluster_type=\"stable\",\n               run_every_n_secs=30):\n\n    self._model_name = model_name\n    self._start_time_secs = start_time_secs\n    self._cluster_type = cluster_type\n    self._run_every_n_secs = run_every_n_secs\n    self._is_first_step = True\n    self._mcli = cli.get_cli(utils.get_metric_prefix())\n    am = alert_manager.get_default_alert_manager()\n    if am:\n      proto = alert_pb2.AlertProto()\n      proto.training_alert.prefix = utils.get_metric_prefix()\n      am.add_rules(proto)\n\n  def begin(self):\n    self._global_step_tensor = tf.compat.v1.train.get_global_step()\n\n  def before_run(self, run_context):\n    if self._is_first_step is True:\n      self._emit_step = run_context.session.run(self._global_step_tensor)\n      self._emit_time = int(time.time())\n      if self._start_time_secs is not None:\n        tags = {\n            \"model_name\": self._model_name,\n            \"cluster_type\": self._cluster_type\n        }\n        run_start_elapsed_time = self._emit_time - self._start_time_secs\n        logging.info(\"Run start took {}s.\".format(run_start_elapsed_time))\n        self._mcli.emit_timer(\"run_start_elapsed_time.all\",\n                              run_start_elapsed_time, tags)\n      self._is_first_step = False\n    return session_run_hook.SessionRunArgs({\n        \"global_step\": self._global_step_tensor,\n    })\n\n  def after_run(self, run_context, run_values):\n    end_time = int(time.time())\n    elapsed_time = end_time - self._emit_time\n    if elapsed_time >= self._run_every_n_secs:\n      global_step = run_values.results[\"global_step\"]\n      step_inerval = global_step - self._emit_step\n      tags = {\n          \"model_name\": self._model_name,\n          \"cluster_type\": self._cluster_type\n      }\n      self._mcli.emit_counter(\"run_steps.all\", step_inerval, tags)\n      self._mcli.emit_timer(\"run_steps_elapsed_time.all\",\n                            elapsed_time / step_inerval, tags)\n      self._emit_step = global_step\n      self._emit_time = end_time\n\n\nclass StepLossMetricHook(tf.estimator.SessionRunHook):\n  \"\"\" Log loss of each step. \"\"\"\n\n  def __init__(self, loss_tensor):\n    self._loss_tensor = loss_tensor\n    self._mcli = cli.get_cli(utils.get_metric_prefix())\n\n  def before_run(self, run_context):\n    return tf.estimator.SessionRunArgs(self._loss_tensor)\n\n  def after_run(self, run_context, run_value):\n    self._mcli.emit_store(\"step_loss\", run_value.results)\n\n\nclass CustomMetricHook(tf.estimator.SessionRunHook):\n  \"\"\" Log group of customed metircs for a batch. \"\"\"\n\n  def __init__(self, metric_tensors):\n    for name in metric_tensors:\n      tensor = metric_tensors[name]\n      if len(tensor.shape.dims) > 0:\n        raise ValueError(\"The metric tensor should be a scalar!\")\n      if tensor.dtype.base_dtype not in (tf.float32, tf.int32):\n        raise ValueError(\n            \"The dtype of a metric tensor should be either tf.float or tf.int32!\"\n        )\n    if len(metric_tensors) == 0:\n      raise ValueError(\"At least one metric tensor should be offered!\")\n    self._metric_tensors = metric_tensors\n    self._mcli = cli.get_cli(utils.get_metric_prefix())\n\n  def before_run(self, run_context):\n    return tf.estimator.SessionRunArgs(self._metric_tensors)\n\n  def after_run(self, run_context, run_value):\n    metric_values = run_value.results\n    for name in metric_values:\n      self._mcli.emit_store(name, float(metric_values[name]))\n\n\nclass Tf2ProfilerHook(tf.estimator.SessionRunHook):\n  \"\"\" Using TF2 profiler in esitmator \"\"\"\n\n  def __init__(self,\n               logdir: str,\n               init_step_range: Tuple[int, int],\n               save_steps: int = None,\n               save_secs: int = None,\n               options: tf.profiler.experimental.ProfilerOptions = None):\n    \"\"\"Only one of save_steps and save_secs should be provided.\"\"\"\n    self._logdir = logdir\n    self._options = options\n    self._start_step, self._end_step = init_step_range\n    if self._start_step is not None and (self._end_step is None or self._end_step <= self._start_step):\n      raise ValueError(\"End step invalid, start_step: {}, end_step: {}\".format(self._start_step, self._end_step))\n    self._default_delta = 10\n    self._delta = self._end_step - self._start_step if self._end_step is not None else self._default_delta\n    if save_steps is not None and save_steps <= self._delta:\n      raise ValueError(\"Save steps must be greater than delta steps(default: {})\".format(self._default_delta))\n    self._timer = tf.estimator.SecondOrStepTimer(every_steps=save_steps,\n                                                 every_secs=save_secs)\n    self._current_step = 0\n    self._trace_me = None\n\n    self._profiling = False\n\n  def begin(self):\n    try:\n      # if enable_sync_training, there is no tf.distribute.Server\n      # we need start profiler server\n      if FLAGS.enable_sync_training:\n        tf.profiler.experimental.server.start(6666)\n    except:\n      logging.warning(\"cannot start profiler server at 6666\")\n\n  def before_run(self, run_context):\n    # fix step-time graph, related issue: https://github.com/tensorflow/profiler/issues/282\n    # TODO(huangruiteng): remove this after updating tensorflow\n    if self._profiling:\n      self._trace_me = _pywrap_traceme.TraceMe(\"TraceContext\", graph_type=\"train\", step_num=self._current_step)\n    return tf.estimator.SessionRunArgs(fetches=None)\n\n  def after_run(self, run_context, run_values: tf.estimator.SessionRunValues):\n    self._current_step += 1\n    if self._profiling:\n      self._trace_me.Stop()\n    if self._start_step is None:\n      self._start_step = self._current_step + 500\n      self._end_step = self._start_step + self._default_delta\n    if self._current_step < self._start_step:\n      return\n    if self._current_step >= self._end_step:\n      self._stop_profiling()\n    if self._timer.should_trigger_for_step(self._current_step):\n      self._start_profiling()\n      self._timer.update_last_triggered_step(self._current_step)\n      self._start_step = self._current_step\n      self._end_step = self._start_step + self._delta\n\n  def end(self, sess):\n    if self._profiling:\n      self._stop_profiling()\n\n  def _start_profiling(self):\n    try:\n      tf.profiler.experimental.start(self._logdir, self._options)\n      self._profiling = True\n    except tf.errors.AlreadyExistsError:\n      # Two cases:\n      # 1. User profiles by themselves.\n      # 2. When profiling by save_secs, it's still profiling after save_secs.\n      # OK to ignore here.\n      self._profiling = True\n\n  def _stop_profiling(self):\n    try:\n      if self._profiling:\n        self._profiling = False\n        tf.profiler.experimental.stop()\n    except tf.errors.UnavailableError:\n      # Maybe user terminates profiling\n      self._profiling = False\n\n\nclass ByteCCLTelemetryHook(tf.estimator.SessionRunHook):\n  \"\"\"Log telemetry information at regular intervals\"\"\"\n\n  def __init__(self, interval: int):\n    \"\"\"Log telemetry information at regular intervals\"\"\"\n    self._interval = interval\n    self._last_step = 0\n    logging.info(f\"Created ByteCCL telemetry hook, interval={interval}\")\n\n  def begin(self):\n    self._global_step_tensor = training_util._get_or_create_global_step_read()\n    if self._global_step_tensor is None:\n      raise RuntimeError(\n          \"Global step should be created to use ByteCCLTelemetryHook\")\n\n  def before_run(self, run_context):\n    return tf.estimator.SessionRunArgs(self._global_step_tensor)\n\n  def after_run(self, run_context, run_values: tf.estimator.SessionRunValues):\n    current_step = run_values.results\n    if current_step > self._last_step + self._interval:\n      self._log_telemetry()\n      self._last_step = current_step\n\n  def end(self, sess):\n    pass\n\n  def _log_telemetry(self):\n    import byteps.tensorflow as bps\n    if bps.rank() == 0:\n      telemetry = bps.get_telemetry()\n      # sample a few operations and show them\n      samples = []\n      num_allreduce_ops = 0\n      for name, mean, stdev, count in telemetry:\n        name = str(name)\n        is_alltoall = 'alltoall' in name.lower()\n        if is_alltoall or ('PushPull' in name and num_allreduce_ops < 3):\n          num_allreduce_ops += 1\n          entry = f'name: {name} mean(ms): {mean:.2f} stdev(ms): {stdev:.2f} count: {count}'\n          samples.append(entry)\n      if samples:\n        logging.info(f'Communication telemetry: {samples} ...')\n\n\nclass NVProfilerHook(Tf2ProfilerHook):\n\n  def __init__(self,\n               init_step_range: Tuple[int, int],\n               save_steps: int = None,\n               save_secs: int = None,\n               options: tf.profiler.experimental.ProfilerOptions = None):\n    super().__init__(None, init_step_range, save_steps, save_secs)\n    import ctypes\n    self._libcudart = ctypes.cdll.LoadLibrary(\"libcudart.so\")  # linux\n\n  def _start_profiling(self):\n    # http://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__PROFILER.html,\n    self._libcudart.cudaProfilerStart()\n    self._profiling = True\n\n  def _stop_profiling(self):\n    if self._profiling:\n      self._profiling = False\n      self._libcudart.cudaProfilerStop()\n\n\nclass KafkaMetricHook(tf.estimator.SessionRunHook):\n  \"\"\" Log group of customed metircs for a batch. \"\"\"\n  __instance = None\n\n  def __new__(cls, *args, **kwargs):\n    if cls.__instance is None:\n      cls.__instance = super().__new__(cls)\n      cls.__instance._kproducer = None\n      cls.__instance._init_kafka()\n\n    return cls.__instance\n\n  @classmethod\n  def _init_kafka(cls):\n    brokers = os.getenv('KAFKA_BROKER_LIST', None)\n    topic = os.getenv('KAFKA_TOPIC_NAME', None)\n    if brokers is None or topic is None:\n      logging.info(\n          'KafkaMetricHook init kafka failed, brokers: {}, topic: {}'.format(\n              brokers, topic))\n      return\n\n    cls.__instance._kproducer = KProducer(brokers, topic)\n    logging.info(\n        'KafkaMetricHook init kafka success, brokers: {}, topic: {}'.format(\n            brokers, topic))\n\n  def __init__(self, deep_insight_op=None):\n    if deep_insight_op is None:\n      collection = tf.compat.v1.get_collection(key='deep_insight_op')\n      if collection:\n        if isinstance(collection, (list, tuple)):\n          deep_insight_op = collection[0]\n        else:\n          deep_insight_op = collection\n    self._metric_tensors = {'deep_insight_op': deep_insight_op}\n\n  def before_run(self, run_context):\n    return tf.estimator.SessionRunArgs(self._metric_tensors)\n\n  def after_run(self, run_context, run_value):\n    if self._kproducer:\n      metric_values = run_value.results\n      msgs = metric_values.get('deep_insight_op')\n      if msgs is not None and len(msgs) > 0:\n        self._kproducer.send(msgs)\n\n  def end(self, session):\n    if self._kproducer:\n      self._kproducer.close()\n      logging.info('KafkaMetricHook end, flush msg, success: {}, failed: {}'.\\\n        format(self._kproducer.success(), self._kproducer.failed()))\n      self._kproducer = None\n\n\ndef default_parse_fn(obj: Any) -> Any:\n  if obj is not None:\n    if isinstance(obj, (str, bytes)):\n      return json.loads(obj)\n  return obj\n\n\ndef default_layout_fn(obj, indent=None) -> str:\n  if isinstance(obj, str):\n    return obj\n  else:\n    try:\n      return json.dumps(obj, indent=indent)\n    except:\n      return repr(obj)\n\n\ndef vepfs_layout_fn(obj) -> str:\n  req_time = obj.get('__REQ_TIME__') or obj.get('req_time')\n  gid = obj.get('__FEED_ID__') or obj.get('feedid') or obj.get('gid') or 'gid'\n  uid = obj.get('__UID__') or obj.get('userid') or obj.get('uid') or 'uid'\n  predict_scores = json.dumps(obj['predict']) if 'predict' in obj else None\n  labels = json.dumps(obj['label']) if 'label' in obj else None\n  return f\"{req_time};{gid};{uid};{predict_scores};{labels}\"\n\n\ndef vepfs_key_fn(obj, worker_id: int, base_name: str) -> str:\n  model_name = obj.get('model_name') or 'model_name'\n  date = obj.get('__REQ_TIME__') or obj.get('req_time')\n  return os.path.join(base_name, model_name, date, f'worker_{worker_id}')\n\n\nclass WriteOnlyFileAndStat(object):\n\n  def __init__(self,\n               key: str,\n               layout_fn: Callable[[Any], str] = None,\n               batch_size: int = 1024,\n               partition_size: int = None,\n               file_ext: str = 'txt'):\n    self.current_partition: int = 0\n    self.current_offset: int = 0\n    self.last_update_time: float = time.time()\n    self.buffer: List[Any] = []\n\n    self.batch_size = batch_size\n    self.partition_size = partition_size or int(1e6)\n    self.layout_fn = layout_fn or default_layout_fn\n    self.file_ext = file_ext\n\n    assert key is not None\n    self.key = key\n    self.stream = None\n    self._lock = RLock()\n\n  def write(self, obj):\n    if len(self.buffer) >= self.batch_size:\n      self.flush()\n\n    with self._lock:\n      if obj is not None:\n        self.buffer.append(self.layout_fn(obj))\n        self.current_offset += 1\n        self.last_update_time = time.time()\n\n  def write_many(self, objs):\n    if objs:\n      for obj in objs:\n        self.write(obj)\n\n  def flush(self, check: bool = True):\n    with self._lock:\n      if self.stream is None:\n        if not tf.io.gfile.exists(path=self.key):\n          tf.io.gfile.makedirs(path=self.key)\n        part_name = os.path.join(\n            self.key, f'part_{self.current_partition:06d}.{self.file_ext}')\n        self.stream = tf.io.gfile.GFile(part_name, 'w+')\n\n      if self.stream is not None:\n        if self.buffer:\n          self.stream.write('\\n'.join(self.buffer))\n          self.stream.write('\\n')\n          self.buffer = []\n        self.stream.flush()\n\n      if check and self.current_offset >= self.partition_size:\n        self.current_partition += 1\n        self.current_offset = 0\n        self.stream.close()\n        part_name = os.path.join(\n            self.key, f'part_{self.current_partition:06d}.{self.file_ext}')\n        self.stream = tf.io.gfile.GFile(part_name, 'w+')\n\n  def close(self):\n    with self._lock:\n      self.flush(check=False)\n      if self.stream is not None:\n        self.stream.close()\n        self.stream = None\n\n  def is_available(self):\n    return (time.time() - self.last_update_time) < 24 * 60 * 60\n\n\nclass FileMetricHook(tf.estimator.SessionRunHook):\n  \"\"\" Log group of customed metircs for a batch. \"\"\"\n  __instance = None\n\n  def __new__(cls, *args, **kwargs):\n    if cls.__instance is None:\n      cls.__instance = super().__new__(cls)\n    return cls.__instance\n\n  def __init__(self,\n               deep_insight_op=None,\n               *,\n               worker_id: int = None,\n               parse_fn: Callable[[Any], Any] = None,\n               key_fn: Callable[[Any, int, str], str] = None,\n               layout_fn: Callable[[Any], str] = None,\n               batch_size: int = 1024,\n               partition_size: int = None,\n               base_name: str = '/vepfs/jaguar_deepinsight_results',\n               file_ext: str = 'txt'):\n    if deep_insight_op is None:\n      collection = tf.compat.v1.get_collection(key='deep_insight_op')\n      if collection:\n        if isinstance(collection, (list, tuple)):\n          deep_insight_op = collection[0]\n        else:\n          deep_insight_op = collection\n      else:\n        deep_insight_op = None\n\n    self._worker_id = worker_id\n    self._key_fn = key_fn\n    self._layout_fn = layout_fn or default_layout_fn\n    self._parse_fn = parse_fn or default_parse_fn\n    self._batch_size = batch_size\n    self._partition_size = partition_size\n    self._base_name = base_name\n    self._file_ext = file_ext\n\n    self._queue: Queue = Queue()\n    self._files: Dict[str, WriteOnlyFileAndStat] = {}\n    self._stopped = False\n    self._metric_tensors = {'deep_insight_op': deep_insight_op}\n    self._thread = None\n\n  def before_run(self, run_context):\n    return tf.estimator.SessionRunArgs(self._metric_tensors)\n\n  def after_run(self, run_context, run_value):\n    if self._thread is None:\n      self._thread = Thread(target=self._send)\n      self._thread.start()\n\n    metric_values = run_value.results\n    msgs = metric_values.get('deep_insight_op')\n    if msgs is not None:\n      if isinstance(msgs, (list, tuple, np.ndarray)):\n        for msg in msgs:\n          if msg:\n            self._queue.put(msg)\n      else:\n        self._queue.put(msgs)\n\n  def end(self, session):\n    logging.info('end FileMetricHook: empty the queue ...')\n    while not self._queue.empty():\n      time.sleep(1)\n    logging.info('end FileMetricHook: queue is empty, begin to stop thread ...')\n    self._stopped = True\n    if self._thread is not None:\n      self._thread.join()\n      self._thread = None\n    logging.info(\n        'end FileMetricHook: thread stopped, begin to close open file ...')\n    for fs in self._files.values():\n      fs.close()\n    logging.info('end FileMetricHook: all done! ')\n\n  def _send(self):\n    last_check_time = time.time()\n    while not self._stopped:\n      try:\n        item = self._queue.get(timeout=1)\n        item = self._parse_fn(item)\n      except Empty as e:\n        continue\n\n      key = self._key_fn(item, self._worker_id, self._base_name)\n      if key not in self._files:\n        file_and_stat = WriteOnlyFileAndStat(\n            key,\n            layout_fn=self._layout_fn,\n            batch_size=self._batch_size,\n            partition_size=self._partition_size,\n            file_ext=self._file_ext)\n        self._files[key] = file_and_stat\n      else:\n        file_and_stat = self._files[key]\n      file_and_stat.write(item)\n\n      if time.time() - last_check_time > 600:\n        to_remove = set()\n        for key, fs in self._files.items():\n          if not fs.is_available():\n            fs.close()\n            to_remove.add(key)\n\n        for key in to_remove:\n          del self._files[key]\n        last_check_time = time.time()\n"
  },
  {
    "path": "monolith/native_training/metric/metric_hook_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport time\nimport json\nfrom datetime import datetime, timedelta\nfrom random import choice, randint\nimport tensorflow as tf\n\nfrom monolith.native_training.metric import metric_hook\n\n\nclass Tf2ProfilerHookTest(tf.test.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    self.logdir = os.path.join(os.environ[\"TEST_TMPDIR\"], self._testMethodName)\n    self.filepattern = os.path.join(self.logdir, \"plugins/profile/*\")\n    self.graph = tf.Graph()\n    with self.graph.as_default():\n      self.global_step = tf.compat.v1.train.get_or_create_global_step()\n      self.train_op = tf.compat.v1.assign_add(self.global_step, 1)\n\n  def _count_files(self):\n    return len(tf.io.gfile.glob(self.filepattern))\n\n  def test_steps(self):\n    with self.graph.as_default():\n      hook = metric_hook.Tf2ProfilerHook(self.logdir, init_step_range=[0, 10], save_steps=50)\n      with tf.compat.v1.train.SingularMonitoredSession(hooks=[hook]) as sess:\n        sess.run(self.train_op)\n    self.assertEqual(self._count_files(), 1)\n    \n  def test_multiple_steps_1(self):\n    with self.graph.as_default():\n      hook = metric_hook.Tf2ProfilerHook(self.logdir, init_step_range=[0, 10], save_steps=30)\n      with tf.compat.v1.train.SingularMonitoredSession(hooks=[hook]) as sess:\n        for _ in range(30):\n          sess.run(self.train_op)\n          # Since profiler directory is named by seconds, we need to make sure\n          # two dumps are in the different folder.\n          time.sleep(0.15)\n      # Triggered at 0~9\n      self.assertEqual(self._count_files(), 1)\n\n  def test_multiple_steps_2(self):\n    with self.graph.as_default():\n      hook = metric_hook.Tf2ProfilerHook(self.logdir, init_step_range=[0, 10], save_steps=30)\n      with tf.compat.v1.train.SingularMonitoredSession(hooks=[hook]) as sess:\n        for _ in range(31):\n          sess.run(self.train_op)\n          # Since profiler directory is named by seconds, we need to make sure\n          # two dumps are in the different folder.\n          time.sleep(0.15)\n      # Triggered at 0~9, 30\n      self.assertEqual(self._count_files(), 2)\n\n  def test_secs_1(self):\n    with self.graph.as_default():\n      hook = metric_hook.Tf2ProfilerHook(self.logdir, init_step_range=[0, 10], save_secs=1)\n      with tf.compat.v1.train.SingularMonitoredSession(hooks=[hook]) as sess:\n        for _ in range(10):\n          sess.run(self.train_op)\n          # In total, we will sleep for 1.5s, but it still remains profiling first step range\n          time.sleep(0.15)\n      self.assertGreaterEqual(self._count_files(), 1)\n \n  def test_secs_2(self):\n    with self.graph.as_default():\n      hook = metric_hook.Tf2ProfilerHook(self.logdir, init_step_range=[0, 10], save_secs=3)\n      with tf.compat.v1.train.SingularMonitoredSession(hooks=[hook]) as sess:\n        for _ in range(21):\n          sess.run(self.train_op)\n          # In total, we will sleep for 3.15s\n          time.sleep(0.15)\n      # At least we will 2 dumps (maybe more depending on how fast we run the program)\n      self.assertGreaterEqual(self._count_files(), 2)\n\n\nclass FileMetricHookTest(tf.test.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    cls.model_name = 'test_model'\n    cls.base_name = f'{os.environ.get(\"HOME\")}/tmp/file_metric_hook'\n    cls.hook = metric_hook.FileMetricHook(worker_id=0,\n                                          key_fn=metric_hook.vepfs_key_fn,\n                                          layout_fn=metric_hook.vepfs_layout_fn,\n                                          batch_size=8,\n                                          partition_size=32,\n                                          base_name=cls.base_name)\n\n  @classmethod\n  def tearDownClass(cls):\n    cls.hook.end(None)\n    date_dir = tf.io.gfile.listdir(path=f'{cls.base_name}/{cls.model_name}')\n    for i in range(7, -1, -1):\n      date = datetime.today() - timedelta(days=i)\n      date_str = date.strftime('%Y%m%d')\n      assert date_str in date_dir\n      path = f'{cls.base_name}/{cls.model_name}/{date_str}/worker_0/'\n      data_dir = tf.io.gfile.listdir(path=path)\n      assert len(data_dir) == 2\n      for df in data_dir:\n        fname = f'{path}{df}'\n        with tf.io.gfile.GFile(fname, 'r') as stream:\n          assert len(stream.readlines()) == 32\n\n  def test_vepfs_key_fn(self):\n    data = {\n        'model_name': 'test_model',\n        'req_time': '20220927',\n        'userid': '1854',\n        'predict': {\n            'feed_comment': 0.5,\n            'click_comment': 0.2,\n            'feed_share': 0.2\n        },\n        'label': {\n            'feed_comment': 0,\n            'click_comment': 1,\n            'feed_share': 0\n        }\n    }\n    self.assertEqual(\n        metric_hook.vepfs_key_fn(data, worker_id=0, base_name=self.base_name),\n        f'{self.base_name}/test_model/20220927/worker_0')\n\n  def test_vepfs_layout_fn(self):\n    data = {\n        'model_name': 'test_model',\n        'req_time': '20220927',\n        'userid': '1854',\n        'predict': {\n            'feed_comment': 0.5,\n            'click_comment': 0.2,\n            'feed_share': 0.2\n        },\n        'label': {\n            'feed_comment': 0,\n            'click_comment': 1,\n            'feed_share': 0\n        }\n    }\n    self.assertEqual(\n        metric_hook.vepfs_layout_fn(data),\n        '20220927;gid;1854;{\"feed_comment\": 0.5, \"click_comment\": 0.2, \"feed_share\": 0.2};{\"feed_comment\": 0, \"click_comment\": 1, \"feed_share\": 0}'\n    )\n\n  def test_after_run(self):\n    run_context = None\n    head_names = ['feed_comment', 'click_comment', 'feed_share']\n    predicts = [0.01, 0.1, 0.2, 0.5, 0.9, 0.99]\n    labels = [0, 1]\n\n    class RunValue(object):\n\n      def __init__(self, rv):\n        self.results = {'deep_insight_op': [json.dumps(rv)]}\n\n    for i in range(7, -1, -1):\n      date = datetime.today() - timedelta(days=i)\n      date_str = date.strftime('%Y%m%d')\n      for _ in range(64):\n        run_value = {\n            'model_name': self.model_name,\n            'req_time': date_str,\n            'feedid': str(randint(1, 4096)),\n            'userid': str(randint(1, 4096)),\n            'predict': {name: choice(predicts) for name in head_names},\n            'label': {name: choice(labels) for name in head_names},\n        }\n        self.hook.after_run(run_context, run_value=RunValue(run_value))\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/metric/utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport datetime\nimport logging\nfrom typing import Dict, List\n\nimport tensorflow as tf\n\nfrom monolith.native_training.metric import deep_insight_ops\n\n\ndef write_deep_insight(features: Dict[str, tf.Tensor],\n                       sample_ratio: float,\n                       model_name: str,\n                       labels: tf.Tensor = None,\n                       preds: tf.Tensor = None,\n                       target: str = None,\n                       targets: List[str] = None,\n                       labels_list: List[tf.Tensor] = None,\n                       preds_list: List[tf.Tensor] = None,\n                       sample_rates_list: List[tf.Tensor] = None,\n                       extra_fields_keys: List[str] = [],\n                       enable_deep_insight_metrics=True,\n                       enable_kafka_metrics=False,\n                       dump_filename=None) -> tf.Tensor:\n  \"\"\" Writes the data into deepinsight\n  Requires 'uid', 'req_time', and 'sample_rate' in features. \n  sample_ratio is deepinsight sample ratio, set value like 0.01.\n  \n  If targets is non-empty, MonolithWriteDeepInsightV2 will be used, enabling:\n  - Multi-target sent as one message;\n  - Dump extra fields.\n  When using MonolithWriteDeepInsightV2, labels/preds/sample_rates should be\n  shape (num_targets, batch_size). sample_rates is optional.\n  Extra fields specified in extra_fields_keys must be present in features, and\n  must have batch_size numbers of values.\n  \"\"\"\n  if 'req_time' not in features:\n    logging.info(\"Disabling deep_insight because req_time is absent\")\n    return tf.no_op()\n\n  is_fake = enable_kafka_metrics or (dump_filename is not None and len(dump_filename) > 0)\n  deep_insight_client = deep_insight_ops.deep_insight_client(\n      enable_deep_insight_metrics, is_fake, dump_filename=dump_filename)\n  req_times = tf.reshape(features[\"req_time\"], [-1])\n\n  if not targets:\n    uids = tf.reshape(features[\"uid\"], [-1])\n    sample_rates = tf.reshape(features[\"sample_rate\"], [-1])\n    deep_insight_op = deep_insight_ops.write_deep_insight(\n        deep_insight_client_tensor=deep_insight_client,\n        uids=uids,\n        req_times=req_times,\n        labels=labels,\n        preds=preds,\n        sample_rates=sample_rates,\n        model_name=model_name,\n        target=target,\n        sample_ratio=sample_ratio,\n        return_msgs=is_fake)\n  else:\n    labels = tf.stack([label if label.shape.rank == 1 else  tf.reshape(label, (-1,))\n                       for label in labels_list if label is not None])\n    preds = tf.stack([pred if pred.shape.rank == 1 else  tf.reshape(pred, (-1,))\n                      for pred in preds_list if pred is not None])\n    if not sample_rates_list:\n      sample_rates_list = [tf.reshape(features[\"sample_rate\"], [-1])\n                          ] * len(targets)\n    elif isinstance(sample_rates_list, (tuple, list)):\n      sample_rates_list = [sample_rate if sample_rate.shape.rank == 1 else  tf.reshape(sample_rate, (-1,))\n                           for sample_rate in sample_rates_list if sample_rate is not None]\n    else:\n      raise Exception(\"sample_rates_list error!\")\n    sample_rates = tf.stack(sample_rates_list)\n    if \"uid\" not in extra_fields_keys:\n      extra_fields_keys.append(\"uid\")\n    extra_fields_values = []\n    for key in extra_fields_keys:\n      extra_fields_values.append(tf.reshape(features[key], [-1]))\n    deep_insight_op = deep_insight_ops.write_deep_insight_v2(\n        deep_insight_client_tensor=deep_insight_client,\n        req_times=req_times,\n        labels=labels,\n        preds=preds,\n        sample_rates=sample_rates,\n        model_name=model_name,\n        sample_ratio=sample_ratio,\n        extra_fields_values=extra_fields_values,\n        extra_fields_keys=extra_fields_keys,\n        targets=targets,\n        return_msgs=is_fake)\n  return deep_insight_op\n"
  },
  {
    "path": "monolith/native_training/metric/utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom unittest import mock\n\nimport tensorflow as tf\n\nfrom monolith.native_training.metric import utils\n\n\nclass DeepInsightTest(tf.test.TestCase):\n\n  @mock.patch(\n      \"monolith.native_training.metric.deep_insight_ops.write_deep_insight\")\n  def test_basic(self, deep_insight_op):\n\n    def fake_call(uids, **kwargs):\n      del kwargs\n      with self.session() as sess:\n        uids = sess.run(uids)\n        self.assertAllEqual(uids, [1, 2, 3])\n\n    deep_insight_op.side_effect = fake_call\n\n    features = {\n        \"uid\": tf.constant([1, 2, 3], dtype=tf.int64),\n        \"req_time\": tf.constant([1, 2, 3], dtype=tf.int64),\n        \"sample_rate\": tf.constant([0.5, 0.5, 0.5], dtype=tf.float32),\n    }\n    labels = tf.constant([1.0, 0.0, 1.0], dtype=tf.float32)\n    preds = tf.constant([0.9, 0.2, 0.8], dtype=tf.float32)\n    model_name = \"test_model\"\n    target = \"target\"\n    utils.write_deep_insight(features, 0.01, labels, preds, model_name, target)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/mlp_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport sys\nimport time\nimport socket\nfrom typing import List\nfrom absl import logging, flags\nimport signal\nfrom subprocess import Popen, PIPE\n\nimport tensorflow as tf\nimport tensorflow.python.data.experimental.service as dsvc\nfrom tensorflow_estimator.python.estimator.util import _DatasetInitializerHook\nfrom monolith.native_training.distribution_utils import get_mpi_rank, \\\n  get_mpi_size, get_mpi_local_size, enable_sync_training, get_device_str\nfrom monolith.native_training.model_export.export_context import \\\n  is_exporting, is_dry_run_or_exporting\n\n\nFLAGS = flags.FLAGS\n\nfrom monolith.native_training import yarn_runtime\n\n\ndef check_port(host: str, port: int, timeout: float = 1) -> bool:\n  is_ipv6 = ':' in host.strip('[]')\n  skt = socket.socket(socket.AF_INET6 if is_ipv6 else socket.AF_INET,\n                      socket.SOCK_STREAM)\n  start = time.time()\n  skt.settimeout(timeout)\n  while True:\n    try:\n      skt.connect((host, int(port)))\n      return True\n    except socket.timeout as e:\n      return False\n    except socket.error as e:\n      now = time.time()\n      remaining = timeout - int(now - start)\n      if remaining > 0:\n        skt.settimeout(remaining)\n        continue\n      else:\n        return False\n\n\nclass MLPEnv(object):\n\n  def __init__(self):\n    self._mlp_env = {\n        k: v\n        for k, v in os.environ.items()\n        if k.startswith('MLP_') or k.startswith('MPI_')\n    }\n    self.framework = self._get('MLP_FRAMEWORK')\n    self.ssh_port = self._get('MLP_SSH_PORT')\n    self.log_path = self._get('MLP_LOG_PATH')\n    self.debug_port = self._get('MLP_DEBUG_PORT')\n    self.entrypoint_dir = self._get('MLP_ENTRYPOINT_DIR')\n    self.task_cmd = self._get('MLP_TASK_CMD')\n    self.role = self._get('MLP_ROLE', \"\").upper()\n    self.all_roles = {\n        k.split('_')[1]: int(self._get(k, 0))\n        for k in self._mlp_env\n        if k.endswith('_NUM') and len(k.split('_')) == 3\n    }\n    if self.enable_mpi:\n      self.index = get_mpi_rank()\n      self.all_roles['WORKER'] = get_mpi_size()\n      self.port = int(self._get('MLP_PORT', 0)) + self.index\n      logging.info(\n          f'total process is {get_mpi_size()}, this is {get_mpi_rank()}, port is {self.port}'\n      )\n    else:\n      self.index = int(self._get('MLP_ROLE_INDEX', 0))\n      self.port = int(self._get('MLP_PORT', 0))\n      logging.info(f'enable_mpi is False, index {self.index}, port {self.port}')\n\n    if len(self._mlp_env) > 0 and len(self.all_roles) > 0:\n      self.avaiable = True\n    else:\n      self.avaiable = False\n\n    self.cpu = int(self._get('MLP_CPU', 0))\n    self.gpu = int(self._get('MLP_GPU', 0))\n    self.gpu_type = self._get('MLP_GPU_TYPE', \"\")\n    self.mem = int(self._get('MLP_MEM', 0))\n\n    self.host = yarn_runtime.get_local_host()  #self._get('MLP_HOST')\n\n    self._has_started_profiler = False\n\n  @property\n  def enable_mpi(self):\n    return 'OMPI_COMM_WORLD_RANK' in os.environ and self.role == \"WORKER\"\n\n  def _get(self, name: str, default=None):\n    value = self._mlp_env.get(name)\n    if value:\n      return value.strip().strip('\"').strip(\"'\")\n    else:\n      return default\n\n  def num_replicas(self, role: str = None):\n    role = (role or self.role).upper()\n    if self.enable_mpi and role == 'WORKER':\n      return get_mpi_size()\n    key = f'MLP_{role}_NUM'\n    logging.info(f\"{key}, mlp_env: {self._mlp_env}\")\n    return int(self._get(key, 0))\n\n  def get_all_host(self,\n                   role: str = None,\n                   is_primary: bool = True) -> List[str]:\n    role = (role or self.role).upper()\n    if is_primary:\n      key = f'MLP_{role}_ALL_PRIMARY_HOSTS'\n    else:\n      key = f'MLP_{role}_ALL_HOSTS'\n    return self._get(key)\n\n  def get_all_addrs(self,\n                    role: str = None,\n                    is_primary: bool = True) -> List[str]:\n    role = (role or self.role).upper()\n    if is_primary:\n      key = f'MLP_{role}_ALL_PRIMARY_ADDRS'\n    else:\n      key = f'MLP_{role}_ALL_ADDRS'\n    addrs = self._get(key)\n    if addrs:\n      return addrs.split(',')\n    else:\n      return []\n\n  def get_host(self,\n               role: str = None,\n               index: int = None,\n               is_primary: bool = True) -> str:\n    role = (role or self.role).upper()\n    if self.enable_mpi and role == 'WORKER':\n      index = (self.index if index is None else index) // get_mpi_local_size()\n    elif role == self.role:\n      index = self.index if index is None else index\n    else:\n      index = 0 if index is None else index\n    if is_primary:\n      key = f'MLP_{role}_{index}_PRIMARY_HOST'\n    else:\n      key = f'MLP_{role}_{index}_HOST'\n\n    return self._get(key)\n\n  def get_addr(self,\n               role: str = None,\n               index: int = None,\n               is_primary: bool = True) -> str:\n    role = (role or self.role).upper()\n    if role == self.role:\n      index = self.index if index is None else index\n    else:\n      index = 0 if index is None else index\n    host = self.get_host(role, index, is_primary)\n\n    if self.enable_mpi and role == 'WORKER':\n      key = f'MLP_{role}_0_PORT'\n      port = self._get(key)\n      if port is not None:\n        port = str(int(port) + index)\n    else:\n      key = f'MLP_{role}_{index}_PORT'\n      port = self._get(key)\n    if host and port:\n      return f'{host}:{port}'\n    else:\n      return None\n\n  def get_port(self, role: str = None, index: int = None) -> int:\n    role = (role or self.role).upper()\n    if self.enable_mpi and role == 'WORKER':\n      index = self.index if index is None else index\n      key = f'MLP_{role}_0_PORT'\n      return self._get(key, 2222) + index\n    else:\n      index = 0 if index is None else index\n      key = f'MLP_{role}_{index}_PORT'\n      return self._get(key, 2222)\n\n  def dispatcher_target(self, role: str = None) -> str:\n    addr = self.dispatcher_addr(role)\n    if addr:\n      return f'grpc://{addr}'\n    else:\n      return 'grpc://localhost:5050'\n\n  def dispatcher_addr(self, role: str = None) -> str:\n    role = (role or 'dispatcher').upper()\n    return self.get_addr(role)\n\n  def wait(self,\n           role: str = None,\n           index: int = 0,\n           timeout: int = -1,\n           use_ssh: bool = True):\n    host = self.get_host(role, index, True)\n    port = self.ssh_port if use_ssh else self.get_port(role, index)\n    if host:\n      current = 0\n      while True:\n        if timeout > 0 and current >= timeout:\n          logging.info(f'wait {host}:{port} timeout!')\n          break\n        if check_port(host, port):\n          return\n        else:\n          time.sleep(5)\n          current += 5\n    else:\n      logging.info('host is None')\n\n  def join(self, role: str = 'worker', index: int = 0, use_ssh: bool = True):\n    self.wait(role, index, use_ssh=use_ssh)\n    host = self.get_host(role, index, True)\n    port = self.ssh_port if use_ssh else self.get_port(role, index)\n    if host:\n      while True:\n        if not check_port(host, port, timeout=60):\n          break  #return\n        else:\n          time.sleep(10)\n    else:\n      logging.info('host is None')\n    if self._has_started_profiler:\n      try:\n        tf.profiler.experimental.stop()\n      except Exception as e:\n        logging.info(f'experimental stop error: {e}')\n      logging.info('profiler stopped!')\n    logging.info(f'current role: {self.role}:{self.index} exit')\n    os._exit(0)\n\n  @property\n  def queue_device(self) -> str:\n    if 'PS' in self.all_roles:\n      return \"/job:ps/task:0/device:CPU:0\"\n    elif 'WORKER' in self.all_roles:\n      return \"/job:worker/task:0/device:CPU:0\"\n    else:\n      return \"/device:CPU:0\"\n\n  def start_profiler(self, port=6666):\n    logging.info(f'start_profiler at {self.host}:{port}')\n    if self.enable_mpi:\n      port += self.index\n    tf.profiler.experimental.server.start(port)\n    self._has_started_profiler = True\n\n  def profiler_trace(self,\n                     role: str = 'dsworker',\n                     index: int = -1,\n                     host_tracer_level=2,\n                     python_tracer_level=0,\n                     device_tracer_level=1,\n                     delay_ms=10000):\n    logdir = self._get(\"TENSORBOARD_LOG_PATH\", \"/tensorboard_logs/\")\n    options = tf.profiler.experimental.ProfilerOptions(\n        host_tracer_level=host_tracer_level,\n        python_tracer_level=python_tracer_level,\n        device_tracer_level=device_tracer_level,\n        delay_ms=delay_ms)\n    if index < 0:\n      all_addrs = self.get_all_addrs(role)\n      service_addr = ','.join(map(lambda addr: f'grpc://{addr}', all_addrs))\n    else:\n      service_addr = f'grpc://{self.get_addr(role, index)}'\n    tf.profiler.experimental.client.trace(service_addr=service_addr,\n                                          logdir=logdir,\n                                          duration_ms=delay_ms,\n                                          options=options)\n\n\ndef add_mpi_exception_hook():\n  if 'OMPI_COMM_WORLD_RANK' not in os.environ:\n    return\n  logging.info(\"add_mpi_exception_hook\")\n\n  # Global error handler\n  def global_except_hook(exctype, value, traceback):\n    try:\n      sys.stderr.write(\n          \"\\n*****************************************************\\n\")\n      sys.stderr.write(\"Uncaught exception was detected on rank {}. \\n\".format(\n          int(os.environ.get('OMPI_COMM_WORLD_RANK', -1))))\n      from traceback import print_exception\n      print_exception(exctype, value, traceback)\n      sys.stderr.write(\n          \"*****************************************************\\n\\n\\n\")\n      sys.stderr.write(\"\\n\")\n      sys.stderr.write(\"Calling MPI_Abort() to shut down MPI processes...\\n\")\n      sys.stderr.flush()\n    finally:\n      try:\n        import mpi4py.MPI\n        mpi4py.MPI.COMM_WORLD.Abort(1)\n      except Exception as e:\n        sys.stderr.write(\n            \"*****************************************************\\n\")\n        sys.stderr.write(\n            \"Sorry, we failed to stop MPI, this process will hang.\\n\")\n        sys.stderr.write(\n            \"*****************************************************\\n\")\n        sys.stderr.flush()\n        raise e\n\n  sys.excepthook = global_except_hook\n\n\nEXTRA_DSWORKERS = []\n\n\ndef mlp_pass(dispatcher_role: str = 'dispatcher',\n             dsworker_role: str = 'dsworker',\n             worker_role: str = 'worker',\n             ps_role: str = 'ps'):\n  dispatcher_role = None if dispatcher_role is None else dispatcher_role.upper()\n  dsworker_role = None if dsworker_role is None else dsworker_role.upper()\n  worker_role = None if worker_role is None else worker_role.upper()\n  pa_role = None if ps_role is None else ps_role.upper()\n\n  if FLAGS.dataset_use_dataservice:\n    _DatasetInitializerHook.begin = begin\n    _DatasetInitializerHook.after_create_session = after_create_session\n  mlp_env = MLPEnv()\n  if mlp_env.avaiable:\n    logging.info('MLP is available')\n    logging.info('mlp_env_host: %s, mlp_env_port: %s', mlp_env.host,\n                 mlp_env.port)\n    if mlp_env.role == dispatcher_role:\n      if dispatcher_role:\n        dispatcher = dsvc.DispatchServer(\n            dsvc.DispatcherConfig(port=mlp_env.port))\n        logging.info('Dispatcher started...')\n        mlp_env.join()\n    elif mlp_env.role == dsworker_role:\n      if dsworker_role:\n        logging.info('Waiting for dispatcher start...')\n        assert dispatcher_role is not None\n        mlp_env.wait(dispatcher_role, use_ssh=False)\n        logging.info('Dispatcher started, dsworker begin to start...')\n        dispatcher_address = mlp_env.dispatcher_addr(role=dispatcher_role)\n        worker = dsvc.WorkerServer(\n            dsvc.WorkerConfig(dispatcher_address=dispatcher_address,\n                              worker_address=f'{mlp_env.host}:{mlp_env.port}',\n                              port=mlp_env.port))\n        logging.info('Dsworker started....')\n        mlp_env.start_profiler()\n        mlp_env.join()\n    elif mlp_env.role == worker_role:\n      if FLAGS.dataset_use_dataservice:\n        if dispatcher_role:\n          logging.info(\"wait dispatcher start ...\")\n          mlp_env.wait(dispatcher_role, use_ssh=False)\n          FLAGS.data_service_dispatcher = mlp_env.dispatcher_target()\n        if dsworker_role:\n          logging.info(\"dispatcher started, wait ds worker start ...\")\n          for idx in range(mlp_env.num_replicas(role=dsworker_role)):\n            mlp_env.wait(dsworker_role, index=idx, use_ssh=False)\n            logging.info(f'dsworker {idx} started! ')\n\n        # Extra dsworkers on GPU worker\n        dispatcher_address = mlp_env.dispatcher_addr(role=dispatcher_role)\n        logging.info(\"dispatcher_address: %s\", dispatcher_address)\n        global EXTRA_DSWORKERS\n        for i in range(FLAGS.num_extra_dsworker_on_gpu_worker):\n          # base port number + gpu worker port offset + number generated by (gpu worker index, extra dswroker index)\n          port = (mlp_env.port - mlp_env.index) + get_mpi_size(\n          ) + mlp_env.index * FLAGS.num_extra_dsworker_on_gpu_worker + i\n          logging.info('extra_port: %s', port)\n          worker = dsvc.WorkerServer(\n              dsvc.WorkerConfig(dispatcher_address=dispatcher_address,\n                                worker_address=f'{mlp_env.host}:{port}',\n                                port=port))\n          EXTRA_DSWORKERS.append(worker)\n        logging.info('Start %s extra dsworkers on GPU worker %s',\n                     len(EXTRA_DSWORKERS), mlp_env.index)\n      logging.info(\n          f\"worker {mlp_env.index} start at {mlp_env.host}:{mlp_env.port}\")\n      logging.info(f'{mlp_env.all_roles}')\n      # if ps_role is None or ps_role not in mlp_env.all_roles:\n      #   mlp_env.start_profiler()\n\n\ndef begin(self):\n  self._initializer = self._iterator.initializer\n  self._broadcast_dataset_id = None\n  if not is_dry_run_or_exporting():\n    self._rank = -1\n    if enable_sync_training():\n      try:\n        enable_bps = int(os.getenv(\"MONOLITH_WITH_BYTEPS\", \"0\"))\n        if enable_bps:\n          import byteps.tensorflow as hvd\n        else:\n          import horovod.tensorflow as hvd\n\n        dataset_ids = tf.compat.v1.get_collection(key='registed_dataset_id')\n        if dataset_ids is not None and len(dataset_ids) > 0:\n          dataset_id = dataset_ids[0]\n          if dataset_id is not None:\n            self._rank = hvd.rank()\n            #with tf.device(None), tf.device(get_device_str(True)):\n            self._broadcast_dataset_id = [\n                dataset_id,\n                hvd.broadcast(tensor=dataset_id,\n                              root_rank=0,\n                              name=\"broadcast_dataset_id\")\n            ]\n            graph.clear_collection(name='registed_dataset_id')\n      except Exception as e:\n        logging.info(f'import byteps/horovod error: {e}')\n\n\ndef after_create_session(self, session, coord):\n  del coord\n  if self._broadcast_dataset_id is not None and not is_dry_run_or_exporting():\n    dataset_id, bc_dataset_id = session.run(self._broadcast_dataset_id)\n    logging.info(\n        f'dataset_id is {dataset_id}, bc_dataset_id is {bc_dataset_id}, rank {self._rank}'\n    )\n    self._broadcast_dataset_id = None\n  session.run(self._initializer)\n"
  },
  {
    "path": "monolith/native_training/model.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom datetime import datetime\nimport numpy as np\nfrom typing import Callable, Dict, List\n\nfrom absl import logging\n\nimport tensorflow as tf\n\nfrom monolith.core import model_registry\nfrom monolith.core.base_model_params import SingleTaskModelParams\nfrom monolith.native_training import entry, feature\nfrom monolith.native_training.input import (generate_ffm_example, slot_to_key)\nimport monolith.native_training.metric.deep_insight_ops as deep_insight_ops\nfrom monolith.native_training.native_task import NativeTask\n\n_NUM_SLOTS = 6\n_FFM_SLOT = ((0, 3, 16), (0, 4, 16), (1, 5, 16), (2, 3, 16), (2, 5, 16))\n_VOCAB_SIZES = [5, 5, 5, 5, 5, 5]\n_NUM_EXAMPLES = 64\n\n\ndef _parse_example(example: str) -> Dict[str, tf.Tensor]:\n\n  def _get_feature_map():\n    feature_map = {}\n    feature_map[\"label\"] = tf.io.FixedLenFeature([], dtype=tf.float32)\n    for i in range(len(_VOCAB_SIZES)):\n      feature_map[slot_to_key(i)] = tf.io.VarLenFeature(dtype=tf.int64)\n    return feature_map\n\n  features = tf.io.parse_example(example, _get_feature_map())\n  for k, v in features.items():\n    if isinstance(v, tf.sparse.SparseTensor):\n      features[k] = tf.RaggedTensor.from_sparse(v)\n  return features\n\n\nclass TestFFMModel(NativeTask):\n\n  def __init__(self, params):\n    super().__init__(params)\n    self.p = params\n\n  def create_input_fn(self, mode):\n\n    def input_fn():\n      # This keeps the training data stability so we can resume training from the\n      # checkpoint.\n      np.random.seed(0)\n      examples = [\n          generate_ffm_example(_VOCAB_SIZES) for i in range(_NUM_EXAMPLES)\n      ]\n      dataset = tf.data.Dataset.from_tensor_slices(examples)\n      dataset = dataset.batch(self.p.train.per_replica_batch_size,\n                              drop_remainder=True)\n      dataset = dataset.map(_parse_example,\n                            num_parallel_calls=tf.data.experimental.AUTOTUNE)\n      dataset = dataset.cache().repeat()\n      dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)\n\n      return dataset\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(features: Dict, mode: tf.estimator.ModeKeys,\n                 config: tf.estimator.RunConfig) -> tf.estimator.EstimatorSpec:\n      del config\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      slots = {}\n      fc = {}\n      bias_list = []\n      for i in range(_NUM_SLOTS):\n        slots.update({\n            i:\n                self.ctx.create_feature_slot(\n                    feature.FeatureSlotConfig(\n                        name=str(i),\n                        has_bias=i <= (_NUM_SLOTS // 2),\n                        bias_optimizer=entry.FtrlOptimizer(\n                            learning_rate=0.1,\n                            initial_accumulator_value=1e-6,\n                            beta=1.0),\n                        default_vec_optimizer=entry.SgdOptimizer(\n                            learning_rate=0.1)))\n        })\n        fc.update({i: feature.FeatureColumnV1(slots[i], slot_to_key(i))})\n        if i <= (_NUM_SLOTS // 2):\n          bias_list.append(fc[i].embedding_lookup(slots[i].get_bias_slice()))\n\n      bias_input = tf.concat(bias_list,\n                             axis=1,\n                             name='concatenate_tensor_from_{}_bias'.format(\n                                 len(bias_list)))\n      sum_bias = tf.reduce_sum(bias_input, axis=1)\n      dot_res = []\n      for user, item, dim in _FFM_SLOT:\n        user_vec = fc[user].embedding_lookup(slots[user].add_feature_slice(dim))\n        item_vec = fc[item].embedding_lookup(slots[item].add_feature_slice(dim))\n        dot_res.append(tf.reduce_sum(tf.multiply(user_vec, item_vec), 1))\n      ffm_out = tf.add_n(dot_res) + sum_bias\n      pred = tf.nn.sigmoid(ffm_out)\n      if mode == tf.estimator.ModeKeys.PREDICT:\n        return tf.estimator.EstimatorSpec(mode, predictions=pred)\n      loss = tf.reduce_sum(\n          tf.losses.binary_crossentropy(features[\"label\"], pred))\n\n      # Write deep insight\n      if self.p.metrics.enable_deep_insight and self.p.metrics.deep_insight_sample_ratio > 0:\n        deep_insight_client = deep_insight_ops.deep_insight_client(False)\n        now = datetime.now()\n        model_name = self.p.metrics.deep_insight_name\n        uids = tf.cast(tf.fill([self.p.train.per_replica_batch_size], 0),\n                       dtype=tf.int64)\n        req_times = tf.cast(tf.fill([self.p.train.per_replica_batch_size],\n                                    int(datetime.timestamp(now))),\n                            dtype=tf.int64)\n        sample_rates = tf.fill([self.p.train.per_replica_batch_size], 0.1)\n        target = \"ctr_head\"\n\n        deep_insight_op = deep_insight_ops.write_deep_insight(\n            deep_insight_client_tensor=deep_insight_client,\n            uids=uids,\n            req_times=req_times,\n            labels=features[\"label\"],\n            preds=pred,\n            sample_rates=sample_rates,\n            model_name=model_name,\n            target=target,\n            sample_ratio=0.01)\n        logging.info(\"model_name: {}, target: {}.\".format(model_name, target))\n      else:\n        deep_insight_op = tf.no_op()\n\n      update_global_step = tf.compat.v1.assign_add(global_step, 1)\n      all_embeddings = [v.get_all_embeddings_concat() for v in fc.values()]\n      emb_grads = tf.gradients(loss, all_embeddings)\n      with tf.control_dependencies([update_global_step]):\n        train_op = tf.group(\n            self.ctx.apply_embedding_gradients(zip(emb_grads, all_embeddings)),\n            deep_insight_op)\n      return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)\n\n    return model_fn\n\n  def create_serving_input_receiver_fn(self):\n\n    def serving_input_receiver_fn():\n      receiver_tensors = {}\n      instances_placeholder = tf.compat.v1.placeholder(dtype=tf.string,\n                                                       shape=(None,))\n      receiver_tensors[\"instances\"] = instances_placeholder\n      parsed_results = _parse_example(instances_placeholder)\n\n      return tf.estimator.export.ServingInputReceiver(parsed_results,\n                                                      receiver_tensors)\n\n    return serving_input_receiver_fn\n\n\n@model_registry.RegisterSingleTaskModel\nclass FFMParams(SingleTaskModelParams):\n\n  def task(self):\n    p = TestFFMModel.params()\n    p.train.per_replica_batch_size = 64\n    return p\n"
  },
  {
    "path": "monolith/native_training/model_comp_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nos.environ[\"MONOLITH_WITH_HOROVOD\"] = \"True\"\nos.environ[\"HOROVOD_AUTOTUNE\"] = \"1\"\nos.environ[\"HOROVOD_CYCLE_TIME\"] = \"0.1\"\nos.environ[\"MONOLITH_SYNC_EMPTY_RANK0_PS_SHARD\"] = \"0\"\nos.environ[\"MONOLITH_WITH_ALLREDUCE_FUSION\"] = \"one\"\nos.environ['MONOLITH_ROOT_LOG_INTERVAL'] = \"10\"\n\nimport time\nimport tensorflow as tf\ntf.compat.v1.set_random_seed(42)\nimport getpass\nfrom tensorflow.python.framework import test_util\n\nfrom monolith.native_training import cpu_training\nfrom monolith.native_training.native_model import MonolithModel\nfrom monolith.native_training.estimator import Estimator, EstimatorSpec\nfrom monolith.native_training.data.training_instance.python.parser_utils import advanced_parse\nfrom monolith.native_training.entry import (AdagradOptimizer, Fp16Compressor,\n                                            ZerosInitializer)\nfrom monolith.native_training import layers\nimport horovod.tensorflow as hvd\n\ndeep = {\n    \"initializer\":\n        ZerosInitializer(),\n    \"optimizer\":\n        AdagradOptimizer(learning_rate=0.05,\n                         weight_decay_factor=0.0,\n                         initial_accumulator_value=0.1,\n                         warmup_steps=0),\n    \"compressor\":\n        Fp16Compressor()\n}\n\nnum_features = 17\nbatch_size = 455\nemb_dim = 15\nfeature_names = [f'feature{i}' for i in range(num_features)]\nfid_max_val = 100000\n\n\ndef lookup_tf_embedding(features, f_name, dim):\n  f = tf.RaggedTensor.from_row_splits(features[f'tf_{f_name}_p1'], features[f'tf_{f_name}_p2'], validate=False)\n  embeddings = tf.nn.embedding_lookup(\n      params=tf.Variable(initial_value=tf.zeros(shape=(fid_max_val + 1, dim))), \n      ids=f.values)\n  return tf.math.segment_sum(embeddings, f.value_rowids())\n\nclass EmbeddingUpdateTask(MonolithModel):\n  \"\"\"A test task that will compare TF and monolith embedding update.\"\"\"\n  \n  def __init__(self, params=None):\n    super(EmbeddingUpdateTask, self).__init__(params)\n    self.train.max_steps = 50\n    self.train.per_replica_batch_size = batch_size\n\n  def input_fn(self, mode):\n\n    def decomp_func(features):\n      # note: this is a workaround to pass fids to model_fn, since all instances of RaggedTensor are gone\n      for i in range(num_features):\n        features[f'tf_feature{i}_p1'] = features[f'feature{i}'].values\n        features[f'tf_feature{i}_p2'] = features[f'feature{i}'].row_splits\n      return advanced_parse(features)\n\n    @tf.function\n    def input_tensors():\n      features = {}\n      for i in range(num_features):\n        features[f'feature{i}'] = tf.random.uniform((tf.random.uniform((), 1, 25, dtype=tf.int32),), 0, fid_max_val, dtype=tf.int64)\n      features['label'] = tf.cast(tf.random.uniform((), 0, 2, dtype=tf.int32), tf.float32)\n      return features\n\n    return tf.data.experimental.Counter(0, 1).map(lambda _: input_tensors(), tf.data.AUTOTUNE).\\\n      apply(tf.data.experimental.dense_to_ragged_batch(batch_size, True)).\\\n        map(decomp_func, tf.data.AUTOTUNE).prefetch(tf.data.AUTOTUNE)\n    \n\n  def model_fn(self, features, mode):\n    with tf.device(\"/device:GPU:0\"):\n      for f_name in feature_names:\n        self.create_embedding_feature_column(f_name, occurrence_threshold=1)\n\n      tf_embeddings = [lookup_tf_embedding(features, f_name, emb_dim) for f_name in feature_names]\n      embeddings = self.lookup_embedding_slice(features=feature_names,\n                                              slice_name='vec',\n                                              slice_dim=emb_dim,\n                                              **deep)\n      # if mode == tf.estimator.ModeKeys.PREDICT:\n      #   return tf.estimator.EstimatorSpec(mode, predictions=tf.constant(0))\n\n      embed_concat = tf.concat(embeddings, axis=1)\n      tf_embed_concat = tf.concat(tf_embeddings, axis=1)\n\n      assert_op = tf.compat.v1.assert_equal(embed_concat, tf_embed_concat)\n      with tf.compat.v1.control_dependencies([assert_op]):  \n        # pred = layers.MLP(\n        #   output_dims=[64, 32, 1], activations='relu')(embed_concat)\n\n        pred = tf.keras.Sequential([\n          tf.keras.layers.Dense(64, activation=\"relu\"),\n          tf.keras.layers.Dense(32, activation=\"relu\"),\n          tf.keras.layers.Dense(1)\n        ])(embed_concat)\n\n        tf_pred = tf.keras.Sequential([\n          tf.keras.layers.Dense(64, activation=\"relu\"),\n          tf.keras.layers.Dense(32, activation=\"relu\"),\n          tf.keras.layers.Dense(1)\n        ])(tf_embed_concat)\n\n      label = features['label']\n      loss = tf.reduce_mean(tf.losses.mean_squared_error(pred, label))\n      tf_loss = tf.reduce_mean(tf.losses.mean_squared_error(tf_pred, label))\n      \n      optimizer = tf.compat.v1.train.AdagradOptimizer(0.05)\n      # with tf.device(\"/device:CPU:0\"):\n        # loss = tf.compat.v1.Print(loss, [loss], 'monolith loss')\n        # tf_loss = tf.compat.v1.Print(tf_loss, [tf_loss], 'tf loss')\n      \n      return EstimatorSpec(\n        loss=loss + tf_loss,\n        pred=[pred, tf_pred],\n        label=[label, label],\n        head_name=['monolith', 'tf'],\n        classification=[False, False],\n        optimizer=optimizer\n      )\n  def serving_input_receiver_fn(self):\n    pass\n\n\nclass CpuSyncTrainTest(tf.test.TestCase):\n  \n  def _create_config(self, gpu, multi_hash_table):\n    return cpu_training.DistributedCpuTrainingConfig(\n      # save_checkpoints_steps=10000,\n      num_ps=0,\n      num_workers=hvd.size(),\n      model_dir=f'/tmp/{getpass.getuser()}/monolith_test/{int(time.time())}',\n      reorder_fids_in_data_pipeline=True,\n      embedding_prefetch_capacity=0,\n      enable_sync_training=True,\n      enable_gpu_training=gpu,\n      enable_realtime_training=False,\n      use_native_multi_hash_table=multi_hash_table,\n      index=hvd.rank(),\n    )\n\n  def test_embedding_update(self):\n    hvd.init()\n    p = EmbeddingUpdateTask.params().instantiate()\n    config = self._create_config(False, False)\n    cpu_training.distributed_sync_train(config, p)\n\n    config = self._create_config(False, True)\n    cpu_training.distributed_sync_train(config, p)\n\n    if test_util.is_gpu_available(cuda_only=True):\n      config = self._create_config(True, False)\n      cpu_training.distributed_sync_train(config, p)\n      \n      config = self._create_config(True, True)\n      cpu_training.distributed_sync_train(config, p)\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/model_dump/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\npy_proto_library(\n    name = \"monolith_model_py_proto\",\n    srcs = [\"monolith_model.proto\"],\n    srcs_version = \"PY2AND3\",\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//monolith/native_training/runtime/hash_table/compressor:float_compressor_py_proto\",\n        \"//monolith/native_training/runtime/hash_table/initializer:initializer_config_py_proto\",\n        \"//monolith/native_training/runtime/hash_table/optimizer:optimizer_py_proto\",\n    ],\n)\n\npy_library(\n    name = \"graph_utils\",\n    srcs = [\"graph_utils.py\"],\n    deps = [\n        \"//idl:line_id_py_proto\",\n        \"//monolith/native_training:utils\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_library(\n    name = \"dump_utils\",\n    srcs = [\"dump_utils.py\"],\n    deps = [\n        \":graph_utils\",\n        \":monolith_model_py_proto\",\n        \"//monolith/native_training/data:feature_list\",\n        \"//monolith/native_training/data:item_pool_hook\",\n        \"//monolith/native_training/data:parsers_py\",\n    ],\n)\n\n"
  },
  {
    "path": "monolith/native_training/model_dump/dump_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport tensorflow as tf\nimport copy\nimport pickle\nfrom io import BytesIO\nfrom inspect import signature, Parameter\nfrom typing import Union, Dict, List, Optional, Any, Set\n\nfrom tensorflow.python.data.ops import dataset_ops\nfrom tensorflow_estimator.python.estimator import util\nfrom tensorflow.core.framework import variable_pb2\nfrom tensorflow.python.ops.variables import Variable\nfrom tensorflow.python.framework import ops\n\nSaveSliceInfo = Variable.SaveSliceInfo\nfrom monolith.native_training import entry\nfrom monolith.native_training.model_dump.monolith_model_pb2 import ProtoModel, ModelDump, \\\n  HashTableConfig, FeatureSliceDim, Combiner, FeatureCombiner\nfrom monolith.native_training.model_dump.graph_utils import DatasetInitHook, GraphDefHelper, \\\n  DRY_RUN, _node_name\nfrom monolith.native_training.data.utils import get_slot_feature_name\nfrom monolith.native_training.embedding_combiners import ReduceMean, ReduceSum, FirstN\nfrom monolith.native_training.runtime.hash_table import embedding_hash_table_pb2\nfrom monolith.native_training.data.parsers import get_default_parser_ctx\nfrom idl.matrix.proto.example_pb2 import OutConfig\nfrom monolith.native_training.data.datasets import POOL_KEY\nfrom monolith.native_training.model_export.export_context import get_current_export_ctx\nfrom monolith.native_training.data.item_pool_hook import ItemPoolSaveRestoreHook\nfrom monolith.native_training.data.feature_list import get_feature_name_and_slot\n\n\nclass DumpUtils(object):\n  _instance = None\n\n  def __new__(cls, *agrs, **kwds):\n    if cls._instance is None:\n      cls._instance = object.__new__(cls)\n\n    return cls._instance\n\n  def __init__(self, enable: bool = False):\n    if not hasattr(self, \"enable\"):\n      self.enable = enable\n      self._params: Set[str] = set()\n      self._run_config = None\n      self._user_params = []\n      self.train, self.train_graph = None, None\n      self.infer, self.infer_graph = None, None\n      self._ps_sub_model, self._dense_sub_model = {}, {}\n      self._table_configs: List[HashTableConfig] = []\n      self._feature_slice_dims: List[FeatureSliceDim] = []\n      self._feature_combiners: List[FeatureCombiner] = []\n\n  def add_config(self, run_config: str):\n    self._run_config = run_config\n\n  def add_user_params(self, user_params: List):\n    self._user_params = user_params\n\n  @property\n  def model_dump(self) -> ProtoModel:\n    graph = tf.compat.v1.get_default_graph()\n    if hasattr(graph, 'monolith_model_dump'):\n      monolith_model_dump = graph.monolith_model_dump\n      return monolith_model_dump\n    else:\n      setattr(graph, 'monolith_model_dump', ProtoModel())\n      return graph.monolith_model_dump\n\n  def update_kwargs_with_default(self, func, kwargs):\n    params = signature(func).parameters\n    for key in kwargs:\n      if (kwargs[key] is not None) or (key\n                                       not in params) or (params[key].default\n                                                          == Parameter.empty):\n        continue\n      kwargs[key] = params[key].default\n\n  def record_feature(self, func):\n\n    def wraper(*args, **kwargs):\n      self.update_kwargs_with_default(func, kwargs)\n      if self.need_record:\n        proto = self.model_dump.features.add()\n        if args:\n          params = signature(func).parameters.values()\n          for p, value in zip(params, args):\n            if p.name == 'self':\n              continue\n            try:\n              if p.name == 'feature_name' and isinstance(value, int):\n                feature_name, _ = get_feature_name_and_slot(value)\n                setattr(proto, p.name, feature_name)\n              else:\n                setattr(proto, p.name, value)\n            except Exception as e:\n              logging.warning(f\"{p.name} is not in proto, {e}\")\n\n        for key, value in kwargs.items():\n          try:\n            if key == 'feature_name' and isinstance(value, int):\n              feature_name, _ = get_feature_name_and_slot(value)\n              setattr(proto, key, feature_name)\n            elif value is not None:\n              setattr(proto, key, value)\n          except Exception as e:\n            logging.warning(f\"{key} is not in proto, func {func}, {e}\")\n\n      return func(*args, **kwargs)\n\n    return wraper\n\n  def record_slice(self, func):\n\n    def warper(*args, **kwargs):\n      self.update_kwargs_with_default(func, kwargs)\n      if self.need_record:\n        if kwargs.get('learning_rate_fn', None) is not None:\n          raise Exception('for safety purpose learning_rate_fn is not allowed')\n        proto = self.model_dump.emb_slices.add()\n        if args:\n          params = signature(func).parameters.values()\n          for p, value in zip(params, args):\n            if p.name == 'self':\n              continue\n\n            try:\n              if value is not None:\n                if p.name == 'features':\n                  proto.features = repr(value)\n                elif p.name in {'initializer', 'optimizer', 'compressor'}:\n                  getattr(proto, p.name).CopyFrom(value.as_proto())\n                else:\n                  setattr(proto, p.name, value)\n            except Exception as e:\n              logging.warning(f\"{p.name} is not in proto, {e}\")\n\n        for key, value in kwargs.items():\n          try:\n            if value is not None:\n              if key == 'features':\n                proto.features = repr(value)\n              elif key in {'initializer', 'optimizer', 'compressor'}:\n                getattr(proto, key).CopyFrom(value.as_proto())\n              else:\n                setattr(proto, key, value)\n          except Exception as e:\n            logging.warning(f\"{key} is not in proto, func {func}, {e}\")\n\n        results = func(*args, **kwargs)\n\n        if isinstance(results, (list, tuple)):\n          for res in results:\n            proto.output_tensor_names.append(res.name)\n        else:\n          proto.output_tensor_names.append(results.name)\n\n        return results\n      else:\n        return func(*args, **kwargs)\n\n    return warper\n\n  def record_receiver(self, func):\n\n    def warper(*args, **kwargs):\n      self.update_kwargs_with_default(func, kwargs)\n      receiver = func(*args, **kwargs)\n      if self.need_record:\n        proto = self.model_dump.serving_input_receiver_fn\n        proto.parser_type = get_default_parser_ctx().parser_type\n        for k, ts in receiver.features.items():\n          if isinstance(ts, tf.RaggedTensor):\n            proto.features[k] = repr({\n                \"values\": ts.values.name,\n                \"row_splits\": ts.row_splits.name,\n                \"is_ragged\": True,\n            })\n          else:\n            if len(ts.shape) > 0:\n              last_dim = ts.shape[-1]\n              if not isinstance(last_dim, int):\n                if hasattr(last_dim, 'value'):\n                  last_dim = last_dim.value\n            else:\n              last_dim = 0\n\n            proto.features[k] = repr({\n                \"name\": ts.name,\n                \"is_ragged\": False,\n                \"dtype\": ts.dtype,\n                \"last_dim\": last_dim\n            })\n\n        for k, ts in receiver.receiver_tensors.items():\n          proto.receiver_name[k] = ts.name\n\n      return receiver\n\n    return warper\n\n  def record_params(self, model):\n    if not self.need_record:\n      return\n    skip_attrs = {\n        '_abc_impl', '_ctx', '_export_outputs', '_global_step', '_losses',\n        '_occurrence_threshold', '_private_children', 'children', 'ctx',\n        'fc_dict', 'fs_dict', 'losses', 'slice_dict', '_layout_dict',\n        '_training_hooks'\n    }\n    for attr_name in dir(model):\n      if attr_name.startswith('__') or attr_name in skip_attrs:\n        continue\n      attr = getattr(model, attr_name)\n      if callable(attr):\n        continue\n      self._params.add(attr_name)\n\n  def get_params_bytes(self, model) -> bytes:\n    if not self.need_record:\n      return\n\n    params = {'_layout_dict'} | self._params\n    model_params = {}\n    for attr_name in params:\n      attr = getattr(model, attr_name)\n      if attr_name == 'p':\n        params = copy.deepcopy(model.p)\n        params.cls = None\n        model_params['p'] = params\n      elif attr_name == '_layout_dict':\n        if attr:\n          model_params[attr_name] = {\n              name: out_cfg.SerializeToString()\n              for name, out_cfg in attr.items()\n          }\n        else:\n          model_params[attr_name] = attr\n      else:\n        model_params[attr_name] = attr\n\n    f = BytesIO()\n    pickle.dump(model_params, f)\n    return f.getvalue()\n\n  @classmethod\n  def add_signature(cls, proto_model, graph: tf.Graph):\n    export_ctx = get_current_export_ctx()\n    if export_ctx:\n      for signature in export_ctx.signatures(graph):\n        signature_proto = proto_model.signature.add()\n        signature_proto.name = signature.name\n        for ip_key, value in signature.inputs.items():\n          signature_proto.inputs[ip_key] = value.name\n        for op_key, value in signature.outputs.items():\n          signature_proto.outputs[op_key] = value.name\n\n  @classmethod\n  def restore_signature(cls, proto_model, graph: tf.Graph):\n    export_ctx = get_current_export_ctx()\n    if export_ctx:\n      for signature in proto_model.signature:\n        name = signature.name\n        inputs = {\n            ip_key: graph.get_tensor_by_name(value)\n            for ip_key, value in signature.inputs.items()\n        }\n        outputs = {\n            op_key: graph.get_tensor_by_name(value)\n            for op_key, value in signature.outputs.items()\n        }\n        export_ctx.add_signature(graph, name, inputs, outputs)\n\n  def add_model_fn(self,\n                   model,\n                   mode: str,\n                   features: Dict[str, tf.Tensor],\n                   label: Union[tf.Tensor, List[tf.Tensor], Dict[str, tf.Tensor]],\n                   loss: Optional[tf.Tensor], \n                   pred: Union[tf.Tensor, List[tf.Tensor], Dict[str, tf.Tensor]],\n                   head_name: Union[str, List[str]],\n                   is_classification: Union[bool, List[bool]]):\n    if not self.need_record: return\n\n    model_dump = self.model_dump\n    model_dump.params = self.get_params_bytes(model)\n    model_fn = model_dump.model_fn\n    if label is not None:\n      if isinstance(label, (tuple, list)):\n        model_fn.label.extend(['' if t is None else t.name for t in label])\n      elif isinstance(label, dict):\n        for value in label.values():\n          model_fn.label.append('' if value is None else value.name)\n      else:\n        model_fn.label.append(label.name)\n    if loss is not None:\n      model_fn.loss = loss.name\n    if isinstance(pred, (tuple, list)):\n      model_fn.predict.extend([t.name for t in pred if t is not None])\n    elif isinstance(pred, dict):\n      for value in pred.values():\n        model_fn.predict.append(value.name)\n    else:\n      model_fn.predict.append(pred.name)\n\n    if head_name:\n      if isinstance(head_name, str):\n        model_fn.head_name.append(head_name)\n      else:\n        assert isinstance(head_name, (list, tuple))\n        model_fn.head_name.extend(head_name)\n    else:\n      if label is not None and isinstance(label, dict):\n        model_fn.head_name.extend(list(label.keys()))\n      if isinstance(pred, dict):\n        model_fn.head_name.extend(list(pred.keys()))\n\n    if is_classification is not None:\n      logging.info(\"dumped is_classification {}\".format(is_classification))\n      if isinstance(is_classification, bool):\n        model_fn.classification.append(is_classification)\n      else:\n        assert isinstance(is_classification, list)\n        model_fn.classification.extend(is_classification)\n    \n    summaries = [x.op.name for x in ops.get_collection(ops.GraphKeys.SUMMARIES)]\n    if len(summaries) > 0:\n      logging.info(\"dumped user summaries {}\".format(summaries))\n      model_fn.summary.extend(summaries)\n\n    regged_features = {fc.feature_name for fc in self.model_dump.features}\n    for name, ts in features.items():\n      if name not in regged_features and not isinstance(ts, tf.RaggedTensor):\n        model_fn.non_ragged_features[name] = ts.name\n\n    graph = tf.compat.v1.get_default_graph()\n    extra_losses = model_fn.extra_losses\n    for ts in getattr(graph, '__losses', []):\n      extra_losses.append(ts.name)\n\n    export_outputs = getattr(graph, '__export_outputs', {})\n    if export_outputs:\n      for name, predict_output in export_outputs.items():\n        extra_output = self.model_dump.extra_output.add()\n        extra_output.signature_name = name\n        outputs = predict_output.outputs\n        if isinstance(outputs, dict):\n          for key, ts in outputs.items():\n            extra_output.fetch_dict[key] = ts.name\n        else:\n          extra_output.fetch_dict[ts.name] = ts.name\n\n    training_hooks = getattr(graph, '__training_hooks', [])\n    if training_hooks:\n      if len(training_hooks) == 1 and isinstance(training_hooks[0],\n                                                 ItemPoolSaveRestoreHook):\n        pass\n      else:\n        raise Exception('For safety purpose, customer hooks is not allowed!')\n\n    self.add_signature(model_dump, graph)\n\n    variables = tf.compat.v1.all_variables()\n    if variables:\n      for v in variables:\n        save_slice_info = v._get_save_slice_info()\n        if save_slice_info is not None:\n          save_slice_info_bytes = save_slice_info.to_proto().SerializeToString()\n          model_dump.save_slice_info[_node_name(v.name)] = save_slice_info_bytes\n\n    if hasattr(graph, 'monolith_model_dump'):\n      graph_def = graph.as_graph_def()\n      if mode == tf.estimator.ModeKeys.TRAIN:\n        self.train = graph.monolith_model_dump\n        self.train_graph = copy.deepcopy(graph_def)\n      else:\n        self.infer = graph.monolith_model_dump\n        self.infer_graph = copy.deepcopy(graph_def)\n\n  def add_input_fn(self, results: Dict[str, Union[tf.Tensor, tf.RaggedTensor]]):\n    if not self.need_record:\n      return\n\n    input_fn = self.model_dump.input_fn\n    if isinstance(results, (list, tuple)):\n      features, label = results[0], results[1]\n    else:\n      features, label = results, None\n\n    assert isinstance(features, dict)\n    for key, ts in features.items():\n      if isinstance(ts, tf.RaggedTensor):\n        input_fn.output_features[key] = repr({\n            \"name\": ts.values.name.split(':')[0],\n            \"is_ragged\": True,\n        })\n      else:\n        input_fn.output_features[key] = repr({\n            \"name\": ts.name,\n            \"is_ragged\": False\n        })\n    if label is not None:\n      input_fn.label = label.name\n\n    input_fn.parser_type = get_default_parser_ctx().parser_type\n    pools = tf.compat.v1.get_collection(POOL_KEY)\n    if pools:\n      assert len(pools) == 1\n      input_fn.item_pool = pools[0].name\n\n  def add_sub_model(self, sub_model_type: str, name: str, graph: tf.Graph):\n    if not self.need_record:\n      return\n    assert sub_model_type in {'ps', 'dense'}\n    if sub_model_type == 'ps' and name in self._ps_sub_model:\n      return\n    if sub_model_type == 'dense' and name in self._dense_sub_model:\n      return\n    logging.info(f'add_sub_model: {sub_model_type}-{name}')\n\n    proto = ProtoModel()\n    graph_def = graph.as_graph_def()\n    proto.graph_def = graph_def.SerializeToString()\n    self.add_signature(proto, graph)\n\n    if sub_model_type == 'ps':\n      self._ps_sub_model[name] = proto\n    elif sub_model_type == 'dense':\n      self._dense_sub_model[name] = proto\n    else:\n      raise Exception('sub_model error!')\n\n  def restore_sub_model(self, sub_model_type: str):\n    export_ctx = get_current_export_ctx()\n    if export_ctx:\n      if sub_model_type == 'ps':\n        for name, sub_model in self._ps_sub_model.items():\n          with export_ctx.sub_graph(name).as_default() as g:\n            sub_graph = tf.compat.v1.GraphDef()\n            sub_graph.ParseFromString(sub_model.graph_def)\n            tf.import_graph_def(sub_graph, name=\"\")\n            self.restore_signature(sub_model, g)\n            logging.info(f'restore_sub_model: {sub_model_type}-{name}')\n      elif sub_model_type == 'dense':\n        for name, sub_model in self._dense_sub_model.items():\n          with export_ctx.dense_sub_graph(name).as_default() as g:\n            sub_graph = tf.compat.v1.GraphDef()\n            sub_graph.ParseFromString(sub_model.graph_def)\n            tf.import_graph_def(sub_graph, name=\"\")\n            self.restore_signature(sub_model, g)\n            logging.info(f'restore_sub_model: {sub_model_type}-{name}')\n      else:\n        raise Exception(f'sub_model_type: {sub_model_type} error ')\n\n  def add_optimizer(self, optimizer):\n    if not self.need_record:\n      return\n    f = BytesIO()\n    pickle.dump(optimizer, f)\n    value = f.getvalue()\n    self.model_dump.optimizer = value\n\n  def dump(self, fname: str):\n    if not self.enable:\n      return\n\n    md = ModelDump()\n    if self._run_config:\n      md.run_config = self._run_config\n\n    if self._user_params:\n      logging.info(\"xxx dump to md with user_params {}\".format(\n          self._user_params))\n      md.user_params.extend(self._user_params)\n\n    md.model_dump['train'].CopyFrom(self.train)\n    md.model_dump['train'].graph_def = self.train_graph.SerializeToString()\n\n    if self.infer is not None:\n      md.model_dump['infer'].CopyFrom(self.infer)\n      md.model_dump['infer'].graph_def = self.infer_graph.SerializeToString()\n\n    for name, sub_model in self._ps_sub_model.items():\n      if sub_model is not None:\n        print('dump ps_sub_model: ', name, flush=True)\n        md.ps_sub_model_dump[name].CopyFrom(sub_model)\n\n    for name, sub_model in self._dense_sub_model.items():\n      if sub_model is not None:\n        print('dump dense_sub_model: ', name, flush=True)\n        md.dense_sub_model_dump[name].CopyFrom(sub_model)\n\n    for table_config in self._table_configs:\n      md.table_configs.add().CopyFrom(table_config)\n\n    for feature_slice_dim in self._feature_slice_dims:\n      md.feature_slice_dims.add().CopyFrom(feature_slice_dim)\n\n    for feature_combiner in self._feature_combiners:\n      md.feature_combiners.add().CopyFrom(feature_combiner)\n\n    with tf.io.gfile.GFile(fname, 'wb') as ostream:\n      ostream.write(file_content=md.SerializeToString())\n\n  def load(self, fname: str):\n    with tf.io.gfile.GFile(fname, 'rb') as ostream:\n      md = ModelDump()\n      md.ParseFromString(ostream.read())\n      self._run_config = md.run_config\n\n      self.train = md.model_dump['train']\n      train_graph = tf.compat.v1.GraphDef()\n      train_graph.ParseFromString(self.train.graph_def)\n      self.train_graph = train_graph\n      self.train.graph_def = b''\n\n      if 'infer' in md.model_dump:\n        self.infer = md.model_dump['infer']\n        infer_graph = tf.compat.v1.GraphDef()\n        infer_graph.ParseFromString(self.infer.graph_def)\n        self.infer_graph = infer_graph\n        self.infer.graph_def = b''\n      else:\n        self.infer = None\n        self.infer_graph = None\n\n      self._table_configs.extend(md.table_configs)\n      self._feature_slice_dims.extend(md.feature_slice_dims)\n      self._feature_combiners.extend(md.feature_combiners)\n      self._user_params.extend(md.user_params)\n      logging.info(\"xxx load from md with user_params {}\".format(\n          self._user_params))\n\n      for name, sub_model in md.ps_sub_model_dump.items():\n        self._ps_sub_model[name] = sub_model\n\n      for name, sub_model in md.dense_sub_model_dump.items():\n        self._dense_sub_model[name] = sub_model\n\n  def get_proto_model(self,\n                      mode: str = tf.estimator.ModeKeys.TRAIN) -> ProtoModel:\n    graph = tf.compat.v1.get_default_graph()\n    if mode in (tf.estimator.ModeKeys.TRAIN, tf.estimator.ModeKeys.EVAL):\n      return self.train\n    else:\n      return self.infer\n\n  def get_graph_helper(self, mode: str) -> GraphDefHelper:\n    graph = tf.compat.v1.get_default_graph()\n    if hasattr(graph, 'graph_def_helper'):\n      return graph.graph_def_helper\n    else:\n      save_slice_info = {}\n      if mode in (tf.estimator.ModeKeys.TRAIN, tf.estimator.ModeKeys.EVAL):\n        for name, info_bytes in self.train.save_slice_info.items():\n          save_slice_info_def = variable_pb2.SaveSliceInfoDef()\n          save_slice_info_def.ParseFromString(info_bytes)\n          save_slice_info[name] = SaveSliceInfo(\n              save_slice_info_def=save_slice_info_def)\n        helper = GraphDefHelper(self.train_graph, save_slice_info)\n      else:\n        for name, info_bytes in self.infer.save_slice_info.items():\n          save_slice_info_def = variable_pb2.SaveSliceInfoDef()\n          save_slice_info_def.ParseFromString(info_bytes)\n          save_slice_info[name] = SaveSliceInfo(\n              save_slice_info_def=save_slice_info_def)\n        helper = GraphDefHelper(self.infer_graph, save_slice_info)\n\n      setattr(graph, 'graph_def_helper', helper)\n      return helper\n\n  def restore_params(self) -> Dict[str, Any]:\n    params = self.train.params\n    if params is None or len(params) == 0:\n      return None\n\n    f = BytesIO(params)\n    model_params = pickle.load(f)\n    layout_dict = model_params.get('_layout_dict')\n    if layout_dict:\n      layout_dict_tmp = {}\n      for name, out_cfg_str in layout_dict.items():\n        out_cfg = OutConfig()\n        out_cfg.ParseFromString(out_cfg_str)\n        layout_dict_tmp[name] = out_cfg\n      layout_dict.update(layout_dict_tmp)\n    else:\n      raise Exception('layout_dict is empty')\n    if '_training_hooks' in model_params:\n      del model_params['_training_hooks']\n    return model_params\n\n  def get_config(self):\n    return self._run_config\n\n  def get_user_params(self):\n    return self._user_params\n\n  @property\n  def need_record(self) -> bool:\n    graph = tf.compat.v1.get_default_graph()\n    return self.enable and not hasattr(graph, DRY_RUN)\n\n  @property\n  def table_configs(self) -> Dict[str, entry.HashTableConfigInstance]:\n    result = {}\n    for tcfg in self._table_configs:\n      table_config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n      table_config.ParseFromString(tcfg.table_config)\n      result[tcfg.name] = entry.HashTableConfigInstance(\n          table_config, list(tcfg.learning_rates),\n          list(tcfg.extra_restore_names))\n    return result\n\n  @table_configs.setter\n  def table_configs(self, table_confs: Dict[str,\n                                            entry.HashTableConfigInstance]):\n    if table_confs:\n      assert isinstance(table_confs, dict)\n      self._table_configs.clear()\n      for name, tcfg in table_confs.items():\n        hash_table_config = HashTableConfig(\n            name=name, table_config=tcfg._table_config.SerializeToString())\n        if tcfg.extra_restore_names:\n          hash_table_config.extra_restore_names.extend(tcfg.extra_restore_names)\n        if tcfg.learning_rate_fns:\n          if all(isinstance(lr, (float, int)) for lr in tcfg.learning_rate_fns):\n            hash_table_config.learning_rates.extend(tcfg.learning_rate_fns)\n          else:\n            raise Exception('learning_rate_fn is not support!')\n        self._table_configs.append(hash_table_config)\n\n  @property\n  def feature_slice_dims(self) -> Dict[str, List[int]]:\n    slice_dims: Dict[str, List[int]] = {}\n    for fsd in self._feature_slice_dims:\n      slice_dims[fsd.name] = list(fsd.dims)\n    return slice_dims\n\n  @feature_slice_dims.setter\n  def feature_slice_dims(self, slice_dims: Dict[str, List[int]]):\n    if slice_dims:\n      assert isinstance(slice_dims, dict)\n      self._feature_slice_dims.clear()\n      for name, dims in slice_dims.items():\n        fsd = FeatureSliceDim(name=name)\n        if dims:\n          fsd.dims.extend(dims)\n\n        self._feature_slice_dims.append(fsd)\n\n  @property\n  def feature_combiners(self):\n    fcombs = {}\n    for fcomb in self._feature_combiners:\n      if fcomb.combiner == Combiner.ReduceSum:\n        fcombs[fcomb.name] = ReduceSum()\n      elif fcomb.combiner == Combiner.ReduceMean:\n        fcombs[fcomb.name] = ReduceMean()\n      else:\n        fcombs[fcomb.name] = FirstN(seq_length=fcomb.max_seq_length)\n    return fcombs\n\n  @feature_combiners.setter\n  def feature_combiners(self, fcombs):\n    if fcombs:\n      assert isinstance(fcombs, dict)\n      self._feature_combiners.clear()\n      for name, fcomb in fcombs.items():\n        fc_proto = FeatureCombiner(name=name,\n                                   max_seq_length=fcomb.max_seq_length)\n        if isinstance(fcomb, ReduceSum):\n          fc_proto.combiner = Combiner.ReduceSum\n        elif isinstance(fcomb, ReduceMean):\n          fc_proto.combiner = Combiner.ReduceMean\n        else:\n          fc_proto.combiner = Combiner.FirstN\n\n        self._feature_combiners.append(fc_proto)\n\n  def get_slot_to_occurrence_threshold(self, mode: str = 'train'):\n    if mode == tf.estimator.ModeKeys.TRAIN:\n      model_dump = self.train\n    else:\n      model_dump = self.infer\n    slot_to_ot = {}\n    for feature in model_dump.features:\n      feature_name, slot = get_feature_name_and_slot(feature.feature_name)\n      if slot is not None:\n        slot_to_ot[slot] = feature.occurrence_threshold\n      else:\n        logging.warning(\n            \"feature[{}] slot is None. pls check feature_list.conf\".format(\n                feature_name))\n    return slot_to_ot\n\n  def get_slot_to_expire_time(self, mode: str = 'train'):\n    if mode == tf.estimator.ModeKeys.TRAIN:\n      model_dump = self.train\n    else:\n      model_dump = self.infer\n    slot_to_et = {}\n    for feature in model_dump.features:\n      feature_name, slot = get_feature_name_and_slot(feature.feature_name)\n      if slot is not None:\n        slot_to_et[slot] = feature.expire_time\n      else:\n        logging.warning(\n            \"feature[{}] slot is None. pls check feature_list.conf\".format(\n                feature_name))\n    return slot_to_et\n\n  @property\n  def has_collected(self) -> bool:\n    if self._table_configs and self._feature_slice_dims and self._feature_combiners:\n      return True\n    else:\n      assert self._table_configs is None or len(self._table_configs) == 0\n      assert self._feature_slice_dims is None or len(\n          self._feature_slice_dims) == 0\n      assert self._feature_combiners is None or len(\n          self._feature_combiners) == 0\n      return False\n\n\ndef parse_input_fn_result(result):\n  input_hooks = []\n  if isinstance(result, dataset_ops.DatasetV2):\n    iterator = dataset_ops.make_initializable_iterator(result)\n    input_hooks.append(util._DatasetInitializerHook(iterator))\n    result = iterator.get_next()\n  else:\n    initializer = tf.compat.v1.get_collection('mkiter')\n    if isinstance(initializer, (list, tuple)) and len(initializer) > 0:\n      initializer = initializer[0]\n    assert initializer is not None\n    input_hooks.append(DatasetInitHook(initializer))\n\n  DumpUtils().add_input_fn(result)\n  return util.parse_iterator_result(result) + (input_hooks,)\n\n\nutil.parse_input_fn_result = parse_input_fn_result\n"
  },
  {
    "path": "monolith/native_training/model_dump/graph_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os, re, time\nimport six, copy, time\nfrom absl import logging, flags\nfrom inspect import signature\nimport pickle\nfrom io import BytesIO\nfrom typing import Dict, Any, Optional, Union, List, Set\nimport tensorflow as tf\nfrom tensorflow.keras import layers\nfrom tensorflow.keras import models\nfrom google.protobuf import text_format\nfrom tensorflow.keras import initializers\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.ops.variables import Variable\n\nSaveSliceInfo = Variable.SaveSliceInfo\n\nfrom idl.matrix.proto.line_id_pb2 import LineId\nfrom idl.matrix.proto.example_pb2 import FeatureConfigs\nfrom tensorflow.python.ops.variables import PartitionedVariable\nfrom tensorflow.python.ops.gen_resource_variable_ops import read_variable_op\nfrom tensorflow.python.ops.ragged.row_partition import RowPartition\nfrom tensorflow.python.framework import ops\nfrom monolith.native_training.utils import add_to_collections, get_collection\n\nDRY_RUN = 'dry_run'\nFLAGS = flags.FLAGS\n\nclass DatasetInitHook(tf.compat.v1.train.SessionRunHook):\n\n  def __init__(self, initializer):\n    self._initializer = initializer\n\n  def after_create_session(self, session, coord):\n    del coord\n    session.run(self._initializer)\n\n\ndef _node_name(name):\n  if name.startswith(\"^\"):\n    return name[1:]\n  else:\n    return name.split(\":\")[0]\n\n\ndef _colocated_node_name(name):\n  \"\"\"Decodes colocated node name and returns it without loc:@ prepended.\"\"\"\n  colocated_node_decoded = name.decode(\"utf-8\")\n  if colocated_node_decoded.startswith(\"loc:@\"):\n    return colocated_node_decoded[5:]\n  return colocated_node_decoded\n\n\nclass EchoInitializer(tf.keras.initializers.Initializer):\n\n  def __init__(self, init_value):\n    self._init_value = init_value\n\n  def __call__(self, shape, dtype=None, **kwargs):\n    if isinstance(self._init_value, (list, tuple)):\n      assert len(self._init_value) == 1\n      init_value = self._init_value[0]\n    else:\n      init_value = self._init_value\n\n    if isinstance(init_value, tf.Tensor):\n      return init_value\n    else:\n      assert len(init_value.outputs) == 1\n      return init_value.outputs[0]\n\n\nclass VariableDef(object):\n\n  def __init__(self,\n               node: tf.compat.v1.NodeDef = None,\n               helper: 'GraphDefHelper' = None):\n    self.node = node\n    self._helper = helper\n    self._name_to_node = helper.name_to_node\n    self._read_ops: List[tf.compat.v1.NodeDef] = []\n\n    self._variable = None\n    self._initializer = None\n\n  @property\n  def dtype(self):\n    return tf.dtypes.DType(self.node.attr['dtype'].type)\n\n  @property\n  def shape(self):\n    return tuple(dim.size for dim in self.node.attr['shape'].shape.dim)\n\n  @property\n  def device(self):\n    return self.node.device\n\n  @property\n  def name(self):\n    return _node_name(self.node.name)\n\n  @property\n  def initializer(self):\n    if self._initializer is None:\n      assign = _node_name(f'{self.node.name}/Assign')\n      assert assign in self._name_to_node\n      assign_node = self._name_to_node[assign]\n      assert len(assign_node.input) == 2\n\n      initializer = None\n      vname = _node_name(self.node.name)\n      for name in assign_node.input:\n        if _node_name(name) != vname:\n          initializer = name\n          break\n      assert initializer is not None\n\n      sub_graph, _ = self._helper.sub_graph(dest_nodes=[initializer],\n                                            source_nodes=None,\n                                            with_library=False)\n      init_ops = tf.compat.v1.import_graph_def(sub_graph,\n                                               return_elements=[initializer],\n                                               name=\"\")\n      self._initializer = EchoInitializer(init_ops)\n\n    return self._initializer\n\n  @property\n  def variable(self):\n    if self._variable is None:\n      vs = tf.compat.v1.get_variable_scope()\n      partitioner = vs._partitioner\n      vs._partitioner = None\n      if self.device is not None and len(self.device) > 0:\n        with tf.compat.v1.device(self.device):\n          self._variable = tf.compat.v1.get_variable(\n              dtype=self.dtype,\n              shape=self.shape,\n              initializer=self.initializer,\n              name=self.node.name)\n      else:\n        self._variable = tf.compat.v1.get_variable(dtype=self.dtype,\n                                                   shape=self.shape,\n                                                   initializer=self.initializer,\n                                                   name=self.node.name)\n      vs._partitioner = partitioner\n      if isinstance(self._variable, PartitionedVariable):\n        self._variable = self._variable._variable_list[0]\n\n    return self._variable\n\n  def add_read(self, node: tf.compat.v1.NodeDef):\n    assert node.op == 'ReadVariableOp'\n    self._read_ops.append(node)\n\n  @property\n  def read_ops(self):\n    return self._read_ops\n\n\nclass PartitionVariableDef(object):\n  PVName = re.compile(r'^(.*)/part_\\d+$')\n\n  def __init__(self, base_name: str, helper: 'GraphDefHelper' = None):\n    self.base_name = base_name\n    self._helper = helper\n    self._name_to_node = helper.name_to_node\n    self._partitions: Dict[str, tf.compat.v1.NodeDef] = {}\n    self._read_ops: Dict[str, List[tf.compat.v1.NodeDef]] = {}\n\n    self._variable = None\n    self._initializer = None\n    self._partitioned_variable = None\n    self._save_slice_info = self._helper._save_slice_info\n\n  def add_partition(self, node: tf.compat.v1.NodeDef):\n    assert node.op == 'VarHandleOp'\n    self._partitions[_node_name(node.name)] = node\n\n  def add_read(self, node: tf.compat.v1.NodeDef):\n    assert node.op == 'ReadVariableOp'\n    name = _node_name(node.input[0])\n    if name in self._read_ops:\n      self._read_ops[name].append(node)\n    else:\n      self._read_ops[name] = [node]\n\n  @classmethod\n  def get_base_name(cls, node: tf.compat.v1.NodeDef) -> Optional[str]:\n    name, op = node.name, node.op\n    if op == \"VarHandleOp\":\n      matched = cls.PVName.match(name)\n      if matched:\n        return matched.group(1)\n    elif op == \"ReadVariableOp\":\n      inputs = [name for name in node.input if not name.startswith('^')]\n      assert len(inputs) == 1\n      matched = cls.PVName.match(inputs[0])\n      if matched:\n        return matched.group(1)\n    return None\n\n  @property\n  def dtype(self):\n    return [\n        tf.dtypes.DType(node.attr['dtype'].type)\n        for node in self._partitions.values()\n    ]\n\n  @property\n  def shape(self):\n    return [\n        tuple(dim.size\n              for dim in node.attr['shape'].shape.dim)\n        for node in self._partitions.values()\n    ]\n\n  @property\n  def device(self):\n    return [node.device for node in self._partitions.values()]\n\n  @property\n  def initializer(self):\n    if self._initializer is None:\n      if len(self._partitions) > 1:\n        dest_nodes = [\n            _node_name(f'{pname}/PartitionedInitializer/Slice')\n            for pname in self._partitions\n        ]\n      else:\n        node_name = _node_name(f'{next(iter(self._partitions))}')\n        slice_node_name = f'{node_name}/PartitionedInitializer/Slice'\n        if slice_node_name in self._name_to_node:\n          dest_nodes = [slice_node_name]\n        else:\n          assign_node_name = f'{node_name}/Assign'\n          assert assign_node_name in self._name_to_node\n          assign_node = self._name_to_node[assign_node_name]\n          assert len(assign_node.input) == 2\n\n          initializer = None\n          for name in assign_node.input:\n            if _node_name(name) != node_name:\n              initializer = name\n              break\n          assert initializer is not None\n          dest_nodes = [initializer]\n\n      sub_graph, _ = self._helper.sub_graph(dest_nodes=dest_nodes,\n                                            source_nodes=None,\n                                            with_library=False)\n      init_ops = tf.compat.v1.import_graph_def(sub_graph,\n                                               return_elements=dest_nodes,\n                                               name=\"\")\n      self._initializer = [EchoInitializer(init_op) for init_op in init_ops]\n    return self._initializer\n\n  @property\n  def variable(self):\n    if self._variable is None:\n      self._variable = {}\n      dtypes, shapes, inits = self.dtype, self.shape, self.initializer\n      group_device = None\n      vs = tf.compat.v1.get_variable_scope()\n      partitioner = vs._partitioner\n      vs._partitioner = None\n      for i, (name, device) in enumerate(zip(self._partitions, self.device)):\n        group_device = device if i == 0 else group_device\n        if group_device is not None and len(group_device) > 0:\n          logging.warning(\"variable[{}] use group_device[{}]\".format(name, group_device))\n          with tf.compat.v1.device(None):\n            with tf.compat.v1.device(group_device):\n              variable = tf.compat.v1.get_variable(dtype=dtypes[i],\n                                                   shape=shapes[i],\n                                                   initializer=inits[i],\n                                                   name=name)\n              if isinstance(variable, PartitionedVariable):\n                variable = variable._variable_list[0]\n              else:\n                save_slice_info = self._save_slice_info.get(name)\n                variable._set_save_slice_info(save_slice_info)\n        else:\n          variable = tf.compat.v1.get_variable(dtype=dtypes[i],\n                                               shape=shapes[i],\n                                               initializer=inits[i],\n                                               name=name)\n          if isinstance(variable, PartitionedVariable):\n            variable = variable._variable_list[0]\n          else:\n            save_slice_info = self._save_slice_info.get(name)\n            variable._set_save_slice_info(save_slice_info)\n\n        self._variable[name] = variable\n      vs._partitioner = partitioner\n\n      # make PartitionedVariable, to check save_slice_info\n      if len(self._variable) > 1:\n        names = sorted(self._variable,\n                       key=lambda x: int(_node_name(x).rsplit('_')[-1]))\n        name = names[0].rsplit('/', maxsplit=1)[0]\n        partitions = [\n            len(shapes) if i == 0 else 1 for i, d in enumerate(shapes[0])\n        ]\n        first_dim = sum(s[0] for s in shapes)\n        if len(shapes[0]) > 1:\n          shape = [first_dim] + list(shapes[0][1:])\n        else:\n          shape = [first_dim]\n        self._partitioned_variable = PartitionedVariable(\n            name=name,\n            shape=shape,\n            dtype=dtypes[0],\n            variable_list=[self._variable[name] for name in names],\n            partitions=partitions)\n\n    return self._variable\n\n  def read_ops(self, pname: str) -> List[tf.compat.v1.NodeDef]:\n    return self._read_ops[pname]\n\n\nclass GraphDefHelper(object):\n\n  def __init__(self, graph_def: tf.compat.v1.GraphDef,\n               save_slice_info: Dict[str, SaveSliceInfo]):\n    if not isinstance(graph_def, tf.compat.v1.GraphDef):\n      raise TypeError(\"graph_def must be a graph_pb2.GraphDef proto.\")\n\n    self.graph_def = graph_def\n    self.name_to_vardef: Dict[str, Union[VariableDef,\n                                         PartitionVariableDef]] = {}\n    self.name_to_input_name: Dict[str,\n                                  Set[str]] = {}  # Keyed by the dest node name.\n    self.name_to_node: Dict[str, tf.compat.v1.NodeDef] = {}\n    self.seq_num_to_node: Dict[int, tf.compat.v1.NodeDef] = {}\n    self.name_to_seq_num: Dict[str, int] = {}\n    self._save_slice_info = save_slice_info\n    self._file_name = None\n\n    seq = 0\n    for node in graph_def.node:\n      node.device = b''\n      name = _node_name(node.name)\n      self.name_to_input_name[name] = set(_node_name(x) for x in node.input)\n      if \"_class\" in node.attr:\n        for colocated_node_name in node.attr[\"_class\"].list.s:\n          self.name_to_input_name[name].add(\n              _colocated_node_name(colocated_node_name))\n        del node.attr[\"_class\"]\n      self.name_to_node[name] = node\n      self.name_to_seq_num[name] = seq\n      self.seq_num_to_node[seq] = node\n      seq += 1\n\n      if node.name == \"PBDataset/file_name\" and node.op == \"Const\":\n        self._file_name = node\n\n      stop_names = {'global_step', 'WorkerCkptMetaInfo'}\n      if node.op == \"VarHandleOp\" and name not in stop_names:\n        base_name = PartitionVariableDef.get_base_name(node)\n        if base_name is not None:\n          if base_name in self.name_to_vardef:\n            self.name_to_vardef[base_name].add_partition(node)\n          else:\n            pvd = PartitionVariableDef(base_name, self)\n            pvd.add_partition(node)\n            self.name_to_vardef[base_name] = pvd\n        else:\n          if name in self.name_to_vardef:\n            if self.name_to_vardef[name].node is None:\n              self.name_to_vardef[name].node = node\n            else:\n              logging.info(\"maybe error, because node is not None\")\n          else:\n            self.name_to_vardef[name] = VariableDef(node, self)\n\n      if node.op == \"ReadVariableOp\":\n        inputs = [name for name in node.input if not name.startswith('^')]\n        assert len(inputs) == 1\n        vname = inputs[0]\n        if vname in stop_names:\n          continue\n\n        base_name = PartitionVariableDef.get_base_name(node)\n        if base_name is not None:\n          if base_name in self.name_to_vardef:\n            self.name_to_vardef[base_name].add_read(node)\n          else:\n            pvd = PartitionVariableDef(base_name, self)\n            pvd.add_read(node)\n            self.name_to_vardef[base_name] = pvd\n        else:\n          base_name = _node_name(node.input[0])\n          if base_name in self.name_to_vardef:\n            self.name_to_vardef[base_name].add_read(node)\n          else:\n            dummy = VariableDef(None, self)\n            dummy.add_read(node)\n            self.name_to_vardef[base_name] = dummy\n\n  @property\n  def library(self):\n    return self.graph_def.library\n\n  @property\n  def versions(self):\n    return self.graph_def.versions\n\n  @classmethod\n  def _check_invalidate_node(clz, graph_def: tf.compat.v1.GraphDef,\n                             input_map: Dict[str, tf.Tensor]):\n    if input_map is None or len(input_map) == 0:\n      return\n\n    exists = set()\n    for node in graph_def.node:\n      for ts_name in node.input:\n        if ts_name.startswith('^'):\n          ts_name = ts_name[1:]\n\n        exists.add(ts_name)\n        if \":\" not in ts_name:\n          exists.add(f'{ts_name}:0')\n\n    invalidate = set(input_map.keys()) - exists\n    for name in invalidate:\n      del input_map[name]\n      logging.warning(f\"{name} is not used in model\")\n\n  def _create_variables(self, variables: Set[str]) -> Dict[str, tf.Tensor]:\n    vread_map = {}\n    graph = tf.compat.v1.get_default_graph()\n    for vardef in self.name_to_vardef.values():\n      if isinstance(vardef, PartitionVariableDef):\n        # remove variable that outside the graph\n        if len(set(vardef._partitions.keys()) - variables) != 0:\n          continue\n        part_var = vardef.variable\n        for pname, v in part_var.items():\n          # v._handle -> Tensor(\"dense/kernel/part_0:0\", shape=(), dtype=resource)\n          # v.value() -> Tensor(\"ReadVariableOp:0\", shape=(48, 512), dtype=float32)\n          # v.read_value() -> Tensor(\"Identity:0\", shape=(48, 512), dtype=float32)\n          # graph.get_tensor_by_name(f'{pname}:0') -> Tensor(\"dense/kernel/part_0:0\",\n          #                                                  shape=(), dtype=resource)\n          for reader in vardef.read_ops(pname):\n            if reader.name == f'{reader.input[0]}/Read/ReadVariableOp':\n              continue\n            vread_map[reader.name] = read_variable_op(resource=v._handle,\n                                                      dtype=v.dtype,\n                                                      name=_node_name(\n                                                          reader.name))\n      else:\n        # remove variable that outside the graph\n        if vardef.name not in variables:\n          continue\n\n        v = vardef.variable\n        for reader in vardef.read_ops:\n          if reader.name == f'{reader.input[0]}/Read/ReadVariableOp':\n            continue\n          vread_map[reader.name] = read_variable_op(resource=v._handle,\n                                                    dtype=v.dtype,\n                                                    name=_node_name(\n                                                        reader.name))\n    return vread_map\n\n  def sub_graph(self,\n                dest_nodes: List[str],\n                source_nodes: Optional[List[str]] = None,\n                with_library: bool = True):\n    if isinstance(dest_nodes, six.string_types):\n      raise TypeError(\"dest_nodes must be a list.\")\n\n    if source_nodes is not None:\n      source_nodes = list(set([_node_name(sn) for sn in source_nodes]))\n      for sn in source_nodes:\n        assert sn in self.name_to_node, f\"{sn} is not in graph\"\n\n    dest_nodes = list(set([_node_name(dn) for dn in dest_nodes]))\n    for dn in dest_nodes:\n      assert dn in self.name_to_node, f\"{dn} is not in graph\"\n\n    # Breadth first search to find all the nodes that we should keep.\n    nodes_to_keep = set()\n    stop_nodes = set() if source_nodes is None else set(source_nodes)\n    next_to_visit = dest_nodes[:]\n    while next_to_visit:\n      node = next_to_visit[0]\n      del next_to_visit[0]\n      if node in nodes_to_keep or node in stop_nodes:\n        # Already visited/stop this node.\n        continue\n      nodes_to_keep.add(node)\n      if node in self.name_to_input_name:\n        next_to_visit += list(self.name_to_input_name[node])\n    nodes_to_keep_list = sorted(list(nodes_to_keep),\n                                key=lambda name: self.name_to_seq_num[name])\n\n    # Now construct the output GraphDef\n    sub_gd = tf.compat.v1.GraphDef()\n    variables = set()\n    for n in nodes_to_keep_list:\n      node = self.name_to_node[n]\n      if node.op not in {\"VarHandleOp\", \"ReadVariableOp\"}:\n        sub_gd.node.extend([copy.deepcopy(node)])\n      elif node.op == \"VarHandleOp\":\n        variables.add(n)\n      elif node.op == \"ReadVariableOp\":\n        name = f'{node.input[0]}/Read/ReadVariableOp'\n        if node.name != name:\n          sub_gd.node.extend([copy.deepcopy(node)])\n\n    if with_library:\n      func_names = set()\n      for node in sub_gd.node:\n        for key, value in node.attr.items():\n          if value.func is not None:\n            name = value.func.name\n            if name:\n              func_names.add(name)\n      for func in self.graph_def.library.function:\n        if \"Dataset\" in func.signature.name or func.signature.name in func_names:\n          ofunc = sub_gd.library.function.add()\n          ofunc.CopyFrom(func)\n          for node_def in ofunc.node_def:\n            node_def.device = b''\n    # out.versions.CopyFrom(self.graph_def.versions)\n\n    return sub_gd, variables\n\n  def import_input_fn(self, input_conf, file_name: str):\n    graph = tf.compat.v1.get_default_graph()\n    dry_run: bool = hasattr(graph, DRY_RUN)\n\n    dest_nodes = []\n    for feat_name, ts_repr in input_conf.output_features.items():\n      ts_dict = eval(ts_repr)\n      dest_nodes.append(ts_dict['name'])\n    if input_conf.label is not None and len(input_conf.label) > 0:\n      dest_nodes.append(input_conf.label)\n    if not dry_run:\n      if \"IteratorToStringHandle\" in self.name_to_node:\n        dest_nodes.append(\"IteratorToStringHandle\")\n      if \"MakeIterator\" in self.name_to_node:\n        dest_nodes.append(\"MakeIterator\")\n    del self._file_name.attr['value'].tensor.string_val[:]\n    file_name_bytes = bytes(file_name, encoding='utf-8')\n    self._file_name.attr['value'].tensor.string_val.append(file_name_bytes)\n    sub_graph, _ = self.sub_graph(dest_nodes=dest_nodes,\n                                  source_nodes=None,\n                                  with_library=True)\n    data_type = getattr(FLAGS, 'data_type')\n    logging.info(f\"[INFO] using the data type {data_type}\")\n    if data_type:\n      # replace pb dataset input pb type\n      for node in sub_graph.node:\n        if node.name == \"PBDataset/input_pb_type\":\n          val_attr = node.attr['value'] # ValAttr\n          val_ts = val_attr.WhichOneof('value') # tensor\n          if val_ts == 'tensor':\n            target_type = data_type.lower().encode()\n            val_attr.tensor.string_val[0] = target_type\n          logging.info(f\"[INFO] using input_pb_type {val_attr.tensor.string_val[0]}\")\n          \n          logging.info(f\"[INFO] the pbdataset/input {node}\")\n\n    return_elements = tf.import_graph_def(sub_graph,\n                                          input_map=None,\n                                          return_elements=dest_nodes,\n                                          name=\"\")\n    if not dry_run:\n      if \"IteratorToStringHandle\" in self.name_to_node:\n        idx = dest_nodes.index(\"IteratorToStringHandle\")\n        tf.compat.v1.add_to_collection(\"iterators\", return_elements[idx])\n      if \"MakeIterator\" in self.name_to_node:\n        idx = dest_nodes.index(\"MakeIterator\")\n        tf.compat.v1.add_to_collection(\"mkiter\", return_elements[idx])\n\n    result = {}\n    for i, (feat_name,\n            ts_repr) in enumerate(input_conf.output_features.items()):\n      ts_dict = eval(ts_repr)\n      if ts_dict['is_ragged']:\n        row_splits, values = return_elements[i].outputs\n        assert ts_dict['name'] == values.name.split(':')[0]\n        row_partition = RowPartition.from_row_splits(row_splits=tf.reshape(\n            row_splits, shape=(-1,)),\n                                                     validate=False,\n                                                     preferred_dtype=None)\n        result[feat_name] = tf.RaggedTensor(tf.reshape(values, shape=(-1,)),\n                                            row_partition,\n                                            internal=True)\n      else:\n        assert ts_dict['name'] == return_elements[i].name\n        result[feat_name] = return_elements[i]\n\n    if input_conf.label is not None and len(input_conf.label) > 0:\n      idx = dest_nodes.index(input_conf.label)\n      result['label'] = return_elements[idx]\n    return result\n\n  def import_model_fn(self, input_map: Dict[str, tf.Tensor], proto_model):\n    source_nodes = list(input_map.keys()) if input_map else None\n\n    model_fn = proto_model.model_fn\n    outputs = list(model_fn.predict)\n    if len(proto_model.extra_output) > 0:\n      for extra_output in proto_model.extra_output:\n        for ts_name in extra_output.fetch_dict.values():\n          node_name = _node_name(ts_name)\n          full_name = ts_name if ':' in ts_name else f'{node_name}:0'\n          if node_name not in outputs and full_name not in outputs:\n            outputs.append(ts_name)\n    if model_fn.loss is not None and len(model_fn.loss) > 0:\n      outputs.append(model_fn.loss)\n    if model_fn.label is not None and len(model_fn.label) > 0:\n      outputs.extend([l for l in model_fn.label if l])\n    for extra_loss in model_fn.extra_losses:\n      if extra_loss not in outputs:\n        outputs.append(extra_loss)\n    signature_input_names = []\n    if len(proto_model.signature) > 0:\n      for signature in proto_model.signature:\n        for ts_name in signature.inputs.values():\n          if ts_name not in signature_input_names:\n            signature_input_names.append(ts_name)\n        for ts_name in signature.outputs.values():\n          node_name = _node_name(ts_name)\n          full_name = ts_name if ':' in ts_name else f'{node_name}:0'\n          if node_name not in outputs and full_name not in outputs:\n            outputs.append(ts_name)\n\n    if len(model_fn.summary) > 0:\n      logging.info(\"load user summaries {}\".format(model_fn.summary))\n      outputs.extend(list(model_fn.summary))\n      summaries = model_fn.summary\n\n    sub_graph, variables = self.sub_graph(dest_nodes=outputs,\n                                          source_nodes=source_nodes,\n                                          with_library=True)\n    self._check_invalidate_node(sub_graph, input_map)\n    vread_map = self._create_variables(variables)\n    if input_map is not None and len(input_map) > 0:\n      vread_map.update(input_map)\n\n    # check input_map for import_graph_def\n    nodes = {_node_name(node.name) for node in sub_graph.node}\n    graph = tf.compat.v1.get_default_graph()\n    if vread_map:\n      ts_names = set()\n      for node in sub_graph.node:\n        for ip_ts_name in node.input:\n          if _node_name(ip_ts_name) not in nodes:\n            ts_names.add(ip_ts_name)\n      vread_map = {\n          key if key in ts_names else _node_name(key): value\n          for key, value in vread_map.items()\n          if key in ts_names or _node_name(key) in ts_names\n      }\n      unknown_input = ts_names - set(vread_map)\n      if unknown_input:\n        logging.info(f\"Debug. unknown_input {unknown_input}\")\n        for ts_name in unknown_input:\n          vread_map[ts_name] = graph.get_tensor_by_name(\n              ts_name if ':' in ts_name else f'{ts_name}:0')\n    else:\n      vread_map = None\n\n    # in case some output tensor not include in graph\n    direct_out, real_out = {}, []\n    for op_ts_name in outputs:\n      if _node_name(op_ts_name) not in nodes:\n        try:\n          direct_out[op_ts_name] = graph.get_tensor_by_name(\n              op_ts_name if ':' in op_ts_name else f'{op_ts_name}:0')\n        except:\n          logging.warning(f'Cannot find {op_ts_name} in both graph and inputs')\n          direct_out[op_ts_name] = None\n      else:\n        real_out.append(op_ts_name)\n    real_result = tf.import_graph_def(sub_graph,\n                                      input_map=vread_map,\n                                      return_elements=real_out,\n                                      name=\"\")\n\n    # collection sparse_feature\n    for node in tf.compat.v1.get_default_graph().as_graph_def().node:\n      if node.op.startswith(\"ShardingSparseFids\"):\n        feature_cfgs = FeatureConfigs()\n        feature_cfgs.ParseFromString(node.attr[\"feature_cfgs\"].s)\n        sparse_features = get_collection(\"sparse_features\")\n        if sparse_features is None:\n          sparse_features = []\n        else:\n          sparse_features = sparse_features[-1]\n        for feat_name, _ in feature_cfgs.feature_configs.items():\n          sparse_features.append(feat_name)\n        sparse_features = list(set(sparse_features))\n        add_to_collections('sparse_features', sparse_features)\n\n    real_result = {name: value for name, value in zip(real_out, real_result)}\n    result = [real_result.get(name, direct_out.get(name)) for name in outputs]\n    if len(model_fn.summary) > 0:\n      for summary in summaries:\n        for sum_ts in real_result.get(summary).outputs:\n          # logging.info(\"[INFO] add summary {} to collection\".format(sum_ts))\n          ops.add_to_collection(ops.GraphKeys.SUMMARIES, sum_ts)\n\n    # check sig_input in graph\n    for op_ts_name in signature_input_names:\n      graph.get_tensor_by_name(op_ts_name if ':' in\n                               op_ts_name else f'{op_ts_name}:0')\n\n    if model_fn.label is not None and len(model_fn.label) > 0:\n      labels = [None] * len(model_fn.label)\n      for i, label in enumerate(model_fn.label):\n        if label:\n          idx = outputs.index(label)\n          labels[i] = result[idx]\n      label = labels if len(labels) > 1 else labels[0]\n    else:\n      label = None\n\n    if model_fn.loss is not None and len(model_fn.loss) > 0:\n      idx = outputs.index(model_fn.loss)\n      loss = result[idx]\n    else:\n      loss = None\n\n    predict = result[:len(list(model_fn.predict))]\n    if len(predict) == 1:\n      predict = predict[0]\n\n    if model_fn.head_name is not None and len(model_fn.head_name) > 0:\n      if len(model_fn.head_name) == 1:\n        head_name = model_fn.head_name[0]\n      else:\n        head_name = list(model_fn.head_name)\n    else:\n      head_name = None\n\n    if model_fn.classification is not None:\n      logging.info(\"load is_classificaiton {}\".format(model_fn.classification))\n      if len(model_fn.classification) == 1:\n        is_classification = model_fn.classification[0]\n      else:\n        is_classification = list(model_fn.classification)\n\n    extra_output_dict = {}\n    if len(proto_model.extra_output) > 0:\n      for extra_output in proto_model.extra_output:\n        real_extra = {}\n        for key, ts_name in extra_output.fetch_dict.items():\n          idx = outputs.index(ts_name)\n          real_extra[key] = result[idx]\n\n        if len(extra_output.fetch_dict) == 1 and key == result[idx].name:\n          extra_output_dict[extra_output.signature_name] = next(\n              iter(real_extra.values()))\n        else:\n          extra_output_dict[extra_output.signature_name] = real_extra\n\n    return label, loss, predict, head_name, extra_output_dict, is_classification\n\n  def import_receiver_fn(self, receiver_conf):\n    dest_nodes, sparse_features, dense_features, extra_features = [], [], [], []\n    dense_feature_shapes, dense_feature_types, extra_feature_shapes = [], [], []\n    parser_type = receiver_conf.parser_type\n    for feat_name, ts_repr in receiver_conf.features.items():\n      ts_dict = eval(ts_repr)\n      if ts_dict['is_ragged']:\n        dest_nodes.append(ts_dict['values'])\n        dest_nodes.append(ts_dict['row_splits'])\n        sparse_features.append(feat_name)\n      else:\n        dest_nodes.append(ts_dict['name'])\n        if hasattr(LineId, feat_name):\n          extra_features.append(feat_name)\n          extra_feature_shapes.append(ts_dict['last_dim'])\n        else:\n          dense_features.append(feat_name)\n          dense_feature_types.append(ts_dict['dtype'])\n          dense_feature_shapes.append(ts_dict['last_dim'])\n    add_to_collections('sparse_features', sparse_features)\n    add_to_collections('dense_features', dense_features)\n    add_to_collections('dense_feature_shapes', dense_feature_shapes)\n    add_to_collections('dense_feature_types', dense_feature_types)\n    add_to_collections('extra_features', extra_features)\n    add_to_collections('extra_feature_shapes', extra_feature_shapes)\n    add_to_collections('variant_type', parser_type)\n\n    num_feature_tensors = len(dest_nodes)\n    for name, ph_name in receiver_conf.receiver_name.items():\n      dest_nodes.append(ph_name)\n\n    sub_graph, _ = self.sub_graph(dest_nodes=dest_nodes,\n                                  source_nodes=None,\n                                  with_library=True)\n    return_elements = tf.import_graph_def(sub_graph,\n                                          input_map=None,\n                                          return_elements=dest_nodes,\n                                          name=\"\")\n\n    idx, features = 0, {}\n    for feat_name, ts_repr in receiver_conf.features.items():\n      ts_dict = eval(ts_repr)\n      if ts_dict['is_ragged']:\n        values, row_splits = return_elements[idx], return_elements[idx + 1]\n        features[feat_name] = tf.RaggedTensor.from_row_splits(values,\n                                                              row_splits,\n                                                              validate=False)\n        idx += 2\n      else:\n        features[feat_name] = return_elements[idx]\n        idx += 1\n\n    receiver_tensors = {}\n    for name in receiver_conf.receiver_name:\n      receiver_tensors[name] = return_elements[idx]\n      idx += 1\n\n    return features, receiver_tensors\n\n  @classmethod\n  def get_optimizer(cls, proto_model):\n    ser_opt = proto_model.optimizer\n    if ser_opt is not None and len(ser_opt) > 0:\n      f = BytesIO(ser_opt)\n      return pickle.load(f)\n    else:\n      return None\n"
  },
  {
    "path": "monolith/native_training/model_dump/graph_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom absl import flags\nfrom monolith.native_training.data.datasets import PBDataset\nfrom monolith.native_training.model_dump.graph_utils import GraphDefHelper\nfrom monolith.native_training.model_export.export_context import get_current_export_ctx\nfrom monolith.native_training.model_dump.dump_utils import DumpUtils\nfrom monolith.native_training.runner_utils import RunnerConfig\n\nfile_name = \"monolith/native_training/data/training_instance/examplebatch.data\"\nFLAGS = flags.FLAGS\n\nclass GraphUtilsTest(tf.test.TestCase):\n\n  @classmethod\n  def setUpClass(cls):\n    FLAGS.data_type = 'examplebatch'\n    cls.dump_utils = DumpUtils()\n    cls.dump_utils.load(\n        \"monolith/native_training/model_dump/test_data/model_dump\")\n\n  def test_load_input_fn(self):\n    proto_model = self.dump_utils.get_proto_model(mode='train')\n    graph_helper = self.dump_utils.get_graph_helper(mode='train')\n    result = graph_helper.import_input_fn(input_conf=proto_model.input_fn,\n                                          file_name=file_name)\n\n    for fname, ts_repr in proto_model.input_fn.output_features.items():\n      ts_dict = eval(ts_repr)\n      if ts_dict['is_ragged']:\n        self.assertTrue(isinstance(result[fname], tf.RaggedTensor))\n      else:\n        self.assertTrue(isinstance(result[fname], tf.Tensor))\n\n  def test_load_receiver(self):\n    proto_model = self.dump_utils.get_proto_model(mode='infer')\n    graph_helper = self.dump_utils.get_graph_helper(mode='infer')\n    features, receiver_tensors = graph_helper.import_receiver_fn(\n        receiver_conf=proto_model.serving_input_receiver_fn)\n\n    for fname, ts_repr in proto_model.serving_input_receiver_fn.features.items(\n    ):\n      ts_dict = eval(ts_repr)\n      if ts_dict['is_ragged']:\n        self.assertTrue(isinstance(features[fname], tf.RaggedTensor))\n      else:\n        self.assertTrue(isinstance(features[fname], tf.Tensor))\n\n    self.assertTrue(len(receiver_tensors) == 1)\n\n  def test_load_mode(self):\n    mode = tf.estimator.ModeKeys.TRAIN\n    proto_model = self.dump_utils.get_proto_model(mode=mode)\n    graph_helper = self.dump_utils.get_graph_helper(mode=mode)\n    self.assertTrue(isinstance(graph_helper, GraphDefHelper))\n\n    graph = tf.compat.v1.get_default_graph()\n    graph.dry_run = True\n    proto_model = self.dump_utils.get_proto_model(mode=mode)\n    graph_helper = self.dump_utils.get_graph_helper(mode=mode)\n    self.assertTrue(isinstance(graph_helper, GraphDefHelper))\n\n    mode = tf.estimator.ModeKeys.PREDICT\n    proto_model = self.dump_utils.get_proto_model(mode=mode)\n    graph_helper = self.dump_utils.get_graph_helper(mode=mode)\n    self.assertTrue(isinstance(graph_helper, GraphDefHelper))\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.INFO)\n\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/model_dump/monolith_model.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n// All fields here should be hidden from the public version.\n\nsyntax = \"proto3\";\npackage monolith.native_training.model_dump;\noption cc_enable_arenas = true;\n\nimport public \"monolith/native_training/runtime/hash_table/compressor/float_compressor.proto\";\nimport public \"monolith/native_training/runtime/hash_table/initializer/initializer_config.proto\";\nimport public \"monolith/native_training/runtime/hash_table/optimizer/optimizer.proto\";\n\nmessage FeatureColumn {\n}\n\nmessage LookupEmbeddingSlice {\n}\n\nmessage InputFn {\n}\n\nmessage ModelFn {\n}\n\nmessage ServingInputReceiverFn {\n}\n\nmessage ExtraOutput {\n}\n\nmessage Signature {\n}\n\nmessage ProtoModel {\n}\n\nmessage HashTableConfig {\n}\n\nmessage FeatureSliceDim {\n}\n\nenum Combiner {\n  // Enum must have at least 1 value.\n  ReduceSum = 0;\n}\n\nmessage FeatureCombiner {\n}\n\nmessage ModelDump {\n}\n"
  },
  {
    "path": "monolith/native_training/model_export/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nproto_library(\n    name = \"export_proto\",\n    srcs = [\"export.proto\"],\n)\n\npy_proto_library(\n    name = \"export_py_proto\",\n    srcs = [\"export.proto\"],\n)\n\npy_library(\n    name = \"export_context\",\n    srcs = [\"export_context.py\"],\n    deps = [\n        \"//monolith:utils\",\n        \"//monolith/native_training:monolith_export\",\n        \"//monolith/native_training:utils\",\n    ],\n)\n\npy_library(\n    name = \"saved_model_exporters\",\n    srcs = [\"saved_model_exporters.py\"],\n    deps = [\n        \":data_gen_utils\",\n        \":export_context\",\n        \"//monolith/native_training:device_utils\",\n        \"//monolith/native_training:hash_table_ops\",\n        \"//monolith/native_training:monolith_export\",\n        \"//monolith/native_training:multi_hash_table_ops\",\n        \"//monolith/native_training:multi_type_hash_table\",\n        \"//monolith/native_training/model_dump:dump_utils\",\n    ],\n)\n\npy_test(\n    name = \"saved_model_exporters_test\",\n    srcs = [\"saved_model_exporters_test.py\"],\n    deps = [\n        \":saved_model_exporters\",\n        \"//monolith/native_training:test_utils\",\n    ],\n)\n\npy_library(\n    name = \"export_state_utils\",\n    srcs = [\"export_state_utils.py\"],\n    deps = [\n        \":export_py_proto\",\n    ],\n)\n\npy_test(\n    name = \"export_state_utils_test\",\n    srcs = [\"export_state_utils_test.py\"],\n    deps = [\n        \":export_state_utils\",\n    ],\n)\n\npy_library(\n    name = \"export_hooks\",\n    srcs = [\"export_hooks.py\"],\n    deps = [\n        \":export_py_proto\",\n        \":export_state_utils\",\n        \":saved_model_exporters\",\n        \"//monolith/native_training:save_utils\",\n    ],\n)\n\npy_test(\n    name = \"export_hooks_test\",\n    srcs = [\"export_hooks_test.py\"],\n    deps = [\n        \":export_hooks\",\n        \":saved_model_exporters\",\n        \"//monolith/native_training:save_utils\",\n    ],\n)\n\npy_binary(\n    name = \"saved_model_visulizer\",\n    srcs = [\"saved_model_visulizer.py\"],\n    deps = [\n        \"//monolith/native_training:distribution_ops\",\n        \"//monolith/native_training:hash_table_ops\",\n    ],\n)\n\npy_binary(\n    name = \"warmup_data_gen\",\n    srcs = [\"warmup_data_gen.py\"],\n    deps = [\n        \":data_gen_utils\",\n        \"//monolith/native_training:cpu_training\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:predict_proto_py_pb2\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_log_proto_py_pb2\",\n    ],\n)\n\npy_binary(\n    name = \"warmup_data_decoder\",\n    srcs = [\"warmup_data_decoder.py\"],\n    deps = [\n        \":data_gen_utils\",\n        \"//monolith/native_training:cpu_training\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:predict_proto_py_pb2\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_log_proto_py_pb2\",\n    ],\n)\n\npy_binary(\n    name = \"warmup_example_batch\",\n    srcs = [\"warmup_example_batch.py\"],\n    deps = [\n        \":data_gen_utils\",\n        \"//monolith/native_training:cpu_training\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:predict_proto_py_pb2\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_log_proto_py_pb2\",\n    ],\n)\n\npy_binary(\n    name = \"demo_export\",\n    srcs = [\"demo_export.py\"],\n    deps = [\n        \":saved_model_exporters\",\n        \"//monolith/native_training:cpu_training\",\n        \"//monolith/native_training:model\",\n        \"//monolith/native_training/data/training_instance:parse_instance_ops_py\",\n    ],\n)\n\npy_test(\n    name = \"demo_export_test\",\n    srcs = [\"demo_export_test.py\"],\n    deps = [\n        \":demo_export\",\n    ],\n)\n\npy_binary(\n    name = \"demo_predictor\",\n    srcs = [\"demo_predictor.py\"],\n    deps = [\n        \"//idl:proto_parser_py_proto\",\n        \"//monolith/native_training:distribution_ops\",\n        \"//monolith/native_training:hash_filter_ops\",\n        \"//monolith/native_training:hash_table_ops\",\n        \"//monolith/native_training:logging_ops\",\n        \"//monolith/native_training:model\",\n        \"//monolith/native_training/data/training_instance:parse_instance_ops_py\",\n    ],\n)\n\npy_binary(\n    name = \"demo_predictor_client\",\n    srcs = [\"demo_predictor_client.py\"],\n    deps = [\n        \":demo_predictor\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:predict_proto_py_pb2\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_service_proto_py_pb2\",\n    ],\n)\n\npy_library(\n    name = \"model_export\",\n    srcs = [\"__init__.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":export_context\",\n        \":saved_model_exporters\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_library(\n    name = \"data_gen_utils\",\n    srcs = [\"data_gen_utils.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":export_context\",\n        \"//idl:example_py_proto\",\n        \"//idl:line_id_py_proto\",\n        \"//monolith/native_training:env_utils\",\n        \"//monolith/native_training:utils\",\n        \"//monolith/native_training/data:feature_list\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:predict_proto_py_pb2\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_log_proto_py_pb2\",\n    ],\n)\n\npy_test(\n    name = \"data_gen_utils_test\",\n    srcs = [\"data_gen_utils_test.py\"],\n    data = [\"//monolith/native_training/data/test_data:test_feature_lists\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":data_gen_utils\",\n        \"//monolith/native_training/data:datasets_py\",\n        \"//monolith/native_training/data:feature_utils_py\",\n        \"//monolith/native_training/data:parsers_py\",\n    ],\n)\n\npy_library(\n    name = \"export_utils\",\n    srcs = [\"export_utils.py\"],\n    deps = [\n        \":export_context\",\n        \"//monolith/native_training:distributed_serving_ops\",\n        \"//monolith/native_training:nested_tensors\",\n    ],\n)\n\npy_test(\n    name = \"export_utils_test\",\n    srcs = [\"export_utils_test.py\"],\n    deps = [\n        \":export_utils\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/model_export/__init__.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport sys as _sys\nimport monolith.native_training.model_export.export_context as export_context\nimport monolith.native_training.model_export.saved_model_exporters as saved_model_exporters\n\n_sys.modules['monolith.model_export.export_context'] = export_context\n_sys.modules[\n    'monolith.model_export.saved_model_exporters'] = saved_model_exporters\ndel _sys\n"
  },
  {
    "path": "monolith/native_training/model_export/data_gen_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport sys\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nfrom random import randint, uniform, choice\nfrom copy import deepcopy\nimport numpy as np\nfrom struct import pack, unpack\nfrom functools import singledispatch\nfrom typing import List, Iterable, Tuple, Dict, Any, get_type_hints\nfrom datetime import datetime\nfrom dataclasses import dataclass\nimport tensorflow as tf\n\nfrom tensorflow_serving.apis import predict_pb2\nfrom tensorflow_serving.apis import prediction_log_pb2\nfrom tensorflow.python.saved_model.signature_constants import DEFAULT_SERVING_SIGNATURE_DEF_KEY\nfrom tensorflow.python.saved_model.model_utils.export_output import PredictOutput\nfrom monolith.native_training import env_utils\nfrom monolith.native_training.data.feature_list import Feature, FeatureList, get_feature_name_and_slot\nfrom idl.matrix.proto.example_pb2 import Example, ExampleBatch\nfrom idl.matrix.proto.example_pb2 import Feature as EFeature\nfrom idl.matrix.proto.line_id_pb2 import LineId\nfrom idl.matrix.proto.proto_parser_pb2 import Instance\nfrom idl.matrix.proto.feature_pb2 import Feature as IFeature\nfrom google.protobuf.descriptor import FieldDescriptor\nfrom monolith.native_training.utils import get_collection\nfrom monolith.native_training.model_export import export_context\n\nMASK_V1 = (1 << 54) - 1\nMAX_SLOT_V1 = (1 << (64 - 54)) - 1\nMASK_V2 = (1 << 48) - 1\nMAX_SLOT_V2 = (1 << (64 - 48)) - 1\n\n\nclass FeatureMeta(object):\n  line_id_fields = {f.name: f for f in LineId.DESCRIPTOR.fields}\n  dtypes = {\n      FieldDescriptor.CPPTYPE_FLOAT: tf.float32,\n      FieldDescriptor.CPPTYPE_DOUBLE: tf.float32,\n      FieldDescriptor.CPPTYPE_UINT32: tf.int32,\n      FieldDescriptor.CPPTYPE_INT32: tf.int32,\n      FieldDescriptor.CPPTYPE_UINT64: tf.int64,\n      FieldDescriptor.CPPTYPE_INT64: tf.int64,\n      FieldDescriptor.CPPTYPE_BOOL: tf.bool,\n      FieldDescriptor.CPPTYPE_STRING: tf.string\n  }\n\n  def __init__(self,\n               name: str,\n               slot: int = None,\n               shape: int = None,\n               dtype: tf.compat.v1.dtypes.DType = None,\n               extra=None):\n    self.name = name\n    self.slot = slot\n\n    if shape is None:\n      self.shape = 1 if self.slot is None else -1\n    else:\n      self.shape = shape\n\n    # infer data type\n    self.dtype = dtype\n    if self.dtype is None:\n      if name in self.line_id_fields:\n        cpp_type = self.line_id_fields[name].cpp_type\n        if cpp_type in self.dtypes:\n          self.dtype = self.dtypes[cpp_type]\n    if self.dtype is None:\n      if slot is None:\n        self.dtype = tf.float32\n      else:\n        self.dtype = tf.int64\n\n    self.extra = extra\n\n\n@dataclass\nclass ParserArgs(object):\n  model_name: str = 'entry'\n  fidv1_features: List[int] = None\n  fidv2_features: List[str] = None\n  sparse_features: List[str] = None\n  dense_features: List[str] = None\n  dense_feature_shapes: List[int] = None\n  dense_feature_types: List[tf.compat.v1.dtypes.DType] = None\n  extra_features: List[str] = None\n  extra_feature_shapes: List[int] = None\n  feature_list: FeatureList = None\n  batch_size: int = 64\n  max_records: int = 1000\n  signature_name: List[str] = None\n  variant_type: str = None\n  warmup_file: str = None\n  drop_rate: float = 0.5\n\n  def __post_init__(self):\n    self.model_name = self.model_name or self.get('model_name')\n    self.fidv1_features = self.fidv1_features or self.get('fidv1_features')\n    self.fidv2_features = self.fidv2_features or self.get('fidv2_features')\n    self.sparse_features = self.sparse_features or self.get('sparse_features')\n    self.dense_features = self.dense_features or self.get('dense_features')\n    self.dense_feature_shapes = self.dense_feature_shapes or self.get(\n        'dense_feature_shapes')\n    self.dense_feature_types = self.dense_feature_types or self.get(\n        'dense_feature_types')\n    self.extra_features = self.extra_features or self.get('extra_features')\n    self.extra_feature_shapes = self.extra_feature_shapes or self.get(\n        'extra_feature_shapes')\n    self.feature_list = self.feature_list or self.get('feature_list')\n    if self.feature_list is None:\n      try:\n        self.feature_list = FeatureList.parse()\n      except:\n        logging.info('cannot get feature_list, pls check!')\n\n    self.signature_name = self.signature_name or self.get('signature_name')\n    if self.signature_name is None:\n      self.signature_name = [DEFAULT_SERVING_SIGNATURE_DEF_KEY]\n    else:\n      if DEFAULT_SERVING_SIGNATURE_DEF_KEY not in self.signature_name:\n        self.signature_name.append(DEFAULT_SERVING_SIGNATURE_DEF_KEY)\n      self.signature_name = list(set(self.signature_name))\n    self.variant_type = self.variant_type or self.get('variant_type')\n\n  @classmethod\n  def get(cls, name):\n    collection = get_collection(name)\n\n    if collection is None:\n      return None\n    elif name == 'signature_name':\n      return list(set(collection))\n    else:\n      return collection[-1]\n\n\ndef gen_fids_v1(slot: int, size: int = 1) -> List[int]:\n  if 0 < slot < MAX_SLOT_V1:\n    return [\n        (slot << 54) | (randint(1, sys.maxsize) & MASK_V1) for _ in range(size)\n    ]\n  else:\n    logging.log_first_n(logging.INFO,\n                        f\"enconter slot bigger the 1023 in fid v1 {slot}\", 10)\n    return []\n\n\ndef gen_fids_v2(slot: int, size: int = 1) -> List[int]:\n  assert 0 < slot < MAX_SLOT_V2\n  return [\n      (slot << 48) | (randint(1, sys.maxsize) & MASK_V2) for _ in range(size)\n  ]\n\n\n@singledispatch\ndef fill_features():\n  raise NotImplementedError(\"Not implemented fill_features\")\n\n\n@fill_features.register(EFeature)\ndef _(feature: EFeature, meta: FeatureMeta, drop_rate: float = 0):\n  (name, size, dtype, feat) = meta.name, meta.shape, meta.dtype, meta.extra\n  if size == -1:  # sparse\n    if '_recent' in name or '_cp' in name or feat.method in {\n        'Combine', 'VectorTopString'\n    }:\n      if drop_rate > 0 and uniform(0, 1) > drop_rate:\n        feature.fid_v2_list.value.extend(gen_fids_v2(feat.slot, randint(0, 2)))\n    elif feat.slot not in {1, 200}:  # user_id, item_id\n      if uniform(0, 1) > drop_rate:\n        feature.fid_v2_list.value.extend(gen_fids_v2(feat.slot, 1))\n    else:\n      feature.fid_v2_list.value.extend(gen_fids_v2(feat.slot, 1))\n  elif dtype == tf.float64:\n    data = [uniform(0, 1) for _ in range(size)]\n    feature.double_list.value.extend(data)\n  elif dtype == tf.float32:\n    data = [uniform(0, 1) for _ in range(size)]\n    feature.float_list.value.extend(data)\n  elif dtype == tf.int64:\n    data = [randint(sys.maxsize // 2, sys.maxsize) for _ in range(size)]\n    feature.int64_list.value.extend(data)\n  else:\n    logging.warning(f'{name} is empty')\n\n\n@fill_features.register(IFeature)\ndef _(feature: IFeature, meta: FeatureMeta, drop_rate: float = 0):\n  (name, size, dtype) = meta.name, meta.shape, meta.dtype\n  feature.name = name\n  if size == -1:  # sparse\n    if '_recent' in name or '_cp' in name or '-' in name:\n      if drop_rate > 0 and uniform(0, 1) > drop_rate:\n        feature.fid.extend(gen_fids_v2(meta.slot, randint(0, 2)))\n    else:\n      feature.fid.extend(gen_fids_v2(meta.slot, 1))\n  elif dtype in {tf.float64, tf.float32}:\n    data = [uniform(0, 1) for _ in range(size)]\n    feature.float_value.extend(data)\n  elif dtype == tf.int64:\n    data = [randint(sys.maxsize // 2, sys.maxsize) for _ in range(size)]\n    feature.int64_value.extend(data)\n  else:\n    logging.warning(f'{name} is empty')\n\n\ndef fill_line_id(line_id,\n                 features: List[FeatureMeta] = None,\n                 hash_len: int = 48,\n                 actions: List[int] = None):\n  MASK = MASK_V1 if hash_len == 54 else MASK_V2\n  if features:\n    for meta in features:\n      name, shape = meta.name, meta.shape\n      if name == 'uid':\n        line_id.uid = (1 << hash_len) | (\n            randint(sys.maxsize // 2, sys.maxsize) & MASK)\n      elif name == 'item_id':\n        line_id.item_id = (200 << hash_len) | (\n            randint(sys.maxsize // 2, sys.maxsize) & MASK)\n      elif name == 'req_time':\n        line_id.req_time = int(datetime.now().timestamp())\n        line_id.sample_rate = 1.0\n      elif name == 'actions':\n        if actions:\n          line_id.actions.extend([choice(actions) for _ in range(shape)])\n        else:\n          line_id.actions.extend([randint(0, 10) for _ in range(shape)])\n      elif name == 'stay_time':\n        line_id.stay_time = uniform(a=0, b=1) * 1000\n      elif hasattr(LineId, name):\n        desc = getattr(LineId, name).DESCRIPTOR\n        if desc.label == FieldDescriptor.LABEL_REPEATED:\n          value_list = getattr(line_id, name)\n          if desc.cpp_type in {\n              FieldDescriptor.CPPTYPE_DOUBLE, FieldDescriptor.CPPTYPE_FLOAT\n          }:\n            value_list.extend([uniform(0, 1) for _ in range(shape)])\n          elif desc.cpp_type in {\n              FieldDescriptor.CPPTYPE_INT32, FieldDescriptor.CPPTYPE_INT64,\n              FieldDescriptor.CPPTYPE_UINT32, FieldDescriptor.CPPTYPE_UINT64\n          }:\n            value_list.extend([randint(0, 10) for _ in range(shape)])\n          elif desc.cpp_type == FieldDescriptor.CPPTYPE_STRING:\n            value_list.extend(['hello world' for _ in range(shape)])\n          elif desc.cpp_type == FieldDescriptor.CPPTYPE_BOOL:\n            value_list.extend([False for _ in range(shape)])\n        else:\n          if desc.cpp_type in {\n              FieldDescriptor.CPPTYPE_DOUBLE, FieldDescriptor.CPPTYPE_FLOAT\n          }:\n            setattr(line_id, name, uniform(0, 1))\n          elif desc.cpp_type in {\n              FieldDescriptor.CPPTYPE_INT32, FieldDescriptor.CPPTYPE_INT64,\n              FieldDescriptor.CPPTYPE_UINT32, FieldDescriptor.CPPTYPE_UINT64\n          }:\n            setattr(line_id, name, randint(0, 10))\n          elif desc.cpp_type == FieldDescriptor.CPPTYPE_STRING:\n            setattr(line_id, name, 'hello world')\n          elif desc.cpp_type == FieldDescriptor.CPPTYPE_BOOL:\n            setattr(line_id, name, False)\n  else:\n    line_id.uid = (1 << hash_len) | (randint(sys.maxsize // 2, sys.maxsize) &\n                                     MASK)\n    line_id.item_id = (200 << hash_len) | (\n        randint(sys.maxsize // 2, sys.maxsize) & MASK)\n    line_id.req_time = int(datetime.now().timestamp())\n    line_id.sample_rate = 1.0\n    line_id.actions.append(randint(0, 10))\n\n\ndef lg_header(source: str):\n  # calc java hash code\n  if source:\n    seed, h = 31, 0\n    for c in source:\n      h = np.int32(seed * h) + ord(c)\n\n    dfhc = int(np.uint32(h)).to_bytes(4, 'little')\n    return pack('4Bi', 0, dfhc[0], dfhc[1], dfhc[2], 0)\n  else:\n    return int.to_bytes(0, 8, byteorder='little')\n\n\ndef sort_header(sort_id: bool, kafka_dump: bool, kafka_dump_prefix: bool):\n  # kafka_dump_prefix: [size: 8 bytes][aggregate_page_sortid_size: 8 bytes]\n  # sort_id: [size: 8 bytes][sort_id: size bytes]\n  # kafka_dump: [kafka_dump: 8 bytes]\n  if sort_id and not (kafka_dump or kafka_dump_prefix):\n    return pack('<Q', 0)\n  elif sort_id and kafka_dump and not kafka_dump_prefix:\n    return pack('<2Q', 0, 0)\n  elif sort_id and kafka_dump_prefix and not kafka_dump:\n    return pack('<3Q', 0, 0, 0)\n  elif not (sort_id or kafka_dump or kafka_dump_prefix):\n    return b''\n  else:\n    raise Exception(\n        'kafka_dump_prefix={kafka_dump_prefix}, sort_id={sort_id}, kafka_dump={kafka_dump} not support'\n    )\n\n\ndef gen_example(sparse_features: List[str],\n                dense_features: List[FeatureMeta] = None,\n                extra_features: List[FeatureMeta] = None,\n                feature_list: FeatureList = None,\n                drop_rate: float = 0,\n                actions: List[int] = None) -> Example:\n  assert len(sparse_features) > 0 and len(sparse_features) == len(\n      set(sparse_features))\n\n  name_to_info = {}\n  for name in sparse_features:\n    try:\n      feat = feature_list.get(name)\n      if feat is not None:\n        name_to_info[name] = FeatureMeta(name,\n                                         slot=feat.slot,\n                                         dtype=tf.int64,\n                                         extra=feat)\n    except:\n      _, slot = get_feature_name_and_slot(name)\n      feat = Feature(feature_name=name, slot=slot)\n      name_to_info[name] = FeatureMeta(name,\n                                       slot=feat.slot,\n                                       dtype=tf.int64,\n                                       extra=feat)\n      # logging.warning(f'cannot find name {name} in feature_list')\n\n  if dense_features:\n    name_to_info.update({meta.name: meta for meta in dense_features})\n\n  assert len(name_to_info) > 0\n\n  example = Example()\n  label_meta = name_to_info.pop('label', None)\n  for name, meta in name_to_info.items():\n    named_feature = example.named_feature.add()\n    if meta.slot:\n      named_feature.id = meta.slot\n    named_feature.name = name\n    fill_features(named_feature.feature, meta, drop_rate)\n\n  fill_line_id(example.line_id, extra_features, actions=actions)\n  if label_meta:\n    example.label.extend([choice([0, 1]) for _ in range(label_meta.shape)])\n  else:\n    example.label.append(choice([0, 1]))\n  return example\n\n\ndef gen_instance(fidv1_features: List[int] = None,\n                 fidv2_features: List[str] = None,\n                 dense_features: List[FeatureMeta] = None,\n                 extra_features: List[FeatureMeta] = None,\n                 feature_list: FeatureList = None,\n                 drop_rate: float = 0,\n                 actions: List[int] = None) -> Instance:\n  inst = Instance()\n  if fidv1_features is not None:\n    assert len(fidv1_features) > 0 and len(fidv1_features) == len(\n        set(fidv1_features))\n    for slot in fidv1_features:\n      size = 1 if slot in {1, 200} else randint(0, 3)\n      fids_v1 = gen_fids_v1(slot, size)\n      if fids_v1:\n        inst.fid.extend(fids_v1)\n\n  name_to_info = {}\n  if fidv2_features:\n    assert len(fidv2_features) > 0 and len(fidv2_features) == len(\n        set(fidv2_features))\n    notfound_names = list()\n    for name in fidv2_features:\n      try:\n        feat = feature_list.get(name)\n        if feat is not None:\n          name_to_info[name] = FeatureMeta(name,\n                                           slot=feat.slot,\n                                           dtype=tf.int64,\n                                           extra=feat)\n      except:\n        notfound_names.append(name)\n    logging.warning(\n        f'Total {len(notfound_names)} features not found in feature_list: {notfound_names}'\n    )\n\n  if dense_features:\n    name_to_info.update({meta.name: meta for meta in dense_features})\n\n  label_meta = name_to_info.pop('label', None)\n  for name, meta in name_to_info.items():\n    feature = inst.feature.add()\n    fill_features(feature, meta, drop_rate)\n\n  fill_line_id(inst.line_id, extra_features, actions=actions)\n  if label_meta:\n    inst.label.extend([choice([0, 1]) for _ in range(label_meta.shape)])\n  else:\n    inst.label.append(choice([0, 1]))\n  return inst\n\n\ndef gen_example_batch(sparse_features: List[str],\n                      dense_features: List[FeatureMeta] = None,\n                      extra_features: List[FeatureMeta] = None,\n                      feature_list: FeatureList = None,\n                      batch_size: int = 64,\n                      drop_rate: float = 0,\n                      actions: List[int] = None) -> ExampleBatch:\n  assert len(sparse_features) > 0 and len(sparse_features) == len(\n      set(sparse_features)) and batch_size > 0\n  name_to_info = {}\n  for name in sparse_features:\n    try:\n      feat = feature_list.get(name)\n      if feat is not None:\n        name_to_info[name] = FeatureMeta(name,\n                                         slot=feat.slot,\n                                         dtype=tf.int64,\n                                         extra=feat)\n    except:\n      _, slot = get_feature_name_and_slot(name)\n      feat = Feature(feature_name=name, slot=slot)\n      name_to_info[name] = FeatureMeta(name,\n                                       slot=feat.slot,\n                                       dtype=tf.int64,\n                                       extra=feat)\n      # logging.warning(f'cannot find name {name} in feature_list')\n\n  if dense_features:\n    name_to_info.update({meta.name: meta for meta in dense_features})\n  assert len(name_to_info) > 0\n\n  example_batch = ExampleBatch(batch_size=batch_size)\n  label_meta = name_to_info.pop('label', None)\n  for name, meta in name_to_info.items():\n    named_feature_list = example_batch.named_feature_list.add()\n    if meta.slot:\n      named_feature_list.id = meta.slot\n    named_feature_list.name = name\n\n    for _ in range(batch_size):\n      feature = named_feature_list.feature.add()\n      fill_features(feature, meta, drop_rate)\n\n  named_feature_list = example_batch.named_feature_list.add()\n  named_feature_list.name = '__LINE_ID__'\n  for _ in range(batch_size):\n    feature = named_feature_list.feature.add()\n    line_id = LineId()\n    fill_line_id(line_id, extra_features, hash_len=48, actions=actions)\n    feature.bytes_list.value.append(line_id.SerializeToString())\n\n  named_feature_list = example_batch.named_feature_list.add()\n  named_feature_list.name = '__LABEL__'\n  for i in range(batch_size):\n    feature = named_feature_list.feature.add()\n    if label_meta:\n      feature.float_list.value.extend(\n          [choice([0, 1]) for _ in range(label_meta.shape)])\n    else:\n      feature.float_list.value.append(i % 2)\n\n  return example_batch\n\n\ndef gen_prediction_log(\n    args: ParserArgs) -> Iterable[prediction_log_pb2.PredictionLog]:\n  assert args.variant_type in {\n      'example', 'instance', 'example_batch', 'examplebatch'\n  } and args.batch_size < args.max_records\n  if args.variant_type == 'example':\n    input_name = 'examples'\n  elif args.variant_type == 'instance':\n    input_name = 'instances'\n  else:\n    input_name = 'example_batch'\n\n  dense_feature_meta = []\n  if args.dense_features:\n    for name, shape, dtype in zip(args.dense_features,\n                                  args.dense_feature_shapes,\n                                  args.dense_feature_types):\n      try:\n        assert shape >= 1\n        try:\n          feat = args.feature_list.get(name)\n          if feat is not None:\n            dense_feature_meta.append(\n                FeatureMeta(name, shape=shape, dtype=dtype, extra=feat))\n          else:\n            dense_feature_meta.append(\n                FeatureMeta(name, shape=shape, dtype=dtype))\n        except:\n          dense_feature_meta.append(FeatureMeta(name, shape=shape, dtype=dtype))\n      except:\n        logging.warning(f'cannot find name {name} in feature_list')\n  else:\n    dense_feature_meta = None\n\n  extra_meta = None\n  if args.extra_features:\n    extra_meta = [\n        FeatureMeta(name=name, shape=shape)\n        for name, shape in zip(args.extra_features, args.extra_feature_shapes)\n    ]\n\n  if args.signature_name is None:\n    args.signature_name = [DEFAULT_SERVING_SIGNATURE_DEF_KEY]\n  num_log = args.max_records // args.batch_size\n  assert num_log >= len(args.signature_name)\n\n  export_ctx = export_context.get_current_export_ctx()\n  graph = tf.compat.v1.get_default_graph()\n  if export_ctx is None:\n    signatures = None\n  else:\n    signatures = {\n        signature.name: signature for signature in export_ctx.signatures(graph)\n    }\n    for name in signatures:\n      if name not in args.signature_name:\n        args.signature_name.append(name)\n\n  for i in range(num_log):\n    request = predict_pb2.PredictRequest()\n    request.model_spec.name = args.model_name\n    signature_name = args.signature_name[i % len(args.signature_name)]\n    request.model_spec.signature_name = signature_name\n\n    if signatures is None or signatures[signature_name].inputs:\n      if signatures is not None:\n        assert input_name in signatures[signature_name].inputs\n      if args.variant_type == 'example':\n        instances = [\n            gen_example(args.sparse_features, dense_feature_meta, extra_meta,\n                        args.feature_list, args.drop_rate).SerializeToString()\n            for _ in range(args.batch_size)\n        ]\n      elif args.variant_type == 'instance':\n        instances = [\n            gen_instance(args.fidv1_features, args.fidv2_features,\n                         dense_feature_meta, extra_meta, args.feature_list,\n                         args.drop_rate).SerializeToString()\n            for _ in range(args.batch_size)\n        ]\n      else:\n        instances = [\n            gen_example_batch(args.sparse_features, dense_feature_meta,\n                              extra_meta, args.feature_list, args.batch_size,\n                              args.drop_rate).SerializeToString()\n        ]\n      request.inputs[input_name].CopyFrom(tf.make_tensor_proto(instances))\n\n    log = prediction_log_pb2.PredictionLog(\n        predict_log=prediction_log_pb2.PredictLog(request=request))\n    yield log\n    if signatures:\n      outputs = signatures[signature_name].outputs\n      if signature_name == DEFAULT_SERVING_SIGNATURE_DEF_KEY and outputs is not None:\n        if len(outputs) > 1 or (len(outputs) == 1 and\n                                PredictOutput._SINGLE_OUTPUT_DEFAULT_NAME\n                                not in outputs):\n          for head_name in outputs:\n            request.output_filter.append(head_name)\n            log = prediction_log_pb2.PredictionLog(\n                predict_log=prediction_log_pb2.PredictLog(request=request))\n            yield log\n            del request.output_filter[:]\n\n\ndef gen_warmup_file(warmup_file: str = None, drop_rate: float = None):\n  warmup_args = ParserArgs(warmup_file=warmup_file)\n\n  if drop_rate is not None:\n    warmup_args.drop_rate = drop_rate\n\n  if not warmup_args.warmup_file:\n    logging.info(f'warmup_file is None, skip')\n    return None\n  elif tf.io.gfile.exists(warmup_args.warmup_file):\n    logging.info(f'{warmup_args.warmup_file} exists, return directly')\n    return warmup_args.warmup_file\n  else:\n    features = warmup_args.fidv1_features or warmup_args.fidv2_features or \\\n      warmup_args.sparse_features or warmup_args.dense_features or warmup_args.extra_features\n    if features is None:\n      logging.warning('features is None, pls. check!')\n      return None\n\n    # if warmup_args.variant_type != 'instance' and warmup_args.feature_list is None:\n    #   logging.warning('feature_list is None, pls. check!')\n    #   return None\n\n    # remove label if exists\n    if warmup_args.dense_features is not None and 'label' in warmup_args.dense_features:\n      dense_features = deepcopy(warmup_args.dense_features)\n      dense_feature_shapes = deepcopy(warmup_args.dense_feature_shapes)\n      dense_feature_types = deepcopy(warmup_args.dense_feature_types)\n\n      idx = warmup_args.dense_features.index('label')\n      if idx is not None and idx >= 0:\n        try:\n          del dense_features[idx]\n          del dense_feature_shapes[idx]\n          del dense_feature_types[idx]\n        except:\n          pass\n    else:\n      dense_features = None\n      dense_feature_shapes = None\n      dense_feature_types = None\n\n    warmup_args.dense_features = dense_features\n    warmup_args.dense_feature_shapes = dense_feature_shapes\n    warmup_args.dense_feature_types = dense_feature_types\n\n    try:\n      logging.info(\n          f'begin to write prediction log to {warmup_args.warmup_file}')\n      dirname = os.path.dirname(warmup_args.warmup_file)\n      if not tf.io.gfile.exists(dirname):\n        tf.io.gfile.makedirs(dirname)\n\n      with tf.io.TFRecordWriter(warmup_args.warmup_file) as writer:\n        for log in gen_prediction_log(warmup_args):\n          writer.write(log.SerializeToString())\n      logging.info(\n          f'finish to write prediction log to {warmup_args.warmup_file}')\n      return warmup_args.warmup_file\n    except Exception as e:\n      logging.warning(f'{type(e)}: {str(e)}')\n      raise e\n\n\ndef gen_random_data_file(data_file_name: str,\n                         args: ParserArgs,\n                         num_batch: int = 128,\n                         source: str = None,\n                         sort_id: bool = True,\n                         kafka_dump: bool = False,\n                         kafka_dump_prefix: bool = False,\n                         actions: List[int] = None):\n  dense_feature_meta = []\n  if args.dense_features:\n    for name, shape, dtype in zip(args.dense_features,\n                                  args.dense_feature_shapes,\n                                  args.dense_feature_types):\n      try:\n        assert shape >= 1\n        try:\n          feat = args.feature_list.get(name)\n          if feat is not None:\n            dense_feature_meta.append(\n                FeatureMeta(name, shape=shape, dtype=dtype, extra=feat))\n          else:\n            dense_feature_meta.append(\n                FeatureMeta(name, shape=shape, dtype=dtype))\n        except:\n          dense_feature_meta.append(FeatureMeta(name, shape=shape, dtype=dtype))\n      except:\n        logging.warning(f'cannot find name {name} in feature_list')\n  else:\n    dense_feature_meta = None\n\n  extra_meta = None\n  if args.extra_features:\n    extra_meta = [\n        FeatureMeta(name=name, shape=shape)\n        for name, shape in zip(args.extra_features, args.extra_feature_shapes)\n    ]\n\n  instances = []\n  for i in range(num_batch):\n    if args.variant_type == 'example':\n      instances.extend([\n          gen_example(args.sparse_features,\n                      dense_feature_meta,\n                      extra_meta,\n                      args.feature_list,\n                      args.drop_rate,\n                      actions=actions).SerializeToString()\n          for _ in range(args.batch_size)\n      ])\n    elif args.variant_type == 'instance':\n      instances.extend([\n          gen_instance(args.fidv1_features,\n                       args.fidv2_features,\n                       dense_feature_meta,\n                       extra_meta,\n                       args.feature_list,\n                       args.drop_rate,\n                       actions=actions).SerializeToString()\n          for _ in range(args.batch_size)\n      ])\n    else:\n      instances.extend([\n          gen_example_batch(args.sparse_features,\n                            dense_feature_meta,\n                            extra_meta,\n                            args.feature_list,\n                            args.batch_size,\n                            args.drop_rate,\n                            actions=actions).SerializeToString()\n      ])\n  if sort_id:\n    header = sort_header(sort_id, kafka_dump, kafka_dump_prefix)\n  else:\n    header = lg_header(source)\n  with open(data_file_name, 'wb') as ostream:\n    for inst in instances:\n      ostream.write(header)\n      ostream.write(int.to_bytes(len(inst), 8, byteorder='little'))\n      ostream.write(inst)\n"
  },
  {
    "path": "monolith/native_training/model_export/data_gen_utils_test.py",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/demo_export.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport getpass\n\nfrom absl import app\nfrom absl import flags\n\nimport tensorflow as tf\n\nfrom monolith.native_training.data.training_instance.python.parse_instance_ops import parse_instances\nfrom monolith.native_training import model\nfrom monolith.native_training import cpu_training\nfrom monolith.native_training.model import TestFFMModel\nfrom monolith.native_training.model_export.export_context import ExportMode, enter_export_mode\nfrom monolith.native_training.model_export.saved_model_exporters import StandaloneExporter, DistributedExporter\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_integer(\n    \"num_ps\",\n    default=5,\n    help=(\"Number of parameter servers. Must align with training.\")),\n\nflags.DEFINE_string(\n    \"model_dir\",\n    default=\"/tmp/{}/monolith/native_training/demo/ckpt\".format(\n        getpass.getuser()),\n    help=(\"Model dir containing training ckpts.\"),\n)\n\nflags.DEFINE_string(\n    \"export_base\",\n    default=\"/tmp/{}/monolith/native_training/demo/saved_model\".format(\n        getpass.getuser()),\n    help=(\"The path to saved exported saved model.\"),\n)\n\nflags.DEFINE_enum_class(\"export_mode\",\n                        default=ExportMode.STANDALONE,\n                        enum_class=ExportMode,\n                        help=\"standalone or distributed\")\n\n\ndef export_saved_model(model_dir, export_base, num_ps, export_mode):\n  tf.compat.v1.disable_eager_execution()\n  tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.INFO)\n  params = TestFFMModel.params()\n  params.name = \"demo_export\"\n  params.train.per_replica_batch_size = 64\n  task = params.instantiate()\n  cpu_training_task = cpu_training.CpuTraining(\n      cpu_training.CpuTrainingConfig(num_ps=num_ps), task)\n  if export_mode == ExportMode.STANDALONE:\n    exporter = StandaloneExporter(cpu_training_task.create_model_fn(),\n                                  model_dir=model_dir,\n                                  export_dir_base=export_base)\n  elif export_mode == ExportMode.DISTRIBUTED:\n    exporter = DistributedExporter(cpu_training_task.create_model_fn(),\n                                   model_dir=model_dir,\n                                   export_dir_base=export_base,\n                                   shared_embedding=False)\n\n  def serving_input_receiver_fn():\n    receiver_tensors = {}\n    features = {}\n    instances_placeholder = tf.compat.v1.placeholder(dtype=tf.string,\n                                                     shape=(None,))\n    receiver_tensors[\"instances\"] = instances_placeholder\n    parsed_results = parse_instances(\n        instances_placeholder,\n        fidv1_features=[i for i in range(model._NUM_SLOTS)],\n        fidv2_features=None,\n        misc_float_features=[],\n        misc_int64_features=[])\n    for i in range(model._NUM_SLOTS):\n      features[\"feature_{}\".format(i)] = parsed_results[\"slot_{}\".format(i)]\n    return tf.estimator.export.ServingInputReceiver(features, receiver_tensors)\n\n  exporter.export_saved_model(serving_input_receiver_fn)\n\n\ndef main(_):\n  export_saved_model(FLAGS.model_dir, FLAGS.export_base, FLAGS.num_ps,\n                     FLAGS.export_mode)\n\n\nif __name__ == \"__main__\":\n  app.run(main)\n"
  },
  {
    "path": "monolith/native_training/model_export/demo_export_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nimport tensorflow as tf\n\nfrom monolith.native_training.model_export.export_context import ExportMode\nfrom monolith.native_training.model_export import demo_export\nfrom monolith.native_training import cpu_training\nfrom monolith.native_training.model import TestFFMModel\n\ntf.compat.v1.disable_eager_execution()\n\n\nclass DemoExportTest(tf.test.TestCase):\n\n  def test_demo_export(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_ffm_model\")\n    params = TestFFMModel.params()\n    params.name = \"test_ffm_model\"\n    params.train.per_replica_batch_size = 64\n    cpu_training.local_train(params, num_ps=5, model_dir=model_dir)\n\n    demo_export.export_saved_model(\n        model_dir,\n        os.path.join(os.environ[\"TEST_TMPDIR\"], \"standalone_saved_model\"), 5,\n        ExportMode.STANDALONE)\n\n    demo_export.export_saved_model(\n        model_dir,\n        os.path.join(os.environ[\"TEST_TMPDIR\"], \"distributed_saved_model\"), 5,\n        ExportMode.DISTRIBUTED)\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/model_export/demo_predictor.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Some examples:\n# bazel run //monolith/native_training/model_export:demo_predictor -- --saved_model_path=/ps_0/1623816010 --signature=lookup\n# bazel run //monolith/native_training/model_export:demo_predictor -- --saved_model_path=/ps_0/1623816010 --signature=hashtable_assign\n# bazel run //monolith/native_training/model_export:demo_predictor -- --saved_model_path=/standalone/1623816010 --signature=serving_default\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport numpy as np\nimport tensorflow as tf\n\nfrom idl.matrix.proto import proto_parser_pb2\nfrom monolith.native_training import model\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_string(\"saved_model_path\",\n                    default=\"\",\n                    help=(\"The path for the demo saved model\"))\n\nflags.DEFINE_string(\"tag_set\", \"serve\", \"tag_set\")\nflags.DEFINE_string(\"signature\", \"serving_default\", \"signature to predict\")\nflags.DEFINE_integer(\"batch_size\", 128, \"batch size\")\n\n\ndef make_fid_v1(slot_id, fid):\n  return (slot_id << 54) | fid\n\n\ndef generate_demo_instance():\n  instance = proto_parser_pb2.Instance()\n  v1_fids = []\n  max_vocab = max(model._VOCAB_SIZES)\n  for i in range(model._NUM_SLOTS):\n    v1_fids.extend(\n        make_fid_v1(i, i * max_vocab + np.random.randint(max_vocab, size=5)))\n  instance.fid.extend(v1_fids)\n  return instance.SerializeToString()\n\n\ndef random_generate_instances(bs):\n  return [generate_demo_instance() for _ in range(bs)]\n\n\ndef random_generate_examples(bs):\n  return [model.generate_ffm_example(model._VOCAB_SIZES) for _ in range(bs)]\n\n\ndef random_generate_int(shape):\n  max_vocab = max(model._VOCAB_SIZES) * model._NUM_SLOTS\n  return np.random.randint(max_vocab, size=shape)\n\n\ndef random_generate_float(shape):\n  return np.random.uniform(size=shape)\n\n\ndef predict():\n  with tf.compat.v1.Session(graph=tf.compat.v1.Graph()) as sess:\n    meta_graph = tf.compat.v1.saved_model.load(sess, {FLAGS.tag_set},\n                                               FLAGS.saved_model_path)\n    input_infos = meta_graph.signature_def[FLAGS.signature].inputs\n    output_infos = meta_graph.signature_def[FLAGS.signature].outputs\n\n    feed_dict = {}\n    for input_name, tensor_info in input_infos.items():\n      shape = [\n          FLAGS.batch_size if dim.size == -1 else dim.size\n          for dim in tensor_info.tensor_shape.dim\n      ]\n      logging.info(\"Generate {} of shape {}\".format(input_name, shape))\n      if tensor_info.dtype == tf.dtypes.string.as_datatype_enum:\n        assert len(shape) == 1\n        feed_dict[tensor_info.name] = random_generate_instances(shape[0])\n      elif tensor_info.dtype == tf.dtypes.int64.as_datatype_enum:\n        feed_dict[tensor_info.name] = random_generate_int(shape)\n      elif tensor_info.dtype == tf.dtypes.float32.as_datatype_enum:\n        feed_dict[tensor_info.name] = random_generate_float(shape)\n      else:\n        raise ValueError(\"{} has invalid setting {}.\".format(\n            input_name, tensor_info))\n    fetch = {\n        output_name: tensor_info.name\n        for output_name, tensor_info in output_infos.items()\n    }\n    logging.info(sess.run(fetch, feed_dict=feed_dict))\n\n\ndef main(_):\n  predict()\n\n\nif __name__ == \"__main__\":\n  logging.set_verbosity(logging.INFO)\n  app.run(main)\n"
  },
  {
    "path": "monolith/native_training/model_export/demo_predictor_client.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport grpc\nimport numpy as np\nimport tensorflow as tf\n\nfrom tensorflow_serving.apis import predict_pb2, get_model_metadata_pb2\nfrom tensorflow_serving.apis import prediction_service_pb2_grpc\n\nfrom monolith.native_training import model\nfrom monolith.native_training.model_export import demo_predictor\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_string(\"server\", \"localhost:8500\", \"PredictionService host:port\")\nflags.DEFINE_string(\"model_name\", \"default\", \"Model name\")\nflags.DEFINE_string(\"signature_name\", \"serving_default\", \"Signature Name\")\nflags.DEFINE_bool(\"use_example\", False, \"tf example or instance\")\n\n\ndef get_signature_def(stub):\n  request = get_model_metadata_pb2.GetModelMetadataRequest()\n  request.model_spec.name = FLAGS.model_name\n  request.metadata_field.append(\"signature_def\")\n  result = stub.GetModelMetadata(request)\n  any_proto = result.metadata[\"signature_def\"]\n\n  signature_def_map = get_model_metadata_pb2.SignatureDefMap()\n  assert any_proto.Is(signature_def_map.DESCRIPTOR)\n  any_proto.Unpack(signature_def_map)\n  signature_def = signature_def_map.signature_def[FLAGS.signature_name]\n  print([x for x in signature_def_map.signature_def])\n  return signature_def\n\n\ndef main(_):\n  channel = grpc.insecure_channel(FLAGS.server)\n  stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)\n  signature_def = get_signature_def(stub)\n\n  request = predict_pb2.PredictRequest()\n  request.model_spec.name = FLAGS.model_name\n  request.model_spec.signature_name = FLAGS.signature_name\n\n  input_infos = signature_def.inputs\n\n  for input_name, tensor_info in input_infos.items():\n    shape = [\n        FLAGS.batch_size if dim.size == -1 else dim.size\n        for dim in tensor_info.tensor_shape.dim\n    ]\n    logging.info(\"Generate {} of shape {}\".format(input_name, shape))\n    if tensor_info.dtype == tf.dtypes.string.as_datatype_enum:\n      assert len(shape) == 1\n      if FLAGS.use_example:\n        examples = demo_predictor.random_generate_examples(shape[0])\n      else:\n        examples = demo_predictor.random_generate_instances(shape[0])\n      request.inputs[input_name].CopyFrom(tf.make_tensor_proto(examples))\n    elif tensor_info.dtype == tf.dtypes.int64.as_datatype_enum:\n      request.inputs[input_name].CopyFrom(\n          tf.make_tensor_proto(demo_predictor.random_generate_int(shape)))\n    elif tensor_info.dtype == tf.dtypes.float32.as_datatype_enum:\n      request.inputs[input_name].CopyFrom(\n          tf.make_tensor_proto(demo_predictor.random_generate_float(shape),\n                               dtype=tf.float32))\n    else:\n      raise ValueError(\"{} has invalid setting {}.\".format(\n          input_name, tensor_info))\n\n  result = stub.Predict(request, 30)\n  logging.info(result)\n\n\nif __name__ == \"__main__\":\n  logging.set_verbosity(logging.INFO)\n  app.run(main)\n"
  },
  {
    "path": "monolith/native_training/model_export/export.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax=\"proto2\";\n\npackage monolith.model_export;\n\nmessage ServingEntry {\n  optional string export_dir = 1;\n  \n  // Records the global step for exported model.\n  optional int64 global_step = 2;\n\n  // TODO(leqi.zou): Add deps to support better recovery.\n}\n\nmessage ServingModelState {\n  repeated ServingEntry entries = 1;\n}\n\n"
  },
  {
    "path": "monolith/native_training/model_export/export_context.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom collections import namedtuple\nfrom collections import defaultdict\nfrom enum import Enum\nfrom typing import List\n\nimport tensorflow as tf\nfrom tensorflow.python.util import tf_contextlib\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.utils import add_to_collections\n\n\nclass ExportMode(Enum):\n  NONE = 0\n  STANDALONE = 1\n  DISTRIBUTED = 2\n\n\nSavedModelSignature = namedtuple('SavedModelSignature',\n                                 ['name', 'inputs', 'outputs'])\n\n\n@monolith_export\nclass ExportContext:\n  \"\"\"保存模型导出的上下文\"\"\"\n\n  def __init__(self, with_remote_gpu=False):\n    self._sub_graphs = defaultdict(lambda: tf.Graph())\n    self._dense_sub_graphs = defaultdict(lambda: tf.Graph())\n    self._signatures = defaultdict(lambda: {})\n    self._with_remote_gpu = with_remote_gpu\n\n  def sub_graph(self, name: str) -> tf.Graph:\n    return self._sub_graphs[name]\n\n  def dense_sub_graph(self, name: str) -> tf.Graph:\n    return self._dense_sub_graphs[name]\n\n  @property\n  def dense_sub_graphs(self):\n    return self._dense_sub_graphs\n\n  @property\n  def sub_graphs(self):\n    return self._sub_graphs\n\n  @property\n  def with_remote_gpu(self):\n    return self._with_remote_gpu\n\n  def signatures(self, graph: tf.Graph) -> List[SavedModelSignature]:\n    return self._signatures[id(graph)].values()\n\n  def add_signature(self, graph: tf.Graph, name: str, inputs, outputs):\n    add_to_collections('signature_name', name)\n    self._signatures[id(graph)][name] = SavedModelSignature(name=name,\n                                                            inputs=inputs,\n                                                            outputs=outputs)\n\n  def merge_signature(self, graph: tf.Graph, name: str, inputs, outputs):\n    if name not in self._signatures[id(graph)]:\n      self._signatures[id(graph)][name] = SavedModelSignature(name=name,\n                                                              inputs={},\n                                                              outputs={})\n    self._signatures[id(graph)][name].inputs.update(inputs)\n    self._signatures[id(graph)][name].outputs.update(outputs)\n\n  @property\n  def sub_graph_num(self):\n    \"\"\"得到当前export_context中sub graph的数量\"\"\"\n    return len(self._sub_graphs)\n\n\nEXPORT_MODE = ExportMode.NONE\nEXPORT_CTX = None\n\n\n@monolith_export\ndef is_exporting():\n  \"\"\"是否在导出模式中\"\"\"\n  return EXPORT_MODE != ExportMode.NONE\n\n\n@monolith_export\ndef is_exporting_standalone():\n  \"\"\"是否在导出单机模型\"\"\"\n  return EXPORT_MODE == ExportMode.STANDALONE\n\n\n@monolith_export\ndef is_exporting_distributed():\n  \"\"\"是否正在导出分布式模型\"\"\"\n  return EXPORT_MODE == ExportMode.DISTRIBUTED\n\n\n@monolith_export\ndef get_current_export_ctx() -> ExportContext:\n  \"\"\"获取当前的上下文\"\"\"\n  return EXPORT_CTX\n\n\n@monolith_export\n@tf_contextlib.contextmanager\ndef enter_export_mode(mode: ExportMode, export_ctx=None):\n  \"\"\"进入模型导出模式，会根据mode构图\n\n  Args:\n    mode (:obj:`ExportMode`): 导出模式，可选ExportMode.DISTRIBUTED, ExportMode.STANDALONE\n    export_ctx (:obj:`ExportContext`, optional): 模型导出上下文\n  \"\"\"\n\n  global EXPORT_MODE, EXPORT_CTX\n  assert EXPORT_MODE is ExportMode.NONE and EXPORT_CTX is None, \"export mode can't be nested\"\n  if export_ctx is None:\n    export_ctx = ExportContext()\n  EXPORT_MODE = mode\n  EXPORT_CTX = export_ctx\n  try:\n    yield export_ctx\n  finally:\n    EXPORT_MODE = ExportMode.NONE\n    EXPORT_CTX = None\n\n\n@monolith_export\ndef is_dry_run_or_exporting():\n  graph = tf.compat.v1.get_default_graph()\n  return is_exporting() or hasattr(graph, 'dry_run')\n"
  },
  {
    "path": "monolith/native_training/model_export/export_hooks.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"A hook that exports the model at the same time we save the checkpoint\"\"\"\nimport os\nimport re\nfrom pathlib import Path\nfrom typing import List\nfrom absl import logging\nimport tensorflow as tf\nimport traceback\n\nfrom monolith.native_training import utils\nfrom monolith.native_training import save_utils\nfrom monolith.native_training.metric import cli\nfrom monolith.native_training.model_export import saved_model_exporters\nfrom monolith.native_training.model_export import export_pb2\nfrom monolith.native_training.model_export import export_state_utils\n\n\ndef get_global_step(checkpoint_path: str):\n  pattern = re.compile(r'^.*model.ckpt-(\\d+)$')\n  matched = pattern.match(checkpoint_path.strip())\n  assert matched is not None\n  return int(matched.group(1))\n\n\nclass ExportSaverListener(tf.estimator.CheckpointSaverListener):\n  \"\"\"A hook that exports saved model whenever a new ckpt is generated.\"\"\"\n\n  def __init__(self,\n               save_path: str,\n               serving_input_receiver_fn,\n               exporter: saved_model_exporters.BaseExporter,\n               exempt_checkpoint_paths: List[str] = None,\n               dense_only: bool = False):\n    super().__init__()\n    self._serving_input_receiver_fn = serving_input_receiver_fn\n    self._helper = save_utils.SaveHelper(save_path)\n    self._exporter = exporter\n    self._exempt_checkpoint_steps = set([\n        get_global_step(p) for p in exempt_checkpoint_paths\n    ]) if exempt_checkpoint_paths else set()\n    self._dense_only = dense_only\n    self._mcli = cli.get_cli(utils.get_metric_prefix())\n    logging.info('Exempt global steps={}'.format(self._exempt_checkpoint_steps))\n\n  def after_save(self, session, global_step_value):\n    checkpoint_file = self._helper.get_ckpt_prefix(global_step_value)\n    export_dirs = self._exporter.export_saved_model(\n        self._serving_input_receiver_fn, checkpoint_file, global_step_value)\n    if isinstance(export_dirs, bytes):\n      export_dirs = [export_dirs]\n    elif isinstance(export_dirs, dict):\n      export_dirs = export_dirs.values()\n\n    for export_dir in export_dirs:\n      self._add_entry_to_state(export_dir, global_step_value)\n      # delete old saved models\n      self._maybe_delete_old_entries(export_dir)\n\n  def _add_entry_to_state(self, export_dir: bytes, global_step_value: int):\n    export_dir = export_dir.decode()\n    export_dir_base = os.path.dirname(export_dir)\n    export_version = os.path.basename(export_dir)\n    state = export_state_utils.get_export_saver_listener_state(export_dir_base)\n\n    entry = export_pb2.ServingEntry()\n    entry.export_dir = export_dir\n    entry.global_step = global_step_value\n    state.entries.append(entry)\n\n    export_state_utils.overwrite_export_saver_listener_state(\n        export_dir_base, state)\n\n    self._update_metrics(export_dir_base, export_version)\n\n  def _maybe_delete_old_entries(self, export_dir: bytes):\n    export_dir = export_dir.decode()\n    export_dir_base = os.path.dirname(export_dir)\n    old_state = export_state_utils.get_export_saver_listener_state(\n        export_dir_base)\n    existing_steps = self._helper.get_existing_checkpoint_steps(\n    ) | self._exempt_checkpoint_steps\n    if self._dense_only:\n      path = Path(export_dir_base)\n      model_dir = str(path.parent.parent)\n      full_stats = tf.train.get_checkpoint_state(model_dir)\n      if full_stats:\n        existing_steps |= set([\n            get_global_step(ckpt)\n            for ckpt in full_stats.all_model_checkpoint_paths\n        ])\n\n    new_state = export_pb2.ServingModelState()\n    for entry in old_state.entries:\n      if entry.global_step in existing_steps:\n        new_state.entries.append(entry)\n      else:\n        try:\n          logging.info(\"Deleted export dir: %s.\", entry.export_dir)\n          tf.io.gfile.rmtree(entry.export_dir)\n        except tf.errors.NotFoundError:\n          logging.warning(\n              \"Hit NotFoundError when deleting '%s', possibly because another \"\n              \"process/thread is also deleting/moving the same file\",\n              entry.export_dir)\n\n    export_state_utils.overwrite_export_saver_listener_state(\n        export_dir_base, new_state)\n\n  def _update_metrics(self, export_dir_base: str, version: str):\n    try:\n      model_name = os.path.basename(export_dir_base)\n      tags = {\n        \"model_name\": model_name,\n      }\n      version = version.split(\".\")[0] # In case version is float\n      if version.isdigit(): # In case rough sort, version maybe xxx_dense\n        self._mcli.emit_store(\"export_models.latest_version\",\n                              int(version), tags)\n        self._mcli.flush()\n    except Exception as e:\n      err_mesg = f\"meet error when trying to emit metric: export_models.latest_version, stack trace: {traceback.format_exc()}\"\n      logging.log_every_n_seconds(logging.WARNING, err_mesg, 1200)\n    \n"
  },
  {
    "path": "monolith/native_training/model_export/export_hooks_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom monolith.native_training import save_utils\nimport os\nimport time\nfrom unittest import mock\n\nimport tensorflow as tf\n\nfrom monolith.native_training.model_export import export_hooks\nfrom monolith.native_training.model_export import export_state_utils\nfrom monolith.native_training import save_utils\n\n\nclass ExportHookTest(tf.test.TestCase):\n\n  def testBasic(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"testBasic_model_dir\")\n    export_dir_base = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                                   \"testBasic_export_dir\")\n    exporter = mock.MagicMock()\n    export_dir = os.path.join(export_dir_base, \"12345678\")\n    os.makedirs(export_dir)\n\n    def export_saved_model(serving_input_receiver_fn, checkpoint_file,\n                           global_step):\n      self.assertEqual(checkpoint_file, model_dir + \"/model.ckpt-10\")\n      return export_dir.encode()\n\n    exporter.export_saved_model.side_effect = export_saved_model\n\n    saver_hook = save_utils.NoFirstSaveCheckpointSaverHook(\n        model_dir,\n        save_steps=10000,\n        listeners=[\n            export_hooks.ExportSaverListener(model_dir + \"/model.ckpt\", None,\n                                             exporter)\n        ])\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    global_step = tf.compat.v1.assign(global_step, 10)\n    with tf.compat.v1.train.SingularMonitoredSession(\n        hooks=[saver_hook]) as sess:\n      sess.run(global_step)\n\n    state = export_state_utils.get_export_saver_listener_state(export_dir_base)\n    # One is before_save, one is after_save.\n    self.assertEqual(len(state.entries), 1)\n    entry = state.entries[0]\n    self.assertEqual(entry.export_dir, export_dir)\n    self.assertEqual(entry.global_step, 10)\n\n  def testExporterReturnsDict(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                             \"testExporterReturnsDict\")\n    export_dir_base = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                                   \"testBasic_export_dir\")\n    exporter = mock.MagicMock()\n    export_dir1 = os.path.join(export_dir_base, \"model1/12345678\")\n    export_dir2 = os.path.join(export_dir_base, \"model2/12345678\")\n    os.makedirs(export_dir1)\n    os.makedirs(export_dir2)\n\n    def export_saved_model(serving_input_receiver_fn, checkpoint_file,\n                           global_step):\n      return {\n          \"model1\": export_dir1.encode(),\n          \"model2\": export_dir2.encode(),\n      }\n\n    exporter.export_saved_model.side_effect = export_saved_model\n\n    saver_hook = save_utils.NoFirstSaveCheckpointSaverHook(\n        model_dir,\n        save_steps=10000,\n        listeners=[\n            export_hooks.ExportSaverListener(model_dir + \"/model.ckpt\", None,\n                                             exporter)\n        ])\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    global_step = tf.compat.v1.assign(global_step, 10)\n    with tf.compat.v1.train.SingularMonitoredSession(\n        hooks=[saver_hook]) as sess:\n      sess.run(global_step)\n\n  def testDeleted(self):\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"testDeleted_model_dir\")\n    export_dir_base = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                                   \"testDeleted_export_dir\")\n    exporter = mock.MagicMock()\n\n    def export_saved_model(serving_input_receiver_fn, checkpoint_file,\n                           global_step):\n      export_dir = os.path.join(export_dir_base, str(time.time()))\n      os.makedirs(export_dir)\n      return export_dir.encode()\n\n    exporter.export_saved_model.side_effect = export_saved_model\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    global_step = tf.compat.v1.assign_add(global_step, 1)\n\n    saver = save_utils.PartialRecoverySaver(tf.compat.v1.global_variables(),\n                                            sharded=True,\n                                            max_to_keep=1,\n                                            keep_checkpoint_every_n_hours=2)\n    saver_hook = save_utils.NoFirstSaveCheckpointSaverHook(\n        model_dir,\n        save_steps=1,\n        saver=saver,\n        listeners=[\n            export_hooks.ExportSaverListener(model_dir + \"/model.ckpt\", None,\n                                             exporter)\n        ])\n\n    with tf.compat.v1.train.SingularMonitoredSession(\n        hooks=[saver_hook]) as sess:\n      sess.run(global_step)\n      sess.run(global_step)\n\n    state = export_state_utils.get_export_saver_listener_state(export_dir_base)\n    # Saved model for step 1 is deleted.\n    self.assertEqual(len(state.entries), 1)\n    entry = state.entries[0]\n    self.assertEqual(entry.global_step, 2)\n    self.assertEqual(len(tf.io.gfile.glob(export_dir_base + \"/*.*\")), 1)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/model_export/export_state_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nimport tensorflow as tf\nfrom google.protobuf import text_format\n\nfrom monolith.native_training.model_export import export_pb2\n\n_ExportSaverListenerStateFile = \"ExportSaverListenerState\"\n\n\ndef get_export_saver_listener_state(\n    export_dir_base: str) -> export_pb2.ServingModelState:\n  filename = os.path.join(export_dir_base, _ExportSaverListenerStateFile)\n  state = export_pb2.ServingModelState()\n  try:\n    with tf.io.gfile.GFile(filename) as f:\n      text = f.read()\n      text_format.Merge(text, state)\n  except tf.errors.NotFoundError:\n    pass\n  return state\n\n\ndef overwrite_export_saver_listener_state(export_dir_base: str,\n                                          state: export_pb2.ServingModelState):\n  filename = os.path.join(export_dir_base, _ExportSaverListenerStateFile)\n  tmp_name = filename + \"-tmp\"\n  tf.io.gfile.makedirs(export_dir_base)\n  with tf.io.gfile.GFile(tmp_name, mode=\"w\") as f:\n    text = text_format.MessageToString(state)\n    f.write(text)\n  tf.io.gfile.rename(tmp_name, filename, overwrite=True)\n"
  },
  {
    "path": "monolith/native_training/model_export/export_state_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport unittest\n\nfrom monolith.native_training.model_export import export_state_utils\nfrom monolith.native_training.model_export import export_pb2\n\n\nclass ExportStateUtilsTest(unittest.TestCase):\n\n  def test_basic(self):\n    state = export_pb2.ServingModelState()\n    entry = state.entries.add()\n    entry.export_dir = \"a\"\n    entry.global_step = 1\n    dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"basic\")\n    export_state_utils.overwrite_export_saver_listener_state(dir, state)\n    new_state = export_state_utils.get_export_saver_listener_state(dir)\n    self.assertEquals(new_state, state)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/model_export/export_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom typing import Callable\n\nimport tensorflow as tf\n\nfrom monolith.native_training import nested_tensors\nfrom monolith.native_training.model_export import export_context\nfrom monolith.native_training import distributed_serving_ops\n\nremote_predict = distributed_serving_ops.remote_predict\n\n\ndef _get_tensor_signature_name(t: tf.Tensor):\n  return t.name.replace(\":\", \"_\")\n\n\nclass RemotePredictHelper:\n\n  def __init__(self, name: str, input_tensors: object,\n               remote_func: Callable[[object], object]):\n    self._name = name\n    self._input_tensors = nested_tensors.NestedTensors(input_tensors)\n    self._remote_func = remote_func\n    self._define_remote_func()\n\n  def _define_remote_func(self):\n    \"\"\"Defines the remote func\"\"\"\n    self._func_defined = True\n    flat_input_tensors = self._input_tensors.get_tensors()\n    phs = []\n    for tensor in flat_input_tensors:\n      phs.append(\n          tf.compat.v1.placeholder(dtype=tensor.dtype,\n                                   shape=tensor.shape,\n                                   name=_get_tensor_signature_name(tensor) +\n                                   \"_remote_input_ph\"))\n    func_input = self._input_tensors.get_nested_result(phs)\n    func_output = self._remote_func(func_input)\n    self._output_tensors = nested_tensors.NestedTensors(func_output)\n    flat_output_tensors = self._output_tensors.get_tensors()\n    self._sig_input = {\n        _get_tensor_signature_name(t): ph\n        for t, ph in zip(flat_input_tensors, phs)\n    }\n    assert len(self._sig_input) == len(\n        flat_input_tensors), f\"Name conflicts: {flat_input_tensors}\"\n    self._sig_output = {\n        _get_tensor_signature_name(t): t for t in flat_output_tensors\n    }\n    assert len(self._sig_output) == len(\n        flat_output_tensors), f\"Name conflicts: {flat_input_tensors}\"\n\n    export_context.get_current_export_ctx().add_signature(\n        tf.compat.v1.get_default_graph(), self._name, self._sig_input,\n        self._sig_output)\n\n  def call_remote_predict(self,\n                          model_name: str,\n                          input_tensors: object = None,\n                          old_model_name: str = None,\n                          task: int = 0):\n    \"\"\"\n    Calls the remote function.\n\n    Args:\n    model_name - the remote model_name that will be used.\n    input_tensors - if None, will use tensors in the __init__\n    old_model_name & task - A deprecated args to support old remote predict\n    \"\"\"\n    flat_input_tensors = None\n    if input_tensors:\n      flat_input_tensors = nested_tensors.NestedTensors(\n          input_tensors).get_tensors()\n    else:\n      flat_input_tensors = self._input_tensors.get_tensors()\n    results = remote_predict(\n        list(self._sig_input.keys()),\n        flat_input_tensors,\n        list(self._sig_output.keys()),\n        model_name,\n        task,\n        old_model_name,\n        output_types=[t.dtype for t in self._sig_output.values()],\n        signature_name=self._name)\n    return self._output_tensors.get_nested_result(results)\n"
  },
  {
    "path": "monolith/native_training/model_export/export_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training.model_export import export_utils\nfrom monolith.native_training.model_export import export_context\n\n\nclass ExportUtilsTest(tf.test.TestCase):\n\n  def testBasic(self):\n    # Currently we only test gramar until we can figure out a way\n    # to compile tensorflow serving here.\n    with export_context.enter_export_mode(\n        export_context.EXPORT_MODE.STANDALONE):\n\n      def remote_func(d):\n        return d[\"a\"] * 3 + d[\"b\"] * 4\n\n      helper = export_utils.RemotePredictHelper(\"test_func\", {\n          \"a\": tf.constant(1),\n          \"b\": tf.constant(2)\n      }, remote_func)\n\n      result = helper.call_remote_predict(\"model_name\")\n      self.assertIsInstance(result, tf.Tensor)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/model_export/saved_model_exporters.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport abc\nimport os\nimport time\nimport contextlib\nfrom typing import Callable, Dict, List, Union\n\nfrom absl import logging\nfrom google.protobuf.any_pb2 import Any\nimport tensorflow as tf\nfrom tensorflow.core.protobuf import meta_graph_pb2\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.saved_model import constants\nfrom tensorflow.python.framework import graph_util\nfrom tensorflow.python.framework import importer\nfrom tensorflow.python.lib.io import file_io\nfrom tensorflow.python.saved_model.loader_impl import parse_saved_model\nfrom tensorflow_estimator.python.estimator.export import export_lib\n\nfrom monolith.native_training import device_utils\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import multi_hash_table_ops\nfrom monolith.native_training import save_utils\nfrom monolith.native_training.distribution_utils import update_session_config_for_gpu\nfrom monolith.native_training.model_export import export_context\nfrom monolith.native_training.monolith_checkpoint_state_pb2 import MonolithCheckpointState\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.model_export.data_gen_utils import gen_warmup_file\nfrom monolith.native_training.model_dump.dump_utils import DumpUtils\n\n\nclass BaseExporter(abc.ABC):\n\n  _ASSET_BASE = \"ASSET_BASE\"\n\n  def __init__(self,\n               model_fn: Callable,\n               model_dir: str,\n               export_dir_base: str,\n               shared_embedding=False,\n               warmup_file: str = None,\n               export_context_list: List = None):\n    \"\"\"\n    Args:    \n      model_fn - the model fn which should have (features, mode, config) as args\n      and return a EstimatorSpec\n      shared_embedding - instead of exporting a hermetic SavedModel, we will use the embedding\n      in the checkpoint instead of copying it.\n      warmup_file - the warmup file name.\n    \"\"\"\n    self._raw_model_fn = model_fn\n    self._model_dir = model_dir\n    self._export_dir_base = export_dir_base\n    self._shared_embedding = shared_embedding\n    self._warmup_file = warmup_file\n    self._export_context_list = export_context_list if export_context_list is not None else []\n\n  @staticmethod\n  def create_asset_base():\n    \"\"\"\n    This method returns a tensor which represents relative path of the assets folder.\n    For example:\n    We have a saved model here:\n    /tmp/${USER}/saved_models/${model_name}/1622840665\n    If we want to ref an asset with path:\n    /tmp/${USER}/saved_models/${model_name}/1622840665/assets/MonolithHashTable_1\n    We should use `tf.strings.join([create_asset_base(), \"MonolithHashTable_1\"])` as the asset path\n    \"\"\"\n\n    try:\n      return tf.compat.v1.get_default_graph().get_tensor_by_name(\n          BaseExporter._ASSET_BASE + \":0\")\n    except KeyError:\n      pass\n\n    asset_dir = \"./\"\n    asset_base = tf.convert_to_tensor(asset_dir,\n                                      dtype=tf.string,\n                                      name=BaseExporter._ASSET_BASE)\n    asset_proto = meta_graph_pb2.AssetFileDef()\n    asset_proto.filename = asset_dir\n    asset_proto.tensor_info.name = asset_base.name\n    asset_any_proto = Any()\n    asset_any_proto.Pack(asset_proto)\n    ops.add_to_collection(constants.ASSETS_KEY, asset_any_proto)\n    return asset_base\n\n  @staticmethod\n  def add_ckpt_to_assets(ckpt_to_export, pattern=\"*\"):\n    hash_table_ckpts = tf.io.gfile.glob(\n        save_utils.SaveHelper.get_ckpt_asset_dir(ckpt_to_export) + pattern)\n    for hash_table_ckpt in hash_table_ckpts:\n      logging.info(hash_table_ckpt)\n      ops.add_to_collection(tf.compat.v1.GraphKeys.ASSET_FILEPATHS,\n                            tf.convert_to_tensor(hash_table_ckpt))\n\n  @staticmethod\n  def build_signature(input_tensor_dict, output_tensor_dict):\n\n    def ensure_tensor_info(maybe_tensor_info):\n      if isinstance(maybe_tensor_info, meta_graph_pb2.TensorInfo):\n        return maybe_tensor_info\n      else:\n        return tf.compat.v1.saved_model.utils.build_tensor_info(\n            maybe_tensor_info)\n\n    return tf.compat.v1.saved_model.build_signature_def(\n        inputs={k: ensure_tensor_info(v) for k, v in input_tensor_dict.items()},\n        outputs={\n            k: ensure_tensor_info(v) for k, v in output_tensor_dict.items()\n        },\n        method_name=tf.compat.v1.saved_model.signature_constants.\n        PREDICT_METHOD_NAME)\n\n  def _get_tensor_name(self, name):\n    if name.startswith(\"^\"):\n      return name[1:]\n    else:\n      return name.split(\":\")[0]\n\n  def _freeze_dense_graph(self, graph_def, signature_def_map, session):\n    dest_nodes = []\n    for signature in signature_def_map.values():\n      for input_item in signature.inputs.values():\n        dest_nodes.append(self._get_tensor_name(input_item.name))\n      for output_item in signature.outputs.values():\n        dest_nodes.append(self._get_tensor_name(output_item.name))\n    logging.info(\"freeze output_node_names: {}\".format(dest_nodes))\n\n    # variable_names_whitelist = []\n    # for node in graph_def.node:\n    #   if node.op == 'VarHandleOp' and node.name.endswith('kernel'):\n    #     variable_names_whitelist.append(node.name)\n    # logging.info(\"freeze list: {}\".format(variable_names_whitelist))\n    variable_names_whitelist = None\n\n    node_device = {}\n    for node in graph_def.node:\n      node_device[node.name] = node.device\n\n    forzen_graph_def = graph_util.convert_variables_to_constants(\n        session, graph_def, dest_nodes,\n        variable_names_whitelist=variable_names_whitelist)\n\n    exists_node_names = set()\n    for node in forzen_graph_def.node:\n      exists_node_names.add(node.name)\n      if node.name in node_device:\n        node.device = node_device[node.name]\n\n    # for node in graph_def.node:\n    #   if len(node.input) == 0 and node.name not in exists_node_names:\n    #     forzen_graph_def.node.append(node)\n\n    return forzen_graph_def\n\n  def _export_saved_model_from_graph(\n      self,\n      graph: tf.Graph,\n      checkpoint_path: str,\n      export_dir_base: str = None,\n      export_dir: str = None,\n      restore_vars=True,\n      restore_hashtable=True,\n      assign_hashtable=True,\n      export_ctx: export_context.ExportContext = None,\n      export_tags=['serve'],\n      assets_extra=None,\n      clear_devices=False,\n      strip_default_attrs=True) -> bytes:\n    \"\"\"\n    Export saved_model from a user constructed graph, a graph can have multiple signatures\n    Signautres are stored in export_ctx.signatures\n    \"\"\"\n\n    assert export_dir or export_dir_base, \"must provide export_dir or export_dir_base\"\n\n    if export_ctx is None:\n      export_ctx = export_context.get_current_export_ctx()\n    assert export_ctx is not None\n\n    if not export_dir:\n      export_dir = export_lib.get_timestamped_export_dir(export_dir_base)\n    temp_export_dir = export_lib.get_temp_export_dir(export_dir)\n\n    builder = tf.compat.v1.saved_model.Builder(temp_export_dir)\n    with graph.as_default():\n      tf.compat.v1.train.get_or_create_global_step(graph)\n      signature_def_map = {}\n      # Add signatures collected in the export_context\n      for signature in export_ctx.signatures(graph):\n        signature_def_map[signature.name] = BaseExporter.build_signature(\n            signature.inputs, signature.outputs)\n\n      if assign_hashtable:\n        # assign signature\n        assign_inputs, assign_outputs = self.build_hashtable_assign_inputs_outputs(\n        )\n        signature_def_map[\"hashtable_assign\"] = BaseExporter.build_signature(\n            assign_inputs, assign_outputs)\n        self.add_multi_hashtable_assign_signatures(signature_def_map)\n      '''\n      To export CPU-trained saved_model for GPU serving, it requires explicit\n      GPU device placement at the exporting time. But it raised error here on\n      CPU-only machines while exporting graph with ops explicitly placed on GPU\n      (due to the necessity of loading the whole graph at runtime for calling save_op).\n      So we use the soft placement here, to avoid raising runtime exceptions,\n      but still successfully record the correct GPU placements to the saved_model.\n      '''\n      session_config = tf.compat.v1.ConfigProto(allow_soft_placement=True)\n      update_session_config_for_gpu(session_config)\n      with tf.compat.v1.Session(config=session_config) as session:\n\n        graph_saver = tf.compat.v1.train.Saver(sharded=True)\n        if restore_vars:\n          try:\n            graph_saver.restore(session, checkpoint_path)\n          except tf.errors.NotFoundError as e:\n            msg = ('Could not load all requested variables from checkpoint. '\n                   'Please make sure your model_fn does not expect variables '\n                   'that were not saved in the checkpoint.\\n\\n'\n                   'Encountered error with mode `{}` while restoring '\n                   'checkpoint from: `{}`. Full Traceback:\\n\\n{}').format(\n                       tf.estimator.ModeKeys.PREDICT, checkpoint_path, e)\n            raise ValueError(msg)\n        restore_op = None\n        if restore_hashtable:\n          restore_op = tf.group(\n              self.create_multi_hashtable_restore_ops(checkpoint_path) +\n              self.create_hashtable_restore_ops(checkpoint_path))\n        restore_op = restore_op or tf.no_op()\n        meta_graph_kwargs = dict(tags=export_tags,\n                                 signature_def_map=signature_def_map,\n                                 assets_collection=tf.compat.v1.get_collection(\n                                     tf.compat.v1.GraphKeys.ASSET_FILEPATHS),\n                                 clear_devices=clear_devices,\n                                 main_op=restore_op,\n                                 saver=graph_saver,\n                                 strip_default_attrs=strip_default_attrs)\n        builder.add_meta_graph_and_variables(session, **meta_graph_kwargs)\n      builder.save()\n\n      # Add the extra assets\n      if assets_extra:\n        assets_extra_path = os.path.join(tf.compat.as_bytes(temp_export_dir),\n                                         tf.compat.as_bytes('assets.extra'))\n        for dest_relative, source in assets_extra.items():\n          dest_absolute = os.path.join(tf.compat.as_bytes(assets_extra_path),\n                                       tf.compat.as_bytes(dest_relative))\n          dest_path = os.path.dirname(dest_absolute)\n          tf.compat.v1.gfile.MakeDirs(dest_path)\n          tf.compat.v1.gfile.Copy(source, dest_absolute)\n\n      tf.io.gfile.rename(temp_export_dir, export_dir)\n      return export_dir if isinstance(export_dir,\n                                      bytes) else export_dir.encode()\n\n  def _export_frozen_saved_model_from_graph(\n      self,\n      graph: tf.Graph,\n      checkpoint_path: str,\n      export_dir_base: str = None,\n      export_dir: str = None,\n      restore_vars=True,\n      export_ctx: export_context.ExportContext = None,\n      export_tags=['serve'],\n      assets_extra=None,\n      clear_devices=False,\n      strip_default_attrs=True) -> bytes:\n    \"\"\"\n    Export saved_model from a user constructed graph, a graph can have multiple signatures\n    Signautres are stored in export_ctx.signatures\n    \"\"\"\n\n    assert export_dir or export_dir_base, \"must provide export_dir or export_dir_base\"\n\n    if export_ctx is None:\n      export_ctx = export_context.get_current_export_ctx()\n    assert export_ctx is not None\n\n    if not export_dir:\n      export_dir = export_lib.get_timestamped_export_dir(export_dir_base)\n    temp_export_dir = export_lib.get_temp_export_dir(export_dir)\n\n    frozen_graph_def = None\n    with graph.as_default():\n      tf.compat.v1.train.get_or_create_global_step(graph)\n      signature_def_map = {}\n      # Add signatures collected in the export_context\n      for signature in export_ctx.signatures(graph):\n        signature_def_map[signature.name] = BaseExporter.build_signature(\n            signature.inputs, signature.outputs)\n\n      session_config = tf.compat.v1.ConfigProto(allow_soft_placement=True)\n      update_session_config_for_gpu(session_config)\n      with tf.compat.v1.Session(config=session_config) as session:\n        graph_saver = tf.compat.v1.train.Saver(sharded=True)\n        if restore_vars:\n          try:\n            graph_saver.restore(session, checkpoint_path)\n          except tf.errors.NotFoundError as e:\n            msg = ('Could not load all requested variables from checkpoint. '\n                   'Please make sure your model_fn does not expect variables '\n                   'that were not saved in the checkpoint.\\n\\n'\n                   'Encountered error with mode `{}` while restoring '\n                   'checkpoint from: `{}`. Full Traceback:\\n\\n{}').format(\n                       tf.estimator.ModeKeys.PREDICT, checkpoint_path, e)\n            raise ValueError(msg)\n        frozen_graph_def = self._freeze_dense_graph(graph.as_graph_def(),\n                                                    signature_def_map,\n                                                    session)\n\n    builder = tf.compat.v1.saved_model.Builder(temp_export_dir)\n    with tf.Graph().as_default() as final_graph:\n      tf.graph_util.import_graph_def(frozen_graph_def, name='')\n      tf.compat.v1.train.get_or_create_global_step(final_graph)\n\n      session_config = tf.compat.v1.ConfigProto(allow_soft_placement=True)\n      update_session_config_for_gpu(session_config)\n      with tf.compat.v1.Session(config=session_config) as session:\n\n        graph_saver = tf.compat.v1.train.Saver(sharded=True)\n        if restore_vars:\n          try:\n            graph_saver.restore(session, checkpoint_path)\n          except tf.errors.NotFoundError as e:\n            msg = ('Could not load all requested variables from checkpoint. '\n                   'Please make sure your model_fn does not expect variables '\n                   'that were not saved in the checkpoint.\\n\\n'\n                   'Encountered error with mode `{}` while restoring '\n                   'checkpoint from: `{}`. Full Traceback:\\n\\n{}').format(\n                       tf.estimator.ModeKeys.PREDICT, checkpoint_path, e)\n            raise ValueError(msg)\n\n        restore_op = tf.no_op()\n        meta_graph_kwargs = dict(tags=export_tags,\n                                 signature_def_map=signature_def_map,\n                                 assets_collection=tf.compat.v1.get_collection(\n                                     tf.compat.v1.GraphKeys.ASSET_FILEPATHS),\n                                 clear_devices=clear_devices,\n                                 main_op=restore_op,\n                                 saver=graph_saver,\n                                 strip_default_attrs=strip_default_attrs)\n        builder.add_meta_graph_and_variables(session, **meta_graph_kwargs)\n      builder.save()\n\n      # Add the extra assets\n      if assets_extra:\n        assets_extra_path = os.path.join(tf.compat.as_bytes(temp_export_dir),\n                                         tf.compat.as_bytes('assets.extra'))\n        for dest_relative, source in assets_extra.items():\n          dest_absolute = os.path.join(tf.compat.as_bytes(assets_extra_path),\n                                       tf.compat.as_bytes(dest_relative))\n          dest_path = os.path.dirname(dest_absolute)\n          tf.compat.v1.gfile.MakeDirs(dest_path)\n          tf.compat.v1.gfile.Copy(source, dest_absolute)\n\n      tf.io.gfile.rename(temp_export_dir, export_dir)\n      return export_dir if isinstance(export_dir,\n                                      bytes) else export_dir.encode()\n\n  def create_hashtable_restore_ops(self, checkpoint_path):\n    \"\"\"\n    Find all the hashtables in the current graph and create restore_op for them.\n    When shared_embedding is False, it adds the hashtable ckpt files into the assets folder\n    \"\"\"\n    ckpt_asset_base = save_utils.SaveHelper.get_ckpt_asset_dir(checkpoint_path)\n\n    restore_ops = []\n    for table in ops.get_collection(hash_table_ops._HASH_TABLE_GRAPH_KEY):\n      tensor_prefix = hash_table_ops._table_tensor_prefix(table)\n      share_embedding = self._shared_embedding if table.export_share_embedding is None else table.export_share_embedding\n      if not share_embedding:\n        BaseExporter.add_ckpt_to_assets(checkpoint_path,\n                                        pattern=tensor_prefix + \"*\")\n        asset_base = BaseExporter.create_asset_base()\n      else:\n        asset_base = ckpt_asset_base\n      table_prefix = tf.strings.join([asset_base, tensor_prefix])\n      table_prefix = tf.strings.join(\n          [asset_base, hash_table_ops._table_tensor_prefix(table)])\n      restore_ops.append(table.restore(table_prefix).as_op())\n    return restore_ops\n\n  def create_multi_hashtable_restore_ops(self, checkpoint_path):\n    \"\"\"\n    Find all the multi-hashtables in the current graph and create restore_op for them.\n    When shared_embedding is False, it adds the hashtable ckpt files into the assets folder\n    \"\"\"\n    ckpt_asset_base = save_utils.SaveHelper.get_ckpt_asset_dir(checkpoint_path)\n\n    restore_ops = []\n    for table in ops.get_collection(\n        multi_hash_table_ops._MULTI_HASH_TABLE_GRAPH_KEY):\n      if not self._shared_embedding:\n        BaseExporter.add_ckpt_to_assets(checkpoint_path,\n                                        pattern=table.shared_name + \"*\")\n        asset_base = BaseExporter.create_asset_base()\n      else:\n        asset_base = ckpt_asset_base\n      table_basename = tf.strings.join([asset_base, table.shared_name])\n      with tf.control_dependencies([table.initializer]):\n        restore_op = table.restore(basename=table_basename).as_op()\n      restore_ops.append(restore_op)\n    return restore_ops\n\n  def build_hashtable_assign_inputs_outputs(self):\n    \"\"\"\n    For all hashtables in the current graph, create assign tensors for them\n    \"\"\"\n    assign_input_tensors, assign_output_tensors = {}, {}\n    for table in ops.get_collection(hash_table_ops._HASH_TABLE_GRAPH_KEY):\n      assign_id = tf.compat.v1.placeholder(dtype=tf.int64, shape=(None,))\n      assign_value = tf.compat.v1.placeholder(dtype=tf.float32,\n                                              shape=(None, table.dim_size))\n      assign_input_tensors[table.name + \"_id\"] = assign_id\n      assign_input_tensors[table.name + \"_value\"] = assign_value\n      updated_table = table.assign(assign_id, assign_value)\n      with tf.control_dependencies(control_inputs=[updated_table.as_op()]):\n        # The size of id tensor is returned here as a dummy value\n        assign_output_tensors[table.name + \"_result\"] = tf.size(assign_id)\n    return assign_input_tensors, assign_output_tensors\n\n  def add_multi_hashtable_assign_signatures(self, signature_def_map: Dict):\n    \"\"\"\n    For all hashtables in the current graph, create assign tensors for them\n    \"\"\"\n\n    for table in ops.get_collection(\n        multi_hash_table_ops._MULTI_HASH_TABLE_GRAPH_KEY):\n      name = table.shared_name + \"/raw_assign\"\n      assert name not in signature_def_map, f\"{name} has already been defined in signature\"\n      input_tensors, output_tensors = dict(), dict()\n      id = tf.compat.v1.placeholder(dtype=tf.int64, shape=(None,))\n      input_tensors[\"id\"] = id\n      id_split = tf.compat.v1.placeholder(dtype=tf.int64, shape=(None,))\n      input_tensors[\"id_split\"] = id_split\n      flat_value = tf.compat.v1.placeholder(dtype=tf.float32, shape=(None,))\n      input_tensors[\"flat_value\"] = flat_value\n      assign_op = table.raw_assign(\n          tf.RaggedTensor.from_row_splits(id, id_split), flat_value).as_op()\n      with tf.control_dependencies([assign_op]):\n        dummy_tensor = tf.constant(0)\n      output_tensors[\"result\"] = dummy_tensor\n      signature_def_map[name] = self.build_signature(input_tensors,\n                                                     output_tensors)\n\n  def _model_fn_with_input_reveiver(self, serving_input_receiver_fn):\n    input_receiver = serving_input_receiver_fn()\n    estimator_spec = self._raw_model_fn(input_receiver.features,\n                                        mode=tf.estimator.ModeKeys.PREDICT,\n                                        config=tf.estimator.RunConfig(\n                                            self._model_dir))\n    export_outputs = export_lib.get_export_outputs(\n        estimator_spec.export_outputs, estimator_spec.predictions)\n    signature_def_map = export_lib.build_all_signature_defs(\n        input_receiver.receiver_tensors,\n        export_outputs,\n        getattr(input_receiver, 'receiver_tensors_alternatives', None),\n        serving_only=True)\n    for signature_name, signature in signature_def_map.items():\n      export_context.get_current_export_ctx().add_signature(\n          tf.compat.v1.get_default_graph(), signature_name, signature.inputs,\n          signature.outputs)\n\n  @abc.abstractmethod\n  def export_saved_model(self,\n                         serving_input_receiver_fn,\n                         checkpoint_path=None,\n                         global_step=None) -> Union[bytes, Dict[str, bytes]]:\n    \"\"\"\n    Export the saved model and returns the path of exported model.\n    Args:\n      checkpoint_path - If None, the latest one will be used.\n    \"\"\"\n    pass\n\n  def gen_warmup_assets(self) -> Dict[str, str]:\n    if not self._warmup_file:\n      return None\n\n    if not tf.io.gfile.exists(self._warmup_file):\n      try:\n        flag = gen_warmup_file(self._warmup_file)\n        if flag is None:\n          return None\n        else:\n          return {'tf_serving_warmup_requests': self._warmup_file}\n      except Exception as e:\n        logging.error(str(e))\n        return None\n    else:\n      return {'tf_serving_warmup_requests': self._warmup_file}\n\n\n@monolith_export\nclass StandaloneExporter(BaseExporter):\n  \"\"\"单机模式的saved model导出器\n  \n  Args:\n    model_fn: 和tf.estimator兼容的model_fn, 以(features, mode, config)作为参数并且返回EstimatorSpec\n    model_dir: 保存checkpoint的目录\n    export_dir_base: 导出saved_model的目标路径\n    shared_embedding: 是否复用checkpoint中的 embedding 文件, \n                      False的话会将embedding文件拷贝至saved_model, 可能会降低导出速度\n    warmup_file: warmup文件, 参考 https://www.tensorflow.org/tfx/serving/saved_model_warmup\n  \n  \"\"\"\n\n  def __init__(self,\n               model_fn: Callable,\n               model_dir: str,\n               export_dir_base: str,\n               shared_embedding=False,\n               warmup_file: str = None,\n               export_context_list: List = None):\n    super(StandaloneExporter,\n          self).__init__(model_fn, model_dir, export_dir_base, shared_embedding,\n                         warmup_file, export_context_list)\n\n  def export_saved_model(self,\n                         serving_input_receiver_fn,\n                         checkpoint_path=None,\n                         global_step=None):\n    \"\"\" 导出saved_model\n    \n    Args:\n      serving_input_receiver_fn: \n        返回 tf.estimator.export.ServingInputReceiver 的函数, 用来将serving 请求映射到模型输入\n      checkpoint_path:\n        可选的checkpoint路径, 为空则使用tf.train.latest_checkpoint(self._model_dir)\n    \"\"\"\n    if not checkpoint_path:\n      checkpoint_path = tf.train.latest_checkpoint(self._model_dir)\n\n    with export_context.enter_export_mode(\n        export_context.ExportMode.STANDALONE\n    ) as export_ctx, contextlib.ExitStack() as stack:\n      other_contexts = [\n          stack.enter_context(ctx()) for ctx in self._export_context_list\n      ]\n      saved_tf_config = os.environ.pop(\"TF_CONFIG\", None)\n      try:\n        with tf.Graph().as_default() as g:\n          self._model_fn_with_input_reveiver(serving_input_receiver_fn)\n          return self._export_saved_model_from_graph(\n              g,\n              checkpoint_path=checkpoint_path,\n              export_dir_base=self._export_dir_base,\n              export_ctx=export_ctx,\n              assets_extra=self.gen_warmup_assets())\n      finally:\n        if saved_tf_config:\n          os.environ[\"TF_CONFIG\"] = saved_tf_config\n\n\n@monolith_export\nclass DistributedExporter(BaseExporter):\n  \"\"\"分布式模型导出器\n  \n  Args: \n    model_fn: 和tf.estimator兼容的model_fn, 以(features, mode, config)作为参数并且返回EstimatorSpec   \n    model_dir: 保存checkpoint的目录\n    export_dir_base: 导出saved_model的目标路径\n    shared_embedding: 是否复用checkpoint中的 embedding 文件, \n                      False的话会将embedding文件拷贝至saved_model, 可能会降低导出速度\n    warmup_file: warmup文件, 参考 https://www.tensorflow.org/tfx/serving/saved_model_warmup\n    include_graphs: Only export saved_models from include_graphs if the param not None, \n                    otherwise export all graphs in export context\n    global_step_as_timestamp: whether to use use global_step export folder name, \n                              useful when we do parallel export in sync_training\n  \n  \"\"\"\n\n  def __init__(self,\n               model_fn: Callable,\n               model_dir: str,\n               export_dir_base: str,\n               shared_embedding=False,\n               warmup_file: str = None,\n               export_context_list: List = None,\n               dense_only=False,\n               allow_gpu=False,\n               with_remote_gpu=False,\n               clear_entry_devices=False,\n               include_graphs: List[str] = None,\n               global_step_as_timestamp: bool = False,\n               freeze_variable: bool = True):\n    super(DistributedExporter,\n          self).__init__(model_fn, model_dir, export_dir_base, shared_embedding,\n                         warmup_file, export_context_list)\n    self._dense_only = dense_only\n    self._allow_gpu = allow_gpu\n    self._with_remote_gpu = with_remote_gpu\n    self._clear_entry_devices = clear_entry_devices\n    self._include_graphs = include_graphs\n    self._global_step_as_timestamp = global_step_as_timestamp\n    self._freeze_variable = freeze_variable\n\n  def _should_export(self, graph_name, export_dir):\n    if tf.io.gfile.exists(export_dir):\n      logging.info(\"skipping duplicated model exportings\")\n      return False\n    return self._include_graphs is None or graph_name in self._include_graphs\n\n  def export_saved_model(self,\n                         serving_input_receiver_fn,\n                         checkpoint_path=None,\n                         global_step=None):\n    \"\"\" 导出saved_model\n    \n    Args:\n      serving_input_receiver_fn: \n        返回 tf.estimator.export.ServingInputReceiver 的函数, 用来将serving 请求映射到模型输入\n      checkpoint_path:\n        可选的checkpoint路径, 为空则使用tf.train.latest_checkpoint(self._model_dir)\n    \"\"\"\n    if not checkpoint_path:\n      checkpoint_path = tf.train.latest_checkpoint(self._model_dir)\n\n    export_ctx = export_context.ExportContext(\n        with_remote_gpu=self._with_remote_gpu)\n\n    with export_context.enter_export_mode(\n        export_context.ExportMode.DISTRIBUTED,\n        export_ctx), contextlib.ExitStack() as stack:\n      other_contexts = [\n          stack.enter_context(ctx()) for ctx in self._export_context_list\n      ]\n\n      saved_tf_config = os.environ.pop(\"TF_CONFIG\", None)\n      result = {}\n      try:\n        # Run model fn and export entry part\n        if self._allow_gpu:\n          device_utils.enable_gpu_training()\n        with tf.Graph().as_default() as g, g.device(\n            device_utils.default_device_fn):\n          self._model_fn_with_input_reveiver(serving_input_receiver_fn)\n\n          if global_step and self._global_step_as_timestamp:\n            timestamp = str(global_step)\n            entry_export_dir = os.path.join(self._export_dir_base, \"entry\",\n                                            timestamp)\n          else:\n            entry_export_dir = export_lib.get_timestamped_export_dir(\n                os.path.join(self._export_dir_base, \"entry\")).decode()\n            timestamp = os.path.basename(entry_export_dir)\n\n          if self._should_export(\"entry\", entry_export_dir):\n            result[\"entry\"] = self._export_saved_model_from_graph(\n                g,\n                checkpoint_path=checkpoint_path,\n                export_dir=entry_export_dir,\n                export_ctx=export_ctx,\n                restore_hashtable=False,\n                assign_hashtable=False,\n                assets_extra=self.gen_warmup_assets(),\n                clear_devices=self._clear_entry_devices,\n            )\n\n        # Export additional dense graph stored in export_ctx\n        DumpUtils().restore_sub_model('dense')\n        for name, graph in export_ctx.dense_sub_graphs.items():\n          DumpUtils().add_sub_model('dense', name, graph)\n          ps_export_dir = os.path.join(\n              self._export_dir_base, name,\n              str(timestamp) + getattr(graph, \"export_suffix\", \"\"))\n          if not self._should_export(name, ps_export_dir):\n            continue\n          result[name] = self._export_saved_model_from_graph(\n              graph,\n              checkpoint_path=checkpoint_path,\n              export_dir=ps_export_dir,\n              export_ctx=export_ctx,\n          )\n\n        if self._dense_only:\n          return result\n\n        # Export PS from graph stored in export_ctx\n        DumpUtils().restore_sub_model('ps')\n        for name, graph in export_ctx.sub_graphs.items():\n          if name.startswith(\"dense\"):\n            continue\n          DumpUtils().add_sub_model('ps', name, graph)\n          ps_export_dir = os.path.join(\n              self._export_dir_base, name,\n              str(timestamp) + getattr(graph, \"export_suffix\", \"\"))\n          if not self._should_export(name, ps_export_dir):\n            continue\n          result[name] = self._export_saved_model_from_graph(\n              graph,\n              checkpoint_path=checkpoint_path,\n              export_dir=ps_export_dir,\n              export_ctx=export_ctx,\n          )\n\n          # Export GPU Dense from graph stored in export_ctx\n          for name, graph in export_ctx.sub_graphs.items():\n            if not name.startswith(\"dense\"):\n              continue\n            DumpUtils().add_sub_model('ps', name, graph)\n            ps_export_dir = os.path.join(\n                self._export_dir_base, name,\n                str(timestamp) + getattr(graph, \"export_suffix\", \"\"))\n            if not self._should_export(name, ps_export_dir):\n              continue\n            if not self._freeze_variable:\n              result[name] = self._export_saved_model_from_graph(\n                  graph,\n                  checkpoint_path=checkpoint_path,\n                  export_dir=ps_export_dir,\n                  export_ctx=export_ctx,\n                  restore_hashtable=False,\n                  assign_hashtable=False)\n            else:\n              result[name] = self._export_frozen_saved_model_from_graph(\n                  graph,\n                  checkpoint_path=checkpoint_path,\n                  export_dir=ps_export_dir,\n                  export_ctx=export_ctx)\n      finally:\n        if saved_tf_config:\n          os.environ[\"TF_CONFIG\"] = saved_tf_config\n    return result\n"
  },
  {
    "path": "monolith/native_training/model_export/saved_model_exporters_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nimport tensorflow as tf\n\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import test_utils\nfrom monolith.native_training import multi_hash_table_ops\nfrom monolith.native_training.model_export import export_context\nfrom monolith.native_training.model_export import saved_model_exporters\n\n\ndef input_fn():\n  return tf.data.Dataset.from_tensor_slices([1]).repeat()\n\n\nclass ModelFnCreator:\n\n  def __init__(self):\n    self._called_in_exported_mode = False\n\n  @property\n  def called_in_exported_mode(self):\n    return self._called_in_exported_mode\n\n  def create_model_fn(self):\n\n    def model_fn(features, mode, config):\n      if export_context.EXPORT_MODE != None:\n        self._called_in_exported_mode = True\n\n      table = hash_table_ops.test_hash_table(2)\n      mtable = multi_hash_table_ops.MultiHashTable.from_configs(\n          configs={\"test\": test_utils.generate_test_hash_table_config(1)})\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      if mode == tf.estimator.ModeKeys.PREDICT:\n        output_tensor = table.lookup([0])\n        output = tf.estimator.export.PredictOutput(output_tensor)\n        moutput = tf.estimator.export.PredictOutput(\n            mtable.lookup({\"test\": [0]})[\"test\"])\n        return tf.estimator.EstimatorSpec(\n            mode=mode,\n            predictions=output_tensor,\n            export_outputs={\n                tf.compat.v1.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:\n                    output,\n                \"table/lookup\":\n                    output,\n                \"mtable/lookup\":\n                    moutput,\n            })\n      add_op = table.assign_add([0], [[1, 2]]).as_op()\n      add_op2 = mtable.assign_add({\"test\": ([0], [[1]])}).as_op()\n      global_step = tf.compat.v1.assign_add(global_step, 1)\n      with tf.control_dependencies([global_step]):\n        print_op = tf.print(\"tensor value:\", table.lookup([0]))\n        print_op2 = tf.print(\"mtable tensor value: \",\n                             mtable.lookup({\"test\": [0]}))\n      ckpt_prefix = config.model_dir + \"/model.ckpt\"\n      return tf.estimator.EstimatorSpec(\n          mode=mode,\n          train_op=tf.group([global_step, add_op, add_op2, print_op,\n                             print_op2]),\n          training_hooks=[\n              tf.estimator.CheckpointSaverHook(\n                  config.model_dir,\n                  save_steps=1000,\n                  listeners=[\n                      hash_table_ops.HashTableCheckpointSaverListener(\n                          ckpt_prefix),\n                      multi_hash_table_ops.\n                      MultiHashTableCheckpointSaverListener(ckpt_prefix),\n                  ])\n          ],\n          loss=tf.constant(0.0))\n\n    return model_fn\n\n\ndef dummy_input_receiver_fn():\n  return tf.estimator.export.ServingInputReceiver({},\n                                                  tf.compat.v1.placeholder(\n                                                      tf.string))\n\n\nclass SavedModelExportersTest(tf.test.TestCase):\n\n  def setUp(self):\n    self._model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                                   self._testMethodName + \"_model_dir\")\n    self._export_dir_base = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                                         self._testMethodName + \"_export_dir\")\n\n  def run_pred(self,\n               export_path,\n               key=tf.compat.v1.saved_model.signature_constants.\n               DEFAULT_SERVING_SIGNATURE_DEF_KEY):\n    g = tf.Graph()\n    with g.as_default(), self.session() as sess:\n      imported = tf.compat.v1.saved_model.load(\n          sess, {tf.compat.v1.saved_model.tag_constants.SERVING}, export_path)\n      pred_name = imported.signature_def[key].outputs[\"output\"].name\n      pred = g.get_tensor_by_name(pred_name)\n      return sess.run(pred)\n\n  def testBasic(self):\n    creator = ModelFnCreator()\n    est = tf.estimator.Estimator(creator.create_model_fn(),\n                                 model_dir=self._model_dir)\n    # Train twice so we guarantee there are 2 ckpts.\n    est.train(input_fn, steps=1)\n    exporter = saved_model_exporters.StandaloneExporter(\n        creator.create_model_fn(), self._model_dir, self._export_dir_base)\n    export_path = exporter.export_saved_model(dummy_input_receiver_fn)\n    self.assertAllEqual(self.run_pred(export_path), [[1, 2]])\n    self.assertAllEqual(self.run_pred(export_path, \"mtable/lookup\"), [[1]])\n    self.assertTrue(creator.called_in_exported_mode)\n    # TODO(leqi.zou) : Add test case for checkpoint_path is not None\n\n  def testSharedEmebdding(self):\n    creator = ModelFnCreator()\n    est = tf.estimator.Estimator(creator.create_model_fn(),\n                                 model_dir=self._model_dir)\n    est.train(input_fn, steps=1)\n    exporter = saved_model_exporters.StandaloneExporter(\n        creator.create_model_fn(),\n        self._model_dir,\n        self._export_dir_base,\n        shared_embedding=True)\n    export_path = exporter.export_saved_model(dummy_input_receiver_fn)\n\n    self.assertAllEqual(self.run_pred(export_path), [[1, 2]])\n    self.assertAllEqual(self.run_pred(export_path, \"mtable/lookup\"), [[1]])\n\n  # TODO(leqi.zou): Add more tests for the distributed hash tables.\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/model_export/saved_model_visulizer.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Imports a protobuf model as a graph in Tensorboard.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport argparse\nimport sys\n\nimport tensorflow as tf\n\nfrom tensorflow.core.protobuf import saved_model_pb2\nfrom tensorflow.python.client import session\nfrom tensorflow.python.framework import importer\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.platform import app\nfrom tensorflow.python.platform import gfile\nfrom tensorflow.python.summary import summary\nfrom tensorflow.python.util import compat\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\nhash_table_ops = gen_monolith_ops\ngen_distribution_ops = gen_monolith_ops\n\n\ndef import_to_tensorboard(model_dir, log_dir):\n  \"\"\"View an imported protobuf model (`.pb` file) as a graph in Tensorboard.\n\n  Args:\n    model_dir: The location of the protobuf (`pb`) model to visualize\n    log_dir: The location for the Tensorboard log to begin visualization from.\n\n  Usage:\n    Call this function with your model location and desired log directory.\n    Launch Tensorboard by pointing it to the log directory.\n    View your imported `.pb` model as a graph.\n  \"\"\"\n  with session.Session(graph=ops.Graph()) as sess:\n    with gfile.FastGFile(model_dir, \"rb\") as f:\n      data = compat.as_bytes(f.read())\n      sm = saved_model_pb2.SavedModel()\n      sm.ParseFromString(data)\n      if 1 != len(sm.meta_graphs):\n        print('More than one graph found. Not sure which to write')\n        sys.exit(1)\n      importer.import_graph_def(sm.meta_graphs[0].graph_def)\n\n    pb_visual_writer = summary.FileWriter(log_dir)\n    pb_visual_writer.add_graph(sess.graph)\n    print(\"Model Imported. Visualize by running: \"\n          \"tensorboard --logdir={} --bind_all\".format(log_dir))\n\n\ndef main(unused_args):\n  import_to_tensorboard(FLAGS.model_dir, FLAGS.log_dir)\n\n\nif __name__ == \"__main__\":\n  parser = argparse.ArgumentParser()\n  parser.register(\"type\", \"bool\", lambda v: v.lower() == \"true\")\n  parser.add_argument(\n      \"--model_dir\",\n      type=str,\n      default=\"\",\n      required=True,\n      help=\"The location of the protobuf (\\'pb\\') model to visualize.\")\n  parser.add_argument(\n      \"--log_dir\",\n      type=str,\n      default=\"\",\n      required=True,\n      help=\"The location for the Tensorboard log to begin visualization from.\")\n  FLAGS, unparsed = parser.parse_known_args()\n  app.run(main=main, argv=[sys.argv[0]] + unparsed)\n"
  },
  {
    "path": "monolith/native_training/model_export/testdata/BUILD",
    "content": "exports_files([\"saved_model\"])\n"
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_0-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_3-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_0/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_0-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_3-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_1/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_0-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_3-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_2/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_0-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_3-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_3/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_0-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_3-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_0b9721ec6fc5396c38499b5be394b722_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_3-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_3fc25c64637605aa3983374cc61db982_4-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_0-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_1-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_e3997af6324e55640d4611001fa3a15b_4-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_0-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_1-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_2-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00002-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_3-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00000-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00001-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/testdata/saved_model/ps_4/1622716114/assets/MonolithHashTable_f6962510869b682fd764009be0e4e9c3_4-00003-of-00004",
    "content": ""
  },
  {
    "path": "monolith/native_training/model_export/warmup_data_decoder.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport re\nimport tensorflow as tf\n\nfrom tensorflow_serving.apis import predict_pb2\nfrom tensorflow_serving.apis import prediction_log_pb2\nfrom monolith.native_training import env_utils\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_string(\"file_name\", None, \"input file name\")\n\n\ndef main(_):\n  try:\n    env_utils.setup_hdfs_env()\n  except:\n    pass\n  tf.compat.v1.enable_eager_execution()\n  tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.INFO)\n  logging.set_verbosity(logging.INFO)\n\n  def decode_fn(record_bytes):\n    log = prediction_log_pb2.PredictionLog()\n    log.ParseFromString(record_bytes)\n    return log\n\n  for i, batch in enumerate(tf.data.TFRecordDataset([FLAGS.file_name])):\n    prediction_log = decode_fn(batch.numpy())\n    predict_log = prediction_log.predict_log\n    request = predict_log.request\n    simple_request_string = re.sub('string_val:.*', 'string_val: ...',\n                                   str(request))\n    logging.info('%dth model_spec:\\n%s', i, simple_request_string)\n\n\nif __name__ == \"__main__\":\n  app.run(main)\n"
  },
  {
    "path": "monolith/native_training/model_export/warmup_data_gen.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\nimport sys\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport tensorflow as tf\n\nfrom tensorflow_serving.apis import predict_pb2\nfrom tensorflow_serving.apis import prediction_log_pb2\nfrom monolith.native_training import env_utils\nfrom monolith.native_training.model_export.data_gen_utils import gen_prediction_log\nfrom monolith.native_training.data.feature_list import FeatureList\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_string(\"file_name\", None, \"input file name\")\nflags.DEFINE_integer(\"batch_size\", 256, \"Batch size of prediction request.\")\nflags.DEFINE_bool(\"lagrangex_header\", False, \"kafka_dump_prefix\")\nflags.DEFINE_bool(\"kafka_dump_prefix\", False, \"kafka_dump_prefix\")\nflags.DEFINE_bool(\"has_sort_id\", True, \"has_sort_id\")\nflags.DEFINE_bool(\"kafka_dump\", False, \"kafka_dump\")\nflags.DEFINE_integer(\"max_records\", 1000, \"Maximum number of warmup records.\")\nflags.DEFINE_string(\"model_name\", \"default\", \"mode name\")\nflags.DEFINE_string(\"signature_names\", \"serving_default\", \"signature names\")\nflags.DEFINE_string(\"output_path\", \"/tmp/tf_warmup_data\",\n                    \"output path of warmup data.\")\nflags.DEFINE_enum(\"variant_type\", \"instance\",\n                  ['instance', 'example', 'example_batch'], \"variant_type\")\nflags.DEFINE_string(\"sparse_features\", None, \"sparse_features\")\nflags.DEFINE_string(\"dense_features\", None, \"dense_features\")\nflags.DEFINE_integer(\"dense_feature_shapes\", None, \"dense_feature_shapes\")\nflags.DEFINE_integer(\"dense_feature_types\", None, \"dense_feature_types\")\nflags.DEFINE_string(\"extra_features\", None, \"extra_features\")\nflags.DEFINE_integer(\"extra_feature_shapes\", None, \"extra_feature_shapes\")\nflags.DEFINE_string(\"feature_list\", None, \"feature_list\")\nflags.DEFINE_enum(\"gen_type\", \"file\", ['file', 'random'], \"gen_type\")\nflags.DEFINE_integer(\"drop_rate\", 0, \"drop_rate\")\n\n\nclass PBReader(object):\n\n  def __init__(self,\n               file_name: str,\n               batch_size: int,\n               lagrangex_header: bool = False,\n               has_sort_id: bool = False,\n               kafka_dump_prefix: bool = False,\n               kafka_dump: bool = False,\n               variant_type: str = 'instance'):\n    self.file_name = file_name\n    assert batch_size > 0\n    self.batch_size = batch_size\n\n    if self.file_name is None or len(self.file_name) == 0:\n      self._stream = sys.stdin.buffer\n    else:\n      self._stream = tf.io.gfile.GFile(self.file_name)\n\n    self.lagrangex_header = lagrangex_header\n    self.has_sort_id = has_sort_id\n    self.kafka_dump_prefix = kafka_dump_prefix\n    self.kafka_dump = kafka_dump\n    self.variant_type = variant_type\n\n    self._curr = 0\n    self._max_iter = None\n\n  def __iter__(self):\n    return self\n\n  def __next__(self):\n    try:\n      self._curr += 1\n      if self._max_iter is not None and self._curr > self._max_iter:\n        raise StopIteration\n\n      pb_items = []\n      if self.variant_type == 'example_batch':\n        # example_batch\n        self._read_header()\n        bin_string = self._stream.read(self._read_size())\n        pb_items.append(bin_string)\n      else:\n        # example/instance\n        for _ in range(self.batch_size):\n          self._read_header()\n          bin_string = self._stream.read(self._read_size())\n          pb_items.append(bin_string)\n\n      return tf.make_tensor_proto(pb_items)\n    except:\n      if self.file_name:\n        self._stream.close()\n\n      raise StopIteration\n\n  def _read_size(self) -> int:\n    size_t = 8\n    try:\n      size_binary = self._stream.read(size_t)\n      if len(size_binary) != size_t:\n        raise EOFError\n    except Exception as e:\n      raise e\n\n    return int.from_bytes(size_binary, byteorder=\"little\")\n\n  def _read_header(self):\n    size, aggregate_page_sortid_size = 0, 0\n    if self.lagrangex_header:\n      size = self._read_size()\n    else:\n      if self.kafka_dump_prefix:\n        size = self._read_size()\n        if size == 0:\n          size = self._read_size()\n        else:\n          aggregate_page_sortid_size = size\n\n      if self.has_sort_id:\n        if aggregate_page_sortid_size == 0:\n          size = self._read_size()\n        else:\n          size = aggregate_page_sortid_size\n        sort_id = self._stream.read(size)\n\n      if self.kafka_dump:\n        size = self._read_size()\n\n  def set_max_iter(self, max_records):\n    if self.variant_type == 'example_batch':\n      assert self.batch_size < max_records\n      self._max_iter = (max_records // self.batch_size)\n    else:\n      self._max_iter = max_records\n\n\ndef gen_prediction_log_from_file(file_name: str = None,\n                                 batch_size: int = 64,\n                                 lagrangex_header: bool = False,\n                                 kafka_dump_prefix=False,\n                                 has_sort_id=True,\n                                 kafka_dump=False,\n                                 max_records=1000,\n                                 variant_type: str = 'instance'):\n  assert variant_type in {'instance', 'example', 'example_batch'}\n  if variant_type == 'instance':\n    input_name = 'instances'\n  elif variant_type == 'example':\n    input_name = 'examples'\n  else:\n    assert lagrangex_header == True\n    input_name = 'example_batch'\n\n  reader = PBReader(file_name, batch_size, lagrangex_header, has_sort_id,\n                    kafka_dump_prefix, kafka_dump, variant_type)\n  reader.set_max_iter(max_records)\n  signature_names = [name.strip() for name in FLAGS.signature_names.split(',')]\n  if 'serving_default' not in signature_names:\n    signature_names.append('serving_default')\n\n  for i, batch in enumerate(reader):\n    request = predict_pb2.PredictRequest()\n    request.model_spec.name = FLAGS.model_name\n    request.model_spec.signature_name = signature_names[i %\n                                                        len(signature_names)]\n    request.inputs[input_name].CopyFrom(batch)\n    log = prediction_log_pb2.PredictionLog(\n        predict_log=prediction_log_pb2.PredictLog(request=request))\n    yield log\n\n\ndef tf_dtype(dtype: str) -> tf.compat.v1.dtypes.DType:\n  if dtype in {'int', 'int32', 'short', 'uint', 'uint32', '3', '22'}:\n    return tf.int32\n  elif dtype in {'int64', 'long', 'uint64', '9', '23'}:\n    return tf.int46\n  elif dtype in {'float', 'float32', '1'}:\n    return tf.float32\n  elif dtype in {'float64', 'double', '2'}:\n    return tf.float64\n  elif dtype in {'bool', 'boolean', '10'}:\n    return tf.bool\n  elif dtype in {'str', 'string', 'char', '7'}:\n    return tf.string\n  else:\n    raise Exception(f'{dtype} error')\n\n\ndef main(_):\n  env_utils.setup_hdfs_env()\n  with tf.io.TFRecordWriter(FLAGS.output_path) as writer:\n    if FLAGS.gen_type == 'file':\n      for log in gen_prediction_log_from_file(\n          FLAGS.file_name, FLAGS.batch_size, FLAGS.lagrangex_header,\n          FLAGS.kafka_dump_prefix, FLAGS.has_sort_id, FLAGS.kafka_dump,\n          FLAGS.max_records, FLAGS.variant_type):\n        writer.write(log.SerializeToString())\n    else:\n      assert FLAGS.sparse_features is not None\n      sparse_features = FLAGS.sparse_features.split(',')\n\n      if FLAGS.dense_features is not None:\n        dense_features = FLAGS.dense_features.split(',')\n        dense_feature_shapes = [\n            int(shape) for shape in FLAGS.dense_feature_shapes.split(',')\n        ]\n        dense_feature_types = [\n            tf_dtype(dtype) for dtype in FLAGS.dense_feature_types.split(',')\n        ]\n      else:\n        dense_features = FLAGS.dense_features\n        dense_feature_shapes = FLAGS.dense_feature_shapes\n        dense_feature_types = FLAGS.dense_feature_types\n\n      if FLAGS.extra_features is not None:\n        extra_features = FLAGS.extra_features.split(',')\n        extra_feature_shapes = [\n            int(shape) for shape in FLAGS.extra_feature_shapes.split(',')\n        ]\n      else:\n        extra_features = FLAGS.extra_features\n        extra_feature_shapes = FLAGS.extra_feature_shapes\n\n      feature_list = FeatureList.parse(FLAGS.feature_list)\n      for log in gen_prediction_log(FLAGS.model_name, sparse_features,\n                                    dense_features, dense_feature_shapes,\n                                    dense_feature_types, extra_features,\n                                    extra_feature_shapes, feature_list,\n                                    FLAGS.batch_size, FLAGS.max_records, None,\n                                    FLAGS.variant_type, FLAGS.drop_rate):\n        writer.write(log.SerializeToString())\n\n\nif __name__ == \"__main__\":\n  logging.set_verbosity(logging.INFO)\n  app.run(main)\n"
  },
  {
    "path": "monolith/native_training/model_export/warmup_example_batch.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport sys\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\n\nimport tensorflow as tf\n\nfrom tensorflow.python.util import compat\n\nfrom tensorflow_serving.apis import predict_pb2\nfrom tensorflow_serving.apis import prediction_log_pb2\nfrom monolith.native_training import env_utils\n\nflags.DEFINE_string('input_folder', '', '')\nflags.DEFINE_string('output_path', '', '')\n\nFLAGS = flags.FLAGS\n\n\ndef gen_prediction_log(input_folder):\n  filenames = tf.io.gfile.listdir(input_folder)\n  for filename in filenames:\n    with tf.io.gfile.GFile(os.path.join(input_folder, filename), 'rb') as f:\n      request = predict_pb2.PredictRequest()\n      print(request.ParseFromString(compat.as_bytes(f.read())))\n      request.model_spec.name = \"default\"\n      request.model_spec.signature_name = \"serving_default\"\n      log = prediction_log_pb2.PredictionLog(\n          predict_log=prediction_log_pb2.PredictLog(request=request))\n      yield log\n\n\ndef main(_):\n  with tf.io.TFRecordWriter(FLAGS.output_path) as writer:\n    for log in gen_prediction_log(FLAGS.input_folder):\n      writer.write(log.SerializeToString())\n\n\nif __name__ == \"__main__\":\n  env_utils.setup_hdfs_env()\n  app.run(main)"
  },
  {
    "path": "monolith/native_training/monolith_checkpoint_state.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\n\npackage monolith.native_training;\n\n// Protocol buffer representing the monolith checkpoint state.\nmessage MonolithCheckpointState {\n  // Paths to all exempt(never-to-be-deleted) model checkpoints\n  repeated string exempt_model_checkpoint_paths = 1;\n  optional int64 last_checkpoint_save_timestamp = 2;\n  \n  enum HashTableType {\n    UNKNOWN = 0;\n    CUCKOO_HASH_MAP = 1;\n    MULTI_CUCKOO_HASH_MAP = 2;\n  }\n  optional HashTableType builtin_hash_table_type = 3;\n}\n"
  },
  {
    "path": "monolith/native_training/monolith_export.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\ndef monolith_export(obj):\n  \"\"\"A dummy decorator to hint this class/function should be exported.\"\"\"\n  obj.__monolith_doc = None\n  return obj\n"
  },
  {
    "path": "monolith/native_training/multi_hash_table_ops.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax=\"proto2\";\n\npackage monolith;\n\nmessage MultiHashTableProto {\n  repeated int32 dims = 1;\n  optional bytes  slot_expire_time_config = 2;\n  repeated string table_names = 3;\n  optional string learning_rate_tensor = 4;\n  optional string shared_name = 5;\n  optional int32 saver_parallel = 6;\n  optional string initializer_op = 7;\n  optional string handle_tensor = 8;\n}\n"
  },
  {
    "path": "monolith/native_training/multi_hash_table_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport abc\nimport copy\nimport concurrent.futures\nimport dataclasses\nimport hashlib\nimport os\nimport threading\nfrom typing import Tuple, Union, Dict, List, Iterable, NamedTuple\nimport collections\n\nfrom absl import logging\nfrom google.protobuf import text_format\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.ops import resources\n\nfrom monolith.native_training import basic_restore_hook\nfrom monolith.native_training import entry\nfrom monolith.native_training import hash_filter_ops\nfrom monolith.native_training.hash_table_utils import infer_dim_size\nfrom monolith.native_training.multi_type_hash_table import BaseMultiTypeHashTable\nfrom monolith.native_training import multi_hash_table_ops_pb2\nfrom monolith.native_training import distributed_serving_ops\nfrom monolith.native_training import graph_meta\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\nfrom monolith.native_training import save_utils\nfrom monolith.native_training.model_export.export_context import \\\n    is_exporting\nfrom monolith.native_training.proto import ckpt_info_pb2\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\nhash_table_ops = gen_monolith_ops\n\n_TIMEOUT_IN_MS = 60 * 60 * 1000\n_MULTI_HASH_TABLE_GRAPH_KEY = \"monolith_multi_hash_tables\"\n\n\nclass CachedConfig(NamedTuple):\n  \"\"\"Cache the config object to reduce the graph size.\"\"\"\n  # Original configs\n  configs: Dict[str, entry.HashTableConfigInstance]\n\n  # Generated data\n  # The table names\n  table_names: Tuple[str]\n  # The multi_hash_table serialized config.\n  mconfig: bytes\n  # table creation may request the data from other devices.\n  mconfig_tensor: tf.Tensor\n  # The dim size in each config to make tf function reused.\n  dims: Tuple[int]\n  # This is generated from mconfig\n  slot_expire_time_config: bytes\n\n\ndef infer_dims(configs: Dict[str, entry.HashTableConfigInstance]):\n  table_names = tuple(sorted(configs.keys()))\n  dims = []\n  for table_name in table_names:\n    config = configs[table_name]\n    table_config = config.table_config\n    dims.append(infer_dim_size(table_config))\n  return dims\n\n\ndef convert_to_cached_config(configs: Dict[str, entry.HashTableConfigInstance]):\n  mconfig = embedding_hash_table_pb2.MultiEmbeddingHashTableConfig()\n  table_names = tuple(sorted(configs.keys()))\n  slot_expire_time_config = None\n  dims = []\n  for table_name in table_names:\n    config = configs[table_name]\n    table_config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n    table_config.CopyFrom(config.table_config)\n    if is_exporting():\n      table_config.entry_config.entry_type = embedding_hash_table_pb2.EntryConfig.EntryType.SERVING\n    mconfig.names.append(table_name)\n    mconfig.configs.append(table_config)\n    dims.append(infer_dim_size(table_config))\n    if not slot_expire_time_config:\n      slot_expire_time_config = table_config.slot_expire_time_config.SerializeToString(\n      )\n  serialized_mconfig = mconfig.SerializeToString()\n  return CachedConfig(configs=configs,\n                      table_names=table_names,\n                      mconfig=serialized_mconfig,\n                      mconfig_tensor=tf.convert_to_tensor(serialized_mconfig),\n                      dims=tuple(dims),\n                      slot_expire_time_config=slot_expire_time_config)\n\n\n@dataclasses.dataclass\nclass MultiHashTableMetadata:\n  name_set: set = dataclasses.field(default_factory=set)\n\n\n# TODO(leqi.zou): Add tf.function when export is fixed\ndef concat_1d_tensors(*args) -> tf.RaggedTensor:\n  \"\"\"Concat 1D tensors into a Raaged Tensor \"\"\"\n  values = tf.concat(args, axis=0)\n  row_lengths = [tf.size(t) for t in args]\n  return tf.RaggedTensor.from_row_lengths(values, row_lengths, validate=False)\n\n\n# TODO(leqi.zou): Add tf.function when export is fixed\ndef get_list_from_flat_value(key: tf.RaggedTensor, dims: Tuple[int],\n                             flat_value: tf.Tensor) -> List[tf.Tensor]:\n  row_lengths = key.row_lengths()\n  value_lengths = row_lengths * dims\n  values = tf.split(flat_value, value_lengths)\n  for i in range(len(dims)):\n    values[i] = tf.reshape(values[i], [-1, dims[i]])\n  return values\n\n\n# TODO(leqi.zou): Add tf.function when export is fixed\ndef flatten_n_tensors(*args) -> tf.Tensor:\n  flattened_tensors = []\n  for tensor in args:\n    flattened_tensors.append(tf.reshape(tensor, shape=[-1]))\n  return tf.concat(flattened_tensors, axis=0)\n\n\nclass RawMultiTypeHashTable(abc.ABC):\n  \"\"\"Raw lookup API to minimize the overhead transferration between differene devices.\"\"\"\n\n  @abc.abstractmethod\n  def get_ragged_id(self, slot_to_id: Dict[str, tf.Tensor]) -> tf.RaggedTensor:\n    \"\"\"Converts ids to a single ragged id. Graph independent.\"\"\"\n    pass\n\n  @abc.abstractmethod\n  def get_flat_value(self, slot_to_value: Dict[str, tf.Tensor]) -> tf.Tensor:\n    \"\"\"Converts values to a single float tensor. Graph independent.\"\"\"\n    pass\n\n  @abc.abstractmethod\n  def get_embeddings(self, ragged_id: tf.RaggedTensor,\n                     value: tf.Tensor) -> Dict[str, tf.Tensor]:\n    \"\"\"Converts returned flat value into the dict of embeddings. Graph independent.\"\"\"\n    pass\n\n  @abc.abstractmethod\n  def raw_lookup(self, ragged_id: tf.RaggedTensor) -> tf.Tensor:\n    pass\n\n  @abc.abstractmethod\n  def raw_apply_gradients(self, ragged_id: tf.RaggedTensor,\n                          flat_grad: tf.Tensor, global_step: tf.Tensor, *args,\n                          **kwargs) -> \"RawMultiTypeHashTable\":\n    pass\n\n  @abc.abstractclassmethod\n  def raw_assign(self, ragged_id: tf.RaggedTensor, flat_value: tf.Tensor, *args,\n                 **kwargs):\n    pass\n\n\ndef _convert_to_int64(t):\n  if isinstance(t, tf.Tensor):\n    return t\n  return tf.convert_to_tensor(t, tf.int64)\n\n\ndef _convert_to_float32(t):\n  if isinstance(t, tf.Tensor):\n    return t\n  return tf.convert_to_tensor(t, tf.float32)\n\n\nclass MultiHashTable(BaseMultiTypeHashTable, RawMultiTypeHashTable):\n  \"\"\"\n  It maps a int64 to a float32 embedding.\n  \"\"\"\n  NAME_PREFIX = \"MonolithMultiHashTable\"\n\n  def __init__(self,\n               cc: CachedConfig = None,\n               hash_filter: tf.Tensor = None,\n               sync_client: tf.Tensor = None,\n               learning_rate_list: List[tf.Tensor] = None,\n               name_suffix: str = \"\",\n               saver_parallel: int = -1,\n               table_proto: multi_hash_table_ops_pb2.MultiHashTableProto = None,\n               import_scope: str = None,\n               device='/device:CPU:0'):\n    if table_proto is not None:\n      self._init_from_proto(table_proto, import_scope)\n      return\n    self._dims = cc.dims\n    self._slot_expire_time_config = cc.slot_expire_time_config\n    self._table_names = cc.table_names\n    self._learning_rate = tf.cast(tf.stack(learning_rate_list), tf.float32)\n    self._saver_parallel = saver_parallel\n    self._shared_name = \"_\".join([MultiHashTable.NAME_PREFIX, name_suffix])\n    self._check_and_insert_name(self._shared_name)\n    self._device = device\n\n    with tf.device(device):\n      # We separate the table creation and use by using a dummy var.\n      init_handle = hash_table_ops.create_monolith_multi_hash_table(\n          filter_handle=hash_filter,\n          sync_client_handle=sync_client,\n          config=cc.mconfig_tensor,\n          shared_name=self._shared_name)\n      self._initializer = init_handle.op\n      self._handle = hash_table_ops.read_monolith_multi_hash_table(\n          shared_name=self._shared_name)\n      self._is_initialized = hash_table_ops.is_hash_table_initialized(\n          init_handle)\n      resources.register_resource(init_handle, self._initializer,\n                                  self._is_initialized)\n    tf.compat.v1.get_collection_ref(_MULTI_HASH_TABLE_GRAPH_KEY).append(self)\n\n  def _init_from_proto(\n      self,\n      proto: multi_hash_table_ops_pb2.MultiHashTableProto = None,\n      import_scope: str = None):\n    assert isinstance(proto, multi_hash_table_ops_pb2.MultiHashTableProto)\n    g = tf.compat.v1.get_default_graph()\n    self._dims = tuple(proto.dims)\n    self._slot_expire_time_config = proto.slot_expire_time_config\n    self._table_names = tuple(proto.table_names)\n    self._learning_rate = g.as_graph_element(\n        ops.prepend_name_scope(proto.learning_rate_tensor, import_scope))\n    self._saver_parallel = proto.saver_parallel\n    self._shared_name = proto.shared_name\n    self._initializer = g.as_graph_element(\n        ops.prepend_name_scope(proto.initializer_op, import_scope))\n    self._handle = g.as_graph_element(\n        ops.prepend_name_scope(proto.handle_tensor, import_scope))\n    init_handle = self._initializer.outputs[0]\n    self._is_initialized = hash_table_ops.is_hash_table_initialized(init_handle)\n    resources.register_resource(init_handle, self._initializer,\n                                self._is_initialized)\n\n  @classmethod\n  def from_cached_config(cls,\n                         cc: CachedConfig,\n                         hash_filter: tf.Tensor = None,\n                         sync_client: tf.Tensor = None,\n                         name_suffix: str = \"\",\n                         saver_parallel: int = -1):\n    table_config = next(iter(cc.configs.values())).table_config\n    assert table_config.HasField(\"type\")\n    table_type = table_config.WhichOneof(\"type\")\n    logging.info(\"Hash table type: {}\".format(table_type))\n    use_gpu = table_type == \"gpucuco\"\n    d = \"/device:GPU:0\" if use_gpu else \"/device:CPU:0\"\n    with tf.device(d):\n      hash_filter = hash_filter if hash_filter is not None else hash_filter_ops.create_dummy_hash_filter(\n          name_suffix=name_suffix)\n      sync_client = sync_client if sync_client is not None else distributed_serving_ops.create_dummy_sync_client(\n      )\n\n    learning_rate_list = []\n\n    table_names = list(cc.configs.keys())\n    for table_name in table_names:\n      config = cc.configs[table_name]\n\n      if len(config.learning_rate_fns) != len(\n          config.table_config.entry_config.segments):\n        raise ValueError(\n            \"Size of learning_rate_fns and size of segments must be equal.\")\n      learning_rate_list.extend(config.call_learning_rate_fns_fewer_ops())\n\n    if tf.compat.v1.get_default_graph() != cc.mconfig_tensor.graph:\n      # In this case, we can't reuse mconfig_tensor\n      cc = cc._replace(mconfig_tensor=tf.convert_to_tensor(cc.mconfig))\n\n    return cls(cc=cc,\n               hash_filter=hash_filter,\n               sync_client=sync_client,\n               learning_rate_list=learning_rate_list,\n               name_suffix=name_suffix,\n               saver_parallel=saver_parallel,\n               device=d)\n\n  @classmethod\n  def from_configs(cls, configs: Dict[str, entry.HashTableConfigInstance],\n                   *args, **kwargs):\n    cc = convert_to_cached_config(configs)\n    return cls.from_cached_config(cc, *args, **kwargs)\n\n  @staticmethod\n  def from_proto(table_proto, import_scope=None):\n    return MultiHashTable(table_proto=table_proto, import_scope=import_scope)\n\n  def to_proto(self, export_scope=None):\n    if (export_scope is not None and\n        not self._handle.name.startswith(export_scope)):\n      return None\n    proto = multi_hash_table_ops_pb2.MultiHashTableProto()\n    proto.dims.extend(self._dims)\n    proto.slot_expire_time_config = self._slot_expire_time_config\n    proto.table_names.extend(self._table_names)\n    proto.learning_rate_tensor = ops.strip_name_scope(self._learning_rate.name,\n                                                      export_scope)\n    proto.saver_parallel = self._saver_parallel\n    proto.shared_name = self._shared_name\n    proto.initializer_op = ops.strip_name_scope(self._initializer.name,\n                                                export_scope)\n    proto.handle_tensor = ops.strip_name_scope(self._handle.name, export_scope)\n    return proto\n\n  @classmethod\n  def _check_and_insert_name(cls, name):\n    meta = graph_meta.get_meta(\"multi_hash_table_metadata\",\n                               MultiHashTableMetadata)\n    if name in meta.name_set:\n      raise ValueError(\"shared_name {} has already been used.\".format(name))\n    meta.name_set.add(name)\n\n  @property\n  def table_names(self):\n    \"\"\"Return table names.\"\"\"\n    return self._table_names\n\n  @property\n  def handle(self):\n    return self._handle\n\n  @property\n  def shared_name(self):\n    return self._shared_name\n\n  @property\n  def initializer(self):\n    return self._initializer\n\n  \"\"\"Implements BaseMultiHashTable\"\"\"\n\n  def assign(self,\n             slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]],\n             req_time: tf.Tensor = None,\n             enable_inter_table_parallelism: bool = False) -> \"MultiHashTable\":\n    ragged_id = self.get_ragged_id(\n        {k: _convert_to_int64(v[0]) for k, v in slot_to_id_and_value.items()})\n    flat_value = self.get_flat_value(\n        {k: _convert_to_float32(v[1]) for k, v in slot_to_id_and_value.items()})\n    return self.raw_assign(ragged_id, flat_value)\n\n  def assign_add(self,\n                 slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]],\n                 req_time: tf.Tensor = None) -> \"MultiHashTable\":\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    ragged_id = self.get_ragged_id(\n        {k: _convert_to_int64(v[0]) for k, v in slot_to_id_and_value.items()})\n    flat_value = self.get_flat_value(\n        {k: _convert_to_float32(v[1]) for k, v in slot_to_id_and_value.items()})\n    new_handle = hash_table_ops.monolith_multi_hash_table_assign_add(\n        mtable=self._handle,\n        id=ragged_id.values,\n        id_split=ragged_id.row_splits,\n        value=flat_value,\n        update_time=req_time)\n    return self._copy_with_new_table(new_handle)\n\n  def reinitialize(self, slot: str,\n                   ids: tf.Tensor) -> Tuple[\"MultiHashTable\", tf.Tensor]:\n    new_handle, status = hash_table_ops.monolith_multi_hash_table_reinitialize(\n        mtable=self._handle, table_name=slot, id=ids)\n    return self._copy_with_new_table(new_handle), status\n\n  def lookup(self, slot_to_id: Dict[str, tf.Tensor]) -> Dict[str, tf.Tensor]:\n    ragged_id = self.get_ragged_id(\n        {k: _convert_to_int64(v) for k, v in slot_to_id.items()})\n    flat_embedding = self.raw_lookup(ragged_id)\n    slot_to_embeddings = self.get_embeddings(ragged_id, flat_embedding)\n    slot_to_embeddings = {\n        k: v for k, v in slot_to_embeddings.items() if k in slot_to_id\n    }\n    return slot_to_embeddings\n\n  def lookup_entry(\n      self,\n      slot_to_id: Dict[str, tf.Tensor],\n      enable_inter_table_parallelism: bool = False) -> Dict[str, tf.Tensor]:\n    raise NotImplementedError(\"\")\n\n  def apply_gradients(self,\n                      slot_to_id_and_grad: Dict[str, Tuple[tf.Tensor,\n                                                           tf.Tensor]],\n                      global_step: tf.Tensor,\n                      req_time: tf.Tensor = None) -> \"MultiHashTable\":\n    ragged_id = self.get_ragged_id(\n        {k: _convert_to_int64(v[0]) for k, v in slot_to_id_and_grad.items()})\n    flat_grad = self.get_flat_value(\n        {k: _convert_to_float32(v[1]) for k, v in slot_to_id_and_grad.items()})\n    return self.raw_apply_gradients(ragged_id, flat_grad, global_step, req_time)\n\n  def save(self, basename: tf.Tensor) -> \"MultiHashTable\":\n    new_handle = hash_table_ops.monolith_multi_hash_table_save(\n        mtable=self._handle,\n        basename=basename,\n        nshards=self._saver_parallel,\n        slot_expire_time_config=self._slot_expire_time_config)\n    return self._copy_with_new_table(new_handle)\n\n  def restore(self, basename: tf.Tensor) -> \"MultiHashTable\":\n    new_handle = hash_table_ops.monolith_multi_hash_table_restore(\n        mtable=self._handle, basename=basename)\n    return self._copy_with_new_table(new_handle)\n\n  def as_op(self, *args, **kwargs):  # pylint: disable=unused-argument\n    return self._handle.op\n\n  def _copy_with_new_table(self, handle: tf.Tensor):\n    copied = copy.copy(self)\n    copied._handle = handle\n    return copied\n\n  def feature_stat(self, basename: tf.Tensor):\n    \"\"\"Only to be called after hash tables are saved.\"\"\"\n    features, counts = hash_table_ops.monolith_multi_hash_table_feature_stat(\n        basename)\n    return features, counts\n\n  \"\"\"\n  Fused ops for sync training.\n  \"\"\"\n\n  # This is a very concise API that supports fused lookup, without mapping the\n  # IDs to its slots.\n  def fused_lookup(self,\n                   ids: tf.Tensor,\n                   fused_slot_size: tf.Tensor,\n                   num_of_shards: int,\n                   req_time=None) -> Tuple[tf.Tensor]:\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    return hash_table_ops.monolith_multi_hash_table_fused_lookup(\n        mtable=self._handle,\n        ids=ids,\n        fused_slot_size=fused_slot_size,\n        num_of_shards=num_of_shards,\n        req_time=req_time)\n\n  # This is a very concise API that supports fused optimize, without mapping the\n  # IDs to its slots.\n  def fused_apply_gradient(\n      self,\n      ids: tf.Tensor,\n      indices: tf.Tensor,\n      fused_slot_size: tf.Tensor,\n      id_grads: tf.Tensor,\n      id_offsets: tf.Tensor,\n      grad_offsets: tf.Tensor,\n      global_step: tf.Tensor,\n      req_time: tf.Tensor,\n      num_of_shards: int,\n      enable_grad_accumulation: bool = False) -> \"MultiHashTable\":\n    handle = hash_table_ops.monolith_multi_hash_table_fused_optimize(\n        mtable=self._handle,\n        ids=ids,\n        indices=indices,\n        fused_slot_size=fused_slot_size,\n        id_grads=id_grads,\n        id_offsets=id_offsets,\n        grad_offsets=grad_offsets,\n        learning_rate_tensors=self._learning_rate,\n        req_time=req_time,\n        global_step=global_step,\n        num_of_shards=num_of_shards,\n        enable_grad_accumulation=enable_grad_accumulation)\n    return self._copy_with_new_table(handle)\n\n  def get_table_dim_sizes(self):\n    return self._dims\n\n  \"\"\"\n  RawMultiTypeHashTable APIs\n  \"\"\"\n\n  def raw_lookup(self, ragged_id: tf.RaggedTensor) -> tf.Tensor:\n    return hash_table_ops.monolith_multi_hash_table_lookup(\n        mtable=self._handle, id=ragged_id.values, id_split=ragged_id.row_splits)\n\n  def raw_apply_gradients(self,\n                          ragged_id: tf.RaggedTensor,\n                          flat_grad: tf.Tensor,\n                          global_step: tf.Tensor,\n                          req_time: tf.Tensor = None) -> RawMultiTypeHashTable:\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    return self._copy_with_new_table(\n        hash_table_ops.monolith_multi_hash_table_optimize(\n            mtable=self._handle,\n            id=ragged_id.values,\n            id_split=ragged_id.row_splits,\n            value=flat_grad,\n            learning_rate=self._learning_rate,\n            update_time=req_time,\n            global_step=global_step))\n\n  def raw_assign(self,\n                 ragged_id: tf.RaggedTensor,\n                 flat_value: tf.Tensor,\n                 req_time: tf.Tensor = None):\n    logging.info(f\"raw_assign {self._handle}\")\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    return self._copy_with_new_table(\n        hash_table_ops.monolith_multi_hash_table_assign(\n            mtable=self._handle,\n            id=ragged_id.values,\n            id_split=ragged_id.row_splits,\n            value=flat_value,\n            update_time=req_time))\n\n  def get_embeddings(self, ragged_id: tf.RaggedTensor,\n                     value: tf.Tensor) -> Dict[str, tf.Tensor]:\n    d = {}\n    values = get_list_from_flat_value(ragged_id, self._dims, value)\n    for name, value in zip(self._table_names, values):\n      d[name] = value\n    return d\n\n  def get_ragged_id(self, slot_to_id: Dict[str, tf.Tensor]):\n    tensors = []\n    empty_id = tf.constant([], dtype=tf.int64)\n    for name in self._table_names:\n      tensors.append(slot_to_id.get(name, empty_id))\n    return concat_1d_tensors(*tensors)\n\n  def get_flat_value(self, slot_to_value: Dict[str, tf.Tensor]):\n    tensors = []\n    empty_value = tf.zeros([0, 1])\n    for name in self._table_names:\n      tensors.append(slot_to_value.get(name, empty_value))\n    return flatten_n_tensors(*tensors)\n\n\nclass MultiHashTableCheckpointSaverListener(tf.estimator.CheckpointSaverListener\n                                           ):\n  \"\"\"Saves the hash tables when saver is run.\"\"\"\n\n  def __init__(self, basename: str, write_ckpt_info: bool = True):\n    \"\"\"|basename| should be a file name which is same as what is passed to saver.\"\"\"\n    super().__init__()\n    self._write_ckpt_info = write_ckpt_info\n    self._helper = save_utils.SaveHelper(basename)\n    self._table_id_to_placeholder = {}\n    self._features_counts_tuples = []\n    self._save_op = self._build_save_graph()\n\n  def before_save(self, sess, global_step_value):\n    \"\"\"\n    We use before save so the checkpoint file is updated after we successfully\n    save the hash table.\n    \"\"\"\n    logging.info(\"Starting saving MultiHashTables.\")\n    feed_dict = {}\n    base_dir = self._helper.get_ckpt_asset_dir(\n        self._helper.get_ckpt_prefix(global_step_value))\n    tf.io.gfile.makedirs(base_dir)\n    for table in tf.compat.v1.get_collection(_MULTI_HASH_TABLE_GRAPH_KEY):\n      table_basename = base_dir + table.shared_name\n      feed_dict.update(\n          {self._table_id_to_placeholder[id(table)]: table_basename})\n    sess.run(self._save_op,\n             feed_dict=feed_dict,\n             options=tf.compat.v1.RunOptions(timeout_in_ms=_TIMEOUT_IN_MS))\n    logging.info(\"Finished saving MultiHashTables.\")\n\n    if self._write_ckpt_info:\n      logging.info(\"Start collecting slot fid count.\")\n      features_counts_list = sess.run(fetches=self._features_counts_tuples,\n                                      feed_dict=feed_dict)\n      logging.info(\"Start writing CkptInfo.\")\n      feature_to_fid_count = collections.defaultdict(int)\n      for features_counts in features_counts_list:\n        features = features_counts[0].tolist()\n        counts = features_counts[1].tolist()\n        if not len(features) == len(counts):\n          raise ValueError(\n              \"Number of features [{}] does not match number of fid counts [{}]\"\n              .format(len(features), len(counts)))\n        for feature, count in zip(features, counts):\n          feature_to_fid_count[feature] += count\n\n      info = ckpt_info_pb2.CkptInfo()\n      for feature, count in feature_to_fid_count.items():\n        info.feature_counts[feature] = count\n      ckpt_dir = os.path.dirname(self._helper._basename)\n      with tf.io.gfile.GFile(\n          os.path.join(ckpt_dir, f\"ckpt.info-{global_step_value}\"), \"w\") as f:\n        f.write(str(info))\n      logging.info(\"Finished writing CkptInfo.\")\n\n  def _build_save_graph(self) -> tf.Operation:\n    save_ops = []\n    for table in ops.get_collection(_MULTI_HASH_TABLE_GRAPH_KEY):\n      table_basename = tf.compat.v1.placeholder(tf.string, shape=[])\n      self._table_id_to_placeholder.update({id(table): table_basename})\n      save_op = table.save(basename=table_basename).as_op()\n      save_ops.append(save_op)\n      if self._write_ckpt_info:\n        with tf.control_dependencies([save_op]):\n          self._features_counts_tuples.append(\n              table.feature_stat(table_basename))\n    with tf.control_dependencies(save_ops):\n      return tf.no_op(name=\"multi_hashtable_save_all\")\n\n\nclass MultiHashTableCheckpointRestorerListener(\n    basic_restore_hook.CheckpointRestorerListener):\n  \"\"\"Restores the hash tables from basename\"\"\"\n\n  def __init__(self, basename: str, ps_monitor=None):\n    super().__init__()\n    self._basename = basename\n    self._ps_monitor = ps_monitor\n\n    self._helper = save_utils.SaveHelper(basename)\n    self._table_id_to_placeholder = {}\n    self._restore_ops_per_device = self._build_restore_graph()\n\n  def before_restore(self, session):\n    \"\"\"\n    We use before restore so as to strictly control the order of restorer listeners.\n\n    \"\"\"\n    ckpt_prefix = tf.train.latest_checkpoint(os.path.dirname(self._basename))\n    if not ckpt_prefix:\n      logging.info(\"No checkpoint found in %s. Skip the hash tables restore.\",\n                   self._basename)\n      return\n\n    logging.info(\"Restore hash tables from %s.\", ckpt_prefix)\n    asset_dir = self._helper.get_ckpt_asset_dir(ckpt_prefix)\n    init_ops = []\n    feed_dict = {}\n    for mtable in tf.compat.v1.get_collection(_MULTI_HASH_TABLE_GRAPH_KEY):\n      init_ops.append(mtable.initializer)\n      table_basename = asset_dir + mtable.shared_name\n      feed_dict.update(\n          {self._table_id_to_placeholder[id(mtable)]: table_basename})\n\n    session.run(init_ops)\n    restore_ops_all = []\n    for device, restore_ops in self._restore_ops_per_device.items():\n      if not self._ps_monitor or self._ps_monitor.is_ps_uninitialized(\n          session, device):\n        restore_ops_all.extend(restore_ops)\n    session.run(restore_ops_all,\n                feed_dict=feed_dict,\n                options=tf.compat.v1.RunOptions(timeout_in_ms=_TIMEOUT_IN_MS))\n    logging.info(\"Finished restore.\")\n\n  def _build_restore_graph(self):\n    restore_ops_per_device = collections.defaultdict(list)\n    for table in ops.get_collection(_MULTI_HASH_TABLE_GRAPH_KEY):\n      table_basename = tf.compat.v1.placeholder(tf.string, shape=[])\n      self._table_id_to_placeholder.update({id(table): table_basename})\n      restore_op = table.restore(basename=table_basename).as_op()\n      restore_ops_per_device[table.handle.device].append(restore_op)\n    return restore_ops_per_device\n\n\nclass MultiHashTableRestorerSaverListener(tf.estimator.CheckpointSaverListener):\n  \"\"\"Since we use restore to remove stale entries,\n  we create a saver listener here.\"\"\"\n\n  def __init__(self, ckpt_prefix: str):\n    self._l = MultiHashTableCheckpointRestorerListener(ckpt_prefix)\n\n  def after_save(self, session, global_step_value):\n    self._l.before_restore(session)\n\n\nops.register_proto_function(\n    _MULTI_HASH_TABLE_GRAPH_KEY,\n    proto_type=multi_hash_table_ops_pb2.MultiHashTableProto,\n    to_proto=MultiHashTable.to_proto,\n    from_proto=MultiHashTable.from_proto)\n\nops.NotDifferentiable(\"IsHashTableInitialized\")\n"
  },
  {
    "path": "monolith/native_training/multi_hash_table_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl.testing import parameterized\nimport hashlib\nimport os\nfrom typing import Dict, List\nfrom unittest import mock\n\nimport tensorflow as tf\nimport tensorflow.python.ops.resources as resources\n\nfrom monolith.native_training import basic_restore_hook\nfrom monolith.native_training import entry\nfrom monolith.native_training import learning_rate_functions\nfrom monolith.native_training import multi_hash_table_ops\nfrom monolith.native_training import test_utils\n\n\ndef _id(x):\n  return tf.constant(x, dtype=tf.int64)\n\n\ndef _value(x):\n  return tf.constant(x, dtype=tf.float32)\n\n\ndef from_configs(configs, *args, **kwargs):\n  \"\"\"We do a serialization/deserialization to make sure it worked in all cases.\"\"\"\n  with tf.name_scope(\"scope\") as scope:\n    table = multi_hash_table_ops.MultiHashTable.from_configs(\n        configs, *args, **kwargs)\n    proto = table.to_proto(export_scope=scope)\n    table = multi_hash_table_ops.MultiHashTable.from_proto(proto,\n                                                           import_scope=scope)\n  return table\n\n\nclass MultiTypeHashTableTest(tf.test.TestCase, parameterized.TestCase):\n\n  def test_lookup_assign_add_reinitialize(self):\n\n    multi_table = multi_hash_table_ops.MultiHashTable.from_configs(\n        configs={\n            \"slot0\": test_utils.generate_test_hash_table_config(1),\n            \"not_used\": test_utils.generate_test_hash_table_config(2),\n            \"slot1\": test_utils.generate_test_hash_table_config(2),\n            \"slot2\": test_utils.generate_test_hash_table_config(2),\n        })\n    multi_table = multi_table.assign_add(\n        slot_to_id_and_value={\n            \"slot0\": (_id([0]), _value([[1]])),\n            \"slot1\": (_id([1]), _value([[2, 2]])),\n            \"slot2\": (_id([2, 3]), _value([[4, 4], [8, 8]]))\n        })\n    values_dict = multi_table.lookup(slot_to_id={\n        \"slot0\": _id([0]),\n        \"slot1\": _id([1]),\n        \"slot2\": _id([2, 3]),\n    })\n    with tf.compat.v1.train.SingularMonitoredSession() as sess:\n      values_dict = sess.run(values_dict)\n    expected_values_dict = {\n        \"slot0\": [[1]],\n        \"slot1\": [[2, 2]],\n        \"slot2\": [[4, 4], [8, 8]]\n    }\n    for slot, values in values_dict.items():\n      self.assertAllEqual(values, expected_values_dict[slot])\n\n    multi_table, status1 = multi_table.reinitialize(\"slot2\", _id([1, 2, 3]))\n    multi_table, status2 = multi_table.reinitialize(\"slot3\", _id([1, 2, 3]))\n    values_dict = multi_table.lookup(slot_to_id={\n        \"slot0\": _id([0]),\n        \"slot1\": _id([1]),\n        \"slot2\": _id([1, 2, 3]),\n    })\n    with tf.compat.v1.train.SingularMonitoredSession() as sess:\n      values_dict, status1, status2 = sess.run([values_dict, status1, status2])\n    expected_values_dict = {\n        \"slot0\": [[1]],\n        \"slot1\": [[2, 2]],\n        \"slot2\": [[0, 0], [0, 0], [0, 0]]\n    }\n    for slot, values in values_dict.items():\n      self.assertAllEqual(values, expected_values_dict[slot])\n    self.assertAllEqual(status1, [0, 1, 1])\n    self.assertAllEqual(status2, [-1, -1, -1])\n\n  def test_apply_gradients(self):\n    with self.session() as sess:\n      multi_table = from_configs(\n          configs={\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot1\": test_utils.generate_test_hash_table_config(2),\n          })\n      sess.run(multi_table.initializer)\n      values_dict = multi_table.lookup(slot_to_id={\n          \"slot0\": _id([0]),\n          \"slot1\": _id([1, 2]),\n      })\n      grads = [tf.constant(2.0), tf.constant([[1.0, 3.0], [2.0, 4.0]])]\n      global_step = tf.constant(0, dtype=tf.int64)\n      multi_table = multi_table.apply_gradients(slot_to_id_and_grad={\n          \"slot0\": (_id([0]), grads[0]),\n          \"slot1\": (_id([1, 2]), grads[1]),\n      },\n                                                global_step=global_step)\n      values_dict = multi_table.lookup(slot_to_id={\n          \"slot0\": _id([0]),\n          \"slot1\": _id([1, 2]),\n      })\n      values_dict = sess.run(values_dict)\n      expected_dict = {\"slot0\": [[-2]], \"slot1\": [[-1, -3], [-2, -4]]}\n      for key in expected_dict:\n        self.assertAllEqual(values_dict[key], expected_dict[key])\n\n  def test_save_restore(self):\n    with tf.Graph().as_default(), self.session() as sess:\n      table_0 = from_configs(\n          configs={\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot1\": test_utils.generate_test_hash_table_config(2),\n              \"slot2\": test_utils.generate_test_hash_table_config(2),\n          })\n      table_0 = table_0.assign_add(\n          slot_to_id_and_value={\n              \"slot0\": (_id([0, 1]), _value([[1], [2]])),\n              \"slot1\": (_id([2, 3, 4, 5]),\n                        _value([[1, 2], [2, 3], [3, 4], [4, 5]])),\n              \"slot2\": (_id([6, 7, 8, 9, 10]),\n                        _value([[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]))\n          })\n      basename = os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_save_restore\",\n                              table_0.shared_name)\n      table_0 = table_0.save(basename)\n      sess.run(table_0.initializer)\n      sess.run(table_0.as_op())\n\n    with tf.Graph().as_default(), self.session() as sess:\n      table_1 = from_configs(\n          configs={\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot2\": test_utils.generate_test_hash_table_config(2),\n              \"slot3\": test_utils.generate_test_hash_table_config(3),\n          })\n      table_1 = table_1.restore(basename)\n      values_dict = table_1.lookup(slot_to_id={\n          \"slot0\": _id([0, 1]),\n          \"slot2\": _id([6, 7, 8, 9, 10]),\n      })\n      sess.run(table_1.initializer)\n      values_dict = sess.run(values_dict)\n      expected_values_dict = {\n          \"slot0\": [[1], [2]],\n          \"slot2\": [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]\n      }\n      for slot, values in values_dict.items():\n        self.assertAllEqual(values, expected_values_dict[slot])\n\n  def test_save_restore_hook(self):\n    basename = os.path.join(os.environ[\"TEST_TMPDIR\"], \"test_save_restore_hook\",\n                            \"model.ckpt\")\n    table = from_configs(\n        configs={\n            \"slot0\": test_utils.generate_test_hash_table_config(1),\n            \"slot1\": test_utils.generate_test_hash_table_config(2),\n            \"slot2\": test_utils.generate_test_hash_table_config(2),\n        })\n    add_op = table.assign_add(\n        slot_to_id_and_value={\n            \"slot0\": (_id([0]), _value([[1]])),\n            \"slot1\": (_id([1]), _value([[2, 2]])),\n            \"slot2\": (_id([2, 3]), _value([[4, 4], [8, 8]]))\n        }).as_op()\n    sub_op = table.assign_add(\n        slot_to_id_and_value={\n            \"slot0\": (_id([0]), _value([[-1]])),\n            \"slot1\": (_id([1]), _value([[-2, -3]])),\n            \"slot2\": (_id([2, 3]), _value([[-4, -5], [-6, -7]]))\n        }).as_op()\n    values_dict = table.lookup(slot_to_id={\n        \"slot0\": _id([0]),\n        \"slot1\": _id([1]),\n        \"slot2\": _id([2, 3]),\n    })\n    saver_listener = multi_hash_table_ops.MultiHashTableCheckpointSaverListener(\n        basename)\n    # We need to create some variables to make saver happy.\n    tf.compat.v1.train.create_global_step()\n    saver = tf.compat.v1.train.Saver(tf.compat.v1.global_variables(),\n                                     sharded=True,\n                                     max_to_keep=10,\n                                     keep_checkpoint_every_n_hours=2)\n    saver_hook = tf.estimator.CheckpointSaverHook(os.path.dirname(basename),\n                                                  save_steps=1000,\n                                                  saver=saver,\n                                                  listeners=[saver_listener])\n    restorer_listener = multi_hash_table_ops.MultiHashTableCheckpointRestorerListener(\n        basename)\n    restore_hook = basic_restore_hook.CheckpointRestorerHook(\n        listeners=[restorer_listener])\n\n    with self.session() as sess:\n      saver_hook.begin()\n      sess.run(tf.compat.v1.global_variables_initializer())\n      resources.initialize_resources(resources.shared_resources()).run()\n      # In the estimator API, graph will be finalized before calling hook\n      g = tf.compat.v1.get_default_graph()\n      g.finalize()\n      sess.run(add_op)\n      saver_hook.after_create_session(sess, None)\n      sess.run(sub_op)\n      # restore will override sub_op\n      restore_hook.after_create_session(sess, None)\n      values_dict = sess.run(values_dict)\n      expected_values_dict = {\n          \"slot0\": [[1]],\n          \"slot1\": [[2, 2]],\n          \"slot2\": [[4, 4], [8, 8]]\n      }\n      for slot, values in values_dict.items():\n        self.assertAllEqual(values, expected_values_dict[slot])\n\n  def test_meta_graph_export(self):\n    multi_table = from_configs(\n        configs={\n            \"slot0\": test_utils.generate_test_hash_table_config(1),\n            \"slot1\": test_utils.generate_test_hash_table_config(2),\n        })\n    meta = tf.compat.v1.train.export_meta_graph()\n    self.assertIn(multi_hash_table_ops._MULTI_HASH_TABLE_GRAPH_KEY,\n                  meta.collection_def)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/multi_type_hash_table.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom __future__ import annotations\n\nimport abc\nfrom collections import defaultdict\nimport copy\nimport dataclasses\nimport hashlib\nimport itertools\nimport re\nfrom typing import Callable, Dict, Iterable, List, Tuple\n\nfrom absl import logging\nimport tensorflow as tf\n\nfrom monolith.native_training import device_utils\nfrom monolith.native_training import distribution_ops\nfrom monolith.native_training import entry\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import utils\nfrom monolith.native_training import prefetch_queue\nfrom monolith.native_training.hash_table_utils import infer_dim_size\n\n\nclass BaseMultiTypeHashTable(abc.ABC):\n\n  # https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations\n  # Allow nested instances.\n  _table: BaseMultiTypeHashTable\n  _tables: List[BaseMultiTypeHashTable]\n  # Allow pipelined graph execution.\n  _local_queue_hooks: List[prefetch_queue.EnqueueHook |\n                           prefetch_queue.AsyncPushHook]\n\n  @abc.abstractmethod\n  def lookup(self, slot_to_id: Dict[str, tf.Tensor]) -> Dict[str, tf.Tensor]:\n    pass\n\n  @abc.abstractmethod\n  def assign(\n      self, slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> BaseMultiTypeHashTable:\n    pass\n\n  @abc.abstractmethod\n  def assign_add(\n      self, slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> BaseMultiTypeHashTable:\n    pass\n\n  @abc.abstractmethod\n  def reinitialize(self, slot: str,\n                   ids: tf.Tensor) -> Tuple[BaseMultiTypeHashTable, tf.Tensor]:\n    pass\n\n  @abc.abstractmethod\n  def apply_gradients(self, slot_to_id_and_grad: Dict[str, Tuple[tf.Tensor,\n                                                                 tf.Tensor]],\n                      *args) -> BaseMultiTypeHashTable:\n    pass\n\n  @abc.abstractmethod\n  def as_op(self, name=None) -> tf.Operation:\n    pass\n\n  def add_queue_hook(self, hook):\n    # Allow pipelined graph execution.\n    if not getattr(self, \"_local_queue_hooks\", None):\n      self._local_queue_hooks = []\n    self._local_queue_hooks.append(hook)\n\n  def get_queue_hooks(self):\n    hooks = copy.copy(getattr(self, \"_local_queue_hooks\", []))\n    if getattr(self, \"_table\", None):\n      hooks.extend(self._table.get_queue_hooks())\n    if getattr(self, \"_tables\", None):\n      hooks.extend(\n          itertools.chain.from_iterable(\n              [t.get_queue_hooks() for t in self._tables]))\n    return hooks\n\n  @abc.abstractmethod\n  def get_table_dim_sizes(self) -> List[int]:\n    pass\n\n\n# TODO(leqi.zou): Makes this have a better name.\nclass MultiTypeHashTable(BaseMultiTypeHashTable):\n  \"\"\"\n  A hash tables that support different types of embeddings (they may have different dimensions/ optimizers). \n  Different types are distinguished by \"Slot\". Slot is the type of ids, the embeddings in the same\n  slot has the same dimension.\n\n  The functionality are same as BaseHashTable. The only difference is that now the input is a map, which\n  maps slot to ids/values.\n\n  hash_table_factory has two params: name_suffix & hash_table_config\n  \"\"\"\n\n  def __init__(\n      self, slot_to_config: Dict[str, entry.HashTableConfigInstance],\n      hash_table_factory: Callable[[str, entry.HashTableConfigInstance],\n                                   hash_table_ops.BaseHashTable]):\n    self._slot_to_config = slot_to_config\n    self._hash_tables = {}\n    self._hash_table_resources = []\n    learning_rate_tensors = []\n    for slot in sorted(self._slot_to_config.keys()):\n      # We need to keep the order here.\n      config = self._slot_to_config[slot]\n      self._hash_tables[slot] = hash_table_factory(slot, config)\n      # Here we setup the hashtable resources based on\n      # self._slot_to_config.keys()\n      self._hash_table_resources.append(self._hash_tables[slot].as_op())\n      learning_rate_tensors.append(config.call_learning_rate_fns())\n\n    # Build flattened learning rate tensor for fused apply gradient.\n    self._learning_rate_tensors = tf.concat(learning_rate_tensors, 0)\n\n  def lookup(self, slot_to_id: Dict[str, tf.Tensor]) -> Dict[str, tf.Tensor]:\n    slot_to_embedding = {}\n    for slot, id in slot_to_id.items():\n      embedding = self._hash_tables[slot].lookup(id)\n      slot_to_embedding[slot] = embedding\n    return slot_to_embedding\n\n  def assign(\n      self, slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> MultiTypeHashTable:\n    return self._update(\"assign\", slot_to_id_and_value)\n\n  def assign_add(\n      self, slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> MultiTypeHashTable:\n    return self._update(\"assign_add\", slot_to_id_and_value)\n\n  def reinitialize(self, slot: str,\n                   ids: tf.Tensor) -> Tuple[MultiTypeHashTable, tf.Tensor]:\n    raise NotImplementedError(\n        \"MultiTypeHashTable dost not support reinitialize!\")\n\n  def apply_gradients(\n      self,\n      slot_to_id_and_grad: Dict[str, Tuple[tf.Tensor, tf.Tensor]],\n      *args,\n      **kwargs,\n  ) -> MultiTypeHashTable:\n    return self._update(\"apply_gradients\", slot_to_id_and_grad, *args, **kwargs)\n\n  def _update(\n      self,\n      method_name: str,\n      slot_to_id_and_tensor: Dict[str, Tuple[tf.Tensor, tf.Tensor]],\n      *args,\n      **kwargs,\n  ) -> MultiTypeHashTable:\n    updated_tables = dict(self._hash_tables)\n    for slot, (id, tensor) in slot_to_id_and_tensor.items():\n      updated_tables[slot] = getattr(self._hash_tables[slot],\n                                     method_name)(id, tensor, *args, **kwargs)\n    return self._copy_with_new_tables(updated_tables)\n\n  def as_op(self, name=None) -> tf.Operation:\n    name = name or \"mtht_ao\"\n    with tf.control_dependencies(\n        [table.as_op() for table in self._hash_tables.values()]):\n      c = tf.no_op(name=(\"{}/done\".format(name)))\n    return c\n\n  def _copy_with_new_tables(\n      self, tables: Dict[int, tf.Tensor]) -> \"MultiTypeHashTable\":\n    copied = copy.copy(self)\n    # Update the hash_table_resources everytime when there is a table update.\n    hash_table_resources = []\n    for slot in sorted(self._slot_to_config.keys()):\n      hash_table_resources.append(tables[slot].as_op())\n    copied.__dict__[\"_hash_tables\"] = tables\n    copied.__dict__[\"_hash_table_resources\"] = hash_table_resources\n    return copied\n\n  # This is a very concise API that supports fused lookup, without mapping the\n  # IDs to its slots.\n  def fused_lookup(self,\n                   ids: tf.Tensor,\n                   fused_slot_size: tf.Tensor,\n                   num_of_shards: int,\n                   req_time=None) -> Tuple[tf.Tensor]:\n    if req_time is None:\n      req_time = tf.constant(0, dtype=tf.int64)\n    return hash_table_ops.fused_lookup(self._hash_table_resources, ids,\n                                       fused_slot_size, num_of_shards, req_time)\n\n  # This is a very concise API that supports fused optimize, without mapping the\n  # IDs to its slots.\n  def fused_apply_gradient(\n      self,\n      ids: tf.Tensor,\n      indices: tf.Tensor,\n      fused_slot_size: tf.Tensor,\n      id_grads: tf.Tensor,\n      id_offsets: tf.Tensor,\n      grad_offsets: tf.Tensor,\n      global_step: tf.Tensor,\n      req_time: tf.Tensor,\n      num_of_shards: int,\n      enable_grad_accumulation: bool = False) -> MultiTypeHashTable:\n    table_handles_output = hash_table_ops.fused_apply_gradient(\n        self._hash_table_resources, ids, indices, fused_slot_size, id_grads,\n        id_offsets, grad_offsets, self._learning_rate_tensors, req_time,\n        global_step, num_of_shards, enable_grad_accumulation)\n    copied = copy.copy(self)\n    updated_tables = dict(self._hash_tables)\n    for slot, handle in zip(sorted(self._slot_to_config.keys()),\n                            table_handles_output):\n      updated_tables[slot] = self._hash_tables[slot].table_update(handle)\n    copied.__dict__[\"_hash_tables\"] = updated_tables\n    copied.__dict__[\"_hash_table_resources\"] = table_handles_output\n    return copied\n\n  def get_table_dim_sizes(self) -> List[int]:\n    return [\n        self._hash_tables[slot].dim_size\n        for slot in sorted(self._slot_to_config.keys())\n    ]\n\n\n@dataclasses.dataclass\nclass _IndexedValues:\n  \"\"\"\n  _IndexedValues represents tensors merged from multiple slots.\n  slots are a list of string represents where values are coming from\n  indices are a tensor of 1-D int64 ranged in [0, len(slots)) represents the slot of that value is slots[index]\n  values are a tensor represents a list tensors which merged from multiple slots.\n  \"\"\"\n  slots: List[tf.Tensor]\n  index: tf.Tensor\n  value: tf.Tensor\n\n\nclass MergedMultiTypeHashTable(BaseMultiTypeHashTable):\n  \"\"\"A decorator that merge slots which have the same embedding config.\n  This helps reduce the size of graph. However, the caller need to make sure \n  ids in different slots are different.\"\"\"\n\n  def __init__(self, slot_to_config: Dict[str, entry.HashTableConfigInstance],\n               factory: Callable[[Dict[str, entry.HashTableConfigInstance]],\n                                 BaseMultiTypeHashTable]):\n    self._slot_to_config = slot_to_config\n    logging.info(\n        \"Create MergedMultiTypeHashTable: 1) reverse feature_name -> config into config -> feature_name_list\"\n    )\n    self._slot_mapping: Dict[str, str] = {}  # feature/slot -> merged_slot\n    deduped_config_to_slots = defaultdict(list)\n    for slot in sorted(slot_to_config):\n      config = slot_to_config[slot]\n      # Use str of config as the key for merging slots.\n      deduped_config_to_slots[str(config)].append(slot)\n\n    logging.info(\n        \"Create MergedMultiTypeHashTable: 2) gen merged_slot and map merged_slot -> config\"\n    )\n    merged_slot_to_config = {}\n    for key, slots in deduped_config_to_slots.items():\n\n      def get_merged_str(strs: List[str]):\n        concat = \",\".join(strs)\n        return concat, hashlib.md5(concat.encode()).hexdigest()\n\n      slots_str, merged_slot = get_merged_str(slots)\n      logging.info(\"Merged '{}' into '{}'\".format(slots_str, merged_slot))\n      merged_config = copy.copy(slot_to_config[slots[0]])\n      # replace \"fc_slot_*\" to \"slot_*\"\n      old_slots = [\n          slot[3:] if re.match(\"^fc_slot_[0-9]*$\", slot) else slot\n          for slot in slots\n      ]\n      _, old_merged_slot = get_merged_str(old_slots)\n      if old_merged_slot != merged_slot:\n        merged_config.extra_restore_names.append(old_merged_slot)\n\n      merged_slot_to_config[merged_slot] = merged_config\n      for slot in slots:\n        self._slot_mapping[slot] = merged_slot\n\n    self._merged_slot_to_config = merged_slot_to_config\n\n    logging.info(\n        f\"Create MergedMultiTypeHashTable: 3) sub hash tables {factory}\")\n    self._table = factory(merged_slot_to_config)\n\n  @property\n  def slot_mapping(self):\n    \"\"\"Returns slot mapping.\"\"\"\n    return self._slot_mapping\n\n  def lookup(self,\n             slot_to_id: Dict[str, tf.Tensor],\n             auxiliary_bundle=None,\n             early_reorder_indicies_res_pack=None):\n    if auxiliary_bundle is None:\n      auxiliary_bundle = {}\n\n    if early_reorder_indicies_res_pack:\n      merged_slot_to_sizes, res_pack = early_reorder_indicies_res_pack\n      merged_slot_to_id = {\n          k: None for k in merged_slot_to_sizes.keys()\n          # None to keep interface, we will only use the keys\n      }\n      auxiliary_bundle['merged_slot_to_sizes'] = merged_slot_to_sizes\n      merged_slot_to_embedding, auxiliary_bundle = self._table.lookup(\n          merged_slot_to_id, auxiliary_bundle, res_pack)\n    else:\n      logging.info(\n          \"Lookup MergedMultiTypeHashTable: 1) merged the features ids belong the same hash table\"\n      )\n      merged_slot_to_id, merged_slot_to_sizes = self._get_merged_to_indexed_tensor(\n          slot_to_id)\n      auxiliary_bundle['merged_slot_to_sizes'] = merged_slot_to_sizes\n      logging.info(\n          f\"Lookup MergedMultiTypeHashTable: 2) lookup sub hash table {self._table} for embeddings\"\n      )\n      merged_slot_to_embedding = self._table.lookup(merged_slot_to_id)\n\n    merged_slot_to_slots = defaultdict(list)\n    for k in sorted(self._slot_mapping.keys()):\n      if k in slot_to_id:\n        merged_slot_to_slots[self._slot_mapping[k]].append(k)\n\n    merged_slot_to_sizes = auxiliary_bundle.pop('merged_slot_to_sizes')\n    logging.info(\n        \"Lookup MergedMultiTypeHashTable: 3) split the lookuped embeddings into feature_name -> embedding\"\n    )\n    slot_to_embedding = {}\n    for merged_slot, emb in merged_slot_to_embedding.items():\n      sizes = merged_slot_to_sizes[merged_slot]\n      slots = merged_slot_to_slots[merged_slot]\n      with device_utils.maybe_device_if_allowed(\"/device:GPU:0\"):\n        embs = tf.split(emb, sizes, axis=0, num=len(slots))\n      for slot, emb in zip(slots, embs):\n        slot_to_embedding[slot] = emb\n\n    if auxiliary_bundle:\n      return slot_to_embedding, auxiliary_bundle\n    return slot_to_embedding\n\n  def assign(\n      self, slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> MergedMultiTypeHashTable:\n    return self._update(self._table.assign, slot_to_id_and_value)\n\n  def assign_add(\n      self, slot_to_id_and_value: Dict[str, Tuple[tf.Tensor, tf.Tensor]]\n  ) -> MergedMultiTypeHashTable:\n    return self._update(self._table.assign_add, slot_to_id_and_value)\n\n  def reinitialize(\n      self, slot: str,\n      ids: tf.Tensor) -> Tuple[MergedMultiTypeHashTable, tf.Tensor]:\n    raise NotImplementedError(\n        \"MergedMultiTypeHashTable dost not support reinitialize!\")\n\n  def apply_gradients(self, slot_to_id_and_grad: Dict[str, Tuple[tf.Tensor,\n                                                                 tf.Tensor]],\n                      *args, **kwargs) -> MergedMultiTypeHashTable:\n    return self._update(self._table.apply_gradients, slot_to_id_and_grad, *args,\n                        **kwargs)\n\n  def _update(self, method, slot_to_id_and_tensor: Dict[str, Tuple[tf.Tensor,\n                                                                   tf.Tensor]],\n              *args, **kwargs):\n    if kwargs.pop(\"skip_merge_id\", False):\n      # To avoid redundant cpu usage, in MergedMultiTypeHashTable\n      # sync training only passes the slot_to_grad for apply_gradient\n      slot_to_grad = slot_to_id_and_tensor\n      with device_utils.maybe_device_if_allowed(\"/device:GPU:0\"):\n        merged_slot_to_grad, _ = self._get_merged_to_indexed_tensor(\n            slot_to_grad)\n      return self._copy_with_new_table(\n          method(merged_slot_to_grad, *args, **kwargs))\n\n    slot_to_id = {k: v[0] for k, v in slot_to_id_and_tensor.items()}\n    merged_slot_to_id, _ = self._get_merged_to_indexed_tensor(slot_to_id)\n    slot_to_tensor = {k: v[1] for k, v in slot_to_id_and_tensor.items()}\n    with device_utils.maybe_device_if_allowed(\"/device:GPU:0\"):\n      merged_slot_to_tensor, _ = self._get_merged_to_indexed_tensor(\n          slot_to_tensor)\n    merged_slot_to_id_and_tensor = {}\n    for slot in merged_slot_to_id:\n      merged_slot_to_id_and_tensor[slot] = (merged_slot_to_id[slot],\n                                            merged_slot_to_tensor[slot])\n    return self._copy_with_new_table(\n        method(merged_slot_to_id_and_tensor, *args, **kwargs))\n\n  def as_op(self, name=None) -> tf.Operation:\n    return self._table.as_op(name)\n\n  def _get_merged_to_indexed_tensor(\n      self, slot_to_tensor: Dict[str, tf.Tensor]\n  ) -> Tuple[Dict[str, tf.Tensor], Dict[str, tf.Tensor]]:\n    merged_slot_to_tensors = defaultdict(list)\n    merged_slot_to_sizes = defaultdict(list)\n    for slot in sorted(slot_to_tensor.keys()):\n      # We sorted the merged slot keys here to guarantee, the merging order.\n      tensor = slot_to_tensor[slot]\n      merged_slot = self._slot_mapping[slot]\n      merged_slot_to_sizes[merged_slot].append(tf.shape(tensor)[0])\n      merged_slot_to_tensors[merged_slot].append(tensor)\n\n    return {k: tf.concat(v, axis=0) for k, v in merged_slot_to_tensors.items()}, \\\n           {k: tf.stack(v) for k, v in merged_slot_to_sizes.items()}\n\n  def _copy_with_new_table(\n      self, table: BaseMultiTypeHashTable) -> MergedMultiTypeHashTable:\n    copied = copy.copy(self)\n    copied._table = table\n    return copied\n\n  def get_table_dim_sizes(self) -> List[int]:\n    return [\n        infer_dim_size(self._merged_slot_to_config[slot].table_config)\n        for slot in sorted(self._merged_slot_to_config.keys())\n    ]\n"
  },
  {
    "path": "monolith/native_training/multi_type_hash_table_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport hashlib\nfrom typing import Dict, List\nfrom unittest import mock\n\nimport tensorflow as tf\n\nfrom monolith.native_training import entry\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import learning_rate_functions\nfrom monolith.native_training import multi_type_hash_table\nfrom monolith.native_training import test_utils\n\n\ndef factory(idx: int, config):\n  return hash_table_ops.hash_table_from_config(config=config,\n                                               name_suffix=str(idx))\n\n\ndef _id(x):\n  return tf.constant(x, dtype=tf.int64)\n\n\ndef _value(x):\n  return tf.constant(x, dtype=tf.float32)\n\n\nclass MultiTypeHashTableTest(tf.test.TestCase):\n\n  def test_basic(self):\n    with self.session() as sess:\n      hash_table = multi_type_hash_table.MultiTypeHashTable(\n          {\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot1\": test_utils.generate_test_hash_table_config(2),\n              \"slot2\": test_utils.generate_test_hash_table_config(2),\n          }, factory)\n      hash_table = hash_table.assign_add({\n          \"slot0\": (_id([0]), _value([[1]])),\n          \"slot1\": (_id([1]), _value([[2, 2]])),\n          \"slot2\": (_id([2, 3]), _value([[4, 4], [8, 8]]))\n      })\n      values_dict = hash_table.lookup({\n          \"slot0\": _id([0]),\n          \"slot1\": _id([1]),\n          \"slot2\": _id([2, 3]),\n      })\n      values_dict = sess.run(values_dict)\n    expected_values_dict = {\n        \"slot0\": [[1]],\n        \"slot1\": [[2, 2]],\n        \"slot2\": [[4, 4], [8, 8]]\n    }\n    for slot, values in values_dict.items():\n      self.assertAllEqual(values, expected_values_dict[slot])\n\n  def test_apply_gradients(self):\n    with self.session() as sess:\n      hash_table = multi_type_hash_table.MultiTypeHashTable(\n          {\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot1\": test_utils.generate_test_hash_table_config(2),\n          }, factory)\n      values_dict = hash_table.lookup({\n          \"slot0\": _id([0]),\n          \"slot1\": _id([1, 2]),\n      })\n      grads = [tf.constant(2.0), tf.constant([[1.0, 3.0], [2.0, 4.0]])]\n      global_step = tf.constant(0, dtype=tf.int64)\n      hash_table = hash_table.apply_gradients(\n          {\n              \"slot0\": (_id([0]), grads[0]),\n              \"slot1\": (_id([1, 2]), grads[1]),\n          }, global_step)\n      values_dict = hash_table.lookup({\n          \"slot0\": _id([0]),\n          \"slot1\": _id([1, 2]),\n      })\n      values_dict = sess.run(values_dict)\n      expected_dict = {\"slot0\": [[-2]], \"slot1\": [[-1, -3], [-2, -4]]}\n      for key in expected_dict:\n        self.assertAllEqual(values_dict[key], expected_dict[key])\n\n  def test_apply_gradients_with_learning_rate_decay(self):\n    with self.session() as sess:\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      hash_table = multi_type_hash_table.MultiTypeHashTable(\n          {\n              \"slot0\":\n                  test_utils.generate_test_hash_table_config(\n                      1,\n                      learning_rate=learning_rate_functions.PolynomialDecay(\n                          initial_learning_rate=0.1,\n                          decay_steps=10,\n                          end_learning_rate=1.1)),\n              \"slot1\":\n                  test_utils.generate_test_hash_table_config(\n                      2,\n                      learning_rate=learning_rate_functions.PolynomialDecay(\n                          initial_learning_rate=0.1,\n                          decay_steps=10,\n                          end_learning_rate=1.1)),\n          }, factory)\n      values_dict = hash_table.lookup({\n          \"slot0\": _id([0]),\n          \"slot1\": _id([1, 2]),\n      })\n      grads = [tf.constant(2.0), tf.constant([[1.0, 3.0], [2.0, 4.0]])]\n      hash_table = hash_table.apply_gradients(\n          {\n              \"slot0\": (_id([0]), grads[0]),\n              \"slot1\": (_id([1, 2]), grads[1]),\n          }, global_step)\n      values_dict = hash_table.lookup({\n          \"slot0\": _id([0]),\n          \"slot1\": _id([1, 2]),\n      })\n      values_dict = sess.run(values_dict)\n      expected_dict = {\"slot0\": [[-0.2]], \"slot1\": [[-0.1, -0.3], [-0.2, -0.4]]}\n      for key in expected_dict:\n        self.assertAllClose(values_dict[key], expected_dict[key])\n\n  def test_apply_gradients_without_lookup(self):\n    with self.session() as sess:\n      hash_table = multi_type_hash_table.MultiTypeHashTable(\n          {\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot1\": test_utils.generate_test_hash_table_config(2)\n          }, factory)\n      global_step = tf.constant(0, dtype=tf.int64)\n      hash_table = hash_table.apply_gradients(\n          {\n              \"slot0\": (_id([1]), tf.constant(3.0)),\n              \"slot1\": (_id([2, 2]), tf.constant([[1.1, 3.1], [2.2, 4.2]])),\n          }, global_step)\n      values_dict = hash_table.lookup({\"slot0\": _id([1]), \"slot1\": _id([1, 2])})\n      values_dict = sess.run(values_dict)\n      expected_dict = {\"slot0\": [[-3]], \"slot1\": [[0, 0], [-3.3, -7.3]]}\n      for key in expected_dict:\n        self.assertAllClose(values_dict[key], expected_dict[key])\n\n  def test_fused_lookup(self):\n    with self.session() as sess:\n      hash_table = multi_type_hash_table.MultiTypeHashTable(\n          {\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot1\": test_utils.generate_test_hash_table_config(2),\n              \"slot2\": test_utils.generate_test_hash_table_config(2),\n          }, factory)\n      hash_table = hash_table.assign_add({\n          \"slot0\": (_id([0]), _value([[1]])),\n          \"slot1\": (_id([1]), _value([[2, 2]])),\n          \"slot2\": (_id([2, 3]), _value([[4, 4], [8, 8]]))\n      })\n      values_dict = hash_table.fused_lookup([0, 1, 2, 3], [1, 1, 2], 1)\n      embeddings, recv_splits, id_offsets, emb_offsets, indices = sess.run(\n          values_dict)\n    self.assertAllEqual(embeddings, [1, 2, 2, 4, 4, 8, 8])\n    self.assertAllEqual(recv_splits, [7])\n    self.assertAllEqual(id_offsets, [0, 1, 2, 4])\n    self.assertAllEqual(emb_offsets, [0, 1, 3, 7])\n\n  def test_fused_lookup_multi_shards(self):\n    with self.session() as sess:\n      hash_table = multi_type_hash_table.MultiTypeHashTable(\n          {\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot1\": test_utils.generate_test_hash_table_config(2),\n              \"slot2\": test_utils.generate_test_hash_table_config(2),\n          }, factory)\n      hash_table = hash_table.assign_add({\n          \"slot0\": (_id([0]), _value([[1]])),\n          \"slot1\": (_id([1]), _value([[2, 2]])),\n          \"slot2\": (_id([2, 3]), _value([[4, 4], [8, 8]]))\n      })\n      values_dict = hash_table.fused_lookup([0, 2, 1, 3], [1, 0, 1, 0, 1, 1], 2)\n      embeddings, recv_splits, id_offsets, emb_offsets, indices = sess.run(\n          values_dict)\n    self.assertAllEqual(embeddings, [1, 4, 4, 2, 2, 8, 8])\n    self.assertAllEqual(recv_splits, [3, 4])\n    self.assertAllEqual(id_offsets, [0, 1, 1, 2, 2, 3, 4])\n    self.assertAllEqual(emb_offsets, [0, 1, 1, 3, 3, 5, 7])\n\n  def test_fused_apply_gradients(self):\n    with self.session() as sess:\n      hash_table = multi_type_hash_table.MultiTypeHashTable(\n          {\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot1\": test_utils.generate_test_hash_table_config(2)\n          }, factory)\n      ids = tf.constant([0, 1, 2], dtype=tf.int64)\n      fused_slot_size = tf.constant([1, 2])\n      embeddings, _, id_offsets, emb_offsets, indices = hash_table.fused_lookup(\n          ids, fused_slot_size, 1)\n      grads = tf.constant([2.0, 1.0, 3.0, 2.0, 4.0])\n      hash_table = hash_table.fused_apply_gradient(\n          ids, indices, fused_slot_size, grads, id_offsets, emb_offsets,\n          tf.constant(0, dtype=tf.int64), tf.constant(0, dtype=tf.int64), 1)\n      lookup_op = hash_table.fused_lookup(ids, fused_slot_size, 1)\n      embeddings, recv_splits, id_offsets, emb_offsets, indices = sess.run(\n          lookup_op)\n    self.assertAllEqual(embeddings, [-2, -1, -3, -2, -4])\n    self.assertAllEqual(recv_splits, [5])\n    self.assertAllEqual(id_offsets, [0, 1, 3])\n    self.assertAllEqual(emb_offsets, [0, 1, 5])\n\n  def test_fused_apply_gradients_missing_tables(self):\n    with self.session() as sess:\n      hash_table = multi_type_hash_table.MultiTypeHashTable(\n          {\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot1\": test_utils.generate_test_hash_table_config(2)\n          }, factory)\n      ids = tf.constant([1, 1], dtype=tf.int64)\n      fused_slot_size = tf.constant([1, 0, 1, 0])\n      embeddings, _, id_offsets, emb_offsets, indices = hash_table.fused_lookup(\n          ids, fused_slot_size, 2)\n      grads = tf.constant([1.0, 2.0])\n      hash_table = hash_table.fused_apply_gradient(\n          ids, indices, fused_slot_size, grads, id_offsets, emb_offsets,\n          tf.constant(0, dtype=tf.int64), tf.constant(0, dtype=tf.int64), 2)\n      lookup_op = hash_table.fused_lookup(ids, fused_slot_size, 2)\n      embeddings, recv_splits, id_offsets, emb_offsets, indices = sess.run(\n          lookup_op)\n    self.assertAllEqual(embeddings, [-3, -3])\n    self.assertAllEqual(recv_splits, [1, 1])\n    self.assertAllEqual(id_offsets, [0, 1, 1, 2, 2])\n    self.assertAllEqual(emb_offsets, [0, 1, 1, 2, 2])\n\n\ndef _multi_type_factory(slot_to_config):\n  return multi_type_hash_table.MultiTypeHashTable(slot_to_config, factory)\n\n\nclass MergedMultiTypeHashTable(tf.test.TestCase):\n\n  def testBasic(self):\n    with self.session() as sess:\n      hash_table = multi_type_hash_table.MergedMultiTypeHashTable(\n          {\n              \"slot0\": test_utils.generate_test_hash_table_config(1),\n              \"slot1\": test_utils.generate_test_hash_table_config(2),\n              \"slot2\": test_utils.generate_test_hash_table_config(2),\n          }, _multi_type_factory)\n      # slot 1 & 2 should be merged.\n      updated_hash_table = hash_table.assign_add({\n          \"slot0\": (_id([0]), _value([[1]])),\n          \"slot1\": (_id([1]), _value([[2, 2]])),\n          \"slot2\": (_id([1]), _value([[4, 4]]))\n      })\n      values_dict = updated_hash_table.lookup({\n          \"slot0\": _id([0]),\n          \"slot1\": _id([1]),\n          \"slot2\": _id([1]),\n      })\n      values_dict = sess.run(values_dict)\n      expected_values_dict = {\n          \"slot0\": [[1]],\n          \"slot1\": [[6, 6]],\n          \"slot2\": [[6, 6]]\n      }\n      for slot, values in values_dict.items():\n        self.assertAllEqual(values, expected_values_dict[slot])\n      global_step = tf.constant(0, dtype=tf.int64)\n      updated_hash_table = hash_table.apply_gradients(\n          {\n              \"slot0\": (_id([0]), _value([[-1]])),\n              \"slot1\": (_id([1]), _value([[1, 1]])),\n              \"slot2\": (_id([1]), _value([[1, 1]]))\n          }, global_step)\n      values_dict = updated_hash_table.lookup({\n          \"slot0\": _id([0]),\n          \"slot1\": _id([1]),\n      })\n      values_dict = sess.run(values_dict)\n      expected_values_dict = {\"slot0\": [[2]], \"slot1\": [[4, 4]]}\n      for slot, values in values_dict.items():\n        self.assertAllEqual(values, expected_values_dict[slot])\n\n  def testNameStability(self):\n    factory = mock.MagicMock()\n\n    def call(slot_to_config: Dict[str, entry.HashTableConfigInstance]):\n      self.assertListEqual(list(slot_to_config.keys()),\n                           [\"e21904dd414d1780e5fc904866dc69c2\"])\n      return _multi_type_factory(slot_to_config)\n\n    factory.side_effect = call\n    hash_table = multi_type_hash_table.MergedMultiTypeHashTable(\n        {\n            \"slot0\": test_utils.generate_test_hash_table_config(1),\n            \"slot1\": test_utils.generate_test_hash_table_config(1),\n        }, factory)\n\n  def testRestoreName(self):\n    factory = mock.MagicMock()\n\n    def call(slot_to_config: Dict[str, entry.HashTableConfigInstance]):\n      config = next(iter(slot_to_config.values()))\n      expected_name = hashlib.md5(\"slot_0\".encode()).hexdigest()\n      self.assertListEqual(config.extra_restore_names, [expected_name])\n      return _multi_type_factory(slot_to_config)\n\n    hash_table = multi_type_hash_table.MergedMultiTypeHashTable(\n        {\n            \"fc_slot_0\": test_utils.generate_test_hash_table_config(1),\n        }, factory)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/native_model.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom posixpath import split\nfrom monolith.native_training.distributed_serving_ops import remote_predict\nfrom monolith.native_training.utils import with_params\nfrom absl import logging, flags\nfrom abc import ABC, abstractmethod\nfrom copy import deepcopy\nfrom datetime import datetime\nfrom functools import partial\nimport os, math, time\nimport hashlib\nfrom typing import Tuple, Dict, Iterable, Union, Optional\nimport numpy as np\n\nimport tensorflow as tf\nfrom tensorflow.estimator.export import ServingInputReceiver\nfrom tensorflow.python.data.ops.dataset_ops import DatasetV2\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.saved_model.signature_constants import DEFAULT_SERVING_SIGNATURE_DEF_KEY\n\nfrom monolith.core import hyperparams\nfrom monolith.native_training.entry import *\nfrom monolith.native_training.feature import *\nfrom monolith.core.base_layer import get_layer_loss\nfrom monolith.core.hyperparams import update_params\n\nfrom monolith.native_training import distribution_ops\nfrom monolith.native_training import file_ops\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training.native_task_context import get\nimport monolith.native_training.feature_utils as feature_utils\nfrom monolith.native_training.estimator import EstimatorSpec\nfrom monolith.native_training.embedding_combiners import FirstN, Combiner\nfrom monolith.native_training.graph_utils import add_batch_norm_into_update_ops\nfrom monolith.native_training.layers import LogitCorrection\nfrom monolith.native_training.native_task import NativeTask, NativeContext\nfrom monolith.native_training.metric import utils as metric_utils\nfrom monolith.native_training.model_export import export_context\nfrom monolith.native_training.model_export.export_context import is_exporting, is_exporting_distributed\nfrom monolith.native_training.data.feature_list import get_feature_name_and_slot\nfrom monolith.native_training.monolith_export import monolith_export\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\nfrom monolith.native_training.data.feature_utils import switch_slot, switch_slot_batch\nfrom monolith.native_training.data.utils import get_slot_feature_name, enable_tob_env, register_slots\nfrom monolith.native_training.utils import add_to_collections\nfrom monolith.native_training.model_dump.dump_utils import DumpUtils\nfrom monolith.native_training.dense_reload_utils import CustomRestoreListener, CustomRestoreListenerKey\nfrom monolith.native_training.layers.utils import dim_size\nfrom monolith.native_training.metric.metric_hook import KafkaMetricHook, FileMetricHook, vepfs_key_fn, vepfs_layout_fn\nfrom idl.matrix.proto.example_pb2 import OutConfig, OutType, TensorShape\nfrom monolith.native_training.data.datasets import POOL_KEY, PBDataset, PbType\nfrom monolith.native_training.data.parsers import ParserCtx, get_default_parser_ctx, parse_instances, parse_examples\nfrom monolith.native_training.model_dump.graph_utils import _node_name\nfrom monolith.native_training.distribution_utils import enable_sync_training\nfrom monolith.native_training.device_utils import input_device_fn, model_device_fn, serving_input_device_fn, maybe_device_if_allowed\n\nFLAGS = flags.FLAGS\ndump_utils = DumpUtils(enable=False)\n\n\n@monolith_export\ndef get_sigmoid_loss_and_pred(\n    name,\n    logits,\n    label,\n    batch_size: int,\n    sample_rate: Union[tf.Tensor, float] = 1.0,\n    sample_bias: bool = False,\n    mode: tf.estimator.ModeKeys = tf.estimator.ModeKeys.TRAIN,\n    instance_weight: tf.Tensor = None,\n    mask: tf.Tensor = None,\n    logit_clip_threshold: Optional[float] = None,\n    predict_before_correction: bool = True):\n  \"\"\"对二分类, 基于sigmoid计算loss和predict\n\n  由于负例采样, fast_emit等原因, 需要对logit进进较正, 在get_sigmoid_loss_and_pred会透明地进行\n\n  Args:\n    name (:obj:`str`): 名称\n    logits (:obj:`tf.Tensor`): 样本logits(无偏logit), 可用于直接predict, 但是不能用于直接计算loss\n    label (:obj:`tf.Tensor`): 样本标签\n    batch_size (:obj:`int`): 批大小\n    sample_rate (:obj:`tf.Tensor`): 负例采样的采样率\n    sample_bias (:obj:`bool`): 是否有开启fast_emit\n    mode (:obj:`str`): 运行模式, 可以是train/eval/predict等\n    mask (:obj:`tf.Tensor`): Apply boolean mask to loss before reduce_sum\n\n  \"\"\"\n\n  logits = tf.reshape(logits, shape=(-1,))\n  batch_size = dim_size(logits, 0)\n  if mode != tf.estimator.ModeKeys.PREDICT:\n    if sample_rate is not None and isinstance(sample_rate, float):\n      sample_rate = tf.fill(dims=(batch_size,), value=sample_rate)\n    if sample_rate is None:\n      sample_rate = tf.fill(dims=(batch_size,), value=1.0)\n    src = LogitCorrection(activation=None,\n                          sample_bias=sample_bias,\n                          name='sample_rate_correction')\n    logits_biased = src((logits, sample_rate))\n    if predict_before_correction:\n      pred = tf.nn.sigmoid(logits, name='{name}_sigmoid_pred'.format(name=name))\n    else:\n      pred = tf.nn.sigmoid(logits_biased,\n                           name='{name}_sigmoid_pred'.format(name=name))\n\n    if logit_clip_threshold is not None:\n      assert 0 < logit_clip_threshold < 1\n      threshold = math.log((1 - logit_clip_threshold) / logit_clip_threshold)\n      logits_biased = tf.clip_by_value(logits_biased, -threshold, threshold)\n\n    losses = tf.nn.sigmoid_cross_entropy_with_logits(\n        labels=tf.reshape(label, shape=(-1,)),\n        logits=logits_biased,\n        name='{name}_sigmoid_loss'.format(name=name))\n    if instance_weight is not None:\n      instance_weight = tf.reshape(instance_weight, shape=(-1,))\n    if mask is not None:\n      mask = tf.reshape(mask, shape=(-1,))\n      losses = tf.boolean_mask(losses, mask)\n      if instance_weight is not None:\n        instance_weight = tf.boolean_mask(instance_weight, mask)\n    if instance_weight is not None:\n      losses = tf.multiply(losses, instance_weight)\n    loss = tf.reduce_sum(losses)\n  else:\n    loss = None\n    pred = tf.nn.sigmoid(logits, name='{name}_sigmoid_pred'.format(name=name))\n\n  return loss, pred\n\n\n@monolith_export\ndef get_softmax_loss_and_pred(name, logits, label, mode):\n  \"\"\"对多分类, 基于softmax计算loss和predict\n\n  Args:\n    name (:obj:`str`): 名称\n    logits (:obj:`tf.Tensor`): 样本logits\n    label (:obj:`tf.Tensor`): 样本标签\n    mode (:obj:`str`): 运行模式, 可以是train/eval/predict等\n\n  \"\"\"\n\n  pred = tf.argmax(tf.nn.softmax(logits,\n                                 name='{name}_softmax_pred'.format(name=name)),\n                   axis=1)\n  if mode != tf.estimator.ModeKeys.PREDICT:\n    loss = tf.nn.softmax_cross_entropy_with_logits(\n        labels=label,\n        logits=logits,\n        name='{name}_softmax_loss'.format(name=name))\n  else:\n    loss = None\n\n  return loss, pred\n\n\nclass DeviceCtxType(object):\n  INPUT_FN: str = 'input_fn'\n  MODEL_FN: str = 'model_fn'\n  INPUT_RECEIVER_FN: str = 'input_receiver_fn'\n  OTHERS: str = 'others'\n\n  @classmethod\n  def all_types(cls):\n    return {cls.INPUT_FN, cls.MODEL_FN, cls.INPUT_RECEIVER_FN, cls.OTHERS}\n\n\nclass MonolithDeviceCtx(object):\n\n  def __init__(self, ctx_type: str):\n    assert ctx_type is not None and ctx_type in DeviceCtxType.all_types()\n    self.ctx_type = ctx_type\n    self._current = None\n    self._device_fn = None\n\n  def __enter__(self):\n    if not enable_sync_training() or export_context.is_exporting():\n      return\n\n    if self.ctx_type == DeviceCtxType.INPUT_FN:\n      self._device_fn = input_device_fn\n    elif self.ctx_type == DeviceCtxType.MODEL_FN:\n      self._device_fn = model_device_fn\n    elif self.ctx_type == DeviceCtxType.INPUT_RECEIVER_FN:\n      self._device_fn = serving_input_device_fn\n    else:\n      return\n    self._current = tf.compat.v1.device(self._device_fn)\n    return self._current.__enter__()\n\n  def __exit__(self, exc_type, exc_val, exc_tb):\n    if self._current is not None:\n      if self.ctx_type == DeviceCtxType.MODEL_FN:\n        self.ensure_variables_in_device()\n      self._current.__exit__(exc_type, exc_val, exc_tb)\n      self._current = None\n      self._device_fn = None\n\n  def ensure_variables_in_device(self):\n    graph = tf.compat.v1.get_default_graph()\n    for op in graph.get_operations():\n      if op.name.startswith('global_step'):\n        graph._apply_device_functions(op)\n\n\n\n\n@monolith_export\nclass MonolithBaseModel(NativeTask, ABC):\n  \"\"\"模型开发的基类\"\"\"\n\n  @classmethod\n  def params(cls):\n    p = super(MonolithBaseModel, cls).params()\n    p.define(\"output_path\", None, \"The output path of predict/eval\")\n    p.define(\"output_fields\", None, \"The output fields\")\n    p.define(\"delimiter\", '\\t', \"The delimiter of output file\")\n    p.define('file_name', '', 'the test input file name')\n    p.define('enable_grads_and_vars_summary', False,\n             'enable_grads_and_vars_summary')\n    # p.define(\"only_save_item_cache_hashtable\", False, \"if set, then only save item cache table in next run\")\n    p.define('dense_weight_decay', 0.0, 'dense_weight_decay')\n    p.define(\"clip_norm\", 1000.0, \"float, clip_norm\")\n    p.define(\"sparse_norm_warmup_steps\", None, \"int, sparse norm warmup steps\")\n    p.define('default_occurrence_threshold', 0, 'int')\n    return p\n\n  def __init__(self, params):\n    super(MonolithBaseModel, self).__init__(params)\n    enable_tob_env()\n    self.fs_dict = {}\n    self.fc_dict = {}\n    # feature_name -> slice_name -> FeatureSlice(feature_slot, start, end)\n    self.slice_dict = {}\n    self._layout_dict = {}\n    self._occurrence_threshold = {}\n    self._use_dense_allreduce = FLAGS.enable_sync_training\n    self._share_slot_mapping = {}\n\n  def __getattr__(self, name):\n    if \"p\" in self.__dict__:\n      if hasattr(self.p, name):\n        return getattr(self.p, name)\n      elif name == 'batch_size':\n        if self.p.mode == tf.estimator.ModeKeys.EVAL:\n          return self.p.eval.per_replica_batch_size\n        else:\n          return self.p.train.per_replica_batch_size\n\n    if (hasattr(type(self), name) and\n        isinstance(getattr(type(self), name), property)):\n      return getattr(type(self), name).fget(self)\n    else:\n      return super(MonolithBaseModel, self).__getattr__(name)\n\n  def __setattr__(self, key, value):\n    if 'p' in self.__dict__:\n      if hasattr(self.p, key):\n        setattr(self.p, key, value)\n        return value\n      elif key == 'batch_size':\n        self.p.eval.per_replica_batch_size = value\n        self.p.train.per_replica_batch_size = value\n        return value\n\n    super(MonolithBaseModel, self).__setattr__(key, value)\n    return value\n\n  def __deepcopy__(self, memo):\n    cls = self.__class__\n    result = cls.__new__(cls)\n    memo[id(self)] = result\n    for name, value in self.__dict__.items():\n      if name == 'dump_utils':\n        result.__dict__[name] = value\n      else:\n        result.__dict__[name] = deepcopy(value)\n    return result\n\n  def _get_file_ops(self, features, pred):\n    assert self.p.output_fields is not None\n    output_path = os.path.join(self.p.output_path,\n                               f\"part-{get().worker_index:05d}\")\n    op_file = file_ops.WritableFile(output_path)\n    op_fields = [features[field] for field in self.p.output_fields.split(',')]\n    if isinstance(pred, (tuple, list)):\n      op_fields.extend(pred)\n    elif isinstance(pred, dict):\n      sorted_keys = list(sorted(pred.keys()))\n      logging.info('sorted_keys: %s', sorted_keys)\n      op_fields.extend([pred[k] for k in sorted_keys])\n    else:\n      op_fields.append(pred)\n    fmt = self.p.delimiter.join([\"{}\"] * len(op_fields)) + \"\\n\"\n    try:\n      op_fields_tmp = [\n        tf.squeeze(tensor, axis=-1) if tensor.shape.rank > 1 and tensor.shape.as_list()[-1] == 1 else tensor\n        for tensor in op_fields if tensor is not None]\n      op_fields = op_fields_tmp\n    except Exception as e:\n      pass\n    result = tf.nest.map_structure(\n      tf.stop_gradient,\n      tf.map_fn(fn=lambda t: tf.strings.format(fmt, t, summarize=-1),\n                elems=tuple(op_fields),\n                dtype=tf.string,\n                fn_output_signature=tf.string)\n    )\n    write_op = op_file.append(tf.strings.reduce_join(result))\n    return op_file, write_op\n\n  def _dump_item_embedding_ops(self, features):\n    assert isinstance(self, DeepRoughSortBaseModel)\n    assert 'item_id' in features and 'item_bias' in features and 'item_vec' in features\n    item_cache_table_path = self._cal_item_cache_table_path()\n    cache_table_file_name = \"MonolithHashTable_cached_item_embeddings-00000-of-00001\"\n    output_path = os.path.join(item_cache_table_path, cache_table_file_name)\n    logging.info(f\"_dump_item_embedding_ops: output_path={output_path}\")\n    op_file = file_ops.WritableFile(output_path)\n    write_op = op_file.append_entry_dump(features['item_id'], features['item_bias'], features['item_vec'])\n    return op_file, write_op\n    \n  def _get_real_mode(self, mode: tf.estimator.ModeKeys):\n    if mode == tf.estimator.ModeKeys.PREDICT:\n      return mode\n    elif mode == tf.estimator.ModeKeys.TRAIN:\n      return self.mode\n    else:\n      raise ValueError('model error!')\n\n  def is_fused_layout(self) -> bool:\n    return self.ctx.layout_factory is not None\n\n  def instantiate(self):\n    \"\"\"实例化对像\"\"\"\n    return self\n\n  def add_loss(self, losses):\n    \"\"\"用于追加辅助loss, 如layer loss等\n\n    Args:\n      losses (:obj:`List[tf.Tensor]`): 辅助loss列表\n\n    \"\"\"\n\n    if losses:\n      if isinstance(losses, (list, tuple)):\n        self.losses.extend(losses)\n      else:\n        self.losses.append(losses)\n\n  @property\n  def losses(self):\n    graph = tf.compat.v1.get_default_graph()\n    if hasattr(graph, '__losses'):\n      return getattr(graph, '__losses')\n    else:\n      setattr(graph, '__losses', [])\n      return graph.__losses\n\n  @losses.setter\n  def losses(self, losses):\n    graph = tf.compat.v1.get_default_graph()\n    if hasattr(graph, '__losses'):\n      graph.__losses = losses\n    else:\n      setattr(graph, '__losses', losses)\n\n  @property\n  def _global_step(self):\n    with maybe_device_if_allowed('/device:GPU:0'):\n      return tf.compat.v1.train.get_or_create_global_step()\n\n  @property\n  def _training_hooks(self):\n    graph = tf.compat.v1.get_default_graph()\n    if hasattr(graph, '__training_hooks'):\n      return getattr(graph, '__training_hooks')\n    else:\n      setattr(graph, '__training_hooks', [])\n      return graph.__training_hooks\n\n  @_training_hooks.setter\n  def _training_hooks(self, hooks):\n    graph = tf.compat.v1.get_default_graph()\n    if hasattr(graph, '__training_hooks'):\n      graph.__training_hooks = hooks\n    else:\n      setattr(graph, '__training_hooks', hooks)\n\n  def clean(self):\n    # update fs_dict, fc_dict, slice_dict\n    self.fs_dict = {}\n    self.fc_dict = {}\n    self.slice_dict = {}  # slot_id -> Dict[slot_id, slice]\n    self._occurrence_threshold = {}\n\n  def create_input_fn(self, mode):\n    \"\"\"生成input_fn\"\"\"\n\n    def input_fn_internal():\n      with MonolithDeviceCtx(ctx_type=DeviceCtxType.INPUT_FN):\n        return self.input_fn(mode)\n\n    return input_fn_internal\n\n  def create_model_fn(self):\n    \"\"\"生成model_fn\"\"\"\n    self.clean()\n\n    def model_fn_internal(\n        features: Dict[str, tf.Tensor], mode: tf.estimator.ModeKeys,\n        config: tf.estimator.RunConfig) -> tf.estimator.EstimatorSpec:\n\n      global_step = self._global_step\n      real_mode = self._get_real_mode(mode)\n      with MonolithDeviceCtx(ctx_type=DeviceCtxType.MODEL_FN):\n        local_spec = self.model_fn(features, real_mode)\n\n      # get label, loss, pred and head_name from model_fn result\n      if isinstance(local_spec, EstimatorSpec):\n        label, loss, pred = local_spec.label, local_spec.loss, local_spec.pred\n        if isinstance(pred, dict):\n          assert label is None or isinstance(label, dict)\n          head_name, pred = list(zip(*pred.items()))\n        else:\n          head_name = local_spec.head_name or self.metrics.deep_insight_target.split(\n              ',')\n        is_classification = local_spec.classification\n      elif isinstance(local_spec, (tuple, list)):\n        label, loss, pred = local_spec\n        if isinstance(pred, dict):\n          assert label is None or isinstance(label, dict)\n          head_name, pred = list(zip(*pred.items()))\n        else:\n          head_name = self.metrics.deep_insight_target\n        assert head_name is not None\n        is_classification = True\n        logging.warning(\n            'if this is not a classification task, pls. return EstimatorSpec in model_fn and specify it'\n        )\n      else:\n        raise Exception(\"EstimatorSpec Error!\")\n\n      # check label/pred/head_name\n      if isinstance(pred, (list, tuple, dict)):\n        assert isinstance(head_name, (list, tuple))\n        assert isinstance(pred, (list, tuple))\n        if label is not None:\n          assert len(head_name) == len(label)\n          assert len(label) == len(pred)\n      else:\n        if isinstance(head_name, (list, tuple)):\n          assert len(head_name) == 1\n          head_name = head_name[0]\n        assert isinstance(head_name, str)\n        if label is not None:\n          assert isinstance(label, tf.Tensor)\n        if isinstance(pred, (list, tuple)):\n          assert len(pred) == 1\n          pred = pred[0]\n          assert isinstance(pred, tf.Tensor)\n\n      if label is not None:\n        if isinstance(label, dict):\n          label = {\n              key: None if value is None else tf.identity(value, name=key)\n              for key, value in label.items()\n          }\n        elif isinstance(label, (list, tuple)):\n          label = [\n              None if l is None else tf.identity(\n                  l, name=f'label_{_node_name(l.name)}') for l in label\n          ]\n        else:\n          label = label if label is None else tf.identity(\n              label, name=f'label_{_node_name(label.name)}')\n\n      dump_utils.add_model_fn(self, mode, features, label, loss, pred,\n                              head_name, is_classification)\n\n      if self.losses:\n        loss = loss + tf.add_n(self.losses)\n\n      # in predict mode, when enable_resource_constrained_roughsort, only generate item_cache_hashtable\n      if not is_exporting() and real_mode == tf.estimator.ModeKeys.PREDICT and FLAGS.enable_resource_constrained_roughsort:\n        assert isinstance(self, DeepRoughSortBaseModel)\n        if isinstance(pred, (list, tuple)):\n          assert isinstance(head_name,\n                            (list, tuple)) and len(pred) == len(head_name)\n          predictions = dict(zip(head_name, pred))\n        else:\n          predictions = pred\n      \n        item_cache_op_file, item_cache_write_op = self._dump_item_embedding_ops(features)\n        close_hook = file_ops.FileCloseHook([item_cache_op_file])\n        with tf.control_dependencies(control_inputs=[item_cache_write_op]):\n          if isinstance(predictions, dict):\n            predictions = {k: tf.identity(v) for k, v in predictions.items()}\n          else:\n            predictions = tf.identity(predictions)\n          return tf.estimator.EstimatorSpec(tf.estimator.ModeKeys.PREDICT,\n                                            loss=tf.constant(1),\n                                            train_op=tf.no_op(),\n                                            training_hooks=[close_hook] +\n                                            self._training_hooks,\n                                            predictions=predictions)\n\n      if real_mode == tf.estimator.ModeKeys.PREDICT:\n        if isinstance(pred, (list, tuple)):\n          assert isinstance(head_name,\n                            (list, tuple)) and len(pred) == len(head_name)\n          predictions = dict(zip(head_name, pred))\n        else:\n          predictions = pred\n\n        if is_exporting() or self.p.output_path is None:\n          spec = tf.estimator.EstimatorSpec(real_mode,\n                                            predictions=predictions,\n                                            training_hooks=self._training_hooks)\n        else:\n          op_file, write_op = self._get_file_ops(features, pred)\n          close_hook = file_ops.FileCloseHook([op_file])\n          with tf.control_dependencies(control_inputs=[write_op]):\n            if isinstance(pred, dict):\n              predictions = {k: tf.identity(v) for k, v in predictions.items()}\n            else:\n              predictions = tf.identity(predictions)\n            spec = tf.estimator.EstimatorSpec(mode,\n                                              training_hooks=[close_hook] +\n                                              self._training_hooks,\n                                              predictions=predictions)\n        if is_exporting() and self._export_outputs:\n          self._export_outputs.update(spec.export_outputs)\n          return spec._replace(export_outputs=self._export_outputs)\n        else:\n          return spec\n\n      train_ops = []\n      targets, labels_list, preds_list = [], [], []\n      if isinstance(pred, (list, tuple, dict)):\n        assert isinstance(label,\n                          (list, tuple, dict)) and len(pred) == len(label)\n        assert isinstance(head_name,\n                          (list, tuple)) and len(pred) == len(head_name)\n        if isinstance(is_classification, (tuple, list, dict)):\n          assert len(pred) == len(is_classification)\n        else:\n          is_classification = [is_classification] * len(pred)\n\n        for i, name in enumerate(head_name):\n          label_tensor = label[i] if isinstance(label,\n                                                (list, tuple)) else label[name]\n          pred_tensor = pred[i] if isinstance(pred,\n                                              (list, tuple)) else pred[name]\n          head_classification = is_classification[i] if isinstance(\n              is_classification, (list, tuple)) else is_classification[name]\n\n          targets.append(name)\n          labels_list.append(label_tensor)\n          preds_list.append(pred_tensor)\n\n          if not FLAGS.disable_native_metrics:\n            if head_classification:\n              auc_per_core, auc_update_op = tf.compat.v1.metrics.auc(\n                  labels=label_tensor, predictions=pred_tensor, name=name)\n              auc_head_name = \"{}_auc\".format(name)\n              print_op = tf.print(auc_head_name, auc_per_core, output_stream=sys.stderr)\n              with tf.control_dependencies([print_op]):\n                tf.compat.v1.summary.scalar(auc_head_name, tf.identity(auc_per_core))\n              train_ops.append(auc_update_op)\n            else:\n              mean_squared_error, mse_update_op = tf.compat.v1.metrics.mean_squared_error(\n                  labels=label_tensor, predictions=pred_tensor, name=name)\n              mse_head_name = \"{}_mse\".format(name)\n              print_op = tf.print(mse_head_name, mean_squared_error, output_stream=sys.stderr)\n              with tf.control_dependencies([print_op]):\n                tf.compat.v1.summary.scalar(mse_head_name, tf.identity(mean_squared_error))\n              train_ops.append(mse_update_op)\n      else:\n        targets.append(head_name)\n        labels_list.append(label)\n        preds_list.append(pred)\n\n        if not FLAGS.disable_native_metrics:\n          if is_classification:\n            auc_per_core, auc_update_op = tf.compat.v1.metrics.auc(\n                labels=label, predictions=pred, name=head_name)\n            auc_head_name = \"{}_auc\".format(head_name)\n            print_op = tf.print(auc_head_name, auc_per_core, output_stream=sys.stderr)\n            with tf.control_dependencies([print_op]):\n              tf.compat.v1.summary.scalar(auc_head_name, tf.identity(auc_per_core))\n            train_ops.append(auc_update_op)\n          else:\n            mean_squared_error, mse_update_op = tf.compat.v1.metrics.mean_squared_error(\n                labels=label, predictions=pred, name=head_name)\n            mse_head_name = \"{}_mse\".format(head_name)\n            print_op = tf.print(mse_head_name, mean_squared_error, output_stream=sys.stderr)\n            with tf.control_dependencies([print_op]):\n              tf.compat.v1.summary.scalar(mse_head_name, tf.identity(mean_squared_error))\n            train_ops.append(mse_update_op)\n\n      enable_metrics = self.metrics.enable_kafka_metrics or self.metrics.enable_file_metrics or self.metrics.enable_deep_insight\n      if enable_metrics and self.metrics.deep_insight_sample_ratio > 0:\n        model_name = self.metrics.deep_insight_name\n        sample_ratio = self.metrics.deep_insight_sample_ratio\n        extra_fields_keys = self.metrics.extra_fields_keys\n\n        dump_filename = f\"{self.metrics.dump_filename}.part-{get().worker_index:05d}\" if self.metrics.dump_filename else None\n        deep_insight_op = metric_utils.write_deep_insight(\n            features=features,\n            sample_ratio=self.metrics.deep_insight_sample_ratio,\n            labels=label,\n            preds=pred,\n            model_name=model_name or \"model_name\",\n            target=self.metrics.deep_insight_target,\n            targets=targets,\n            labels_list=labels_list,\n            preds_list=preds_list,\n            extra_fields_keys=extra_fields_keys,\n            enable_kafka_metrics=self.metrics.enable_kafka_metrics or\n            self.metrics.enable_file_metrics,\n            dump_filename=dump_filename)\n        logging.info(\"model_name: {}, target: {}.\".format(\n            model_name, self.metrics.deep_insight_target))\n        train_ops.append(deep_insight_op)\n        tf.compat.v1.add_to_collection(\"deep_insight_op\", deep_insight_op)\n        if self.metrics.enable_kafka_metrics:\n          self.add_training_hook(KafkaMetricHook(deep_insight_op))\n        elif self.metrics.enable_file_metrics:\n          self.add_training_hook(\n              FileMetricHook(deep_insight_op,\n                             worker_id=get().worker_index,\n                             parse_fn=self.metrics.parse_fn,\n                             key_fn=self.metrics.key_fn or vepfs_key_fn,\n                             layout_fn=self.metrics.layout_fn or\n                             vepfs_layout_fn,\n                             base_name=self.metrics.file_base_name,\n                             file_ext=self.metrics.file_ext))\n        logging.info(\"model_name: {}, target {}\".format(model_name, head_name))\n\n      if real_mode == tf.estimator.ModeKeys.EVAL:\n        if is_exporting() or self.output_path is None:\n          if isinstance(pred, (list, tuple)):\n            train_ops.extend(pred)\n          else:\n            train_ops.append(pred)\n          return tf.estimator.EstimatorSpec(mode,\n                                            loss=loss,\n                                            train_op=tf.group(train_ops),\n                                            training_hooks=self._training_hooks)\n        else:\n          op_file, write_op = self._get_file_ops(features, pred)\n          close_hook = file_ops.FileCloseHook([op_file])\n          with tf.control_dependencies(control_inputs=[write_op]):\n            if isinstance(pred, (list, tuple)):\n              train_ops.extend([tf.identity(p) for p in pred])\n            else:\n              train_ops.append(tf.identity(pred))\n            return tf.estimator.EstimatorSpec(mode,\n                                              loss=loss,\n                                              train_op=tf.group(train_ops),\n                                              training_hooks=[close_hook] +\n                                              self._training_hooks)\n      else:  # training\n        if hasattr(local_spec, 'optimizer'):\n          dense_optimizer = local_spec.optimizer\n        elif hasattr(self, '_default_dense_optimizer'):\n          dense_optimizer = self._default_dense_optimizer\n        else:\n          raise Exception(\"dense_optimizer not found!\")\n        dump_utils.add_optimizer(dense_optimizer)\n\n        train_ops.append(\n            feature_utils.apply_gradients_with_var_optimizer(\n                self.ctx,\n                self.fc_dict.values(),\n                dense_optimizer,\n                loss,\n                clip_type=feature_utils.GradClipType.ClipByGlobalNorm,\n                clip_norm=self.clip_norm,\n                dense_weight_decay=self.dense_weight_decay,\n                global_step=self._global_step,\n                grads_and_vars_summary=self.enable_grads_and_vars_summary,\n                sparse_norm_warmup_steps=self.sparse_norm_warmup_steps,\n                is_fused_layout=self.is_fused_layout(),\n                use_allreduce=self._use_dense_allreduce))\n        add_batch_norm_into_update_ops()\n        update_ops = tf.compat.v1.get_collection(\n            tf.compat.v1.GraphKeys.UPDATE_OPS)\n        logging.info('update_ops: %s', update_ops)\n        with tf.compat.v1.control_dependencies(update_ops):\n          train_op = tf.group(train_ops)\n\n        return tf.estimator.EstimatorSpec(mode,\n                                          loss=loss,\n                                          train_op=train_op,\n                                          training_hooks=self._training_hooks)\n\n    return model_fn_internal\n\n  def create_serving_input_receiver_fn(self):\n    \"\"\"生在Serving数据流, serving_input_receiver_fn\"\"\"\n\n    def serving_input_receiver_fn_internal():\n      with MonolithDeviceCtx(ctx_type=DeviceCtxType.INPUT_RECEIVER_FN):\n        return self.serving_input_receiver_fn()\n\n    return dump_utils.record_receiver(serving_input_receiver_fn_internal)\n\n  @abstractmethod\n  def input_fn(self, mode: tf.estimator.ModeKeys) -> DatasetV2:\n    \"\"\"抽象方法, 定义数据流\n\n    Args:\n      mode (:obj:`str`): 训练模式, train/eval/predict等\n\n    Returns:\n      DatasetV2, TF数据集\n\n    \"\"\"\n\n    raise NotImplementedError('input_fn() not Implemented')\n\n  @abstractmethod\n  def model_fn(\n      self, features: Dict[str, tf.Tensor], mode: tf.estimator.ModeKeys\n  ) -> Union[EstimatorSpec, Tuple[tf.Tensor, tf.Tensor, tf.Tensor]]:\n    \"\"\"抽象方法, 定义模型\n\n    Args:\n      features (:obj:`Dict[str, tf.Tensor]`): 特征\n      mode (:obj:`str`): 训练模式, train/eval/predict等\n\n    Returns:\n      Union[EstimatorSpec, Tuple[tf.Tensor, tf.Tensor, tf.Tensor]], 可以是tuple, 包括(loss, label, predict),\n                                                                    也可以是EstimatorSpec\n    \"\"\"\n\n    raise NotImplementedError('generate_model() not Implemented')\n\n  @abstractmethod\n  def serving_input_receiver_fn(self) -> ServingInputReceiver:\n    \"\"\"Serving数据流, 训练数据流与Serving数据流或能不一样\n\n    Returns:\n      ServingInputReceiver\n\n    \"\"\"\n\n    raise NotImplementedError('serving_input_receiver_fn() not Implemented')\n\n  @property\n  def _export_outputs(self):\n    graph = tf.compat.v1.get_default_graph()\n    if hasattr(graph, '__export_outputs'):\n      return getattr(graph, '__export_outputs')\n    else:\n      setattr(graph, '__export_outputs', {})\n      return graph.__export_outputs\n\n  def add_extra_output(self,\n                       name: str,\n                       outputs: Union[tf.Tensor, Dict[str, tf.Tensor]],\n                       head_name: str = None,\n                       head_type: str = None):\n    \"\"\"如果有出多输出, 可以用add_extra_output, 每个输出会成为Serving中的一个Signature\n\n    Args:\n      name (:obj:`str`): 签名的名称\n      outputs (:obj:`Union[tf.Tensor, Dict[str, tf.Tensor]]`): 输出, 可以是一个Tensor, 也可以是一个Dict[str, tf.Tensor]\n      head_name (:obj:`str`): output对应的head的名称\n      head_name (:obj:`str`): output对应的head的类型, 如user, item, context等\n\n    \"\"\"\n\n    add_to_collections('signature_name', name)\n    if is_exporting():\n      exported_outputs = self._export_outputs\n      if name not in exported_outputs:\n        exported_outputs[name] = tf.estimator.export.PredictOutput(outputs)\n      else:\n        raise KeyError(\"key {name} exists!\".format(name=name))\n\n  def add_training_hook(self, hook):\n    if isinstance(hook, KafkaMetricHook):\n      if any(isinstance(h, KafkaMetricHook) for h in self._training_hooks):\n        return\n    elif isinstance(hook, FileMetricHook):\n      if any(isinstance(h, FileMetricHook) for h in self._training_hooks):\n        return\n    self._training_hooks.append(hook)\n\n  def add_layout(self, name: str, slice_list: list, out_type: str,\n                 shape_list: list):\n    if out_type == 'concat':\n      out_conf = OutConfig(out_type=OutType.CONCAT)\n    elif out_type == 'stack':\n      out_conf = OutConfig(out_type=OutType.STACK)\n    elif out_type == 'addn':\n      out_conf = OutConfig(out_type=OutType.ADDN)\n    else:\n      out_conf = OutConfig(out_type=OutType.NONE)\n\n    for feature_name, slice_conf in slice_list:\n      slice_config = out_conf.slice_configs.add()\n      slice_config.feature_name = feature_name\n      slice_config.start = slice_conf.start\n      slice_config.end = slice_conf.end\n\n    for shape in shape_list:\n      shape_dims = out_conf.shape.add()\n      for i, dim in enumerate(shape):\n        if i == 0:\n          shape_dims.dims.append(-1)\n        else:\n          if isinstance(dim, int):\n            shape_dims.dims.append(dim)\n          else:\n            assert hasattr(dim, 'value')\n            shape_dims.dims.append(dim.value)\n\n    self._layout_dict[name] = out_conf\n\n  @property\n  def layout_dict(self):\n    return self._layout_dict\n\n  @layout_dict.setter\n  def layout_dict(self, layouts):\n    self._layout_dict = layouts\n\n\n@monolith_export\nclass MonolithModel(MonolithBaseModel):\n  '''模型开发的基类\n\n  Args:\n      params (:obj:`Params`): 配置参数, 默认为None\n  '''\n\n  @classmethod\n  def params(cls):\n    p = super(MonolithModel, cls).params()\n    p.define(\"feature_list\", None, \"The feature_list conf file.\")\n    return p\n\n  def __init__(self, params=None):\n    params = params or type(self).params()\n    super(MonolithModel, self).__init__(params)\n    dump_utils.enable = FLAGS.enable_model_dump\n\n  def _get_fs_conf(self, shared_name: str, slot: int, occurrence_threshold: int,\n                   expire_time: int) -> FeatureSlotConfig:\n    return FeatureSlotConfig(\n        name=shared_name,\n        has_bias=False,\n        slot_id=slot,\n        occurrence_threshold=occurrence_threshold,\n        expire_time=expire_time,\n        hashtable_config=entry.GpucucoHashTableConfig()\n        if self.p.train.use_gpu_emb_table else entry.CuckooHashTableConfig())\n\n  def _embedding_slice_lookup(self, fc: Union[str, FeatureColumn],\n                              slice_name: str, slice_dim: int,\n                              initializer: Initializer, optimizer: Optimizer,\n                              compressor: Compressor, learning_rate_fn,\n                              slice_list: list) -> FeatureSlice:\n    assert not self.is_fused_layout()\n    if isinstance(fc, str):\n      fc = self.fc_dict[fc]\n\n    feature_slot = fc.feature_slot\n    feature_name = self._share_slot_mapping.get(\n      fc.feature_name, fc.feature_name)\n\n    if feature_name in self.slice_dict:\n      if slice_name in self.slice_dict[feature_name]:\n        fc_slice = self.slice_dict[feature_name][slice_name]\n      else:\n        fc_slice = feature_slot.add_feature_slice(slice_dim, initializer,\n                                                  optimizer, compressor,\n                                                  learning_rate_fn)\n        self.slice_dict[feature_name][slice_name] = fc_slice\n    else:\n      fc_slice = feature_slot.add_feature_slice(slice_dim, initializer,\n                                                optimizer, compressor,\n                                                learning_rate_fn)\n      self.slice_dict[feature_name] = {slice_name: fc_slice}\n\n    slice_list.append((fc.feature_name, fc_slice))\n    return fc.embedding_lookup(fc_slice)\n\n  @dump_utils.record_feature\n  def create_embedding_feature_column(self,\n                                      feature_name,\n                                      occurrence_threshold: int = None,\n                                      expire_time: int = 36500,\n                                      max_seq_length: int = 0,\n                                      shared_name: str = None,\n                                      combiner: str = None) -> FeatureColumn:\n    \"\"\"创建嵌入特征列(embedding feature column)\n\n    Args:\n      feature_name (:obj:`Any`): 特征列的名字\n      occurrence_threshold (:obj:`int`): 用于低频特征过滤, 如果出现次数小于`occurrence_threshold`, 则这个特征将大概率不会进入模型\n      expire_time (:obj:`int`): 特征过期时间, 如果一个特征在`expire_time`之内没有更新了, 则这个特征可能从hash表中移除\n      max_seq_length (:obj:`int`): 如果设为0, 表示非序列特征, 如果设为正数, 则表示序列特征的长度\n      shared_name (:obj:`str`): 共享embedding. 如果本feature与另一个feature共享embedding, 则可以将被共享feature设为`shared_name`\n\n    Returns:\n     FeatureColumn, 特征列\n\n    \"\"\"\n\n    if combiner and isinstance(combiner, str):\n      assert combiner in {'reduce_sum', 'reduce_mean', 'first_n'}\n      if combiner == 'reduce_sum':\n        combiner = FeatureColumn.reduce_sum()\n      elif combiner == 'reduce_mean':\n        combiner = FeatureColumn.reduce_mean()\n      else:\n        combiner = FeatureColumn.first_n()\n    feature_name, slot = get_feature_name_and_slot(feature_name)\n\n    if feature_name in self.fc_dict:\n      return self.fc_dict[feature_name]\n    else:\n      if shared_name is not None and len(shared_name) > 0:\n        self._share_slot_mapping[feature_name] = shared_name\n        if shared_name in self.fs_dict:\n          fs = self.fs_dict[shared_name]\n        elif shared_name in self.fc_dict:\n          fs = self.fc_dict[shared_name].feature_slot\n        else:\n          try:\n            shared_name, shared_slot = get_feature_name_and_slot(shared_name)\n            shared_fs = self.ctx.create_feature_slot(\n                self._get_fs_conf(shared_name, shared_slot,\n                                  occurrence_threshold, expire_time))\n            self.fs_dict[shared_name] = shared_fs\n            fs = shared_fs\n          except:\n            raise Exception(\n                f\"{feature_name} shared embedding with {shared_name}, so {shared_name} should create first!\"\n            )\n      else:\n        fs = self.ctx.create_feature_slot(\n            self._get_fs_conf(feature_name, slot, occurrence_threshold,\n                              expire_time))\n      if combiner is None:\n        if max_seq_length > 0:\n          combiner = FeatureColumn.first_n(max_seq_length)\n        else:\n          combiner = FeatureColumn.reduce_sum()\n      fc = FeatureColumn(fs, feature_name, combiner=combiner)\n      self.fc_dict[feature_name] = fc\n      return fc\n\n  @dump_utils.record_slice\n  def lookup_embedding_slice(self,\n                             features,\n                             slice_name,\n                             slice_dim=None,\n                             initializer: Initializer = None,\n                             optimizer: Optimizer = None,\n                             compressor: Compressor = None,\n                             learning_rate_fn=None,\n                             group_out_type: str = 'add_n',\n                             out_type: str = None) -> tf.Tensor:\n    \"\"\"Monolith中embedding是分切片的, 每个切片可以有独立的初始化器, 优化器, 压缩器, 学习率等. 切片的引入使Embedding更加强大. 如某些情况\n    下要共享Embedding, 另一些情况下要独立Embedding, 与一些域交叉要用一种Embedding, 与另一些域交叉用另一种Embedding等. 切片的引入可以方便\n    解上以上问题. 切片与完整Embedding的关系由Monolith自动维护, 对用户透明.\n\n    Args:\n      slice_name (:obj:`str`): 切片名称\n      features (:obj:`List[str], Dict[str, int]`): 支持三种形式\n        1) 特征名列表, 此时每个切片的长度相同, 由`slice_dim`确定, 不能为None\n        2) 特征 (特征名, 切片长度) 列表, 此时每个切片的长度可以不同, 全局的`slice_dim`必须为None\n        3) 特征字典, 特征名 -> 切片长度, 此时每个切片的长度可以不同, 全局的`slice_dim`必须为None\n      slice_dim (:obj:`int`): 切片长度\n      initializer (:obj:`Initializer`): 切片的初始化器, Monolith中的初始化器,  不能是TF中的\n      optimizer (:obj:`Optimizer`): 切片的优化器, Monolith中的优化器,  不能是TF中的\n      compressor (:obj:`Compressor`): 切片的压缩器, 用于在Servering模型加载时将模型压缩\n      learning_rate_fn (:obj:`tf.Tensor`): 切片的学习率\n\n    \"\"\"\n    concat = \",\".join(sorted(map(str, features)))\n    layout_name = f'{slice_name}_{hashlib.md5(concat.encode()).hexdigest()}'\n    if self.is_fused_layout():\n      if isinstance(features, (list, tuple)) and isinstance(slice_dim, int):\n        if all(isinstance(ele, (tuple, list)) for ele in features):\n          raise ValueError(\"group pool is not support when fused_layout\")\n      return self.ctx.layout_factory.get_layout(layout_name)\n\n    feature_embeddings, slice_list = [], []\n    if isinstance(features, dict):\n      for fc_name, sdim in features.items():\n        fc_name, _ = get_feature_name_and_slot(fc_name)\n        feature_embeddings.append(\n            self._embedding_slice_lookup(fc_name, slice_name, sdim, initializer,\n                                         optimizer, compressor,\n                                         learning_rate_fn, slice_list))\n    elif isinstance(features, (list, tuple)) and isinstance(slice_dim, int):\n      if all(isinstance(ele, (str, int, FeatureColumn)) for ele in features):\n        # a list of feature with fixed dim\n        for fc_name in features:\n          fc_name, _ = get_feature_name_and_slot(fc_name)\n          feature_embeddings.append(\n              self._embedding_slice_lookup(fc_name, slice_name, slice_dim,\n                                           initializer, optimizer, compressor,\n                                           learning_rate_fn, slice_list))\n      elif all(isinstance(ele, (tuple, list)) for ele in features):\n        assert group_out_type in {'concat', 'add_n'}\n        for group_name in features:\n          assert all(isinstance(ele, int) for ele in group_name)\n          local_embeddings = []\n          for fc_name in group_name:\n            fc_name, _ = get_feature_name_and_slot(fc_name)\n            local_embeddings.append(\n                self._embedding_slice_lookup(fc_name, slice_name, slice_dim,\n                                             initializer, optimizer, compressor,\n                                             learning_rate_fn, slice_list))\n          if group_out_type == 'add_n':\n            feature_embeddings.append(tf.add_n(local_embeddings))\n          else:\n            feature_embeddings.append(tf.concat(local_embeddings, axis=1))\n      else:\n        raise ValueError(\"ValueError for features\")\n    elif isinstance(features, (list, tuple)):\n      if all([\n          isinstance(ele, (tuple, list)) and len(ele) == 2 for ele in features\n      ]):\n        for fc_name, sdim in features:\n          fc_name, _ = get_feature_name_and_slot(fc_name)\n          feature_embeddings.append(\n              self._embedding_slice_lookup(fc_name, slice_name, sdim,\n                                           initializer, optimizer, compressor,\n                                           learning_rate_fn, slice_list))\n      else:\n        raise ValueError(\"ValueError for features\")\n    else:\n      raise ValueError(\"ValueError for features\")\n\n    if out_type is None:\n      shape_list = [emb.shape for emb in feature_embeddings]\n      self.add_layout(layout_name, slice_list, out_type, shape_list)\n      return feature_embeddings\n    else:\n      assert out_type in {'concat', 'stack', 'add_n', 'addn'}\n      if out_type == 'concat':\n        out = tf.concat(feature_embeddings, axis=1, name=layout_name)\n        self.add_layout(layout_name,\n                        slice_list,\n                        out_type,\n                        shape_list=[out.shape])\n        return out\n      elif out_type == 'stack':\n        out = tf.stack(feature_embeddings, axis=1, name=layout_name)\n        self.add_layout(layout_name,\n                        slice_list,\n                        out_type,\n                        shape_list=[out.shape])\n        return out\n      else:\n        out = tf.add_n(feature_embeddings, name=layout_name)\n        self.add_layout(layout_name, slice_list, 'addn', shape_list=[out.shape])\n        return out\n\n  def share_slot(self,\n                 features: Union[tf.Tensor, Dict[str, tf.RaggedTensor]] = None,\n                 share_meta: Dict[str, Tuple[bool, int]] = None,\n                 variant_type: str = 'example',\n                 suffix: str = 'share'):\n    for name, (inplace, slot) in share_meta.items():\n      shared_name = f'{name}_{suffix}'\n      if not inplace:\n        register_slots({shared_name: slot})\n      else:\n        register_slots({name: slot})\n\n    if features is not None and isinstance(features, dict):\n      for name, (inplace, slot) in share_meta.items():\n        if inplace:\n          features[name] = switch_slot(features[name], slot)\n        else:\n          features[shared_name] = switch_slot(features[name], slot)\n      return features\n    else:\n      map_fn = lambda tensor: switch_slot_batch(tensor, share_meta, \n        variant_type=variant_type, suffix=suffix)\n      return map_fn\n\n"
  },
  {
    "path": "monolith/native_training/native_task.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport abc\nimport dataclasses\nfrom typing import Any, Callable, Dict, Iterable, Tuple, Union\n\nimport tensorflow as tf\n\nfrom monolith.core import hyperparams\nfrom monolith.core.base_task import BaseTask\nfrom monolith.native_training import feature\nfrom monolith.native_training import prefetch_queue\nfrom monolith.native_training.model_export.export_context import ExportMode\nfrom idl.matrix.proto.example_pb2 import OutConfig, OutType, TensorShape\n\n\nclass NativeContext:\n  \"\"\"Provides the context of the NativeTask.\"\"\"\n\n  def __init__(self,\n               feature_factory: feature.FeatureFactory = None,\n               async_function_mgr: prefetch_queue.AsyncFunctionMgr = None,\n               layout_factory: feature.EmbeddingLayoutFactory = None):\n    self.feature_factory = feature_factory\n    self.async_function_mgr = async_function_mgr\n    self.layout_factory = layout_factory\n    if layout_factory and feature_factory:\n      raise ValueError(\n          \"Cannot set feature_factory and layout_factory in the same time\")\n\n  # Provides some convinient functions\n\n  def create_feature_slot(\n      self, config: feature.FeatureSlotConfig) -> feature.FeatureSlot:\n    \"\"\"Creates a feature slot.\"\"\"\n    # No TensorFlow op is created at this function call.\n    if self.layout_factory:\n      return self.layout_factory.create_feature_slot(config)\n    else:\n      return self.feature_factory.create_feature_slot(config)\n\n  def apply_embedding_gradients(self,\n                                grads_and_vars: Iterable[Tuple[tf.Tensor,\n                                                               tf.Tensor]],\n                                scale=1):\n    \"\"\"\n    Apply gradients for embeddings. Notice vars must be coming from FeatureColumn's\n    get_all_embeddings_concatenated.\n    \"\"\"\n    if self.layout_factory:\n      return self.layout_factory.apply_gradients(grads_and_vars)\n    else:\n      return self.feature_factory.apply_gradients(grads_and_vars, scale=scale)\n\n  def add_async_function(\n      self,\n      target: Callable,\n      args: Tuple = None,\n      kwargs: Dict = None,\n      is_async: bool = None,\n      queue_name: str = \"async_queue\") -> Union[tf.Operation, Any]:\n    \"\"\"Adds async func.\n    Returns an enqueue op if is_async. Otherwise, returns calling result of target.\n\n    Args:\n      is_async - if not specified, will use default value in async_function_mgr.\n\n    Requirements: \n    - target should return ops/tensors which can be added to session.run\n    All tensors used by |async_function| should *ONLY* come from arguments passed in.\n    Otherwise, we may use updated value in the async function.\n    TODO(leqi.zou): Adds a check for this.\"\"\"\n    return self.async_function_mgr.add_async_function(target,\n                                                      args,\n                                                      kwargs,\n                                                      is_async=is_async,\n                                                      queue_name=queue_name)\n\n\nclass NativeTask(BaseTask, abc.ABC):\n  \"\"\"\n  A task is supported to be train/eval/serving in multiple devices with native tensorflow\n  code.\n  \"\"\"\n\n  @classmethod\n  def params(cls):\n    p = super(NativeTask, cls).params()\n    # metrics\n    p.define(\"metrics\", hyperparams.Params(), \"Metric parameters.\")\n    p.metrics.define(\"enable_deep_insight\", False,\n                     'Whether enable deep insight.')\n    p.metrics.define(\"deep_insight_target\", \"ctr_head\", \"Deep insight target.\")\n    p.metrics.define('deep_insight_name', None, 'str')\n    p.metrics.define('deep_insight_sample_ratio', 0.01, 'float')\n    p.metrics.define('extra_fields_keys', [],\n                     'extra_fields_keys for deepinsight, List[str]')\n\n    # [todo] (fitz) the mode will remove when the estimator is ready\n    p.define(\"mode\", tf.estimator.ModeKeys.TRAIN, \"run mode\")\n\n    p.metrics.define(\"enable_throughput_hook\", True,\n                     \"If enables throughput hook.\")\n    p.metrics.define(\"enable_kafka_metrics\", False, \"enable_kafka_metrics\")\n    p.metrics.define(\n        \"enable_tf2_profiler_hook\", True,\n        \"If enables tf profiler hook. When enabled, remeber to increase worker's memory.\"\n    )\n\n    p.metrics.define(\"enable_file_metrics\", False, \"enable_file_metrics\")\n    p.metrics.define(\"file_base_name\", '/vepfs/jaguar_deepinsight_results',\n                     \"file_base_name\")\n    p.metrics.define(\"file_ext\", 'txt', \"file_ext\")\n    p.metrics.define(\"parse_fn\", None, \"parse_fn\")\n    p.metrics.define(\"key_fn\", None, \"key_fn\")\n    p.metrics.define(\"layout_fn\", None, \"layout_fn\")\n    p.metrics.define(\"dump_filename\", '', \"Dump filename\")\n    p.metrics.define('use_data_service', False, \"use data service\")\n\n    p.train.define(\n        'max_pending_seconds_for_barrier', 30,\n        'Maximum waiting time for barrier block. Used for testing in most cases.'\n    )\n    p.train.define(\n        \"slow_start_steps\", 0,\n        (\"How many steps will worker wait before they start to train.\"\n         \" The formula of wait is `slow_start_steps * log(1 + index)`\"))\n    p.train.define(\n        \"sample_bias\", 0.,\n        \"Sample bias is a float scalar which acts as compensation for ads \"\n        \"realtime training (FastEmit training instance).\")\n    p.train.define(\"use_gpu_emb_table\", False,\n                   \"Use GPU embedding table for sync training if enabled.\")\n    p.train.define(\"use_fountain\", False,\n                   \"Use fountain data service if enabled.\")\n    p.train.define(\"fountain_zk_host\", \"\", \"zk_host for fountain service.\")\n    p.train.define(\"fountain_model_name\", \"\",\n                   \"model_name for fountain service.\")\n    p.train.define(\"fountain_parse_on_server\", False,\n                   \"Parsing logic on fountain server.\")\n    p.train.define(\"fountain_precompute_value_rowids\", False,\n                   \"Parsing logic on fountain server.\")\n\n    p.define(\"serving\", hyperparams.Params(), \"Serving parameters.\")\n    p.serving.define(\n        \"export_with_gpu_allowed\", False,\n        \"When true it allows cpu/gpu training to export model graph \"\n        \"with specified gpu device placement contexts.\")\n    p.serving.define(\n        \"export_with_cleared_entry_devices\", False,\n        \"When true it clears the devices in the exported model graph\"\n        \"for entry only at DistributedExporter Mode.\")\n    p.serving.define(\n        \"export_when_saving\", False,\n        \"When true, a valid create_serving_input_fn must be provided. The \"\n        \"framework will do export when saving. \")\n    p.serving.define(\n        \"export_dir_base\", \"exported_models\",\n        \"The base dir (either relative to model_dir or an absolute path) When \"\n        \"exporting models.\")\n    p.serving.define(\"export_mode\", ExportMode.DISTRIBUTED,\n                     \"standalone or distributed.\")\n    p.serving.define(\n        \"shared_embedding\", True,\n        \"If true, instead of exporting a hermetic SavedModel, we will use the \"\n        \"embedding in checkpoints instead of copying it.\")\n    p.serving.define(\"with_remote_gpu\", False,\n                     \"If true, the whole dense will be put on the GPU.\")\n\n    return p\n\n  def __init__(self, params):\n    super().__init__(params)\n    self._ctx = NativeContext()\n    self.p = params\n\n  @property\n  def ctx(self) -> NativeContext:\n    \"\"\"Returns task ctx.\"\"\"\n    return self._ctx\n\n  @abc.abstractmethod\n  def create_input_fn(self, mode):\n    \"\"\"\n    Same as BaseTask.create_input_fn\n    \"\"\"\n\n  @abc.abstractmethod\n  def create_model_fn(self):\n    \"\"\"\n    For the child class, returned model_fn must follow the signature of\n    (features, mode, config) -> SomeEstimatorSpec\n    \"\"\"\n\n  def create_serving_input_receiver_fn(self):\n    \"\"\"Returns a serving input fn for serving. \n    See https://www.tensorflow.org/api_docs/python/tf/estimator/Estimator#export_saved_model\n    for the possible return values for this method.\n    By default, None is provided (which is invalid if we enable serving).\n    \"\"\"\n    return None\n"
  },
  {
    "path": "monolith/native_training/native_task_context.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport contextlib\n\nfrom typing import NamedTuple\nfrom monolith.agent_service.backends import SyncBackend\n\n\nclass NativeTaskContext(NamedTuple):\n  num_ps: int\n  ps_index: int\n  num_workers: int\n  worker_index: int\n  # Model name is used to uniquely identify a model\n  # It will influence how we export models and do the serving.\n  model_name: str\n  sync_backend: SyncBackend\n  server_type: str\n\n\n_CTX = None\n\n\n@contextlib.contextmanager\ndef with_ctx(ctx: NativeTaskContext):\n  global _CTX\n  old_ctx = _CTX\n  _CTX = ctx\n  try:\n    yield\n  finally:\n    if old_ctx is not None:\n      _CTX = old_ctx\n\n\ndef get():\n  if _CTX is None:\n    return NativeTaskContext(num_ps=0,\n                             ps_index=0,\n                             num_workers=1,\n                             worker_index=0,\n                             server_type=\"\",\n                             model_name=\"\",\n                             sync_backend=None)\n  else:\n    return _CTX\n"
  },
  {
    "path": "monolith/native_training/nested_tensors.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport copy\nimport itertools\nfrom typing import List\n\nimport tensorflow as tf\n\n\ndef _iterate(nested, action):\n  \"\"\"Iterate nested structures. `action` should take element and returns a element.\"\"\"\n  if nested is None:\n    pass\n  elif isinstance(nested, (list, tuple)):\n    r = []\n    for v in nested:\n      r.append(_iterate(v, action))\n    if isinstance(nested, tuple):\n      r = tuple(r)\n    nested = r\n  elif isinstance(nested, dict):\n    for k, v in nested.items():\n      nested[k] = _iterate(nested[k], action)\n  else:\n    nested = action(nested)\n\n  return nested\n\n\nclass NestedTensors:\n\n  def __init__(self, nested):\n    self._nested = nested\n    self._id_mapping = {}\n    self._ragged_tensors = []\n    self._tensors = []\n    self._other_objs = []\n    self._nested = _iterate(self._nested, self._add_tensor)\n\n  def _add_tensor(self, tensor):\n    obj_id = id(tensor)\n    if not obj_id in self._id_mapping:\n      if isinstance(tensor, tf.Tensor):\n        self._id_mapping[obj_id] = (0, len(self._tensors))\n        self._tensors.append(tensor)\n      elif isinstance(tensor, tf.RaggedTensor):\n        if tensor.ragged_rank != 1:\n          raise ValueError(\"Nested tensor doesn't support nested RaggedTensor.\")\n        self._id_mapping[obj_id] = (1, len(self._ragged_tensors))\n        self._ragged_tensors.append(tensor)\n      elif isinstance(tensor, (bool, int, str, tf.Variable, None)):\n        # There are some cases we want to keep it as it is.\n        self._id_mapping[obj_id] = (2, len(self._other_objs))\n        self._other_objs.append(tensor)\n      else:\n        raise ValueError(\"Tensor is not supported. {}\".format(tensor))\n\n    return obj_id\n\n  def get_tensors(self) -> List[tf.Tensor]:\n    flatten_ragged_tensors = self._ragged_to_flatten(self._ragged_tensors)\n    return self._tensors + flatten_ragged_tensors\n\n  def get_nested_result(self, tensors: List[tf.Tensor]):\n    flatten_ragged_tensors = tensors[len(self._tensors):]\n    tensors = tensors[:len(self._tensors)]\n    assert len(flatten_ragged_tensors) == len(self._ragged_tensors) * 2\n    ragged_tensors = self._flatten_to_ragged(flatten_ragged_tensors)\n\n    tensor_tuple = (tensors, ragged_tensors, self._other_objs)\n    result = copy.deepcopy(self._nested)\n\n    def action(obj_id):\n      idx = self._id_mapping[obj_id]\n      return tensor_tuple[idx[0]][idx[1]]\n\n    return _iterate(result, action)\n\n  @staticmethod\n  def _convert_ragged_to_tensors(ragged):\n    return ragged.values, ragged.row_splits\n\n  @staticmethod\n  def _convert_tensors_to_ragged(values, row_splits):\n    return tf.RaggedTensor.from_row_splits(values, row_splits, validate=False)\n\n  def _ragged_to_flatten(self, ragged_tensors):\n    return list(\n        itertools.chain.from_iterable((self._convert_ragged_to_tensors(ragged)\n                                       for ragged in ragged_tensors)))\n\n  def _flatten_to_ragged(self, tensors):\n    ragged_values = tensors[::2]\n    ragged_row_splits = tensors[1::2]\n    return [\n        self._convert_tensors_to_ragged(*combined)\n        for combined in zip(ragged_values, ragged_row_splits)\n    ]"
  },
  {
    "path": "monolith/native_training/nested_tensors_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training import nested_tensors\n\n\nclass NestedTensorTest(tf.test.TestCase):\n\n  def testBasic(self):\n    n = nested_tensors.NestedTensors({\n        \"a\": tf.ones([]),\n        \"b\": (tf.ones([]), tf.ones([])),\n    })\n    tensors = n.get_tensors()\n    replaced = [tf.zeros_like(tensor) for tensor in tensors]\n    result = n.get_nested_result(replaced)\n    result = self.evaluate(result)\n    self.assertDictEqual(result, {\"a\": 0, \"b\": (0, 0)})\n\n  def testConstant(self):\n    n = nested_tensors.NestedTensors({\"a\": {\"b\": 2}})\n    tensors = n.get_tensors()\n    self.assertLen(tensors, 0)\n    result = n.get_nested_result([])\n    self.assertDictEqual(result, {\"a\": {\"b\": 2}})\n\n  def testRaggedTensor(self):\n\n    n = nested_tensors.NestedTensors(tf.ragged.constant([[], [1], [2, 3]]))\n    tensors = n.get_tensors()\n    result = n.get_nested_result(tensors)\n    self.assertAllEqual(result, [[], [1], [2, 3]])\n\n  def testRaggedTensorWithPlaceHolder(self):\n\n    n = nested_tensors.NestedTensors(tf.ragged.constant([[], [1], [2, 3]]))\n    tensors = n.get_tensors()\n    phs = [tf.compat.v1.placeholder(dtype=t.dtype) for t in tensors]\n    result = n.get_nested_result(tensors)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/net_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport os\nfrom queue import Queue, Empty\nimport socket\nimport threading\nfrom typing import Dict, List\nimport ipaddress\n\n\nclass NodeAliveChecker:\n\n  def __init__(self, addrs: List, timeout: int = 1, num_thread: int = 10):\n    self._addrs = addrs\n    self._timeout = timeout\n    self._num_thread = num_thread\n\n    self._lock = threading.Lock()\n    self._alive = set()\n    self._dead = set()\n\n    self._q = Queue()\n    for addr in self._addrs:\n      self._q.put(addr)\n    self._start()\n\n  def _ping(self, addr):\n    skt = None\n    try:\n      ip, port = addr.rsplit(':', 1)\n      ip = ip.strip('[]')\n      is_ipv6 = is_ipv6_address(ip)\n      skt = socket.socket(socket.AF_INET6 if is_ipv6 else socket.AF_INET,\n                          socket.SOCK_STREAM)\n      skt.settimeout(self._timeout)\n\n      skt.connect((ip, int(port)))\n      with self._lock:\n        self._alive.add(addr)\n    except Exception as err:\n      print(\"cannot connect to {}, because {}\".format(addr, err))\n      with self._lock:\n        self._dead.add(addr)\n    finally:\n      if skt:\n        skt.close()\n\n  def _check_open(self):\n    try:\n      while True:\n        addr = self._q.get_nowait()\n        self._ping(addr)\n    except Empty as err:\n      pass\n\n  def _start(self):\n    threads = []\n    for i in range(self._num_thread):\n      t = threading.Thread(target=self._check_open)\n      t.start()\n      threads.append(t)\n\n    for t in threads:\n      t.join()\n\n  def all_nodes_alive(self):\n    with self._lock:\n      return len(self._dead) == 0\n\n  def get_dead_nodes(self):\n    with self._lock:\n      return list(self._dead)\n\n  def get_alive_nodes(self):\n    with self._lock:\n      return list(self._alive)\n\n  def get_addrs(self):\n    with self._lock:\n      return self._addrs\n\n\ndef is_ipv6_address(ip: str):\n  try:\n    ip_obj = ipaddress.ip_address(ip)\n  except ValueError:\n    return False\n  return ip_obj.version == 6\n\n\ndef concat_ip_and_port(ip: str, port: int):\n  if not is_ipv6_address(ip):\n    return f\"{ip}:{port}\"\n  else:\n    return f\"[{ip}]:{port}\"\n\n\ndef get_local_ip():\n  try:\n    return socket.getaddrinfo(socket.gethostname(), None)[0][4][0]\n  except socket.gaierror:\n    return socket.getaddrinfo(socket.gethostname(),\n                              None,\n                              family=socket.AF_INET6)[0][4][0]\n\n\ndef is_ipv4_supported():\n  return not is_ipv6_address(get_local_ip())\n\n\ndef get_local_server_addr(port: int):\n  \"\"\"Given a port. Returns an addr.\n  In the machine that supports IPv4, it is equivalent to gethostbyname(gethostname()).\n  \"\"\"\n  return concat_ip_and_port(get_local_ip(), port)\n\n\nclass AddressFamily(object):\n  IPV4 = 'ipv4'\n  IPV6 = 'ipv6'\n"
  },
  {
    "path": "monolith/native_training/net_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport unittest\nfrom unittest import mock\nimport random\nimport time\n\nfrom monolith.native_training import net_utils\n\n_SOCKET = 'monolith.native_training.net_utils.socket.socket'\n_FAILED_TIME = 0\n_DEAD_SET = set()\n\n\nclass socket:\n  AF_INET = -1\n  SOCK_STREAM = -1\n\n  def __init__(self, family=-1, stype=-1):\n    self._family = family\n    self._stype = stype\n    self._timeout = 1\n\n  def settimeout(self, timeout):\n    self._timeout = timeout\n\n  def connect(self, addr):\n    ip, port = addr\n    sleep = random.uniform(0, 2 * self._timeout)\n    time.sleep(sleep)\n    print('sleep {}, connect to {}:{}'.format(sleep, ip, port))\n    if sleep > self._timeout:\n      global _FAILED_TIME\n      global _DEAD_SET\n      _FAILED_TIME += 1\n      tmp_add = ':'.join([ip, str(port)])\n      _DEAD_SET.add(tmp_add)\n      raise RuntimeError('{}:{} connect error'.format(ip, port))\n\n  def close(self):\n    pass\n\n  @classmethod\n  def socket(cls, family, stype):\n    return socket(family, stype)\n\n\nclass NetUtilsTest(unittest.TestCase):\n\n  def test_basic(self):\n    with mock.patch(_SOCKET) as tmp_socket:\n      tmp_socket.return_value = socket()\n\n      addrs = [\n          'localhost:1233', 'localhost:1234', 'localhost:1235',\n          'localhost:1236', 'localhost:1238'\n      ]\n      alive_checker = net_utils.NodeAliveChecker(addrs)\n      self.assertEqual(set(alive_checker.get_addrs()), set(addrs))\n\n      self.assertEqual(len(alive_checker.get_alive_nodes()), 5 - _FAILED_TIME)\n      self.assertEqual(len(alive_checker.get_dead_nodes()), _FAILED_TIME)\n      self.assertEqual(set(alive_checker.get_alive_nodes()),\n                       set(addrs) - _DEAD_SET)\n      self.assertEqual(set(alive_checker.get_dead_nodes()), _DEAD_SET)\n\n      self.assertEqual(alive_checker.all_nodes_alive(), _FAILED_TIME == 0)\n\n  def test_concat_ip_and_port(self):\n    self.assertEqual(net_utils.concat_ip_and_port(\"localhost\", 10),\n                     \"localhost:10\")\n    self.assertEqual(net_utils.concat_ip_and_port(\"127.0.0.1\", 10),\n                     \"127.0.0.1:10\")\n    self.assertEqual(net_utils.concat_ip_and_port(\"::1\", 10), \"[::1]:10\")\n\n  def test_get_local_server_addr(self):\n    self.assertIsNotNone(net_utils.get_local_server_addr(10))\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/optimizers/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_cc_test\", \"tf_custom_op_library\", \"tf_kernel_library\")\n\npackage(\n    default_visibility = [\"//visibility:public\"],\n)\n\n\ntf_kernel_library(\n    name = \"training_ops\",\n    srcs = [\n        \"cc/kernels/training_op_helpers.h\",\n        \"cc/kernels/training_ops.h\",\n        \"cc/kernels/training_ops.cc\",\n        \"cc/training_ops.cc\",\n    ],\n    gpu_srcs = [\n        \"cc/kernels/training_op_helpers.h\",\n        \"cc/kernels/training_ops.h\",\n        \"cc/kernels/training_ops_gpu.cu.cc\",\n    ],\n    deps = [\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:gpu_device_array_for_custom_op\",\n    ],\n    alwayslink = 1,\n)\n\npy_library(\n    name = \"adamom\",\n    srcs = [\"adamom.py\"],\n    deps = [\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"adamom_test\",\n    srcs = [\"adamom_test.py\"],\n    deps = [\n        \":adamom\",\n    ],\n)\n\npy_library(\n    name = \"shampoo\",\n    srcs = [\"shampoo.py\"],\n    deps = [\n        \"//monolith:utils\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\npy_library(\n    name = \"rmsprop\",\n    srcs = [\"rmsprop.py\"],\n    deps = [\n        \"//monolith/native_training/runtime/ops:gen_monolith_ops\",\n    ],\n)\n\npy_test(\n    name = \"rmsprop_test\",\n    srcs = [\"rmsprop_test.py\"],\n    deps = [\n        \":rmsprop\",\n    ],\n)\n\npy_test(\n    name = \"rmspropv2_test\",\n    srcs = [\"rmspropv2_test.py\"],\n    deps = [\n        \":rmsprop\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/optimizers/adamom.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\ntraining_ops = gen_monolith_ops\n\n\nclass AdamomOptimizer(tf.compat.v1.train.Optimizer):\n  def __init__(self,\n               learning_rate=5e-6,\n               ada_decay: float = 0.9999,\n               mom_decay: float = 0.99,\n               epsilon: float = 1e-6,\n               weight_decay: float = 0.0,\n               use_locking: bool = False,\n               name=\"Adamom\"):\n    super().__init__(use_locking, name)\n    self._learning_rate = learning_rate\n    self._ada_decay = ada_decay\n    self._mom_decay = mom_decay\n    self._epsilon = epsilon\n    self._weight_decay = weight_decay\n    # Created in Initialize.\n    self._learning_rate_tensor = None\n\n  def _create_slots(self, var_list):\n    # Create slots for the first and second moments.\n    for v in var_list:\n      self._zeros_slot(v, \"m\", self._name + \"/m\")\n      self._zeros_slot(v, \"v\", self._name + \"/v\")\n      self._zeros_slot(v, \"c\", self._name + \"/c\")\n\n  def _prepare(self):\n    learning_rate = self._call_if_callable(self._learning_rate)\n    self._learning_rate_tensor = tf.convert_to_tensor(learning_rate,\n                                                      name=\"learning_rate\")\n\n  def _resource_apply_dense(self, grad, var):\n    m = self.get_slot(var, \"m\")\n    v = self.get_slot(var, \"v\")\n    c = self.get_slot(var, \"c\")\n    return training_ops.resource_apply_adamom(var.handle,\n                                              m.handle,\n                                              v.handle,\n                                              c.handle,\n                                              tf.cast(\n                                                  self._learning_rate_tensor,\n                                                  grad.dtype.base_dtype),\n                                              self._ada_decay,\n                                              self._mom_decay,\n                                              self._epsilon,\n                                              self._weight_decay,\n                                              grad,\n                                              use_locking=self._use_locking)\n"
  },
  {
    "path": "monolith/native_training/optimizers/adamom_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom tensorflow.python.framework.ops import name_from_scope_name\n\nfrom monolith.native_training.optimizers import adamom\n\n\nclass AdamomTest(tf.test.TestCase):\n\n  def testBasic(self):\n    v = tf.Variable([0.1], name=\"var\")\n    loss = 0.12 * v\n    opt = adamom.AdamomOptimizer(learning_rate=0.1,\n                                 weight_decay=0.01,\n                                 ada_decay=0.99,\n                                 mom_decay=0.9)\n    update = opt.minimize(loss)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      sess.run(update)\n      all_vars = tf.compat.v1.all_variables()\n      vars_map = sess.run({var.name: var for var in all_vars})\n    eps = 1e-8\n    found_count = 0\n    for name, val in vars_map.items():\n      if name.find(\"/m\") >= 0:\n        found_count += 1\n        self.assertNear(val, 0.0121, eps)\n      elif name.find(\"/c\") >= 0:\n        found_count += 1\n        self.assertNear(val, 1.0, eps)\n      elif name.find(\"/v\") >= 0:\n        found_count += 1\n        self.assertNear(val, 0.014641, eps)\n      else:\n        found_count += 1\n        # Must be variable\n        self.assertNear(val, 0.090000336, eps)\n    self.assertEqual(found_count, 4)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()"
  },
  {
    "path": "monolith/native_training/optimizers/cc/kernels/training_op_helpers.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n\n/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n// Copied from tensorflow/core/kernels/training_op_helpers.h\n#ifndef MONOLITH_NATIVE_TRAINING_OPTIMIZERS_CC_TRAINING_OP_HELPERS_H_\n#define MONOLITH_NATIVE_TRAINING_OPTIMIZERS_CC_TRAINING_OP_HELPERS_H_\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_var.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/variant.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\ntypedef Eigen::ThreadPoolDevice CPUDevice;\ntypedef Eigen::GpuDevice GPUDevice;\n\n#ifdef TENSORFLOW_USE_SYCL\ntypedef Eigen::SyclDevice SYCLDevice;\n#endif  // TENSORFLOW_USE_SYCL\n\nenum DenseUpdateType { ADD, SUB, ASSIGN };\n\nnamespace functor {\n\ntemplate <typename Device, typename T, DenseUpdateType OP>\nstruct DenseUpdate {\n  void operator()(const Device& d, typename TTypes<T>::Flat params,\n                  typename TTypes<T>::ConstFlat update);\n};\n\ntemplate <typename T>\nstruct DenseUpdate<CPUDevice, T, ADD> {\n  void operator()(const CPUDevice& d, typename TTypes<T>::Flat params,\n                  typename TTypes<T>::ConstFlat update) {\n    params.device(d) += update;\n  }\n};\n\ntemplate <typename T>\nstruct DenseUpdate<CPUDevice, T, SUB> {\n  void operator()(const CPUDevice& d, typename TTypes<T>::Flat params,\n                  typename TTypes<T>::ConstFlat update) {\n    params.device(d) -= update;\n  }\n};\n\ntemplate <typename T>\nstruct DenseUpdate<CPUDevice, T, ASSIGN> {\n  void operator()(const CPUDevice& d, typename TTypes<T>::Flat params,\n                  typename TTypes<T>::ConstFlat update) {\n    params.device(d) = update;\n  }\n};\n\n#if GOOGLE_CUDA\ntemplate <typename T>\nstruct DenseUpdate<GPUDevice, T, ASSIGN> {\n  void operator()(const GPUDevice& d, typename TTypes<T>::Flat params,\n                  typename TTypes<T>::ConstFlat update) {\n    params.device(d) = update;\n  }\n};\n\ntemplate <typename T>\nstruct DenseUpdate<GPUDevice, T, ADD> {\n  void operator()(const GPUDevice& d, typename TTypes<T>::Flat params,\n                  typename TTypes<T>::ConstFlat update) {\n    params.device(d) += update;\n  }\n};\n\ntemplate <typename T>\nstruct DenseUpdate<GPUDevice, T, SUB> {\n  void operator()(const GPUDevice& d, typename TTypes<T>::Flat params,\n                  typename TTypes<T>::ConstFlat update) {\n    params.device(d) -= update;\n  }\n};\n#endif\n\n#ifdef TENSORFLOW_USE_SYCL\ntemplate <typename T>\nstruct DenseUpdate<SYCLDevice, T, ADD> {\n  void operator()(const SYCLDevice& d, typename TTypes<T>::Flat params,\n                  typename TTypes<T>::ConstFlat update) {\n    params.device(d) += update;\n  }\n};\n\ntemplate <typename T>\nstruct DenseUpdate<SYCLDevice, T, SUB> {\n  void operator()(const SYCLDevice& d, typename TTypes<T>::Flat params,\n                  typename TTypes<T>::ConstFlat update) {\n    params.device(d) -= update;\n  }\n};\n\ntemplate <typename T>\nstruct DenseUpdate<SYCLDevice, T, ASSIGN> {\n  void operator()(const SYCLDevice& d, typename TTypes<T>::Flat params,\n                  typename TTypes<T>::ConstFlat update) {\n    params.device(d) = update;\n  }\n};\n#endif  // TENSORFLOW_USE_SYCL\n\n}  // end namespace functor\n\nusing shape_inference::DimensionHandle;\nusing shape_inference::InferenceContext;\nusing shape_inference::ShapeHandle;\n\n// Must be called before performing a sparse operation on a variable. Ensures\n// that no concurrent dense operations can happen while holding the variable's\n// lock.\ntemplate <typename Device, typename T>\nStatus EnsureSparseVariableAccess(OpKernelContext* ctx, Var* var) {\n  if (var->copy_on_read_mode.load()) {\n    return Status::OK();\n  }\n  mutex_lock ml(*var->mu());\n  // Once copy-on-read mode is True the refcount is guaranteed to be 1. This can\n  // also happen if there are no concurrent reads of the variable and\n  // copy-on-read mode is false.\n  if (var->tensor()->RefCountIsOne()) {\n    var->copy_on_read_mode.store(true);\n    return Status::OK();\n  }\n  PersistentTensor unused;\n  Tensor* tmp;\n  if (std::is_same<T, Variant>::value) {\n    AllocatorAttributes attr;\n    attr.set_on_host(true);\n    TF_RETURN_IF_ERROR(ctx->allocate_persistent(\n        var->tensor()->dtype(), var->tensor()->shape(), &unused, &tmp, attr));\n\n    const auto elements_in = var->tensor()->flat<Variant>();\n    auto elements_out = tmp->flat<Variant>();\n    for (int64 i = 0; i < elements_in.size(); ++i) {\n      elements_out(i) = elements_in(i);\n    }\n  } else {\n    AllocatorAttributes attr;\n    attr.set_gpu_compatible(true);\n    attr.set_nic_compatible(true);\n    TF_RETURN_IF_ERROR(ctx->allocate_persistent(\n        var->tensor()->dtype(), var->tensor()->shape(), &unused, &tmp, attr));\n    functor::DenseUpdate<Device, T, ASSIGN> copy_functor;\n    copy_functor(ctx->eigen_device<Device>(), tmp->flat<T>(),\n                 const_cast<const Tensor*>(var->tensor())->flat<T>());\n  }\n  *var->tensor() = *tmp;\n  var->copy_on_read_mode.store(true);\n  return Status::OK();\n}\n\n// Utility structure that releases a sequence of borrowed mutexes when it is\n// deleted.\nstruct VariableInputLockHolder {\n public:\n  VariableInputLockHolder(\n      std::vector<Var*> vars, std::unique_ptr<std::vector<mutex_lock>> locks,\n      std::unique_ptr<std::vector<tf_shared_lock>> shared_locks)\n      : vars_(std::move(vars)),\n        locks_(std::move(locks)),\n        shared_locks_(std::move(shared_locks)) {}\n\n  VariableInputLockHolder(VariableInputLockHolder&& other)\n      : vars_(std::move(other.vars_)),\n        locks_(std::move(other.locks_)),\n        shared_locks_(std::move(other.shared_locks_)) {}\n\n  ~VariableInputLockHolder() {\n    // Release the locks before unreffing the Vars, because each lock\n    // is potentially borrowed from a Var in vars_.\n    locks_.reset();\n    for (Var* var : vars_) {\n      var->Unref();\n    }\n  }\n\n private:\n  std::vector<Var*> vars_;\n  // NOTE: Use a `std::unique_ptr` instead of moving in a vector directly,\n  // because a `std::vector<mutex_lock>` is not movable on all platforms.\n  std::unique_ptr<std::vector<mutex_lock>> locks_;\n  std::unique_ptr<std::vector<tf_shared_lock>> shared_locks_;\n};\n\n// Returns a borrowed pointer to the mutex for the variable `input` in `ctx`.\n//\n// If `input` corresponds to a `DT_RESOURCE`-type variable input,\n// `*maybe_resource` will be updated to contain the underlying resource, and the\n// caller will be responsible for calling `Unref()` on that resource.\ntemplate <typename Device, typename T>\nmutex* GetTrainingVariableMutex(OpKernelContext* ctx, int input, bool sparse,\n                                Var** maybe_resource) {\n  *maybe_resource = nullptr;\n  if (ctx->input_dtype(input) == DT_RESOURCE) {\n    if (LookupResource(ctx, HandleFromInput(ctx, input), maybe_resource).ok()) {\n      if (sparse) {\n        EnsureSparseVariableAccess<Device, T>(ctx, *maybe_resource)\n            .IgnoreError();\n      }\n      return (*maybe_resource)->mu();\n    } else {\n      ctx->CtxFailureWithWarning(\n          errors::Internal(\"Invalid variable reference.\"));\n      return nullptr;\n    }\n  }\n  return ctx->input_ref_mutex(input);\n}\n\n// MaybeLockVariableInputMutexesInOrder is a helper function to acquire mutexes\n// in address order to mitigate deadlock.  Returns a structure that, when\n// deleted, will release the acquired mutexes. Safe to pass duplicates - will\n// only lock each distinct mutex once. If sparse is true will ensure the\n// variable gets switched to copy-on-read mode before trying to acquire the\n// locks. If do_lock is false, returns immediately for reference variables. For\n// resource variables in copy-on-read-mode it will grab a shared lock if do_lock\n// is false, exclusive lock otherwise.  Note that this silently doesn't lock\n// mutexes for invalid variable references; in all usages this is followed by\n// GetInputTensor which will signal a failure.\ntemplate <typename Device, typename T>\nVariableInputLockHolder MaybeLockVariableInputMutexesInOrder(\n    OpKernelContext* ctx, bool do_lock, bool sparse,\n    const std::vector<int>& input_ids) {\n  bool any_resource = false;\n  for (auto i : input_ids) {\n    if (ctx->input_dtype(i) == DT_RESOURCE) {\n      any_resource = true;\n      break;\n    }\n  }\n  if (!do_lock && !any_resource) {\n    return VariableInputLockHolder({}, {}, {});\n  }\n  std::vector<Var*> vars;\n  std::vector<mutex*> mutexes;\n  std::vector<int> acquire_order;\n  for (auto input : input_ids) {\n    Var* var;\n    mutex* mutex =\n        GetTrainingVariableMutex<Device, T>(ctx, input, sparse, &var);\n    if (var) vars.push_back(var);\n    // Only lock each mutex once if duplicates exist (n^2 but n is 2 or 3).\n    if (std::find(mutexes.begin(), mutexes.end(), mutex) == mutexes.end()) {\n      acquire_order.push_back(mutexes.size());\n      mutexes.push_back(mutex);\n    }\n  }\n  std::sort(acquire_order.begin(), acquire_order.end(),\n            [&mutexes](int a, int b) { return mutexes[a] < mutexes[b]; });\n\n  auto locks = absl::make_unique<std::vector<mutex_lock>>();\n  auto shared_locks = absl::make_unique<std::vector<tf_shared_lock>>();\n  locks->reserve(acquire_order.size());\n\n  for (auto input : acquire_order) {\n    Var* var;\n    mutex* mu = GetTrainingVariableMutex<Device, T>(ctx, input, sparse, &var);\n    core::ScopedUnref scoped_unref(var);\n    if (mu != nullptr) {\n      if (!sparse || do_lock) {\n        locks->emplace_back(*mu);\n      } else {\n        shared_locks->emplace_back(*mu);\n      }\n    }\n  }\n  return VariableInputLockHolder(std::move(vars), std::move(locks),\n                                 std::move(shared_locks));\n}\n\ninline void MaybeForwardRefInputToRefOutput(OpKernelContext* ctx, int input,\n                                            int output) {\n  if (ctx->input_dtype(input) != DT_RESOURCE) {\n    ctx->forward_ref_input_to_ref_output(input, output);\n  }\n}\n\n// This is for use with ResourceVariables to ensure *tensor has a\n// reference count of 1 before you update it.\n// REQUIRES: If you pass in variable->tensor(), *variable->mu() must be held.\ntemplate <typename Device, typename T>\nStatus PrepareToUpdateVariable(OpKernelContext* ctx, Tensor* tensor,\n                               bool copy_on_read_mode) {\n  if (copy_on_read_mode || !tensor->RefCountIsOne()) {\n    // Tensor's buffer is in use by some read, so we need to copy before\n    // updating.\n    PersistentTensor unused;\n    Tensor* tmp;\n    if (std::is_same<T, Variant>::value) {\n      AllocatorAttributes attr;\n      attr.set_on_host(true);\n      TF_RETURN_IF_ERROR(ctx->allocate_persistent(\n          tensor->dtype(), tensor->shape(), &unused, &tmp, attr));\n\n      const auto elements_in = tensor->flat<Variant>();\n      auto elements_out = tmp->flat<Variant>();\n      for (int64 i = 0; i < elements_in.size(); ++i) {\n        elements_out(i) = elements_in(i);\n      }\n    } else {\n      AllocatorAttributes attr;\n      attr.set_gpu_compatible(true);\n      attr.set_nic_compatible(true);\n      TF_RETURN_IF_ERROR(ctx->allocate_persistent(\n          tensor->dtype(), tensor->shape(), &unused, &tmp, attr));\n      functor::DenseUpdate<Device, T, ASSIGN> copy_functor;\n      copy_functor(ctx->eigen_device<Device>(), tmp->flat<T>(),\n                   const_cast<const Tensor*>(tensor)->flat<T>());\n    }\n    *tensor = *tmp;\n  }\n  return Status::OK();\n}\n\n// This gives you `*out`, a tensor you can update, corresponding to a variable\n// passed as input index `input`.  This handles the differences between\n// reference and resource variables. For reference variables we can just grab\n// the tensor, grabbing the lock if lock_held is False.\n//\n// For resource variables we, if sparse is true, ensure it's in copy-on-read\n// mode, and then, regardless of the value of sparse, ensure its refcount is 1\n// (by potentially copying its contents). In this case lock_held is ignored.\ntemplate <typename Device, typename T>\nStatus GetInputTensorFromVariable(OpKernelContext* ctx, int input,\n                                  bool lock_held, bool sparse, Tensor* out) {\n  if (ctx->input_dtype(input) == DT_RESOURCE) {\n    core::RefCountPtr<Var> var;\n    TF_RETURN_IF_ERROR(LookupResource(ctx, HandleFromInput(ctx, input), &var));\n    if (sparse) {\n      TF_RETURN_IF_ERROR(EnsureSparseVariableAccess<Device, T>(ctx, var.get()));\n      *out = *var->tensor();\n      return Status::OK();\n    }\n    TF_RETURN_IF_ERROR(PrepareToUpdateVariable<Device, T>(\n        ctx, var->tensor(), var->copy_on_read_mode.load()));\n    *out = *var->tensor();\n    return Status::OK();\n  }\n  *out = ctx->mutable_input(input, lock_held);\n  return Status::OK();\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_OPTIMIZERS_CC_TRAINING_OP_HELPERS_H_\n"
  },
  {
    "path": "monolith/native_training/optimizers/cc/kernels/training_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#define EIGEN_USE_THREADS\n#include \"monolith/native_training/optimizers/cc/kernels/training_ops.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntemplate <>\nstruct ApplyRmsprop<CPUDevice> {\n  void operator()(const CPUDevice& d, typename TTypes<float>::Flat var,\n                  typename TTypes<float>::Flat m,\n                  typename TTypes<float>::Flat v,\n                  typename TTypes<float>::ConstScalar lr,\n                  typename TTypes<float>::ConstScalar beta1,\n                  typename TTypes<float>::ConstScalar beta2,\n                  typename TTypes<float>::ConstScalar epsilon,\n                  typename TTypes<float>::ConstScalar weight_decay,\n                  typename TTypes<float>::ConstFlat grad, bool update_slots) {\n    auto grad_after_decay = weight_decay() * var + grad;\n    if (update_slots) {\n      v.device(d) += (grad_after_decay.square() - v) * (1.0f - beta2());\n      m.device(d) =\n          beta1() * m + (grad_after_decay * lr()) * (v + epsilon()).rsqrt();\n      var.device(d) -= m;\n    }\n  }\n};\n\ntemplate <>\nstruct ApplyRmspropV2<CPUDevice> {\n  void operator()(const CPUDevice& d, typename TTypes<float>::Flat var,\n                  typename TTypes<float>::Flat m,\n                  typename TTypes<float>::Flat v,\n                  typename TTypes<float>::ConstScalar lr,\n                  typename TTypes<float>::ConstScalar beta1,\n                  typename TTypes<float>::ConstScalar beta2,\n                  typename TTypes<float>::ConstScalar epsilon,\n                  typename TTypes<float>::ConstScalar weight_decay,\n                  typename TTypes<float>::ConstFlat grad, bool update_slots) {\n    auto grad_after_decay = weight_decay() * var + grad;\n    if (update_slots) {\n      v.device(d) = beta2() * v + grad_after_decay.square();\n      //      m.device(d) = beta1() * m + (grad_after_decay * lr()) * (v +\n      //      epsilon()).rsqrt();\n      m.device(d) =\n          beta1() * m + (grad_after_decay * lr()) / (v.sqrt() + epsilon());\n      var.device(d) -= m;\n    }\n  }\n};\n\ntemplate <>\nstruct ApplyAdamom<CPUDevice> {\n  void operator()(const CPUDevice& d, typename TTypes<float>::Flat var,\n                  typename TTypes<float>::Flat m,\n                  typename TTypes<float>::Flat v,\n                  typename TTypes<float>::Flat c,\n                  typename TTypes<float>::ConstScalar lr,\n                  typename TTypes<float>::ConstScalar ada_decay,\n                  typename TTypes<float>::ConstScalar mom_decay,\n                  typename TTypes<float>::ConstScalar epsilon,\n                  typename TTypes<float>::ConstScalar weight_decay,\n                  typename TTypes<float>::ConstFlat grad, bool update_slots) {\n    auto grad_after_decay = weight_decay() * var + grad;\n\n    if (update_slots) {\n      m.device(d) = mom_decay() * m + (1.0f - mom_decay()) * grad_after_decay;\n      v.device(d) = ada_decay() * v + grad_after_decay * grad_after_decay;\n      c.device(d) = ada_decay() * c + 1.0f;\n    }\n    var.device(d) -= m * lr() * (v / c + epsilon()).rsqrt();\n  }\n};\n\ntemplate <>\nstruct ApplyAdamomV2<CPUDevice> {\n  void operator()(const CPUDevice& d, typename TTypes<float>::Flat var,\n                  typename TTypes<float>::Flat m,\n                  typename TTypes<float>::Flat v,\n                  typename TTypes<float>::Flat c,\n                  typename TTypes<float>::ConstScalar lr,\n                  typename TTypes<float>::ConstScalar ada_decay,\n                  typename TTypes<float>::ConstScalar mom_decay,\n                  typename TTypes<float>::ConstScalar epsilon,\n                  typename TTypes<float>::ConstScalar weight_decay,\n                  typename TTypes<float>::ConstFlat grad, bool update_slots) {\n    auto grad_after_decay = weight_decay() * var + grad;\n\n    if (update_slots) {\n      m.device(d) = mom_decay() * m + (1.0f - mom_decay()) * grad_after_decay;\n      v.device(d) = ada_decay() * v + grad_after_decay * grad_after_decay;\n      c.device(d) = ada_decay() * c + 1.0f;\n    }\n    var.device(d) -= m * lr() / ((v / c).sqrt() + epsilon());\n  }\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"ResourceApplyAdamom\")\n                            // .HostMemory(\"var\")\n                            // .HostMemory(\"m\")\n                            // .HostMemory(\"v\")\n                            // .HostMemory(\"c\")\n                            .Device(DEVICE_CPU),\n                        ApplyAdamomOp<CPUDevice>);\n\nREGISTER_KERNEL_BUILDER(Name(\"ResourceApplyRmsprop\").Device(DEVICE_CPU),\n                        ApplyRmspropOp<CPUDevice>);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/optimizers/cc/kernels/training_ops.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_OPTIMIZERS_CC_TRAINING_OPS_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_OPTIMIZERS_CC_TRAINING_OPS_H_\n\n#include \"monolith/native_training/optimizers/cc/kernels/training_op_helpers.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntemplate <typename Device>\nstruct ApplyRmsprop {\n  void operator()(const Device& d, typename TTypes<float>::Flat var,\n                  typename TTypes<float>::Flat m,\n                  typename TTypes<float>::Flat v,\n                  typename TTypes<float>::ConstScalar lr,\n                  typename TTypes<float>::ConstScalar beta1,\n                  typename TTypes<float>::ConstScalar beta2,\n                  typename TTypes<float>::ConstScalar epsilon,\n                  typename TTypes<float>::ConstScalar weight_decay,\n                  typename TTypes<float>::ConstFlat grad, bool update_slots);\n};\n\ntemplate <typename Device>\nstruct ApplyRmspropV2 {\n  void operator()(const Device& d, typename TTypes<float>::Flat var,\n                  typename TTypes<float>::Flat m,\n                  typename TTypes<float>::Flat v,\n                  typename TTypes<float>::ConstScalar lr,\n                  typename TTypes<float>::ConstScalar beta1,\n                  typename TTypes<float>::ConstScalar beta2,\n                  typename TTypes<float>::ConstScalar epsilon,\n                  typename TTypes<float>::ConstScalar weight_decay,\n                  typename TTypes<float>::ConstFlat grad, bool update_slots);\n};\n\ntemplate <typename Device>\nstruct ApplyAdamom {\n  void operator()(const Device& d, typename TTypes<float>::Flat var,\n                  typename TTypes<float>::Flat m,\n                  typename TTypes<float>::Flat v,\n                  typename TTypes<float>::Flat c,\n                  typename TTypes<float>::ConstScalar lr,\n                  typename TTypes<float>::ConstScalar ada_decay,\n                  typename TTypes<float>::ConstScalar mom_decay,\n                  typename TTypes<float>::ConstScalar epsilon,\n                  typename TTypes<float>::ConstScalar weight_decay,\n                  typename TTypes<float>::ConstFlat grad, bool update_slots);\n};\n\ntemplate <typename Device>\nstruct ApplyAdamomV2 {\n  void operator()(const Device& d, typename TTypes<float>::Flat var,\n                  typename TTypes<float>::Flat m,\n                  typename TTypes<float>::Flat v,\n                  typename TTypes<float>::Flat c,\n                  typename TTypes<float>::ConstScalar lr,\n                  typename TTypes<float>::ConstScalar ada_decay,\n                  typename TTypes<float>::ConstScalar mom_decay,\n                  typename TTypes<float>::ConstScalar epsilon,\n                  typename TTypes<float>::ConstScalar weight_decay,\n                  typename TTypes<float>::ConstFlat grad, bool update_slots);\n};\n\ntemplate <typename Device>\nclass ApplyRmspropOp : public OpKernel {\n public:\n  explicit ApplyRmspropOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"use_locking\", &use_exclusive_lock_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"update_slots\", &update_slots_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"use_v2\", &use_v2_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    const bool sparse = false;\n    auto locks = MaybeLockVariableInputMutexesInOrder<Device, float>(\n        ctx, use_exclusive_lock_, sparse, {0, 1});\n    Tensor var;\n    OP_REQUIRES_OK(ctx, GetInputTensorFromVariable<Device, float>(\n                            ctx, 0, use_exclusive_lock_, sparse, &var));\n    Tensor m;\n    OP_REQUIRES_OK(ctx, GetInputTensorFromVariable<Device, float>(\n                            ctx, 1, use_exclusive_lock_, sparse, &m));\n    Tensor v;\n    OP_REQUIRES_OK(ctx, GetInputTensorFromVariable<Device, float>(\n                            ctx, 2, use_exclusive_lock_, sparse, &v));\n    OP_REQUIRES(\n        ctx, var.IsInitialized(),\n        errors::FailedPrecondition(\n            \"Attempting to use uninitialized variables: \", requested_input(0)));\n    OP_REQUIRES(\n        ctx, m.IsInitialized(),\n        errors::FailedPrecondition(\n            \"Attempting to use uninitialized variables: \", requested_input(1)));\n    OP_REQUIRES(\n        ctx, v.IsInitialized(),\n        errors::FailedPrecondition(\n            \"Attempting to use uninitialized variables: \", requested_input(2)));\n    const Tensor& lr = ctx->input(3);\n    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(lr.shape()),\n                errors::InvalidArgument(\"lr is not a scalar: \",\n                                        lr.shape().DebugString()));\n    const Tensor& beta1 = ctx->input(4);\n    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(beta1.shape()),\n                errors::InvalidArgument(\"beta1 is not a scalar: \",\n                                        beta1.shape().DebugString()));\n    const Tensor& beta2 = ctx->input(5);\n    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(beta2.shape()),\n                errors::InvalidArgument(\"beta2 is not a scalar: \",\n                                        beta2.shape().DebugString()));\n    const Tensor& epsilon = ctx->input(6);\n    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(epsilon.shape()),\n                errors::InvalidArgument(\"epsilon is not a scalar: \",\n                                        epsilon.shape().DebugString()));\n    const Tensor& weight_decay = ctx->input(7);\n    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(weight_decay.shape()),\n                errors::InvalidArgument(\"weight_decay is not a scalar: \",\n                                        weight_decay.shape().DebugString()));\n    const Tensor& grad = ctx->input(8);\n    OP_REQUIRES(ctx, var.shape().IsSameSize(m.shape()),\n                errors::InvalidArgument(\"var and m do not have the same shape\",\n                                        var.shape().DebugString(), \" \",\n                                        m.shape().DebugString()));\n    OP_REQUIRES(ctx, var.shape().IsSameSize(v.shape()),\n                errors::InvalidArgument(\"var and v do not have the same shape\",\n                                        var.shape().DebugString(), \" \",\n                                        v.shape().DebugString()));\n    OP_REQUIRES(\n        ctx, var.shape().IsSameSize(grad.shape()),\n        errors::InvalidArgument(\"var and grad do not have the same shape\",\n                                var.shape().DebugString(), \" \",\n                                grad.shape().DebugString()));\n\n    const Device& device = ctx->eigen_device<Device>();\n    if (!use_v2_) {\n      ApplyRmsprop<Device>()(\n          device, var.flat<float>(), m.flat<float>(), v.flat<float>(),\n          lr.scalar<float>(), beta1.scalar<float>(), beta2.scalar<float>(),\n          epsilon.scalar<float>(), weight_decay.scalar<float>(),\n          grad.flat<float>(), update_slots_);\n    } else {\n      ApplyRmspropV2<Device>()(\n          device, var.flat<float>(), m.flat<float>(), v.flat<float>(),\n          lr.scalar<float>(), beta1.scalar<float>(), beta2.scalar<float>(),\n          epsilon.scalar<float>(), weight_decay.scalar<float>(),\n          grad.flat<float>(), update_slots_);\n    }\n\n    MaybeForwardRefInputToRefOutput(ctx, 0, 0);\n  }\n\n private:\n  bool use_exclusive_lock_;\n  bool update_slots_;\n  bool use_v2_;\n};\n\ntemplate <typename Device>\nclass ApplyAdamomOp : public OpKernel {\n public:\n  explicit ApplyAdamomOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"use_locking\", &use_exclusive_lock_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"update_slots\", &update_slots_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"use_v2\", &use_v2_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    const bool sparse = false;\n    auto locks = MaybeLockVariableInputMutexesInOrder<CPUDevice, float>(\n        ctx, use_exclusive_lock_, sparse, {0, 1});\n    Tensor var;\n    OP_REQUIRES_OK(ctx, GetInputTensorFromVariable<Device, float>(\n                            ctx, 0, use_exclusive_lock_, sparse, &var));\n    Tensor m;\n    OP_REQUIRES_OK(ctx, GetInputTensorFromVariable<Device, float>(\n                            ctx, 1, use_exclusive_lock_, sparse, &m));\n    Tensor v;\n    OP_REQUIRES_OK(ctx, GetInputTensorFromVariable<Device, float>(\n                            ctx, 2, use_exclusive_lock_, sparse, &v));\n    Tensor c;\n    OP_REQUIRES_OK(ctx, GetInputTensorFromVariable<Device, float>(\n                            ctx, 3, use_exclusive_lock_, sparse, &c));\n    OP_REQUIRES(\n        ctx, var.IsInitialized(),\n        errors::FailedPrecondition(\n            \"Attempting to use uninitialized variables: \", requested_input(0)));\n    OP_REQUIRES(\n        ctx, m.IsInitialized(),\n        errors::FailedPrecondition(\n            \"Attempting to use uninitialized variables: \", requested_input(1)));\n    OP_REQUIRES(\n        ctx, v.IsInitialized(),\n        errors::FailedPrecondition(\n            \"Attempting to use uninitialized variables: \", requested_input(2)));\n    OP_REQUIRES(\n        ctx, c.IsInitialized(),\n        errors::FailedPrecondition(\n            \"Attempting to use uninitialized variables: \", requested_input(3)));\n    const Tensor& lr = ctx->input(4);\n    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(lr.shape()),\n                errors::InvalidArgument(\"lr is not a scalar: \",\n                                        lr.shape().DebugString()));\n    const Tensor& ada_decay = ctx->input(5);\n    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(ada_decay.shape()),\n                errors::InvalidArgument(\"ada_decay is not a scalar: \",\n                                        ada_decay.shape().DebugString()));\n    const Tensor& mom_decay = ctx->input(6);\n    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(mom_decay.shape()),\n                errors::InvalidArgument(\"mom_decay is not a scalar: \",\n                                        mom_decay.shape().DebugString()));\n    const Tensor& epsilon = ctx->input(7);\n    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(epsilon.shape()),\n                errors::InvalidArgument(\"epsilon is not a scalar: \",\n                                        epsilon.shape().DebugString()));\n    const Tensor& weight_decay = ctx->input(8);\n    OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(weight_decay.shape()),\n                errors::InvalidArgument(\"weight_decay is not a scalar: \",\n                                        weight_decay.shape().DebugString()));\n    const Tensor& grad = ctx->input(9);\n    OP_REQUIRES(ctx, var.shape().IsSameSize(m.shape()),\n                errors::InvalidArgument(\n                    \"var and accum do not have the same shape\",\n                    var.shape().DebugString(), \" \", m.shape().DebugString()));\n    OP_REQUIRES(ctx, var.shape().IsSameSize(v.shape()),\n                errors::InvalidArgument(\n                    \"var and accum do not have the same shape\",\n                    var.shape().DebugString(), \" \", v.shape().DebugString()));\n    OP_REQUIRES(ctx, var.shape().IsSameSize(c.shape()),\n                errors::InvalidArgument(\n                    \"var and accum do not have the same shape\",\n                    var.shape().DebugString(), \" \", c.shape().DebugString()));\n    OP_REQUIRES(\n        ctx, var.shape().IsSameSize(grad.shape()),\n        errors::InvalidArgument(\"var and grad do not have the same shape\",\n                                var.shape().DebugString(), \" \",\n                                grad.shape().DebugString()));\n\n    const Device& device = ctx->eigen_device<Device>();\n    if (!use_v2_) {\n      ApplyAdamom<Device>()(\n          device, var.flat<float>(), m.flat<float>(), v.flat<float>(),\n          c.flat<float>(), lr.scalar<float>(), ada_decay.scalar<float>(),\n          mom_decay.scalar<float>(), epsilon.scalar<float>(),\n          weight_decay.scalar<float>(), grad.flat<float>(), update_slots_);\n    } else {\n      ApplyAdamomV2<Device>()(\n          device, var.flat<float>(), m.flat<float>(), v.flat<float>(),\n          c.flat<float>(), lr.scalar<float>(), ada_decay.scalar<float>(),\n          mom_decay.scalar<float>(), epsilon.scalar<float>(),\n          weight_decay.scalar<float>(), grad.flat<float>(), update_slots_);\n    }\n\n    MaybeForwardRefInputToRefOutput(ctx, 0, 0);\n  }\n\n private:\n  bool use_exclusive_lock_;\n  bool update_slots_;\n  bool use_v2_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_OPTIMIZERS_CC_TRAINING_OPS_H_\n"
  },
  {
    "path": "monolith/native_training/optimizers/cc/kernels/training_ops_gpu.cu.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#if GOOGLE_CUDA || TENSORFLOW_USE_ROCM\n\n#define EIGEN_USE_GPU\n\n#include \"monolith/native_training/optimizers/cc/kernels/training_ops.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\ntypedef Eigen::GpuDevice GPUDevice;\n\ntemplate <>\nstruct ApplyRmsprop<GPUDevice> {\n  void operator()(const GPUDevice& d, typename TTypes<float>::Flat var,\n                  typename TTypes<float>::Flat m,\n                  typename TTypes<float>::Flat v,\n                  typename TTypes<float>::ConstScalar lr,\n                  typename TTypes<float>::ConstScalar beta1,\n                  typename TTypes<float>::ConstScalar beta2,\n                  typename TTypes<float>::ConstScalar epsilon,\n                  typename TTypes<float>::ConstScalar weight_decay,\n                  typename TTypes<float>::ConstFlat grad, bool update_slots) {\n    Eigen::array<typename TTypes<float>::Tensor::Index, 1> bcast;\n    bcast[0] = grad.dimension(0);\n    Eigen::Sizes<1> single;\n    auto grad_after_decay =\n        weight_decay.reshape(single).broadcast(bcast) * var + grad;\n    if (update_slots) {\n      v.device(d) +=\n          (grad_after_decay.square() - v) *\n          (beta2.constant(1.0f) - beta2).reshape(single).broadcast(bcast);\n      m.device(d) = beta1.reshape(single).broadcast(bcast) * m +\n                    (grad_after_decay * lr.reshape(single).broadcast(bcast)) *\n                        (v + epsilon.reshape(single).broadcast(bcast)).rsqrt();\n      var.device(d) -= m;\n    }\n  }\n};\n\ntemplate <>\nstruct ApplyRmspropV2<GPUDevice> {\n  void operator()(const GPUDevice& d, typename TTypes<float>::Flat var,\n                  typename TTypes<float>::Flat m,\n                  typename TTypes<float>::Flat v,\n                  typename TTypes<float>::ConstScalar lr,\n                  typename TTypes<float>::ConstScalar beta1,\n                  typename TTypes<float>::ConstScalar beta2,\n                  typename TTypes<float>::ConstScalar epsilon,\n                  typename TTypes<float>::ConstScalar weight_decay,\n                  typename TTypes<float>::ConstFlat grad, bool update_slots) {\n    Eigen::array<typename TTypes<float>::Tensor::Index, 1> bcast;\n    bcast[0] = grad.dimension(0);\n    Eigen::Sizes<1> single;\n    auto grad_after_decay =\n        weight_decay.reshape(single).broadcast(bcast) * var + grad;\n    if (update_slots) {\n      v.device(d) = beta2.reshape(single).broadcast(bcast) * v +\n                    grad_after_decay.square();\n      //      m.device(d) = beta1() * m + (grad_after_decay * lr()) * (v +\n      //      epsilon()).rsqrt();\n      m.device(d) = beta1.reshape(single).broadcast(bcast) * m +\n                    (grad_after_decay * lr.reshape(single).broadcast(bcast)) /\n                        (v.sqrt() + epsilon.reshape(single).broadcast(bcast));\n      var.device(d) -= m;\n    }\n  }\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"ResourceApplyRmsprop\").Device(DEVICE_GPU),\n                        ApplyRmspropOp<GPUDevice>);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // GOOGLE_CUDA || TENSORFLOW_USE_ROCM\n"
  },
  {
    "path": "monolith/native_training/optimizers/cc/training_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#define EIGEN_USE_THREADS\n#include \"monolith/native_training/optimizers/cc/kernels/training_op_helpers.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntemplate <bool is_resource>\nShapeHandle ShapeOrHandleShape(InferenceContext* c, int input) {\n  auto* handle_data = c->input_handle_shapes_and_types(input);\n  if (handle_data != nullptr && !handle_data->empty() &&\n      (*handle_data)[0].dtype != DT_INVALID) {\n    return (*handle_data)[0].shape;\n  }\n  return c->input(input);\n}\n\ntemplate <>\nShapeHandle ShapeOrHandleShape<true>(InferenceContext* c, int input) {\n  auto* handle_data = c->input_handle_shapes_and_types(input);\n  if (handle_data != nullptr && !handle_data->empty() &&\n      (*handle_data)[0].dtype != DT_INVALID) {\n    return (*handle_data)[0].shape;\n  }\n  // If a resource input is missing shape information, we should return\n  // UnknownShape rather than the shape of the input, which is a scalar\n  // resource handle.\n  return c->UnknownShape();\n}\n\n// Handle the gradient and, if <is_sparse>, indices inputs.\n// <s> is an input+output parameter, containing the current known input shape to\n// the gradient.\ntemplate <bool is_sparse, bool is_resource>\nstatic Status HandleGradAndIndicesInputs(InferenceContext* c, int grad_idx,\n                                         ShapeHandle* s) {\n  ShapeHandle grad = ShapeOrHandleShape<is_resource>(c, grad_idx);\n  if (!is_sparse) {\n    TF_RETURN_IF_ERROR(c->Merge(*s, grad, s));\n    return Status::OK();\n  }\n  // Indices is a vector where indices.dim[0].rank == grad[0].rank.\n  ShapeHandle indices;\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(grad_idx + 1), 1, &indices));\n  DimensionHandle unused;\n  TF_RETURN_IF_ERROR(c->Merge(c->Dim(indices, 0), c->Dim(grad, 0), &unused));\n  // Trailing part of grad matches trailing part of *s.\n  ShapeHandle grad_unknown_first;\n  TF_RETURN_IF_ERROR(\n      c->ReplaceDim(grad, 0, c->UnknownDim(), &grad_unknown_first));\n  TF_RETURN_IF_ERROR(c->Merge(*s, grad_unknown_first, s));\n\n  return Status::OK();\n}\n\nstatic Status ApplyAdamomShapeFn(InferenceContext* c) {\n  ShapeHandle unused;\n  ShapeHandle s = ShapeOrHandleShape</*is_resource=*/true>(c, 0);  // var\n  TF_RETURN_IF_ERROR(\n      c->Merge(s, ShapeOrHandleShape</*is_resource=*/true>(c, 1), &s));  // m\n  TF_RETURN_IF_ERROR(\n      c->Merge(s, ShapeOrHandleShape</*is_resource=*/true>(c, 2), &s));  // v\n  TF_RETURN_IF_ERROR(\n      c->Merge(s, ShapeOrHandleShape</*is_resource=*/true>(c, 3), &s));  // c\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(4), 0, &unused));              // lr\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(5), 0, &unused));  // ada_decay\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(6), 0, &unused));  // mom_decay\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(7), 0, &unused));  // epsilon\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(8), 0, &unused));  // weight_decay\n  TF_RETURN_IF_ERROR(\n      HandleGradAndIndicesInputs</*is_sparse=*/false, /*is_resource=*/true>(\n          c, 9 /* grad_idx */, &s));\n  if (c->num_outputs() > 0) {\n    c->set_output(0, s);\n  }\n  return Status::OK();\n}\n\nREGISTER_OP(\"ResourceApplyAdamom\")\n    .Input(\"var: resource\")\n    .Input(\"m: resource\")\n    .Input(\"v: resource\")\n    .Input(\"c: resource\")\n    .Input(\"learning_rate: float\")\n    .Input(\"ada_decay: float\")\n    .Input(\"mom_decay: float\")\n    .Input(\"epsilon: float\")\n    .Input(\"weight_decay: float\")\n    .Input(\"grad: float\")\n    .Attr(\"use_locking: bool = false\")\n    .Attr(\"update_slots: bool = true\")\n    .Attr(\"use_v2: bool = false\")\n    .SetShapeFn(ApplyAdamomShapeFn);\n\nstatic Status ApplyRmspropShapeFn(InferenceContext* c) {\n  ShapeHandle unused;\n  ShapeHandle s = ShapeOrHandleShape</*is_resource=*/true>(c, 0);  // var\n  TF_RETURN_IF_ERROR(\n      c->Merge(s, ShapeOrHandleShape</*is_resource=*/true>(c, 1), &s));  // m\n  TF_RETURN_IF_ERROR(\n      c->Merge(s, ShapeOrHandleShape</*is_resource=*/true>(c, 2), &s));  // v\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 0, &unused));              // lr\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(4), 0, &unused));  // beta1\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(5), 0, &unused));  // beta2\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(6), 0, &unused));  // epsilon\n  TF_RETURN_IF_ERROR(c->WithRank(c->input(7), 0, &unused));  // weight_decay\n  TF_RETURN_IF_ERROR(\n      HandleGradAndIndicesInputs</*is_sparse=*/false, /*is_resource=*/true>(\n          c, 8 /* grad_idx */, &s));\n  if (c->num_outputs() > 0) {\n    c->set_output(0, s);\n  }\n  return Status::OK();\n}\n\nREGISTER_OP(\"ResourceApplyRmsprop\")\n    .Input(\"var: resource\")\n    .Input(\"m: resource\")\n    .Input(\"v: resource\")\n    .Input(\"learning_rate: float\")\n    .Input(\"beta1: float\")\n    .Input(\"beta2: float\")\n    .Input(\"epsilon: float\")\n    .Input(\"weight_decay: float\")\n    .Input(\"grad: float\")\n    .Attr(\"use_locking: bool = false\")\n    .Attr(\"update_slots: bool = true\")\n    .Attr(\"use_v2: bool = false\")\n    .SetShapeFn(ApplyRmspropShapeFn);\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/optimizers/rmsprop.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\nfrom typing import Union, Callable\nimport tensorflow as tf\n\nimport numpy as np\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.keras.optimizer_v2 import optimizer_v2\nfrom tensorflow.python.ops import array_ops\nfrom tensorflow.python.ops import math_ops\nfrom tensorflow.python.ops import state_ops\nfrom tensorflow.python.ops import init_ops\nfrom tensorflow.python.ops import gen_array_ops\nfrom tensorflow.python.util.tf_export import keras_export\nfrom tensorflow.keras.initializers import Constant\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\ntraining_ops = gen_monolith_ops\n\n\nclass RmspropOptimizer(tf.compat.v1.train.Optimizer):\n  \"\"\"http://www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf\"\"\"\n\n  def __init__(self,\n               learning_rate=5e-6,\n               beta1: float = 0.99,\n               beta2: float = 0.999,\n               epsilon: float = 1e-8,\n               weight_decay: float = 0.0,\n               use_locking: bool = False,\n               use_v2: bool = False,\n               name=\"Rmsprop\"):\n    super().__init__(use_locking, name)\n    self._learning_rate = learning_rate\n    self._beta1 = beta1\n    self._beta2 = beta2\n    self._epsilon = epsilon\n    self._weight_decay = weight_decay\n    self._use_v2 = use_v2\n    # Created in Initialize.\n    self._learning_rate_tensor = None\n\n  def _create_slots(self, var_list):\n    # Create slots for the first and second moments.\n    for v in var_list:\n      self._zeros_slot(v, \"m\", self._name + \"/m\")\n      self._zeros_slot(v, \"v\", self._name + \"/v\")\n\n  def _prepare(self):\n    learning_rate = self._call_if_callable(self._learning_rate)\n    self._learning_rate_tensor = tf.convert_to_tensor(learning_rate,\n                                                      name=\"learning_rate\")\n\n  def _apply_dense(self, grad, var):\n    raise NotImplementedError(\n        \"Please use tf.compat.v1.disable_eager_execution() instead of tf.compat.v1.disable_v2_behavior()\"\n    )\n\n  def _resource_apply_dense(self, grad, var):\n    m = self.get_slot(var, \"m\")\n    v = self.get_slot(var, \"v\")\n    return training_ops.resource_apply_rmsprop(var.handle,\n                                               m.handle,\n                                               v.handle,\n                                               tf.cast(\n                                                   self._learning_rate_tensor,\n                                                   grad.dtype.base_dtype),\n                                               self._beta1,\n                                               self._beta2,\n                                               self._epsilon,\n                                               self._weight_decay,\n                                               grad,\n                                               use_locking=self._use_locking,\n                                               use_v2=self._use_v2)\n"
  },
  {
    "path": "monolith/native_training/optimizers/rmsprop_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\n\nimport tensorflow as tf\nfrom tensorflow.python.framework.ops import name_from_scope_name\nfrom tensorflow.python.framework import test_util\n\nfrom monolith.native_training.optimizers import rmsprop\n\n\ndef build_graph() -> tf.Operation:\n  v = tf.Variable([0.1], name=\"var\")\n  loss = 0.12 * v\n  opt = rmsprop.RmspropOptimizer(learning_rate=0.1,\n                                 weight_decay=1,\n                                 beta1=0.9,\n                                 beta2=0.9,\n                                 epsilon=0.1)\n  return opt.minimize(loss)\n\n\nclass RmspropTest(tf.test.TestCase):\n\n  def testBasic(self):\n    with tf.Graph().as_default(), test_util.use_gpu():\n      train_op = build_graph()\n      with self.session() as sess:\n        sess.run(tf.compat.v1.global_variables_initializer())\n        sess.run(train_op)\n        all_vars = tf.compat.v1.all_variables()\n        if tf.test.is_gpu_available():\n          for var in all_vars:\n            self.assertEqual(var.device, '/device:GPU:0')\n        vars_map_maybe_on_gpu = sess.run({var.name: var for var in all_vars})\n    eps = 1e-8\n    found_count = 0\n    for name, val in vars_map_maybe_on_gpu.items():\n      if name.find(\"/m\") >= 0:\n        found_count += 1\n        self.assertNear(val, 0.06794526153774846, eps)\n      elif name.find(\"/v\") >= 0:\n        found_count += 1\n        self.assertNear(val, 0.00484, eps)\n      else:\n        found_count += 1\n        # Must be variable\n        self.assertNear(val, 0.03205473846225154, eps)\n    self.assertEqual(found_count, 3)\n\n    with tf.Graph().as_default(), test_util.force_cpu():\n      train_op = build_graph()\n      with self.session() as sess:\n        sess.run(tf.compat.v1.global_variables_initializer())\n        sess.run(train_op)\n        all_vars = tf.compat.v1.all_variables()\n        for var in all_vars:\n          self.assertEqual(var.device, '/device:CPU:0')\n        vars_map_on_cpu = sess.run({var.name: var for var in all_vars})\n    self.assertEqual(vars_map_maybe_on_gpu, vars_map_on_cpu)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/optimizers/rmspropv2_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom tensorflow.python.framework.ops import name_from_scope_name\nfrom tensorflow.python.framework import test_util\nfrom monolith.native_training.optimizers import rmsprop\n\n\ndef build_graph() -> tf.Operation:\n  v = tf.Variable([0.1], name=\"var\")\n  loss = 0.12 * v\n  opt = rmsprop.RmspropOptimizer(learning_rate=0.1,\n                                 weight_decay=1,\n                                 beta1=0.9,\n                                 beta2=0.9,\n                                 epsilon=0.1,\n                                 use_v2=True)\n  return opt.minimize(loss)\n\n\nclass RmspropTest(tf.test.TestCase):\n\n  def testBasic(self):\n    with tf.Graph().as_default(), test_util.use_gpu():\n      train_op = build_graph()\n      with self.session() as sess:\n        sess.run(tf.compat.v1.global_variables_initializer())\n        sess.run(train_op)\n        all_vars = tf.compat.v1.all_variables()\n        if tf.test.is_gpu_available():\n          for var in all_vars:\n            self.assertEqual(var.device, '/device:GPU:0')\n        vars_map_maybe_on_gpu = sess.run({var.name: var for var in all_vars})\n    eps = 1e-8\n    found_count = 0\n    for name, val in vars_map_maybe_on_gpu.items():\n      if name.find(\"/m\") >= 0:\n        found_count += 1\n        self.assertNear(val, 0.068750, eps)\n      elif name.find(\"/v\") >= 0:\n        found_count += 1\n        self.assertNear(val, 0.0484, eps)\n      else:\n        found_count += 1\n        # Must be variable\n        self.assertNear(val, 0.031250, eps)\n    self.assertEqual(found_count, 3)\n\n    with tf.Graph().as_default(), test_util.force_cpu():\n      train_op = build_graph()\n      with self.session() as sess:\n        sess.run(tf.compat.v1.global_variables_initializer())\n        sess.run(train_op)\n        all_vars = tf.compat.v1.all_variables()\n        for var in all_vars:\n          self.assertEqual(var.device, '/device:CPU:0')\n        vars_map_on_cpu = sess.run({var.name: var for var in all_vars})\n    self.assertEqual(vars_map_maybe_on_gpu, vars_map_on_cpu)\n\n  def testWeightDecay(self):\n    with tf.Graph().as_default(), test_util.use_gpu():\n      train_op = build_graph()\n      with self.session() as sess:\n        sess.run(tf.compat.v1.global_variables_initializer())\n        sess.run(train_op)\n        all_vars = tf.compat.v1.all_variables()\n        if tf.test.is_gpu_available():\n          for var in all_vars:\n            self.assertEqual(var.device, '/device:GPU:0')\n        vars_map_maybe_on_gpu = sess.run({var.name: var for var in all_vars})\n    eps = 1e-8\n    found_count = 0\n    for name, val in vars_map_maybe_on_gpu.items():\n      if name.find(\"/m\") >= 0:\n        found_count += 1\n        self.assertNear(val, 0.068750, eps)\n      elif name.find(\"/v\") >= 0:\n        found_count += 1\n        self.assertNear(val, 0.0484, eps)\n      else:\n        found_count += 1\n        # Must be variable\n        self.assertNear(val, 0.031250, eps)\n    self.assertEqual(found_count, 3)\n\n    with tf.Graph().as_default(), test_util.force_cpu():\n      train_op = build_graph()\n      with self.session() as sess:\n        sess.run(tf.compat.v1.global_variables_initializer())\n        sess.run(train_op)\n        all_vars = tf.compat.v1.all_variables()\n        for var in all_vars:\n          self.assertEqual(var.device, '/device:CPU:0')\n        vars_map_on_cpu = sess.run({var.name: var for var in all_vars})\n    self.assertEqual(vars_map_maybe_on_gpu, vars_map_on_cpu)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/optimizers/shampoo.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom tensorflow.python.ops import state_ops, io_ops\n\n\n@tf.function\ndef eigen_inverse_root(mat, p, head, tail, damping=1e-3):\n\n  alpha = -1.0 / p\n  dim = mat.shape[0]\n\n  eval, evec = tf.linalg.eigh(mat)\n  non_zero = tf.where(tf.greater(eval, damping))\n  zeros = tf.cond(\n      tf.greater(tf.size(non_zero),\n                 0), lambda: tf.cast(tf.reduce_min(non_zero), dtype=\"int32\"),\n      lambda: tf.constant(0, dtype=\"int32\"))  # Count the number of zeros\n  eval_p = tf.pow(tf.maximum(eval, damping), alpha)\n\n  if tf.greater(head + tail, dim):\n    zeros = 0\n    head = dim\n    tail = 0\n  elif tf.greater(zeros + head + tail, dim):\n    zeros = dim - head - tail\n\n  eval_ht = tf.concat([eval_p[zeros:zeros + head], eval_p[dim - tail:]], 0)\n  # selected eigenvalues\n  evec_ht = tf.concat([evec[:, zeros:zeros + head], evec[:, dim - tail:]], 1)\n  # selected eigenvectors\n\n  if tf.equal(zeros + head + tail, dim):\n    offset = 0.0\n  else:\n    offset = tf.reduce_mean(eval[zeros + head:dim - tail])\n\n  return evec_ht, eval_ht - offset, offset\n\n\ndef apply_sparse_precond(tensor, pvec, pval, offset):\n\n  tensor_tmp_1 = tf.tensordot(tensor, pvec, axes=[[0], [0]])\n  tensor_tmp_2 = tf.multiply(tensor_tmp_1, pval)\n  tensor_tmp_3 = tf.tensordot(tensor_tmp_2, pvec, axes=[[-1], [-1]])\n\n  rank = len(tensor.shape)\n  tensor_transpose = tf.transpose(tensor, perm=list(range(1, rank)) + [0])\n\n  return tensor_tmp_3 + tensor_transpose * offset\n\n\nclass ShampooOptimizer(tf.compat.v1.train.Optimizer):\n\n  def __init__(self,\n               learning_rate=0.03,\n               beta_1: float = 0.9,\n               beta_2: float = 1.0,\n               warmup: int = 5000,\n               tau_1: int = 200,\n               tau_2: int = 20,\n               eigen_head: int = 100,\n               eigen_tail: int = 100,\n               damping_epsilon: float = 1e-3,\n               use_locking: bool = False,\n               name=\"Shampoo\",\n               **kwargs):\n    super().__init__(use_locking, name, **kwargs)\n    self._learning_rate = learning_rate\n    self._beta_1 = beta_1\n    self._beta_2 = beta_2\n    self._warmup = warmup\n    self._tau_1 = tau_1\n    self._tau_2 = tau_2\n    self._eigen_head = eigen_head\n    self._eigen_tail = eigen_tail\n    self._damping_epsilon = damping_epsilon\n\n  def _create_slots(self, var_list):\n\n    for var in var_list:\n      for i, dim in enumerate(var.shape):\n        eigens = min(dim, self._eigen_head + self._eigen_tail)\n        self._get_or_make_slot(var, tf.zeros([dim, dim]), \"s\" + str(i),\n                               self._name + \"/s\" + str(i))\n        self._get_or_make_slot(var, tf.zeros([dim, dim]), \"g\" + str(i),\n                               self._name + \"/g\" + str(i))\n        self._get_or_make_slot(var, tf.zeros([dim, eigens]), \"pvec\" + str(i),\n                               self._name + \"/pvec\" + str(i))\n        self._get_or_make_slot(var, tf.zeros([eigens]), \"pval\" + str(i),\n                               self._name + \"/pval\" + str(i))\n        self._get_or_make_slot(var, tf.zeros([]), \"o\" + str(i),\n                               self._name + \"/o\" + str(i))\n\n      self._zeros_slot(var, 'd', self._name + \"/d\")\n      self._zeros_slot(var, 'm', self._name + \"/m\")\n      self._zeros_slot(var, 'pm', self._name + \"/pm\")\n\n  def _resource_apply_dense(self, grad, var):\n\n    lr = self._learning_rate\n    beta_1 = self._beta_1\n    beta_2 = self._beta_2\n    warmup = self._warmup\n    tau_1 = tf.cast(self._tau_1, dtype='int32')\n    tau_2 = tf.cast(self._tau_2, dtype='int32')\n    eigen_head = self._eigen_head\n    eigen_tail = self._eigen_tail\n    damping_epsilon = self._damping_epsilon\n\n    global_step = tf.cast(tf.compat.v1.train.get_global_step(), dtype='int32')\n    if_update_stat = tf.equal(tf.math.mod(global_step, tau_2), 0)\n    if_warmed_up = tf.greater(global_step, warmup)\n    if_update_precond = tf.math.logical_and(\n        if_warmed_up, tf.equal(tf.math.mod(global_step, tau_1), 0))\n\n    global_step_f = tf.cast(global_step, dtype='float32')\n    warmup_f = tf.cast(self._warmup, dtype='float32')\n    warmup_rate = tf.minimum(tf.maximum(global_step_f / warmup_f - 1.0, 0.0),\n                             1.0)\n\n    if_stat_momentum = tf.less(\n        beta_2,\n        1.0 - 1e-10)  # if beta_2 = 1.0, do not use momentum on statistics\n\n    ops = []\n    rank = len(grad.shape)\n    grad_precond = grad\n\n    for i in range(rank):\n      axes = list(range(i)) + list(range(i + 1, rank))\n\n      g = self.get_slot(var, 'g' + str(i))\n      g_t = tf.cond(\n          if_update_stat, lambda: state_ops.assign(\n              g, tf.tensordot(grad, grad, axes=[axes, axes])),\n          lambda: tf.identity(g))\n\n      s = self.get_slot(var, 's' + str(i))\n      s_t = tf.cond(\n          if_stat_momentum,\n          lambda: state_ops.assign(s, beta_2 * s + (1 - beta_2) * g_t),\n          lambda: state_ops.assign_add(s, g_t))\n\n      pvec = self.get_slot(var, 'pvec' + str(i))\n      pval = self.get_slot(var, 'pval' + str(i))\n      offset = self.get_slot(var, 'o' + str(i))\n\n      def update_precond():\n        pvec_t, pval_t, offset_t = eigen_inverse_root(s_t, 2 * rank, eigen_head,\n                                                      eigen_tail,\n                                                      damping_epsilon)\n        return (state_ops.assign(pvec, pvec_t), state_ops.assign(pval, pval_t),\n                state_ops.assign(offset, offset_t))\n\n      pvec_t, pval_t, offset_t = tf.cond(\n          if_update_precond, lambda: update_precond(), lambda:\n          (tf.identity(pvec), tf.identity(pval), tf.identity(offset)))\n\n      grad_precond = apply_sparse_precond(grad_precond, pvec_t, pval_t,\n                                          offset_t)\n      ops += [\n          g_t,\n          s_t,\n          pvec_t,\n          pval_t,\n          offset_t,\n      ]\n\n    d = self.get_slot(var, 'd')\n    d_t = state_ops.assign_add(d, grad * grad)\n\n    m = self.get_slot(var, 'm')\n    m_t = state_ops.assign(\n        m, beta_1 * m + (1 - beta_1) * grad * tf.math.rsqrt(d_t + 1e-30))\n\n    pm = self.get_slot(var, 'pm')\n    pm_t = state_ops.assign(pm, beta_1 * pm + (1.0 - beta_1) * grad_precond)\n\n    update_diag = lr * m_t\n    # AdaGrad gradient used in warmup steps\n\n    update_second = lr * tf.norm(m_t) / (tf.norm(pm_t) + 1e-10) * pm_t\n    # Shampoo gradient normalized by AdaGrad\n\n    var_t = tf.cond(\n        if_warmed_up, lambda: state_ops.assign_sub(var, (\n            1.0 - warmup_rate) * update_diag + warmup_rate * update_second),\n        lambda: state_ops.assign_sub(var, update_diag))\n\n    ops += [d_t, m_t, pm_t, var_t]\n    return tf.group(*ops)\n\n  def _resource_apply_sparse(self, grad, var):\n    raise tf.no_op()\n"
  },
  {
    "path": "monolith/native_training/prefetch_queue.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport copy\nfrom itertools import accumulate\nfrom itertools import chain\nfrom typing import Any, Callable, Dict, List, Optional, Tuple, Union\n\nfrom absl import logging\n\nimport tensorflow as tf\nfrom tensorflow.python.ops import data_flow_ops\nfrom tensorflow.python.ops import gen_data_flow_ops\n\nfrom monolith.native_training import utils\nfrom monolith.native_training import nested_tensors\n\n\n# Similar to https://github.com/tensorflow/tensorflow/commit/f98b3bc7012085096d8171fe56f6004677461567#\nclass _GPUCompatiblePaddingFIFOQueue(data_flow_ops.QueueBase):\n  \"\"\"A queue implementation that dequeues elements in first-in first-out order.\n  GPUCompatiblePaddingFIFOQueue is like PaddingFIFOQueue, \n  but the queue resource may be placed either on a CPU or on a GPU. \n  It is not cross-device: enqueues and dequeues\n  will be colocated with the queue resource.\n  \"\"\"\n\n  def __init__(self,\n               capacity,\n               dtypes,\n               shapes,\n               names=None,\n               shared_name=None,\n               name=\"padding_fifo_queue\"):\n    \"\"\"A `PaddingFIFOQueue` may contain components with dynamic shape, while also\n    supporting `dequeue_many`.\n    \n    The `shapes` argument must be specified; each component of a queue\n    element must have the respective shape.  Shapes of fixed\n    rank but variable size are allowed by setting any shape dimension to None.\n    \n    Args:\n      capacity: An integer. The upper bound on the number of elements\n        that may be stored in this queue.\n      dtypes:  A list of `DType` objects. The length of `dtypes` must equal\n        the number of tensors in each queue element.\n      shapes: A list of `TensorShape` objects, with the same length as\n        `dtypes`.  Any dimension in the `TensorShape` containing value\n        `None` is dynamic and allows values to be enqueued with\n         variable size in that dimension.\n      names: (Optional.) A list of string naming the components in the queue\n        with the same length as `dtypes`, or `None`.  If specified the dequeue\n        methods return a dictionary with the names as keys.\n      shared_name: (Optional.) If non-empty, this queue will be shared under\n        the given name across multiple sessions.\n      name: Optional name for the queue operation.\n    \"\"\"\n    dtypes = data_flow_ops._as_type_list(dtypes)\n    shapes = data_flow_ops._as_shape_list(shapes,\n                                          dtypes,\n                                          unknown_dim_allowed=True)\n    names = data_flow_ops._as_name_list(names, dtypes)\n    if len(dtypes) != len(shapes):\n      raise ValueError(\"Shapes must be provided for all components, \"\n                       f\"but received {len(dtypes)} dtypes and \"\n                       f\"{len(shapes)} shapes.\")\n    # init_scope() context required\n    queue_ref = gen_data_flow_ops.padding_fifo_queue_v2(\n        component_types=dtypes,\n        shapes=shapes,\n        capacity=capacity,\n        shared_name=data_flow_ops._shared_name(shared_name),\n        name=name)\n\n    super().__init__(dtypes, shapes, names, queue_ref)\n\n  def enqueue_many(self, vals, name=None):\n    \"\"\"enqueue_many is not supported on GPUCompatiblePaddingFIFOQueue.\"\"\"\n    raise NotImplementedError(\n        \"GPUCompatiblePaddingFIFOQueue does not support enqueue_many or dequeue_many, \"\n        \"only enqueue and dequeue.\")\n\n  def dequeue_many(self, n, name=None):\n    \"\"\"dequeue_many is not supported on GPUCompatiblePaddingFIFOQueue.\"\"\"\n    raise NotImplementedError(\n        \"GPUCompatiblePaddingFIFOQueue does not support enqueue_many or dequeue_many, \"\n        \"only enqueue and dequeue.\")\n\n\nclass _QueueBase:\n  \"\"\"Monolith Specialized Prefetch QueueBase.\"\"\"\n\n  @property\n  def queue(self):\n    raise NotImplementedError\n\n  @property\n  def queues(self):\n    raise NotImplementedError\n\n  @property\n  def enqueue_op(self):\n    raise NotImplementedError\n\n  def dequeue(self):\n    raise NotImplementedError\n\n\nclass _FIFOQueue(_QueueBase):\n\n  def __init__(self,\n               dense_list: Optional[List[tf.Tensor]] = None,\n               capacity: int = 2,\n               queue_name: str = \"prefetch_queue\"):\n    if dense_list is None:\n      raise ValueError(\"Arguments `dense_list` should not be empty.\")\n    if dense_list is None:\n      dense_list = []\n    else:\n      if not isinstance(dense_list, list):\n        raise TypeError(\"dense_list should be a list of `tf.Tensor`s\")\n    self._dense_list = dense_list\n    flatten_tensor_list = self._dense_list\n    dtypes = [f.dtype for f in flatten_tensor_list]\n    shapes = [f.shape for f in flatten_tensor_list]\n    with tf.init_scope():\n      self._queue = _GPUCompatiblePaddingFIFOQueue(capacity,\n                                                   dtypes=dtypes,\n                                                   shapes=shapes,\n                                                   name=queue_name)\n    self._enqueue_op = self._queue.enqueue(flatten_tensor_list)\n\n  @property\n  def queue(self):\n    return self._queue\n\n  @property\n  def queues(self):\n    return [self._queue]\n\n  @property\n  def enqueue_op(self):\n    return self._enqueue_op\n\n  def dequeue(self):\n    with tf.init_scope():\n      dequeue_tensor_list = self._queue.dequeue()\n    if not isinstance(dequeue_tensor_list, list):\n      assert len(self._dense_list) == 1\n      return [dequeue_tensor_list]\n    return dequeue_tensor_list\n\n\nclass _MultiFIFOQueue(_QueueBase):\n  \"\"\"Multi-Device FIFOQueue that supports CPU and GPU tensors in queue.\"\"\"\n\n  def __init__(self,\n               dense_list: Optional[List[tf.Tensor]] = None,\n               capacity: int = 2,\n               queue_name: str = \"prefetch_queue\"):\n    # Don't call the super() constructor here. Just inherit for interfaces.\n    self._qs = []\n    dense_list_cpu, dense_list_gpu = self._split_tensor_list_by_device(\n        dense_list)\n    with tf.device(\"/device:CPU:0\"):\n      queue = _FIFOQueue(dense_list=dense_list_cpu,\n                         capacity=capacity,\n                         queue_name=queue_name)\n    self._qs.append(queue)\n    if dense_list_gpu:\n      with tf.device(\"/device:GPU:0\"):\n        queue_gpu = _FIFOQueue(dense_list=dense_list_gpu,\n                               capacity=capacity,\n                               queue_name=queue_name + \"_gpu\")\n      self._qs.append(queue_gpu)\n\n    # enqueue altogether\n    self._enqueue_op = tf.group([q.enqueue_op for q in self._qs])\n\n  @property\n  def queue(self):\n    if len(self._qs) == 1:\n      return self._qs[0].queue\n    else:\n      raise NotImplementedError(\n          \"When using multi-device queues, this interface is disabled.\"\n          \"Check if a tensor to be enqueued is mistakenly placed on GPU.\")\n\n  @property\n  def queues(self):\n    return [q.queue for q in self._qs]\n\n  @property\n  def enqueue_op(self):\n    return self._enqueue_op\n\n  def dequeue(self):\n    n = len(self._qs)\n    if n == 1:\n      return self._qs[0].dequeue()\n    else:\n      # We assume here that when we dequeue, we dequeue both CPU and GPU tensors together;\n      # otherwise we need to enforce mutual control dependencies with gate_op at python level.\n      # Therefore, a C++ implementation of this multi-device FIFOQueue would be a better choice.\n      # TODO(peng.wu): make this whole queue implementation work at C++ TF queue resource level.\n      return self._merge_tensor_list_by_device([q.dequeue() for q in self._qs])\n\n  def size(self):\n    if len(self._qs) == 1:\n      return self.queue.size()\n    else:\n      # Based on the assumption commented in the above \"dequeue()\" method,\n      # it allows to check cpu-device queue size to get the size for all.\n      return self.queues[0].size()\n\n  def _split_tensor_list_by_device(self, tensors):\n    \"\"\"List of tensors to (List of CPU Tensors, List of GPU Tensors).\"\"\"\n    split_tensors = ([], [])  # 0: CPU, 1: GPU\n    self._split_tensors_indices = []\n    for f in tensors:\n      tuple_i = 1 if \"GPU\" in f.device else 0\n      l = split_tensors[tuple_i]\n      idx = (tuple_i, len(l))\n      l.append(f)\n      self._split_tensors_indices.append(idx)\n    return split_tensors\n\n  def _merge_tensor_list_by_device(self, tensor_lists):\n    return [tensor_lists[i][j] for i, j in self._split_tensors_indices]\n\n\nclass MultiQueueRunner(tf.compat.v1.train.QueueRunner):\n\n  def _init_from_args(self,\n                      queue=None,\n                      enqueue_ops=None,\n                      close_op=None,\n                      cancel_op=None,\n                      queue_closed_exception_types=None):\n    \"\"\"Create a QueueRunner from arguments.\"\"\"\n    if isinstance(queue, list):\n      close_op = tf.group([q.close() for q in queue])\n      cancel_op = tf.group(\n          [q.close(cancel_pending_enqueues=True) for q in queue])\n    # else fallback to original QueueRunner\n    super()._init_from_args(\n        queue=queue,\n        enqueue_ops=enqueue_ops,\n        close_op=close_op,\n        cancel_op=cancel_op,\n        queue_closed_exception_types=queue_closed_exception_types)\n\n  @property\n  def queue(self):\n    if isinstance(self._queue, list):\n      raise NotImplementedError(\n          \"When using multi-device queues, this interface is disabled.\")\n    return self._queue\n\n  @property\n  def name(self):\n    if isinstance(self._queue, list):\n      return self._queue[0].name\n    return self._queue.name\n\n\nclass EnqueueHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, q: _QueueBase):\n    self._q_runner = MultiQueueRunner(q.queues, [q.enqueue_op])\n    self._threads = []\n\n  def after_create_session(self, session, coord):\n    self._threads = self._q_runner.create_threads(session,\n                                                  coord=coord,\n                                                  start=True,\n                                                  daemon=True)\n\n\ndef enqueue_dicts_with_queue_return(\n    tensors,\n    capacity: int = 1,\n    queue_name: str = \"prefetch_queue\") -> Tuple[Any, Optional[_FIFOQueue]]:\n  \"\"\"tensors can be any nested structures (list, tuple, dict) with tensors\"\"\"\n  if capacity == 0:\n    return tensors, None\n  nested = nested_tensors.NestedTensors(tensors)\n  flatten_tensors = nested.get_tensors()\n  queue = _MultiFIFOQueue(dense_list=flatten_tensors,\n                          capacity=capacity,\n                          queue_name=queue_name)\n  with tf.init_scope():\n    dequeue_dense_list = queue.dequeue()\n  return nested.get_nested_result(dequeue_dense_list), queue\n\n\nclass AsyncPushHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, queue, ops):\n    self._queue = queue\n    self._queue_init = False\n    self._run_ops = ops\n\n  def begin(self):\n    self._queue_size = self._queue.size()\n\n  def before_run(self, run_context):\n    if self._queue_init:\n      return tf.estimator.SessionRunArgs(self._run_ops)\n\n  def after_run(self, run_context, run_values):\n    if not self._queue_init:\n      self._queue_init = run_context.session.run(self._queue_size) > 0\n\n  def end(self, session):\n    while session.run(self._queue_size) > 0:\n      session.run(self._run_ops)\n\n\nclass AsyncFunctionMgr:\n  \"\"\"A class that supports adding async functions\"\"\"\n\n  def __init__(self, is_async: bool = True):\n    \"\"\"\n    Args:\n      is_async - by default, added async function will be executed asyncly or not.\n    \"\"\"\n    self._is_async = is_async\n    self._hooks = []\n\n  def add_async_function(\n      self,\n      target: Callable,\n      args: Tuple = None,\n      kwargs: Dict = None,\n      is_async: bool = None,\n      queue_name: str = \"async_queue\") -> Union[tf.Operation, Any]:\n    \"\"\"\n    Args:\n      is_async - if execute target synchronously. If None, will use default value in __init__.\n    \"\"\"\n    if is_async is None:\n      is_async = self._is_async\n    if args is None:\n      args = ()\n    if kwargs is None:\n      kwargs = {}\n\n    if is_async:\n      # This prevents from using an empty input list.\n      args = (args) + (tf.constant(0, name=\"dummy_tensor_for_async_function\"),)\n      (args, kwargs), queue = enqueue_dicts_with_queue_return(\n          (args, kwargs), queue_name=queue_name)\n      dummy_op = tf.no_op(name=\"dummy_depended_op_for_async_function\")\n      with tf.init_scope(), tf.control_dependencies([args[-1], dummy_op]):\n        run_ops = target(*args[0:-1], **kwargs)\n      self._hooks.append(AsyncPushHook(queue, run_ops))\n\n      # Check ops dependence in async func.\n      utils.check_ops_dependence(queue.enqueue_op.name, dummy_op.name)\n\n      return queue.enqueue_op\n    else:\n      return target(*args, **kwargs)\n\n  @property\n  def hooks(self):\n    return self._hooks\n"
  },
  {
    "path": "monolith/native_training/prefetch_queue_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport time\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import test_util\n\nfrom monolith.native_training import nested_tensors\nfrom monolith.native_training import prefetch_queue\n\n\nclass GPUCompatiblePaddingFIFOQueueTests(tf.test.TestCase):\n\n  def testEnqueueAndDequeue(self):\n    with test_util.use_gpu():\n      q = prefetch_queue._GPUCompatiblePaddingFIFOQueue(10, tf.float32, ((),))\n      elems_numpy = [10.0, 20.0, 30.0]\n      _a = tf.constant(2.0)\n      elems = [tf.constant(x) / _a for x in elems_numpy]\n      # MemcpyH2D * 3: [10.0, 20.0, 30.0]\n      # MemcpyH2D * 3: _a\n      # GPU RealDiv * 3\n      for x in elems:\n        self.evaluate(q.enqueue((x,)))\n\n      _b = tf.constant(3.0)\n      _c = tf.constant(1.0)\n      dequeued_tensor = q.dequeue()\n      for i in range(len(elems)):\n        # Ensure same device\n        self.assertEqual(elems[0].device, dequeued_tensor.device)\n        # MemcpyH2D * 3: _b\n        # MemcpyH2D * 3: _c\n        # GPU Mul * 3\n        # GPU Add * 3\n        # MemcpyD2H * 3: return\n        vals = self.evaluate(dequeued_tensor * _b + _c)\n        self.assertEqual([elems_numpy[i] / 2 * 3 + 1], vals)\n\n  def testGPUQueueCPUTensor(self):\n    with tf.device(\"CPU:0\"):\n      elems_numpy = [7, 8, 9]\n      _a = tf.constant(5)\n      elems = [tf.constant(x) * _a for x in elems_numpy]\n\n    with test_util.use_gpu():\n      # MemcpyH2D * 3\n      q = prefetch_queue._GPUCompatiblePaddingFIFOQueue(10, tf.int32, ((),))\n      # MemcpyD2H * 3\n      # Note that even though the below enqueue/dequeue are declared on CPU,\n      # it still copys-to and holds the enqueued resources on GPU.\n      # So to pin the tensors on CPU, we need to declare the queue itself on CPU.\n\n    for x in elems:\n      with tf.device(\"CPU:0\"):\n        self.evaluate(q.enqueue((x,)))\n\n    with tf.device(\"CPU:0\"):\n      dequeued_tensor = q.dequeue()\n\n    for i in range(len(elems)):\n      self.assertEqual(elems[0].device, dequeued_tensor.device)\n      with tf.device(\"CPU:0\"):\n        vals = self.evaluate(dequeued_tensor + 2)\n      self.assertEqual([elems_numpy[i] * 5 + 2], vals)\n\n  def testMultiEnqueueAndDequeue(self):\n    with test_util.use_gpu():\n      q = prefetch_queue._GPUCompatiblePaddingFIFOQueue(10,\n                                                        (tf.int32, tf.float32),\n                                                        ((), ()))\n      elems_numpy = [(5, 10.0), (10, 20.0), (15, 30.0)]\n      elems = [(tf.constant(x), tf.constant(y)) for x, y in elems_numpy]\n\n      for x, y in elems:\n        self.evaluate(q.enqueue((x, y)))\n\n      dequeued_tensor = q.dequeue()\n      print(dequeued_tensor[0].device)\n      for i in range(len(elems)):\n        self.assertEqual(elems[i][0].device, dequeued_tensor[0].device)\n        x_val, y_val = self.evaluate(dequeued_tensor)\n        x, y = elems_numpy[i]\n        self.assertEqual(x, x_val)\n        self.assertEqual(y, y_val)\n\n  def testIdentityHelper(self):\n    with tf.device(\"CPU:0\"):\n      a = tf.constant(1)\n      b = a + 1\n    with test_util.use_gpu():\n      c = tf.identity(b)\n      q = prefetch_queue._GPUCompatiblePaddingFIFOQueue(1, tf.int32, ((),))\n      self.evaluate(q.enqueue(c))  # MemcpyH2D: CPU b to GPU c pinned in queue\n      self.assertAllEqual(self.evaluate(q.dequeue()), 2)  # MemcpyD2H: return\n\n\nclass FIFOQueueTest(tf.test.TestCase):\n\n  def test_fifo_queue_data(self):\n    dense_tensors = [tf.constant(2.), tf.constant([[3], [4]])]\n    ragged_tensors = [\n        tf.ragged.constant([[2], [-1, 3]]),\n        tf.ragged.constant([[1.], []])\n    ]\n    nested = nested_tensors.NestedTensors((dense_tensors, ragged_tensors))\n    flatten_tensors = nested.get_tensors()\n    queue = prefetch_queue._FIFOQueue(flatten_tensors)\n    dequeued = queue.dequeue()\n    dequeued_dense, dequeued_ragged = nested.get_nested_result(dequeued)\n    with self.session() as sess:\n      sess.run(queue.enqueue_op)\n      dequeued_dense, dequeued_ragged = sess.run(\n          [dequeued_dense, dequeued_ragged])\n    self.assertAllClose(dequeued_dense[0], 2.)\n    self.assertAllEqual(dequeued_dense[1], [[3], [4]])\n    self.assertAllEqual(dequeued_ragged[0], [[2], [-1, 3]])\n    self.assertAllClose(dequeued_ragged[1], [[1.], []])\n\n  def test_fifo_queue_capacity(self):\n    dense_tensors = [tf.constant([2])]\n    queue = prefetch_queue._FIFOQueue(dense_tensors, capacity=4)\n    dequeue_result = queue.dequeue()\n    with self.session() as sess:\n      for _ in range(4):\n        sess.run(queue.enqueue_op)\n      for _ in range(4):\n        result = sess.run(dequeue_result)\n        self.assertAllEqual(result[0], [2])\n\n\nclass PrefetchTest(tf.test.TestCase):\n\n  def test_enqueue_dicts_with_queue_return(self):\n    dense_dicts = [{\n        \"dense_0_0\": tf.constant(2.),\n        \"dense_0_1\": tf.constant([[3], [4]])\n    }, {\n        \"dense_1_0\": tf.constant([0])\n    }]\n    ragged_dicts = [{\n        \"ragged_0_0\": tf.ragged.constant([[2], [-1, 3]]),\n        \"ragged_0_1\": tf.ragged.constant([[1.], []])\n    }, {\n        \"ragged_1_0\": tf.ragged.constant([[0, 0], [1]])\n    }]\n    with test_util.use_gpu():\n      dense_dicts[0][\"dense_0_0\"] += 1.0\n    result = prefetch_queue.enqueue_dicts_with_queue_return(\n        (dense_dicts, ragged_dicts), capacity=3)\n    (dequeue_dense_dicts, dequeue_ragged_dicts), queue = result\n\n    with self.session() as sess:\n      for _ in range(5):\n        sess.run(queue.enqueue_op)\n        dense_dicts_result = sess.run(dequeue_dense_dicts)\n        self.assertAllClose(dense_dicts_result[0][\"dense_0_0\"], 2. + 1.0)\n        self.assertAllEqual(dense_dicts_result[0][\"dense_0_1\"], [[3], [4]])\n        self.assertAllEqual(dense_dicts_result[1][\"dense_1_0\"], [0])\n\n        sess.run(queue.enqueue_op)\n        dense_dicts_result, ragged_dicts_result = sess.run(\n            [dequeue_dense_dicts, dequeue_ragged_dicts])\n        self.assertAllEqual(ragged_dicts_result[0][\"ragged_0_0\"],\n                            [[2], [-1, 3]])\n        self.assertAllClose(ragged_dicts_result[0][\"ragged_0_1\"], [[1.], []])\n        self.assertAllEqual(ragged_dicts_result[1][\"ragged_1_0\"], [[0, 0], [1]])\n        self.assertAllClose(dense_dicts_result[0][\"dense_0_0\"], 2. + 1.0)\n\n  def test_enqueue_dicts_with_queue_return(self):\n    tensors = ([{\n        \"a\": tf.constant(1.0),\n        \"b\": \"abc\",\n        \"c\": None,\n        \"d\": None,\n    }], {\n        \"a\": tf.Variable(0.5),\n        \"b\": tf.ragged.constant([[1.0]])\n    })\n    dequeued_tensors, q = prefetch_queue.enqueue_dicts_with_queue_return(\n        tensors)\n    self.assertAllEqual(dequeued_tensors[0][0][\"b\"], \"abc\")\n    del dequeued_tensors[0][0][\"b\"]\n    self.assertEqual(dequeued_tensors[0][0][\"c\"], None)\n    del dequeued_tensors[0][0][\"c\"]\n    self.assertEqual(dequeued_tensors[0][0][\"d\"], None)\n    del dequeued_tensors[0][0][\"d\"]\n\n    with tf.compat.v1.train.SingularMonitoredSession() as sess:\n      sess.run(q.enqueue_op)\n      tensors = sess.run(dequeued_tensors)\n      self.assertAllEqual(tensors[0][0][\"a\"], 1.0)\n      self.assertAllEqual(tensors[1][\"a\"], 0.5)\n      self.assertAllEqual(tensors[1][\"b\"], [[1.0]])\n\n  def test_enqueue_dicts_with_control_flow(self):\n    v = tf.Variable(0)\n    with tf.control_dependencies([v.assign_add(1)]):\n      tensor, q = prefetch_queue.enqueue_dicts_with_queue_return(tf.constant(0))\n    with tf.compat.v1.train.MonitoredSession() as sess:\n      sess.run(q.enqueue_op)\n      sess.run(tensor)\n      self.assertAllEqual(sess.run(v), 1)\n\n  def test_enqueue_with_zero_capacity(self):\n    dense_dicts = [{\"dense\": tf.constant([0])}]\n    ragged_dicts = [{\"ragged\": tf.ragged.constant([[0, 0], [1]])}]\n    result = prefetch_queue.enqueue_dicts_with_queue_return(\n        (dense_dicts, ragged_dicts), 0)\n    (dequeue_dense_dicts, dequeue_ragged_dicts), queue = result\n    with self.session() as sess:\n      dequeue_dense_dicts = sess.run(dequeue_dense_dicts)\n      dequeue_ragged_dicts = sess.run(dequeue_ragged_dicts)\n    self.assertAllEqual(dequeue_dense_dicts[0][\"dense\"], [0])\n    self.assertAllEqual(dequeue_ragged_dicts[0][\"ragged\"], [[0, 0], [1]])\n\n  def test_estimator_prefetch(self):\n\n    def input_fn():\n      return tf.data.Dataset.range(0, 20).map(\n          lambda x: {\"rag\": tf.ragged.constant([[0], []], dtype=tf.int64) + x})\n\n    def model_fn(features, mode):\n      ragged = features[\"rag\"]\n      ragged_dicts = [{\"ragged\": ragged}]\n      dequeue_raggeds, queue = prefetch_queue.enqueue_dicts_with_queue_return(\n          ragged_dicts)\n      predictions = dequeue_raggeds[0][\"ragged\"].values\n      enqueue_hook = prefetch_queue.EnqueueHook(queue)\n\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      train_op = tf.compat.v1.assign_add(global_step, 1)\n      return tf.estimator.EstimatorSpec(mode=mode,\n                                        predictions=predictions,\n                                        prediction_hooks=(enqueue_hook,),\n                                        train_op=train_op)\n\n    estimator = tf.estimator.Estimator(model_fn)\n    predicts = estimator.predict(input_fn)\n    self.assertAllEqual(list(range(20)), list(predicts))\n\n\nclass AsyncManagerTest(tf.test.TestCase):\n\n  def testBasic(self):\n    x = tf.Variable(0.0)\n\n    def add(y):\n      return x.assign_add(y)\n\n    mgr = prefetch_queue.AsyncFunctionMgr()\n    op = mgr.add_async_function(add, (tf.constant(1.0),))\n    with tf.compat.v1.train.SingularMonitoredSession(hooks=mgr.hooks) as sess:\n      sess.run(op)\n      # Make push happen.\n      sess.run(op)\n      x_value = sess.run(x)\n      # Since it is async pushed, the value will be 1.\n      self.assertAllEqual(x_value, 1.0)\n\n  def testSync(self):\n    x = tf.Variable(0.0)\n\n    def add(y):\n      return x.assign_add(y)\n\n    mgr = prefetch_queue.AsyncFunctionMgr(is_async=False)\n    op = mgr.add_async_function(add, (tf.constant(1.0),))\n    with tf.compat.v1.train.SingularMonitoredSession(hooks=mgr.hooks) as sess:\n      sess.run(op)\n      x_value = sess.run(x)\n      self.assertAllEqual(x_value, 1.0)\n\n  def testEmptyInput(self):\n    x = tf.Variable(0)\n\n    def add():\n      return x.assign_add(1)\n\n    mgr = prefetch_queue.AsyncFunctionMgr()\n    op = mgr.add_async_function(add)\n    with tf.compat.v1.train.SingularMonitoredSession(hooks=mgr.hooks) as sess:\n      sess.run(op)\n      # Make push happen.\n      sess.run(op)\n      x_value = sess.run(x)\n      # Since it is async pushed, the value will be 1.\n      self.assertAllEqual(x_value, 1)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/proto/BUILD",
    "content": "load(\"@com_github_grpc_grpc//bazel:python_rules.bzl\", \"py_grpc_library\", \"py_proto_library\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nproto_library(\n    name = \"primus_am_service_proto\",\n    srcs = [\"primus_am_service.proto\"],\n    deps = [\"@com_google_protobuf//:wrappers_proto\"],\n)\n\npy_proto_library(\n    name = \"primus_am_service_py_proto\",\n    deps = [\n        \":primus_am_service_proto\",\n    ],\n)\n\npy_grpc_library(\n    name = \"primus_am_service_py_proto_grpc\",\n    srcs = [\"primus_am_service_proto\"],\n    deps = [\":primus_am_service_py_proto\"],\n)\n\nproto_library(\n    name = \"debugging_info_proto\",\n    srcs = [\"debugging_info.proto\"],\n)\n\npy_proto_library(\n    name = \"debugging_info_py_proto\",\n    deps = [\n        \":debugging_info_proto\",\n    ],\n)\n\nproto_library(\n    name = \"ckpt_info_proto\",\n    srcs = [\"ckpt_info.proto\"],\n)\n\npy_proto_library(\n    name = \"ckpt_info_py_proto\",\n    deps = [\n        \":ckpt_info_proto\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/proto/ckpt_info.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto3\";\n\npackage monolith;\n\nmessage CkptInfo {\n  map<int32, int64> slot_counts = 1;\n  map<string, int64> feature_counts = 2;\n}"
  },
  {
    "path": "monolith/native_training/proto/debugging_info.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto3\";\n\nmessage DebuggingInfo {\n  Cluster cluster = 1;\n  uint32 num_workers = 2;\n  repeated FeatureNameConfig feature_name_configs = 3;\n}\n\nmessage Cluster {\n  string chief_addr = 1;\n  repeated string ps_addrs = 2;\n}\n\nmessage FeatureNameConfig {\n  string feature_name = 1;\n  string config_str = 2;\n}\n"
  },
  {
    "path": "monolith/native_training/proto/primus_am_service.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto3\";\noption java_package = \"com.bytedance.primus.proto\";\noption java_generate_equals_and_hash = true;\n\nimport \"google/protobuf/wrappers.proto\";\npackage primus;\n\nenum PonyState {\n  PS_REGISTERING = 0;\n  PS_REGISTERED = 1;\n  RUNNING = 2;\n  FINISH = 3;\n}\n\nmessage PonyHeartbeatRequest {\n}\n\nmessage PonyHeartbeatResponse {\n  PonyState state = 1;\n}\n\nmessage PSInfo {\n  string ip = 1;\n  repeated int32 ports = 2;\n  int32 shard_id = 3;\n  string name = 4;\n}\n\nmessage PonyGetPSInfoRequest {\n}\n\nmessage PonyGetPSInfoResponse {\n  repeated PSInfo ps_info = 1;\n}\n\nmessage PonyStartWorkerRequest {\n}\n\nmessage PonyStartWorkerResponse {\n}\n\nmessage SucceedRequest {\n  int32 exit_code = 1;\n  string diagnose = 2;\n  google.protobuf.Int64Value graceful_shutdown_timeout_ms = 3;\n}\n\nmessage SucceedResponse {\n}\n\nmessage KillRequest {\n  int32 exit_code = 1;\n  string diagnose = 2;\n  google.protobuf.Int64Value graceful_shutdown_timeout_ms = 3;\n}\n\nmessage KillResponse {\n}\n\nmessage SuspendRequest {\n  int32 snapshot_id = 1;\n}\n\nmessage SuspendResponse {\n}\n\nmessage SuspendStatusRequest {\n}\n\nmessage SuspendStatusResponse {\n  bool succeed = 1;\n  string message = 2;\n}\n\nmessage ResumeRequest {\n}\n\nmessage ResumeResponse {\n}\n\nmessage GetSnapshotRequest {\n  int32 snapshot_id = 1;\n}\n\nmessage GetSnapshotResponse {\n  bool available = 1;\n  string dir = 2;\n}\n\nmessage ProgressRequest {\n}\n\nmessage ProgressResponse {\n  float progress = 1;\n}\n\nmessage StarvingRequest {\n}\n\nmessage StarvingResponse {\n  bool starving = 1;\n}\n\nmessage StatusRequest {\n}\n\nmessage StatusResponse {\n  string app_id = 1;\n  string final_status = 2;\n  string track_url = 3;\n}\n\nmessage TaskTimePointRequest {\n}\n\nmessage TaskTimePointResponse {\n  string time_point = 1;\n}\n\nmessage CreateSavepointRequest {\n  string savepoint_dir = 1;\n}\n\nmessage CreateSavepointResponse {\n  int32 code = 1;\n  string message = 2;\n  string savepoint_id = 3;\n}\n\nmessage CreateSavepointStatusRequest {\n  string savepoint_restore_id = 1;\n}\n\nmessage CreateSavepointStatusResponse {\n  enum CreateSavepointState {\n    PENDING = 0;\n    RUNNING = 1;\n    SUCCEEDED = 2;\n    FAILED = 3;;\n  }\n  int32 code = 1;\n  string message = 2;\n  CreateSavepointState create_savepoint_state = 3;\n}\n\nservice AppMasterService {\n  rpc ponyHeartbeat (PonyHeartbeatRequest) returns (PonyHeartbeatResponse);\n  rpc ponyGetPSInfo (PonyGetPSInfoRequest) returns (PonyGetPSInfoResponse);\n  rpc ponyStartWorker (PonyStartWorkerRequest) returns (PonyStartWorkerResponse);\n  rpc succeed (SucceedRequest) returns (SucceedResponse);\n  rpc kill (KillRequest) returns (KillResponse);\n  rpc suspend (SuspendRequest) returns (SuspendResponse);\n  rpc suspendStatus (SuspendStatusRequest) returns (SuspendStatusResponse);\n  rpc resume (ResumeRequest) returns (ResumeResponse);\n  rpc getSnapshot (GetSnapshotRequest) returns (GetSnapshotResponse);\n  rpc progress (ProgressRequest) returns (ProgressResponse);\n  rpc isStarving (StarvingRequest) returns (StarvingResponse);\n  rpc status (StatusRequest) returns (StatusResponse);\n  rpc getTaskTimePoint (TaskTimePointRequest) returns (TaskTimePointResponse);\n  rpc createSavepoint(CreateSavepointRequest) returns (CreateSavepointResponse);\n  rpc createSavepointStatus(CreateSavepointStatusRequest) returns (CreateSavepointStatusResponse);\n}\n"
  },
  {
    "path": "monolith/native_training/ps_benchmark.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport dataclasses\nimport time\nfrom typing import Dict, List\n\nfrom absl import logging\nimport tensorflow as tf\n\nfrom monolith.native_training.optimizers.adamom import AdamomOptimizer\nfrom monolith.native_training import logging_ops\nfrom monolith.native_training import native_task\nfrom monolith.native_training import service_discovery\nfrom monolith.native_training import utils\n\n# We need a scope name unique enough to prevent name confliction.\n_SCOPE_NAME = \"machine_benchmark_I_AM_PECULIAR\"\n\n\n@dataclasses.dataclass\nclass BenchmarkConfig:\n  ps_list: List\n  num_ps_required: int\n  num_workers: int\n  index: int\n  benchmark_secs: float = 60.0\n  # If non-empty, it will skip the benchmark and\n  # use `ps_str_overridden` here. Separated by `,`\n  #\n  # Example: `127.0.0.1:1,127.0.0.1:2`\n  ps_str_overridden: str = \"\"\n\n\nclass _BenchmarkWorkerHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, config: BenchmarkConfig, throughput_tensor: tf.Tensor):\n    with tf.name_scope(_SCOPE_NAME):\n      self._config = config\n      self._throughput_tensor = throughput_tensor\n\n      self._result = tf.Variable(\"\", trainable=False)\n      self._result_placeholder = tf.compat.v1.placeholder(tf.string, [])\n      self._result_assign = self._result.assign(self._result_placeholder)\n      self._ready = tf.Variable([False] * self._config.num_workers,\n                                trainable=False)\n      self._make_ready = self._ready[self._config.index].assign(True)\n      self._done = tf.Variable([False] * self._config.num_workers,\n                               trainable=False)\n      self._make_done = self._done[self._config.index].assign(True)\n      self._start_time = None\n\n  def after_create_session(self, sess, coord):\n    sess.run(self._make_ready)\n    self._wait(lambda: sum(sess.run(self._ready)) >= int(self._config.\n                                                         num_workers * 0.9))\n    # Before we start we wait for another 1 secs to make sure everyone\n    # got the result.\n    time.sleep(1)\n    logging.info(\"Benchmark started.\")\n    self._start_time = time.time()\n\n  def before_run(self, run_context):\n    if self._config.ps_str_overridden:\n      run_context.session.run(\n          self._result_assign,\n          feed_dict={self._result_placeholder: self._config.ps_str_overridden})\n    result_value = run_context.session.run(self._result)\n    if result_value:\n      raise tf.errors.OutOfRangeError(None, None, \"Benchmark is done already.\")\n\n  def after_run(self, run_context, run_values):\n    duration = time.time() - self._start_time\n    logging.info(\"Benchmarking {} seconds\".format(duration))\n    run_context.request_stop()\n\n  def end(self, sess):\n    sess.run(self._make_done)\n    self._wait(lambda: sum(sess.run(self._done)) == self._config.num_workers,\n               timeout=10)\n\n    if self._config.index == 0 and not self._config.ps_str_overridden:\n      # OK now we know how ps should look like.\n      throughput_value = sess.run(self._throughput_tensor)\n      reversed_sorted_throughput_and_ps = sorted(\n          [[throughput, i, self._config.ps_list[i].split(\":\")[0]]\n           for i, throughput in enumerate(throughput_value)])\n      sorted_throughput_and_ps = [\n          item[:] for item in reversed_sorted_throughput_and_ps\n      ]\n\n      logging.info(\"Measure result (throughput, ps): {}\".format([\n          \"ps_{}({}):{}\".format(ps, ip, throughput)\n          for throughput, ps, ip in reversed(sorted_throughput_and_ps)\n      ]))\n\n      for i in range(len(reversed_sorted_throughput_and_ps) - 1):\n        for j in range(i + 1, len(reversed_sorted_throughput_and_ps)):\n          if reversed_sorted_throughput_and_ps[i][\n              2] == reversed_sorted_throughput_and_ps[j][2]:\n            sorted_throughput_and_ps[j][0] += reversed_sorted_throughput_and_ps[\n                i][0]\n\n      sorted_throughput_and_ps = sorted(sorted_throughput_and_ps, reverse=True)\n\n      logging.info(\n          \"Measure result (throughput, ps) (ps with the same ip addresses had their throughput adjusted): {}\"\n          .format([\n              \"ps_{}({}):{}\".format(ps, ip, throughput)\n              for throughput, ps, ip in sorted_throughput_and_ps\n          ]))\n\n      selected_ps = [\n          self._config.ps_list[i] for throughput, i, _ in\n          sorted_throughput_and_ps[:self._config.num_ps_required]\n      ]\n      ps_str = \",\".join(selected_ps)\n      sess.run(self._result_assign,\n               feed_dict={self._result_placeholder: ps_str})\n\n    ps_str = \"\"\n\n    def ps_ready():\n      nonlocal ps_str\n      ps_str = sess.run(self._result)\n      return bool(ps_str)\n\n    self._wait(ps_ready)\n    self._config.ps_list.clear()\n    selected_ps = ps_str.decode().split(\",\")\n    for i in range(self._config.num_ps_required):\n      self._config.ps_list.append(selected_ps[i])\n\n  def _wait(self, cond, timeout=3600):\n    start_time = time.time()\n    while time.time() - start_time < timeout:\n      if cond():\n        break\n      time.sleep(0.5)\n\n\nclass _DummyCheckpointSaverHook(tf.estimator.CheckpointSaverHook):\n  \"\"\"A saver hook which won't perform the first save (which happpend on after_create_session).\"\"\"\n\n  def __init__(self, checkpoint_dir=None, save_steps=10240, **kwargs):\n    if not checkpoint_dir:\n      checkpoint_dir = os.path.join(os.environ.get('HOME', \"/\"), 'tmp')\n    super(_DummyCheckpointSaverHook, self).__init__(checkpoint_dir, save_steps)\n    logging.info(\"Create DummyCheckpointSaverHook.\")\n\n  def begin(self):\n    return\n\n  def after_create_session(self, session, coord):\n    return\n\n  def before_run(self, run_context):\n    return None\n\n  def after_run(self, run_context, run_values):\n    return\n\n  def end(self, session):\n    return\n\n  def _save(self, session, step: int) -> bool:\n    return False\n\n\nclass PsBenchMarkTask(native_task.NativeTask):\n\n  @classmethod\n  def params(cls):\n    p = super().params()\n    p.define(\"bm_config\", None, \"The BenchmarkConfig.\")\n    return p\n\n  def create_input_fn(self, mode):\n    del mode\n\n    def input_fn():\n      with tf.name_scope(_SCOPE_NAME):\n        return tf.data.Dataset.from_tensor_slices([[\n            tf.constant(0.12),\n            tf.constant(0.23),\n            tf.constant(0.34),\n            tf.constant(0.45)\n        ]]).repeat().prefetch(2)\n\n    return input_fn\n\n  def create_model_fn(self):\n\n    def model_fn(features, mode, config):\n      logging.info(\"Running model_fn of the ps benchmark\")\n      del config\n      bm_config: BenchmarkConfig = self.p.bm_config\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      with tf.name_scope(_SCOPE_NAME):\n        throughputs = []\n        for ps_i in range(len(bm_config.ps_list)):\n          with tf.device(utils.ps_device(ps_i)):\n            var = tf.Variable(initial_value=[[0.0] * 256] * 256, trainable=True)\n            with tf.control_dependencies([features]):\n              ts_before = tf.timestamp()\n\n            i = tf.constant(0)\n\n          grad = tf.reshape(tf.tile(features, [16384]), [256, 256])\n\n          def while_body(i):\n            nonlocal var\n            nonlocal grad\n            with tf.control_dependencies([i]):\n              new_grads = tf.split(grad, [64, 64, 64, 64], axis=1)\n              output_grads = []\n              for ii in range(4):\n                sum_grads = []\n                for jj in range(10):\n                  a, b, c, d = tf.split(new_grads[ii] +\n                                        tf.cast(jj / 10, dtype=tf.float32),\n                                        [16, 16, 16, 16],\n                                        axis=1)\n                  for _ in range(10):\n                    sum_grads.append(tf.math.sqrt(tf.math.sqrt(a * b) * c + d))\n                output_grads.append(tf.math.add_n(sum_grads))\n              concat_grads = tf.concat(output_grads, -1)\n              var_fetched = tf.identity(var)\n            with tf.control_dependencies([var_fetched, concat_grads]):\n              return i + 1\n\n          def cond(i):\n            nonlocal ts_before\n            with tf.control_dependencies([i]):\n              ts_now = tf.timestamp()\n            return ts_now - ts_before <= bm_config.benchmark_secs\n\n          with tf.device(utils.ps_device(ps_i)):\n            (i,) = tf.while_loop(cond, while_body, [i])\n            j = tf.identity(i)\n          with tf.control_dependencies([j]):\n            ts_now = tf.timestamp()\n            throughput = tf.cast(j, tf.float32) / tf.cast(\n                ts_now - ts_before, tf.float32)\n          throughputs.append(throughput)\n        mean_throughput, update_op = tf.compat.v1.metrics.mean_tensor(\n            tf.stack(throughputs))\n        hook = _BenchmarkWorkerHook(bm_config, mean_throughput)\n        saver_hook = _DummyCheckpointSaverHook()\n        inc_global_step = global_step.assign_add(1)\n        if mode == tf.estimator.ModeKeys.PREDICT:\n          return tf.estimator.EstimatorSpec(mode=mode,\n                                            predictions=tf.constant(0.0))\n        return tf.estimator.EstimatorSpec(mode=mode,\n                                          loss=tf.constant(0.0),\n                                          train_op=tf.group(\n                                              update_op, inc_global_step),\n                                          training_hooks=[hook],\n                                          training_chief_hooks=[saver_hook])\n\n    return model_fn\n"
  },
  {
    "path": "monolith/native_training/ps_benchmark_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nimport tensorflow as tf\n\nfrom absl import app\n\nfrom monolith.native_training import ps_benchmark\nfrom monolith.native_training import cpu_training\nfrom monolith.native_training import utils\n\n\nclass PsBenchmarkTest(tf.test.TestCase):\n\n  def testBasic(self):\n    p = ps_benchmark.PsBenchMarkTask.params()\n    p.bm_config = ps_benchmark.BenchmarkConfig(ps_list=[\"ps0\", \"ps1\"],\n                                               num_ps_required=1,\n                                               num_workers=1,\n                                               index=0,\n                                               benchmark_secs=1.0)\n    cpu_training.local_train(p,\n                             num_ps=2,\n                             model_dir=utils.get_test_tmp_dir() + \"/basic\")\n    self.assertEqual(len(p.bm_config.ps_list), 1)\n\n  def testSkipBenchmark(self):\n    p = ps_benchmark.PsBenchMarkTask.params()\n    p.bm_config = ps_benchmark.BenchmarkConfig(ps_list=[\"ps0\", \"ps1\"],\n                                               num_ps_required=1,\n                                               num_workers=1,\n                                               index=0,\n                                               benchmark_secs=1.0,\n                                               ps_str_overridden=\"overridden\")\n    cpu_training.local_train(p,\n                             num_ps=2,\n                             model_dir=utils.get_test_tmp_dir() +\n                             \"/skip_benchmark\")\n    self.assertEqual(p.bm_config.ps_list[0], \"overridden\")\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  app.run(tf.test.main)\n"
  },
  {
    "path": "monolith/native_training/ragged_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\nops = gen_monolith_ops\n\n\ndef fused_value_rowids(rt: tf.RaggedTensor):\n  \"\"\"Equivalent to rt.value_rowids(), but with much less ops.\"\"\"\n  if not isinstance(rt, tf.RaggedTensor):\n    raise ValueError(\"rt must be RaggedTensor\")\n  if not hasattr(rt, \"monolith_fused_value_rowids\"):\n    rt.monolith_fused_value_rowids = ops.monolith_fused_value_rowids(\n        rt.row_splits)\n  return rt.monolith_fused_value_rowids\n"
  },
  {
    "path": "monolith/native_training/ragged_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training import ragged_utils\n\n\nclass RaggedUtilsTestCase(tf.test.TestCase):\n\n  def test_basic(self):\n    rt = tf.ragged.constant([[], [1], [2, 3]])\n    valueids = ragged_utils.fused_value_rowids(rt)\n    valueids2 = ragged_utils.fused_value_rowids(rt)\n    self.assertIs(valueids, valueids2)\n    self.assertAllEqual(valueids, [1, 2, 2])\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/remote_predict_ops.py",
    "content": ""
  },
  {
    "path": "monolith/native_training/restore_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\n\nimport tensorflow as tf\n\nfrom monolith.native_training import basic_restore_hook\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training import save_utils\nfrom monolith.native_training import utils\n\n\ndef _generate_config(servers, job_name=utils.PS_JOB_NAME):\n  \"\"\"Generates a config based on servers\"\"\"\n  cluster_def = tf.train.ClusterDef()\n  job = cluster_def.job.add()\n  job.name = job_name\n  for i, server in enumerate(servers):\n    job.tasks[i] = server.target[len('grpc://'):]\n  session_config = tf.compat.v1.ConfigProto(cluster_def=cluster_def)\n  session_config.experimental.share_session_state_in_clusterspec_propagation = True\n  return session_config\n\n\ndef _get_id_tensor(x):\n  return tf.constant(x, dtype=tf.int64)\n\n\nclass PartialRestoreTest(tf.test.TestCase):\n\n  def build_graph(self):\n    with tf.device(utils.ps_device(0)):\n      global_step = tf.compat.v1.train.get_or_create_global_step()\n      global_step_op = tf.compat.v1.assign_add(global_step, 1)\n      v0 = tf.Variable(0, name=\"v0\")\n      op0 = tf.compat.v1.assign_add(v0, 1)\n      hash_table0 = hash_table_ops.test_hash_table(1, name_suffix=\"0\")\n      add_op0 = hash_table0.assign_add(_get_id_tensor([0]),\n                                       tf.constant([[1]],\n                                                   dtype=tf.float32)).as_op()\n      lookup0 = hash_table0.lookup(_get_id_tensor([0]))\n    with tf.device(utils.ps_device(1)):\n      v1 = tf.Variable(0, name=\"v1\")\n      op1 = tf.compat.v1.assign_add(v1, 1)\n      hash_table1 = hash_table_ops.test_hash_table(1, name_suffix=\"1\")\n      add_op1 = hash_table1.assign_add(_get_id_tensor([1]),\n                                       tf.constant([[1]],\n                                                   dtype=tf.float32)).as_op()\n      lookup1 = hash_table1.lookup(_get_id_tensor([1]))\n    return tf.group(global_step_op, op0, op1, add_op0,\n                    add_op1), v0, v1, lookup0, lookup1\n\n  def test_restore_with_ps_monitor(self):\n    basename = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                            \"test_restore_with_ps_monitor\", \"model.ckpt\")\n\n    with tf.compat.v1.Graph().as_default():\n      train_op, v0, v1, lookup0, lookup1 = self.build_graph()\n      ps_monitor = save_utils.PsMonitor(2)\n      saver = save_utils.PartialRecoverySaver(sharded=True,\n                                              max_to_keep=10,\n                                              keep_checkpoint_every_n_hours=2,\n                                              ps_monitor=ps_monitor)\n      tf.compat.v1.add_to_collection(tf.compat.v1.GraphKeys.SAVERS, saver)\n      saver_listener = hash_table_ops.HashTableCheckpointSaverListener(basename)\n      saver_hook = save_utils.NoFirstSaveCheckpointSaverHook(\n          os.path.dirname(basename),\n          save_steps=1,\n          saver=saver,\n          listeners=[saver_listener])\n      restore_listener = hash_table_ops.HashTableCheckpointRestorerListener(\n          basename, ps_monitor)\n      restore_hook = basic_restore_hook.CheckpointRestorerHook(\n          listeners=[restore_listener])\n\n      server1 = tf.distribute.Server.create_local_server()\n      server2 = tf.distribute.Server.create_local_server()\n      config = _generate_config([server1, server2])\n\n      # save checkpoint at first session\n      with tf.compat.v1.train.SingularMonitoredSession(\n          hooks=[restore_hook, saver_hook],\n          master=server1.target,\n          config=config,\n          checkpoint_dir=os.path.dirname(basename)) as mon_sess:\n        sess = mon_sess.raw_session()\n        sess.run(train_op)\n        v0_val = sess.run(v0)\n        v1_val = sess.run(v1)\n        embedding0 = sess.run(lookup0)\n        embedding1 = sess.run(lookup1)\n\n        self.assertAllEqual(v0_val, 1)\n        self.assertAllEqual(v1_val, 1)\n        self.assertAllEqual(embedding0, [[1]])\n        self.assertAllEqual(embedding1, [[1]])\n\n      # change variables at second session\n      with tf.compat.v1.Session(server1.target, config=config) as sess:\n        sess.run(train_op)\n        v0_val = sess.run(v0)\n        v1_val = sess.run(v1)\n        embedding0 = sess.run(lookup0)\n        embedding1 = sess.run(lookup1)\n\n        self.assertAllEqual(v0_val, 2)\n        self.assertAllEqual(v1_val, 2)\n        self.assertAllEqual(embedding0, [[2]])\n        self.assertAllEqual(embedding1, [[2]])\n\n      server3 = tf.distribute.Server.create_local_server()\n      server4 = tf.distribute.Server.create_local_server()\n      config = _generate_config([server3, server4])\n\n      # restore all variables at third session\n      with tf.compat.v1.train.SingularMonitoredSession(\n          hooks=[restore_hook, saver_hook],\n          master=server3.target,\n          config=config,\n          checkpoint_dir=os.path.dirname(basename)) as mon_sess:\n        sess = mon_sess.raw_session()\n        v0_val = sess.run(v0)\n        v1_val = sess.run(v1)\n        embedding0 = sess.run(lookup0)\n        embedding1 = sess.run(lookup1)\n\n        self.assertAllEqual(v0_val, 1)\n        self.assertAllEqual(v1_val, 1)\n        self.assertAllEqual(embedding0, [[1]])\n        self.assertAllEqual(embedding1, [[1]])\n\n      server5 = tf.distribute.Server.create_local_server()\n      config = _generate_config([server1, server5])\n\n      # partial restore at fourth session\n      with tf.compat.v1.train.SingularMonitoredSession(\n          hooks=[restore_hook, saver_hook],\n          master=server1.target,\n          config=config,\n          checkpoint_dir=os.path.dirname(basename)) as mon_sess:\n        sess = mon_sess.raw_session()\n        v0_val = sess.run(v0)\n        v1_val = sess.run(v1)\n        embedding0 = sess.run(lookup0)\n        embedding1 = sess.run(lookup1)\n\n        self.assertAllEqual(v0_val, 2)\n        self.assertAllEqual(v1_val, 1)\n        self.assertAllEqual(embedding0, [[2]])\n        self.assertAllEqual(embedding1, [[1]])\n\n  def test_restore_without_ps_monitor(self):\n    basename = os.path.join(os.environ[\"TEST_TMPDIR\"],\n                            \"test_restore_without_ps_monitor\", \"model.ckpt\")\n\n    with tf.compat.v1.Graph().as_default():\n      train_op, v0, v1, lookup0, lookup1 = self.build_graph()\n      saver = save_utils.PartialRecoverySaver(sharded=True,\n                                              max_to_keep=10,\n                                              keep_checkpoint_every_n_hours=2)\n      tf.compat.v1.add_to_collection(tf.compat.v1.GraphKeys.SAVERS, saver)\n      saver_listener = hash_table_ops.HashTableCheckpointSaverListener(basename)\n      saver_hook = save_utils.NoFirstSaveCheckpointSaverHook(\n          os.path.dirname(basename),\n          save_steps=1,\n          saver=saver,\n          listeners=[saver_listener])\n      restore_listener = hash_table_ops.HashTableCheckpointRestorerListener(\n          basename)\n      restore_hook = basic_restore_hook.CheckpointRestorerHook(\n          listeners=[restore_listener])\n\n      server1 = tf.distribute.Server.create_local_server()\n      server2 = tf.distribute.Server.create_local_server()\n      config = _generate_config([server1, server2])\n\n      # save checkpoint at first session\n      with tf.compat.v1.train.SingularMonitoredSession(\n          hooks=[restore_hook, saver_hook],\n          master=server1.target,\n          config=config,\n          checkpoint_dir=os.path.dirname(basename)) as mon_sess:\n        sess = mon_sess.raw_session()\n        sess.run(train_op)\n        v0_val = sess.run(v0)\n        v1_val = sess.run(v1)\n        embedding0 = sess.run(lookup0)\n        embedding1 = sess.run(lookup1)\n\n        self.assertAllEqual(v0_val, 1)\n        self.assertAllEqual(v1_val, 1)\n        self.assertAllEqual(embedding0, [[1]])\n        self.assertAllEqual(embedding1, [[1]])\n\n      # change variables at second session\n      with tf.compat.v1.Session(server1.target, config=config) as sess:\n        sess.run(train_op)\n        v0_val = sess.run(v0)\n        v1_val = sess.run(v1)\n        embedding0 = sess.run(lookup0)\n        embedding1 = sess.run(lookup1)\n\n        self.assertAllEqual(v0_val, 2)\n        self.assertAllEqual(v1_val, 2)\n        self.assertAllEqual(embedding0, [[2]])\n        self.assertAllEqual(embedding1, [[2]])\n\n      # restore all variables at third session\n      with tf.compat.v1.train.SingularMonitoredSession(\n          hooks=[restore_hook, saver_hook],\n          master=server1.target,\n          config=config,\n          checkpoint_dir=os.path.dirname(basename)) as mon_sess:\n        sess = mon_sess.raw_session()\n        v0_val = sess.run(v0)\n        v1_val = sess.run(v1)\n        embedding0 = sess.run(lookup0)\n        embedding1 = sess.run(lookup1)\n\n        self.assertAllEqual(v0_val, 1)\n        self.assertAllEqual(v1_val, 1)\n        self.assertAllEqual(embedding0, [[1]])\n        self.assertAllEqual(embedding1, [[1]])\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/runner_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging, flags\nfrom contextlib import contextmanager\nfrom dataclasses import dataclass, field, Field\nfrom enum import Enum\nimport json\nimport os, sys, traceback\nfrom threading import RLock\nimport time\nfrom absl.flags import FlagValues\nfrom google.protobuf import text_format\n\nimport tensorflow as tf\nfrom tensorflow.python.lib.io import file_io\nfrom tensorflow.python.util.tf_export import tf_export\nfrom tensorflow.python.training import checkpoint_management\nfrom tensorflow.python.training.checkpoint_state_pb2 import CheckpointState\n\nfrom monolith.native_training.cpu_training import DistributedCpuTrainingConfig\nfrom monolith.native_training.service_discovery import ServiceDiscoveryType, \\\n  ConsulServiceDiscovery, TfConfigServiceDiscovery, ZKServiceDiscovery, MLPServiceDiscovery\nfrom monolith.native_training import gflags_utils\nfrom monolith.native_training.monolith_checkpoint_state_pb2 import MonolithCheckpointState\nfrom monolith.native_training.net_utils import AddressFamily\nfrom monolith.native_training import save_utils\nfrom monolith.native_training.mlp_utils import mlp_pass, add_mpi_exception_hook, MLPEnv\n\nFLAGS = flags.FLAGS\nold_isabs = os.path.isabs\nold_get_checkpoint_state = checkpoint_management.get_checkpoint_state\n\n\ndef isabs(path: str):\n  if path.startswith('hdfs:/'):\n    return True\n  else:\n    return old_isabs(path)\n\n\n# [todo](fitz) part of function will move to Rec Platfrom, this is a tem solution\ndef gen_get_checkpoint_state():\n  # ensure get the same value when call in the same process\n  _lock = RLock()\n\n  @tf_export(\"train.get_checkpoint_state\")\n  def _get_checkpoint_state_internal(checkpoint_dir, latest_filename=None):\n    latest_filename = latest_filename or 'checkpoint'\n    with _lock:\n      checkpoint_state = old_get_checkpoint_state(checkpoint_dir,\n                                                  latest_filename)\n      cur_cnt, max_retry = 0, 5\n      coord_checkpoint_filename = checkpoint_management._GetCheckpointFilename(\n          checkpoint_dir, latest_filename)\n      while checkpoint_state is None and tf.io.gfile.exists(\n          coord_checkpoint_filename) and cur_cnt < max_retry:\n        checkpoint_state = old_get_checkpoint_state(checkpoint_dir,\n                                                    latest_filename)\n        cur_cnt += 1\n      if cur_cnt >= max_retry:\n        raise Exception(\"read ckpt error!\")\n\n      try:\n        if FLAGS.restore_ckpt is not None:\n          if latest_filename != 'checkpoint' or checkpoint_state is None:\n            return checkpoint_state\n\n          dirname_from_ckpt_state = os.path.dirname(\n              checkpoint_state.model_checkpoint_path)\n          restore_ckpt = os.path.join(dirname_from_ckpt_state,\n                                      os.path.basename(FLAGS.restore_ckpt))\n          restore_ckpt_file = os.path.join(checkpoint_dir, 'restore_ckpt')\n\n          if restore_ckpt != checkpoint_state.model_checkpoint_path and restore_ckpt in checkpoint_state.all_model_checkpoint_paths:\n            if FLAGS.mode == tf.estimator.ModeKeys.TRAIN:\n              if not tf.io.gfile.exists(restore_ckpt_file):\n                checkpoint_state.model_checkpoint_path = restore_ckpt\n              else:\n                logging.info(\n                    f'mode is {FLAGS.mode} and {restore_ckpt_file} file exists, keep {checkpoint_state.model_checkpoint_path}'\n                )\n            else:\n              logging.info(f'mode is {FLAGS.mode}, ignore {restore_ckpt_file}')\n              checkpoint_state.model_checkpoint_path = restore_ckpt\n          else:\n            if restore_ckpt == checkpoint_state.model_checkpoint_path:\n              logging.warning(\n                  f\"model_checkpoint_path and {FLAGS.restore_ckpt} are identity\"\n              )\n            else:\n              logging.warning(\n                  f\"checkpoint {FLAGS.restore_ckpt} not exists in {checkpoint_dir}\"\n              )\n\n          if FLAGS.mode == tf.estimator.ModeKeys.TRAIN:\n            if not tf.io.gfile.exists(restore_ckpt_file):\n              checkpoint_state.model_checkpoint_path = restore_ckpt\n              with tf.io.gfile.GFile(restore_ckpt_file, 'w') as gfile:\n                gfile.write(restore_ckpt)\n              checkpoint_filename = os.path.join(checkpoint_dir,\n                                                 latest_filename)\n              file_io.atomic_write_string_to_file(\n                  checkpoint_filename,\n                  text_format.MessageToString(checkpoint_state))\n              logging.info(\n                  f'mode is {FLAGS.mode} and no {restore_ckpt_file} file exists, apply {restore_ckpt}'\n              )\n      except flags._exceptions.UnparsedFlagAccessError as e:\n        pass\n      except Exception as e:\n        logging.info(f\"get_checkpoint_state: {e}\")\n        exc_type, exc_value, exc_traceback_obj = sys.exc_info()\n        logging.error(f\"exc_type: {exc_type}\")\n        logging.error(f\"exc_value: {exc_value}\")\n        traceback.print_tb(exc_traceback_obj, limit=10)\n\n      return checkpoint_state\n\n  return _get_checkpoint_state_internal\n\n\nos.path.isabs = isabs\ncheckpoint_management.get_checkpoint_state = gen_get_checkpoint_state()\ntf.train.get_checkpoint_state = checkpoint_management.get_checkpoint_state\n\n\nclass ContainerType(Enum):\n  DOCKER = 1\n  NATIVE = 2\n\n\n@gflags_utils.update_by_flags\n@gflags_utils.extract_flags_decorator(remove_flags={'device_fn'},\n                                      is_nested=True)\n@dataclass\nclass RunnerConfig(DistributedCpuTrainingConfig):\n  \"\"\"RunnerConfig for start a running.\n  \n  attributes:\n    :param task: Name of the task class to run, or the run py file name\n    :param tf_config: The TF_CONFIG env variable from primus, a json string.\n    :param deep_insight_name: the deep_insight name, which should be identity during the whole job.\n    :param discovery_type: service discovery type, which can be primus, consul and zk.\n    :param zk_server: The ZK server\n    :param zk_watch_address_family: We register both ipv4 and ipv6 when serving,\n              and watch either ipv4 or ipv6 when synchronizing parameters.\n    :param is_local: Whether is local running.\n    :param enable_fid_dedup: Whether enable fid dedup in PS.\n    :param bzid: In realtime native training, business id of the job.\n    :param ps_replica_num: In realtime native training, the number of online ps replica.\n    :param tf_grpc_worker_cache_threads: Env variable for TF_GRPC_WORKER_CACHE_THREADS\n    :param monolith_grpc_worker_service_handler_multiplier: the multiplier of the number of default gprc service handler.\n    :param params_override: Override to model params. A JSON string.\n    :param base_name: Base name while enable realtime training.\n    :param data_type: The input data proto type, can be Instance/Example/ExampleBatch.\n    :param feature_list: The feature list name\n    :param lagrangex_header: Whether has lagrangex_header\n    :param sort_id: Whether has sort_id\n    :param kafka_dump: Whether has kafka_dump\n    :param kafka_dump_prefix: Whether has kafka_dump_prefix\n    :param restore_dir: The directory where the model restore.\n    :param restore_ckpt: The directory where the model restore.\n    :param deep_insight_target: Deep insight target name, if there are multi target, use comma split.\n    :param deep_insight_sample_ratio: Deep insight sample ratio.\n    :param unified_serving: Whether serving cluster is deployed in unified mode \n    :param use_estimator: Whether use estimator to run a model\n    :param kafka_topics: kafka topics for streaming, when no forier and flink\n    :param kafka_group_id: kafka group_id for streaming, when no forier and flink\n    :param kafka_servers: kafka servers for streaming, when no forier and flink\n    :param input_path: The input hdfs path for training/eval.\n    :param wildcard: Wildcard for filter input files.\n    :param start_date: The start date of training/eval, include.\n    :param end_date: The end date of training/eval, exclude.\n    :param start_hour: The start hour of training, include.\n    :param end_hour: The end hour of training, exclude.\n    :param is_hourly: Whether the input data is hourly partitioned.\n    :param enable_dynamic_sharding: Whether switch on dynamic_sharding\n    :param max_task_num_per_worker: Number of data reader task per worker, the same as primus setting\n    :param disable_native_metrics: Whether disable tensorflow native metrics, such as auc, mse.\n  \"\"\"\n\n  task: str = None\n  tf_config: str = None\n  deep_insight_name: str = None\n  discovery_type: ServiceDiscoveryType = ServiceDiscoveryType.CONSUL\n  zk_server: str = None\n  zk_watch_address_family: str = AddressFamily.IPV4\n  is_local: bool = False\n  enable_fid_dedup: bool = False\n  bzid: str = None\n  ps_replica_num: int = None\n  tf_grpc_worker_cache_threads: int = 16\n  monolith_grpc_worker_service_handler_multiplier: float = 1.0\n  params_override: str = None\n  base_name: str = None\n  data_type: str = None\n  feature_list: str = None\n  lagrangex_header: bool = False\n  sort_id: bool = True\n  kafka_dump: bool = False\n  kafka_dump_prefix: bool = False\n  restore_dir: str = None\n  restore_ckpt: str = None\n  deep_insight_target: str = None\n  deep_insight_sample_ratio: float = None\n  unified_serving: bool = False\n  use_estimator: bool = False\n  kafka_topics: str = None\n  kafka_group_id: str = None\n  kafka_servers: str = None\n  input_path: str = None\n  is_hourly: bool = False\n  wildcard: str = None\n  start_date: str = None\n  end_date: str = None\n  start_hour: int = None\n  end_hour: int = None\n  enable_dynamic_sharding: bool = False\n  max_task_num_per_worker: int = 1\n  disable_native_metrics: bool = True\n\n  def __post_init__(self):\n    mlp_pass()\n    add_mpi_exception_hook()\n    try:\n      gflags_utils.update(self)\n    except:\n      logging.info(\"update RunnerConfig failed\")\n\n    if self.enable_gpu_training and self.enable_partial_sync_training:\n      if (self.index <= 0 or self.index is None) and self.server_type == 'worker':\n        self.index = int(os.environ.get('OMPI_COMM_WORLD_RANK') or '0')\n\n    if self.kafka_topics:\n      if isinstance(self.kafka_topics, str):\n        self.kafka_topics = self.kafka_topics.split(',')\n      FLAGS.kafka_topics = ','.join(self.kafka_topics)\n    if self.kafka_group_id:\n      FLAGS.kafka_group_id = self.kafka_group_id\n    if self.kafka_servers:\n      FLAGS.kafka_servers = self.kafka_servers\n\n    assert self.zk_watch_address_family in [\n        AddressFamily.IPV4, AddressFamily.IPV6\n    ]\n\n    try:\n      if self.restore_ckpt != FLAGS.restore_ckpt and FLAGS.restore_ckpt is None:\n        FLAGS.restore_ckpt = self.restore_ckpt\n    except flags._exceptions.UnparsedFlagAccessError:\n      pass\n\n    is_chief = self.is_local or (self.server_type == \"worker\" and\n                                 self.index == 0)\n    if self.restore_dir is not None and len(self.restore_dir) > 0:\n      if is_chief:\n        self._copy_ckpt_file()\n      else:\n        monolith_checkpoint_filename = os.path.join(\n            self.model_dir, save_utils.MONOLITH_CKPT_STATE_FILE_NAME)\n        while True:\n          if tf.io.gfile.exists(monolith_checkpoint_filename):\n            break\n          logging.info(\"Waiting for chief setting up restore_dir...\")\n          time.sleep(30)\n\n  def _copy_ckpt_file(self):\n    logging.info(f\"restore_dir is {self.restore_dir}\")\n    src_file = os.path.join(self.restore_dir, 'checkpoint')\n    if tf.io.gfile.exists(src_file):\n      if not tf.io.gfile.exists(self.model_dir):\n        tf.io.gfile.makedirs(self.model_dir)\n        logging.info(f\"makedirs {self.model_dir} done!\")\n\n      # because we fix os.path.isabs, path startswith 'hdfs:/' is view as abs path\n      # 1) get_checkpoint_state will add restore_dir for relative path to make a abs path\n      #    if it is already abs path (including hdfs path), keep it as is\n      # 2) for path start with 'hdfs:/' will seam as abs path, and do not add prefix any more\n      try:\n        restore_checkpoint_state = old_get_checkpoint_state(\n            self.restore_dir)  # abs path\n        if self.restore_ckpt is None:\n          model_checkpoint_path = restore_checkpoint_state.model_checkpoint_path\n        else:\n          dirname = os.path.dirname(\n              restore_checkpoint_state.model_checkpoint_path)\n          basename = os.path.basename(self.restore_ckpt)\n          model_checkpoint_path = os.path.join(dirname, basename)\n          if model_checkpoint_path not in restore_checkpoint_state.all_model_checkpoint_paths:\n            logging.warning(\n                f'{model_checkpoint_path} is not in restore all_model_checkpoint_paths'\n            )\n            model_checkpoint_path = restore_checkpoint_state.model_checkpoint_path\n\n        checkpoint_state = CheckpointState(\n            model_checkpoint_path=model_checkpoint_path)\n        checkpoint_state.all_model_checkpoint_paths.append(\n            model_checkpoint_path)\n      except Exception as e:\n        logging.warning(e)\n        return\n\n      # we use the checkpoint file as a flag, if it exists, the restore_dir ckpt will not take action\n      checkpoint_filename = os.path.join(self.model_dir, 'checkpoint')\n      if tf.io.gfile.exists(checkpoint_filename):\n        return\n\n      try:\n        file_io.atomic_write_string_to_file(\n            checkpoint_filename, text_format.MessageToString(checkpoint_state))\n        logging.info(\"write checkpoint file done!\")\n\n        # write the restore ckpt to monolith_checkpoint, so that the previous ckpts would not remove by ckpt mamager\n        monolith_checkpoint_filename = os.path.join(\n            self.model_dir, save_utils.MONOLITH_CKPT_STATE_FILE_NAME)\n        monolith_ckpt_state = save_utils.get_monolith_checkpoint_state(\n            self.restore_dir,\n            remove_invalid_path=True) or MonolithCheckpointState()\n        exempt_model_checkpoint_paths = monolith_ckpt_state.exempt_model_checkpoint_paths\n        del exempt_model_checkpoint_paths[:]\n        if tf.io.gfile.exists(monolith_checkpoint_filename):\n          # in case there is a 'monolith_checkpoint' file\n          file_content = file_io.read_file_to_string(\n              monolith_checkpoint_filename)\n          text_format.Merge(file_content, monolith_ckpt_state)\n\n        for restore_ckpt_path in checkpoint_state.all_model_checkpoint_paths:\n          if restore_ckpt_path not in exempt_model_checkpoint_paths:\n            exempt_model_checkpoint_paths.append(restore_ckpt_path)\n\n        file_io.atomic_write_string_to_file(\n            monolith_checkpoint_filename,\n            text_format.MessageToString(monolith_ckpt_state),\n            overwrite=True)\n        logging.info(\"write monolith checkpoint file done!\")\n      except Exception as e:\n        logging.warning(e)\n        logging.warning(f\"checkpoint exist in {self.model_dir}\")\n    else:\n      logging.warning(f\"no checkpoint in {self.restore_dir}\")\n\n\ndef get_discovery(runner_conf: RunnerConfig, psm: str = None):\n  if runner_conf.is_local:\n    discovery = None\n  elif runner_conf.discovery_type == ServiceDiscoveryType.PRIMUS:\n    assert runner_conf.tf_config is not None\n    tf_config = json.loads(runner_conf.tf_config)\n    discovery = TfConfigServiceDiscovery(tf_config)\n    runner_conf.server_type = discovery.server_type\n    runner_conf.index = discovery.index\n  elif runner_conf.discovery_type == ServiceDiscoveryType.CONSUL:\n    # For async training, PS discovery is inside the process.\n    discovery = ConsulServiceDiscovery(psm)\n  elif runner_conf.discovery_type == ServiceDiscoveryType.MLP:\n    # For async training, PS discovery is inside the process.\n    discovery = MLPServiceDiscovery()\n  else:\n    discovery = ZKServiceDiscovery(runner_conf.deep_insight_name,\n                                   runner_conf.zk_server)\n\n  return discovery\n\n\n@contextmanager\ndef monolith_discovery(runner_conf: RunnerConfig):\n  discovery = None\n  try:\n    if runner_conf.is_local:\n      yield None\n    else:\n      from monolith.native_training import env_utils\n      psm = env_utils.generate_psm_from_uuid(runner_conf.uuid)\n      discovery = get_discovery(runner_conf, psm)\n\n      logging.info('enter monolith_discovery!')\n      yield discovery\n  except Exception as e:\n    raise e\n  finally:\n    if discovery is not None:\n      discovery.close()\n\n    logging.info('exit monolith_discovery!')\n"
  },
  {
    "path": "monolith/native_training/runner_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import flags, logging\nimport json\nimport os\nfrom google.protobuf import text_format\nfrom kazoo.handlers.threading import KazooTimeoutError\n\nimport tensorflow as tf\nfrom tensorflow.python.lib.io import file_io\nfrom tensorflow.python.training.checkpoint_state_pb2 import CheckpointState\n\nfrom monolith.native_training.runner_utils import RunnerConfig, get_discovery\nfrom monolith.native_training.service_discovery import ServiceDiscoveryType, \\\n  ConsulServiceDiscovery, TfConfigServiceDiscovery, ZKServiceDiscovery\n\n\nclass RunnerUtilsTest(tf.test.TestCase):\n\n  def test_get_discovery_local(self):\n    config = RunnerConfig(is_local=True)\n    discovery = get_discovery(config)\n    config.is_local = False\n    self.assertEqual(discovery, None)\n\n  def test_get_discovery_primus(self):\n    tf_config = {\n        'cluster': {\n            'ps': ['localhost:1111', 'localhost:1112'],\n            'worker': ['localhost:1113', 'localhost:1114'],\n            'chief': ['localhost:1115']\n        },\n        'task': {\n            'type': 'chief',\n            'index': 0\n        }\n    }\n\n    config = config = RunnerConfig(is_local=False,\n                                   tf_config=json.dumps(tf_config),\n                                   discovery_type=ServiceDiscoveryType.PRIMUS)\n    discovery = get_discovery(config)\n    self.assertEqual(isinstance(discovery, TfConfigServiceDiscovery), True)\n\n  def test_get_discovery_consul(self):\n    psm = 'data.monolith.123456'\n    config = RunnerConfig(is_local=False,\n                          discovery_type=ServiceDiscoveryType.CONSUL)\n    discovery = get_discovery(config, psm)\n    self.assertEqual(isinstance(discovery, ConsulServiceDiscovery), True)\n\n  def test_get_discovery_zk(self):\n    config = RunnerConfig(is_local=False,\n                          discovery_type=ServiceDiscoveryType.ZK,\n                          zk_server=\"127.0.0.1:0\")\n    try:\n      discovery = get_discovery(config)\n      self.assertEqual(isinstance(discovery, ZKServiceDiscovery), True)\n    except KazooTimeoutError as e:\n      logging.info('kazoo example: {}'.format(e))\n\n  def test_copy_ckpt(self):\n    restore_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"runner_utils_test\",\n                               \"restore_dir\")\n    if not tf.io.gfile.exists(restore_dir):\n      tf.io.gfile.makedirs(restore_dir)\n    ckpt = CheckpointState(model_checkpoint_path='model.ckpt-61')\n    ckpt.all_model_checkpoint_paths.extend(\n        ['model.ckpt-61', 'model.ckpt-30', 'model.ckpt-0'])\n    file_io.atomic_write_string_to_file(os.path.join(restore_dir, 'checkpoint'),\n                                        text_format.MessageToString(ckpt))\n\n    model_dir = os.path.join(os.environ[\"TEST_TMPDIR\"], \"runner_utils_test\",\n                             \"model_dir\")\n    if not tf.io.gfile.exists(model_dir):\n      tf.io.gfile.makedirs(model_dir)\n    config = RunnerConfig(is_local=True,\n                          restore_dir=restore_dir,\n                          model_dir=model_dir,\n                          restore_ckpt='model.ckpt-30')\n    ckpt2 = tf.train.get_checkpoint_state(model_dir)\n    self.assertTrue(\n        tf.io.gfile.exists(os.path.join(model_dir, 'monolith_checkpoint')))\n    self.assertTrue(tf.io.gfile.exists(os.path.join(model_dir, 'restore_ckpt')))\n    self.assertEqual(os.path.basename(ckpt2.model_checkpoint_path),\n                     'model.ckpt-30')\n    # Make sure other workers can go through once chief init the dir\n    config = RunnerConfig(server_type=\"worker\",\n                          index=2,\n                          restore_dir=restore_dir,\n                          model_dir=model_dir)\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/runtime/allocator/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_test\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime:__subpackages__\"])\n\ncc_library(\n    name = \"block_allocator\",\n    srcs = [\"block_allocator.cc\"],\n    hdrs = [\"block_allocator.h\"],\n    deps = [\n        \"//monolith/native_training/runtime/concurrency:xorshift\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_test(\n    name = \"block_allocator_test\",\n    srcs = [\"block_allocator_test.cc\"],\n    deps = [\n        \":block_allocator\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/allocator/block_allocator.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <algorithm>\n\n#include \"monolith/native_training/runtime/allocator/block_allocator.h\"\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/synchronization/mutex.h\"\n\nnamespace monolith {\nnamespace allocator {\n\nconst int BlockAllocator::kStartBlcokSize = 1024;\n\nvoid* BlockAllocator::Allocate(size_t cl) {\n  size_t size = Align(cl);\n\n  if (size <= free_) {\n    void* ptr = reinterpret_cast<void*>(free_ptr_);\n    free_ptr_ += size;\n    free_ -= size;\n    return ptr;\n  } else {\n    const size_t block_size = std::max(current_block_size_, size);\n    if (current_block_size_ < max_block_size_) {\n      current_block_size_ *= 2;\n    }\n    allocated_size_ += block_size;\n    blocks_.push_back(std::make_unique<char[]>(block_size));\n    char* block_ptr = blocks_.back().get();\n    free_ptr_ = block_ptr + size;\n    free_ = block_size - size;\n    return reinterpret_cast<void*>(block_ptr);\n  }\n}\n\nvoid BlockAllocator::DeallocateAll() {\n  blocks_.clear();\n  free_ = 0;\n  allocated_size_ = 0;\n}\n\nBlockAllocator* GetThreadLocalAllocator(size_t key) {\n  thread_local absl::flat_hash_map<size_t, std::unique_ptr<BlockAllocator>> m;\n  auto it = m.find(key);\n  if (it == m.end()) {\n    auto it2 = m.insert({key, std::make_unique<BlockAllocator>()});\n    return it2.first->second.get();\n  } else {\n    return it->second.get();\n  }\n}\n\n}  // namespace allocator\n}  // namespace monolith"
  },
  {
    "path": "monolith/native_training/runtime/allocator/block_allocator.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_ALLOCATOR_BLOCK_ALLOCATOR_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_ALLOCATOR_BLOCK_ALLOCATOR_H_\n\n#include <atomic>\n#include <cassert>\n#include <memory>\n#include <vector>\n\n#include <sys/param.h>\n#include \"absl/container/flat_hash_map.h\"\n#include \"glog/logging.h\"\n#include \"monolith/native_training/runtime/concurrency/xorshift.h\"\n\nnamespace monolith {\nnamespace allocator {\n\n//-------------------------------------------------------------------\n// It is not thread safe!\n//-------------------------------------------------------------------\n\nclass BlockAllocator {\n public:\n  static const int kStartBlcokSize;\n\n  BlockAllocator()\n      : current_block_size_(kStartBlcokSize),\n        allocated_size_(0),\n        free_ptr_(nullptr),\n        free_(0) {}\n\n  BlockAllocator(const BlockAllocator &) = delete;\n  BlockAllocator &operator=(const BlockAllocator &) = delete;\n\n  ~BlockAllocator() {}\n\n  // BlockAllocator owns the pointer.\n  void *Allocate(size_t cl);\n\n  void DeallocateAll();\n\n  size_t AllocatedSize() { return allocated_size_; }\n\n private:\n  size_t Align(size_t size) { return (size + (kAlign - 1)) & ~(kAlign - 1); }\n\n  // This must be the power of 2.\n  static const size_t kAlign = 8;\n\n  std::vector<std::unique_ptr<char[]>> blocks_;\n  size_t current_block_size_;\n  size_t max_block_size_ = 1 * 1024 * 1024;\n  size_t allocated_size_;\n  char *free_ptr_;\n  size_t free_;\n};\n\n// Thread safe version of BlockingAllocator by sharding.\nclass TSBlockAllocator {\n public:\n  explicit TSBlockAllocator(int num_shards = 8) : num_shards_(num_shards) {\n    for (int i = 0; i < num_shards_; ++i) {\n      mus_.push_back(std::make_unique<absl::Mutex>());\n      allocs_.push_back(std::make_unique<BlockAllocator>());\n    }\n  }\n\n  // BlockAllocator owns the pointer.\n  void *Allocate(size_t cl) {\n    const int shard = concurrency::XorShift::Rand32ThreadSafe() % num_shards_;\n    {\n      absl::MutexLock l(mus_[shard].get());\n      return allocs_[shard]->Allocate(cl);\n    }\n  }\n\n  void DeallocateAll() {\n    for (int shard = 0; shard < num_shards_; ++shard) {\n      absl::MutexLock l(mus_[shard].get());\n      allocs_[shard]->DeallocateAll();\n    }\n  }\n\n  size_t AllocatedSize() {\n    size_t allocated_size = 0;\n    for (int shard = 0; shard < num_shards_; ++shard) {\n      absl::MutexLock l(mus_[shard].get());\n      allocated_size += allocs_[shard]->AllocatedSize();\n    }\n    return allocated_size;\n  }\n\n private:\n  int num_shards_;\n  std::vector<std::unique_ptr<absl::Mutex>> mus_;\n  std::vector<std::unique_ptr<BlockAllocator>> allocs_;\n};\n\n// This defines an address space for EmbeddingHashTable's RawEntry, it supports\n// up to 2^32 entries.\nstruct EntryAddress {\n  // 2^3 = 8 shards per thread-safe embedding block allocator\n  uint32_t shard_id : 3;\n\n  // No more than 2^17 = 131072 blocks per embedding allocator\n  uint32_t block_id : 17;\n\n  // 2^12 = 4096 entries per block\n  uint32_t entry_id : 12;\n};\n\n// Thread compatible\nclass EmbeddingBlockAllocator {\n public:\n  // This must be the power of 2.\n  static const size_t kAlign = 8;\n  static const size_t kMaxBlockNum = 1 << 17;\n  static const size_t kMaxEntryNum = 1 << 12;\n\n  explicit EmbeddingBlockAllocator(size_t entry_byte_size)\n      : entry_byte_size_aligned_(Align(entry_byte_size)),\n        block_size_(Align(entry_byte_size) * kMaxEntryNum) {\n    Reset();\n  }\n\n  ~EmbeddingBlockAllocator() { FreeBlocks(); }\n\n  EmbeddingBlockAllocator(const EmbeddingBlockAllocator &) = delete;\n  EmbeddingBlockAllocator &operator=(const EmbeddingBlockAllocator &) = delete;\n\n  void *GetEntryPointer(EntryAddress entry_address) const {\n    return cur_block_head_.load(\n               std::memory_order_relaxed)[entry_address.block_id] +\n           entry_byte_size_aligned_ * entry_address.entry_id;\n  }\n\n  EntryAddress AllocateOne() {\n    EntryAddress addr;\n    if (entry_id_ < kMaxEntryNum) {\n      addr.block_id = blocks_->size() - 1;\n      addr.entry_id = entry_id_;\n      entry_id_ += 1;\n    } else {\n      if (blocks_->size() == kMaxBlockNum) {\n        throw std::bad_alloc();\n      }\n      if (blocks_->size() == blocks_->capacity()) {\n        auto new_blocks = std::make_unique<std::vector<char *>>();\n        new_blocks->reserve(blocks_->capacity() * 2);\n        new_blocks->insert(new_blocks->begin(), blocks_->begin(),\n                           blocks_->end());\n        cur_block_head_.store(new_blocks->data());\n        blocks_snapshots_.push_back(std::move(blocks_));\n        blocks_ = std::move(new_blocks);\n      }\n\n      allocated_size_ += block_size_;\n      blocks_->push_back(new char[block_size_]);\n      addr.block_id = blocks_->size() - 1;\n      addr.entry_id = 0;\n      entry_id_ = 1;\n    }\n    return addr;\n  }\n\n  void DeallocateAll() { Reset(); }\n\n  size_t AllocatedSize() { return allocated_size_; }\n\n private:\n  size_t Align(size_t size) const {\n    return (size + (kAlign - 1)) & ~(kAlign - 1);\n  }\n\n  void Reset() {\n    FreeBlocks();\n    blocks_snapshots_.clear();\n    blocks_snapshots_.shrink_to_fit();\n    blocks_ = std::make_unique<std::vector<char *>>();\n    blocks_->reserve(1);\n    allocated_size_ = 0;\n    entry_id_ = kMaxEntryNum;\n    cur_block_head_.store(blocks_->data());\n  }\n\n  void FreeBlocks() {\n    if (blocks_) {\n      for (char *block : *blocks_) {\n        delete[] block;\n      }\n      blocks_ = nullptr;\n    }\n  }\n\n  std::unique_ptr<std::vector<char *>> blocks_;\n  // Stores blocks_.data(). Should be always valid.\n  std::atomic<char **> cur_block_head_;\n  // Used to save blocks snapshots, used for lock-free looking up\n  std::vector<std::unique_ptr<std::vector<char *>>> blocks_snapshots_;\n  size_t entry_byte_size_aligned_;\n  size_t block_size_;\n  size_t allocated_size_;\n  size_t entry_id_;\n};\n\n// Thread safe version of EmbeddingBlockAllocator by sharding.\nclass TSEmbeddingBlockAllocator {\n public:\n  explicit TSEmbeddingBlockAllocator(int64_t entry_byte_size) {\n    for (int i = 0; i < kNumShards; ++i) {\n      mus_.push_back(std::make_unique<absl::Mutex>());\n      allocs_.push_back(\n          std::make_unique<EmbeddingBlockAllocator>(entry_byte_size));\n    }\n  }\n\n  void *GetEntryPointer(EntryAddress address) const {\n    return allocs_[address.shard_id]->GetEntryPointer(address);\n  }\n\n  EntryAddress AllocateOne() {\n    const int shard = concurrency::XorShift::Rand32ThreadSafe() % kNumShards;\n    EntryAddress addr;\n    {\n      absl::WriterMutexLock l(mus_[shard].get());\n      addr = allocs_[shard]->AllocateOne();\n    }\n    addr.shard_id = shard;\n    return addr;\n  }\n\n  void DeallocateAll() {\n    for (int shard = 0; shard < kNumShards; ++shard) {\n      absl::MutexLock l(mus_[shard].get());\n      allocs_[shard]->DeallocateAll();\n    }\n  }\n\n  size_t AllocatedSize() {\n    size_t allocated_size = 0;\n    for (int shard = 0; shard < kNumShards; ++shard) {\n      absl::MutexLock l(mus_[shard].get());\n      allocated_size += allocs_[shard]->AllocatedSize();\n    }\n    return allocated_size;\n  }\n\n private:\n  static const size_t kNumShards = 1 << 3;\n\n  std::vector<std::unique_ptr<absl::Mutex>> mus_;\n  std::vector<std::unique_ptr<EmbeddingBlockAllocator>> allocs_;\n};\n\n}  // namespace allocator\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_ALLOCATOR_BLOCK_ALLOCATOR_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/allocator/block_allocator_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/allocator/block_allocator.h\"\n\n#include <cstring>\n#include <memory>\n#include <thread>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace monolith {\nnamespace allocator {\nnamespace {\n\nTEST(BlockAllocatorTest, Basic) {\n  std::unique_ptr<BlockAllocator> allocator =\n      std::make_unique<BlockAllocator>();\n\n  size_t size1 = 10;\n  char* ptr1 = reinterpret_cast<char*>(allocator->Allocate(size1));\n  EXPECT_NE(ptr1, nullptr);\n\n  ptr1[size1 - 1] = 'x';\n  EXPECT_EQ(allocator->AllocatedSize(), BlockAllocator::kStartBlcokSize);\n\n  size_t size2 = 12;\n  char* ptr2 = reinterpret_cast<char*>(allocator->Allocate(size2));\n\n  // Test Align.\n  EXPECT_EQ(ptr2 - ptr1, 16);\n\n  // Can't fit into current block anymore due to align.\n  size_t size3 = BlockAllocator::kStartBlcokSize - size1 - size2;\n  char* ptr3 = reinterpret_cast<char*>(allocator->Allocate(size3));\n  EXPECT_NE(ptr3, nullptr);\n\n  // Next block will be doubled.\n  EXPECT_EQ(allocator->AllocatedSize(), BlockAllocator::kStartBlcokSize * 3);\n\n  allocator->DeallocateAll();\n  EXPECT_EQ(allocator->AllocatedSize(), 0);\n}\n\nTEST(BlockAllocatorTest, AllocateLarge) {\n  size_t block_size = 1 << 20;\n  auto allocator = std::make_unique<BlockAllocator>();\n  char* p = reinterpret_cast<char*>(allocator->Allocate(block_size));\n  std::memset(p, 0, block_size);\n}\n\nTEST(BlockAllocatorTest, TSBlockAllocator) {\n  TSBlockAllocator alloc;\n  auto func = [&alloc]() {\n    for (int i = 0; i < 100; ++i) {\n      char* p = reinterpret_cast<char*>(alloc.Allocate(16));\n      std::memset(p, 0, 16);\n    }\n  };\n  std::vector<std::thread> ths;\n  for (int i = 0; i < 15; ++i) {\n    ths.push_back(std::thread(func));\n  }\n  for (auto& th : ths) {\n    th.join();\n  }\n  alloc.DeallocateAll();\n  EXPECT_THAT(alloc.AllocatedSize(), 0);\n}\n\nTEST(EmbeddingBlockAllocatorTest, EmbeddingBlockAllocatorAllocateMany) {\n  EmbeddingBlockAllocator alloc(8);\n  for (int i = 0; i < EmbeddingBlockAllocator::kMaxEntryNum * 10; ++i) {\n    auto addr = alloc.AllocateOne();\n    void* real_addr = alloc.GetEntryPointer(addr);\n    std::memset(real_addr, 0, 8);\n  }\n}\n\nTEST(EmbeddingBlockAllocatorTest, TSEmbeddingBlockAllocator) {\n  TSEmbeddingBlockAllocator alloc(16);\n  auto func = [&alloc]() {\n    for (int i = 0; i < 100; ++i) {\n      EntryAddress p = alloc.AllocateOne();\n      std::memset(static_cast<char*>(alloc.GetEntryPointer(p)), 0, 16);\n    }\n  };\n  std::vector<std::thread> threads;\n  for (int i = 0; i < 15; ++i) {\n    threads.emplace_back(func);\n  }\n  for (auto& t : threads) {\n    t.join();\n  }\n  alloc.DeallocateAll();\n  EXPECT_THAT(alloc.AllocatedSize(), 0);\n}\n\n}  // namespace\n}  // namespace allocator\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/common/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_test\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime:__subpackages__\"])\n\ncc_library(\n    name = \"cpu_info\",\n    srcs = [\"cpu_info.cc\"],\n    hdrs = [\"cpu_info.h\"],\n    deps = [\n    ],\n)\n\ncc_library(\n    name = \"metrics_internal_deps\",\n)\n\ncc_library(\n    name = \"metrics\",\n    srcs = [\"metrics.cc\"],\n    hdrs = [\"metrics.h\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":metrics_internal_deps\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_library(\n    name = \"linalg_utils\",\n    hdrs = [\"linalg_utils.h\"],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_test(\n    name = \"linalg_utils_test\",\n    srcs = [\n        \"linalg_utils_test.cc\",\n    ],\n    deps = [\n        \":linalg_utils\",\n        \"@com_google_glog//:glog\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\n"
  },
  {
    "path": "monolith/native_training/runtime/common/cpu_info.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"monolith/native_training/runtime/common/cpu_info.h\"\n#include <iostream>\n#include <mutex>\n#include <string>\n\n#if defined(__x86_64__) || defined(__i386__)\n\n#define GETCPUID(a, b, c, d, a_inp, c_inp) \\\n  asm(\"mov %%rbx, %%rdi\\n\"                 \\\n      \"cpuid\\n\"                            \\\n      \"xchg %%rdi, %%rbx\\n\"                \\\n      : \"=a\"(a), \"=D\"(b), \"=c\"(c), \"=d\"(d) \\\n      : \"a\"(a_inp), \"2\"(c_inp))\n#endif\n\nnamespace monolith {\n\nclass CPUIDInfo;\nvoid InitCPUIDInfo();\n\nCPUIDInfo *cpuid = nullptr;\n\n#if defined(__x86_64__) || defined(__i386__)\nint GetXCR0EAX() {\n  int eax, edx;\n  asm(\"XGETBV\" : \"=a\"(eax), \"=d\"(edx) : \"c\"(0));\n  return eax;\n}\n\n// Structure for basic CPUID info\nclass CPUIDInfo {\n public:\n  CPUIDInfo()\n      : have_adx_(0),\n        have_aes_(0),\n        have_avx_(0),\n        have_avx2_(0),\n        have_avx512f_(0),\n        have_avx512cd_(0),\n        have_avx512er_(0),\n        have_avx512pf_(0),\n        have_avx512vl_(0),\n        have_avx512bw_(0),\n        have_avx512dq_(0),\n        have_avx512vbmi_(0),\n        have_avx512ifma_(0),\n        have_avx512_4vnniw_(0),\n        have_avx512_4fmaps_(0),\n        have_bmi1_(0),\n        have_bmi2_(0),\n        have_cmov_(0),\n        have_cmpxchg16b_(0),\n        have_cmpxchg8b_(0),\n        have_f16c_(0),\n        have_fma_(0),\n        have_mmx_(0),\n        have_pclmulqdq_(0),\n        have_popcnt_(0),\n        have_prefetchw_(0),\n        have_prefetchwt1_(0),\n        have_rdrand_(0),\n        have_rdseed_(0),\n        have_smap_(0),\n        have_sse_(0),\n        have_sse2_(0),\n        have_sse3_(0),\n        have_sse4_1_(0),\n        have_sse4_2_(0),\n        have_ssse3_(0),\n        have_hypervisor_(0) {}\n\n  static void Initialize() {\n    // Initialize cpuid struct\n    cpuid = new CPUIDInfo;\n\n    uint32_t eax, ebx, ecx, edx;\n\n    // Get vendor string (issue CPUID with eax = 0)\n    GETCPUID(eax, ebx, ecx, edx, 0, 0);\n    cpuid->vendor_str_.append(reinterpret_cast<char *>(&ebx), 4);\n    cpuid->vendor_str_.append(reinterpret_cast<char *>(&edx), 4);\n    cpuid->vendor_str_.append(reinterpret_cast<char *>(&ecx), 4);\n\n    // To get general information and extended features we send eax = 1 and\n    // ecx = 0 to cpuid.  The response is returned in eax, ebx, ecx and edx.\n    // (See Intel 64 and IA-32 Architectures Software Developer's Manual\n    // Volume 2A: Instruction Set Reference, A-M CPUID).\n    GETCPUID(eax, ebx, ecx, edx, 1, 0);\n\n    cpuid->model_num_ = static_cast<int>((eax >> 4) & 0xf);\n    cpuid->family_ = static_cast<int>((eax >> 8) & 0xf);\n\n    cpuid->have_aes_ = (ecx >> 25) & 0x1;\n    cpuid->have_cmov_ = (edx >> 15) & 0x1;\n    cpuid->have_cmpxchg16b_ = (ecx >> 13) & 0x1;\n    cpuid->have_cmpxchg8b_ = (edx >> 8) & 0x1;\n    cpuid->have_mmx_ = (edx >> 23) & 0x1;\n    cpuid->have_pclmulqdq_ = (ecx >> 1) & 0x1;\n    cpuid->have_popcnt_ = (ecx >> 23) & 0x1;\n    cpuid->have_rdrand_ = (ecx >> 30) & 0x1;\n    cpuid->have_sse2_ = (edx >> 26) & 0x1;\n    cpuid->have_sse3_ = ecx & 0x1;\n    cpuid->have_sse4_1_ = (ecx >> 19) & 0x1;\n    cpuid->have_sse4_2_ = (ecx >> 20) & 0x1;\n    cpuid->have_sse_ = (edx >> 25) & 0x1;\n    cpuid->have_ssse3_ = (ecx >> 9) & 0x1;\n    cpuid->have_hypervisor_ = (ecx >> 31) & 1;\n\n    const uint64_t xcr0_xmm_mask = 0x2;\n    const uint64_t xcr0_ymm_mask = 0x4;\n    const uint64_t xcr0_maskreg_mask = 0x20;\n    const uint64_t xcr0_zmm0_15_mask = 0x40;\n    const uint64_t xcr0_zmm16_31_mask = 0x80;\n\n    const uint64_t xcr0_avx_mask = xcr0_xmm_mask | xcr0_ymm_mask;\n    const uint64_t xcr0_avx512_mask = xcr0_avx_mask | xcr0_maskreg_mask |\n                                      xcr0_zmm0_15_mask | xcr0_zmm16_31_mask;\n\n    const bool have_avx =\n        // Does the OS support XGETBV instruction use by applications?\n        ((ecx >> 27) & 0x1) &&\n        // Does the OS save/restore XMM and YMM state?\n        ((GetXCR0EAX() & xcr0_avx_mask) == xcr0_avx_mask) &&\n        // Is AVX supported in hardware?\n        ((ecx >> 28) & 0x1);\n\n    const bool have_avx512 =\n        // Does the OS support XGETBV instruction use by applications?\n        ((ecx >> 27) & 0x1) &&\n        // Does the OS save/restore ZMM state?\n        ((GetXCR0EAX() & xcr0_avx512_mask) == xcr0_avx512_mask);\n\n    cpuid->have_avx_ = have_avx;\n    cpuid->have_fma_ = have_avx && ((ecx >> 12) & 0x1);\n    cpuid->have_f16c_ = have_avx && ((ecx >> 29) & 0x1);\n\n    // Get standard level 7 structured extension features (issue CPUID with\n    // eax = 7 and ecx= 0), which is required to check for AVX2 support as\n    // well as other Haswell (and beyond) features.  (See Intel 64 and IA-32\n    // Architectures Software Developer's Manual Volume 2A: Instruction Set\n    // Reference, A-M CPUID).\n    GETCPUID(eax, ebx, ecx, edx, 7, 0);\n\n    cpuid->have_adx_ = (ebx >> 19) & 0x1;\n    cpuid->have_avx2_ = have_avx && ((ebx >> 5) & 0x1);\n    cpuid->have_bmi1_ = (ebx >> 3) & 0x1;\n    cpuid->have_bmi2_ = (ebx >> 8) & 0x1;\n    cpuid->have_prefetchwt1_ = ecx & 0x1;\n    cpuid->have_rdseed_ = (ebx >> 18) & 0x1;\n    cpuid->have_smap_ = (ebx >> 20) & 0x1;\n\n    cpuid->have_avx512f_ = have_avx512 && ((ebx >> 16) & 0x1);\n    cpuid->have_avx512cd_ = have_avx512 && ((ebx >> 28) & 0x1);\n    cpuid->have_avx512er_ = have_avx512 && ((ebx >> 27) & 0x1);\n    cpuid->have_avx512pf_ = have_avx512 && ((ebx >> 26) & 0x1);\n    cpuid->have_avx512vl_ = have_avx512 && ((ebx >> 31) & 0x1);\n    cpuid->have_avx512bw_ = have_avx512 && ((ebx >> 30) & 0x1);\n    cpuid->have_avx512dq_ = have_avx512 && ((ebx >> 17) & 0x1);\n    cpuid->have_avx512vbmi_ = have_avx512 && ((ecx >> 1) & 0x1);\n    cpuid->have_avx512ifma_ = have_avx512 && ((ebx >> 21) & 0x1);\n    cpuid->have_avx512_4vnniw_ = have_avx512 && ((edx >> 2) & 0x1);\n    cpuid->have_avx512_4fmaps_ = have_avx512 && ((edx >> 3) & 0x1);\n  }\n\n  static bool TestFeature(CPUFeature feature) {\n    InitCPUIDInfo();\n    // clang-format off\n    switch (feature) {\n      case ADX: return cpuid->have_adx_;\n      case AES: return cpuid->have_aes_;\n      case AVX2: return cpuid->have_avx2_;\n      case AVX: return cpuid->have_avx_;\n      case AVX512F: return cpuid->have_avx512f_;\n      case AVX512CD: return cpuid->have_avx512cd_;\n      case AVX512PF: return cpuid->have_avx512pf_;\n      case AVX512ER: return cpuid->have_avx512er_;\n      case AVX512VL: return cpuid->have_avx512vl_;\n      case AVX512BW: return cpuid->have_avx512bw_;\n      case AVX512DQ: return cpuid->have_avx512dq_;\n      case AVX512VBMI: return cpuid->have_avx512vbmi_;\n      case AVX512IFMA: return cpuid->have_avx512ifma_;\n      case AVX512_4VNNIW: return cpuid->have_avx512_4vnniw_;\n      case AVX512_4FMAPS: return cpuid->have_avx512_4fmaps_;\n      case BMI1: return cpuid->have_bmi1_;\n      case BMI2: return cpuid->have_bmi2_;\n      case CMOV: return cpuid->have_cmov_;\n      case CMPXCHG16B: return cpuid->have_cmpxchg16b_;\n      case CMPXCHG8B: return cpuid->have_cmpxchg8b_;\n      case F16C: return cpuid->have_f16c_;\n      case FMA: return cpuid->have_fma_;\n      case MMX: return cpuid->have_mmx_;\n      case PCLMULQDQ: return cpuid->have_pclmulqdq_;\n      case POPCNT: return cpuid->have_popcnt_;\n      case PREFETCHW: return cpuid->have_prefetchw_;\n      case PREFETCHWT1: return cpuid->have_prefetchwt1_;\n      case RDRAND: return cpuid->have_rdrand_;\n      case RDSEED: return cpuid->have_rdseed_;\n      case SMAP: return cpuid->have_smap_;\n      case SSE2: return cpuid->have_sse2_;\n      case SSE3: return cpuid->have_sse3_;\n      case SSE4_1: return cpuid->have_sse4_1_;\n      case SSE4_2: return cpuid->have_sse4_2_;\n      case SSE: return cpuid->have_sse_;\n      case SSSE3: return cpuid->have_ssse3_;\n      case HYPERVISOR: return cpuid->have_hypervisor_;\n      default:break;\n    }\n    // clang-format on\n    return false;\n  }\n\n  std::string vendor_str() const { return vendor_str_; }\n  int family() const { return family_; }\n  int model_num() { return model_num_; }\n\n private:\n  int have_adx_ : 1;\n  int have_aes_ : 1;\n  int have_avx_ : 1;\n  int have_avx2_ : 1;\n  int have_avx512f_ : 1;\n  int have_avx512cd_ : 1;\n  int have_avx512er_ : 1;\n  int have_avx512pf_ : 1;\n  int have_avx512vl_ : 1;\n  int have_avx512bw_ : 1;\n  int have_avx512dq_ : 1;\n  int have_avx512vbmi_ : 1;\n  int have_avx512ifma_ : 1;\n  int have_avx512_4vnniw_ : 1;\n  int have_avx512_4fmaps_ : 1;\n  int have_bmi1_ : 1;\n  int have_bmi2_ : 1;\n  int have_cmov_ : 1;\n  int have_cmpxchg16b_ : 1;\n  int have_cmpxchg8b_ : 1;\n  int have_f16c_ : 1;\n  int have_fma_ : 1;\n  int have_mmx_ : 1;\n  int have_pclmulqdq_ : 1;\n  int have_popcnt_ : 1;\n  int have_prefetchw_ : 1;\n  int have_prefetchwt1_ : 1;\n  int have_rdrand_ : 1;\n  int have_rdseed_ : 1;\n  int have_smap_ : 1;\n  int have_sse_ : 1;\n  int have_sse2_ : 1;\n  int have_sse3_ : 1;\n  int have_sse4_1_ : 1;\n  int have_sse4_2_ : 1;\n  int have_ssse3_ : 1;\n  int have_hypervisor_ : 1;\n  std::string vendor_str_;\n  int family_;\n  int model_num_;\n};\n\nstd::once_flag cpuid_once_flag;\n\nvoid InitCPUIDInfo() {\n  // This ensures that CPUIDInfo::Initialize() is called exactly\n  // once regardless of how many threads concurrently call us\n  std::call_once(cpuid_once_flag, CPUIDInfo::Initialize);\n}\n#endif\n\nbool TestCPUFeature(CPUFeature feature) {\n#if defined(__x86_64__) || defined(__i386__)\n  return CPUIDInfo::TestFeature(feature);\n#else\n  return false;\n#endif\n}\n\nstd::string CPUVendorIDString() {\n#if defined(__x86_64__) || defined(__i386__)\n  InitCPUIDInfo();\n  return cpuid->vendor_str();\n#else\n  return \"\";\n#endif\n}\n\nint CPUFamily() {\n#if defined(__x86_64__) || defined(__i386__)\n  InitCPUIDInfo();\n  return cpuid->family();\n#else\n  return 0;\n#endif\n}\n\nint CPUModelNum() {\n#if defined(__x86_64__) || defined(__i386__)\n  InitCPUIDInfo();\n  return cpuid->model_num();\n#else\n  return 0;\n#endif\n}\n\nint CPUIDNumSMT() {\n// https://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration\n// https://software.intel.com/en-us/articles/intel-sdm (Vol 3A)\n// Section: Detecting Hardware Multi-threads Support and Topology\n// Uses CPUID Leaf 11 to enumerate system topology on Intel x86 architectures\n// Other cases not supported\n#if defined(__x86_64__) || defined(__i386__)\n  uint32_t eax, ebx, ecx, edx;\n  // Check if system supports Leaf 11\n  GETCPUID(eax, ebx, ecx, edx, 0, 0);\n  if (eax >= 11) {\n    // 1) Leaf 11 available? CPUID.(EAX=11, ECX=0):EBX != 0\n    // 2) SMT_Mask_Width = CPUID.(EAX=11, ECX=0):EAX[4:0] if CPUID.(EAX=11,\n    // ECX=0):ECX[15:8] is 1\n    GETCPUID(eax, ebx, ecx, edx, 11, 0);\n    if (ebx != 0 && ((ecx & 0xff00) >> 8) == 1) {\n      return 1 << (eax & 0x1f);  // 2 ^ SMT_Mask_Width\n    }\n  }\n  return 0;\n#else\n  return 0;\n#endif\n}\n\n// If the CPU feature isn't present, log a fatal error.\nvoid CheckFeatureOrDie(monolith::CPUFeature feature,\n                       const std::string &feature_name) {\n  if (!monolith::TestCPUFeature(feature)) {\n    std::cerr << \"The library was compiled to use \" << feature_name\n              << \" instructions, but these aren't available on your machine.\";\n    std::abort();\n  }\n}\n\nvoid RunCPUGuard() {\n#if defined(_ENABLE_AVX) && defined(__AVX__)\n  CheckFeatureOrDie(monolith::CPUFeature::AVX, \"AVX\");\n#endif\n}\n\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/common/cpu_info.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_COMMON_CPU_INFO_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_COMMON_CPU_INFO_H_\n\n#include <string>\n\nnamespace monolith {\n\n// Mostly ISA related features that we care about\nenum CPUFeature {\n  // Do not change numeric assignments.\n  MMX = 0,\n  SSE = 1,\n  SSE2 = 2,\n  SSE3 = 3,\n  SSSE3 = 4,\n  SSE4_1 = 5,\n  SSE4_2 = 6,\n  CMOV = 7,\n  CMPXCHG8B = 8,\n  CMPXCHG16B = 9,\n  POPCNT = 10,\n  AES = 11,\n  AVX = 12,\n  RDRAND = 13,\n  AVX2 = 14,\n  FMA = 15,\n  F16C = 16,\n  PCLMULQDQ = 17,\n  RDSEED = 18,\n  ADX = 19,\n  SMAP = 20,\n\n  // Prefetch Vector Data Into Caches with Intent to Write and T1 Hint\n  // http://www.felixcloutier.com/x86/PREFETCHWT1.html.\n  // You probably want PREFETCHW instead.\n  PREFETCHWT1 = 21,\n\n  BMI1 = 22,\n  BMI2 = 23,\n  HYPERVISOR = 25,  // 0 when on a real CPU, 1 on (well-behaved) hypervisor.\n\n  // Prefetch Data into Caches in Anticipation of a Write (3D Now!).\n  // http://www.felixcloutier.com/x86/PREFETCHW.html\n  PREFETCHW = 26,\n\n  // AVX-512: 512-bit vectors (plus masking, etc.) in Knights Landing,\n  // Skylake\n  // Xeon, etc.; each of these entries is a different subset of\n  // instructions,\n  // various combinations of which occur on various CPU types.\n  AVX512F = 27,        // Foundation\n  AVX512CD = 28,       // Conflict detection\n  AVX512ER = 29,       // Exponential and reciprocal\n  AVX512PF = 30,       // Prefetching\n  AVX512VL = 31,       // Shorter vector lengths\n  AVX512BW = 32,       // Byte and word\n  AVX512DQ = 33,       // Dword and qword\n  AVX512VBMI = 34,     // Bit manipulation\n  AVX512IFMA = 35,     // Integer multiply-add\n  AVX512_4VNNIW = 36,  // Integer neural network\n  AVX512_4FMAPS = 37,  // Floating point neural network\n};\n\n// Checks whether the current processor supports one of the features above.\n// Checks CPU registers to return hardware capabilities.\nbool TestCPUFeature(CPUFeature feature);\n\n// Returns CPU Vendor string (i.e. 'GenuineIntel', 'AuthenticAMD', etc.)\nstd::string CPUVendorIDString();\n\n// Returns CPU family.\nint CPUFamily();\n\n// Returns CPU model number.\nint CPUModelNum();\n\n// Returns nominal core processor cycles per second of each processor.\ndouble NominalCPUFrequency();\n\n// Returns num of hyperthreads per physical core\nint CPUIDNumSMT();\n\nvoid CheckFeatureOrDie(monolith::CPUFeature feature,\n                       const std::string &feature_name);\n\nvoid RunCPUGuard();\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_COMMON_CPU_INFO_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/common/linalg_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_COMMON_LINALG_UTILS_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_COMMON_LINALG_UTILS_H_\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <numeric>\n\nnamespace monolith {\nnamespace common {\n\ntemplate <class T>\ntypename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type\nIsAlmostEqual(T x, T y, int ulp = 2) {\n  // the machine epsilon has to be scaled to the magnitude of the values used\n  // and multiplied by the desired precision in ULPs (units in the last place)\n  return std::abs(x - y) <=\n             std::numeric_limits<T>::epsilon() * std::abs(x + y) * ulp\n         // unless the result is subnormal\n         || std::abs(x - y) < std::numeric_limits<T>::min();\n}\n\ntemplate <class T>\ntypename std::enable_if<!std::numeric_limits<T>::is_integer, T>::type\nL2NormSquare(const T* data, size_t length) {\n  T sum = 0;\n  for (size_t i = 0; i < length; ++i) {\n    sum += data[i] * data[i];\n  }\n\n  return sum;\n}\n\n}  // namespace common\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_COMMON_LINALG_UTILS_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/common/linalg_utils_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/common/linalg_utils.h\"\n\n#include \"glog/logging.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace monolith {\nnamespace common {\n\nTEST(LinalgUtils, IsAlmostEqual) {\n  EXPECT_TRUE(IsAlmostEqual(0.f, 0.f));\n  EXPECT_FALSE(IsAlmostEqual(0.f, 1e-6f));\n}\n\nTEST(LinalgUtils, L2NormSquare) {\n  std::vector<float> vec1 = {};\n  EXPECT_TRUE(IsAlmostEqual(L2NormSquare(vec1.data(), vec1.size()), 0.f));\n\n  std::vector<float> vec2 = {1};\n  EXPECT_TRUE(IsAlmostEqual(L2NormSquare(vec2.data(), vec2.size()), 1.f));\n\n  std::vector<float> vec3 = {1, 2, 3, 4};\n  EXPECT_TRUE(IsAlmostEqual(L2NormSquare(vec3.data(), vec3.size()), 30.f));\n}\n\n}  // namespace common\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/common/metrics.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/common/metrics.h\"\n\n#include <cstdlib>\n\n#include \"glog/logging.h\"\n\nnamespace monolith {\ncpputil::metrics2::MetricCollector *GetMetrics() {\n  static auto *metrics = new cpputil::metrics2::MetricCollector();\n  return metrics;\n}\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/common/metrics.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_COMMON_METRICS_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_COMMON_METRICS_H_\n#include <string>\n#include <vector>\n\nnamespace cpputil {\nnamespace metrics2 {\n\n// This is a dummy implementation\n// Will be replaced by a unified interface\nclass MetricCollector {\n public:\n  typedef std::vector<std::pair<std::string, std::string>> TagkvList;\n\n  MetricCollector() = default;\n  virtual ~MetricCollector() = default;\n\n  template <class T>\n  int init(const T& conf) {\n    return 0;\n  }\n\n  int define_tagk(const std::string& tagk) { return 0; }\n\n  int define_tagkv(const std::string& tagk,\n                   const std::vector<std::string>& tagv_list) {\n    return 0;\n  }\n\n  int define_counter(const std::string& name) { return 0; }\n\n  int define_counter(const std::string& name, const std::string& ) {\n    return 0;\n  }\n\n  int define_rate_counter(const std::string& name) { return 0; }\n\n  int define_rate_counter(const std::string& name,\n                          const std::string& ) {\n    return 0;\n  }\n\n  int define_meter(const std::string& name) { return 0; }\n\n  int define_meter(const std::string& name, const std::string& ) {\n    return 0;\n  }\n\n  int define_timer(const std::string& name) { return 0; }\n\n  int define_timer(const std::string& name, const std::string& ) {\n    return 0;\n  }\n\n  int define_store(const std::string& name) { return 0; }\n\n  int define_store(const std::string& name, const std::string& ) {\n    return 0;\n  }\n\n  int define_ts_store(const std::string& name) { return 0; }\n\n  int define_ts_store(const std::string& name, const std::string& ) {\n    return 0;\n  }\n\n  int emit_counter(const std::string& name, double value) const { return 0; }\n\n  int emit_counter(const std::string& name, double value,\n                   std::string tagkv) const {\n    return 0;\n  }\n\n  int emit_counter(const std::string& name, double value,\n                   const TagkvList& tagkv_list) const {\n    return 0;\n  }\n\n  int emit_rate_counter(const std::string& name, double value) const {\n    return 0;\n  }\n\n  int emit_rate_counter(const std::string& name, double value,\n                        const std::string& tagkv) const {\n    return 0;\n  }\n\n  int emit_rate_counter(const std::string& name, double value,\n                        const TagkvList& tagkv_list) {\n    return 0;\n  }\n\n  int emit_meter(const std::string& name, double value) const { return 0; }\n\n  int emit_meter(const std::string& name, double value,\n                 const std::string& tagkv) const {\n    return 0;\n  }\n\n  int emit_meter(const std::string& name, double value,\n                 const TagkvList& tagkv_list) {\n    return 0;\n  }\n\n  int emit_timer(const std::string& name, double value) const { return 0; }\n\n  int emit_timer(const std::string& name, double value,\n                 std::string tagkv) const {\n    return 0;\n  }\n\n  int emit_timer(const std::string& name, double value,\n                 const TagkvList& tagkv_list) const {\n    return 0;\n  }\n\n  int emit_store(const std::string& name, double value) const { return 0; }\n\n  int emit_store(const std::string& name, double value,\n                 std::string tagkv) const {\n    return 0;\n  }\n\n  int emit_store(const std::string& name, double value,\n                 const TagkvList& tagkv_list) const {\n    return 0;\n  }\n\n  int emit_ts_store(const std::string& name, double value, time_t ts) const {\n    return 0;\n  }\n\n  int emit_ts_store(const std::string& name, double value, time_t ts,\n                    std::string tagkv) const {\n    return 0;\n  }\n\n  int emit_ts_store(const std::string& name, double value, time_t ts,\n                    const TagkvList& tagkv_list) const {\n    return 0;\n  }\n\n  int reset_counter(const std::string& name) const { return 0; }\n\n  int reset_counter(const std::string& name, std::string tagkv) const {\n    return 0;\n  }\n\n  int reset_counter(const std::string& name,\n                    const TagkvList& tagkv_list) const {\n    return 0;\n  }\n\n  int reset_rate_counter(const std::string& name) const { return 0; }\n\n  int reset_rate_counter(const std::string& name, const std::string& tagkv) {\n    return 0;\n  }\n\n  int reset_rate_counter(const std::string& name, const TagkvList& tagkv_list) {\n    return 0;\n  }\n\n  int reset_timer(const std::string& name) const { return 0; }\n\n  int reset_timer(const std::string& name, std::string tagkv) const {\n    return 0;\n  }\n\n  int reset_timer(const std::string& name, const TagkvList& tagkv_list) const {\n    return 0;\n  }\n\n  int reset_store(const std::string& name) const { return 0; }\n\n  int reset_store(const std::string& name, std::string tagkv) const {\n    return 0;\n  }\n\n  int reset_store(const std::string& name, const TagkvList& tagkv_list) const {\n    return 0;\n  }\n\n  int reset_ts_store(const std::string& name) const { return 0; }\n\n  int reset_ts_store(const std::string& name, std::string tagkv) const {\n    return 0;\n  }\n\n  int reset_ts_store(const std::string& name,\n                     const TagkvList& tagkv_list) const {\n    return 0;\n  }\n\n  // deprecated\n  static int start_flush_thread() { return 1; }\n\n  // deprecated\n  static int start_listening_thread() { return 1; }\n\n  static std::string make_tagkv(const TagkvList& tagkv_list) { return \"\"; }\n};\n\n}  // namespace metrics2\n}  // namespace cpputil\nnamespace monolith {\n\ncpputil::metrics2::MetricCollector *GetMetrics();\n\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_COMMON_METRICS_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/common/metrics_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <stdlib.h>\n#include <unistd.h>\n\n#include \"glog/logging.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/common/metrics.h\"\n\nnamespace monolith {\n\nTEST(MetricsTest, Default) {\n  putenv(const_cast<char *>(\"TCE_PSM=data.tob.test\"));\n  static cpputil::metrics2::MetricCollector *metrics1 = monolith::GetMetrics();\n  static cpputil::metrics2::MetricCollector *metrics2 = monolith::GetMetrics();\n  EXPECT_EQ(metrics1, metrics2);\n}\n\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_proto_library\", \"cc_test\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime:__subpackages__\"])\n\ncc_library(\n    name = \"thread_pool\",\n    srcs = [\"thread_pool.cc\"],\n    hdrs = [\"thread_pool.h\"],\n    deps = [\n        \"@com_google_absl//absl/synchronization\",\n    ],\n)\n\ncc_library(\n    name = \"queue\",\n    hdrs = [\"queue.h\"],\n    deps = [],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_test(\n    name = \"queue_test\",\n    srcs = [\"queue_test.cc\"],\n    deps = [\n        \":queue\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"sleeper\",\n    hdrs = [\"sleeper.h\"],\n    deps = [],\n)\n\ncc_library(\n    name = \"micro_one_bit_spin_lock\",\n    hdrs = [\"micro_one_bit_spin_lock.h\"],\n    deps = [\n        \":sleeper\",\n        \"@com_google_glog//:glog\"\n    ],\n)\n\ncc_library(\n    name = \"xorshift\",\n    hdrs = [\"xorshift.h\"],\n    srcs = [\"xorshift.cc\"],\n    deps = [],\n)\n\ncc_binary(\n    name = \"xorshift_test\",\n    srcs = [\"xorshift_test.cc\"],\n    deps = [\n        \":xorshift\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_binary(\n    name = \"random_number_generator_benchmark\",\n    srcs = [\"random_number_generator_benchmark.cc\"],\n    deps = [\n        \":xorshift\",\n        \"@com_google_absl//absl/random\",\n        \"@com_github_google_benchmark//:benchmark\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/micro_one_bit_spin_lock.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_MICRO_ONE_BIT_SPIN_LOCK_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_MICRO_ONE_BIT_SPIN_LOCK_H_\n\n#include \"monolith/native_training/runtime/concurrency/sleeper.h\"\n#include \"glog/logging.h\"\n\n\nnamespace monolith {\nnamespace concurrency {\n\n// ensure never modify the other bits of lock_ when are not holding the lock\nstruct MicroOneBitSpinLock {\n private:\n  static const uint8_t MASK = 0x1;\n\n public:\n  enum { FREE = 0, LOCKED = MASK };\n  // lock_ can't be std::atomic<> to preserve POD-ness.\n  mutable uint8_t lock_;\n\n  // Initialize this MSL.  It is unnecessary to call this if you\n  // zero-initialize the MicroSpinLock.\n  void Init() {\n    Payload()->store(Payload()->load() & ~MASK);\n  }\n\n  bool TryLock() {\n    uint8_t val = Payload()->load();\n    return CompareAndSwap(val & ~MASK, val | MASK);\n  }\n\n  void Lock() {\n    Sleeper sleeper;\n    do {\n      while ((Payload()->load() & MASK) != FREE) {\n        sleeper.Wait();\n      }\n    } while (!TryLock());\n    DCHECK((Payload()->load() & MASK) == LOCKED);\n  }\n\n  void Unlock() {\n    uint8_t val = Payload()->load();\n    CHECK((val & MASK) == LOCKED);\n    Payload()->store(val & ~MASK, std::memory_order_release);\n  }\n\n  uint8_t Value() const {\n    return Payload()->load() >> 1;\n  }\n\n  void Set(uint8_t val) {\n    Payload()->store((val << 1) + (Payload()->load() & MASK));\n  }\n\n private:\n  std::atomic<uint8_t>* Payload() const {\n    return reinterpret_cast<std::atomic<uint8_t>*>(&this->lock_);\n  }\n\n  bool CompareAndSwap(uint8_t compare, uint8_t newVal) {\n    return std::atomic_compare_exchange_strong_explicit(Payload(), &compare, newVal,\n                                                        std::memory_order_acquire,\n                                                        std::memory_order_relaxed);\n  }\n};\n\nstatic_assert(std::is_pod<MicroOneBitSpinLock>::value,\n              \"MicroOneBitSpinLock must be kept a POD type.\");\n\n}  // namespace concurrency\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_MICRO_ONE_BIT_SPIN_LOCK_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/queue.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_QUEUE_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_QUEUE_H_\n\n#include <chrono>\n#include <condition_variable>\n#include <mutex>\n#include <queue>\n#include <thread>\n\nnamespace monolith {\nnamespace concurrency {\n\ntemplate <typename T, bool Ordered = false>\nclass Queue {\n public:\n  // Create a queue object with a given maximum size(default: max_size=1).\n  // If max_size is 0, the queue size is infinite.\n  explicit Queue(size_t max_size = 1)\n      : max_size_(max_size == 0 ? std::numeric_limits<size_t>::max()\n                                : max_size) {}\n\n  Queue(const Queue&) = delete;  // disable copying\n\n  Queue& operator=(const Queue&) = delete;  // disable assignment\n\n  // Return the front item of the queue, it blocks if no item was available.\n  T front() {\n    std::unique_lock<std::mutex> lock(mutex_);\n    while (queue_.empty()) {\n      enqueue_cond_.wait(lock);\n    }\n    return _top();\n  }\n\n  // Remove and return an item from the queue, it blocks if no item was\n  // available.\n  T pop() {\n    std::unique_lock<std::mutex> lock(mutex_);\n    while (queue_.empty()) {\n      enqueue_cond_.wait(lock);\n    }\n    auto val = _top();\n    queue_.pop();\n    lock.unlock();\n    dequeue_cond_.notify_one();\n    return val;\n  }\n\n  // Remove an item(and assign to T& item) from the queue, it blocks if\n  // no item was available.\n  void pop(T& item) {  // NOLINT\n    std::unique_lock<std::mutex> lock(mutex_);\n    while (queue_.empty()) {\n      enqueue_cond_.wait(lock);\n    }\n    item = _top();\n    queue_.pop();\n    lock.unlock();\n    dequeue_cond_.notify_one();\n  }\n\n  // Try to remove an item(and assign to T& item) from the queue, it blocks\n  // at most 'timeout' duration and return false if no item was available\n  // within that time.\n  template <typename Rep = int64_t, typename Period = std::milli>\n  bool try_pop(T& item, std::chrono::duration<Rep, Period> timeout) {  // NOLINT\n    std::unique_lock<std::mutex> lock(mutex_);\n    if (!enqueue_cond_.wait_for(lock, timeout,\n                                [this] { return !queue_.empty(); })) {\n      return false;\n    }\n\n    item = _top();\n    queue_.pop();\n    lock.unlock();\n    dequeue_cond_.notify_one();\n    return true;\n  }\n\n  // Try to push an item into the queue, it blocks at most 'timeout'\n  // duration and return false if no free slot was available within\n  // that time.\n  template <typename Rep = int64_t, typename Period = std::milli>\n  bool try_push(T item, std::chrono::duration<Rep, Period> timeout) {\n    std::unique_lock<std::mutex> lock(mutex_);\n    if (!dequeue_cond_.wait_for(lock, timeout,\n                                [this] { return queue_.size() < max_size_; })) {\n      return false;\n    }\n\n    queue_.push(std::move(item));\n    lock.unlock();\n    enqueue_cond_.notify_one();\n    return true;\n  }\n\n  // Put an item into the queue, it blocks if no free slot was available.\n  void push(T item) {\n    std::unique_lock<std::mutex> lock(mutex_);\n    while (queue_.size() >= max_size_) {\n      dequeue_cond_.wait(lock);\n    }\n\n    queue_.push(std::move(item));\n    lock.unlock();\n    enqueue_cond_.notify_one();\n  }\n\n  // Return true if the queue is empty, false otherwise (not reliable).\n  bool empty() {\n    std::unique_lock<std::mutex> lock(mutex_);\n    return queue_.empty();\n  }\n\n private:\n  template <bool enabled = Ordered>\n  inline typename std::enable_if<enabled, T>::type _top() {\n    return queue_.top();\n  }\n\n  template <bool enabled = Ordered>\n  inline typename std::enable_if<!enabled, T>::type _top() {\n    return queue_.front();\n  }\n\n private:\n  size_t max_size_;\n\n  typename std::conditional<Ordered, std::priority_queue<T>,\n                            std::queue<T>>::type queue_;\n\n  std::mutex mutex_;\n\n  std::condition_variable enqueue_cond_;\n\n  std::condition_variable dequeue_cond_;\n};\n}  // namespace concurrency\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_QUEUE_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/queue_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/concurrency/queue.h\"\n\n#include <atomic>\n#include <memory>\n#include <thread>\n\n#include \"gtest/gtest.h\"\n\nusing std::chrono::duration_cast;\nusing std::chrono::high_resolution_clock;\nusing std::chrono::microseconds;\nusing std::chrono::milliseconds;\n\nnamespace monolith {\nnamespace concurrency {\nnamespace {\n\nfloat PushTimeout(int timeout /* milliseconds */) {\n  monolith::concurrency::Queue<int> queue(1);\n  queue.push(1);\n  auto start = high_resolution_clock::now();\n  EXPECT_FALSE(queue.try_push(2, milliseconds(timeout)));\n  auto elapsed = high_resolution_clock::now() - start;\n\n  return duration_cast<microseconds>(elapsed).count() / 1000.f;\n}\n\nfloat PopTimeout(int timeout /* milliseconds */) {\n  monolith::concurrency::Queue<int> queue(1);\n  queue.push(1);\n  int item = queue.pop();\n  EXPECT_EQ(item, 1);\n\n  auto start = high_resolution_clock::now();\n  EXPECT_FALSE(queue.try_pop(item, milliseconds(timeout)));\n  auto elapsed = high_resolution_clock::now() - start;\n\n  return duration_cast<microseconds>(elapsed).count() / 1000.f;\n}\n\nTEST(QueueTest, Basic) {\n  std::atomic_int producer_count(0);\n  std::atomic_int consumer_count(0);\n  std::atomic<bool> done(false);\n  monolith::concurrency::Queue<int> queue(128);\n\n  const int iterations = 10 * 10000;\n  const int producer_thread_count = 10;\n  const int consumer_thread_count = 10;\n  const std::chrono::microseconds timeout(10);\n\n  auto producer = [&]() {\n    for (int i = 0; i != iterations; ++i) {\n      int value = ++producer_count;\n      while (!queue.try_push(value, timeout)) {}\n    }\n  };\n\n  auto consumer = [&]() {\n    int value;\n    while (!done) {\n      while (queue.try_pop(value, timeout))\n        ++consumer_count;\n    }\n\n    while (queue.try_pop(value, timeout))\n      ++consumer_count;\n  };\n\n  std::vector<std::thread> producer_threads, consumer_threads;\n\n  for (int i = 0; i != producer_thread_count; ++i) {\n    producer_threads.emplace_back(producer);\n  }\n  for (int i = 0; i != consumer_thread_count; ++i) {\n    consumer_threads.emplace_back(consumer);\n  }\n\n  for (auto& t : producer_threads) {\n    if (t.joinable()) {\n      t.join();\n    }\n  }\n\n  done = true;\n  for (auto& t : consumer_threads) {\n    if (t.joinable()) {\n      t.join();\n    }\n  }\n\n  EXPECT_EQ(producer_count, iterations * producer_thread_count);\n  EXPECT_EQ(consumer_count, iterations * consumer_thread_count);\n}\n\nTEST(QueueTest, Timeout) {\n  EXPECT_NEAR(PushTimeout(1), 1.f, 0.5);\n  EXPECT_NEAR(PushTimeout(10), 10.f, 2);\n  EXPECT_NEAR(PushTimeout(1000), 1000.f, 20);\n\n  EXPECT_NEAR(PopTimeout(1), 1.f, 0.5);\n  EXPECT_NEAR(PopTimeout(10), 10.f, 2);\n  EXPECT_NEAR(PopTimeout(1000), 1000.f, 20);\n}\n\n}  // namespace\n}  // namespace concurrency\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/random_number_generator_benchmark.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <random>\n#include \"absl/random/random.h\"\n#include \"benchmark/benchmark.h\"\n#include \"monolith/native_training/runtime/concurrency/xorshift.h\"\n\nnamespace monolith {\nnamespace concurrency {\nnamespace {\n\nconst int NUM = 1000000;\n\nvoid BM_STL(benchmark::State& state) {  // NOLINT\n  std::random_device random_device;\n  std::mt19937 engine{random_device()};\n  std::uniform_int_distribution<uint32_t> dist(\n      0, std::numeric_limits<uint32_t>::max());\n  for (auto _ : state) {\n    for (int i = 0; i < NUM; ++i) {\n      dist(engine);\n    }\n  }\n}\n\nvoid BM_Absl(benchmark::State& state) {  // NOLINT\n  absl::BitGen bit_gen;\n  for (auto _ : state) {\n    for (int i = 0; i < NUM; ++i) {\n      absl::Uniform(bit_gen, 0u, std::numeric_limits<uint32_t>::max());\n    }\n  }\n}\n\nvoid BM_XorShift(benchmark::State& state) {  // NOLINT\n  for (auto _ : state) {\n    for (int i = 0; i < NUM; ++i) {\n      XorShift::Rand32ThreadSafe();\n    }\n  }\n}\n\n// Run on (96 X 3900 MHz CPU s)\n// CPU Caches:\n//     L1 Data 32K (x48)\n//     L1 Instruction 32K (x48)\n//     L2 Unified 1024K (x48)\n//     L3 Unified 36608K (x2)\n// Load Average: 10.64, 12.83, 14.44\n// ------------------------------------------------------\n// Benchmark            Time             CPU   Iterations\n// ------------------------------------------------------\n// BM_STL         5849400 ns      5849341 ns          117\n// BM_Absl        5574646 ns      5574647 ns          126\n// BM_XorShift    3250932 ns      3244318 ns          216\nBENCHMARK(BM_STL);\nBENCHMARK(BM_Absl);\nBENCHMARK(BM_XorShift);\n\n}  // namespace\n}  // namespace concurrency\n}  // namespace monolith\n\nBENCHMARK_MAIN();\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/sleeper.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/*\n * Copyright (c) Meta Platforms, Inc. and affiliates.\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 */\n\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_SLEEPER_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_SLEEPER_H_\n\nnamespace monolith {\nnamespace concurrency {\n\n// detection for 64 bit\n#if defined(__x86_64__) || defined(_M_X64)\n#define FOLLY_X64 1\n#else\n#define FOLLY_X64 0\n#endif\n\n#if defined(__aarch64__)\n#define FOLLY_AARCH64 1\n#else\n#define FOLLY_AARCH64 0\n#endif\n\ninline void asm_volatile_pause() {\n#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))\n  ::_mm_pause();\n#elif defined(__i386__) || FOLLY_X64\n  asm volatile(\"pause\");\n#elif FOLLY_AARCH64 || defined(__arm__)\n  asm volatile(\"yield\");\n#elif FOLLY_PPC64\n  asm volatile(\"or 27,27,27\");\n#endif\n}\n\n/*\n * A helper object for the contended case. Starts off with eager\n * spinning, and falls back to sleeping for small quantums.\n */\nclass Sleeper {\n  static const uint32_t kMaxActiveSpin = 4000;\n\n  uint32_t spinCount;\n\n public:\n  Sleeper() : spinCount(0) {}\n\n  void Wait() {\n    if (spinCount < kMaxActiveSpin) {\n      ++spinCount;\n      asm_volatile_pause();\n    } else {\n      /*\n       * Always sleep 0.5ms, assuming this will make the kernel put\n       * us down for whatever its minimum timer resolution is (in\n       * linux this varies by kernel version from 1ms to 10ms).\n       */\n      struct timespec ts = {0, 500000};\n      nanosleep(&ts, nullptr);\n    }\n  }\n};\n\n}  // namespace concurrency\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_SLEEPER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/thread_pool.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n\n/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"thread_pool.h\"\n\nnamespace monolith {\nnamespace concurrency {\n\nThreadPool::~ThreadPool() {\n  {\n    absl::MutexLock l(&mu_);\n    for (size_t i = 0; i < threads_.size(); i++) {\n      queue_.push(nullptr);  // Shutdown signal.\n    }\n  }\n  for (auto &t : threads_) {\n    t.join();\n  }\n}\n\nvoid ThreadPool::Schedule(std::function<void()> func) {\n  assert(func != nullptr);\n  absl::MutexLock l(&mu_);\n  queue_.push(std::move(func));\n}\n\nvoid ThreadPool::WorkLoop() {\n  while (true) {\n    std::function<void()> func;\n    {\n      absl::MutexLock l(&mu_);\n      mu_.Await(absl::Condition(this, &ThreadPool::WorkAvailable));\n      func = std::move(queue_.front());\n      queue_.pop();\n    }\n    if (func == nullptr) {  // Shutdown signal.\n      break;\n    }\n    func();\n  }\n}\n\n}  // namespace concurrency\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/thread_pool.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_THREAD_POOL_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_THREAD_POOL_H_\n\n#include <cassert>\n#include <cstddef>\n#include <functional>\n#include <queue>\n#include <thread>\n#include <vector>\n#include \"absl/base/thread_annotations.h\"\n#include \"absl/synchronization/mutex.h\"\n\nnamespace monolith {\nnamespace concurrency {\n/**\n * A simple ThreadPool implementation for tests/benchmarks.\n */\nclass ThreadPool {\n public:\n  explicit ThreadPool(int num_threads) {\n    for (int i = 0; i < num_threads; ++i) {\n      threads_.emplace_back(&ThreadPool::WorkLoop, this);\n    }\n  }\n\n  ThreadPool(const ThreadPool &) = delete;\n  ThreadPool &operator=(const ThreadPool &) = delete;\n\n  ~ThreadPool();\n\n  // Schedule a function to be run on a ThreadPool thread immediately.\n  void Schedule(std::function<void()> func);\n\n private:\n  bool WorkAvailable() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {\n    return !queue_.empty();\n  }\n\n  void WorkLoop();\n\n  absl::Mutex mu_;\n  std::queue<std::function<void()>> queue_ ABSL_GUARDED_BY(mu_);\n  std::vector<std::thread> threads_;\n};\n\n}  // namespace concurrency\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_THREAD_POOL_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/xorshift.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/concurrency/xorshift.h\"\n\nnamespace monolith {\nnamespace concurrency {\n\nuint64_t XorShift::XorShift1024Star() {\n  uint64_t s0 = s[p];\n  uint64_t s1 = s[p = (p + 1) & 15];\n  s1 ^= s1 << 31;  // a\n  s1 ^= s1 >> 11;  // b\n  s0 ^= s0 >> 30;  // c\n  return (s[p] = s0 ^ s1) * UINT64_C(1181783497276652981);\n}\n\nuint64_t XorShift::XorShift128Plus() {\n  uint64_t x = s[0];\n  uint64_t const y = s[1];\n  s[0] = y;\n  x ^= x << 23;        // a\n  x ^= x >> 17;        // b\n  x ^= y ^ (y >> 26);  // c\n  s[1] = x;\n  return x + y;\n}\n\nuint64_t XorShift::XorShift64Star() {\n  x ^= x >> 12;  // a\n  x ^= x << 25;  // b\n  x ^= x >> 27;  // c\n  return x * UINT64_C(2685821657736338717);\n}\n\nuint32_t XorShift::Rand32ThreadSafe() {\n  static thread_local XorShift xor_shift;\n  return xor_shift.Rand32();\n}\n\n}  // namespace concurrency\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/xorshift.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_XORSHIFT_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_XORSHIFT_H_\n\n#include <cstdint>\n#include <ctime>\n#include <iostream>\n#include <limits>\n#include <vector>\n\nnamespace monolith {\nnamespace concurrency {\n\nclass XorShift {\n public:\n  XorShift() : p(0) {\n    srand(time(0));\n    x = (uint64_t)std::rand() * RAND_MAX + std::rand();\n    for (uint64_t& i : s) {\n      i = XorShift64Star();\n    }\n  }\n\n  uint32_t Rand32() { return (uint32_t)XorShift128Plus(); }\n  static uint32_t Rand32ThreadSafe();\n\n private:\n  uint64_t XorShift1024Star();\n  uint64_t XorShift128Plus();\n  uint64_t XorShift64Star();\n\n private:\n  uint64_t s[16];\n  int p;\n  uint64_t x; /* The state must be seeded with a nonzero value. */\n};\n\n}  // namespace concurrency\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_CONCURRENCY_XORSHIFT_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/concurrency/xorshift_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/concurrency/xorshift.h\"\n#include <random>\n#include <thread>\n\n#include \"gtest/gtest.h\"\n\nnamespace monolith {\nnamespace concurrency {\nnamespace {\n\nconst int64_t NUM = 1000000, RADIUS = std::numeric_limits<uint32_t>::max() / 2;\nconst int64_t RADIUS_SQUARE = RADIUS * RADIUS;\nconst float PI = 3.14f, eps = 0.01f;\n\nfloat EstimatingPiWithMonteCarlo(const std::function<uint32_t()>& generator) {\n  int count = 0;\n  for (int i = 0; i < NUM; ++i) {\n    auto x = generator() % RADIUS;\n    auto y = generator() % RADIUS;\n    if (x * x + y * y < RADIUS_SQUARE) {\n      ++count;\n    }\n  }\n\n  return static_cast<float>(count * 4) / NUM;\n}\n\nTEST(XorShift, SingleThread) {\n  std::random_device random_device;\n  std::mt19937 engine{random_device()};\n  std::uniform_int_distribution<> dist(0, RADIUS);\n  float pi1 = EstimatingPiWithMonteCarlo([&]() { return dist(engine); });\n  EXPECT_NEAR(pi1, PI, eps);\n\n  XorShift generator;\n  float pi2 = EstimatingPiWithMonteCarlo([&]() { return generator.Rand32(); });\n  EXPECT_NEAR(pi2, PI, eps);\n}\n\nTEST(XorShift, MultiThread) {\n  std::random_device random_device;\n  std::mt19937 engine{random_device()};\n  std::uniform_int_distribution<> dist(0, RADIUS);\n  float pi = EstimatingPiWithMonteCarlo([&]() { return dist(engine); });\n  EXPECT_NEAR(pi, PI, eps);\n\n  int thread_num = 10;\n  std::vector<float> pi_array(thread_num);\n  std::vector<std::thread> threads;\n  for (int i = 0; i < thread_num; ++i) {\n    threads.emplace_back([&]() {\n      float pi = EstimatingPiWithMonteCarlo(\n          [&]() { return XorShift::Rand32ThreadSafe(); });\n      EXPECT_NEAR(pi, PI, eps);\n    });\n  }\n  for (auto& t : threads) {\n    t.join();\n  }\n}\n\n}  // namespace\n}  // namespace concurrency\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/deep_insight/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_test\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_custom_op_library\")\n\npackage(default_visibility = [\"//monolith/native_training:__subpackages__\"])\n\ncc_library(\n    name = \"deep_insight_internal_deps\",\n)\n\ncc_library(\n    name = \"deep_insight\",\n    srcs = [\"deep_insight.cc\"],\n    hdrs = [\"deep_insight.h\"],\n    deps = [\n        \":deep_insight_internal_deps\",\n        \"//monolith/native_training/runtime/common:metrics\",\n        \"//third_party/nlohmann:json\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_test(\n    name = \"deep_insight_test\",\n    srcs = [\"deep_insight_test.cc\"],\n    deps = [\n        \":deep_insight\",\n        \"//monolith/native_training/runtime/common:metrics\",\n        \"@com_google_glog//:glog\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/deep_insight/deep_insight.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cmath>\n\n#include \"glog/logging.h\"\n#include \"monolith/native_training/runtime/common/metrics.h\"\n\nnamespace monolith {\nnamespace deep_insight {\n\n\n}  // namespace deep_insight\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/deep_insight/deep_insight.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_DEEP_INSIGHT\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_DEEP_INSIGHT\n#include <ctime>\n#include <iostream>\n#include <string>\n\n#include \"gflags/gflags.h\"\n#include \"glog/logging.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace monolith {\nnamespace deep_insight {\n\nclass ExtraField {\n public:\n  explicit ExtraField(const std::string& k) : key_(k) {}\n  virtual void add_to(nlohmann::json* j) = 0;\n  const std::string& key() { return key_; }\n\n private:\n  std::string key_;\n};\n\nclass FloatExtraField : public ExtraField {\n public:\n  explicit FloatExtraField(const std::string& k, const float& v)\n      : ExtraField(k), value_(v) {}\n  void add_to(nlohmann::json* j) { (*j)[\"extra_float\"][key()] = value_; }\n\n private:\n  float value_;\n};\n\nclass Int64ExtraField : public ExtraField {\n public:\n  explicit Int64ExtraField(const std::string& k, const int64_t& v)\n      : ExtraField(k), value_(v) {}\n  void add_to(nlohmann::json* j) { (*j)[\"extra_int\"][key()] = value_; }\n\n private:\n  int64_t value_;\n};\n\nclass StringExtraField : public ExtraField {\n public:\n  explicit StringExtraField(const std::string& k, const std::string& v)\n      : ExtraField(k), value_(v) {}\n  void add_to(nlohmann::json* j) { (*j)[\"extra_str\"][key()] = value_; }\n\n private:\n  std::string value_;\n};\nclass DeepInsight {\n public:\n  template <typename... Args>\n  explicit DeepInsight(Args...) {}\n\n  template <typename... Args>\n  std::string SendV2(Args...) {\n    return \"\";\n  }\n\n  template <typename... Args>\n  bool HitSampleRatio(Args...) { return false; }\n\n  int64_t GenerateTrainingTime() { return 0; }\n\n  uint64_t GetTotalSendCounter() { return 0; }\n};\n}  // namespace deep_insight\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_DEEP_INSIGHT\n"
  },
  {
    "path": "monolith/native_training/runtime/deep_insight/deep_insight_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/deep_insight/deep_insight.h\"\n\n#include <string>\n#include <vector>\n\n#include \"glog/logging.h\"\n#include \"gtest/gtest.h\"\n\nusing monolith::deep_insight::ExtraField;\nusing monolith::deep_insight::FloatExtraField;\nusing monolith::deep_insight::Int64ExtraField;\nusing monolith::deep_insight::StringExtraField;\nusing json = nlohmann::json;\n\nnamespace monolith {\nnamespace deep_insight {\n\n\n}  // namespace deep_insight\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_test\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime:__subpackages__\"])\n\ncc_library(\n    name = \"types\",\n    hdrs = [\"types.h\"],\n)\n\ncc_library(\n    name = \"filter\",\n    hdrs = [\"filter.h\"],\n    deps = [\n        \":types\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_cc_proto\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_factory\",\n    ],\n)\n\ncc_library(\n    name = \"hash_filter\",\n    hdrs = [\"hash_filter.h\"],\n    srcs = [\"hash_filter.cc\"],\n    deps = [\n        \":filter\",\n        \":types\",\n        \"@com_google_absl//absl/algorithm:container\",\n        \"@com_google_absl//absl/hash\",\n        \"@com_google_absl//absl/types:span\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_test(\n    name = \"hash_filter_test\",\n    srcs = [\"hash_filter_test.cc\"],\n    deps = [\n        \":hash_filter\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"sliding_hash_filter\",\n    srcs = [\"sliding_hash_filter.cc\"],\n    hdrs = [\"sliding_hash_filter.h\"],\n    deps = [\n        \":hash_filter\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_cc_proto\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_library(\n    name = \"dummy_hash_filter\",\n    hdrs = [\"dummy_hash_filter.h\"],\n    deps = [\n        \":hash_filter\",\n    ],\n)\n\ncc_test(\n    name = \"sliding_hash_filter_test\",\n    srcs = [\"sliding_hash_filter_test.cc\"],\n    deps = [\n        \":sliding_hash_filter\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"probabilistic_filter\",\n    hdrs = [\"probabilistic_filter.h\"],\n    srcs = [\"probabilistic_filter.cc\"],\n    deps = [\n        \":filter\",\n        \":hash_filter\",\n        \"//monolith/native_training/runtime/concurrency:xorshift\",\n    ],\n)\n\ncc_binary(\n    name = \"probabilistic_filter_test\",\n    srcs = [\"probabilistic_filter_test.cc\"],\n    deps = [\n        \":probabilistic_filter\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/dummy_hash_filter.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/**\n * Implement a dummy hash filter which has no real hash filter logic inside.\n * It will always return HashFilter<uint16_t>::max_count() so that all FIDs can\n * pass the hash filter check.\n **/\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_DUMMY_HASH_FILTER_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_DUMMY_HASH_FILTER_H_\n\n#include <functional>\n\n#include \"monolith/native_training/runtime/hash_filter/filter.h\"\n#include \"monolith/native_training/runtime/hash_filter/hash_filter.h\"\n\nnamespace monolith {\nnamespace hash_filter {\n\nclass DummyHashFilter : public Filter {\n public:\n  DummyHashFilter() = default;\n  DummyHashFilter(const DummyHashFilter& other) {}\n  uint32_t add(FID fid, uint32_t count) {\n    return HashFilter<uint16_t>::max_count();\n  }\n  uint32_t get(FID fid) const { return HashFilter<uint16_t>::max_count(); }\n  uint32_t size_mb() const { return 0; }\n  size_t estimated_total_element() const { return 0; }\n  size_t failure_count() const { return 0; }\n  size_t split_num() const { return 0; }\n\n  bool ShouldBeFiltered(\n      int64_t fid, int64_t count, int64_t slot_occurrence_threshold,\n      const monolith::hash_table::EmbeddingHashTableInterface* table) override {\n    return false;\n  }\n\n  DummyHashFilter& operator=(DummyHashFilter const&) = delete;\n  DummyHashFilter* clone() const { return new DummyHashFilter(*this); }\n};\n\n}  // namespace hash_filter\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_DUMMY_HASH_FILTER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/filter.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_FILTER_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_FILTER_H_\n#include <string>\n\n#include \"monolith/native_training/runtime/hash_filter/types.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_interface.h\"\n\nnamespace monolith {\nnamespace hash_filter {\n\nclass Filter {\n public:\n  Filter() : capacity_(0) {}\n  virtual uint32_t add(FID fid, uint32_t count) = 0;\n  virtual uint32_t get(FID fid) const = 0;\n  virtual uint32_t size_mb() const = 0;\n  virtual size_t estimated_total_element() const = 0;\n  virtual size_t failure_count() const = 0;\n  virtual size_t capacity() const { return capacity_; }\n  virtual size_t split_num() const = 0;\n  virtual bool exceed_limit() const { return false; }\n  virtual void set_name(const std::string& name) { name_ = name; }\n  virtual Filter* clone() const = 0;\n  virtual bool ShouldBeFiltered(\n      int64_t fid, int64_t count, int64_t slot_occurrence_threshold,\n      const monolith::hash_table::EmbeddingHashTableInterface* table) = 0;\n  virtual void Save(\n      int split_idx,\n      std::function<void(::monolith::hash_table::HashFilterSplitMetaDump)>\n          write_meta_fn,\n      std::function<void(::monolith::hash_table::HashFilterSplitDataDump)>\n          write_data_fn) const {}\n  virtual void Restore(\n      int split_idx,\n      std::function<bool(::monolith::hash_table::HashFilterSplitMetaDump*)>\n          get_meta_fn,\n      std::function<bool(::monolith::hash_table::HashFilterSplitDataDump*)>\n          get_data_fn) {}\n\n  virtual ~Filter() {}\n  constexpr static unsigned char count_bit = 4;\n  constexpr static uint32_t max_count() { return (1 << count_bit) - 1; }\n\n protected:\n  size_t capacity_;\n  std::string name_;\n};\n\n}  // namespace hash_filter\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_FILTER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/hash_filter.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_filter/hash_filter.h\"\n\nnamespace monolith {\nnamespace hash_filter {\n\nnamespace proto2 = google::protobuf;\n\nusing ::monolith::hash_table::SlidingHashFilterMetaDump;\nusing ::monolith::hash_table::HashFilterSplitMetaDump;\nusing ::monolith::hash_table::HashFilterSplitDataDump;\n\nconst int kMaxNumPerTfRecord = 10000;\n\ntemplate <>\nvoid HashFilter<uint16_t>::Save(\n    const SlidingHashFilterMetaDump& sliding_hash_filter_meta_dump,\n    std::function<void(HashFilterSplitMetaDump)> write_meta_fn,\n    std::function<void(HashFilterSplitDataDump)> write_data_fn) const {\n  // Write meta part with one tf-record with HashFilterSplitMetaDump type.\n  HashFilterSplitMetaDump meta_dump;\n  meta_dump.set_failure_count(failure_count_);\n  meta_dump.set_total_size(total_size_);\n  meta_dump.set_num_elements(num_elements_);\n  meta_dump.set_fill_rate(fill_rate_);\n  *(meta_dump.mutable_sliding_hash_filter_meta()) =\n      sliding_hash_filter_meta_dump;\n  write_meta_fn(std::move(meta_dump));\n\n  // Write data part with multiple tf-records of HashFilterSplitDataDump type.\n  int tf_record_num =\n      (map_.size() + kMaxNumPerTfRecord - 1) / kMaxNumPerTfRecord;\n\n  for (int record_idx = 0; record_idx < tf_record_num; record_idx++) {\n    int start = record_idx * kMaxNumPerTfRecord;\n    int end =\n        std::min(start + kMaxNumPerTfRecord, static_cast<int>(map_.size()));\n    HashFilterSplitDataDump data_dump;\n    data_dump.set_offset(start);\n    for (int i = start; i < end; i++) {\n      data_dump.add_data(map_[i]);\n    }\n    write_data_fn(std::move(data_dump));\n  }\n}\n\ntemplate <>\nvoid HashFilter<uint16_t>::Restore(\n    HashFilterSplitMetaDump meta_dump,\n    std::function<bool(HashFilterSplitDataDump*)> get_data_fn) {\n  // Restore hash filter meta.\n  failure_count_ = meta_dump.failure_count();\n  total_size_ = meta_dump.total_size();\n  num_elements_ = meta_dump.num_elements();\n  fill_rate_ = meta_dump.fill_rate();\n\n  // Restore hash filter data.\n  map_.resize(total_size_ + MAX_STEP, 0);\n  HashFilterSplitDataDump data_dump;\n  while (get_data_fn(&data_dump)) {\n    int offset = data_dump.offset();\n    for (int i = 0; i < data_dump.data_size(); i++) {\n      map_[offset + i] = (uint16_t)(data_dump.data(i));\n    }\n  }\n}\n\n}  // namespace hash_filter\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/hash_filter.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_HASH_FILTER_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_HASH_FILTER_H_\n#include <cstdint>\n#include <fstream>\n#include <limits>\n\n#include \"absl/algorithm/container.h\"\n#include \"absl/hash/hash.h\"\n#include \"absl/types/span.h\"\n\n#include \"monolith/native_training/runtime/hash_filter/filter.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n\nnamespace monolith {\nnamespace hash_filter {\n\ntemplate <typename DATA>\nclass HashFilter;\ntemplate <typename DATA>\nclass HashFilterIterator {\n  friend class HashFilter<DATA>;\n\n public:\n  HashFilterIterator() : filter_(NULL), pvalue_(NULL), sign_(0) {}\n  uint32_t add(uint32_t add_count) {\n    assert(valid() && \"check validation before add\");\n    if (add_count > HashFilter<DATA>::max_count())\n      add_count = HashFilter<DATA>::max_count();\n    if (*pvalue_ == 0) {\n      ++filter_->num_elements_;\n      if (filter_->num_elements_ > filter_->capacity_) {\n        filter_->num_elements_ = filter_->capacity_;\n        /*\n        InvalidOperation io;\n        io.why = \"total elements exceeds filter capacity\";\n        throw io;\n        */\n      }\n      *pvalue_ = (sign_ << HashFilter<DATA>::count_bit) + add_count;\n      return 0;\n    }\n    unsigned char count = *pvalue_ & HashFilter<DATA>::max_count();\n    if (count + add_count >= HashFilter<DATA>::max_count())\n      *pvalue_ |= HashFilter<DATA>::max_count();\n    else\n      *pvalue_ += add_count;\n    return count;\n  }\n\n  uint32_t get() const {\n    if (!pvalue_) return HashFilter<DATA>::max_count();\n    return *pvalue_ & HashFilter<DATA>::max_count();\n  }\n\n  bool valid() const { return pvalue_ != NULL; }\n\n  bool empty() const {\n    assert(valid() && \"check validation before empty\");\n    return *pvalue_ == 0;\n  }\n\n private:\n  explicit HashFilterIterator(HashFilter<DATA>* filter, DATA* pvalue, DATA sign)\n      : filter_(filter), pvalue_(pvalue), sign_(sign) {}\n  HashFilter<DATA>* filter_;\n  DATA* pvalue_;\n  DATA sign_;\n};\n\ntemplate <typename DATA>\nclass HashFilter : public Filter {\n  friend class HashFilterIterator<DATA>;\n\n public:\n  explicit HashFilter(size_t capacity, double fill_rate = 1.5)\n      : failure_count_(0),\n        total_size_(capacity * fill_rate),\n        num_elements_(0),\n        fill_rate_(fill_rate) {\n    capacity_ = capacity;\n    map_.resize(total_size_ + MAX_STEP, 0);\n  }\n\n  uint32_t add(FID fid, uint32_t count) override {\n    HashFilterIterator<DATA> iter = find(fid, MAX_STEP);\n    if (iter.valid()) {\n      return iter.add(count);\n    }\n    failure_count_ += 1;\n    return max_count();\n  }\n\n  uint32_t get(FID fid) const override {\n    return const_cast<HashFilter*>(this)->find(fid, MAX_STEP).get();\n  }\n\n  HashFilterIterator<DATA> find(FID fid, int max_step) {\n    assert(max_step <= MAX_STEP && \"illegal max_step\");\n    DATA sign = signature(fid);\n    int step = 0;\n    size_t hash_value = hash(fid) % total_size_;\n    DATA* pvalue = reinterpret_cast<DATA*>(&map_[hash_value]);\n    do {\n      if (*pvalue == 0 || (*pvalue >> count_bit) == sign) {\n        return HashFilterIterator<DATA>(this, pvalue, sign);\n      }\n      ++pvalue;\n      if (pvalue == &(*map_.end())) {\n        pvalue = &map_[0];\n      }\n    } while (++step < max_step);\n    return HashFilterIterator<DATA>(this, NULL, sign);\n  }\n\n  bool full() const { return num_elements_ >= capacity_ - 1; }\n\n  // TODO make this async\n  void async_clear() {\n    fill(map_.begin(), map_.end(), 0);\n    num_elements_ = 0;\n    failure_count_ = 0;\n  }\n\n  uint32_t size_mb() const override {\n    return map_.size() * sizeof(DATA) / 1024.0 / 1024.0;\n  }\n\n  static size_t size_byte(size_t capacity, double fill_rate = 1.5) {\n    return capacity * sizeof(DATA) * fill_rate + MAX_STEP;\n  }\n\n  size_t failure_count() const override { return failure_count_; }\n\n  size_t split_num() const override { return 0; }\n\n  DATA signature(FID fid) const { return (fid >> 17 | fid << 15) & sign_mask; }\n\n  size_t estimated_total_element() const override { return num_elements_; }\n\n  bool exceed_limit() const override { return num_elements_ >= capacity_; }\n\n  HashFilter* clone() const override { return new HashFilter(*this); }\n\n  bool ShouldBeFiltered(\n      int64_t fid, int64_t count, int64_t slot_occurrence_threshold,\n      const monolith::hash_table::EmbeddingHashTableInterface* table) override {\n    if (slot_occurrence_threshold <= 0) {\n      return false;\n    }\n    return add(fid, count) < slot_occurrence_threshold;\n  }\n\n  bool operator==(const HashFilter& other) const {\n    return total_size_ == other.total_size_ &&\n           num_elements_ == other.num_elements_ &&\n           capacity_ == other.capacity_ && fill_rate_ == other.fill_rate_ &&\n           map_ == other.map_;\n  }\n\n  void Save(const ::monolith::hash_table::SlidingHashFilterMetaDump&\n                sliding_hash_filter_meta_dump,\n            std::function<void(::monolith::hash_table::HashFilterSplitMetaDump)>\n                write_meta_fn,\n            std::function<void(::monolith::hash_table::HashFilterSplitDataDump)>\n                write_data_fn) const;\n\n  void Restore(\n      ::monolith::hash_table::HashFilterSplitMetaDump dump,\n      std::function<bool(::monolith::hash_table::HashFilterSplitDataDump*)>\n          get_data_fn);\n\n  constexpr static DATA sign_mask =\n      ((1 << (sizeof(DATA) * 8)) - 1) >> count_bit;\n\n private:\n  constexpr static int DUMP_VALUE_SIZE = 1024 * 1024 * 20;  // 10-20MB\n  constexpr static int MAX_STEP = 64;\n  size_t hash(FID fid) const { return absl::Hash<FID>()(fid); }\n  std::vector<DATA> map_;\n  uint64_t failure_count_;\n  uint64_t total_size_;\n  uint64_t num_elements_;\n  double fill_rate_;\n};\n\n}  // namespace hash_filter\n}  // namespace monolith\n\n/* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_HASH_FILTER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/hash_filter_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <fstream>\n#include <unordered_map>\n\n#include <gtest/gtest.h>\n#include \"monolith/native_training/runtime/hash_filter/hash_filter.h\"\n\nnamespace monolith {\nnamespace hash_filter {\n\ntemplate <typename DATA>\nvoid test_simple_bloom(size_t key_num) {\n  HashFilter<DATA> counter(key_num);\n  for (uint32_t i = 0; i <= HashFilter<DATA>::max_count(); ++i) {\n    uint32_t expect = std::min(i + 1, HashFilter<DATA>::max_count());\n    EXPECT_EQ(i, counter.add(1, 1));\n    EXPECT_EQ(expect, counter.get(1));\n  }\n  ASSERT_EQ(HashFilter<DATA>::max_count(), counter.add(1, 1));\n  ASSERT_EQ(HashFilter<DATA>::max_count(), counter.get(1));\n}\n\nTEST(HashFilterTest, test_simple) {\n  test_simple_bloom<uint8_t>(1);\n  test_simple_bloom<uint8_t>(3);\n  test_simple_bloom<uint8_t>(100);\n  test_simple_bloom<uint16_t>(1);\n  test_simple_bloom<uint16_t>(3);\n  test_simple_bloom<uint16_t>(100);\n}\n\ntemplate <typename DATA>\nvoid test_count() {\n  HashFilter<DATA> filter(1000000);\n  std::srand(std::time(NULL));\n  filter.add(std::rand(), 2);\n  EXPECT_EQ(1llu, filter.estimated_total_element());\n  int key_number = 10;\n  for (int i = 0; i != key_number; ++i) {\n    filter.add(std::rand(), 2);\n  }\n  EXPECT_EQ(size_t(key_number + 1), filter.estimated_total_element());\n\n  HashFilter<DATA> filter2(1000000);\n  filter2.add(10000002961562801052lu, 1);\n  filter2.add(10000002961562801052lu, 20);\n  filter2.add(10000002961562801052lu, 1);\n  EXPECT_EQ(1llu, filter2.estimated_total_element());\n  std::unique_ptr<HashFilter<DATA>> filter3(filter2.clone());\n  EXPECT_TRUE(filter2 == *filter3);\n}\n\nTEST(HashFilterTest, test_count) {\n  test_count<uint8_t>();\n  test_count<uint16_t>();\n}\n\ntemplate <typename DATA>\nvoid compare_to_unordered_map(int key_number, double expected_rate,\n                              double fill_rate) {\n  HashFilter<DATA> filter(key_number, fill_rate);\n  std::unordered_map<int, uint32_t> map_counter;\n  std::srand(std::time(NULL));\n  for (int i = 0; i != key_number; ++i) {\n    int num = std::rand();\n    if (map_counter[num] < HashFilter<DATA>::max_count() - 1) {\n      map_counter[num] += 2;\n      filter.add(num, 2);\n    }\n  }\n  ASSERT_LE(filter.estimated_total_element(), map_counter.size());\n  int error_counter = 0;\n  for (auto iter = map_counter.begin(); iter != map_counter.end(); ++iter) {\n    if (filter.get(iter->first) != iter->second) error_counter += 1;\n  }\n  double error_rate = error_counter / double(map_counter.size());\n  double diff = error_rate / expected_rate;\n  std::cout << \"conflict rate diff \" << diff << std::endl;\n  EXPECT_GT(diff, 0.5);\n  EXPECT_LT(diff, 2);\n  EXPECT_EQ(0llu, filter.failure_count());\n}\n\nTEST(HashFilterTest, compare_to_unordered_map) {\n  compare_to_unordered_map<uint8_t>(1000000, 0.0208, 4);\n  compare_to_unordered_map<uint16_t>(1000000, 0.000242, 2);\n}\n\ntemplate <typename DATA>\nvoid TestSkipZeroThresholdFeatures() {\n  HashFilter<DATA> filter(1000000, 10);\n  for (uint32_t i = 0; i < 5; ++i) {\n    int64_t fid_with_zero_threshold = i;\n    EXPECT_FALSE(filter.ShouldBeFiltered(fid_with_zero_threshold, 1, 0,\n                                         nullptr /* table */));\n    int64_t normal_fid = i * 2;\n    EXPECT_TRUE(filter.ShouldBeFiltered(normal_fid, 1, 1, nullptr /* table */));\n  }\n  // Only the normal_fids can be added to the filter.\n  EXPECT_EQ(5llu, filter.estimated_total_element());\n}\n\nTEST(HashFilterTest, SkipZeroThresholdFeatures) {\n  TestSkipZeroThresholdFeatures<uint8_t>();\n  TestSkipZeroThresholdFeatures<uint16_t>();\n}\n\n/* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */\n\n}  // namespace hash_filter\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/probabilistic_filter.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_filter/probabilistic_filter.h\"\n\nnamespace monolith {\nnamespace hash_filter {\n\nusing ::monolith::concurrency::XorShift;\n\nbool ProbabilisticFilter::InsertedIntoHashTableUnequalProbability(\n    int64_t count, int64_t slot_occurrence_threshold) {\n  return XorShift::Rand32ThreadSafe() * slot_occurrence_threshold <\n         std::numeric_limits<uint32_t>::max() * count;\n}\n\nbool ProbabilisticFilter::InsertedIntoHashTableEqualProbability(\n    int64_t count, int64_t slot_occurrence_threshold) {\n  float epsilon = 0.05;\n  float p = 1 - std::pow(epsilon,\n                         1.f / static_cast<float>(slot_occurrence_threshold));\n\n  return XorShift::Rand32ThreadSafe() < std::numeric_limits<uint32_t>::max() *\n                                            (1.f - std::pow(1.f - p, count));\n}\n\nbool ProbabilisticFilter::ShouldBeFiltered(\n    int64_t fid, int64_t count, int64_t slot_occurrence_threshold,\n    const monolith::hash_table::EmbeddingHashTableInterface* table) {\n  if (table && !table->Contains(fid)) {\n    if (equal_probability_) {\n      return !InsertedIntoHashTableEqualProbability(count,\n                                                    slot_occurrence_threshold);\n    } else {\n      return !InsertedIntoHashTableUnequalProbability(\n          count, slot_occurrence_threshold);\n    }\n  }\n\n  return false;\n}\n\n}  // namespace hash_filter\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/probabilistic_filter.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_PROBABILISTIC_FILTER_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_PROBABILISTIC_FILTER_H_\n\n#include \"monolith/native_training/runtime/concurrency/xorshift.h\"\n#include \"monolith/native_training/runtime/hash_filter/filter.h\"\n#include \"monolith/native_training/runtime/hash_filter/hash_filter.h\"\n\nnamespace monolith {\nnamespace hash_filter {\n\nclass ProbabilisticFilter : public Filter {\n public:\n  explicit ProbabilisticFilter(bool equal_probability = false)\n      : equal_probability_(equal_probability) {}\n\n  uint32_t add(FID fid, uint32_t count) override {\n    return HashFilter<uint16_t>::max_count();\n  }\n\n  uint32_t get(FID fid) const override {\n    return HashFilter<uint16_t>::max_count();\n  }\n\n  uint32_t size_mb() const override { return 0; }\n\n  size_t estimated_total_element() const override { return 0; }\n\n  size_t failure_count() const override { return 0; }\n\n  size_t split_num() const override { return 0; }\n\n  Filter* clone() const override { return new ProbabilisticFilter(*this); }\n\n  bool InsertedIntoHashTableUnequalProbability(\n      int64_t count, int64_t slot_occurrence_threshold);\n\n  bool InsertedIntoHashTableEqualProbability(int64_t count,\n                                             int64_t slot_occurrence_threshold);\n\n  bool ShouldBeFiltered(\n      int64_t fid, int64_t count, int64_t slot_occurrence_threshold,\n      const monolith::hash_table::EmbeddingHashTableInterface* table) override;\n\n private:\n  bool equal_probability_;\n};\n\n}  // namespace hash_filter\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_PROBABILISTIC_FILTER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/probabilistic_filter_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_filter/probabilistic_filter.h\"\n#include \"google/protobuf/text_format.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_factory.h\"\n\nnamespace monolith {\nnamespace hash_filter {\nnamespace {\n\nusing ::monolith::hash_table::EmbeddingHashTableConfig;\nusing ::monolith::hash_table::EmbeddingHashTableInterface;\nnamespace proto2 = google::protobuf;\n\nTEST(ProbabilisticFilterTest, UnequalProbability) {\n  ProbabilisticFilter filter;\n\n  int fid_num = 10000, slot_occurrence_threshold = 7;\n  int count1 = 0, count2 = 0;\n  float tolerance_ratio = 0.01f;\n  for (int i = 0; i < fid_num; ++i) {\n    if (filter.InsertedIntoHashTableUnequalProbability(\n            1, slot_occurrence_threshold)) {\n      ++count1;\n    }\n    if (filter.InsertedIntoHashTableUnequalProbability(\n            2, slot_occurrence_threshold)) {\n      ++count2;\n    }\n  }\n\n  EXPECT_NEAR(fid_num / slot_occurrence_threshold, count1,\n              fid_num * tolerance_ratio);\n  EXPECT_NEAR(fid_num / slot_occurrence_threshold * 2, count2,\n              fid_num * tolerance_ratio * 2);\n}\n\nTEST(ProbabilisticFilterTest, EqualProbability) {\n  ProbabilisticFilter filter;\n\n  int fid_num = 10000, slot_occurrence_threshold = 7;\n  int count1 = 0, count2 = 0;\n  float tolerance_ratio = 0.05;\n  float p = 1 - std::pow(tolerance_ratio,\n                         1.f / static_cast<float>(slot_occurrence_threshold));\n\n  for (int i = 0; i < fid_num; ++i) {\n    if (filter.InsertedIntoHashTableEqualProbability(\n            1, slot_occurrence_threshold)) {\n      ++count1;\n    }\n    if (filter.InsertedIntoHashTableEqualProbability(\n            2, slot_occurrence_threshold)) {\n      ++count2;\n    }\n  }\n\n  EXPECT_NEAR(fid_num * p, count1, fid_num * tolerance_ratio);\n  EXPECT_NEAR(fid_num * (1.f - std::pow(1 - p, 2)), count2,\n              fid_num * tolerance_ratio * 2);\n}\n\nTEST(ProbabilisticFilterTest, ShouldBeFiltered) {\n  EmbeddingHashTableConfig config;\n  EXPECT_TRUE(proto2::TextFormat::ParseFromString(R\"(\n    entry_config {\n      segments {\n        dim_size: 1\n        init_config { zeros {} }\n        opt_config { sgd {} }\n      }\n    }\n    cuckoo {}\n  )\",\n                                                  &config));\n  std::unique_ptr<EmbeddingHashTableInterface> table =\n      NewEmbeddingHashTableFromConfig(config);\n\n  ProbabilisticFilter filter(false);\n\n  int fid_num = 10000, slot_occurrence_threshold = 7;\n  int count1 = 0, count2 = 0, count3 = 0;\n  float tolerance_ratio = 0.01f;\n  for (int i = 0; i < fid_num; ++i) {\n    if (!filter.ShouldBeFiltered(i, 1, slot_occurrence_threshold,\n                                 table.get())) {\n      ++count1;\n    }\n\n    if (!filter.ShouldBeFiltered(i, 2, slot_occurrence_threshold,\n                                 table.get())) {\n      ++count2;\n    }\n\n    if (!filter.ShouldBeFiltered(i, 3, slot_occurrence_threshold, nullptr)) {\n      ++count3;\n    }\n  }\n\n  EXPECT_NEAR(fid_num / slot_occurrence_threshold, count1,\n              fid_num * tolerance_ratio);\n  EXPECT_NEAR(fid_num / slot_occurrence_threshold * 2, count2,\n              fid_num * tolerance_ratio);\n  EXPECT_EQ(fid_num, count3);\n}\n\n}  // namespace\n}  // namespace hash_filter\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/sliding_hash_filter.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <iostream>\n\n#include \"absl/strings/str_format.h\"\n#include \"glog/logging.h\"\n\n#include \"monolith/native_training/runtime/hash_filter/sliding_hash_filter.h\"\n\nnamespace monolith {\nnamespace hash_filter {\n\nusing ::monolith::hash_table::HashFilterSplitDataDump;\nusing ::monolith::hash_table::HashFilterSplitMetaDump;\nusing ::monolith::hash_table::SlidingHashFilterMetaDump;\n\nSlidingHashFilter::SlidingHashFilter(size_t capacity, int split_num)\n    : split_num_(split_num), head_(0), head_increment_(0), failure_count_(0) {\n  capacity_ = capacity;\n  if (capacity_ < 300) capacity_ = 300;\n  if (split_num < 5) split_num = 5;\n  filters_.resize(split_num);\n  max_backward_step_ = split_num - max_forward_step_;\n  // max_forward_step_ - 1 blocks are kept empty for looing forward\n  size_t split_capacity = get_split_capacity(capacity_, split_num);\n  for (auto& filter : filters_) {\n    filter.reset(new HashFilter<uint16_t>(split_capacity, 1.2));\n  }\n}\n\nSlidingHashFilter::SlidingHashFilter(const SlidingHashFilter& other)\n    : max_backward_step_(other.max_backward_step_),\n      filters_(other.filters_.size()),\n      head_(other.head_),\n      head_increment_(other.head_increment_),\n      failure_count_(other.failure_count_) {\n  capacity_ = other.capacity_;\n  if (&other == this) {\n    return;\n  }\n  for (size_t i = 0; i != filters_.size(); ++i) {\n    filters_[i].reset(other.filters_[i]->clone());\n  }\n}\n\nuint32_t SlidingHashFilter::add(FID fid, uint32_t count) {\n  uint32_t old_count = 0;\n\n  // Look forward to find current value\n  HashFilterIterator<uint16_t> curr_iter = bidirectional_find(\n      head_, max_forward_step_, fid, false,\n      std::bind(&SlidingHashFilter::next, this, std::placeholders::_1));\n  if (curr_iter.valid()) {\n    if (!curr_iter.empty()) {\n      return curr_iter.add(count);\n    }\n  } else {\n    failure_count_ += 1;\n    return HashFilter<uint16_t>::max_count();\n  }\n\n  // Look backward to find old value\n  HashFilterIterator<uint16_t> old_iter = bidirectional_find(\n      prev(head_), std::min(head_increment_, max_backward_step_), fid, true,\n      std::bind(&SlidingHashFilter::prev, this, std::placeholders::_1));\n  if (old_iter.valid()) {\n    old_count = old_iter.get();\n    curr_iter.add(old_count + count);\n  } else {\n    curr_iter.add(count);\n  }\n  if (filters_[head_]->full()) {\n    head_ = next(head_);\n    head_increment_ += 1;\n    filters_[(head_ + max_forward_step_ - 1) % filters_.size()]->async_clear();\n  }\n  return old_count;\n}\n\nuint32_t SlidingHashFilter::get(FID fid) const {\n  // Look forward to find current value\n  HashFilterIterator<uint16_t> curr_iter = bidirectional_find(\n      head_, max_forward_step_, fid, false,\n      std::bind(&SlidingHashFilter::next, this, std::placeholders::_1));\n  if (curr_iter.valid()) {\n    if (!curr_iter.empty()) {\n      return curr_iter.get();\n    }\n  } else {\n    return HashFilter<uint16_t>::max_count();\n  }\n\n  // Look backward to find old value\n  HashFilterIterator<uint16_t> iter = bidirectional_find(\n      prev(head_), std::min(head_increment_, max_backward_step_), fid, true,\n      std::bind(&SlidingHashFilter::prev, this, std::placeholders::_1));\n  if (iter.valid()) {\n    return iter.get();\n  } else {\n    return 0;\n  }\n}\n\nHashFilterIterator<uint16_t> SlidingHashFilter::bidirectional_find(\n    size_t begin, int max_look, FID fid, bool exhaust,\n    std::function<size_t(size_t)> go) const {\n  size_t index = begin;\n  for (int i = 0; i != max_look; ++i) {\n    HashFilterIterator<uint16_t> iter = filters_[index]->find(fid, MAX_STEP);\n    // Looking forward only needs a valid position\n    // Looking backward needs a non-empty position\n    if (iter.valid() && (!exhaust || (exhaust && !iter.empty()))) return iter;\n    index = go(index);\n  }\n  return HashFilterIterator<uint16_t>();\n}\n\nsize_t SlidingHashFilter::estimated_total_element() const {\n  size_t result = 0;\n  for (auto& filter : filters_) {\n    result += filter->estimated_total_element();\n  }\n  return result;\n}\n\nSlidingHashFilter* SlidingHashFilter::clone() const {\n  return new SlidingHashFilter(*this);\n}\n\nbool SlidingHashFilter::operator==(const SlidingHashFilter& other) const {\n  if (!(max_forward_step_ == other.max_forward_step_ && head_ == other.head_ &&\n        head_increment_ % filters_.size() ==\n            other.head_increment_ % filters_.size() &&\n        capacity_ == other.capacity_ &&\n        filters_.size() == other.filters_.size())) {\n    return false;\n  }\n  for (size_t i = 0; i != filters_.size(); ++i) {\n    if (!(*filters_[i] == *other.filters_[i])) {\n      return false;\n    }\n  }\n  return true;\n}\n\nSlidingHashFilterMetaDump SlidingHashFilter::GetMetaDump() const {\n  SlidingHashFilterMetaDump dump;\n  dump.set_split_num(split_num_);\n  dump.set_max_forward_step(max_forward_step_);\n  dump.set_max_backward_step(max_backward_step_);\n  dump.set_max_step(MAX_STEP);\n  dump.set_head(head_);\n  dump.set_head_increment(head_increment_);\n  dump.set_failure_count(failure_count_);\n  return dump;\n}\n\n// Saves the data.\nvoid SlidingHashFilter::Save(\n    int split_idx, std::function<void(HashFilterSplitMetaDump)> write_meta_fn,\n    std::function<void(HashFilterSplitDataDump)> write_data_fn) const {\n  auto meta_dump = GetMetaDump();\n  filters_[split_idx]->Save(meta_dump, std::move(write_meta_fn),\n                            std::move(write_data_fn));\n}\n\nvoid SlidingHashFilter::ValidateData(uint32_t expect_value, uint32_t ckpt_value,\n                                     const char* msg) {\n  if (ckpt_value != expect_value) {\n    throw std::runtime_error(\n        absl::StrFormat(\"%s: %d does't match with : %d read from hash \"\n                        \"filter checkpoint file.\",\n                        msg, expect_value, ckpt_value));\n  }\n}\n\nvoid SlidingHashFilter::RestoreMetaDump(const HashFilterSplitMetaDump& dump) {\n  auto& sliding_hash_filter_meta_dump = dump.sliding_hash_filter_meta();\n\n  ValidateData(split_num_, sliding_hash_filter_meta_dump.split_num(),\n               \"split_num\");\n  ValidateData(max_forward_step_,\n               sliding_hash_filter_meta_dump.max_forward_step(),\n               \"max_forward_step\");\n  ValidateData(max_backward_step_,\n               sliding_hash_filter_meta_dump.max_backward_step(),\n               \"max_backward_step\");\n  ValidateData(MAX_STEP, sliding_hash_filter_meta_dump.max_step(), \"max_step\");\n  head_ = sliding_hash_filter_meta_dump.head();\n  head_increment_ = sliding_hash_filter_meta_dump.head_increment();\n  failure_count_ = sliding_hash_filter_meta_dump.failure_count();\n}\n\n// Restores the data from get_fn.\nvoid SlidingHashFilter::Restore(\n    int split_idx, std::function<bool(HashFilterSplitMetaDump*)> get_meta_fn,\n    std::function<bool(HashFilterSplitDataDump*)> get_data_fn) {\n  HashFilterSplitMetaDump meta_dump;\n  get_meta_fn(&meta_dump);\n  RestoreMetaDump(meta_dump);\n  filters_[split_idx]->Restore(meta_dump, std::move(get_data_fn));\n}\n\n}  // namespace hash_filter\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/sliding_hash_filter.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_SLIDING_HASH_FILTER_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_SLIDING_HASH_FILTER_H_\n\n#include <functional>\n\n#include \"monolith/native_training/runtime/hash_filter/filter.h\"\n#include \"monolith/native_training/runtime/hash_filter/hash_filter.h\"\n\nnamespace monolith {\nnamespace hash_filter {\n\nclass SlidingHashFilter : public Filter {\n public:\n  SlidingHashFilter(size_t capacity, int split_num);\n  SlidingHashFilter(const SlidingHashFilter& other);\n  uint32_t add(FID fid, uint32_t count);\n  uint32_t get(FID fid) const;\n  uint32_t size_mb() const { return filters_[0]->size_mb() * filters_.size(); }\n\n  static size_t get_split_capacity(size_t capacity, int split_num) {\n    return capacity / (split_num - max_forward_step_ + 1);\n  }\n\n  static uint32_t size_byte(size_t capacity, int split_num) {\n    return HashFilter<uint16_t>::size_byte(\n               get_split_capacity(capacity, split_num), 1.2) *\n           split_num;\n  }\n\n  size_t estimated_total_element() const;\n  size_t failure_count() const { return failure_count_; }\n  size_t split_num() const { return split_num_; }\n\n  bool ShouldBeFiltered(\n      int64_t fid, int64_t count, int64_t slot_occurrence_threshold,\n      const monolith::hash_table::EmbeddingHashTableInterface* table) override {\n    if (slot_occurrence_threshold <= 0) {\n      return false;\n    }\n    return this->add(fid, count) < slot_occurrence_threshold;\n  }\n\n  SlidingHashFilter& operator=(SlidingHashFilter const&) = delete;\n  SlidingHashFilter* clone() const;\n  bool operator==(SlidingHashFilter const&) const;\n\n  // Saves the data.\n  virtual void Save(\n      int split_idx,\n      std::function<void(::monolith::hash_table::HashFilterSplitMetaDump)>\n          write_meta_fn,\n      std::function<void(::monolith::hash_table::HashFilterSplitDataDump)>\n          write_data_fn) const;\n\n  // Restores the data from get_fn.\n  virtual void Restore(\n      int split_idx,\n      std::function<bool(::monolith::hash_table::HashFilterSplitMetaDump*)>\n          get_meta_fn,\n      std::function<bool(::monolith::hash_table::HashFilterSplitDataDump*)>\n          get_data_fn);\n\n private:\n  size_t prev(size_t index) const {\n    if (index == 0) return filters_.size() - 1;\n    return index - 1;\n  }\n  size_t next(size_t index) const {\n    if (index == filters_.size() - 1) return 0;\n    return index + 1;\n  }\n  HashFilterIterator<uint16_t> bidirectional_find(\n      size_t begin, int max_look, FID fid, bool exhaust,\n      std::function<size_t(size_t)> go) const;\n\n  ::monolith::hash_table::SlidingHashFilterMetaDump GetMetaDump() const;\n  void RestoreMetaDump(\n      const ::monolith::hash_table::HashFilterSplitMetaDump& dump);\n  void ValidateData(uint32_t expect_value, uint32_t ckpt_value,\n                    const char* msg);\n\n  size_t split_num_;\n  constexpr static int max_forward_step_ = 2;\n  int max_backward_step_;\n  constexpr static int MAX_STEP = 16;\n  std::vector<std::unique_ptr<HashFilter<uint16_t>>> filters_;\n  size_t head_;\n  int head_increment_;\n  size_t failure_count_;\n};\n}  // namespace hash_filter\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_SLIDING_HASH_FILTER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/sliding_hash_filter_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_filter/sliding_hash_filter.h\"\n\n#include <fstream>\n#include <unordered_map>\n\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_filter/types.h\"\n\nnamespace monolith {\nnamespace hash_filter {\nnamespace {\n\nstatic void test_simple_bloom(size_t key_num) {\n  SlidingHashFilter counter(key_num, 10);\n  for (uint32_t i = 0; i <= HashFilter<uint16_t>::max_count(); ++i) {\n    EXPECT_EQ(i, counter.add(1, 1));\n  }\n  ASSERT_EQ(HashFilter<uint16_t>::max_count(), counter.add(1, 1));\n}\n\nTEST(SlidingHashFilterTest, test_simple) {\n  test_simple_bloom(1);\n  test_simple_bloom(3);\n  test_simple_bloom(100);\n}\n\nTEST(SlidingHashFilterTest, test_count) {\n  SlidingHashFilter filter(1000000, 10);\n  filter.add(std::rand(), 2);\n  EXPECT_EQ(1llu, filter.estimated_total_element());\n  size_t key_number = 10;\n  for (size_t i = 0; i != key_number; ++i) {\n    filter.add(std::rand(), 2);\n  }\n  EXPECT_EQ(key_number + 1, filter.estimated_total_element());\n\n  SlidingHashFilter filter2(1000000, 10);\n  filter2.add(10000002961562801052lu, 1);\n  filter2.add(10000002961562801052lu, 20);\n  filter2.add(10000002961562801052lu, 1);\n  EXPECT_EQ(1llu, filter2.estimated_total_element());\n\n  std::unique_ptr<SlidingHashFilter> filter3(filter2.clone());\n  EXPECT_TRUE(filter2 == *filter3);\n}\n\ntemplate <typename Container>\nstatic void check_conflict_rate(SlidingHashFilter& filter,\n                                const Container& map_counter,\n                                double expected_rate) {\n  int error_counter = 0;\n  for (auto iter = map_counter.begin(); iter != map_counter.end(); ++iter) {\n    if (filter.get(iter->first) != iter->second) error_counter += 1;\n  }\n  double error_rate = error_counter / double(map_counter.size());\n  std::cout << \"expect:\" << expected_rate << \" actual:\" << error_rate\n            << std::endl;\n  EXPECT_NEAR(expected_rate, error_rate, expected_rate / 2);\n  EXPECT_GT(map_counter.size() / 10000, filter.failure_count());\n}\n\nstatic void compare_to_unordered_map(int key_number, double expected_rate,\n                                     size_t capacity) {\n  std::srand(capacity);\n  int split_num = 10;\n  SlidingHashFilter filter(capacity, split_num);\n  std::unordered_map<int, uint32_t> map_counter;\n  for (int i = 0; i != key_number; ++i) {\n    int num = std::rand();\n    if (map_counter[num] < HashFilter<uint16_t>::max_count() - 1) {\n      map_counter[num] += 2;\n      filter.add(num, 2);\n    }\n  }\n  check_conflict_rate(filter, map_counter, expected_rate);\n}\n\nTEST(SlidingHashFilterTest, compare_to_unordered_map) {\n  compare_to_unordered_map(1000000, 0.00908, 1000000);\n  compare_to_unordered_map(1000000, 0.50, 500000);\n}\n\nTEST(SlidingHashFilterTest, SkipZeroThresholdFeatures) {\n  SlidingHashFilter filter(1000000, 10);\n  for (uint32_t i = 0; i < 5; ++i) {\n    int64_t fid_with_zero_threshold = i;\n    EXPECT_FALSE(filter.ShouldBeFiltered(fid_with_zero_threshold, 1, 0,\n                                         nullptr /* table */));\n    int64_t normal_fid = i * 2;\n    EXPECT_TRUE(filter.ShouldBeFiltered(normal_fid, 1, 1, nullptr /* table */));\n  }\n  // Only the normal_fids can be added to the filter.\n  EXPECT_EQ(5llu, filter.estimated_total_element());\n}\n\n/* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */\n\n}  // namespace\n}  // namespace hash_filter\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_filter/types.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_TYPES_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_TYPES_H_\n#include <cstdint>\n\nnamespace monolith {\nnamespace hash_filter {\n\nusing FID = uint64_t;\n\n}  // namespace hash_filter\n}  // namesapce monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_FILTER_TYPES_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_proto_library\", \"cc_test\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\nload(\n    \"@org_tensorflow//tensorflow:tensorflow.bzl\",\n    \"if_cuda_is_configured_compat\",\n    \"tf_gpu_kernel_library_allow_except\",\n)\n\npackage(default_visibility = [\"//monolith/native_training/runtime:__subpackages__\"])\n\nproto_library(\n    name = \"embedding_hash_table_proto\",\n    srcs = [\"embedding_hash_table.proto\"],\n    deps = [\n        \"//monolith/native_training/runtime/hash_table/compressor:float_compressor_proto\",\n        \"//monolith/native_training/runtime/hash_table/initializer:initializer_config_proto\",\n        \"//monolith/native_training/runtime/hash_table/optimizer:optimizer_proto\",\n    ],\n)\n\ncc_proto_library(\n    name = \"embedding_hash_table_cc_proto\",\n    visibility = [\"//visibility:public\"],\n    deps = [\":embedding_hash_table_proto\"],\n)\n\npy_proto_library(\n    name = \"embedding_hash_table_py_proto\",\n    srcs = [\"embedding_hash_table.proto\"],\n    srcs_version = \"PY2AND3\",\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"//monolith/native_training/runtime/hash_table/compressor:float_compressor_py_proto\",\n        \"//monolith/native_training/runtime/hash_table/initializer:initializer_config_py_proto\",\n        \"//monolith/native_training/runtime/hash_table/optimizer:optimizer_py_proto\",\n    ],\n)\n\ncc_library(\n    name = \"entry_accessor\",\n    srcs = [\"entry_accessor.cc\"],\n    hdrs = [\n        \"entry_accessor.h\",\n        \"entry_accessor_decorator.h\",\n        \"quantized_entry_accessor.h\",\n    ],\n    deps = [\n        \":embedding_hash_table_cc_proto\",\n        \":utils\",\n        \"//monolith/native_training/runtime/hash_table/compressor:float_compressor\",\n        \"//monolith/native_training/runtime/hash_table/initializer:initializer_combination\",\n        \"//monolith/native_training/runtime/hash_table/initializer:initializer_factory\",\n        \"//monolith/native_training/runtime/hash_table/optimizer:optimizer_combination\",\n        \"//monolith/native_training/runtime/hash_table/optimizer:optimizer_factory\",\n        \"//monolith/native_training/runtime/hash_table/retriever:fake_quant_retriever\",\n        \"//monolith/native_training/runtime/hash_table/retriever:hash_net_retriever\",\n        \"//monolith/native_training/runtime/hash_table/retriever:raw_retriever\",\n        \"//monolith/native_training/runtime/hash_table/retriever:retriever_combination\",\n        \"@com_google_absl//absl/algorithm:container\",\n        \"@com_google_absl//absl/status\",\n        \"@com_google_absl//absl/time\",\n        \"@com_google_absl//absl/types:span\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_test(\n    name = \"entry_accessor_test\",\n    srcs = [\"entry_accessor_test.cc\"],\n    deps = [\n        \":embedding_hash_table_cc_proto\",\n        \":entry_accessor\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"quantized_entry_accessor\",\n    hdrs = [\n        \"entry_accessor_decorator.h\",\n        \"quantized_entry_accessor.h\",\n    ],\n    deps = [\n        \":entry_accessor\",\n        \"//monolith/native_training/runtime/hash_table/compressor:fake_quantizer\",\n    ],\n)\n\ncc_test(\n    name = \"quantized_entry_accessor_test\",\n    srcs = [\"quantized_entry_accessor_test.cc\"],\n    deps = [\n        \":quantized_entry_accessor\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ntf_gpu_kernel_library_allow_except(\n    name = \"embedding_hash_table_interface\",\n    srcs = [],\n    hdrs = [\"embedding_hash_table_interface.h\"],\n    deps = [\n        \":embedding_hash_table_cc_proto\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ncc_library(\n    name = \"embedding_hash_table_factory_internal_deps\",\n)\n\ntf_gpu_kernel_library_allow_except(\n    name = \"embedding_hash_table_factory\",\n    srcs = [\"embedding_hash_table_factory.cc\"],\n    hdrs = [\"embedding_hash_table_factory.h\"],\n    deps = [\n        \":embedding_hash_table_cc_proto\",\n        \":embedding_hash_table_factory_internal_deps\",\n        \":embedding_hash_table_interface\",\n        \":entry_accessor\",\n        \"//monolith/native_training/runtime/hash_table/cuckoohash:cuckoo_embedding_hash_table\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_library(\n    name = \"embedding_hash_table_test\",\n    hdrs = [\"embedding_hash_table_test.h\"],\n    deps = [\n        \":embedding_hash_table_cc_proto\",\n        \":embedding_hash_table_factory\",\n        \":embedding_hash_table_interface\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_binary(\n    name = \"hash_table_benchmark\",\n    srcs = [\"hash_table_benchmark.cc\"],\n    deps = [\n        \":embedding_hash_table_cc_proto\",\n        \":embedding_hash_table_factory\",\n        \":embedding_hash_table_interface\",\n        \"//monolith/native_training/runtime/concurrency:thread_pool\",\n        \"@com_github_google_benchmark//:benchmark\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_library(\n    name = \"utils\",\n    hdrs = [\"utils.h\"],\n)\n\ncc_library(\n    name = \"entry_defs\",\n    hdrs = [\"entry_defs.h\"],\n    deps = [\n        \"//monolith/native_training/runtime/allocator:block_allocator\",\n    ],\n)\n\ncc_test(\n    name = \"entry_defs_test\",\n    srcs = [\"entry_defs_test.cc\"],\n    deps = [\n        \":entry_defs\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/compressor/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_proto_library\", \"cc_test\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime/hash_table:__subpackages__\"])\n\nproto_library(\n    name = \"float_compressor_proto\",\n    srcs = [\"float_compressor.proto\"],\n)\n\ncc_proto_library(\n    name = \"float_compressor_cc_proto\",\n    deps = [\n        \":float_compressor_proto\",\n    ],\n)\n\npy_proto_library(\n    name = \"float_compressor_py_proto\",\n    srcs = [\"float_compressor.proto\"],\n    srcs_version = \"PY2AND3\",\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"float_compressor\",\n    srcs = [\"float_compressor.cc\"],\n    hdrs = [\"float_compressor.h\"],\n    defines = [\"HALF_ENABLE_F16C_INTRINSICS=0\"],\n    deps = [\n        \":float_compressor_cc_proto\",\n        \":fake_quantizer\",\n        \":hash_net_quantizer\",\n        \"//third_party/half_sourceforge_net:half\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ncc_test(\n    name = \"float_compressor_test\",\n    srcs = [\"float_compressor_test.cc\"],\n    deps = [\n        \":float_compressor\",\n        \"@com_google_absl//absl/types:span\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"fake_quantizer\",\n    hdrs = [\"fake_quantizer.h\"],\n    deps = [],\n)\n\ncc_test(\n    name = \"fake_quantizer_test\",\n    srcs = [\"fake_quantizer_test.cc\"],\n    deps = [\n        \":fake_quantizer\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"hash_net_quantizer\",\n    hdrs = [\"hash_net_quantizer.h\"],\n    deps = [\n        \":float_compressor_cc_proto\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_absl//absl/synchronization\",\n        \"@com_google_glog//:glog\",\n        \"@org_tensorflow//tensorflow/core/platform:logging\",\n    ],\n)\n\ncc_test(\n    name = \"hash_net_quantizer_test\",\n    srcs = [\"hash_net_quantizer_test.cc\"],\n    deps = [\n        \":hash_net_quantizer\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/compressor/fake_quantizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_COMPRESSOR_FAKE_QUANTIZER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_COMPRESSOR_FAKE_QUANTIZER_H_\n\n#include <cmath>\n\nnamespace monolith {\nnamespace hash_table {\n\n// Quantization Aware Training.\n// This class quantize a float32 number into an int8_t.\n// TODO(zhangbiao.david): support specifying min, max, num_bits etc.\nclass FakeQuantizer {\n public:\n  explicit FakeQuantizer(float r)\n      : r_(r), step_(r_ / kNegativeSlotNum), half_step_(step_ / 2) {}\n\n  // Quantize a given floating-point number.\n  float Quantize(float f) const { return IntegerToFloat(QuantizeToInteger(f)); }\n\n  // Quantize a floating-point number into integer representation.\n  int8_t QuantizeToInteger(float f) const {\n    // Round f to nearest float slot. E.g.,\n    // Assuming step = 1.0, and f = 3.6, we want nstep = 4.\n    if (std::isnan(f)) {\n      return 0;\n    }\n\n    if (f >= 0) {\n      f += half_step_;\n    } else {\n      f -= half_step_;\n    }\n    int nstep = f / step_;\n\n    if (nstep > kPositiveSlotNum) {\n      nstep = kPositiveSlotNum;\n    } else if (nstep < -kNegativeSlotNum) {\n      nstep = -kNegativeSlotNum;\n    }\n\n    return nstep;\n  }\n\n  // Restores an integer representation to a floating-point number.\n  float IntegerToFloat(int8_t x) const { return x * step_; }\n\n private:\n  static constexpr int kNumBits = sizeof(int8_t) * 8;\n  static constexpr int kSlotNum = 1 << kNumBits;\n  static constexpr int kPositiveSlotNum = kSlotNum / 2 - 1;\n  static constexpr int kNegativeSlotNum = kSlotNum / 2;\n\n  const float r_;\n  const float step_;\n  const float half_step_;\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_COMPRESSOR_FAKE_QUANTIZER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/compressor/fake_quantizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include \"monolith/native_training/runtime/hash_table/compressor/fake_quantizer.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Lt;\nusing ::testing::Le;\nusing ::testing::FloatEq;\nusing ::testing::Not;\nusing ::testing::Eq;\n\nTEST(FakeQuantizer, Quantization) {\n  FakeQuantizer model(5.0f);\n\n  EXPECT_THAT(model.Quantize(100.0), Lt(5.0));\n\n  // Symmetric\n  EXPECT_THAT(model.Quantize(0.0), 0.0f);\n\n  const float kStep = 5.0f / 128;\n  // Make sure quantization result is small enough.\n  EXPECT_THAT(std::abs(model.Quantize(3.5) - 3.5), Lt(kStep));\n\n  // Make sure round works correctly.\n  EXPECT_THAT(model.Quantize(kStep * 1.4), kStep);\n  EXPECT_THAT(model.Quantize(kStep * 1.6), kStep * 2);\n  EXPECT_THAT(model.Quantize(-kStep * 1.4), -kStep);\n  EXPECT_THAT(model.Quantize(-kStep * 1.6), -kStep * 2);\n  EXPECT_THAT(model.Quantize(std::nan(\"\")), 0);\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/compressor/float_compressor.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/compressor/float_compressor.h\"\n\n#include <exception>\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/compressor/fake_quantizer.h\"\n#include \"monolith/native_training/runtime/hash_table/compressor/hash_net_quantizer.h\"\n#include \"third_party/half_sourceforge_net/half.hpp\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass FloatCompressorBase : public FloatCompressorInterface {\n public:\n  FloatCompressorBase(int dim_size, int64_t size_bytes,\n                      int64_t uncompressed_size_bytes)\n      : dim_size_(dim_size),\n        size_bytes_(size_bytes),\n        uncompressed_size_bytes_(uncompressed_size_bytes) {}\n\n  // Use final to inline this when possible.\n  int64_t SizeBytes() const final { return size_bytes_; }\n\n  int64_t UncompressedSizeBytes() const final {\n    return uncompressed_size_bytes_;\n  }\n\n  int DimSize() const final { return dim_size_; }\n\n private:\n  int dim_size_;\n  int64_t size_bytes_;\n  int64_t uncompressed_size_bytes_;\n};\n\nclass Fp32FloatCompressor final : public FloatCompressorBase {\n public:\n  explicit Fp32FloatCompressor(const FloatCompressorConfig::Fp32& config)\n      : FloatCompressorBase(config.dim_size(),\n                            config.dim_size() * sizeof(float),\n                            config.dim_size() * sizeof(float)) {}\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Fp32(D=%d)\", FloatCompressorBase::DimSize());\n  }\n\n  void Encode(absl::Span<const float> num, void* compressed) const override {\n    auto* f = reinterpret_cast<float*>(compressed);\n    for (int i = 0; i < DimSize(); ++i) {\n      f[i] = num[i];\n    }\n  }\n\n  void Decode(const void* compressed, absl::Span<float> num) const override {\n    const auto* f = reinterpret_cast<const float*>(compressed);\n    for (int i = 0; i < DimSize(); ++i) {\n      num[i] = f[i];\n    }\n  }\n};\n\n// Converts a float to fp16\nclass Fp16FloatCompressor final : public FloatCompressorBase {\n public:\n  explicit Fp16FloatCompressor(const FloatCompressorConfig::Fp16& config)\n      : FloatCompressorBase(config.dim_size(),\n                            config.dim_size() * sizeof(int16_t),\n                            config.dim_size() * sizeof(float)) {}\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Fp16(D=%d)\", FloatCompressorBase::DimSize());\n  }\n\n  void Encode(absl::Span<const float> num, void* compressed) const override {\n    auto* i16 = reinterpret_cast<int16_t*>(compressed);\n    for (int i = 0; i < DimSize(); ++i) {\n      half_float::half x(num[i]);\n      i16[i] = *reinterpret_cast<int16_t*>(&x);\n    }\n  }\n\n  void Decode(const void* compressed, absl::Span<float> num) const override {\n    const auto* i16 = reinterpret_cast<const int16_t*>(compressed);\n    for (int i = 0; i < DimSize(); ++i) {\n      num[i] = *reinterpret_cast<const half_float::half*>(&i16[i]);\n    }\n  }\n};\n\n// Converts a float to fixed range int8.\nclass FixedR8FloatCompressor final : public FloatCompressorBase {\n public:\n  explicit FixedR8FloatCompressor(const FloatCompressorConfig::FixedR8& config)\n      : FloatCompressorBase(config.dim_size(),\n                            config.dim_size() * sizeof(int8_t),\n                            config.dim_size() * sizeof(float)),\n        fake_quantizer_(config.r()) {\n    LOG_EVERY_N(INFO, 100) << \"FixedR8FloatCompressor config: \"\n                           << config.DebugString();\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"FixedR8(D=%d)\", FloatCompressorBase::DimSize());\n  }\n\n  void Encode(absl::Span<const float> num, void* compressed) const override {\n    auto* i8 = reinterpret_cast<int8_t*>(compressed);\n    for (int i = 0; i < DimSize(); ++i) {\n      i8[i] = fake_quantizer_.QuantizeToInteger(num[i]);\n    }\n  }\n\n  void Decode(const void* compressed, absl::Span<float> num) const override {\n    const auto* i8 = reinterpret_cast<const int8_t*>(compressed);\n    for (int i = 0; i < DimSize(); ++i) {\n      num[i] = fake_quantizer_.IntegerToFloat(i8[i]);\n    }\n  }\n\n private:\n  FakeQuantizer fake_quantizer_;\n};\n\n// Converts a float to one bit.\n// We use an int8_t for testing stage\nclass OneBitFloatCompressor final : public FloatCompressorBase {\n public:\n  explicit OneBitFloatCompressor(const FloatCompressorConfig::OneBit& config)\n      : FloatCompressorBase(config.dim_size(),\n                            config.dim_size() * sizeof(int8_t),\n                            config.dim_size() * sizeof(float)),\n        hash_net_quantizer_(config) {}\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"OneBit(D=%d)\", FloatCompressorBase::DimSize());\n  }\n\n  void Encode(absl::Span<const float> num, void* compressed) const override {\n    auto* i8 = reinterpret_cast<int8_t*>(compressed);\n    for (int i = 0; i < DimSize(); ++i) {\n      i8[i] = hash_net_quantizer_.Forward(num[i]) > 0 ? 1 : -1;\n    }\n  }\n\n  void Decode(const void* compressed, absl::Span<float> num) const override {\n    float amplitude = hash_net_quantizer_.GetConfig().amplitude();\n    const auto* i8 = reinterpret_cast<const int8_t*>(compressed);\n    for (int i = 0; i < DimSize(); ++i) {\n      num[i] = static_cast<float>(i8[i]) * amplitude;\n    }\n  }\n\n private:\n  HashNetQuantizer hash_net_quantizer_;\n};\n\nclass CombinedFloatCompressor final : public FloatCompressorBase {\n public:\n  CombinedFloatCompressor(std::unique_ptr<FloatCompressorInterface> compressor1,\n                          std::unique_ptr<FloatCompressorInterface> compressor2)\n      : FloatCompressorBase(compressor1->DimSize() + compressor2->DimSize(),\n                            compressor1->SizeBytes() + compressor2->SizeBytes(),\n                            compressor1->UncompressedSizeBytes() +\n                                compressor2->UncompressedSizeBytes()),\n        compressor1_(std::move(compressor1)),\n        compressor2_(std::move(compressor2)),\n        compressor1_dim_size_(compressor1_->DimSize()),\n        compressor1_size_bytes_(compressor1_->SizeBytes()) {}\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"%s|%s\", compressor1_->DebugString(),\n                           compressor2_->DebugString());\n  }\n\n  void Encode(absl::Span<const float> num, void* compressed) const override {\n    absl::Span<const float> num1 = num.subspan(0, compressor1_dim_size_);\n    compressor1_->Encode(num1, compressed);\n    absl::Span<const float> num2 = num.subspan(compressor1_dim_size_);\n    void* compressed2 =\n        reinterpret_cast<char*>(compressed) + compressor1_size_bytes_;\n    compressor2_->Encode(num2, compressed2);\n  }\n\n  void Decode(const void* compressed, absl::Span<float> num) const override {\n    absl::Span<float> num1 = num.subspan(0, compressor1_dim_size_);\n    compressor1_->Decode(compressed, num1);\n    absl::Span<float> num2 = num.subspan(compressor1_dim_size_);\n    const void* compressed2 =\n        reinterpret_cast<const char*>(compressed) + compressor1_size_bytes_;\n    compressor2_->Decode(compressed2, num2);\n  }\n\n private:\n  std::unique_ptr<FloatCompressorInterface> compressor1_;\n\n  std::unique_ptr<FloatCompressorInterface> compressor2_;\n  const int compressor1_dim_size_;\n  const int64_t compressor1_size_bytes_;\n};\n\n}  // namespace\n\nstd::unique_ptr<FloatCompressorInterface> NewFloatCompressor(\n    FloatCompressorConfig config) {\n  switch (config.type_case()) {\n    case FloatCompressorConfig::kFp32:\n      return std::make_unique<Fp32FloatCompressor>(\n          std::move(*config.mutable_fp32()));\n    case FloatCompressorConfig::kFp16:\n      return std::make_unique<Fp16FloatCompressor>(\n          std::move(*config.mutable_fp16()));\n    case FloatCompressorConfig::kFixedR8:\n      return std::make_unique<FixedR8FloatCompressor>(\n          std::move(*config.mutable_fixed_r8()));\n    case FloatCompressorConfig::kOneBit:\n      return std::make_unique<OneBitFloatCompressor>(\n          std::move(*config.mutable_one_bit()));\n    default:\n      throw std::invalid_argument(absl::StrFormat(\n          \"Unknown tpye of float compressor. %s\", config.ShortDebugString()));\n  }\n}\n\nstd::unique_ptr<FloatCompressorInterface> CombineFloatCompressor(\n    std::unique_ptr<FloatCompressorInterface> compressor1,\n    std::unique_ptr<FloatCompressorInterface> compressor2) {\n  return std::make_unique<CombinedFloatCompressor>(std::move(compressor1),\n                                                   std::move(compressor2));\n}\n\n}  // namespace hash_table\n}  // namespace monolith"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/compressor/float_compressor.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_COMPRESSOR_ENTRY_SERVING_COMPRESSOR\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_COMPRESSOR_ENTRY_SERVING_COMPRESSOR\n#include \"absl/types/span.h\"\n\n#include \"monolith/native_training/runtime/hash_table/compressor/float_compressor.pb.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\n// Used to compress float number in online serving PS to save the memory.\nclass FloatCompressorInterface {\n public:\n  virtual ~FloatCompressorInterface() = default;\n\n  virtual std::string DebugString() const = 0;\n\n  // How many bytes are required for the compressor.\n  virtual int64_t SizeBytes() const = 0;\n\n  // How many bytes are required if not compressed.\n  virtual int64_t UncompressedSizeBytes() const = 0;\n\n  // How many dimensions this compressor support.\n  virtual int DimSize() const = 0;\n\n  // Encodes a list of floats into compressed.\n  virtual void Encode(absl::Span<const float> num, void* compressed) const = 0;\n\n  // Decodes a list of Int into a list of float.\n  virtual void Decode(const void* compressed, absl::Span<float> num) const = 0;\n};\n\nstd::unique_ptr<FloatCompressorInterface> NewFloatCompressor(\n    FloatCompressorConfig config);\n\nstd::unique_ptr<FloatCompressorInterface> CombineFloatCompressor(\n    std::unique_ptr<FloatCompressorInterface> compressor1,\n    std::unique_ptr<FloatCompressorInterface> compressor2);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_COMPRESSOR_ENTRY_SERVING_COMPRESSOR\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/compressor/float_compressor.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\n\npackage monolith.hash_table;\n\nmessage FloatCompressorConfig {\n  // Not compressed. Useful in the test.\n  message Fp32 {\n    optional int32 dim_size = 1;\n  }\n\n  // Using half-precision floating-point format.\n  message Fp16 {\n    optional int32 dim_size = 1;\n  }\n  // Corresponding to qat8 in Bytedance PS.\n  message FixedR8 {\n    optional int32 dim_size = 1;\n    optional float r = 2 [default = 1.0];\n  }\n\n  // HashNet\n  message OneBit {\n    optional int32 dim_size = 1;\n    optional int64 step_size = 2 [default = 200];\n    optional float init_scale = 3 [default = 1.0];\n    optional float max_scale = 4 [default = 10000.0];\n    optional float amplitude = 5 [default = 0.1];\n  }\n\n  oneof type {\n    Fp32 fp32 = 1;\n    Fp16 fp16 = 2;\n    FixedR8 fixed_r8 = 3;\n    OneBit one_bit = 4;\n  }\n}\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/compressor/float_compressor_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/compressor/float_compressor.h\"\n\n#include <string>\n\n#include \"absl/types/span.h\"\n#include \"gmock/gmock.h\"\n#include \"google/protobuf/text_format.h\"\n#include \"gtest/gtest.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::ElementsAre;\nusing ::testing::FloatNear;\nusing ::testing::Pointwise;\n\nstd::vector<float> EncodeDecode(const FloatCompressorInterface& compressor,\n                                absl::Span<const float> num) {\n  auto compressed = std::make_unique<char[]>(compressor.SizeBytes());\n  compressor.Encode(num, compressed.get());\n  std::vector<float> decoded(compressor.DimSize());\n  compressor.Decode(compressed.get(), absl::MakeSpan(decoded));\n  return decoded;\n}\n\nstd::vector<float> EncodeDecode(const FloatCompressorConfig& config,\n                                absl::Span<const float> num) {\n  auto compressor = NewFloatCompressor(config);\n  return EncodeDecode(*compressor, num);\n}\n\nFloatCompressorConfig ParseConfig(const std::string& text) {\n  FloatCompressorConfig c;\n  GOOGLE_CHECK(google::protobuf::TextFormat::ParseFromString(text, &c));\n  return c;\n}\n\nTEST(Fp32FloatCompressorTest, Basic) {\n  EXPECT_THAT(\n      EncodeDecode(ParseConfig(R\"(fp32 { dim_size: 3})\"), {0.1, 0.2, 10000.0}),\n      ElementsAre(0.1, 0.2, 10000.0));\n}\n\nTEST(Fp16FloatCompressorTest, Basic) {\n  EXPECT_THAT(\n      EncodeDecode(ParseConfig(R\"(fp16 { dim_size: 3})\"), {0.1, 0.2, 10000.0}),\n      Pointwise(FloatNear(1e-4), {0.1, 0.2, 10000.0}));\n}\n\nTEST(FixedR8FloatCompressorTest, Basic) {\n  const float kStep = 5.0f / 128;\n  EXPECT_THAT(EncodeDecode(ParseConfig(R\"(fixed_r8 { dim_size: 3 r : 5})\"),\n                           {100.0, 0.0, 3.5}),\n              Pointwise(FloatNear(kStep), {5.0, 0.0, 3.5}));\n  EXPECT_THAT(\n      EncodeDecode(ParseConfig(R\"(fixed_r8 { dim_size: 4 r : 5})\"),\n                   {kStep * 1.4f, kStep * 1.6f, -kStep * 1.4f, -kStep * 1.6f}),\n      ElementsAre(kStep, kStep * 2, -kStep, -kStep * 2));\n}\n\nTEST(OneBitFloatCompressorTest, Basic) {\n  EXPECT_THAT(\n      EncodeDecode(\n          ParseConfig(R\"(one_bit { dim_size: 7 step_size : 5 amplitude: 1.0})\"),\n          {100.0, 0.1, 0.00001, 0.0, -0.00001, -0.1, -100.0}),\n      Pointwise(FloatNear(0.1f), {1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0}));\n}\n\nTEST(CombinedFloatCompressorTest, Basic) {\n  auto compressor1 = NewFloatCompressor(ParseConfig(R\"(fp16 { dim_size: 1 })\"));\n  auto compressor2 = NewFloatCompressor(ParseConfig(R\"(fp16 { dim_size: 2 })\"));\n  auto compressor =\n      CombineFloatCompressor(std::move(compressor1), std::move(compressor2));\n  EXPECT_THAT(EncodeDecode(*compressor, {1.0, 2.0, 3.0, 4.0}),\n              Pointwise(FloatNear(1e-4), {1.0, 2.0, 3.0}));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/compressor/hash_net_quantizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_COMPRESSOR_HASH_NET_QUANTIZER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_COMPRESSOR_HASH_NET_QUANTIZER_H_\n\n#include <atomic>\n#include <cmath>\n#include <cstdint>\n#include <vector>\n\n#include \"absl/strings/str_format.h\"\n#include \"absl/synchronization/mutex.h\"\n#include \"glog/logging.h\"\n#include \"tensorflow/core/platform/logging.h\"\n\n#include \"monolith/native_training/runtime/hash_table/compressor/float_compressor.pb.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nclass HashNetQuantizer {\n public:\n  explicit HashNetQuantizer(FloatCompressorConfig_OneBit config)\n      : config_(std::move(config)) {\n    scale_ = config_.init_scale();\n    LOG(INFO) << absl::StrFormat(\"HashNetQuantizer: %s, scale = %.6f\",\n                                 config_.ShortDebugString(), scale_.load());\n  }\n\n  float Forward(float f) const {\n    return config_.amplitude() * std::tanh(scale_ * f);\n  }\n\n  void Backward(float num, float* grad, int64_t global_step) const {\n    if (global_step % config_.step_size() == 0) {\n      scale_ = config_.init_scale() *\n               std::pow(1.f + kGamma * static_cast<float>(global_step), kPower);\n      scale_ = std::min(scale_.load(), config_.max_scale());\n      LOG_EVERY_N_SEC(INFO, 60) << absl::StrFormat(\n          \"HashNetQuantizer: %s, scale = %.6f, global_step = %ld\",\n          config_.ShortDebugString(), scale_, global_step);\n    }\n\n    float y = std::tanh(scale_ * num);\n    *grad *= config_.amplitude() * scale_ * (1.f - y * y);\n  }\n\n  float GetScale() const { return scale_; }\n\n  const FloatCompressorConfig_OneBit& GetConfig() const { return config_; }\n\n private:\n  static constexpr float kGamma = 0.005;\n  static constexpr float kPower = 0.5;\n\n  mutable std::atomic<float> scale_;\n  FloatCompressorConfig_OneBit config_;\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_COMPRESSOR_HASH_NET_QUANTIZER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/compressor/hash_net_quantizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/compressor/hash_net_quantizer.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nTEST(HashNetQuantizer, ForwardAndBackward) {\n  FloatCompressorConfig_OneBit config;\n  config.set_step_size(1000);\n  HashNetQuantizer model(config);\n\n  EXPECT_FLOAT_EQ(model.GetScale(), 1.0f);\n  EXPECT_FLOAT_EQ(model.Forward(1.0f), 0.07615941f);\n  EXPECT_FLOAT_EQ(model.Forward(2.0f), 0.09640275f);\n\n  float grad = 1.0f, grad2 = 2.0f;\n  model.Backward(2.0f, &grad, 0);\n  model.Backward(2.0f, &grad2, 999);\n  EXPECT_FLOAT_EQ(grad, 0.00706508f);\n  EXPECT_FLOAT_EQ(grad2, 0.01413016f);\n\n  grad = 100.0f, grad2 = 200.0f;\n  model.Backward(2.0f, &grad, 1000);\n  model.Backward(2.0f, &grad2, 1001);\n  EXPECT_FLOAT_EQ(grad, 0.005442613f);\n  EXPECT_FLOAT_EQ(grad2, 0.01088523f);\n  EXPECT_FLOAT_EQ(model.Forward(2.0f), 0.09998888f);\n  EXPECT_FLOAT_EQ(model.GetScale(), 2.44948974f);\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/cuckoohash/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", \"cc_library\", \"cc_test\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime:__subpackages__\"])\n\ncc_library(\n    name = \"cuckoohash\",\n    hdrs = [\n        \"bucket_container.hpp\",\n        \"cuckoohash_config.hpp\",\n        \"cuckoohash_map.hpp\",\n        \"cuckoohash_util.hpp\",\n    ],\n    visibility = [\n        \"//monolith/feature_engineering/runtime:__subpackages__\",\n        \"//monolith/native_training/runtime:__subpackages__\",\n    ],\n    deps = [\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_interface\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_library(\n    name = \"cuckoo_embedding_hash_table\",\n    srcs = [\"cuckoo_embedding_hash_table.cc\"],\n    hdrs = [\"cuckoo_embedding_hash_table.h\"],\n    deps = [\n        \":cuckoohash\",\n        \"//monolith/native_training/runtime/allocator:block_allocator\",\n        \"//monolith/native_training/runtime/common:linalg_utils\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_cc_proto\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_interface\",\n        \"//monolith/native_training/runtime/hash_table:entry_accessor\",\n        \"//monolith/native_training/runtime/hash_table:entry_defs\",\n        # TODO(zhen.li1): refactor the methods in this experimental library to normal\n        # library.\n        \"//monolith/native_training/data/training_instance:reader_util\",\n    ],\n)\n\ncc_test(\n    name = \"cuckoo_embedding_hash_table_test\",\n    srcs = [\"cuckoo_embedding_hash_table_test.cc\"],\n    deps = [\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_test\",\n    ],\n)\n\ncc_binary(\n    name = \"cuckoo_embedding_hash_table_benchmark\",\n    srcs = [\"cuckoo_embedding_hash_table_benchmark.cc\"],\n    deps = [\n        \"//monolith/native_training/runtime/concurrency:thread_pool\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_cc_proto\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_factory\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_interface\",\n        \"@com_github_google_benchmark//:benchmark\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/cuckoohash/CUCKOO_ORIGINAL_LICENSE",
    "content": "Copyright (C) 2013, Carnegie Mellon University and Intel Corporation\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n---------------------------\n\nThe third-party libraries have their own licenses, as detailed in their source\nfiles."
  },
  {
    "path": "monolith/native_training/runtime/hash_table/cuckoohash/bucket_container.hpp",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef _MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_BUCKET_CONTAINER_HPP\n#define _MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_BUCKET_CONTAINER_HPP\n\n#include <array>\n#include <atomic>\n#include <cassert>\n#include <cstddef>\n#include <iostream>  // NOLINT\n#include <memory>\n#include <type_traits>\n#include <utility>\n\n#include \"monolith/native_training/runtime/hash_table/cuckoohash/cuckoohash_util.hpp\"\n\nnamespace libcuckoo {\n\n/**\n * bucket_container manages storage of key-value pairs for the table.\n * It stores the items inline in uninitialized memory, and keeps track of which\n * slots have live data and which do not. It also stores a partial hash for\n * each live key. It is sized by powers of two.\n *\n * @tparam Key type of keys in the table\n * @tparam T type of values in the table\n * @tparam Allocator type of key-value pair allocator\n * @tparam Partial type of partial keys\n * @tparam SLOT_PER_BUCKET number of slots for each bucket in the table\n */\ntemplate <class Key, class T, class Allocator, class Partial,\n          std::size_t SLOT_PER_BUCKET>\nclass bucket_container {\n public:\n  using key_type = Key;\n  using mapped_type = T;\n  using value_type = std::pair<const Key, T>;\n\n private:\n  using traits_ = typename std::allocator_traits<\n      Allocator>::template rebind_traits<value_type>;\n\n public:\n  using allocator_type = typename traits_::allocator_type;\n  using partial_t = Partial;\n  using size_type = typename traits_::size_type;\n  using reference = value_type &;\n  using const_reference = const value_type &;\n  using pointer = typename traits_::pointer;\n  using const_pointer = typename traits_::const_pointer;\n\n  /*\n   * The bucket type holds SLOT_PER_BUCKET key-value pairs, along with their\n   * partial keys and occupancy info. It uses aligned_storage arrays to store\n   * the keys and values to allow constructing and destroying key-value pairs\n   * in place. The lifetime of bucket data should be managed by the container.\n   * It is the user's responsibility to confirm whether the data they are\n   * accessing is live or not.\n   */\n  class bucket {\n   public:\n    bucket() noexcept : occupied_() {}\n\n    const value_type &kvpair(size_type ind) const {\n      return *static_cast<const value_type *>(\n          static_cast<const void *>(&values_[ind]));\n    }\n    value_type &kvpair(size_type ind) {\n      return *static_cast<value_type *>(static_cast<void *>(&values_[ind]));\n    }\n\n    const key_type &key(size_type ind) const {\n      return storage_kvpair(ind).first;\n    }\n    key_type &&movable_key(size_type ind) {\n      return std::move(storage_kvpair(ind).first);\n    }\n\n    const mapped_type &mapped(size_type ind) const {\n      return storage_kvpair(ind).second;\n    }\n    mapped_type &mapped(size_type ind) { return storage_kvpair(ind).second; }\n\n    partial_t partial(size_type ind) const { return partials_[ind]; }\n    partial_t &partial(size_type ind) { return partials_[ind]; }\n\n    bool occupied(size_type ind) const { return occupied_[ind]; }\n    bool &occupied(size_type ind) { return occupied_[ind]; }\n\n   private:\n    friend class bucket_container;\n\n    using storage_value_type = std::pair<Key, T>;\n\n    const storage_value_type &storage_kvpair(size_type ind) const {\n      return *static_cast<const storage_value_type *>(\n          static_cast<const void *>(&values_[ind]));\n    }\n    storage_value_type &storage_kvpair(size_type ind) {\n      return *static_cast<storage_value_type *>(\n          static_cast<void *>(&values_[ind]));\n    }\n\n    std::array<typename std::aligned_storage<sizeof(storage_value_type),\n                                             alignof(storage_value_type)>::type,\n               SLOT_PER_BUCKET>\n        values_;\n    std::array<partial_t, SLOT_PER_BUCKET> partials_;\n    std::array<bool, SLOT_PER_BUCKET> occupied_;\n  };\n\n  bucket_container(size_type hp, const allocator_type &allocator)\n      : allocator_(allocator),\n        bucket_allocator_(allocator),\n        hashpower_(hp),\n        buckets_(bucket_allocator_.allocate(size())) {\n    // The bucket default constructor is nothrow, so we don't have to\n    // worry about dealing with exceptions when constructing all the\n    // elements.\n    static_assert(std::is_nothrow_constructible<bucket>::value,\n                  \"bucket_container requires bucket to be nothrow \"\n                  \"constructible\");\n    for (size_type i = 0; i < size(); ++i) {\n      traits_::construct(allocator_, &buckets_[i]);\n    }\n  }\n\n  ~bucket_container() noexcept { destroy_buckets(); }\n\n  bucket_container(const bucket_container &bc)\n      : allocator_(\n            traits_::select_on_container_copy_construction(bc.allocator_)),\n        bucket_allocator_(allocator_),\n        hashpower_(bc.hashpower()),\n        buckets_(transfer(bc.hashpower(), bc, std::false_type())) {}\n\n  bucket_container(const bucket_container &bc, const allocator_type &a)\n      : allocator_(a),\n        bucket_allocator_(allocator_),\n        hashpower_(bc.hashpower()),\n        buckets_(transfer(bc.hashpower(), bc, std::false_type())) {}\n\n  bucket_container(bucket_container &&bc)\n      : allocator_(std::move(bc.allocator_)),\n        bucket_allocator_(allocator_),\n        hashpower_(bc.hashpower()),\n        buckets_(std::move(bc.buckets_)) {\n    // De-activate the other buckets container\n    bc.buckets_ = nullptr;\n  }\n\n  bucket_container(bucket_container &&bc, const allocator_type &a)\n      : allocator_(a), bucket_allocator_(allocator_) {\n    move_assign(bc, std::false_type());\n  }\n\n  bucket_container &operator=(const bucket_container &bc) {\n    destroy_buckets();\n    copy_allocator(allocator_, bc.allocator_,\n                   typename traits_::propagate_on_container_copy_assignment());\n    bucket_allocator_ = allocator_;\n    hashpower(bc.hashpower());\n    buckets_ = transfer(bc.hashpower(), bc, std::false_type());\n    return *this;\n  }\n\n  bucket_container &operator=(bucket_container &&bc) {\n    destroy_buckets();\n    move_assign(bc, typename traits_::propagate_on_container_move_assignment());\n    return *this;\n  }\n\n  void swap(bucket_container &bc) noexcept {\n    swap_allocator(allocator_, bc.allocator_,\n                   typename traits_::propagate_on_container_swap());\n    swap_allocator(bucket_allocator_, bc.bucket_allocator_,\n                   typename traits_::propagate_on_container_swap());\n    // Regardless of whether we actually swapped the allocators or not, it will\n    // always be okay to do the remainder of the swap. This is because if the\n    // allocators were swapped, then the subsequent operations are okay. If the\n    // allocators weren't swapped but compare equal, then we're okay. If they\n    // weren't swapped and compare unequal, then behavior is undefined, so\n    // we're okay.\n    size_t bc_hashpower = bc.hashpower();\n    bc.hashpower(hashpower());\n    hashpower(bc_hashpower);\n    std::swap(buckets_, bc.buckets_);\n  }\n\n  size_type hashpower() const {\n    return hashpower_.load(std::memory_order_acquire);\n  }\n\n  void hashpower(size_type val) {\n    hashpower_.store(val, std::memory_order_release);\n  }\n\n  size_type size() const { return size_type(1) << hashpower(); }\n\n  allocator_type get_allocator() const { return allocator_; }\n\n  bucket &operator[](size_type i) { return buckets_[i]; }\n  const bucket &operator[](size_type i) const { return buckets_[i]; }\n\n  // Constructs live data in a bucket\n  template <typename K, typename... Args>\n  void setKV(size_type ind, size_type slot, partial_t p, K &&k,\n             Args &&...args) {\n    bucket &b = buckets_[ind];\n    assert(!b.occupied(slot));\n    b.partial(slot) = p;\n    traits_::construct(allocator_, std::addressof(b.storage_kvpair(slot)),\n                       std::piecewise_construct,\n                       std::forward_as_tuple(std::forward<K>(k)),\n                       std::forward_as_tuple(std::forward<Args>(args)...));\n    // This must occur last, to enforce a strong exception guarantee\n    b.occupied(slot) = true;\n  }\n\n  // Destroys live data in a bucket\n  void eraseKV(size_type ind, size_type slot) {\n    bucket &b = buckets_[ind];\n    assert(b.occupied(slot));\n    b.occupied(slot) = false;\n    traits_::destroy(allocator_, std::addressof(b.storage_kvpair(slot)));\n  }\n\n  // Destroys all the live data in the buckets. Does not deallocate the bucket\n  // memory.\n  void clear() noexcept {\n    static_assert(std::is_nothrow_destructible<key_type>::value &&\n                      std::is_nothrow_destructible<mapped_type>::value,\n                  \"bucket_container requires key and value to be nothrow \"\n                  \"destructible\");\n    for (size_type i = 0; i < size(); ++i) {\n      bucket &b = buckets_[i];\n      for (size_type j = 0; j < SLOT_PER_BUCKET; ++j) {\n        if (b.occupied(j)) {\n          eraseKV(i, j);\n        }\n      }\n    }\n  }\n\n  // Destroys and deallocates all data in the buckets. After this operation,\n  // the bucket container will have no allocated data. It is still valid to\n  // swap, move or copy assign to this container.\n  void clear_and_deallocate() noexcept { destroy_buckets(); }\n\n private:\n  using bucket_traits_ = typename traits_::template rebind_traits<bucket>;\n  using bucket_pointer = typename bucket_traits_::pointer;\n\n  // true here means the allocators from `src` are propagated on libcuckoo_copy\n  template <typename A>\n  void copy_allocator(A &dst, const A &src, std::true_type) {  // NOLINT\n    dst = src;\n  }\n\n  template <typename A>\n  void copy_allocator(A &dst, const A &src, std::false_type) {}  // NOLINT\n\n  // true here means the allocators from `src` are propagated on libcuckoo_swap\n  template <typename A>\n  void swap_allocator(A &dst, A &src, std::true_type) {  // NOLINT\n    std::swap(dst, src);\n  }\n\n  template <typename A>\n  void swap_allocator(A &, A &, std::false_type) {}\n\n  // true here means the bucket allocator should be propagated\n  void move_assign(bucket_container &src, std::true_type) {  // NOLINT\n    allocator_ = std::move(src.allocator_);\n    bucket_allocator_ = allocator_;\n    hashpower(src.hashpower());\n    buckets_ = src.buckets_;\n    src.buckets_ = nullptr;\n  }\n\n  void move_assign(bucket_container &src, std::false_type) {  // NOLINT\n    hashpower(src.hashpower());\n    if (allocator_ == src.allocator_) {\n      buckets_ = src.buckets_;\n      src.buckets_ = nullptr;\n    } else {\n      buckets_ = transfer(src.hashpower(), src, std::true_type());\n    }\n  }\n\n  void destroy_buckets() noexcept {\n    if (buckets_ == nullptr) {\n      return;\n    }\n    // The bucket default constructor is nothrow, so we don't have to\n    // worry about dealing with exceptions when constructing all the\n    // elements.\n    static_assert(std::is_nothrow_destructible<bucket>::value,\n                  \"bucket_container requires bucket to be nothrow \"\n                  \"destructible\");\n    clear();\n    for (size_type i = 0; i < size(); ++i) {\n      traits_::destroy(allocator_, &buckets_[i]);\n    }\n    bucket_allocator_.deallocate(buckets_, size());\n    buckets_ = nullptr;\n  }\n\n  // `true` here refers to whether or not we should move\n  void move_or_copy(size_type dst_ind, size_type dst_slot, bucket &src,  // NOLINT\n                    size_type src_slot, std::true_type) {\n    setKV(dst_ind, dst_slot, src.partial(src_slot), src.movable_key(src_slot),\n          std::move(src.mapped(src_slot)));\n  }\n\n  void move_or_copy(size_type dst_ind, size_type dst_slot, bucket &src,  // NOLINT\n                    size_type src_slot, std::false_type) {\n    setKV(dst_ind, dst_slot, src.partial(src_slot), src.key(src_slot),\n          src.mapped(src_slot));\n  }\n\n  template <bool B>\n  bucket_pointer transfer(\n      size_type dst_hp,\n      typename std::conditional<B, bucket_container &,\n                                const bucket_container &>::type src,\n      std::integral_constant<bool, B> move) {\n    assert(dst_hp >= src.hashpower());\n    bucket_container dst(dst_hp, get_allocator());\n    // Move/copy all occupied slots of the source buckets\n    for (size_t i = 0; i < src.size(); ++i) {\n      for (size_t j = 0; j < SLOT_PER_BUCKET; ++j) {\n        if (src.buckets_[i].occupied(j)) {\n          dst.move_or_copy(i, j, src.buckets_[i], j, move);\n        }\n      }\n    }\n    // Take away the pointer from `dst` and return it\n    bucket_pointer dst_pointer = dst.buckets_;\n    dst.buckets_ = nullptr;\n    return dst_pointer;\n  }\n\n  // This allocator matches the value_type, but is not used to construct\n  // storage_value_type pairs, or allocate buckets\n  allocator_type allocator_;\n  // This allocator is used for actually allocating buckets. It is simply\n  // copy-constructed from `allocator_`, and will always be copied whenever\n  // allocator_ is copied.\n  typename traits_::template rebind_alloc<bucket> bucket_allocator_;\n  // This needs to be atomic, since it can be read and written by multiple\n  // threads not necessarily synchronized by a lock.\n  std::atomic<size_type> hashpower_;\n  // These buckets are protected by striped locks (external to the\n  // BucketContainer), which must be obtained before accessing a bucket.\n  bucket_pointer buckets_;\n\n  // If the key and value are Trivial, the bucket be serilizable. Since we\n  // already disallow user-specialized instances of std::pair, we know that the\n  // default implementation of std::pair uses a default copy constructor, so\n  // this should be okay. We could in theory just check if the type is\n  // TriviallyCopyable but this check is not available on some compilers we\n  // want to support.\n  template <typename ThisKey, typename ThisT>\n  friend typename std::enable_if<std::is_trivial<ThisKey>::value &&\n                                     std::is_trivial<ThisT>::value,\n                                 std::ostream &>::type\n  operator<<(std::ostream &os,\n             const bucket_container<ThisKey, ThisT, Allocator, Partial,\n                                    SLOT_PER_BUCKET> &bc) {\n    size_type hp = bc.hashpower();\n    os.write(reinterpret_cast<const char *>(&hp), sizeof(size_type));\n    os.write(reinterpret_cast<const char *>(bc.buckets_),\n             sizeof(bucket) * bc.size());\n    return os;\n  }\n\n  template <typename ThisKey, typename ThisT>\n  friend typename std::enable_if<std::is_trivial<ThisKey>::value &&\n                                     std::is_trivial<ThisT>::value,\n                                 std::istream &>::type\n  operator>>(std::istream &is, bucket_container<ThisKey, ThisT, Allocator,\n                                                Partial, SLOT_PER_BUCKET> &bc) {\n    size_type hp;\n    is.read(reinterpret_cast<char *>(&hp), sizeof(size_type));\n    bucket_container new_bc(hp, bc.get_allocator());\n    is.read(reinterpret_cast<char *>(new_bc.buckets_),\n            new_bc.size() * sizeof(bucket));\n    bc.swap(new_bc);\n    return is;\n  }\n};\n\n}  // namespace libcuckoo\n\n#endif  // _MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_BUCKET_CONTAINER_HPP\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/cuckoohash/cuckoo_embedding_hash_table.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/cuckoohash/cuckoo_embedding_hash_table.h\"\n\n#include <cstdlib>\n#include <utility>\n\n#include \"absl/strings/str_format.h\"\n#include \"absl/synchronization/mutex.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"monolith/native_training/runtime/allocator/block_allocator.h\"\n#include \"monolith/native_training/runtime/common/linalg_utils.h\"\n#include \"monolith/native_training/runtime/hash_table/cuckoohash/cuckoohash_map.hpp\"\n#include \"monolith/native_training/runtime/hash_table/entry_defs.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing allocator::EntryAddress;\nusing allocator::TSEmbeddingBlockAllocator;\nusing common::IsAlmostEqual;\nusing common::L2NormSquare;\n\nconst int64_t kSecPerDay = 24 * 60 * 60;\n\n// A helper that wraps the object with a init_fn.\ntemplate <class T>\nclass WithInitFn : public T {\n public:\n  template <typename... Args>\n  explicit WithInitFn(const std::function<void(T&)>& init_fn, Args&&... args)\n      : T(std::forward<Args>(args)...) {\n    init_fn(*this);\n  }\n};\n\ntemplate <class TVal>\nclass EntryHelper {};\n\ntemplate <>\nclass EntryHelper<PackedEntry> {\n public:\n  EntryHelper(size_t entry_size)\n      : entry_size_(entry_size), alloc_(entry_size) {}\n\n  template <typename Map, typename... Args>\n  bool Upsert(Map* m, Args&&... args) {\n    return m->upsert(std::forward<Args>(args)..., &alloc_);\n  }\n\n  void* Get(const PackedEntry& entry) const {\n    return alloc_.GetEntryPointer(entry.get_entry_addr());\n  }\n\n  void DeallocateAll() { alloc_.DeallocateAll(); }\n\n private:\n  size_t entry_size_;\n  allocator::TSEmbeddingBlockAllocator alloc_;\n};\n\ntemplate <>\nclass EntryHelper<RawEntry> {\n public:\n  EntryHelper(size_t entry_size) : entry_size_(entry_size) {}\n\n  template <typename Map, typename... Args>\n  bool Upsert(Map* m, Args&&... args) {\n    return m->upsert(std::forward<Args>(args)..., entry_size_);\n  }\n\n  void* Get(const RawEntry& entry) const { return entry.get(); }\n\n  void DeallocateAll() {}\n\n private:\n  size_t entry_size_;\n};\n\ntemplate <int64_t length>\nclass EntryHelper<InlineEntry<length>> {\n public:\n  template <typename Map, typename... Args>\n  bool Upsert(Map* m, Args&&... args) {\n    return m->upsert(std::forward<Args>(args)...);\n  }\n\n  const void* Get(const InlineEntry<length>& entry) const {\n    return entry.get();\n  }\n  void* Get(InlineEntry<length>& entry) { return entry.get(); }\n\n  void DeallocateAll() {}\n};\n\nstruct Params {\n  CuckooEmbeddingHashTableConfig config;\n  std::unique_ptr<EntryAccessorInterface> accessor;\n  uint64_t initial_capacity;\n  SlotExpireTimeConfig slot_expire_time_config;\n  bool skip_zero_embedding = false;\n};\n\ntemplate <class EntryType>\nclass CuckooEmbeddingHashTable : public EmbeddingHashTableInterface {\n public:\n  using MapType = libcuckoo::cuckoohash_map<int64_t, WithInitFn<EntryType>>;\n  explicit CuckooEmbeddingHashTable(Params p,\n                                    EntryHelper<EntryType> entry_helper)\n      : config_(std::move(p.config)),\n        accessor_(std::move(p.accessor)),\n        entry_helper_(std::move(entry_helper)),\n        default_expire_time_(p.slot_expire_time_config.default_expire_time()),\n        skip_zero_embedding_(p.skip_zero_embedding),\n        m_(p.initial_capacity) {\n    slot_to_expire_time_ =\n        std::make_unique<absl::flat_hash_map<int64_t, int>>();\n    for (const auto& slot_expire_time :\n         p.slot_expire_time_config.slot_expire_times()) {\n      (*slot_to_expire_time_)[slot_expire_time.slot()] =\n          slot_expire_time.expire_time();\n    }\n    LOG_FIRST_N(INFO, 1) << \"skip_zero_embedding: \" << skip_zero_embedding_;\n  }\n\n  // Returns the corresponding entry for |ids|.\n  int64_t BatchLookup(absl::Span<int64_t> ids,\n                      absl::Span<absl::Span<float>> embeddings) const override {\n    int64_t found = 0;\n    for (unsigned int index = 0; index < ids.size(); ++index) {\n      int64_t id = ids[index];\n      found += Lookup(id, embeddings[index]);\n    }\n\n    return found;\n  }\n\n  // Handles the corresponding entry for |ids|.\n  void BatchLookupEntry(absl::Span<int64_t> ids,\n                        absl::Span<EntryDump> entries) const override {\n    for (unsigned int index = 0; index < ids.size(); ++index) {\n      int64_t id = ids[index];\n      LookupEntry(id, entries.subspan(index, index + 1));\n    }\n  }\n\n  // Returns the corresponding entry for |id|.\n  int64_t Lookup(int64_t id, absl::Span<float> embedding) const override {\n    auto find_fn = [&](EntryType& entry) {\n      accessor_->Fill(entry_helper_.Get(entry), embedding);\n    };\n    if (m_.find_fn(id, find_fn)) {\n      return 1;\n    }\n    // By default, returns all zero.\n    std::memset(embedding.data(), 0, sizeof(float) * embedding.size());\n    return 0;\n  }\n\n  // Handles the corresponding entry for |id|.\n  void LookupEntry(int64_t id, absl::Span<EntryDump> entry) const override {\n    auto find_fn = [&](EntryType& raw_entry) {\n      entry[0] = std::move(accessor_->Save(entry_helper_.Get(raw_entry),\n                                           raw_entry.GetTimestamp()));\n    };\n    if (m_.find_fn(id, find_fn)) {\n      return;\n    }\n  }\n\n  // Update the hash table entry directly.\n  void Assign(absl::Span<const int64_t> ids,\n              absl::Span<const absl::Span<const float>> updates,\n              int64_t update_time) override {\n    for (size_t i = 0; i < ids.size(); ++i) {\n      int64_t id = ids[i];\n      auto update = updates[i];\n\n      if (skip_zero_embedding_ &&\n          IsAlmostEqual(L2NormSquare(update.data(), update.size()), 0.f)) {\n        m_.erase(id);\n        LOG_EVERY_N(INFO, 10000)\n            << \"Assign erase \" << google::COUNTER << \" zero embeddings.\";\n      } else {\n        UpsertEntry(id, [&](EntryType& entry) {\n          entry.SetTimestamp(update_time);\n          accessor_->Assign(update, entry_helper_.Get(entry));\n        });\n      }\n    }\n  }\n\n  // Update the hash table entry directly.\n  void AssignAdd(int64_t id, absl::Span<const float> update,\n                 int64_t update_time) override {\n    UpsertEntry(id, [&](EntryType& entry) {\n      entry.SetTimestamp(update_time);\n      accessor_->AssignAdd(update, entry_helper_.Get(entry));\n    });\n  }\n\n  void Reinitialize(absl::Span<const int64_t> ids,\n                    absl::Span<int> status) override {\n    int64_t update_time = absl::ToUnixSeconds(absl::Now());\n    for (size_t i = 0; i < ids.size(); ++i) {\n      int64_t id = ids[i];\n      bool existed = !UpsertEntry(id, [&](EntryType& entry) {\n        entry.SetTimestamp(update_time);\n        accessor_->Init(entry_helper_.Get(entry));\n      });\n      status[i] = existed;\n    }\n  }\n\n  // Update the hash table based on optimizer.\n  void BatchOptimize(absl::Span<int64_t> ids,\n                     absl::Span<absl::Span<const float>> grads,\n                     absl::Span<const float> learning_rates,\n                     int64_t update_time, const int64_t global_step) override {\n    for (size_t i = 0; i < ids.size(); ++i) {\n      Optimize(ids[i], grads[i], learning_rates, update_time, global_step);\n    }\n  }\n\n  // Update the hash table based on optimizer.\n  void Optimize(int64_t id, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates, int64_t update_time,\n                const int64_t global_step) override {\n    UpsertEntry(id, [&](EntryType& entry) {\n      entry.SetTimestamp(update_time);\n      accessor_->Optimize(entry_helper_.Get(entry), grad, learning_rates,\n                          global_step);\n    });\n  }\n\n  // Evict the outdated hash table values based on the expire time and last\n  // updated time.\n  virtual void Evict(int64_t max_update_time) {\n    auto should_be_evict_fn = [this, max_update_time](const int64_t& key,\n                                                      const EntryType& entry) {\n      const int64_t timestamp = entry.GetTimestamp();\n      int expire_time = default_expire_time_;\n      // TODO(zhen.li1): evict assumes the fid is v2 version.\n      auto expire_time_iter = slot_to_expire_time_->find(slot_id_v2(key));\n      if (expire_time_iter != slot_to_expire_time_->end()) {\n        expire_time = expire_time_iter->second;\n      }\n      return max_update_time - timestamp >= expire_time * kSecPerDay;\n    };\n    m_.evict(should_be_evict_fn);\n  }\n\n  // Check if a given id exists in the hashtable\n  bool Contains(const int64_t id) { return m_.contains(id); }\n\n  class CuckooLockCtx : public LockCtx {\n   public:\n    explicit CuckooLockCtx(typename MapType::locked_table table)\n        : table_(std::move(table)) {}\n    ~CuckooLockCtx() override = default;\n\n   private:\n    typename MapType::locked_table table_;\n  };\n\n  std::unique_ptr<LockCtx> LockAll() override {\n    return std::make_unique<CuckooLockCtx>(m_.lock_table());\n  }\n\n  // Saves the data. The implementation should guarantee that different shard\n  // can be dumped in the parallel.\n  void Save(DumpShard shard, WriteFn write_fn,\n            DumpIterator* iter) const override {\n    auto dump_fn = [&](const int64_t& key, const EntryType& entry) {\n      EntryDump dump =\n          accessor_->Save(entry_helper_.Get(entry), entry.GetTimestamp());\n      dump.set_id(key);\n      return write_fn(std::move(dump));\n    };\n    m_.partial_dump(shard, dump_fn, iter);\n  }\n\n  // Restores the data from get_fn. The implementation should guarantee that\n  // different shard can be dumped in the parallel.\n  // |get_fn| returns false if it is end of stream.\n  int64_t Restore(DumpShard shard,\n                  std::function<bool(EntryDump*, int64_t*)> get_fn) override {\n    EntryDump dump;\n    int64_t max_update_ts = 0;\n    while (get_fn(&dump, &max_update_ts)) {\n      if (skip_zero_embedding_ &&\n          IsAlmostEqual(L2NormSquare(dump.num().data(), dump.num_size()),\n                        0.f)) {\n        LOG_EVERY_N(INFO, 1000000)\n            << \"Restore skip \" << google::COUNTER << \" zero embeddings.\";\n        continue;\n      }\n\n      UpsertEntry(dump.id(), [&](EntryType& entry) {\n        uint32_t timestamp_sec = 0;\n        accessor_->Restore(entry_helper_.Get(entry), &timestamp_sec,\n                           std::move(dump));\n        entry.SetTimestamp(timestamp_sec);\n      });\n    }\n    return max_update_ts;\n  }\n\n  // Clears data of hash table.\n  void Clear() override {\n    auto fn = [this]() { entry_helper_.DeallocateAll(); };\n    m_.clear_with_callback(fn);\n  }\n\n  int64_t Size() const override { return m_.size(); }\n\n  int DimSize() const override { return accessor_->DimSize(); }\n\n  int SliceSize() const override { return accessor_->SliceSize(); }\n\n  bool Contains(int64_t id) const override { return m_.contains(id); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\n        R\"({\"accessor\": %s, \"size\": %ld, \"memory\": %ld, \"memory_if_not_compressed\": %ld, \"load_factor\": %f})\",\n        accessor_->DebugString(), Size(),\n        Size() * (accessor_->SizeBytes() + sizeof(int64_t)),\n        Size() * (accessor_->UncompressedSizeBytes() + sizeof(int64_t)),\n        m_.load_factor());\n  }\n\n private:\n  bool UpsertEntry(int64_t id,\n                   const std::function<void(EntryType&)>& upsert_fn) {\n    auto init_fn = [&](EntryType& entry) {\n      accessor_->Init(entry_helper_.Get(entry));\n      upsert_fn(entry);\n    };\n    return entry_helper_.Upsert(&m_, id, upsert_fn, init_fn);\n  }\n\n  CuckooEmbeddingHashTableConfig config_;\n  std::unique_ptr<EntryAccessorInterface> accessor_;\n  EntryHelper<EntryType> entry_helper_;\n  std::unique_ptr<absl::flat_hash_map<int64_t, int>> slot_to_expire_time_;\n  int64_t default_expire_time_;\n  bool skip_zero_embedding_;\n  MapType m_;\n};\n\ntemplate <int64_t length>\nstd::unique_ptr<EmbeddingHashTableInterface> CreateInlineEntryTable(\n    Params p, int64_t size_bytes) {\n  if (size_bytes > InlineEntry<length>::capacity()) {\n    std::abort();\n  }\n  return std::make_unique<CuckooEmbeddingHashTable<InlineEntry<length>>>(\n      std::move(p), EntryHelper<InlineEntry<length>>());\n}\n\n}  // namespace\n\nstd::unique_ptr<EmbeddingHashTableInterface> NewCuckooEmbeddingHashTable(\n    CuckooEmbeddingHashTableConfig config,\n    std::unique_ptr<EntryAccessorInterface> accessor,\n    EmbeddingHashTableConfig::EntryType type, uint64_t initial_capacity,\n    const SlotExpireTimeConfig& slot_expire_time_config,\n    bool skip_zero_embedding) {\n  const int64_t size_bytes = accessor->SizeBytes();\n  Params p = {std::move(config), std::move(accessor), initial_capacity,\n              slot_expire_time_config, skip_zero_embedding};\n  if (type == EmbeddingHashTableConfig::PACKED) {\n    EntryHelper<PackedEntry> helper(size_bytes);\n    return std::make_unique<CuckooEmbeddingHashTable<PackedEntry>>(\n        std::move(p), std::move(helper));\n  } else if (type == EmbeddingHashTableConfig::RAW) {\n    if (size_bytes <= 12) {\n      return CreateInlineEntryTable<16>(std::move(p), size_bytes);\n    } else if (size_bytes <= 20) {\n      return CreateInlineEntryTable<24>(std::move(p), size_bytes);\n    } else if (size_bytes <= 28) {\n      return CreateInlineEntryTable<32>(std::move(p), size_bytes);\n    } else {\n      EntryHelper<RawEntry> helper(size_bytes);\n      return std::make_unique<CuckooEmbeddingHashTable<RawEntry>>(\n          std::move(p), std::move(helper));\n    }\n  }\n  // Should not reach here.\n  throw std::invalid_argument(\n      absl::StrFormat(\"Unknown entry type table. %d\", type));\n  return nullptr;\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/cuckoohash/cuckoo_embedding_hash_table.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOO_EMBEDDING_HASH_TABLE\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOO_EMBEDDING_HASH_TABLE\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_interface.h\"\n#include \"monolith/native_training/runtime/hash_table/entry_accessor.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<EmbeddingHashTableInterface> NewCuckooEmbeddingHashTable(\n    CuckooEmbeddingHashTableConfig config,\n    std::unique_ptr<EntryAccessorInterface> accessor,\n    EmbeddingHashTableConfig::EntryType type, uint64_t initial_capacity,\n    const SlotExpireTimeConfig& slot_expire_time_config,\n    bool skip_zero_embedding);\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOO_EMBEDDING_HASH_TABLE\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/cuckoohash/cuckoo_embedding_hash_table_benchmark.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <atomic>\n#include \"absl/random/random.h\"\n#include \"absl/strings/str_format.h\"\n#include \"benchmark/benchmark.h\"\n#include \"glog/logging.h\"\n#include \"google/protobuf/text_format.h\"\n#include \"monolith/native_training/runtime/concurrency/thread_pool.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_factory.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nnamespace proto2 = ::google::protobuf;\n\nconstexpr int64_t kMaxId = 1 << 15;\n\nstd::unique_ptr<EmbeddingHashTableInterface> SetupHashTable() {\n  EmbeddingHashTableConfig config;\n  CHECK(proto2::TextFormat::ParseFromString(R\"(\n    entry_config {\n      segments {\n        dim_size: 1\n        init_config { zeros {} }\n        opt_config { ftrl {} }\n      }\n      segments {\n        dim_size: 32\n        init_config { zeros {} }\n        opt_config { sgd {} }\n      }\n    }\n    cuckoo {}\n  )\",\n                                            &config));\n  auto table = NewEmbeddingHashTableFromConfig(config);\n  for (int64_t i = 0; i < kMaxId; ++i) {\n    table->AssignAdd(i, std::vector<float>(33, 0.0f), 0);\n  }\n  return table;\n}\n\nstd::vector<int64_t> SetupPickedIds(int num) {\n  absl::BitGen bitgen;\n  std::vector<int64_t> ids(num);\n  for (int i = 0; i < num; ++i) {\n    ids[i] = absl::Uniform(bitgen, 0u, kMaxId);\n  }\n  return ids;\n}\n\nstd::vector<int64_t> ids = SetupPickedIds(1000 * 256);  // NOLINT\nauto table = SetupHashTable();                          // NOLINT\n\nvoid BM_LookUp(benchmark::State& state) {  // NOLINT\n  int64_t thread_num = state.range(0);\n  monolith::concurrency::ThreadPool thread_pool(thread_num);\n\n  for (auto _ : state) {\n    std::atomic_int join(thread_num);\n    auto optimize = [&]() {\n      // OPTIMIZE: remove memory allocation overhead\n      std::vector<float> embeddings(33, 0);\n      for (int64_t id : ids) {\n        table->Lookup(id, absl::MakeSpan(embeddings));\n      }\n      --join;\n    };\n\n    // Simulate multi-workers lookup simultaneously\n    for (int64_t i = 0; i < thread_num; ++i) {\n      thread_pool.Schedule(optimize);\n    }\n    while (join) {\n    }\n  }\n}\n\nvoid BM_BatchLookUp(benchmark::State& state) {  // NOLINT\n  int64_t thread_num = state.range(0);\n  monolith::concurrency::ThreadPool thread_pool(thread_num);\n\n  for (auto _ : state) {\n    std::atomic_int join(thread_num);\n    auto optimize = [&]() {\n      // OPTIMIZE: remove memory allocation overhead\n      std::vector<float> data(ids.size() * 33);\n      std::vector<absl::Span<float>> embeddings;\n      embeddings.reserve(ids.size());\n      for (size_t i = 0; i < ids.size(); ++i) {\n        embeddings.push_back(absl::MakeSpan(data.data() + i * 33, 33));\n      }\n      table->BatchLookup(absl::MakeSpan(ids), absl::MakeSpan(embeddings));\n      --join;\n    };\n\n    // Simulate multi-workers lookup simultaneously\n    for (int64_t i = 0; i < thread_num; ++i) {\n      thread_pool.Schedule(optimize);\n    }\n    while (join) {\n    }\n  }\n}\n\nvoid BM_Optimize(benchmark::State& state) {  // NOLINT\n  int64_t thread_num = state.range(0);\n  monolith::concurrency::ThreadPool thread_pool(thread_num);\n  std::vector<float> grad(33, 1.f);\n\n  for (auto _ : state) {\n    std::atomic_int join(thread_num);\n    auto optimize = [&]() {\n      for (int64_t id : ids) {\n        table->Optimize(id, absl::MakeSpan(grad), {0.01f, 0.01f}, 0);\n      }\n      --join;\n    };\n\n    // Simulate multi-workers optimize simultaneously\n    for (int64_t i = 0; i < thread_num; ++i) {\n      thread_pool.Schedule(optimize);\n    }\n    while (join) {\n    }\n  }\n}\n\nvoid BM_BatchOptimize(benchmark::State& state) {  // NOLINT\n  int64_t thread_num = state.range(0);\n  monolith::concurrency::ThreadPool thread_pool(thread_num);\n  std::vector<float> data(ids.size() * 33, 1.f);\n  std::vector<absl::Span<const float>> grads;\n  grads.reserve(ids.size());\n  for (size_t i = 0; i < ids.size(); ++i) {\n    grads.emplace_back(absl::MakeSpan(data.data() + i * 33, 33));\n  }\n\n  for (auto _ : state) {\n    std::atomic_int join(thread_num);\n    auto optimize = [&]() {\n      table->BatchOptimize(absl::MakeSpan(ids), absl::MakeSpan(grads), {0.01f, 0.01f}, 0);\n      --join;\n    };\n\n    // Simulate multi-workers optimize simultaneously\n    for (int64_t i = 0; i < thread_num; ++i) {\n      thread_pool.Schedule(optimize);\n    }\n    while (join) {\n    }\n  }\n}\n\nBENCHMARK(BM_LookUp)->Arg(1)->Arg(10);\nBENCHMARK(BM_BatchLookUp)->Arg(1)->Arg(10);\n\nBENCHMARK(BM_Optimize)->Arg(1)->Arg(10);\nBENCHMARK(BM_BatchOptimize)->Arg(1)->Arg(10);\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n\nBENCHMARK_MAIN();\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/cuckoohash/cuckoo_embedding_hash_table_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"gmock/gmock.h\"\n#include \"google/protobuf/text_format.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_test.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nnamespace proto2 = google::protobuf;\nusing ::testing::ElementsAre;\n\nstd::tuple<EmbeddingHashTableConfig, std::vector<float>>\nGetTestOneDimSgdHashTable(\n    EmbeddingHashTableConfig::EntryType type = EmbeddingHashTableConfig::PACKED,\n    bool skip_zero_embedding = false) {\n  EmbeddingHashTableConfig config;\n  if (skip_zero_embedding) {\n    EXPECT_TRUE(proto2::TextFormat::ParseFromString(R\"(\n    entry_config {\n      segments {\n        dim_size: 1\n        comp_config { fp32 {} }\n      }\n      entry_type: SERVING\n    }\n    initial_capacity: 1\n    cuckoo {}\n    skip_zero_embedding: true\n  )\",\n                                                    &config));\n  } else {\n    EXPECT_TRUE(proto2::TextFormat::ParseFromString(R\"(\n    entry_config {\n      segments {\n        dim_size: 1\n        init_config { zeros {} }\n        opt_config { sgd {} }\n      }\n    }\n    initial_capacity: 1\n    cuckoo {}\n  )\",\n                                                    &config));\n  }\n\n  config.set_entry_type(type);\n  std::vector<float> learning_rates(1, 0.01f);\n  return std::make_tuple(config, learning_rates);\n}\n\nINSTANTIATE_TEST_CASE_P(\n    CuckooHashmapReadWrite, ReadWriteEmbeddingHashTableTest,\n    ::testing::Values(\n        GetTestOneDimSgdHashTable(EmbeddingHashTableConfig::PACKED),\n        GetTestOneDimSgdHashTable(EmbeddingHashTableConfig::RAW)));\n\nINSTANTIATE_TEST_CASE_P(CuckooHashmapRestore, SaveRestoreEmbeddingHashTestTest,\n                        ::testing::Values(GetTestOneDimSgdHashTable()));\n\nINSTANTIATE_TEST_CASE_P(OneTimeEvict, EmbeddingHashTableEvictTest,\n                        ::testing::Values(GetTestOneDimSgdHashTable()));\n\nINSTANTIATE_TEST_CASE_P(EvictWhileRehash, EmbeddingHashTableEvictTest,\n                        ::testing::Values(GetTestOneDimSgdHashTable()));\n\nINSTANTIATE_TEST_CASE_P(SkipZeroEmbedding,\n                        EmbeddingHashTableSkipZeroEmbeddingTest,\n                        ::testing::Values(GetTestOneDimSgdHashTable(\n                            EmbeddingHashTableConfig::PACKED, true)));\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/cuckoohash/cuckoohash_config.hpp",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/** \\file */\n\n#ifndef _MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_CUCKOOHASH_CONFIG_HPP\n#define _MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_CUCKOOHASH_CONFIG_HPP\n\n#include <cstddef>\n#include <limits>\n\nnamespace libcuckoo {\n\n// The default maximum number of keys per bucket\nconstexpr size_t DEFAULT_SLOT_PER_BUCKET = 4;\n\n// The default number of elements in an empty hash table\nconstexpr size_t DEFAULT_SIZE = (1U << 16) * DEFAULT_SLOT_PER_BUCKET;\n\n// The default minimum load factor that the table allows for automatic\n// expansion. It must be a number between 0.0 and 1.0. The table will throw\n// load_factor_too_low if the load factor falls below this value\n// during an automatic expansion.\nconstexpr double DEFAULT_MINIMUM_LOAD_FACTOR = 0.05;\n\n// An alias for the value that sets no limit on the maximum hashpower. If this\n// value is set as the maximum hashpower limit, there will be no limit. This\n// is also the default initial value for the maximum hashpower in a table.\nconstexpr size_t NO_MAXIMUM_HASHPOWER = std::numeric_limits<size_t>::max();\n\n// set LIBCUCKOO_DEBUG to 1 to enable debug output\n#define LIBCUCKOO_DEBUG 0\n\n}  // namespace libcuckoo\n\n#endif  // _MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_CUCKOOHASH_CONFIG_HPP\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/cuckoohash/cuckoohash_map.hpp",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/** This cuckoo hash implementation is adapted from\n * https://github.com/efficient/libcuckoo.git */\n\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_CUCKOOHASH_MAP_HPP_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_CUCKOOHASH_MAP_HPP_\n\n#include <algorithm>\n#include <array>\n#include <atomic>\n#include <bitset>\n#include <cassert>\n#include <cstdint>\n#include <cstdlib>\n#include <functional>\n#include <iostream>  // NOLINT\n#include <iterator>\n#include <limits>\n#include <list>\n#include <memory>\n#include <mutex>\n#include <stdexcept>\n#include <string>\n#include <thread>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"absl/container/internal/hash_function_defaults.h\"  // IWYU pragma: export\n#include \"glog/logging.h\"\n\n#include \"monolith/native_training/runtime/hash_table/cuckoohash/bucket_container.hpp\"\n#include \"monolith/native_training/runtime/hash_table/cuckoohash/cuckoohash_config.hpp\"\n#include \"monolith/native_training/runtime/hash_table/cuckoohash/cuckoohash_util.hpp\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_interface.h\"\n\nnamespace libcuckoo {\n\n/**\n * A concurrent hash table\n *\n * @tparam Key type of keys in the table\n * @tparam T type of values in the table\n * @tparam Hash type of hash functor\n * @tparam KeyEqual type of equality comparison functor\n * @tparam Allocator type of allocator. We suggest using an aligned allocator,\n * because the table relies on types that are over-aligned to optimize\n * concurrent cache usage.\n * @tparam SLOT_PER_BUCKET number of slots for each bucket in the table\n */\n// We pick absl map over std::hash since the hash function is more evenly\n// distributed.\ntemplate <class Key, class T,\n          class Hash = absl::container_internal::hash_default_hash<Key>,\n          class KeyEqual = absl::container_internal::hash_default_eq<Key>,\n          class Allocator = std::allocator<std::pair<const Key, T>>,\n          std::size_t SLOT_PER_BUCKET = DEFAULT_SLOT_PER_BUCKET>\nclass cuckoohash_map {\n private:\n  // Type of the partial key\n  using partial_t = uint8_t;\n\n  // The type of the buckets container\n  using buckets_t =\n      bucket_container<Key, T, Allocator, partial_t, SLOT_PER_BUCKET>;\n\n public:\n  /** @name Type Declarations */\n  /**@{*/\n\n  using key_type = typename buckets_t::key_type;\n  using mapped_type = typename buckets_t::mapped_type;\n  /**\n   * This type is defined as an @c std::pair. Note that table behavior is\n   * undefined if a user-defined specialization of @c std::pair<Key, T> or @c\n   * std::pair<const Key, T> exists.\n   */\n  using value_type = typename buckets_t::value_type;\n  using size_type = typename buckets_t::size_type;\n  using difference_type = std::ptrdiff_t;\n  using hasher = Hash;\n  using key_equal = KeyEqual;\n  using allocator_type = typename buckets_t::allocator_type;\n  using reference = typename buckets_t::reference;\n  using const_reference = typename buckets_t::const_reference;\n  using pointer = typename buckets_t::pointer;\n  using const_pointer = typename buckets_t::const_pointer;\n  class locked_table;\n\n  /**@}*/\n\n  /** @name Table Parameters */\n  /**@{*/\n\n  /**\n   * The number of slots per hash bucket\n   */\n  static constexpr uint16_t slot_per_bucket() { return SLOT_PER_BUCKET; }\n\n  /**@}*/\n\n  /** @name Constructors, Destructors, and Assignment */\n  /**@{*/\n\n  /**\n   * Creates a new cuckohash_map instance\n   *\n   * @param n the number of elements to reserve space for initially\n   * @param hf hash function instance to use\n   * @param equal equality function instance to use\n   * @param alloc allocator instance to use\n   */\n  cuckoohash_map(size_type n = DEFAULT_SIZE, const Hash &hf = Hash(),\n                 const KeyEqual &equal = KeyEqual(),\n                 const Allocator &alloc = Allocator())\n      : hash_fn_(hf),\n        eq_fn_(equal),\n        buckets_(reserve_calc(n), alloc),\n        old_buckets_(0, alloc),\n        all_locks_(get_allocator()),\n        num_remaining_lazy_rehash_locks_(0),\n        minimum_load_factor_(DEFAULT_MINIMUM_LOAD_FACTOR),\n        maximum_hashpower_(NO_MAXIMUM_HASHPOWER),\n        max_num_worker_threads_(0) {\n    all_locks_.emplace_back(std::min(bucket_count(), size_type(kMaxNumLocks)),\n                            spinlock(), get_allocator());\n  }\n\n  /**\n   * Constructs the map with the contents of the range @c [first, last].  If\n   * multiple elements in the range have equivalent keys, it is unspecified\n   * which element is inserted.\n   *\n   * @param first the beginning of the range to copy from\n   * @param last the end of the range to copy from\n   * @param n the number of elements to reserve space for initially\n   * @param hf hash function instance to use\n   * @param equal equality function instance to use\n   * @param alloc allocator instance to use\n   */\n  template <typename InputIt>\n  cuckoohash_map(InputIt first, InputIt last, size_type n = DEFAULT_SIZE,\n                 const Hash &hf = Hash(), const KeyEqual &equal = KeyEqual(),\n                 const Allocator &alloc = Allocator())\n      : cuckoohash_map(n, hf, equal, alloc) {\n    for (; first != last; ++first) {\n      insert(first->first, first->second);\n    }\n  }\n\n  /**\n   * Copy constructor. If @p other is being modified concurrently, behavior is\n   * unspecified.\n   *\n   * @param other the map being copied\n   */\n  cuckoohash_map(const cuckoohash_map &other) = default;\n\n  /**\n   * Copy constructor with separate allocator. If @p other is being modified\n   * concurrently, behavior is unspecified.\n   *\n   * @param other the map being copied\n   * @param alloc the allocator instance to use with the map\n   */\n  cuckoohash_map(const cuckoohash_map &other, const Allocator &alloc)\n      : hash_fn_(other.hash_fn_),\n        eq_fn_(other.eq_fn_),\n        buckets_(other.buckets_, alloc),\n        old_buckets_(other.old_buckets_, alloc),\n        all_locks_(alloc),\n        num_remaining_lazy_rehash_locks_(\n            other.num_remaining_lazy_rehash_locks_),\n        minimum_load_factor_(other.minimum_load_factor_),\n        maximum_hashpower_(other.maximum_hashpower_),\n        max_num_worker_threads_(other.max_num_worker_threads_) {\n    if (other.get_allocator() == alloc) {\n      all_locks_ = other.all_locks_;\n    } else {\n      add_locks_from_other(other);\n    }\n  }\n\n  /**\n   * Move constructor. If @p other is being modified concurrently, behavior is\n   * unspecified.\n   *\n   * @param other the map being moved\n   */\n  cuckoohash_map(cuckoohash_map &&other) = default;\n\n  /**\n   * Move constructor with separate allocator. If the map being moved is being\n   * modified concurrently, behavior is unspecified.\n   *\n   * @param other the map being moved\n   * @param alloc the allocator instance to use with the map\n   */\n  cuckoohash_map(cuckoohash_map &&other, const Allocator &alloc)\n      : hash_fn_(std::move(other.hash_fn_)),\n        eq_fn_(std::move(other.eq_fn_)),\n        buckets_(std::move(other.buckets_), alloc),\n        old_buckets_(std::move(other.old_buckets_), alloc),\n        all_locks_(alloc),\n        num_remaining_lazy_rehash_locks_(\n            other.num_remaining_lazy_rehash_locks_),\n        minimum_load_factor_(other.minimum_load_factor_),\n        maximum_hashpower_(other.maximum_hashpower_),\n        max_num_worker_threads_(other.max_num_worker_threads_) {\n    if (other.get_allocator() == alloc) {\n      all_locks_ = std::move(other.all_locks_);\n    } else {\n      add_locks_from_other(other);\n    }\n  }\n\n  /**\n   * Constructs the map with the contents of initializer list @c init.\n   *\n   * @param init initializer list to initialize the elements of the map with\n   * @param n the number of elements to reserve space for initially\n   * @param hf hash function instance to use\n   * @param equal equality function instance to use\n   * @param alloc allocator instance to use\n   */\n  cuckoohash_map(std::initializer_list<value_type> init,\n                 size_type n = DEFAULT_SIZE, const Hash &hf = Hash(),\n                 const KeyEqual &equal = KeyEqual(),\n                 const Allocator &alloc = Allocator())\n      : cuckoohash_map(init.begin(), init.end(), n, hf, equal, alloc) {}\n\n  /**\n   * Exchanges the contents of the map with those of @p other\n   *\n   * @param other the map to exchange contents with\n   */\n  void swap(cuckoohash_map &other) noexcept {\n    std::swap(hash_fn_, other.hash_fn_);\n    std::swap(eq_fn_, other.eq_fn_);\n    buckets_.swap(other.buckets_);\n    all_locks_.swap(other.all_locks_);\n    other.minimum_load_factor_.store(\n        minimum_load_factor_.exchange(other.minimum_load_factor(),\n                                      std::memory_order_release),\n        std::memory_order_release);\n    other.maximum_hashpower_.store(\n        maximum_hashpower_.exchange(other.maximum_hashpower(),\n                                    std::memory_order_release),\n        std::memory_order_release);\n  }\n\n  /**\n   * Copy assignment operator. If @p other is being modified concurrently,\n   * behavior is unspecified.\n   *\n   * @param other the map to assign from\n   * @return @c *this\n   */\n  cuckoohash_map &operator=(const cuckoohash_map &other) = default;\n\n  /**\n   * Move assignment operator. If @p other is being modified concurrently,\n   * behavior is unspecified.\n   *\n   * @param other the map to assign from\n   * @return @c *this\n   */\n  cuckoohash_map &operator=(cuckoohash_map &&other) = default;\n\n  /**\n   * Initializer list assignment operator\n   *\n   * @param ilist an initializer list to assign from\n   * @return @c *this\n   */\n  cuckoohash_map &operator=(std::initializer_list<value_type> ilist) {\n    clear();\n    for (const auto &item : ilist) {\n      insert(item.first, item.second);\n    }\n    return *this;\n  }\n\n  /**@}*/\n\n  /** @name Table Details\n   *\n   * Methods for getting information about the table. Methods that query\n   * changing properties of the table are not synchronized with concurrent\n   * operations, and may return out-of-date information if the table is being\n   * concurrently modified. They will also continue to work after the container\n   * has been moved.\n   *\n   */\n  /**@{*/\n\n  /**\n   * Returns the function that hashes the keys\n   *\n   * @return the hash function\n   */\n  hasher hash_function() const { return hash_fn_; }\n\n  /**\n   * Returns the function that compares keys for equality\n   *\n   * @return the key comparison function\n   */\n  key_equal key_eq() const { return eq_fn_; }\n\n  /**\n   * Returns the allocator associated with the map\n   *\n   * @return the associated allocator\n   */\n  allocator_type get_allocator() const { return buckets_.get_allocator(); }\n\n  /**\n   * Returns the hashpower of the table, which is log<SUB>2</SUB>(@ref\n   * bucket_count()).\n   *\n   * @return the hashpower\n   */\n  size_type hashpower() const { return buckets_.hashpower(); }\n\n  /**\n   * Returns the number of buckets in the table.\n   *\n   * @return the bucket count\n   */\n  size_type bucket_count() const { return buckets_.size(); }\n\n  /**\n   * Returns whether the table is empty or not.\n   *\n   * @return true if the table is empty, false otherwise\n   */\n  bool empty() const { return size() == 0; }\n\n  /**\n   * Returns the number of elements in the table.\n   *\n   * @return number of elements in the table\n   */\n  size_type size() const {\n    if (all_locks_.size() == 0) {\n      return 0;\n    }\n    counter_type s = 0;\n    for (spinlock &lock : get_current_locks()) {\n      s += lock.elem_counter();\n    }\n    assert(s >= 0);\n    return static_cast<size_type>(s);\n  }\n\n  /** Returns the current capacity of the table, that is, @ref bucket_count()\n   * &times; @ref slot_per_bucket().\n   *\n   * @return capacity of table\n   */\n  size_type capacity() const { return bucket_count() * slot_per_bucket(); }\n\n  /**\n   * Returns the percentage the table is filled, that is, @ref size() &divide;\n   * @ref capacity().\n   *\n   * @return load factor of the table\n   */\n  double load_factor() const {\n    return static_cast<double>(size()) / static_cast<double>(capacity());\n  }\n\n  /**\n   * Sets the minimum load factor allowed for automatic expansions. If an\n   * expansion is needed when the load factor of the table is lower than this\n   * threshold, @ref load_factor_too_low is thrown. It will not be\n   * thrown for an explicitly-triggered expansion.\n   *\n   * @param mlf the load factor to set the minimum to\n   * @throw std::invalid_argument if the given load factor is less than 0.0\n   * or greater than 1.0\n   */\n  void minimum_load_factor(const double mlf) {\n    if (mlf < 0.0) {\n      throw std::invalid_argument(\"load factor \" + std::to_string(mlf) +\n                                  \" cannot be \"\n                                  \"less than 0\");\n    } else if (mlf > 1.0) {\n      throw std::invalid_argument(\"load factor \" + std::to_string(mlf) +\n                                  \" cannot be \"\n                                  \"greater than 1\");\n    }\n    minimum_load_factor_.store(mlf, std::memory_order_release);\n  }\n\n  /**\n   * Returns the minimum load factor of the table\n   *\n   * @return the minimum load factor\n   */\n  double minimum_load_factor() const {\n    return minimum_load_factor_.load(std::memory_order_acquire);\n  }\n\n  /**\n   * Sets the maximum hashpower the table can be. If set to @ref\n   * NO_MAXIMUM_HASHPOWER, there will be no limit on the hashpower.\n   * Otherwise, the table will not be able to expand beyond the given\n   * hashpower, either by an explicit or an automatic expansion.\n   *\n   * @param mhp the hashpower to set the maximum to\n   * @throw std::invalid_argument if the current hashpower exceeds the limit\n   */\n  void maximum_hashpower(size_type mhp) {\n    if (hashpower() > mhp) {\n      throw std::invalid_argument(\"maximum hashpower \" + std::to_string(mhp) +\n                                  \" is less than current hashpower\");\n    }\n    maximum_hashpower_.store(mhp, std::memory_order_release);\n  }\n\n  /**\n   * Returns the maximum hashpower of the table\n   *\n   * @return the maximum hashpower\n   */\n  size_type maximum_hashpower() const {\n    return maximum_hashpower_.load(std::memory_order_acquire);\n  }\n\n  /**\n   * Set the maximum number of extra worker threads the table can spawn when\n   * doing large batch operations. Currently batch operations occur in the\n   * following scenarios.\n   *   - Any resizing operation which invokes cuckoo_expand_simple. This\n   *   includes any explicit rehash/resize operation, or any general resize if\n   *   the data is not nothrow-move-constructible.\n   *   - Creating a locked_table or resizing within a locked_table.\n   *\n   * @param num_threads the number of extra threads\n   */\n  void max_num_worker_threads(size_type extra_threads) {\n    max_num_worker_threads_.store(extra_threads, std::memory_order_release);\n  }\n\n  /**\n   * Returns the maximum number of extra worker threads.\n   */\n  size_type max_num_worker_threads() const {\n    return max_num_worker_threads_.load(std::memory_order_acquire);\n  }\n\n  /**@}*/\n\n  /** @name Table Operations\n   *\n   * These are operations that affect the data in the table. They are safe to\n   * call concurrently with each other.\n   *\n   */\n  /**@{*/\n\n  /**\n   * Searches the table for @p key, and invokes @p fn on the value. @p fn is\n   * not allowed to modify the contents of the value if found.\n   *\n   * @tparam K type of the key. This can be any type comparable with @c key_type\n   * @tparam F type of the functor. It should implement the method\n   * <tt>void operator()(const mapped_type&)</tt>.\n   * @param key the key to search for\n   * @param fn the functor to invoke if the element is found\n   * @return true if the key was found and functor invoked, false otherwise\n   */\n  template <typename K, typename F>\n  bool find_fn(const K &key, F fn) const {\n    const hash_value hv = hashed_key(key);\n    const auto b = snapshot_and_lock_two<normal_mode>(hv);\n    const table_position pos = cuckoo_find(key, hv.partial, b.i1, b.i2);\n    if (pos.status == ok) {\n      fn(buckets_[pos.index].mapped(pos.slot));\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  /**\n   * Searches the table for @p key, and invokes @p fn on the value. @p fn is\n   * allow to modify the contents of the value if found.\n   *\n   * @tparam K type of the key. This can be any type comparable with @c key_type\n   * @tparam F type of the functor. It should implement the method\n   * <tt>void operator()(mapped_type&)</tt>.\n   * @param key the key to search for\n   * @param fn the functor to invoke if the element is found\n   * @return true if the key was found and functor invoked, false otherwise\n   */\n  template <typename K, typename F>\n  bool update_fn(const K &key, F fn) {\n    const hash_value hv = hashed_key(key);\n    const auto b = snapshot_and_lock_two<normal_mode>(hv);\n    const table_position pos = cuckoo_find(key, hv.partial, b.i1, b.i2);\n    if (pos.status == ok) {\n      fn(buckets_[pos.index].mapped(pos.slot));\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  /**\n   * Searches for @p key in the table, and invokes @p fn on the value if the\n   * key is found. The functor can mutate the value, and should return @c true\n   * in order to erase the element, and @c false otherwise.\n   *\n   * @tparam K type of the key\n   * @tparam F type of the functor. It should implement the method\n   * <tt>bool operator()(mapped_type&)</tt>.\n   * @param key the key to possibly erase from the table\n   * @param fn the functor to invoke if the element is found\n   * @return true if @p key was found and @p fn invoked, false otherwise\n   */\n  template <typename K, typename F>\n  bool erase_fn(const K &key, F fn) {\n    const hash_value hv = hashed_key(key);\n    const auto b = snapshot_and_lock_two<normal_mode>(hv);\n    const table_position pos = cuckoo_find(key, hv.partial, b.i1, b.i2);\n    if (pos.status == ok) {\n      if (fn(buckets_[pos.index].mapped(pos.slot))) {\n        del_from_bucket(pos.index, pos.slot);\n      }\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  /**\n   * Searches for @p key in the table. If the key is found, then @p fn is\n   * called on the existing value, and nothing happens to the passed-in key and\n   * values. The functor can mutate the value, and should return @c true in\n   * order to erase the element, and @c false otherwise. If the key is not\n   * found and must be inserted, the pair will be constructed by forwarding the\n   * given key and values. If there is no room left in the table, it will be\n   * automatically expanded. Expansion may throw exceptions.\n   *\n   * @tparam K type of the key\n   * @tparam F type of the functor. It should implement the method\n   * <tt>bool operator()(mapped_type&)</tt>.\n   * @tparam Args list of types for the value constructor arguments\n   * @param key the key to insert into the table\n   * @param fn the functor to invoke if the element is found. If your @p fn\n   * needs more data that just the value being modified, consider implementing\n   * it as a lambda with captured arguments.\n   * @param val a list of constructor arguments with which to create the value\n   * @return true if a new key was inserted, false if the key was already in\n   * the table\n   */\n  template <typename K, typename F, typename... Args>\n  bool uprase_fn(K &&key, F fn, Args &&... val) {\n    hash_value hv = hashed_key(key);\n    auto b = snapshot_and_lock_two<normal_mode>(hv);\n    table_position pos = cuckoo_insert_loop<normal_mode>(hv, b, key);\n    if (pos.status == ok) {\n      add_to_bucket(pos.index, pos.slot, hv.partial, std::forward<K>(key),\n                    std::forward<Args>(val)...);\n    } else {\n      if (fn(buckets_[pos.index].mapped(pos.slot))) {\n        del_from_bucket(pos.index, pos.slot);\n      }\n    }\n    return pos.status == ok;\n  }\n\n  /**\n   * Equivalent to calling @ref uprase_fn with a functor that modifies the\n   * given value and always returns false (meaning the element is not removed).\n   * The passed-in functor must implement the method <tt>void\n   * operator()(mapped_type&)</tt>.\n   */\n  template <typename K, typename F, typename... Args>\n  bool upsert(K &&key, F fn, Args &&... val) {\n    return uprase_fn(std::forward<K>(key),\n                     [&fn](mapped_type &v) {\n                       fn(v);\n                       return false;\n                     },\n                     std::forward<Args>(val)...);\n  }\n\n  /**\n   * Copies the value associated with @p key into @p val. Equivalent to\n   * calling @ref find_fn with a functor that copies the value into @p val. @c\n   * mapped_type must be @c CopyAssignable.\n   */\n  template <typename K>\n  bool find(const K &key, mapped_type &val) const {  // NOLINT\n    return find_fn(key, [&val](const mapped_type &v) mutable { val = v; });\n  }\n\n  /** Searches the table for @p key, and returns the associated value it\n   * finds. @c mapped_type must be @c CopyConstructible.\n   *\n   * @tparam K type of the key\n   * @param key the key to search for\n   * @return the value associated with the given key\n   * @throw std::out_of_range if the key is not found\n   */\n  template <typename K>\n  mapped_type find(const K &key) const {\n    const hash_value hv = hashed_key(key);\n    const auto b = snapshot_and_lock_two<normal_mode>(hv);\n    const table_position pos = cuckoo_find(key, hv.partial, b.i1, b.i2);\n    if (pos.status == ok) {\n      return buckets_[pos.index].mapped(pos.slot);\n    } else {\n      throw std::out_of_range(\"key not found in table\");\n    }\n  }\n\n  /**\n   * Returns whether or not @p key is in the table. Equivalent to @ref\n   * find_fn with a functor that does nothing.\n   */\n  template <typename K>\n  bool contains(const K &key) const {\n    return find_fn(key, [](const mapped_type &) {});\n  }\n\n  /**\n   * Updates the value associated with @p key to @p val. Equivalent to\n   * calling @ref update_fn with a functor that assigns the existing mapped\n   * value to @p val. @c mapped_type must be @c MoveAssignable or @c\n   * CopyAssignable.\n   */\n  template <typename K, typename V>\n  bool update(const K &key, V &&val) {\n    return update_fn(key, [&val](mapped_type &v) { v = std::forward<V>(val); });\n  }\n\n  /**\n   * Inserts the key-value pair into the table. Equivalent to calling @ref\n   * upsert with a functor that does nothing.\n   */\n  template <typename K, typename... Args>\n  bool insert(K &&key, Args &&... val) {\n    return upsert(std::forward<K>(key), [](mapped_type &) {},\n                  std::forward<Args>(val)...);\n  }\n\n  /**\n   * Inserts the key-value pair into the table. If the key is already in the\n   * table, assigns the existing mapped value to @p val. Equivalent to\n   * calling @ref upsert with a functor that assigns the mapped value to @p\n   * val.\n   */\n  template <typename K, typename V>\n  bool insert_or_assign(K &&key, V &&val) {\n    return upsert(std::forward<K>(key), [&val](mapped_type &m) { m = val; },\n                  std::forward<V>(val));\n  }\n\n  /**\n   * Erases the key from the table. Equivalent to calling @ref erase_fn with a\n   * functor that just returns true.\n   */\n  template <typename K>\n  bool erase(const K &key) {\n    return erase_fn(key, [](mapped_type &) { return true; });\n  }\n\n  /**\n   * Resizes the table to the given hashpower. If this hashpower is not larger\n   * than the current hashpower, then it decreases the hashpower to the\n   * maximum of the specified value and the smallest hashpower that can hold\n   * all the elements currently in the table.\n   *\n   * @param n the hashpower to set for the table\n   * @return true if the table changed size, false otherwise\n   */\n  bool rehash(size_type n) { return cuckoo_rehash<normal_mode>(n); }\n\n  /**\n   * Reserve enough space in the table for the given number of elements. If\n   * the table can already hold that many elements, the function will shrink\n   * the table to the smallest hashpower that can hold the maximum of the\n   * specified amount and the current table size.\n   *\n   * @param n the number of elements to reserve space for\n   * @return true if the size of the table changed, false otherwise\n   */\n  bool reserve(size_type n) { return cuckoo_reserve<normal_mode>(n); }\n\n  /**\n   * Removes all elements in the table, calling their destructors.\n   */\n  void clear() {\n    auto all_locks_manager = lock_all(normal_mode());\n    cuckoo_clear();\n  }\n\n  void clear_with_callback(std::function<void()> fn) {\n    auto all_locks_manager = lock_all(normal_mode());\n    cuckoo_clear();\n    fn();\n  }\n\n  /**\n   * Construct a @ref locked_table object that owns all the locks in the\n   * table.\n   *\n   * @return a \\ref locked_table instance\n   */\n  locked_table lock_table() { return locked_table(*this); }\n\n  /**\n   * The hashtable is equally partitioned into total_shards number of\n   * partitions.\n   * Calling partial_dump with shard_idx value i dumps ith parition using\n   * dump_fn.\n   *\n   * @param shard\n   * @param dump_fn\n   *\n   */\n  void partial_dump(\n      monolith::hash_table::EmbeddingHashTableInterface::DumpShard shard,\n      std::function<bool(const Key &, const T &)> dump_fn,\n      monolith::hash_table::EmbeddingHashTableInterface::DumpIterator *iter)\n      const {\n    const size_type hash_size = hashsize(hashpower());\n    int Q = hash_size / shard.total;\n    int R = hash_size % shard.total;\n    int begin = (shard.idx * Q) + std::min(shard.idx, R);\n    int end = begin + Q + (shard.idx < R ? 1 : 0);\n    int64_t count = 0;\n    const int64_t bucket_offset = iter->offset / slot_per_bucket();\n    int64_t slot_offset = iter->offset % slot_per_bucket();\n    auto get_offset = [this](size_type bucket, size_type slot) {\n      return bucket * this->slot_per_bucket() + slot;\n    };\n    const int64_t begin_bucket_idx = begin + bucket_offset;\n    for (size_type i = begin_bucket_idx; i < end; ++i) {\n      auto &bucket = buckets_[i];\n      for (size_type j = slot_offset; j < slot_per_bucket(); ++j) {\n        if (bucket.occupied(j)) {\n          ++count;\n          const bool result = dump_fn(bucket.key(j), bucket.mapped(j));\n          if (!result || count >= shard.limit) {\n            iter->offset = get_offset((i - begin), j + 1);\n            return;\n          }\n        }\n      }\n      slot_offset = 0;\n    }\n    // Using +1 here since end might equal to begin.\n    iter->offset = get_offset(end - begin + 1, 0);\n  }\n\n  void evict(std::function<bool(const Key &, const T &)> should_be_evict_fn) {\n    locks_t &locks = get_current_locks();\n    for (size_t l = 0; l < locks.size(); ++l) {\n      spinlock &lock = locks[l];\n      if (!lock.is_migrated()) continue;\n      lock.lock();\n      const auto &lock_manager = LockManager(&lock);\n      for (size_type bucket_ind = l; bucket_ind < buckets_.size();\n           bucket_ind += kMaxNumLocks) {\n        auto &bucket = buckets_[bucket_ind];\n        for (size_type bucket_slot = 0; bucket_slot < slot_per_bucket();\n             ++bucket_slot) {\n          if (!bucket.occupied(bucket_slot)) {\n            continue;\n          }\n          const auto &kv = bucket.kvpair(bucket_slot);\n          const auto &key = kv.first;\n          const auto &entry = kv.second;\n          if (should_be_evict_fn(key, entry)) {\n            del_from_bucket(bucket_ind, bucket_slot);\n          }\n        }\n      }\n    }\n  }\n\n private:\n  // Constructor helpers\n\n  void add_locks_from_other(const cuckoohash_map &other) {\n    locks_t &other_locks = other.get_current_locks();\n    all_locks_.emplace_back(other_locks.size(), spinlock(), get_allocator());\n    std::copy(other_locks.begin(), other_locks.end(),\n              get_current_locks().begin());\n  }\n\n  // Hashing types and functions\n\n  // true if the key is small and simple, which means using partial keys for\n  // lookup would probably slow us down\n  static constexpr bool is_simple() {\n    return std::is_pod<key_type>::value && sizeof(key_type) <= 8;\n  }\n\n  // Whether or not the data is nothrow-move-constructible.\n  static constexpr bool is_data_nothrow_move_constructible() {\n    return std::is_nothrow_move_constructible<key_type>::value &&\n           std::is_nothrow_move_constructible<mapped_type>::value;\n  }\n\n  // Contains a hash and partial for a given key. The partial key is used for\n  // partial-key cuckoohashing, and for finding the alternate bucket of that a\n  // key hashes to.\n  struct hash_value {\n    size_type hash;\n    partial_t partial;\n  };\n\n  template <typename K>\n  hash_value hashed_key(const K &key) const {\n    const size_type hash = hash_function()(key);\n    return {hash, partial_key(hash)};\n  }\n\n  template <typename K>\n  size_type hashed_key_only_hash(const K &key) const {\n    return hash_function()(key);\n  }\n\n  // hashsize returns the number of buckets corresponding to a given\n  // hashpower.\n  static inline size_type hashsize(const size_type hp) {\n    return size_type(1) << hp;\n  }\n\n  // hashmask returns the bitmask for the buckets array corresponding to a\n  // given hashpower.\n  static inline size_type hashmask(const size_type hp) {\n    return hashsize(hp) - 1;\n  }\n\n  // The partial key must only depend on the hash value. It cannot change with\n  // the hashpower, because, in order for `cuckoo_fast_double` to work\n  // properly, the alt_index must only grow by one bit at the top each time we\n  // expand the table.\n  static partial_t partial_key(const size_type hash) {\n    const uint64_t hash_64bit = hash;\n    const uint32_t hash_32bit = (static_cast<uint32_t>(hash_64bit) ^\n                                 static_cast<uint32_t>(hash_64bit >> 32));\n    const uint16_t hash_16bit = (static_cast<uint16_t>(hash_32bit) ^\n                                 static_cast<uint16_t>(hash_32bit >> 16));\n    const uint8_t hash_8bit = (static_cast<uint8_t>(hash_16bit) ^\n                               static_cast<uint8_t>(hash_16bit >> 8));\n    return hash_8bit;\n  }\n\n  // index_hash returns the first possible bucket that the given hashed key\n  // could be.\n  static inline size_type index_hash(const size_type hp, const size_type hv) {\n    return hv & hashmask(hp);\n  }\n\n  // alt_index returns the other possible bucket that the given hashed key\n  // could be. It takes the first possible bucket as a parameter. Note that\n  // this function will return the first possible bucket if index is the\n  // second possible bucket, so alt_index(ti, partial, alt_index(ti, partial,\n  // index_hash(ti, hv))) == index_hash(ti, hv).\n  static inline size_type alt_index(const size_type hp, const partial_t partial,\n                                    const size_type index) {\n    // ensure tag is nonzero for the multiply. 0xc6a4a7935bd1e995 is the\n    // hash constant from 64-bit MurmurHash2\n    const size_type nonzero_tag = static_cast<size_type>(partial) + 1;\n    return (index ^ (nonzero_tag * 0xc6a4a7935bd1e995)) & hashmask(hp);\n  }\n\n  // Locking types\n\n  // Counter type\n  using counter_type = int64_t;\n\n  // A fast, lightweight spinlock\n  //\n  // Per-spinlock, we also maintain some metadata about the contents of the\n  // table. Storing data per-spinlock avoids false sharing issues when multiple\n  // threads need to update this metadata. We store the following information:\n  //\n  // - elem_counter: A counter indicating how many elements in the table are\n  // under this lock. One can compute the size of the table by summing the\n  // elem_counter over all locks.\n  //\n  // - is_migrated: When resizing with cuckoo_fast_doulbe, we do not\n  // immediately rehash elements from the old buckets array to the new one.\n  // Instead, we'll mark all of the locks as not migrated. So anybody trying to\n  // acquire the lock must also migrate the corresponding buckets if\n  // !is_migrated.\n  LIBCUCKOO_SQUELCH_PADDING_WARNING\n  class LIBCUCKOO_ALIGNAS(64) spinlock {\n   public:\n    spinlock() : elem_counter_(0), is_migrated_(true) { lock_.clear(); }\n\n    spinlock(const spinlock &other) noexcept\n        : elem_counter_(other.elem_counter()),\n          is_migrated_(other.is_migrated()) {\n      lock_.clear();\n    }\n\n    spinlock &operator=(const spinlock &other) noexcept {\n      elem_counter() = other.elem_counter();\n      is_migrated() = other.is_migrated();\n      return *this;\n    }\n\n    void lock() noexcept {\n      while (lock_.test_and_set(std::memory_order_acq_rel))\n        ;  // NOLINT\n    }\n\n    void unlock() noexcept { lock_.clear(std::memory_order_release); }\n\n    bool try_lock() noexcept {\n      return !lock_.test_and_set(std::memory_order_acq_rel);\n    }\n\n    counter_type &elem_counter() noexcept { return elem_counter_; }\n    counter_type elem_counter() const noexcept { return elem_counter_; }\n\n    bool &is_migrated() noexcept { return is_migrated_; }\n    bool is_migrated() const noexcept { return is_migrated_; }\n\n   private:\n    std::atomic_flag lock_;\n    counter_type elem_counter_;\n    bool is_migrated_;\n  };\n\n  template <typename U>\n  using rebind_alloc =\n      typename std::allocator_traits<allocator_type>::template rebind_alloc<U>;\n\n  using locks_t = std::vector<spinlock, rebind_alloc<spinlock>>;\n  using all_locks_t = std::list<locks_t, rebind_alloc<locks_t>>;\n\n  // Classes for managing locked buckets. By storing and moving around sets of\n  // locked buckets in these classes, we can ensure that they are unlocked\n  // properly.\n\n  struct LockDeleter {\n    void operator()(spinlock *l) const { l->unlock(); }\n  };\n\n  using LockManager = std::unique_ptr<spinlock, LockDeleter>;\n\n  // Each of the locking methods can operate in two modes: locked_table_mode\n  // and normal_mode. When we're in locked_table_mode, we assume the caller has\n  // already taken all locks on the buckets. We also require that all data is\n  // rehashed immediately, so that the caller never has to look through any\n  // locks. In normal_mode, we actually do take locks, and can rehash lazily.\n  using locked_table_mode = std::integral_constant<bool, true>;\n  using normal_mode = std::integral_constant<bool, false>;\n\n  class TwoBuckets {\n   public:\n    TwoBuckets() {}\n    TwoBuckets(size_type i1_, size_type i2_, locked_table_mode)\n        : i1(i1_), i2(i2_) {}\n    TwoBuckets(locks_t &locks, size_type i1_, size_type i2_,\n               normal_mode)  // NOLINT\n        : i1(i1_),\n          i2(i2_),\n          first_manager_(&locks[lock_ind(i1)]),\n          second_manager_((lock_ind(i1) != lock_ind(i2)) ? &locks[lock_ind(i2)]\n                                                         : nullptr) {}\n\n    void unlock() {\n      first_manager_.reset();\n      second_manager_.reset();\n    }\n\n    size_type i1, i2;\n\n   private:\n    LockManager first_manager_, second_manager_;\n  };\n\n  struct AllUnlocker {\n    void operator()(cuckoohash_map *map) const {\n      for (auto it = first_locked; it != map->all_locks_.end(); ++it) {\n        locks_t &locks = *it;\n        for (spinlock &lock : locks) {\n          lock.unlock();\n        }\n      }\n    }\n\n    typename all_locks_t::iterator first_locked;\n  };\n\n  using AllLocksManager = std::unique_ptr<cuckoohash_map, AllUnlocker>;\n\n  // This exception is thrown whenever we try to lock a bucket, but the\n  // hashpower is not what was expected\n  class hashpower_changed {};\n\n  // After taking a lock on the table for the given bucket, this function will\n  // check the hashpower to make sure it is the same as what it was before the\n  // lock was taken. If it isn't unlock the bucket and throw a\n  // hashpower_changed exception.\n  inline void check_hashpower(size_type hp, spinlock &lock) const {  // NOLINT\n    if (hashpower() != hp) {\n      lock.unlock();\n      LIBCUCKOO_DBG(\"%s\", \"hashpower changed\\n\");\n      throw hashpower_changed();\n    }\n  }\n\n  // If necessary, rehashes the buckets corresponding to the given lock index,\n  // and sets the is_migrated flag to true. We should only ever do migrations\n  // if the data is nothrow move constructible, so this function is noexcept.\n  //\n  // This only works if our current locks array is at the maximum size, because\n  // otherwise, rehashing could require taking other locks. Assumes the lock at\n  // the given index is taken.\n  //\n  // If IS_LAZY is true, we assume the lock is being rehashed in a lazy\n  // (on-demand) fashion, so we additionally decrement the number of locks we\n  // need to lazy_rehash. This may trigger false sharing with other\n  // lazy-rehashing threads, but the hope is that the fraction of such\n  // operations is low-enough to not significantly impact overall performance.\n  static constexpr bool kIsLazy = true;\n  static constexpr bool kIsNotLazy = false;\n\n  template <bool IS_LAZY>\n  void rehash_lock(size_t l) const noexcept {\n    locks_t &locks = get_current_locks();\n    spinlock &lock = locks[l];\n    if (lock.is_migrated()) return;\n\n    assert(is_data_nothrow_move_constructible());\n    assert(locks.size() == kMaxNumLocks);\n    assert(old_buckets_.hashpower() + 1 == buckets_.hashpower());\n    assert(old_buckets_.size() >= kMaxNumLocks);\n    // Iterate through all buckets in old_buckets that are controlled by this\n    // lock, and move them into the current buckets array.\n    for (size_type bucket_ind = l; bucket_ind < old_buckets_.size();\n         bucket_ind += kMaxNumLocks) {\n      move_bucket(old_buckets_, buckets_, bucket_ind);\n    }\n    lock.is_migrated() = true;\n\n    if (IS_LAZY) {\n      decrement_num_remaining_lazy_rehash_locks();\n    }\n  }\n\n  // locks the given bucket index.\n  //\n  // throws hashpower_changed if it changed after taking the lock.\n  LockManager lock_one(size_type, size_type, locked_table_mode) const {\n    return LockManager();\n  }\n\n  LockManager lock_one(size_type hp, size_type i, normal_mode) const {\n    locks_t &locks = get_current_locks();\n    const size_type l = lock_ind(i);\n    spinlock &lock = locks[l];\n    lock.lock();\n    check_hashpower(hp, lock);\n    rehash_lock<kIsLazy>(l);\n    return LockManager(&lock);\n  }\n\n  // locks the two bucket indexes, always locking the earlier index first to\n  // avoid deadlock. If the two indexes are the same, it just locks one.\n  //\n  // throws hashpower_changed if it changed after taking the lock.\n  TwoBuckets lock_two(size_type, size_type i1, size_type i2,\n                      locked_table_mode) const {\n    return TwoBuckets(i1, i2, locked_table_mode());\n  }\n\n  TwoBuckets lock_two(size_type hp, size_type i1, size_type i2,\n                      normal_mode) const {\n    size_type l1 = lock_ind(i1);\n    size_type l2 = lock_ind(i2);\n    if (l2 < l1) {\n      std::swap(l1, l2);\n    }\n    locks_t &locks = get_current_locks();\n    locks[l1].lock();\n    check_hashpower(hp, locks[l1]);\n    if (l2 != l1) {\n      locks[l2].lock();\n    }\n    rehash_lock<kIsLazy>(l1);\n    rehash_lock<kIsLazy>(l2);\n    return TwoBuckets(locks, i1, i2, normal_mode());\n  }\n\n  // lock_three locks the three bucket indexes in numerical order, returning\n  // the containers as a two (i1 and i2) and a one (i3). The one will not be\n  // active if i3 shares a lock index with i1 or i2.\n  //\n  // throws hashpower_changed if it changed after taking the lock.\n  std::pair<TwoBuckets, LockManager> lock_three(size_type, size_type i1,\n                                                size_type i2, size_type,\n                                                locked_table_mode) const {\n    return std::make_pair(TwoBuckets(i1, i2, locked_table_mode()),\n                          LockManager());\n  }\n\n  std::pair<TwoBuckets, LockManager> lock_three(size_type hp, size_type i1,\n                                                size_type i2, size_type i3,\n                                                normal_mode) const {\n    std::array<size_type, 3> l{{lock_ind(i1), lock_ind(i2), lock_ind(i3)}};\n    // Lock in order.\n    if (l[2] < l[1]) std::swap(l[2], l[1]);\n    if (l[2] < l[0]) std::swap(l[2], l[0]);\n    if (l[1] < l[0]) std::swap(l[1], l[0]);\n    locks_t &locks = get_current_locks();\n    locks[l[0]].lock();\n    check_hashpower(hp, locks[l[0]]);\n    if (l[1] != l[0]) {\n      locks[l[1]].lock();\n    }\n    if (l[2] != l[1]) {\n      locks[l[2]].lock();\n    }\n    rehash_lock<kIsLazy>(l[0]);\n    rehash_lock<kIsLazy>(l[1]);\n    rehash_lock<kIsLazy>(l[2]);\n    return std::make_pair(TwoBuckets(locks, i1, i2, normal_mode()),\n                          LockManager((lock_ind(i3) == lock_ind(i1) ||\n                                       lock_ind(i3) == lock_ind(i2))\n                                          ? nullptr\n                                          : &locks[lock_ind(i3)]));\n  }\n\n  // snapshot_and_lock_two loads locks the buckets associated with the given\n  // hash value, making sure the hashpower doesn't change before the locks are\n  // taken. Thus it ensures that the buckets and locks corresponding to the\n  // hash value will stay correct as long as the locks are held. It returns\n  // the bucket indices associated with the hash value and the current\n  // hashpower.\n  template <typename TABLE_MODE>\n  TwoBuckets snapshot_and_lock_two(const hash_value &hv) const {\n    while (true) {\n      // Keep the current hashpower and locks we're using to compute the buckets\n      const size_type hp = hashpower();\n      const size_type i1 = index_hash(hp, hv.hash);\n      const size_type i2 = alt_index(hp, hv.partial, i1);\n      try {\n        return lock_two(hp, i1, i2, TABLE_MODE());\n      } catch (hashpower_changed &) {\n        // The hashpower changed while taking the locks. Try again.\n        continue;\n      }\n    }\n  }\n\n  // lock_all takes all the locks, and returns a deleter object that releases\n  // the locks upon destruction. It does NOT perform any hashpower checks, or\n  // rehash any un-migrated buckets.\n  //\n  // Note that after taking all the locks, it is okay to resize the buckets_\n  // container, since no other threads should be accessing the buckets.\n  AllLocksManager lock_all(locked_table_mode) { return AllLocksManager(); }\n\n  AllLocksManager lock_all(normal_mode) {\n    // all_locks_ should never decrease in size, so if it is non-empty now, it\n    // will remain non-empty\n    assert(!all_locks_.empty());\n    const auto first_locked = std::prev(all_locks_.end());\n    auto current_locks = first_locked;\n    while (current_locks != all_locks_.end()) {\n      locks_t &locks = *current_locks;\n      for (spinlock &lock : locks) {\n        lock.lock();\n      }\n      ++current_locks;\n    }\n    // Once we have taken all the locks of the \"current\" container, nobody\n    // else can do locking operations on the table.\n    return AllLocksManager(this, AllUnlocker{first_locked});\n  }\n\n  // lock_ind converts an index into buckets to an index into locks.\n  static inline size_type lock_ind(const size_type bucket_ind) {\n    return bucket_ind & (kMaxNumLocks - 1);\n  }\n\n  // Data storage types and functions\n\n  // The type of the bucket\n  using bucket = typename buckets_t::bucket;\n\n  // Status codes for internal functions\n\n  enum cuckoo_status {\n    ok,\n    failure,\n    failure_key_not_found,\n    failure_key_duplicated,\n    failure_table_full,\n    failure_under_expansion,\n  };\n\n  // A composite type for functions that need to return a table position, and\n  // a status code.\n  struct table_position {\n    size_type index;\n    size_type slot;\n    cuckoo_status status;\n  };\n\n  // Searching types and functions\n\n  // cuckoo_find searches the table for the given key, returning the position\n  // of the element found, or a failure status code if the key wasn't found.\n  // It expects the locks to be taken and released outside the function.\n  template <typename K>\n  table_position cuckoo_find(const K &key, const partial_t partial,\n                             const size_type i1, const size_type i2) const {\n    int slot = try_read_from_bucket(buckets_[i1], partial, key);\n    if (slot != -1) {\n      return table_position{i1, static_cast<size_type>(slot), ok};\n    }\n    slot = try_read_from_bucket(buckets_[i2], partial, key);\n    if (slot != -1) {\n      return table_position{i2, static_cast<size_type>(slot), ok};\n    }\n    return table_position{0, 0, failure_key_not_found};\n  }\n\n  // try_read_from_bucket will search the bucket for the given key and return\n  // the index of the slot if found, or -1 if not found.\n  template <typename K>\n  int try_read_from_bucket(const bucket &b, const partial_t partial,\n                           const K &key) const {\n    // Silence a warning from MSVC about partial being unused if is_simple.\n    (void)partial;\n    for (int i = 0; i < static_cast<int>(slot_per_bucket()); ++i) {\n      if (!b.occupied(i) || (!is_simple() && partial != b.partial(i))) {\n        continue;\n      } else if (key_eq()(b.key(i), key)) {\n        return i;\n      }\n    }\n    return -1;\n  }\n\n  // Insertion types and function\n\n  /**\n   * Runs cuckoo_insert in a loop until it succeeds in insert and upsert, so\n   * we pulled out the loop to avoid duplicating logic.\n   *\n   * @param hv the hash value of the key\n   * @param b bucket locks\n   * @param key the key to insert\n   * @return table_position of the location to insert the new element, or the\n   * site of the duplicate element with a status code if there was a duplicate.\n   * In either case, the locks will still be held after the function ends.\n   * @throw load_factor_too_low if expansion is necessary, but the\n   * load factor of the table is below the threshold\n   */\n  template <typename TABLE_MODE, typename K>\n  table_position cuckoo_insert_loop(hash_value hv, TwoBuckets &b,\n                                    K &key) {  // NOLINT\n    table_position pos;\n    while (true) {\n      const size_type hp = hashpower();\n      pos = cuckoo_insert<TABLE_MODE>(hv, b, key);\n      switch (pos.status) {\n        case ok:\n        case failure_key_duplicated:\n          return pos;\n        case failure_table_full:\n          // Expand the table and try again, re-grabbing the locks\n          cuckoo_fast_double<TABLE_MODE, automatic_resize>(hp);\n          b = snapshot_and_lock_two<TABLE_MODE>(hv);\n          break;\n        case failure_under_expansion:\n          // The table was under expansion while we were cuckooing. Re-grab the\n          // locks and try again.\n          b = snapshot_and_lock_two<TABLE_MODE>(hv);\n          break;\n        default:\n          assert(false);\n      }\n    }\n  }\n\n  // cuckoo_insert tries to find an empty slot in either of the buckets to\n  // insert the given key into, performing cuckoo hashing if necessary. It\n  // expects the locks to be taken outside the function. Before inserting, it\n  // checks that the key isn't already in the table. cuckoo hashing presents\n  // multiple concurrency issues, which are explained in the function. The\n  // following return states are possible:\n  //\n  // ok -- Found an empty slot, locks will be held on both buckets after the\n  // function ends, and the position of the empty slot is returned\n  //\n  // failure_key_duplicated -- Found a duplicate key, locks will be held, and\n  // the position of the duplicate key will be returned\n  //\n  // failure_under_expansion -- Failed due to a concurrent expansion\n  // operation. Locks are released. No meaningful position is returned.\n  //\n  // failure_table_full -- Failed to find an empty slot for the table. Locks\n  // are released. No meaningful position is returned.\n  template <typename TABLE_MODE, typename K>\n  table_position cuckoo_insert(const hash_value hv, TwoBuckets &b,\n                               K &key) {  // NOLINT\n    int res1, res2;\n    bucket &b1 = buckets_[b.i1];\n    if (!try_find_insert_bucket(b1, res1, hv.partial, key)) {\n      return table_position{b.i1, static_cast<size_type>(res1),\n                            failure_key_duplicated};\n    }\n    bucket &b2 = buckets_[b.i2];\n    if (!try_find_insert_bucket(b2, res2, hv.partial, key)) {\n      return table_position{b.i2, static_cast<size_type>(res2),\n                            failure_key_duplicated};\n    }\n    if (res1 != -1) {\n      return table_position{b.i1, static_cast<size_type>(res1), ok};\n    }\n    if (res2 != -1) {\n      return table_position{b.i2, static_cast<size_type>(res2), ok};\n    }\n\n    // We are unlucky, so let's perform cuckoo hashing.\n    size_type insert_bucket = 0;\n    size_type insert_slot = 0;\n    cuckoo_status st = run_cuckoo<TABLE_MODE>(b, insert_bucket, insert_slot);\n    if (st == failure_under_expansion) {\n      // The run_cuckoo operation operated on an old version of the table,\n      // so we have to try again. We signal to the calling insert method\n      // to try again by returning failure_under_expansion.\n      return table_position{0, 0, failure_under_expansion};\n    } else if (st == ok) {\n      assert(TABLE_MODE() == locked_table_mode() ||\n             !get_current_locks()[lock_ind(b.i1)].try_lock());\n      assert(TABLE_MODE() == locked_table_mode() ||\n             !get_current_locks()[lock_ind(b.i2)].try_lock());\n      assert(!buckets_[insert_bucket].occupied(insert_slot));\n      assert(insert_bucket == index_hash(hashpower(), hv.hash) ||\n             insert_bucket == alt_index(hashpower(), hv.partial,\n                                        index_hash(hashpower(), hv.hash)));\n      // Since we unlocked the buckets during run_cuckoo, another insert\n      // could have inserted the same key into either b.i1 or\n      // b.i2, so we check for that before doing the insert.\n      table_position pos = cuckoo_find(key, hv.partial, b.i1, b.i2);\n      if (pos.status == ok) {\n        pos.status = failure_key_duplicated;\n        return pos;\n      }\n      return table_position{insert_bucket, insert_slot, ok};\n    }\n    assert(st == failure);\n    LIBCUCKOO_DBG(\n        \"hash table is full (hashpower = %zu, hash_items = %zu,\"\n        \"load factor = %.2f), need to increase hashpower\\n\",\n        hashpower(), size(), load_factor());\n    return table_position{0, 0, failure_table_full};\n  }\n\n  // add_to_bucket will insert the given key-value pair into the slot. The key\n  // and value will be move-constructed into the table, so they are not valid\n  // for use afterwards.\n  template <typename K, typename... Args>\n  void add_to_bucket(const size_type bucket_ind, const size_type slot,\n                     const partial_t partial, K &&key, Args &&... val) {\n    buckets_.setKV(bucket_ind, slot, partial, std::forward<K>(key),\n                   std::forward<Args>(val)...);\n    ++get_current_locks()[lock_ind(bucket_ind)].elem_counter();\n  }\n\n  // try_find_insert_bucket will search the bucket for the given key, and for\n  // an empty slot. If the key is found, we store the slot of the key in\n  // `slot` and return false. If we find an empty slot, we store its position\n  // in `slot` and return true. If no duplicate key is found and no empty slot\n  // is found, we store -1 in `slot` and return true.\n  template <typename K>\n  bool try_find_insert_bucket(const bucket &b, int &slot,  // NOLINT\n                              const partial_t partial, const K &key) const {\n    // Silence a warning from MSVC about partial being unused if is_simple.\n    (void)partial;\n    slot = -1;\n    for (int i = 0; i < static_cast<int>(slot_per_bucket()); ++i) {\n      if (b.occupied(i)) {\n        if (!is_simple() && partial != b.partial(i)) {\n          continue;\n        }\n        if (key_eq()(b.key(i), key)) {\n          slot = i;\n          return false;\n        }\n      } else {\n        slot = i;\n      }\n    }\n    return true;\n  }\n\n  // CuckooRecord holds one position in a cuckoo path. Since cuckoopath\n  // elements only define a sequence of alternate hashings for different hash\n  // values, we only need to keep track of the hash values being moved, rather\n  // than the keys themselves.\n  typedef struct {\n    size_type bucket;\n    size_type slot;\n    hash_value hv;\n  } CuckooRecord;\n\n  // The maximum number of items in a cuckoo BFS path. It determines the\n  // maximum number of slots we search when cuckooing.\n  static constexpr uint8_t MAX_BFS_PATH_LEN = 5;\n\n  // An array of CuckooRecords\n  using CuckooRecords = std::array<CuckooRecord, MAX_BFS_PATH_LEN>;\n\n  // run_cuckoo performs cuckoo hashing on the table in an attempt to free up\n  // a slot on either of the insert buckets, which are assumed to be locked\n  // before the start. On success, the bucket and slot that was freed up is\n  // stored in insert_bucket and insert_slot. In order to perform the search\n  // and the swaps, it has to release the locks, which can lead to certain\n  // concurrency issues, the details of which are explained in the function.\n  // If run_cuckoo returns ok (success), then `b` will be active, otherwise it\n  // will not.\n  template <typename TABLE_MODE>\n  cuckoo_status run_cuckoo(TwoBuckets &b, size_type &insert_bucket,  // NOLINT\n                           size_type &insert_slot) {                 // NOLINT\n    // We must unlock the buckets here, so that cuckoopath_search and\n    // cuckoopath_move can lock buckets as desired without deadlock.\n    // cuckoopath_move has to move something out of one of the original\n    // buckets as its last operation, and it will lock both buckets and\n    // leave them locked after finishing. This way, we know that if\n    // cuckoopath_move succeeds, then the buckets needed for insertion are\n    // still locked. If cuckoopath_move fails, the buckets are unlocked and\n    // we try again. This unlocking does present two problems. The first is\n    // that another insert on the same key runs and, finding that the key\n    // isn't in the table, inserts the key into the table. Then we insert\n    // the key into the table, causing a duplication. To check for this, we\n    // search the buckets for the key we are trying to insert before doing\n    // so (this is done in cuckoo_insert, and requires that both buckets are\n    // locked). Another problem is that an expansion runs and changes the\n    // hashpower, meaning the buckets may not be valid anymore. In this\n    // case, the cuckoopath functions will have thrown a hashpower_changed\n    // exception, which we catch and handle here.\n    size_type hp = hashpower();\n    b.unlock();\n    CuckooRecords cuckoo_path;\n    bool done = false;\n    try {\n      while (!done) {\n        const int depth =\n            cuckoopath_search<TABLE_MODE>(hp, cuckoo_path, b.i1, b.i2);\n        if (depth < 0) {\n          break;\n        }\n\n        if (cuckoopath_move<TABLE_MODE>(hp, cuckoo_path, depth, b)) {\n          insert_bucket = cuckoo_path[0].bucket;\n          insert_slot = cuckoo_path[0].slot;\n          assert(insert_bucket == b.i1 || insert_bucket == b.i2);\n          assert(TABLE_MODE() == locked_table_mode() ||\n                 !get_current_locks()[lock_ind(b.i1)].try_lock());\n          assert(TABLE_MODE() == locked_table_mode() ||\n                 !get_current_locks()[lock_ind(b.i2)].try_lock());\n          assert(!buckets_[insert_bucket].occupied(insert_slot));\n          done = true;\n          break;\n        }\n      }\n    } catch (hashpower_changed &) {\n      // The hashpower changed while we were trying to cuckoo, which means\n      // we want to retry. b.i1 and b.i2 should not be locked\n      // in this case.\n      return failure_under_expansion;\n    }\n    return done ? ok : failure;\n  }\n\n  // cuckoopath_search finds a cuckoo path from one of the starting buckets to\n  // an empty slot in another bucket. It returns the depth of the discovered\n  // cuckoo path on success, and -1 on failure. Since it doesn't take locks on\n  // the buckets it searches, the data can change between this function and\n  // cuckoopath_move. Thus cuckoopath_move checks that the data matches the\n  // cuckoo path before changing it.\n  //\n  // throws hashpower_changed if it changed during the search.\n  template <typename TABLE_MODE>\n  int cuckoopath_search(const size_type hp,\n                        CuckooRecords &cuckoo_path,  // NOLINT\n                        const size_type i1, const size_type i2) {\n    b_slot x = slot_search<TABLE_MODE>(hp, i1, i2);\n    if (x.depth == -1) {\n      return -1;\n    }\n    // Fill in the cuckoo path slots from the end to the beginning.\n    for (int i = x.depth; i >= 0; i--) {\n      cuckoo_path[i].slot = x.pathcode % slot_per_bucket();\n      x.pathcode /= slot_per_bucket();\n    }\n    // Fill in the cuckoo_path buckets and keys from the beginning to the\n    // end, using the final pathcode to figure out which bucket the path\n    // starts on. Since data could have been modified between slot_search\n    // and the computation of the cuckoo path, this could be an invalid\n    // cuckoo_path.\n    CuckooRecord &first = cuckoo_path[0];\n    if (x.pathcode == 0) {\n      first.bucket = i1;\n    } else {\n      assert(x.pathcode == 1);\n      first.bucket = i2;\n    }\n    {\n      const auto lock_manager = lock_one(hp, first.bucket, TABLE_MODE());\n      const bucket &b = buckets_[first.bucket];\n      if (!b.occupied(first.slot)) {\n        // We can terminate here\n        return 0;\n      }\n      first.hv = hashed_key(b.key(first.slot));\n    }\n    for (int i = 1; i <= x.depth; ++i) {\n      CuckooRecord &curr = cuckoo_path[i];\n      const CuckooRecord &prev = cuckoo_path[i - 1];\n      assert(prev.bucket == index_hash(hp, prev.hv.hash) ||\n             prev.bucket ==\n                 alt_index(hp, prev.hv.partial, index_hash(hp, prev.hv.hash)));\n      // We get the bucket that this slot is on by computing the alternate\n      // index of the previous bucket\n      curr.bucket = alt_index(hp, prev.hv.partial, prev.bucket);\n      const auto lock_manager = lock_one(hp, curr.bucket, TABLE_MODE());\n      const bucket &b = buckets_[curr.bucket];\n      if (!b.occupied(curr.slot)) {\n        // We can terminate here\n        return i;\n      }\n      curr.hv = hashed_key(b.key(curr.slot));\n    }\n    return x.depth;\n  }\n\n  // cuckoopath_move moves keys along the given cuckoo path in order to make\n  // an empty slot in one of the buckets in cuckoo_insert. Before the start of\n  // this function, the two insert-locked buckets were unlocked in run_cuckoo.\n  // At the end of the function, if the function returns true (success), then\n  // both insert-locked buckets remain locked. If the function is\n  // unsuccessful, then both insert-locked buckets will be unlocked.\n  //\n  // throws hashpower_changed if it changed during the move.\n  template <typename TABLE_MODE>\n  bool cuckoopath_move(const size_type hp,\n                       CuckooRecords &cuckoo_path,        // NOLINT\n                       size_type depth, TwoBuckets &b) {  // NOLINT\n    if (depth == 0) {\n      // There is a chance that depth == 0, when try_add_to_bucket sees\n      // both buckets as full and cuckoopath_search finds one empty. In\n      // this case, we lock both buckets. If the slot that\n      // cuckoopath_search found empty isn't empty anymore, we unlock them\n      // and return false. Otherwise, the bucket is empty and insertable,\n      // so we hold the locks and return true.\n      const size_type bucket_i = cuckoo_path[0].bucket;\n      assert(bucket_i == b.i1 || bucket_i == b.i2);\n      b = lock_two(hp, b.i1, b.i2, TABLE_MODE());\n      if (!buckets_[bucket_i].occupied(cuckoo_path[0].slot)) {\n        return true;\n      } else {\n        b.unlock();\n        return false;\n      }\n    }\n\n    while (depth > 0) {\n      CuckooRecord &from = cuckoo_path[depth - 1];\n      CuckooRecord &to = cuckoo_path[depth];\n      const size_type fs = from.slot;\n      const size_type ts = to.slot;\n      TwoBuckets twob;\n      LockManager extra_manager;\n      if (depth == 1) {\n        // Even though we are only swapping out of one of the original\n        // buckets, we have to lock both of them along with the slot we\n        // are swapping to, since at the end of this function, they both\n        // must be locked. We store tb inside the extrab container so it\n        // is unlocked at the end of the loop.\n        std::tie(twob, extra_manager) =\n            lock_three(hp, b.i1, b.i2, to.bucket, TABLE_MODE());\n      } else {\n        twob = lock_two(hp, from.bucket, to.bucket, TABLE_MODE());\n      }\n\n      bucket &fb = buckets_[from.bucket];\n      bucket &tb = buckets_[to.bucket];\n\n      // We plan to kick out fs, but let's check if it is still there;\n      // there's a small chance we've gotten scooped by a later cuckoo. If\n      // that happened, just... try again. Also the slot we are filling in\n      // may have already been filled in by another thread, or the slot we\n      // are moving from may be empty, both of which invalidate the swap.\n      // We only need to check that the hash value is the same, because,\n      // even if the keys are different and have the same hash value, then\n      // the cuckoopath is still valid.\n      if (tb.occupied(ts) || !fb.occupied(fs) ||\n          hashed_key_only_hash(fb.key(fs)) != from.hv.hash) {\n        return false;\n      }\n\n      buckets_.setKV(to.bucket, ts, fb.partial(fs), fb.movable_key(fs),\n                     std::move(fb.mapped(fs)));\n      buckets_.eraseKV(from.bucket, fs);\n      if (depth == 1) {\n        // Hold onto the locks contained in twob\n        b = std::move(twob);\n      }\n      depth--;\n    }\n    return true;\n  }\n\n  // A constexpr version of pow that we can use for various compile-time\n  // constants and checks.\n  static constexpr size_type const_pow(size_type a, size_type b) {\n    return (b == 0) ? 1 : a * const_pow(a, b - 1);\n  }\n\n  // b_slot holds the information for a BFS path through the table.\n  struct b_slot {\n    // The bucket of the last item in the path.\n    size_type bucket;\n    // a compressed representation of the slots for each of the buckets in\n    // the path. pathcode is sort of like a base-slot_per_bucket number, and\n    // we need to hold at most MAX_BFS_PATH_LEN slots. Thus we need the\n    // maximum pathcode to be at least slot_per_bucket()^(MAX_BFS_PATH_LEN).\n    uint16_t pathcode;\n    static_assert(const_pow(slot_per_bucket(), MAX_BFS_PATH_LEN) <\n                      std::numeric_limits<decltype(pathcode)>::max(),\n                  \"pathcode may not be large enough to encode a cuckoo \"\n                  \"path\");\n    // The 0-indexed position in the cuckoo path this slot occupies. It must\n    // be less than MAX_BFS_PATH_LEN, and also able to hold negative values.\n    int8_t depth;\n    static_assert(MAX_BFS_PATH_LEN - 1 <=\n                      std::numeric_limits<decltype(depth)>::max(),\n                  \"The depth type must able to hold a value of\"\n                  \" MAX_BFS_PATH_LEN - 1\");\n    static_assert(-1 >= std::numeric_limits<decltype(depth)>::min(),\n                  \"The depth type must be able to hold a value of -1\");\n    b_slot() {}\n    b_slot(const size_type b, const uint16_t p, const decltype(depth) d)\n        : bucket(b), pathcode(p), depth(d) {\n      assert(d < MAX_BFS_PATH_LEN);\n    }\n  };\n\n  // b_queue is the queue used to store b_slots for BFS cuckoo hashing.\n  class b_queue {\n   public:\n    b_queue() noexcept : first_(0), last_(0) {}\n\n    void enqueue(b_slot x) {\n      assert(!full());\n      slots_[last_++] = x;\n    }\n\n    b_slot dequeue() {\n      assert(!empty());\n      assert(first_ < last_);\n      b_slot &x = slots_[first_++];\n      return x;\n    }\n\n    bool empty() const { return first_ == last_; }\n\n    bool full() const { return last_ == MAX_CUCKOO_COUNT; }\n\n   private:\n    // The size of the BFS queue. It holds just enough elements to fulfill a\n    // MAX_BFS_PATH_LEN search for two starting buckets, with no circular\n    // wrapping-around. For one bucket, this is the geometric sum\n    // sum_{k=0}^{MAX_BFS_PATH_LEN-1} slot_per_bucket()^k\n    // = (1 - slot_per_bucket()^MAX_BFS_PATH_LEN) / (1 - slot_per_bucket())\n    //\n    // Note that if slot_per_bucket() == 1, then this simply equals\n    // MAX_BFS_PATH_LEN.\n    static_assert(slot_per_bucket() > 0,\n                  \"SLOT_PER_BUCKET must be greater than 0.\");\n    static constexpr size_type MAX_CUCKOO_COUNT =\n        2 * ((slot_per_bucket() == 1)\n                 ? MAX_BFS_PATH_LEN\n                 : (const_pow(slot_per_bucket(), MAX_BFS_PATH_LEN) - 1) /\n                       (slot_per_bucket() - 1));\n    // An array of b_slots. Since we allocate just enough space to complete a\n    // full search, we should never exceed the end of the array.\n    b_slot slots_[MAX_CUCKOO_COUNT];\n    // The index of the head of the queue in the array\n    size_type first_;\n    // One past the index of the last_ item of the queue in the array.\n    size_type last_;\n  };\n\n  // slot_search searches for a cuckoo path using breadth-first search. It\n  // starts with the i1 and i2 buckets, and, until it finds a bucket with an\n  // empty slot, adds each slot of the bucket in the b_slot. If the queue runs\n  // out of space, it fails.\n  //\n  // throws hashpower_changed if it changed during the search\n  template <typename TABLE_MODE>\n  b_slot slot_search(const size_type hp, const size_type i1,\n                     const size_type i2) {\n    b_queue q;\n    // The initial pathcode informs cuckoopath_search which bucket the path\n    // starts on\n    q.enqueue(b_slot(i1, 0, 0));\n    q.enqueue(b_slot(i2, 1, 0));\n    while (!q.empty()) {\n      b_slot x = q.dequeue();\n      auto lock_manager = lock_one(hp, x.bucket, TABLE_MODE());\n      bucket &b = buckets_[x.bucket];\n      // Picks a (sort-of) random slot to start from\n      size_type starting_slot = x.pathcode % slot_per_bucket();\n      for (size_type i = 0; i < slot_per_bucket(); ++i) {\n        uint16_t slot = (starting_slot + i) % slot_per_bucket();\n        if (!b.occupied(slot)) {\n          // We can terminate the search here\n          x.pathcode = x.pathcode * slot_per_bucket() + slot;\n          return x;\n        }\n\n        // If x has less than the maximum number of path components,\n        // create a new b_slot item, that represents the bucket we would\n        // have come from if we kicked out the item at this slot.\n        const partial_t partial = b.partial(slot);\n        if (x.depth < MAX_BFS_PATH_LEN - 1) {\n          assert(!q.full());\n          b_slot y(alt_index(hp, partial, x.bucket),\n                   x.pathcode * slot_per_bucket() + slot, x.depth + 1);\n          q.enqueue(y);\n        }\n      }\n    }\n    // We didn't find a short-enough cuckoo path, so the search terminated.\n    // Return a failure value.\n    return b_slot(0, 0, -1);\n  }\n\n  // cuckoo_fast_double will double the size of the table by taking advantage\n  // of the properties of index_hash and alt_index. If the key's move\n  // constructor is not noexcept, we use cuckoo_expand_simple, since that\n  // provides a strong exception guarantee.\n  template <typename TABLE_MODE, typename AUTO_RESIZE>\n  cuckoo_status cuckoo_fast_double(size_type current_hp) {\n    if (!is_data_nothrow_move_constructible()) {\n      LIBCUCKOO_DBG(\"%s\",\n                    \"cannot run cuckoo_fast_double because key-value\"\n                    \" pair is not nothrow move constructible\");\n      return cuckoo_expand_simple<TABLE_MODE, AUTO_RESIZE>(current_hp + 1);\n    }\n    const size_type new_hp = current_hp + 1;\n    auto all_locks_manager = lock_all(TABLE_MODE());\n    cuckoo_status st = check_resize_validity<AUTO_RESIZE>(current_hp, new_hp);\n    if (st != ok) {\n      return st;\n    }\n\n    // Finish rehashing any un-rehashed buckets, so that we can move out any\n    // remaining data in old_buckets_.  We should be running cuckoo_fast_double\n    // only after trying to cuckoo for a while, which should mean we've tried\n    // going through most of the table and thus done a lot of rehashing\n    // already. So this shouldn't be too expensive.\n    //\n    // We restrict ourselves to the current thread because we want to avoid\n    // possibly spawning extra threads in this function, unless the\n    // circumstances are predictable (i.e. data is nothrow move constructible,\n    // we're in locked_table mode and must keep the buckets_ container\n    // up-to-date, etc).\n    //\n    // If we have fewer than kNumLocks buckets, there shouldn't be any buckets\n    // left to rehash, so this should be a no-op.\n    {\n      locks_t &current_locks = get_current_locks();\n      for (size_t i = 0; i < current_locks.size(); ++i) {\n        rehash_lock<kIsNotLazy>(i);\n      }\n      num_remaining_lazy_rehash_locks(0);\n    }\n\n    // Resize the locks array if necessary. This is done before we update the\n    // hashpower so that other threads don't grab the new hashpower and the old\n    // locks.\n    maybe_resize_locks(size_type(1) << new_hp);\n    locks_t &current_locks = get_current_locks();\n\n    // Move the current buckets into old_buckets_, and create a new empty\n    // buckets container, which will become the new current one. The\n    // old_buckets_ data will be destroyed when move-assigning to buckets_.\n    old_buckets_.swap(buckets_);\n    buckets_ = buckets_t(new_hp, get_allocator());\n\n    // If we have less than kMaxNumLocks buckets, we do a full rehash in the\n    // current thread. On-demand rehashing wouldn't be very easy with less than\n    // kMaxNumLocks buckets, because it would require taking extra lower-index\n    // locks to do the rehashing. Because kMaxNumLocks is relatively small,\n    // this should not be very expensive. We have already set all locks to\n    // migrated at the start of the function, so we shouldn't have to touch\n    // them again.\n    //\n    // Otherwise, if we're in locked_table_mode, the expectation is that we can\n    // access the latest data in buckets_ without taking any locks. So we must\n    // rehash the data immediately. This would not be much different from\n    // lazy-rehashing in locked_table_mode anyways, because it would still be\n    // going on in one thread.\n    if (old_buckets_.size() < kMaxNumLocks) {\n      for (size_type i = 0; i < old_buckets_.size(); ++i) {\n        move_bucket(old_buckets_, buckets_, i);\n      }\n      // This will also delete the old_buckets_ data.\n      num_remaining_lazy_rehash_locks(0);\n    } else {\n      // Mark all current locks as un-migrated, so that we rehash the data\n      // on-demand when the locks are taken.\n      for (spinlock &lock : current_locks) {\n        lock.is_migrated() = false;\n      }\n      num_remaining_lazy_rehash_locks(current_locks.size());\n      if (std::is_same<TABLE_MODE, locked_table_mode>::value) {\n        rehash_with_workers();\n      }\n    }\n    return ok;\n  }\n\n  void move_bucket(buckets_t &old_buckets, buckets_t &new_buckets,  // NOLINT\n                   size_type old_bucket_ind) const noexcept {\n    const size_t old_hp = old_buckets.hashpower();\n    const size_t new_hp = new_buckets.hashpower();\n\n    // By doubling the table size, the index_hash and alt_index of each key got\n    // one bit added to the top, at position old_hp, which means anything we\n    // have to move will either be at the same bucket position, or exactly\n    // hashsize(old_hp) later than the current bucket.\n    bucket &old_bucket = old_buckets_[old_bucket_ind];\n    const size_type new_bucket_ind = old_bucket_ind + hashsize(old_hp);\n    size_type new_bucket_slot = 0;\n\n    // For each occupied slot, either move it into its same position in the\n    // new buckets container, or to the first available spot in the new\n    // bucket in the new buckets container.\n    for (size_type old_bucket_slot = 0; old_bucket_slot < slot_per_bucket();\n         ++old_bucket_slot) {\n      if (!old_bucket.occupied(old_bucket_slot)) {\n        continue;\n      }\n      const hash_value hv = hashed_key(old_bucket.key(old_bucket_slot));\n      const size_type old_ihash = index_hash(old_hp, hv.hash);\n      const size_type old_ahash = alt_index(old_hp, hv.partial, old_ihash);\n      const size_type new_ihash = index_hash(new_hp, hv.hash);\n      const size_type new_ahash = alt_index(new_hp, hv.partial, new_ihash);\n      size_type dst_bucket_ind, dst_bucket_slot;\n      if ((old_bucket_ind == old_ihash && new_ihash == new_bucket_ind) ||\n          (old_bucket_ind == old_ahash && new_ahash == new_bucket_ind)) {\n        // We're moving the key to the new bucket\n        dst_bucket_ind = new_bucket_ind;\n        dst_bucket_slot = new_bucket_slot++;\n      } else {\n        // We're moving the key to the old bucket\n        assert((old_bucket_ind == old_ihash && new_ihash == old_ihash) ||\n               (old_bucket_ind == old_ahash && new_ahash == old_ahash));\n        dst_bucket_ind = old_bucket_ind;\n        dst_bucket_slot = old_bucket_slot;\n      }\n      new_buckets.setKV(dst_bucket_ind, dst_bucket_slot++,\n                        old_bucket.partial(old_bucket_slot),\n                        old_bucket.movable_key(old_bucket_slot),\n                        std::move(old_bucket.mapped(old_bucket_slot)));\n    }\n  }\n\n  // Checks whether the resize is okay to proceed. Returns a status code, or\n  // throws an exception, depending on the error type.\n  using automatic_resize = std::integral_constant<bool, true>;\n  using manual_resize = std::integral_constant<bool, false>;\n\n  template <typename AUTO_RESIZE>\n  cuckoo_status check_resize_validity(const size_type orig_hp,\n                                      const size_type new_hp) {\n    const size_type mhp = maximum_hashpower();\n    if (mhp != NO_MAXIMUM_HASHPOWER && new_hp > mhp) {\n      throw maximum_hashpower_exceeded(new_hp);\n    }\n    if (AUTO_RESIZE::value && load_factor() < minimum_load_factor()) {\n      throw load_factor_too_low(minimum_load_factor());\n    }\n    if (hashpower() != orig_hp) {\n      // Most likely another expansion ran before this one could grab the\n      // locks\n      LIBCUCKOO_DBG(\"%s\", \"another expansion is on-going\\n\");\n      return failure_under_expansion;\n    }\n    return ok;\n  }\n\n  // When we expand the contanier, we may need to expand the locks array, if\n  // the current locks array is smaller than the maximum size and also smaller\n  // than the number of buckets in the upcoming buckets container. In this\n  // case, we grow the locks array to the smaller of the maximum lock array\n  // size and the bucket count. This is done by allocating an entirely new lock\n  // container, taking all the locks, copying over the counters, and then\n  // finally adding it to the end of `all_locks_`, thereby designating it the\n  // \"current\" locks container. It is the responsibility of the caller to\n  // unlock all locks taken, including the new locks, whenever it is done with\n  // them, so that old threads can resume and potentially re-start.\n  void maybe_resize_locks(size_type new_bucket_count) {\n    locks_t &current_locks = get_current_locks();\n    if (!(current_locks.size() < kMaxNumLocks &&\n          current_locks.size() < new_bucket_count)) {\n      return;\n    }\n\n    locks_t new_locks(std::min(size_type(kMaxNumLocks), new_bucket_count),\n                      spinlock(), get_allocator());\n    assert(new_locks.size() > current_locks.size());\n    std::copy(current_locks.begin(), current_locks.end(), new_locks.begin());\n    for (spinlock &lock : new_locks) {\n      lock.lock();\n    }\n    all_locks_.emplace_back(std::move(new_locks));\n  }\n\n  // cuckoo_expand_simple will resize the table to at least the given\n  // new_hashpower. When we're shrinking the table, if the current table\n  // contains more elements than can be held by new_hashpower, the resulting\n  // hashpower will be greater than `new_hp`. It needs to take all the bucket\n  // locks, since no other operations can change the table during expansion.\n  // Throws maximum_hashpower_exceeded if we're expanding beyond the\n  // maximum hashpower, and we have an actual limit.\n  template <typename TABLE_MODE, typename AUTO_RESIZE>\n  cuckoo_status cuckoo_expand_simple(size_type new_hp) {\n    auto all_locks_manager = lock_all(TABLE_MODE());\n    const size_type hp = hashpower();\n    cuckoo_status st = check_resize_validity<AUTO_RESIZE>(hp, new_hp);\n    if (st != ok) {\n      return st;\n    }\n\n    // Finish rehashing any data into buckets_.\n    rehash_with_workers();\n\n    // Creates a new hash table with hashpower new_hp and adds all the elements\n    // from buckets_ and old_buckets_. Allow this map to spawn extra threads if\n    // it needs to resize during the resize.\n    cuckoohash_map new_map(hashsize(new_hp) * slot_per_bucket(),\n                           hash_function(), key_eq(), get_allocator());\n    new_map.max_num_worker_threads(max_num_worker_threads());\n\n    parallel_exec(0, hashsize(hp), [this, &new_map](size_type i, size_type end,\n                                                    std::exception_ptr &eptr) {\n      try {\n        for (; i < end; ++i) {\n          auto &bucket = buckets_[i];\n          for (size_type j = 0; j < slot_per_bucket(); ++j) {\n            if (bucket.occupied(j)) {\n              new_map.insert(bucket.movable_key(j),\n                             std::move(bucket.mapped(j)));\n            }\n          }\n        }\n      } catch (...) {\n        eptr = std::current_exception();\n      }\n    });\n\n    // Finish rehashing any data in new_map.\n    new_map.rehash_with_workers();\n\n    // Swap the buckets_ container with new_map's. This is okay, because we\n    // have all the locks, so nobody else should be reading from the buckets\n    // array. Then the old buckets will be deleted when new_map is deleted.\n    maybe_resize_locks(new_map.bucket_count());\n    buckets_.swap(new_map.buckets_);\n\n    return ok;\n  }\n\n  // Executes the function over the given range, splitting the work between the\n  // current thread and any available worker threads.\n  //\n  // In the noexcept version, the functor must implement operator()(size_type\n  // start, size_type end).\n  //\n  // In the non-noexcept version, the functor will receive an additional\n  // std::exception_ptr& argument.\n\n  template <typename F>\n  void parallel_exec_noexcept(size_type start, size_type end, F func) {\n    const size_type num_extra_threads = max_num_worker_threads();\n    const size_type num_workers = 1 + num_extra_threads;\n    size_type work_per_thread = (end - start) / num_workers;\n    std::vector<std::thread, rebind_alloc<std::thread>> threads(\n        get_allocator());\n    threads.reserve(num_extra_threads);\n    for (size_type i = 0; i < num_extra_threads; ++i) {\n      threads.emplace_back(func, start, start + work_per_thread);\n      start += work_per_thread;\n    }\n    func(start, end);\n    for (std::thread &t : threads) {\n      t.join();\n    }\n  }\n\n  template <typename F>\n  void parallel_exec(size_type start, size_type end, F func) {\n    const size_type num_extra_threads = max_num_worker_threads();\n    const size_type num_workers = 1 + num_extra_threads;\n    size_type work_per_thread = (end - start) / num_workers;\n    std::vector<std::thread, rebind_alloc<std::thread>> threads(\n        get_allocator());\n    threads.reserve(num_extra_threads);\n\n    std::vector<std::exception_ptr, rebind_alloc<std::exception_ptr>> eptrs(\n        num_workers, nullptr, get_allocator());\n    for (size_type i = 0; i < num_extra_threads; ++i) {\n      threads.emplace_back(func, start, start + work_per_thread,\n                           std::ref(eptrs[i]));\n      start += work_per_thread;\n    }\n    func(start, end, std::ref(eptrs.back()));\n    for (std::thread &t : threads) {\n      t.join();\n    }\n    for (std::exception_ptr &eptr : eptrs) {\n      if (eptr) std::rethrow_exception(eptr);\n    }\n  }\n\n  // Does a batch resize of the remaining data in old_buckets_. Assumes all the\n  // locks have already been taken.\n  void rehash_with_workers() noexcept {\n    locks_t &current_locks = get_current_locks();\n    parallel_exec_noexcept(0, current_locks.size(),\n                           [this](size_type start, size_type end) {\n                             for (size_type i = start; i < end; ++i) {\n                               rehash_lock<kIsNotLazy>(i);\n                             }\n                           });\n    num_remaining_lazy_rehash_locks(0);\n  }\n\n  // Deletion functions\n\n  // Removes an item from a bucket, decrementing the associated counter as\n  // well.\n  void del_from_bucket(const size_type bucket_ind, const size_type slot) {\n    buckets_.eraseKV(bucket_ind, slot);\n    --get_current_locks()[lock_ind(bucket_ind)].elem_counter();\n  }\n\n  // Empties the table, calling the destructors of all the elements it removes\n  // from the table. It assumes the locks are taken as necessary.\n  void cuckoo_clear() {\n    buckets_.clear();\n    // This will also clear out any data in old_buckets and delete it, if we\n    // haven't already.\n    num_remaining_lazy_rehash_locks(0);\n    for (spinlock &lock : get_current_locks()) {\n      lock.elem_counter() = 0;\n      lock.is_migrated() = true;\n    }\n  }\n\n  // Rehashing functions\n\n  template <typename TABLE_MODE>\n  bool cuckoo_rehash(size_type n) {\n    const size_type hp = hashpower();\n    if (n == hp) {\n      return false;\n    }\n    return cuckoo_expand_simple<TABLE_MODE, manual_resize>(n) == ok;\n  }\n\n  template <typename TABLE_MODE>\n  bool cuckoo_reserve(size_type n) {\n    const size_type hp = hashpower();\n    const size_type new_hp = reserve_calc(n);\n    if (new_hp == hp) {\n      return false;\n    }\n    return cuckoo_expand_simple<TABLE_MODE, manual_resize>(new_hp) == ok;\n  }\n\n  // Miscellaneous functions\n\n  // reserve_calc takes in a parameter specifying a certain number of slots\n  // for a table and returns the smallest hashpower that will hold n elements.\n  static size_type reserve_calc(const size_type n) {\n    const size_type buckets = (n + slot_per_bucket() - 1) / slot_per_bucket();\n    size_type blog2;\n    for (blog2 = 0; (size_type(1) << blog2) < buckets; ++blog2)\n      ;  // NOLINT\n    assert(n <= buckets * slot_per_bucket() && buckets <= hashsize(blog2));\n    return blog2;\n  }\n\n  // This class is a friend for unit testing\n  friend class UnitTestInternalAccess;\n\n  static constexpr size_type kMaxNumLocks = 1UL << 16;\n\n  locks_t &get_current_locks() const { return all_locks_.back(); }\n\n  // Get/set/decrement num remaining lazy rehash locks. If we reach 0 remaining\n  // lazy locks, we can deallocate the memory in old_buckets_.\n  size_type num_remaining_lazy_rehash_locks() const {\n    return num_remaining_lazy_rehash_locks_.load(std::memory_order_acquire);\n  }\n\n  void num_remaining_lazy_rehash_locks(size_type n) const {\n    num_remaining_lazy_rehash_locks_.store(n, std::memory_order_release);\n    if (n == 0) {\n      old_buckets_.clear_and_deallocate();\n    }\n  }\n\n  void decrement_num_remaining_lazy_rehash_locks() const {\n    size_type old_num_remaining = num_remaining_lazy_rehash_locks_.fetch_sub(\n        1, std::memory_order_acq_rel);\n    assert(old_num_remaining >= 1);\n    if (old_num_remaining == 1) {\n      old_buckets_.clear_and_deallocate();\n    }\n  }\n\n  // Member variables\n\n  // The hash function\n  hasher hash_fn_;\n\n  // The equality function\n  key_equal eq_fn_;\n\n  // container of buckets. The size or memory location of the buckets cannot be\n  // changed unless all the locks are taken on the table. Thus, it is only safe\n  // to access the buckets_ container when you have at least one lock held.\n  //\n  // Marked mutable so that const methods can rehash into this container when\n  // necessary.\n  mutable buckets_t buckets_;\n\n  // An old container of buckets, containing data that may not have been\n  // rehashed into the current one. If valid, this will always have a hashpower\n  // exactly one less than the one in buckets_.\n  //\n  // Marked mutable so that const methods can rehash into this container when\n  // necessary.\n  mutable buckets_t old_buckets_;\n\n  // A linked list of all lock containers. We never discard lock containers,\n  // since there is currently no mechanism for detecting when all threads are\n  // done looking at the memory. The back lock container in this list is\n  // designated the \"current\" one, and is used by all operations taking locks.\n  // This container can be modified if either it is empty (which should only\n  // occur during construction), or if the modifying thread has taken all the\n  // locks on the existing \"current\" container. In the latter case, a\n  // modification must take place before a modification to the hashpower, so\n  // that other threads can detect the change and adjust appropriately. Marked\n  // mutable so that const methods can access and take locks.\n  mutable all_locks_t all_locks_;\n\n  // A small wrapper around std::atomic to make it copyable for constructors.\n  template <typename AtomicT>\n  class CopyableAtomic : public std::atomic<AtomicT> {\n   public:\n    using std::atomic<AtomicT>::atomic;\n\n    CopyableAtomic(const CopyableAtomic &other) noexcept\n        : CopyableAtomic(other.load(std::memory_order_acquire)) {}\n\n    CopyableAtomic &operator=(const CopyableAtomic &other) noexcept {\n      this->store(other.load(std::memory_order_acquire),\n                  std::memory_order_release);\n      return *this;\n    }\n  };\n\n  // We keep track of the number of remaining locks in the latest locks array,\n  // that remain to be rehashed. Once this reaches 0, we can free the memory of\n  // the old buckets. It should only be accessed or modified when\n  // lazy-rehashing a lock, so not in the common case.\n  //\n  // Marked mutable so that we can modify this during rehashing.\n  mutable CopyableAtomic<size_t> num_remaining_lazy_rehash_locks_;\n\n  // Stores the minimum load factor allowed for automatic expansions. Whenever\n  // an automatic expansion is triggered (during an insertion where cuckoo\n  // hashing fails, for example), we check the load factor against this\n  // double, and throw an exception if it's lower than this value. It can be\n  // used to signal when the hash function is bad or the input adversarial.\n  CopyableAtomic<double> minimum_load_factor_;\n\n  // stores the maximum hashpower allowed for any expansions. If set to\n  // NO_MAXIMUM_HASHPOWER, this limit will be disregarded.\n  CopyableAtomic<size_type> maximum_hashpower_;\n\n  // Maximum number of extra threads to spawn when doing any large batch\n  // operations.\n  CopyableAtomic<size_type> max_num_worker_threads_;\n\n public:\n  /**\n   * An ownership wrapper around a @ref cuckoohash_map table instance. When\n   * given a table instance, it takes all the locks on the table, blocking all\n   * outside operations on the table. Because the locked_table has unique\n   * ownership of the table, it can provide a set of operations on the table\n   * that aren't possible in a concurrent context.\n   *\n   * The locked_table interface is very similar to the STL unordered_map\n   * interface, and for functions whose signatures correspond to unordered_map\n   * methods, the behavior should be mostly the same.\n   */\n  class locked_table {\n   public:\n    /** @name Type Declarations */\n    /**@{*/\n\n    using key_type = typename cuckoohash_map::key_type;\n    using mapped_type = typename cuckoohash_map::mapped_type;\n    using value_type = typename cuckoohash_map::value_type;\n    using size_type = typename cuckoohash_map::size_type;\n    using difference_type = typename cuckoohash_map::difference_type;\n    using hasher = typename cuckoohash_map::hasher;\n    using key_equal = typename cuckoohash_map::key_equal;\n    using allocator_type = typename cuckoohash_map::allocator_type;\n    using reference = typename cuckoohash_map::reference;\n    using const_reference = typename cuckoohash_map::const_reference;\n    using pointer = typename cuckoohash_map::pointer;\n    using const_pointer = typename cuckoohash_map::const_pointer;\n\n    /**\n     * A constant iterator over a @ref locked_table, which allows read-only\n     * access to the elements of the table. It fulfills the\n     * BidirectionalIterator concept.\n     */\n    class const_iterator {\n     public:\n      using difference_type = typename locked_table::difference_type;\n      using value_type = typename locked_table::value_type;\n      using pointer = typename locked_table::const_pointer;\n      using reference = typename locked_table::const_reference;\n      using iterator_category = std::bidirectional_iterator_tag;\n\n      const_iterator() {}\n\n      // Return true if the iterators are from the same locked table and\n      // location, false otherwise.\n      bool operator==(const const_iterator &it) const {\n        return buckets_ == it.buckets_ && index_ == it.index_ &&\n               slot_ == it.slot_;\n      }\n\n      bool operator!=(const const_iterator &it) const {\n        return !(operator==(it));\n      }\n\n      reference operator*() const { return (*buckets_)[index_].kvpair(slot_); }\n\n      pointer operator->() const { return std::addressof(operator*()); }\n\n      // Advance the iterator to the next item in the table, or to the end\n      // of the table. Returns the iterator at its new position.\n      const_iterator &operator++() {\n        // Move forward until we get to a slot that is occupied, or we\n        // get to the end\n        ++slot_;\n        for (; index_ < buckets_->size(); ++index_) {\n          for (; slot_ < slot_per_bucket(); ++slot_) {\n            if ((*buckets_)[index_].occupied(slot_)) {\n              return *this;\n            }\n          }\n          slot_ = 0;\n        }\n        assert(std::make_pair(index_, slot_) == end_pos(*buckets_));\n        return *this;\n      }\n\n      // Advance the iterator to the next item in the table, or to the end\n      // of the table. Returns the iterator at its old position.\n      const_iterator operator++(int) {\n        const_iterator old(*this);\n        ++(*this);\n        return old;\n      }\n\n      // Move the iterator back to the previous item in the table. Returns\n      // the iterator at its new position.\n      const_iterator &operator--() {\n        // Move backward until we get to the beginning. Behavior is\n        // undefined if we are iterating at the first element, so we can\n        // assume we'll reach an element. This means we'll never reach\n        // index_ == 0 and slot_ == 0.\n        if (slot_ == 0) {\n          --index_;\n          slot_ = slot_per_bucket() - 1;\n        } else {\n          --slot_;\n        }\n        while (!(*buckets_)[index_].occupied(slot_)) {\n          if (slot_ == 0) {\n            --index_;\n            slot_ = slot_per_bucket() - 1;\n          } else {\n            --slot_;\n          }\n        }\n        return *this;\n      }\n\n      // Move the iterator back to the previous item in the table.\n      // Returns the iterator at its old position. Behavior is undefined\n      // if the iterator is at the beginning.\n      const_iterator operator--(int) {\n        const_iterator old(*this);\n        --(*this);\n        return old;\n      }\n\n     protected:\n      // The buckets owned by the locked table being iterated over. Even\n      // though const_iterator cannot modify the buckets, we don't mark\n      // them const so that the mutable iterator can derive from this\n      // class. Also, since iterators should be default constructible,\n      // copyable, and movable, we have to make this a raw pointer type.\n      buckets_t *buckets_;\n\n      // The bucket index of the item being pointed to. For implementation\n      // convenience, we let it take on negative values.\n      size_type index_;\n\n      // The slot in the bucket of the item being pointed to. For\n      // implementation convenience, we let it take on negative values.\n      size_type slot_;\n\n      // Returns the position signifying the end of the table\n      static std::pair<size_type, size_type> end_pos(const buckets_t &buckets) {\n        return std::make_pair(buckets.size(), 0);\n      }\n\n      // The private constructor is used by locked_table to create\n      // iterators from scratch. If the given index_-slot_ pair is at the\n      // end of the table, or the given spot is occupied, stay. Otherwise,\n      // step forward to the next data item, or to the end of the table.\n      const_iterator(buckets_t &buckets, size_type index,  // NOLINT\n                     size_type slot) noexcept\n          : buckets_(std::addressof(buckets)),\n            index_(index),\n            slot_(slot) {\n        if (std::make_pair(index_, slot_) != end_pos(*buckets_) &&\n            !(*buckets_)[index_].occupied(slot_)) {\n          operator++();\n        }\n      }\n\n      friend class locked_table;\n    };\n\n    /**\n     * An iterator over a @ref locked_table, which allows read-write access\n     * to elements of the table. It fulfills the BidirectionalIterator\n     * concept.\n     */\n    class iterator : public const_iterator {\n     public:\n      using pointer = typename cuckoohash_map::pointer;\n      using reference = typename cuckoohash_map::reference;\n\n      iterator() {}\n\n      bool operator==(const iterator &it) const {\n        return const_iterator::operator==(it);\n      }\n\n      bool operator!=(const iterator &it) const {\n        return const_iterator::operator!=(it);\n      }\n\n      reference operator*() {\n        return (*const_iterator::buckets_)[const_iterator::index_].kvpair(\n            const_iterator::slot_);\n      }\n\n      pointer operator->() { return std::addressof(operator*()); }\n\n      iterator &operator++() {\n        const_iterator::operator++();\n        return *this;\n      }\n\n      iterator operator++(int) {\n        iterator old(*this);\n        const_iterator::operator++();\n        return old;\n      }\n\n      iterator &operator--() {\n        const_iterator::operator--();\n        return *this;\n      }\n\n      iterator operator--(int) {\n        iterator old(*this);\n        const_iterator::operator--();\n        return old;\n      }\n\n     private:\n      iterator(buckets_t &buckets, size_type index,\n               size_type slot) noexcept  // NOLINT\n          : const_iterator(buckets, index, slot) {}\n\n      friend class locked_table;\n    };\n\n    /**@}*/\n\n    /** @name Table Parameters */\n    /**@{*/\n\n    static constexpr size_type slot_per_bucket() {\n      return cuckoohash_map::slot_per_bucket();\n    }\n\n    /**@}*/\n\n    /** @name Constructors, Destructors, and Assignment */\n    /**@{*/\n\n    locked_table() = delete;\n    locked_table(const locked_table &) = delete;\n    locked_table &operator=(const locked_table &) = delete;\n\n    locked_table(locked_table &&lt) noexcept\n        : map_(std::move(lt.map_)),\n          all_locks_manager_(std::move(lt.all_locks_manager_)) {}\n\n    locked_table &operator=(locked_table &&lt) noexcept {\n      unlock();\n      map_ = std::move(lt.map_);\n      all_locks_manager_ = std::move(lt.all_locks_manager_);\n      return *this;\n    }\n\n    /**\n     * Unlocks the table, thereby freeing the locks on the table, but also\n     * invalidating all iterators and table operations with this object. It\n     * is idempotent.\n     */\n    void unlock() { all_locks_manager_.reset(); }\n\n    /**@}*/\n\n    /** @name Table Details\n     *\n     * Methods for getting information about the table. Many are identical\n     * to their @ref cuckoohash_map counterparts. Only new functions or\n     * those with different behavior are documented.\n     *\n     */\n    /**@{*/\n\n    /**\n     * Returns whether the locked table has ownership of the table\n     *\n     * @return true if it still has ownership, false otherwise\n     */\n    bool is_active() const { return static_cast<bool>(all_locks_manager_); }\n\n    hasher hash_function() const { return map_.get().hash_function(); }\n\n    key_equal key_eq() const { return map_.get().key_eq(); }\n\n    allocator_type get_allocator() const { return map_.get().get_allocator(); }\n\n    size_type hashpower() const { return map_.get().hashpower(); }\n\n    size_type bucket_count() const { return map_.get().bucket_count(); }\n\n    bool empty() const { return map_.get().empty(); }\n\n    size_type size() const { return map_.get().size(); }\n\n    size_type capacity() const { return map_.get().capacity(); }\n\n    double load_factor() const { return map_.get().load_factor(); }\n\n    void minimum_load_factor(const double mlf) {\n      map_.get().minimum_load_factor(mlf);\n    }\n\n    double minimum_load_factor() const {\n      return map_.get().minimum_load_factor();\n    }\n\n    void maximum_hashpower(size_type mhp) { map_.get().maximum_hashpower(mhp); }\n\n    size_type maximum_hashpower() const {\n      return map_.get().maximum_hashpower();\n    }\n\n    void max_num_worker_threads(size_type extra_threads) {\n      map_.get().max_num_worker_threads(extra_threads);\n    }\n\n    size_type max_num_worker_threads() const {\n      return map_.get().max_num_worker_threads();\n    }\n\n    /**@}*/\n\n    /** @name Iterators */\n    /**@{*/\n\n    /**\n     * Returns an iterator to the beginning of the table. If the table is\n     * empty, it will point past the end of the table.\n     *\n     * @return an iterator to the beginning of the table\n     */\n\n    iterator begin() { return iterator(map_.get().buckets_, 0, 0); }\n\n    const_iterator begin() const {\n      return const_iterator(map_.get().buckets_, 0, 0);\n    }\n\n    const_iterator cbegin() const { return begin(); }\n\n    /**\n     * Returns an iterator past the end of the table.\n     *\n     * @return an iterator past the end of the table\n     */\n\n    iterator end() {\n      const auto end_pos = const_iterator::end_pos(map_.get().buckets_);\n      return iterator(map_.get().buckets_,\n                      static_cast<size_type>(end_pos.first),\n                      static_cast<size_type>(end_pos.second));\n    }\n\n    const_iterator end() const {\n      const auto end_pos = const_iterator::end_pos(map_.get().buckets_);\n      return const_iterator(map_.get().buckets_,\n                            static_cast<size_type>(end_pos.first),\n                            static_cast<size_type>(end_pos.second));\n    }\n\n    const_iterator cend() const { return end(); }\n\n    /**@}*/\n\n    /** @name Modifiers */\n    /**@{*/\n\n    void clear() { map_.get().cuckoo_clear(); }\n\n    /**\n     * This behaves like the @c unordered_map::try_emplace method.  It will\n     * always invalidate all iterators, due to the possibilities of cuckoo\n     * hashing and expansion.\n     */\n    template <typename K, typename... Args>\n    std::pair<iterator, bool> insert(K &&key, Args &&... val) {\n      hash_value hv = map_.get().hashed_key(key);\n      auto b = map_.get().template snapshot_and_lock_two<locked_table_mode>(hv);\n      table_position pos =\n          map_.get().template cuckoo_insert_loop<locked_table_mode>(hv, b, key);\n      if (pos.status == ok) {\n        map_.get().add_to_bucket(pos.index, pos.slot, hv.partial,\n                                 std::forward<K>(key),\n                                 std::forward<Args>(val)...);\n      } else {\n        assert(pos.status == failure_key_duplicated);\n      }\n      return std::make_pair(iterator(map_.get().buckets_, pos.index, pos.slot),\n                            pos.status == ok);\n    }\n\n    iterator erase(const_iterator pos) {\n      map_.get().del_from_bucket(pos.index_, pos.slot_);\n      return iterator(map_.get().buckets_, pos.index_, pos.slot_);\n    }\n\n    iterator erase(iterator pos) {\n      map_.get().del_from_bucket(pos.index_, pos.slot_);\n      return iterator(map_.get().buckets_, pos.index_, pos.slot_);\n    }\n\n    template <typename K>\n    size_type erase(const K &key) {\n      const hash_value hv = map_.get().hashed_key(key);\n      const auto b =\n          map_.get().template snapshot_and_lock_two<locked_table_mode>(hv);\n      const table_position pos =\n          map_.get().cuckoo_find(key, hv.partial, b.i1, b.i2);\n      if (pos.status == ok) {\n        map_.get().del_from_bucket(pos.index, pos.slot);\n        return 1;\n      } else {\n        return 0;\n      }\n    }\n\n    /**@}*/\n\n    /** @name Lookup */\n    /**@{*/\n\n    template <typename K>\n    iterator find(const K &key) {\n      const hash_value hv = map_.get().hashed_key(key);\n      const auto b =\n          map_.get().template snapshot_and_lock_two<locked_table_mode>(hv);\n      const table_position pos =\n          map_.get().cuckoo_find(key, hv.partial, b.i1, b.i2);\n      if (pos.status == ok) {\n        return iterator(map_.get().buckets_, pos.index, pos.slot);\n      } else {\n        return end();\n      }\n    }\n\n    template <typename K>\n    const_iterator find(const K &key) const {\n      const hash_value hv = map_.get().hashed_key(key);\n      const auto b =\n          map_.get().template snapshot_and_lock_two<locked_table_mode>(hv);\n      const table_position pos =\n          map_.get().cuckoo_find(key, hv.partial, b.i1, b.i2);\n      if (pos.status == ok) {\n        return const_iterator(map_.get().buckets_, pos.index, pos.slot);\n      } else {\n        return end();\n      }\n    }\n\n    template <typename K>\n    mapped_type &at(const K &key) {\n      auto it = find(key);\n      if (it == end()) {\n        throw std::out_of_range(\"key not found in table\");\n      } else {\n        return it->second;\n      }\n    }\n\n    template <typename K>\n    const mapped_type &at(const K &key) const {\n      auto it = find(key);\n      if (it == end()) {\n        throw std::out_of_range(\"key not found in table\");\n      } else {\n        return it->second;\n      }\n    }\n\n    /**\n     * This function has the same lifetime properties as @ref\n     * cuckoohash_map::insert, except that the value is default-constructed,\n     * with no parameters, if it is not already in the table.\n     */\n    template <typename K>\n    T &operator[](K &&key) {\n      auto result = insert(std::forward<K>(key));\n      return result.first->second;\n    }\n\n    template <typename K>\n    size_type count(const K &key) const {\n      const hash_value hv = map_.get().hashed_key(key);\n      const auto b =\n          map_.get().template snapshot_and_lock_two<locked_table_mode>(hv);\n      return map_.get().cuckoo_find(key, hv.partial, b.i1, b.i2).status == ok\n                 ? 1\n                 : 0;\n    }\n\n    template <typename K>\n    std::pair<iterator, iterator> equal_range(const K &key) {\n      auto it = find(key);\n      if (it == end()) {\n        return std::make_pair(it, it);\n      } else {\n        auto start_it = it++;\n        return std::make_pair(start_it, it);\n      }\n    }\n\n    template <typename K>\n    std::pair<const_iterator, const_iterator> equal_range(const K &key) const {\n      auto it = find(key);\n      if (it == end()) {\n        return std::make_pair(it, it);\n      } else {\n        auto start_it = it++;\n        return std::make_pair(start_it, it);\n      }\n    }\n\n    /**@}*/\n\n    /** @name Re-sizing */\n    /**@{*/\n\n    /**\n     * This has the same behavior as @ref cuckoohash_map::rehash, except\n     * that we don't return anything.\n     */\n    void rehash(size_type n) {\n      map_.get().template cuckoo_rehash<locked_table_mode>(n);\n    }\n\n    /**\n     * This has the same behavior as @ref cuckoohash_map::reserve, except\n     * that we don't return anything.\n     */\n    void reserve(size_type n) {\n      map_.get().template cuckoo_reserve<locked_table_mode>(n);\n    }\n\n    /**@}*/\n\n    /** @name Comparison  */\n    /**@{*/\n\n    bool operator==(const locked_table &lt) const {\n      if (size() != lt.size()) {\n        return false;\n      }\n      for (const auto &elem : lt) {\n        auto it = find(elem.first);\n        if (it == end() || it->second != elem.second) {\n          return false;\n        }\n      }\n      return true;\n    }\n\n    bool operator!=(const locked_table &lt) const {\n      if (size() != lt.size()) {\n        return true;\n      }\n      for (const auto &elem : lt) {\n        auto it = find(elem.first);\n        if (it == end() || it->second != elem.second) {\n          return true;\n        }\n      }\n      return false;\n    }\n\n    /**@}*/\n\n   private:\n    // The constructor locks the entire table. We keep this constructor private\n    // (but expose it to the cuckoohash_map class), since we don't want users\n    // calling it. We also complete any remaining rehashing in the table, so\n    // that everything is in map.buckets_.\n    locked_table(cuckoohash_map &map) noexcept  // NOLINT\n        : map_(map),\n          all_locks_manager_(map.lock_all(normal_mode())) {\n      map.rehash_with_workers();\n    }\n\n    // Dispatchers for methods on cuckoohash_map\n\n    buckets_t &buckets() { return map_.get().buckets_; }\n\n    const buckets_t &buckets() const { return map_.get().buckets_; }\n\n    void maybe_resize_locks(size_type new_bucket_count) {\n      map_.get().maybe_resize_locks(new_bucket_count);\n    }\n\n    locks_t &get_current_locks() { return map_.get().get_current_locks(); }\n\n    // A reference to the map owned by the table\n    std::reference_wrapper<cuckoohash_map> map_;\n    // A manager for all the locks we took on the table.\n    AllLocksManager all_locks_manager_;\n\n    friend class cuckoohash_map;\n\n    friend std::ostream &operator<<(std::ostream &os, const locked_table &lt) {\n      os << lt.buckets();\n      size_type size = lt.size();\n      os.write(reinterpret_cast<const char *>(&size), sizeof(size_type));\n      double mlf = lt.minimum_load_factor();\n      size_type mhp = lt.maximum_hashpower();\n      os.write(reinterpret_cast<const char *>(&mlf), sizeof(double));\n      os.write(reinterpret_cast<const char *>(&mhp), sizeof(size_type));\n      return os;\n    }\n\n    friend std::istream &operator>>(std::istream &is, locked_table &lt) {\n      is >> lt.buckets();\n\n      // Re-size the locks, and set the size to the stored size\n      lt.maybe_resize_locks(lt.bucket_count());\n      for (auto &lock : lt.get_current_locks()) {\n        lock.elem_counter() = 0;\n      }\n      size_type size;\n      is.read(reinterpret_cast<char *>(&size), sizeof(size_type));\n      if (size > 0) {\n        lt.get_current_locks()[0].elem_counter() = size;\n      }\n\n      double mlf;\n      size_type mhp;\n      is.read(reinterpret_cast<char *>(&mlf), sizeof(double));\n      is.read(reinterpret_cast<char *>(&mhp), sizeof(size_type));\n      lt.minimum_load_factor(mlf);\n      lt.maximum_hashpower(mhp);\n      return is;\n    }\n  };\n};\n\n/**\n * Specializes the @c std::swap algorithm for @c cuckoohash_map. Calls @c\n * lhs.swap(rhs).\n *\n * @param lhs the map on the left side to swap\n * @param lhs the map on the right side to swap\n */\ntemplate <class Key, class T, class Hash, class KeyEqual, class Allocator,\n          std::size_t SLOT_PER_BUCKET>\nvoid swap(\n    cuckoohash_map<Key, T, Hash, KeyEqual, Allocator, SLOT_PER_BUCKET> &lhs,\n    cuckoohash_map<Key, T, Hash, KeyEqual, Allocator, SLOT_PER_BUCKET>\n        &rhs) noexcept {\n  lhs.swap(rhs);\n}\n\n}  // namespace libcuckoo\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_CUCKOOHASH_MAP_HPP_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/cuckoohash/cuckoohash_util.hpp",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/** \\file */\n\n#ifndef _MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_CUCKOOHASH_UTIL_HPP\n#define _MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_CUCKOOHASH_UTIL_HPP\n\n#include <exception>\n#include <thread>\n#include <utility>\n#include <vector>\n\n#include \"monolith/native_training/runtime/hash_table/cuckoohash/cuckoohash_config.hpp\"  // for LIBCUCKOO_DEBUG\n\nnamespace libcuckoo {\n\n#if LIBCUCKOO_DEBUG\n// When \\ref LIBCUCKOO_DEBUG is 0, LIBCUCKOO_DBG will printing out status\n// messages in various situations\n#define LIBCUCKOO_DBG(fmt, ...)                                     \\\n  fprintf(stderr,                                                   \\\n          \"\\x1b[32m\"                                                \\\n          \"[libcuckoo:%s:%d:%lu] \" fmt                              \\\n          \"\"                                                        \\\n          \"\\x1b[0m\",                                                \\\n          __FILE__, __LINE__,                                       \\\n          std::hash<std::thread::id>()(std::this_thread::get_id()), \\\n          __VA_ARGS__)\n#else\n// When \\ref LIBCUCKOO_DEBUG is 0, LIBCUCKOO_DBG does nothing\n#define LIBCUCKOO_DBG(fmt, ...) \\\n  do {                          \\\n  } while (0)\n#endif\n\n/**\n * alignas() requires GCC >= 4.9, so we stick with the alignment attribute for\n * GCC.\n */\n#ifdef __GNUC__\n#define LIBCUCKOO_ALIGNAS(x) __attribute__((aligned(x)))\n#else\n#define LIBCUCKOO_ALIGNAS(x) alignas(x)\n#endif\n\n/**\n * At higher warning levels, MSVC produces an annoying warning that alignment\n * may cause wasted space: \"structure was padded due to __declspec(align())\".\n */\n#ifdef _MSC_VER\n#define LIBCUCKOO_SQUELCH_PADDING_WARNING __pragma(warning(suppress : 4324))\n#else\n#define LIBCUCKOO_SQUELCH_PADDING_WARNING\n#endif\n\n/**\n * At higher warning levels, MSVC may issue a deadcode warning which depends on\n * the template arguments given. For certain other template arguments, the code\n * is not really \"dead\".\n */\n#ifdef _MSC_VER\n#define LIBCUCKOO_SQUELCH_DEADCODE_WARNING_BEGIN \\\n  do {                                           \\\n    __pragma(warning(push));                     \\\n    __pragma(warning(disable : 4702))            \\\n  } while (0)\n#define LIBCUCKOO_SQUELCH_DEADCODE_WARNING_END __pragma(warning(pop))\n#else\n#define LIBCUCKOO_SQUELCH_DEADCODE_WARNING_BEGIN\n#define LIBCUCKOO_SQUELCH_DEADCODE_WARNING_END\n#endif\n\n/**\n * Thrown when an automatic expansion is triggered, but the load factor of the\n * table is below a minimum threshold, which can be set by the \\ref\n * cuckoohash_map::minimum_load_factor method. This can happen if the hash\n * function does not properly distribute keys, or for certain adversarial\n * workloads.\n */\nclass load_factor_too_low : public std::exception {\n public:\n  /**\n   * Constructor\n   *\n   * @param lf the load factor of the table when the exception was thrown\n   */\n  load_factor_too_low(const double lf) noexcept : load_factor_(lf) {}  // NOLINT\n\n  /**\n   * @return a descriptive error message\n   */\n  virtual const char *what() const noexcept override {  // NOLINT\n    return \"Automatic expansion triggered when load factor was below \"\n           \"minimum threshold\";\n  }\n\n  /**\n   * @return the load factor of the table when the exception was thrown\n   */\n  double load_factor() const noexcept { return load_factor_; }\n\n private:\n  const double load_factor_;\n};\n\n/**\n * Thrown when an expansion is triggered, but the hashpower specified is greater\n * than the maximum, which can be set with the \\ref\n * cuckoohash_map::maximum_hashpower method.\n */\nclass maximum_hashpower_exceeded : public std::exception {\n public:\n  /**\n   * Constructor\n   *\n   * @param hp the hash power we were trying to expand to\n   */\n  maximum_hashpower_exceeded(const size_t hp) noexcept : hashpower_(hp) {}  // NOLINT\n\n  /**\n   * @return a descriptive error message\n   */\n  virtual const char *what() const noexcept override {  // NOLINT\n    return \"Expansion beyond maximum hashpower\";\n  }\n\n  /**\n   * @return the hashpower we were trying to expand to\n   */\n  size_t hashpower() const noexcept { return hashpower_; }\n\n private:\n  const size_t hashpower_;\n};\n\n}  // namespace libcuckoo\n\n#endif  // _MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_CUCKOOHASH_CUCKOOHASH_UTIL_HPP\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/embedding_hash_table.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\n\nimport public \"monolith/native_training/runtime/hash_table/compressor/float_compressor.proto\";\nimport public \"monolith/native_training/runtime/hash_table/initializer/initializer_config.proto\";\nimport public \"monolith/native_training/runtime/hash_table/optimizer/optimizer.proto\";\n\npackage monolith.hash_table;\n\nmessage EntryConfig {\n  message Segment {\n    optional InitializerConfig init_config = 1;\n    optional OptimizerConfig opt_config = 2;\n    optional FloatCompressorConfig comp_config = 3;\n\n    // Will overwrite dim_size in init_config, opt_config and comp_config.\n    optional int32 dim_size = 7;\n  }\n  repeated Segment segments = 1;\n\n  enum EntryType {\n    UNKNOWN = 0;\n    TRAINING = 1;\n    SERVING = 2;\n  }\n  // If this entry is for serving:\n  // For training entry, comp_config is not used.\n  // For serving entry, init_config & opt_config is not used.\n  optional EntryType entry_type = 2 [default = TRAINING];\n}\n\nmessage EntryDump {\n  optional sfixed64 id = 1;\n  repeated float num = 2;\n  optional OptimizerDump opt = 3;\n  optional int64 last_update_ts_sec = 4;\n}\n\n// Use per slot expire time (in days) to align with sail requirement.\n// In future, slot settings might be deprecated.\nmessage SlotExpireTimeConfig {\n  // These slot and expire time are used to overwrite default slot expire time.\n  message SlotExpireTime {\n    optional uint32 slot = 1;\n    optional uint32 expire_time = 2;\n  }\n\n  repeated SlotExpireTime slot_expire_times = 1;\n  // default expire time is 100 years.\n  optional uint32 default_expire_time = 2 [default = 36500];\n}\n\nmessage CuckooEmbeddingHashTableConfig {\n}\n\n\nmessage EmbeddingHashTableConfig {\n  optional EntryConfig entry_config = 1;\n  enum EntryType {\n    // Memory efficient, but slower.\n    PACKED = 1;\n    // Fastest\n    RAW = 2;\n  }\n  optional EntryType entry_type = 6 [default = PACKED];\n  optional uint64 initial_capacity = 2 [default = 1];\n  optional SlotExpireTimeConfig slot_expire_time_config = 3;\n  oneof type {\n    CuckooEmbeddingHashTableConfig cuckoo = 5;\n  }\n  // Whether to evict features periodically during training and serving.\n  optional bool enable_feature_eviction = 7;\n  // Trigger features eviction every n hours.\n  optional int32 feature_evict_every_n_hours = 8 [default = 240];\n\n  // Whether to erase zero embeddings(l2norm = 0) when serving\n  optional bool skip_zero_embedding = 10 [default = false];\n}\n\nmessage MultiEmbeddingHashTableConfig {\n  repeated string names = 1;\n  repeated EmbeddingHashTableConfig configs = 2;\n}\n\n// Use per slot occurrence threshold config to align with sail requirement.\n// In future, slot settings might be deprecated.\nmessage SlotOccurrenceThresholdConfig {\n  // These slot and occurrence threshold are used to overwrite default\n  // occurrence thresholds.\n  message SlotOccurrenceThreshold {\n    optional uint32 slot = 1;\n    optional uint32 occurrence_threshold = 2;\n  }\n\n  repeated SlotOccurrenceThreshold slot_occurrence_thresholds = 1;\n  optional uint32 default_occurrence_threshold = 2 [default = 0];\n}\n\nmessage SlidingHashFilterMetaDump {\n  optional uint32 split_num = 1 [default = 0];\n  optional uint32 max_forward_step = 2 [default = 0];\n  optional uint32 max_backward_step = 3 [default = 0];\n  optional uint32 max_step = 4 [default = 0];\n  optional uint32 head = 5 [default = 0];\n  optional uint32 head_increment = 6 [default = 0];\n  optional uint64 failure_count = 7 [default = 0];\n}\n\n// Here we make each hash filter split keep the shared meta dump.\n// This meta is small and it can help simplify the design to store\n// the shared meta in a seperate file. We will consider to refine this in\n// future.\nmessage HashFilterSplitMetaDump {\n  optional uint64 failure_count = 1 [default = 0];\n  optional uint64 total_size = 2 [default = 0];\n  optional uint64 num_elements = 3 [default = 0];\n  optional double fill_rate = 4 [default = 0];\n  optional SlidingHashFilterMetaDump sliding_hash_filter_meta = 5;\n}\n\nmessage HashFilterSplitDataDump {\n  optional uint32 offset = 1;\n  repeated uint32 data = 2;\n}\n\nmessage MultiHashTableMetadata {\n  optional string table_name = 1;\n  optional uint64 num_entries = 2;\n}\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/embedding_hash_table_factory.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_factory.h\"\n\n#include <exception>\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/cuckoohash/cuckoo_embedding_hash_table.h\"\n#include \"monolith/native_training/runtime/hash_table/entry_accessor.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<EmbeddingHashTableInterface> NewEmbeddingHashTableFromConfig(\n    EmbeddingHashTableConfig config, GpuExtraArgs args) {\n  switch (config.type_case()) {\n    case EmbeddingHashTableConfig::kCuckoo:\n      if (config.skip_zero_embedding() &&\n          config.entry_config().entry_type() != EntryConfig_EntryType_SERVING) {\n        throw std::invalid_argument(\n            \"Only EntryConfig_EntryType_SERVING supports skip_zero_embedding!\");\n      }\n      return NewCuckooEmbeddingHashTable(\n          config.cuckoo(), NewEntryAccessor(config.entry_config()),\n          config.entry_type(), config.initial_capacity(),\n          config.slot_expire_time_config(), config.skip_zero_embedding());\n    default:\n      throw std::invalid_argument(absl::StrFormat(\n          \"Unknown type of hash table. %s\", config.ShortDebugString()));\n  }\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/embedding_hash_table_factory.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_EMBEDDING_HASH_TABLE_FACTORY\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_EMBEDDING_HASH_TABLE_FACTORY\n#include <memory>\n\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<EmbeddingHashTableInterface> NewEmbeddingHashTableFromConfig(\n    EmbeddingHashTableConfig config, GpuExtraArgs args = GpuExtraArgs{});\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_EMBEDDING_HASH_TABLE_FACTORY\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/embedding_hash_table_interface.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_EMBEDDING_HASH_TABLE_INTERFACE_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_EMBEDDING_HASH_TABLE_INTERFACE_H_\n#include <cstdint>\n#include <memory>\n\n#include \"absl/strings/string_view.h\"\n#include \"absl/types/span.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nclass CucoMultiHashTableOp;\nstruct GpuExtraArgs {\n  CucoMultiHashTableOp* shared_state;\n};\n\n// Hash table maps int64 to a float array with fixed length.\n// Implementation of this interface should guarantee thread safety.\nclass EmbeddingHashTableInterface {\n public:\n  virtual ~EmbeddingHashTableInterface() = default;\n\n  // Returns the corresponding entry for |ids|.\n  virtual int64_t BatchLookup(\n      absl::Span<int64_t> ids,\n      absl::Span<absl::Span<float>> embeddings) const = 0;\n\n  // Handles the corresponding entry for |ids|.\n  virtual void BatchLookupEntry(absl::Span<int64_t> ids,\n                                absl::Span<EntryDump> entries) const = 0;\n\n  // Returns the corresponding entry for |id|.\n  virtual int64_t Lookup(int64_t id, absl::Span<float> embedding) const = 0;\n\n  // Handles the corresponding entry for |id|.\n  virtual void LookupEntry(int64_t id, absl::Span<EntryDump> entry) const = 0;\n\n  // Update the hash table entry directly.\n  virtual void Assign(absl::Span<const int64_t> ids,\n                      absl::Span<const absl::Span<const float>> updates,\n                      int64_t update_time) = 0;\n\n  // Update the hash table entry directly.\n  virtual void AssignAdd(int64_t id, absl::Span<const float> update,\n                         int64_t update_time) = 0;\n\n  // Reinitialize the hash table entry\n  virtual void Reinitialize(absl::Span<const int64_t> ids,\n                            absl::Span<int> status) = 0;\n\n  // Update the hash table based on optimizer.\n  virtual void BatchOptimize(absl::Span<int64_t> ids,\n                             absl::Span<absl::Span<const float>> grads,\n                             absl::Span<const float> learning_rates,\n                             int64_t update_time,\n                             const int64_t global_step = 0) = 0;\n\n  // Update the hash table based on optimizer.\n  virtual void Optimize(int64_t id, absl::Span<const float> grad,\n                        absl::Span<const float> learning_rates,\n                        int64_t update_time, const int64_t global_step = 0) = 0;\n\n  // Evict the outdated hash table values based on the last updated time.\n  virtual void Evict(int64_t max_update_time) = 0;\n\n  // Check if a given id exists in the hashtable\n  virtual bool Contains(const int64_t id) = 0;\n\n  // To utilize multithread, we need to specify how many shard we will use.\n  // Args:\n  // offset - The offset of this shard, should be either 0, or return value from\n  // Save.\n  // limit - how many EntryDump will be fed into write_fn. Default to no limit.\n  struct DumpShard {\n    int idx;\n    int total;\n    int64_t limit = 1LL << 61;\n  };\n\n  struct DumpIterator {\n    int64_t offset = 0;\n  };\n\n  using WriteFn = std::function<bool(EntryDump)>;\n\n  class LockCtx {\n   public:\n    virtual ~LockCtx() = default;\n  };\n  // Locks all entries in the table. This is used together with Save to prevent\n  // concurrent updates.\n  virtual std::unique_ptr<LockCtx> LockAll() = 0;\n\n  // Saves the data. The implementation should guarantee that different shard\n  // can be dumped in the parallel.\n  virtual void Save(DumpShard shard, WriteFn write_fn,\n                    DumpIterator* iter) const = 0;\n\n  // Restores the data from get_fn. The implementation should guarantee that\n  // different shard can be dumped in the parallel.\n  // |get_fn| returns false if it is end of stream.\n  // Returns max_update_ts in this shard.\n  virtual int64_t Restore(DumpShard shard,\n                          std::function<bool(EntryDump*, int64_t*)> get_fn) = 0;\n\n  // Clears data of hash table.\n  virtual void Clear() = 0;\n\n  // Returns the size of the current table.\n  virtual int64_t Size() const = 0;\n\n  // Returns the dimension size of the current table.\n  virtual int DimSize() const = 0;\n\n  virtual int SliceSize() const = 0;\n\n  // Returns true if the current table contains the given key.\n  virtual bool Contains(int64_t id) const = 0;\n\n  virtual std::string DebugString() const = 0;\n};\n\n// A decorator will default redirect all method to base class.\nclass DefaultEmbeddingHashTableDecorator : public EmbeddingHashTableInterface {\n public:\n  DefaultEmbeddingHashTableDecorator(\n      std::unique_ptr<EmbeddingHashTableInterface> base)\n      : base_(std::move(base)) {}\n\n  EmbeddingHashTableInterface* base() const { return base_.get(); }\n\n  EmbeddingHashTableInterface* base() { return base_.get(); }\n\n  int64_t BatchLookup(absl::Span<int64_t> ids,\n                      absl::Span<absl::Span<float>> embeddings) const override {\n    return base_->BatchLookup(ids, embeddings);\n  }\n\n  void BatchLookupEntry(absl::Span<int64_t> ids,\n                        absl::Span<EntryDump> entries) const override {\n    return base_->BatchLookupEntry(ids, entries);\n  }\n\n  int64_t Lookup(int64_t id, absl::Span<float> embedding) const override {\n    return base_->Lookup(id, embedding);\n  }\n\n  void LookupEntry(int64_t id, absl::Span<EntryDump> entry) const override {\n    return base_->LookupEntry(id, entry);\n  }\n\n  void Assign(absl::Span<const int64_t> ids,\n              absl::Span<const absl::Span<const float>> updates,\n              int64_t update_time) override {\n    return base_->Assign(ids, updates, update_time);\n  }\n\n  void AssignAdd(int64_t id, absl::Span<const float> update,\n                 int64_t update_time) override {\n    return base_->AssignAdd(id, update, update_time);\n  }\n\n  void Reinitialize(absl::Span<const int64_t> ids,\n                    absl::Span<int> status) override {\n    base_->Reinitialize(ids, status);\n  }\n\n  void BatchOptimize(absl::Span<int64_t> ids,\n                     absl::Span<absl::Span<const float>> grads,\n                     absl::Span<const float> learning_rates,\n                     int64_t update_time,\n                     const int64_t global_step = 0) override {\n    return base_->BatchOptimize(ids, grads, learning_rates, update_time);\n  }\n\n  void Optimize(int64_t id, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates, int64_t update_time,\n                const int64_t global_step = 0) override {\n    return base_->Optimize(id, grad, learning_rates, update_time);\n  }\n\n  std::unique_ptr<LockCtx> LockAll() override { return base_->LockAll(); }\n\n  void Save(DumpShard shard, WriteFn write_fn,\n            DumpIterator* iter) const override {\n    return base_->Save(shard, std::move(write_fn), iter);\n  }\n\n  int64_t Restore(DumpShard shard,\n                  std::function<bool(EntryDump*, int64_t*)> get_fn) override {\n    return base_->Restore(std::move(shard), std::move(get_fn));\n  }\n\n  void Evict(int64_t max_update_time) { base_->Evict(max_update_time); }\n\n  bool Contains(const int64_t id) { return base_->Contains(id); }\n\n  void Clear() override { return base_->Clear(); }\n\n  int64_t Size() const override { return base_->Size(); }\n\n  int DimSize() const override { return base_->DimSize(); }\n\n  int SliceSize() const override { return base_->SliceSize(); }\n\n  bool Contains(int64_t id) const override { return base_->Contains(id); }\n\n  std::string DebugString() const override { return base_->DebugString(); }\n\n private:\n  std::unique_ptr<EmbeddingHashTableInterface> base_;\n};\n\n// A class that provides some useful functionality. Like default values for\n// some method.\nclass EmbeddingHashTableHelper : public DefaultEmbeddingHashTableDecorator {\n public:\n  explicit EmbeddingHashTableHelper(\n      std::unique_ptr<EmbeddingHashTableInterface> base)\n      : DefaultEmbeddingHashTableDecorator(std::move(base)) {}\n\n  using DefaultEmbeddingHashTableDecorator::Assign;\n  // Provide default parameters.\n  void Assign(absl::Span<const int64_t> ids,\n              absl::Span<const absl::Span<const float>> updates) {\n    return base()->Assign(ids, updates, 0);\n  }\n\n  using DefaultEmbeddingHashTableDecorator::AssignAdd;\n  void AssignAdd(int64_t id, absl::Span<const float> update) {\n    return base()->AssignAdd(id, update, 0);\n  }\n\n  using DefaultEmbeddingHashTableDecorator::BatchOptimize;\n  void BatchOptimize(absl::Span<int64_t> ids,\n                     absl::Span<absl::Span<const float>> grads,\n                     absl::Span<const float> learning_rates) {\n    return base()->BatchOptimize(ids, grads, learning_rates, 0, 0);\n  }\n\n  // Some wrapper for easy use.\n  void AssignOne(int64_t id, absl::Span<const float> update,\n                 int64_t update_time = 0) {\n    return base()->Assign(absl::MakeConstSpan({id}),\n                          absl::MakeConstSpan({update}), update_time);\n  }\n\n  using DefaultEmbeddingHashTableDecorator::Contains;\n  using DefaultEmbeddingHashTableDecorator::Evict;\n\n  using DefaultEmbeddingHashTableDecorator::Save;\n  void Save(DumpShard shard, WriteFn write_fn) {\n    DumpIterator iter;\n    return base()->Save(std::move(shard), std::move(write_fn), &iter);\n  }\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_EMBEDDING_HASH_TABLE_INTERFACE_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/embedding_hash_table_test.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_EMBEDDING_HASH_TABLE_TEST_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_EMBEDDING_HASH_TABLE_TEST_H_\n\n#include <memory>\n#include <thread>\n#include <tuple>\n\n#include \"absl/synchronization/mutex.h\"\n#include \"gmock/gmock.h\"\n#include \"google/protobuf/text_format.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_factory.h\"\n#include \"monolith/native_training/runtime/hash_table/entry_accessor.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nnamespace proto2 = google::protobuf;\nconstexpr int64_t kSecondsPerDay = 24 * 60 * 60;\n\n// The tests assume dim size == 1, sgd optimizer and zero initializer\n// Please see the config in flat_embedding_hash_table_test.cc as a reference\nclass ReadWriteEmbeddingHashTableTest\n    : public ::testing::TestWithParam<\n          std::tuple<EmbeddingHashTableConfig, std::vector<float>>> {};\n\nTEST_P(ReadWriteEmbeddingHashTableTest, SingleThread) {\n  auto p = GetParam();\n  EmbeddingHashTableConfig config = std::get<0>(p);\n  const auto& learning_rates = std::get<1>(p);\n  std::unique_ptr<EmbeddingHashTableInterface> table =\n      NewEmbeddingHashTableFromConfig(config);\n  std::vector<float> num_buffer(1);\n  absl::Span<float> num = absl::MakeSpan(num_buffer);\n  table->Lookup(5, num);\n  EXPECT_THAT(num, ::testing::ElementsAre(0));\n  table->AssignAdd(-10, {2.5}, 100LL);\n  table->Lookup(-10, num);\n  EXPECT_THAT(num, ::testing::ElementsAre(2.5));\n  if (config.entry_config().entry_type() == EntryConfig::TRAINING) {\n    table->Optimize(13, {1.0f}, learning_rates, 0, 0);\n    table->Lookup(13, num);\n    EXPECT_THAT(num, ::testing::ElementsAre(-0.01));\n  }\n\n  std::vector<int64_t> ids{-10, 13, 100};\n  std::vector<float> update1 = {1.0f}, update2 = {2.0f}, update3 = {3.0f};\n  std::vector<absl::Span<const float>> updates = {absl::MakeSpan(update1),\n                                                  absl::MakeSpan(update2),\n                                                  absl::MakeSpan(update3)};\n  table->Assign(absl::MakeSpan(ids), absl::MakeSpan(updates), 0);\n\n  std::vector<int64_t> lookup_ids{5, -10, 13, 100};\n  std::vector<float> emb1 = {0}, emb2 = {0}, emb3 = {0}, emb4 = {0};\n  std::vector<absl::Span<float>> embeddings = {\n      absl::MakeSpan(emb1), absl::MakeSpan(emb2), absl::MakeSpan(emb3),\n      absl::MakeSpan(emb4)};\n  table->BatchLookup(absl::MakeSpan(lookup_ids), absl::MakeSpan(embeddings));\n  EXPECT_THAT(embeddings[0], ::testing::ElementsAre(0));\n  EXPECT_THAT(embeddings[1], ::testing::ElementsAre(1));\n  EXPECT_THAT(embeddings[2], ::testing::ElementsAre(2));\n  EXPECT_THAT(embeddings[3], ::testing::ElementsAre(3));\n\n  std::vector<EntryDump> entries(4);\n  table->BatchLookupEntry(absl::MakeSpan(lookup_ids), absl::MakeSpan(entries));\n  EXPECT_EQ(entries[0].SerializeAsString(), \"\");\n  EntryDump expect;\n  proto2::TextFormat::ParseFromString(R\"(\n    num: 1\n    opt {\n      dump {\n        sgd {\n        }\n      }\n    }\n    last_update_ts_sec: 0\n  )\",\n                                      &expect);\n  EXPECT_EQ(entries[1].SerializeAsString(), expect.SerializeAsString());\n}\n\nTEST_P(ReadWriteEmbeddingHashTableTest, MultiThread) {\n  auto p = GetParam();\n  EmbeddingHashTableConfig config = std::get<0>(p);\n  const auto& learning_rates = std::get<1>(p);\n  std::unique_ptr<EmbeddingHashTableInterface> table =\n      NewEmbeddingHashTableFromConfig(config);\n\n  auto func = [&table](int id) {\n    table->AssignAdd(id, {static_cast<float>(id)}, 0);\n  };\n  auto func2 = [&table](int id, const std::vector<float>& learning_rates) {\n    table->Optimize(id, {static_cast<float>(-200 * id)}, learning_rates, 0, 0);\n  };\n  const int kNumThread = 100;\n  std::vector<std::unique_ptr<std::thread>> threads;\n  for (int i = 0; i < kNumThread; ++i) {\n    threads.emplace_back(std::make_unique<std::thread>(func, i));\n    if (config.entry_config().entry_type() == EntryConfig::TRAINING) {\n      threads.emplace_back(\n          std::make_unique<std::thread>(func2, i, learning_rates));\n    }\n  }\n  for (auto& thread : threads) {\n    thread->join();\n  }\n  for (int i = 0; i < kNumThread; ++i) {\n    std::vector<float> num(1);\n    table->Lookup(i, absl::MakeSpan(num));\n    if (config.entry_config().entry_type() == EntryConfig::TRAINING) {\n      EXPECT_THAT(num, ::testing::ElementsAre(3 * i));\n    } else {\n      EXPECT_THAT(num, ::testing::ElementsAre(i));\n    }\n  }\n}\n\nTEST_P(ReadWriteEmbeddingHashTableTest, Clear) {\n  auto p = GetParam();\n  EmbeddingHashTableConfig config = std::get<0>(p);\n  std::unique_ptr<EmbeddingHashTableHelper> table =\n      std::make_unique<EmbeddingHashTableHelper>(\n          NewEmbeddingHashTableFromConfig(config));\n  table->Assign({1}, {{2.0f}});\n  std::vector<float> emb(1);\n  table->Lookup(1, absl::MakeSpan(emb));\n  EXPECT_THAT(emb, testing::ElementsAre(2.0f));\n  table->Clear();\n  table->Lookup(1, absl::MakeSpan(emb));\n  EXPECT_THAT(emb, testing::ElementsAre(0.0f));\n}\n\nclass SaveRestoreEmbeddingHashTestTest\n    : public ::testing::TestWithParam<\n          std::tuple<EmbeddingHashTableConfig, std::vector<float>>> {};\n\nTEST_P(SaveRestoreEmbeddingHashTestTest, SaveRestore) {\n  auto p = GetParam();\n  auto table = std::make_unique<EmbeddingHashTableHelper>(\n      NewEmbeddingHashTableFromConfig(std::get<0>(p)));\n  table->AssignAdd(5, {2.5}, 0);\n  table->AssignAdd(-3, {-0.5}, 0);\n  std::vector<EntryDump> dumps;\n  auto write_fn = [&dumps](EntryDump dump) {\n    dumps.push_back(dump);\n    return true;\n  };\n  const EmbeddingHashTableInterface::DumpShard kSingleShard{0, 1};\n  table->Save(kSingleShard, write_fn);\n\n  std::unique_ptr<EmbeddingHashTableInterface> table2 =\n      NewEmbeddingHashTableFromConfig(std::get<0>(p));\n  int idx = 0;\n  auto get_fn = [&idx, &dumps](EntryDump* dump, int64_t* max_update_ts) {\n    if (idx == static_cast<int>(dumps.size())) return false;\n    *dump = dumps[idx++];\n    return true;\n  };\n  table2->Restore(kSingleShard, get_fn);\n  std::vector<float> num(1);\n  table2->Lookup(5, absl::MakeSpan(num));\n  EXPECT_THAT(num, ::testing::ElementsAre(2.5));\n  table2->Lookup(-3, absl::MakeSpan(num));\n  EXPECT_THAT(num, ::testing::ElementsAre(-0.5));\n}\n\nTEST_P(SaveRestoreEmbeddingHashTestTest, SaveWithOffset) {\n  auto p = GetParam();\n  std::unique_ptr<EmbeddingHashTableInterface> table =\n      NewEmbeddingHashTableFromConfig(std::get<0>(p));\n  table->AssignAdd(5, {2.5}, 0);\n  table->AssignAdd(-3, {-0.5}, 0);\n  std::vector<EntryDump> dumps;\n  auto write_fn = [&dumps](EntryDump dump) {\n    dumps.push_back(dump);\n    return true;\n  };\n  EmbeddingHashTableInterface::DumpShard shard{0, 1};\n  shard.limit = 1;\n  EmbeddingHashTableInterface::DumpIterator iter;\n  table->Save(shard, write_fn, &iter);\n  EXPECT_THAT(dumps.size(), 1);\n  table->Save(shard, write_fn, &iter);\n  std::vector<int64_t> ids;\n  for (int i = 0; i < dumps.size(); ++i) {\n    ids.push_back(dumps[i].id());\n  }\n  EXPECT_THAT(ids, testing::UnorderedElementsAre(5, -3));\n}\n\nTEST_P(SaveRestoreEmbeddingHashTestTest, SaveRestoreMultithreaded) {\n  auto p = GetParam();\n  auto table =\n      EmbeddingHashTableHelper(NewEmbeddingHashTableFromConfig(std::get<0>(p)));\n  const int kNumThreads = 10;\n  const int kPerThreadIds = 2600;\n  for (int64_t i = 0; i < kNumThreads * kPerThreadIds; ++i) {\n    table.AssignOne(i - kNumThreads * kPerThreadIds / 2, {float(i)});\n  }\n  std::vector<EntryDump> dumps;\n  absl::Mutex mu;\n  auto write_fn = [&dumps, &mu](EntryDump dump) {\n    absl::MutexLock l(&mu);\n    dumps.push_back(dump);\n    return true;\n  };\n  std::vector<std::unique_ptr<std::thread>> save_threads;\n  for (int i = 0; i < kNumThreads; ++i) {\n    auto save_func = [kNumThreads, &table, &write_fn](int i) {\n      EmbeddingHashTableInterface::DumpShard shard{i, kNumThreads};\n      table.Save(shard, write_fn);\n      return true;\n    };\n    save_threads.push_back(std::make_unique<std::thread>(save_func, i));\n  }\n  for (int i = 0; i < kNumThreads; ++i) {\n    save_threads[i]->join();\n  }\n  ASSERT_THAT(dumps.size(), kNumThreads * kPerThreadIds);\n\n  std::unique_ptr<EmbeddingHashTableInterface> table2 =\n      NewEmbeddingHashTableFromConfig(std::get<0>(p));\n\n  std::vector<std::unique_ptr<std::thread>> restore_threads;\n  for (int i = 0; i < kNumThreads; ++i) {\n    auto restore_fn = [&table2, &dumps, kNumThreads, kPerThreadIds](int i) {\n      int idx = i * kPerThreadIds;\n      int end_idx = (i + 1) * kPerThreadIds;\n      auto get_fn = [&dumps, &idx, &end_idx](EntryDump* dump, int64_t*) {\n        if (idx == end_idx) return false;\n        *dump = dumps[idx++];\n        return true;\n      };\n      table2->Restore({i, kNumThreads}, get_fn);\n    };\n    restore_threads.push_back(std::make_unique<std::thread>(restore_fn, i));\n  }\n  for (int i = 0; i < kNumThreads; ++i) {\n    restore_threads[i]->join();\n  }\n  for (int64_t i = 0; i < kNumThreads * kPerThreadIds; ++i) {\n    std::vector<float> nums(1);\n    table.Lookup(i - kNumThreads * kPerThreadIds / 2, absl::MakeSpan(nums));\n    ASSERT_THAT(nums[0], i);\n  }\n}\n\nTEST_P(SaveRestoreEmbeddingHashTestTest, SaveWithStopEarly) {\n  auto p = GetParam();\n  auto table =\n      EmbeddingHashTableHelper(NewEmbeddingHashTableFromConfig(std::get<0>(p)));\n  table.Assign({0, 1}, {{0.0}, {0.0}});\n  int called = 0;\n  auto write_fn = [&called](EntryDump dump) {\n    ++called;\n    return false;\n  };\n  EmbeddingHashTableInterface::DumpShard shard{0, 1};\n  table.Save(shard, write_fn);\n  // Should stop early\n  EXPECT_THAT(called, 1);\n}\n\nclass EmbeddingHashTableEvictTest\n    : public ::testing::TestWithParam<\n          std::tuple<EmbeddingHashTableConfig, std::vector<float>>> {};\n\nTEST_P(EmbeddingHashTableEvictTest, OneTimeEvict) {\n  auto p = GetParam();\n  auto embedding_hash_table_config = std::get<0>(p);\n  auto* slot_expire_time_config =\n      embedding_hash_table_config.mutable_slot_expire_time_config();\n  slot_expire_time_config->set_default_expire_time(14);\n  const std::vector<int64_t> slot_to_expire_time = {0, 5, 6};\n  for (int i = 0; i < slot_to_expire_time.size(); ++i) {\n    auto* expire_time = slot_expire_time_config->add_slot_expire_times();\n    expire_time->set_slot(i);\n    expire_time->set_expire_time(slot_to_expire_time[i]);\n  }\n  auto table = NewEmbeddingHashTableFromConfig(embedding_hash_table_config);\n\n  const int64_t kFidUpdateTime = 1234;\n  const int64_t kSlot1Fid = ((1LL << 48) | (123));\n  const int64_t kSlot2Fid = ((2LL << 48) | (234));\n  const int64_t kSlot3Fid = ((3LL << 48) | (456));\n\n  table->Assign({kSlot1Fid}, {{2.0f}}, kFidUpdateTime);\n  table->Assign({kSlot2Fid}, {{5.0f}}, kFidUpdateTime);\n  table->Assign({kSlot3Fid}, {{7.0f}}, kFidUpdateTime);\n  std::vector<float> emb(1);\n  table->Lookup(kSlot1Fid, absl::MakeSpan(emb));\n  EXPECT_THAT(emb, testing::ElementsAre(2.0f));\n  table->Lookup(kSlot2Fid, absl::MakeSpan(emb));\n  EXPECT_THAT(emb, testing::ElementsAre(5.0f));\n  table->Lookup(kSlot3Fid, absl::MakeSpan(emb));\n  EXPECT_THAT(emb, testing::ElementsAre(7.0f));\n\n  const int64_t current_time = kFidUpdateTime + 5 * kSecondsPerDay + 60;\n  table->Evict(current_time);\n  table->Lookup(kSlot1Fid, absl::MakeSpan(emb));\n  // Slot 1 expire time is 5 days, the time gap is 5 days + 60 seconds, so\n  // should be evited.\n  EXPECT_THAT(emb, testing::ElementsAre(0.0f));\n  table->Lookup(kSlot2Fid, absl::MakeSpan(emb));\n  // Slot 1 expire time is 6 days, the time gap is 5 days + 60 seconds, so\n  // should NOT be evited.\n  EXPECT_THAT(emb, testing::ElementsAre(5.0f));\n  table->Lookup(kSlot3Fid, absl::MakeSpan(emb));\n  // Slot 3 expire time should use default 14 days, the time gap is 5 days\n  // + 60 seconds, so should NOT be evited.\n  EXPECT_THAT(emb, testing::ElementsAre(7.0f));\n}\n\n// Testing evict would work during the hash table rehashing.\nTEST_P(EmbeddingHashTableEvictTest, EvictWhileRehash) {\n  auto p = GetParam();\n  auto embedding_hash_table_config = std::get<0>(p);\n  // We keep the initial capacity very small so that inserting will trigger\n  // rehash.\n  const int64_t kInitialHashTableCapacity = 1;\n  const int64_t kNumInsertThreads = 20;\n  const int64_t kIdPerThread = 50;\n  const float kDefaultValue = 123.0f;\n  embedding_hash_table_config.set_initial_capacity(kInitialHashTableCapacity);\n  auto* slot_expire_time_config =\n      embedding_hash_table_config.mutable_slot_expire_time_config();\n  slot_expire_time_config->set_default_expire_time(0);\n  for (int i = 0; i < kNumInsertThreads; ++i) {\n    auto* expire_time = slot_expire_time_config->add_slot_expire_times();\n    expire_time->set_slot(i);\n    expire_time->set_expire_time(i);\n  }\n  auto table = NewEmbeddingHashTableFromConfig(embedding_hash_table_config);\n\n  const int64_t kFidUpdateTime = 1234;\n  std::vector<std::unique_ptr<std::thread>> insert_threads;\n  for (int i = 0; i < kNumInsertThreads; ++i) {\n    auto insert_func = [&table, kDefaultValue](int i) {\n      for (int id = 0; id < kIdPerThread; ++id) {\n        const int64_t slot_id = i;\n        const int64_t fid = ((slot_id << 48) | id);\n        table->Assign({fid}, {{kDefaultValue}}, kFidUpdateTime + id);\n        std::this_thread::sleep_for(std::chrono::milliseconds(100));\n      }\n    };\n    insert_threads.push_back(std::make_unique<std::thread>(insert_func, i));\n  }\n  const int64_t kCurrentTime =\n      kFidUpdateTime + 5 * kSecondsPerDay + kSecondsPerDay / 2;\n  // Run Evict every 2 seconds 3 times.\n  std::unique_ptr<std::thread> evict_thread;\n  auto evict_func = [&table, kCurrentTime]() {\n    for (int i = 0; i < 3; ++i) {\n      table->Evict(kCurrentTime);\n      std::this_thread::sleep_for(std::chrono::seconds(2));\n    }\n  };\n  evict_thread = std::make_unique<std::thread>(evict_func);\n  for (int i = 0; i < kNumInsertThreads; ++i) {\n    insert_threads[i]->join();\n  }\n  evict_thread->join();\n  // Have a final evict to make sure the test is not flaky.\n  table->Evict(kCurrentTime);\n\n  for (int i = 0; i < kNumInsertThreads; ++i) {\n    std::vector<float> num(1);\n    for (int id = 0; id < kIdPerThread; ++id) {\n      const int64_t slot_id = i;\n      const int64_t fid = ((slot_id << 48) | id);\n      table->Lookup(fid, absl::MakeSpan(num));\n      if (i <= 5) {\n        EXPECT_THAT(num, ::testing::ElementsAre(0));\n      } else {\n        EXPECT_THAT(num, ::testing::ElementsAre(kDefaultValue));\n      }\n    }\n  }\n}\n\nclass EmbeddingHashTableSkipZeroEmbeddingTest\n    : public ::testing::TestWithParam<\n          std::tuple<EmbeddingHashTableConfig, std::vector<float>>> {};\n\nTEST_P(EmbeddingHashTableSkipZeroEmbeddingTest, AssignSkipZeroEmbedding) {\n  auto p = GetParam();\n  auto table =\n      EmbeddingHashTableHelper(NewEmbeddingHashTableFromConfig(std::get<0>(p)));\n  const int kNumThreads = 10;\n  const int kNumIds = 3000;\n\n  auto AssignFn = [&]() {\n    for (int i = 0; i < kNumIds; ++i) {\n      table.Assign({i}, {{static_cast<float>(i % 2)}});\n    }\n  };\n  std::vector<std::unique_ptr<std::thread>> threads;\n  for (int i = 0; i < kNumThreads; ++i) {\n    threads.emplace_back(std::make_unique<std::thread>(AssignFn));\n  }\n  for (auto& thread : threads) {\n    thread->join();\n  }\n\n  for (int64_t i = 0; i < kNumIds; ++i) {\n    std::vector<float> nums(1);\n    table.Lookup(i, absl::MakeSpan(nums));\n    ASSERT_THAT(nums[0], i % 2);\n    if (i % 2 == 0) {\n      EXPECT_FALSE(table.Contains(i));\n    } else {\n      EXPECT_TRUE(table.Contains(i));\n    }\n  }\n}\n\nTEST_P(EmbeddingHashTableSkipZeroEmbeddingTest, RestoreSkipZeroEmbedding) {\n  EmbeddingHashTableConfig config;\n  EXPECT_TRUE(proto2::TextFormat::ParseFromString(R\"(\n    entry_config {\n      segments {\n        dim_size: 1\n        init_config { zeros {} }\n        opt_config { sgd {} }\n      }\n    }\n    initial_capacity: 1\n    cuckoo {}\n  )\",\n                                                  &config));\n  auto table =\n      EmbeddingHashTableHelper(NewEmbeddingHashTableFromConfig(config));\n  const int kNumThreads = 10;\n  const int kNumIds = 3000;\n\n  // Assign\n  auto AssignFn = [&]() {\n    for (int i = 0; i < kNumIds; ++i) {\n      table.Assign({i}, {{static_cast<float>(i % 2)}});\n    }\n  };\n  std::vector<std::unique_ptr<std::thread>> threads;\n  for (int i = 0; i < kNumThreads; ++i) {\n    threads.emplace_back(std::make_unique<std::thread>(AssignFn));\n  }\n  for (auto& thread : threads) {\n    thread->join();\n  }\n\n  for (int64_t i = 0; i < kNumIds; ++i) {\n    std::vector<float> nums(1);\n    table.Lookup(i, absl::MakeSpan(nums));\n    ASSERT_THAT(nums[0], i % 2);\n    EXPECT_TRUE(table.Contains(i));\n  }\n\n  // Save\n  std::vector<EntryDump> dumps;\n  absl::Mutex mu;\n  auto write_fn = [&dumps, &mu](EntryDump dump) {\n    absl::MutexLock l(&mu);\n    dumps.push_back(dump);\n    return true;\n  };\n  std::vector<std::unique_ptr<std::thread>> save_threads;\n  for (int i = 0; i < kNumThreads; ++i) {\n    auto save_func = [kNumThreads, kNumIds, &table, &write_fn](int i) {\n      EmbeddingHashTableInterface::DumpShard shard{i, kNumThreads};\n      table.Save(shard, write_fn);\n      return true;\n    };\n    save_threads.push_back(std::make_unique<std::thread>(save_func, i));\n  }\n  for (int i = 0; i < kNumThreads; ++i) {\n    save_threads[i]->join();\n  }\n  ASSERT_THAT(dumps.size(), kNumIds);\n\n  // Restore\n  auto p = GetParam();\n  auto table2 =\n      EmbeddingHashTableHelper(NewEmbeddingHashTableFromConfig(std::get<0>(p)));\n  std::vector<std::unique_ptr<std::thread>> restore_threads;\n  for (int i = 0; i < kNumThreads; ++i) {\n    auto restore_fn = [&table2, &dumps, kNumThreads, kNumIds](int i) {\n      const int kPerThreadIds = kNumIds / kNumThreads;\n      int idx = i * kPerThreadIds;\n      int end_idx = (i + 1) * kPerThreadIds;\n      auto get_fn = [&dumps, &idx, &end_idx](EntryDump* dump, int64_t*) {\n        if (idx == end_idx) return false;\n        *dump = dumps[idx++];\n        return true;\n      };\n      table2.Restore({i, kNumThreads}, get_fn);\n    };\n    restore_threads.push_back(std::make_unique<std::thread>(restore_fn, i));\n  }\n  for (int i = 0; i < kNumThreads; ++i) {\n    restore_threads[i]->join();\n  }\n\n  for (int64_t i = 0; i < kNumIds; ++i) {\n    std::vector<float> nums(1);\n    table2.Lookup(i, absl::MakeSpan(nums));\n    ASSERT_THAT(nums[0], i % 2);\n    if (i % 2 == 0) {\n      EXPECT_FALSE(table2.Contains(i));\n    } else {\n      EXPECT_TRUE(table2.Contains(i));\n    }\n  }\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_EMBEDDING_HASH_TABLE_TEST_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/entry_accessor.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/entry_accessor.h\"\n\n#include <cstdint>\n#include <exception>\n\n#include \"absl/algorithm/container.h\"\n#include \"absl/base/attributes.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/time/clock.h\"\n#include \"absl/time/time.h\"\n#include \"monolith/native_training/runtime/hash_table/compressor/float_compressor.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_combination.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_config.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_factory.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_combination.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_factory.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/fake_quant_retriever.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/hash_net_retriever.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/raw_retriever.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_combination.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nnamespace proto2 = google::protobuf;\n\nclass ServingEntryAccessor final : public EntryAccessorInterface {\n public:\n  explicit ServingEntryAccessor(\n      std::unique_ptr<FloatCompressorInterface> compressor)\n      : compressor_(std::move(compressor)),\n        size_bytes_(compressor_->SizeBytes()),\n        uncompressed_size_bytes_(compressor_->UncompressedSizeBytes()) {}\n\n  int64_t SizeBytes() const override { return size_bytes_; }\n\n  int64_t UncompressedSizeBytes() const override {\n    return uncompressed_size_bytes_;\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\n        R\"({\"compressor\": \"%s\", \"size_bytes\": %ld, \"uncompressed_size_bytes\": %ld})\",\n        compressor_->DebugString(), SizeBytes(), UncompressedSizeBytes());\n  }\n\n  int DimSize() const override { return compressor_->DimSize(); }\n\n  int SliceSize() const override {\n    throw std::runtime_error(\"ServingEntryAccessor doesn't support SliceSize\");\n  }\n\n  void Init(void* ctx) const override {\n    // No need to initialize serving entry\n  }\n\n  void Fill(const void* ctx, absl::Span<float> num) const override {\n    compressor_->Decode(ctx, num);\n  }\n\n  void Assign(absl::Span<const float> num, void* ctx) const override {\n    compressor_->Encode(num, ctx);\n  }\n\n  void AssignAdd(absl::Span<const float> num, void* ctx) const override {\n    std::vector<float> embedding(num.size());\n    compressor_->Decode(ctx, absl::MakeSpan(embedding));\n    for (int i = 0; i < num.size(); ++i) {\n      embedding[i] += num[i];\n    }\n    compressor_->Encode(embedding, ctx);\n  }\n\n  void Optimize(void* ctx, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    throw std::runtime_error(\"ServingEntryAccessor doesn't support Optimize\");\n  }\n\n  EntryDump Save(const void* ctx, uint32_t timestamp_sec) const override {\n    throw std::runtime_error(\"ServingEntryAccessor doesn't support Save\");\n  }\n\n  void Restore(void* ctx, uint32_t* timestamp_sec, EntryDump dump) const {\n    (void)timestamp_sec;\n    std::vector<float> num(dump.num_size());\n    absl::c_copy(dump.num(), num.begin());\n    compressor_->Encode(num, ctx);\n  }\n\n private:\n  std::unique_ptr<FloatCompressorInterface> compressor_;\n  int size_bytes_;\n  int uncompressed_size_bytes_;\n};\n\n// The layout of ctx is:\n// float * dim_size_ | Info | optimizer_data\nclass EntryAccessor final : public EntryAccessorInterface {\n public:\n  EntryAccessor(std::unique_ptr<InitializerInterface> initializer,\n                std::unique_ptr<OptimizerInterface> optimizer,\n                std::unique_ptr<RetrieverInterface> retriever)\n      : initializer_(std::move(initializer)),\n        optimizer_(std::move(optimizer)),\n        retriever_(std::move(retriever)),\n        optimizer_bytes_(optimizer_->SizeBytes()),\n        uncompressed_optimizer_bytes_(optimizer_->UncompressedSizeBytes()),\n        dim_size_(initializer_->DimSize()),\n        slice_size_(optimizer_->SliceSize()),\n        num_bytes_(retriever_->SizeBytes()) {\n    if (initializer_->DimSize() != optimizer_->DimSize() ||\n        initializer_->DimSize() != retriever_->DimSize()) {\n      throw std::invalid_argument(\n          absl::StrFormat(\"Initializer/Optimizer/Retriever dim size should \"\n                          \"match. But got %d vs %d vs %d\",\n                          initializer_->DimSize(), optimizer_->DimSize(),\n                          retriever_->DimSize()));\n    }\n  }\n\n  EntryAccessor(EntryAccessor&&) = default;\n  EntryAccessor& operator=(EntryAccessor&&) = default;\n\n  int64_t SizeBytes() const override { return num_bytes_ + optimizer_bytes_; }\n\n  int64_t UncompressedSizeBytes() const override {\n    return num_bytes_ + uncompressed_optimizer_bytes_;\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\n        R\"({\"initializer\": \"%s\", \"optimizer\": \"%s\", \"retriever\": \"%s\"})\",\n        initializer_->DebugString(), optimizer_->DebugString(),\n        retriever_->DebugString());\n  }\n\n  int DimSize() const override { return dim_size_; }\n\n  int SliceSize() const override { return slice_size_; }\n\n  absl::Span<float> GetMutableNum(void* ctx) const {\n    float* ctx_float = static_cast<float*>(ctx);\n    return absl::MakeSpan(ctx_float, ctx_float + dim_size_);\n  }\n\n  void Init(void* ctx) const override {\n    auto num_span = GetMutableNum(ctx);\n    initializer_->Initialize(num_span);\n    optimizer_->Init(GetMutableOptimizerCtx(ctx));\n  }\n\n  void Fill(const void* ctx, absl::Span<float> num) const override {\n    retriever_->Retrieve(ctx, num);\n  }\n\n  void Assign(absl::Span<const float> num, void* ctx) const override {\n    auto ctx_float = static_cast<float*>(ctx);\n    auto embedding = absl::MakeSpan(ctx_float, ctx_float + dim_size_);\n    std::memcpy(embedding.data(), num.data(), sizeof(float) * num.size());\n  }\n\n  void AssignAdd(absl::Span<const float> num, void* ctx) const override {\n    auto ctx_float = static_cast<float*>(ctx);\n    auto embedding = absl::MakeSpan(ctx_float, ctx_float + dim_size_);\n    for (int i = 0; i < num.size(); ++i) {\n      embedding[i] += num[i];\n    }\n  }\n\n  void Optimize(void* ctx, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    auto* mutable_grad = const_cast<float*>(grad.data());\n    retriever_->Backward(GetNum(ctx), absl::MakeSpan(mutable_grad, grad.size()),\n                         global_step);\n    optimizer_->Optimize(GetMutableOptimizerCtx(ctx), GetMutableNum(ctx), grad,\n                         learning_rates, global_step);\n  }\n\n  EntryDump Save(const void* ctx, uint32_t timestamp) const override;\n  void Restore(void* ctx, uint32_t* timestamp_sec,\n               EntryDump dump) const override;\n\n private:\n  absl::Span<const float> GetNum(const void* ctx) const {\n    const float* ctx_float = static_cast<const float*>(ctx);\n    return absl::MakeConstSpan(ctx_float, ctx_float + dim_size_);\n  }\n\n  void* GetMutableOptimizerCtx(void* ctx) const {\n    return AddOffset(ctx, num_bytes_);\n  }\n\n  const void* GetOptimizerCtx(const void* ctx) const {\n    return AddOffset(ctx, num_bytes_);\n  }\n\n  std::unique_ptr<InitializerInterface> initializer_;\n  std::unique_ptr<OptimizerInterface> optimizer_;\n  std::unique_ptr<RetrieverInterface> retriever_;\n  const int64_t optimizer_bytes_ = 0;\n  const int64_t uncompressed_optimizer_bytes_ = 0;\n  const int dim_size_ = 0;\n  const int slice_size_ = 0;\n  const int64_t num_bytes_ = 0;\n};\n\nEntryDump EntryAccessor::Save(const void* ctx, uint32_t timestamp_sec) const {\n  EntryDump dump;\n  absl::c_copy(GetNum(ctx),\n               proto2::RepeatedFieldBackInserter(dump.mutable_num()));\n  *dump.mutable_opt() = optimizer_->Save(GetOptimizerCtx(ctx));\n  dump.set_last_update_ts_sec(timestamp_sec);\n  return dump;\n}\n\nvoid EntryAccessor::Restore(void* ctx, uint32_t* timestamp_sec,\n                            EntryDump dump) const {\n  auto num = GetMutableNum(ctx);\n  for (int i = 0; i < dump.num_size(); ++i) {\n    num[i] = dump.num(i);\n  }\n\n  *timestamp_sec = dump.last_update_ts_sec();\n  optimizer_->Restore(GetMutableOptimizerCtx(ctx),\n                      std::move(*dump.mutable_opt()));\n}\n\n// Write dim_size into sub field of T (T can be OptimizerConfig,\n// InitializerConfig or FloatCompressorConfig).\ntemplate <class T>\nvoid WriteDimSize(T* conf, int dim_size) {\n  const proto2::Descriptor* descriptor = conf->GetDescriptor();\n  const proto2::Reflection* reflection = conf->GetReflection();\n  const proto2::OneofDescriptor* type = descriptor->FindOneofByName(\"type\");\n  const proto2::FieldDescriptor* type_field =\n      reflection->GetOneofFieldDescriptor(*conf, type);\n  if (type_field == nullptr ||\n      type_field->type() != proto2::FieldDescriptor::TYPE_MESSAGE) {\n    throw std::invalid_argument(absl::StrFormat(\"%s must be set type. Got %s\",\n                                                descriptor->name(),\n                                                conf->ShortDebugString()));\n  }\n  proto2::Message* type_msg = reflection->MutableMessage(conf, type_field);\n  const proto2::FieldDescriptor* dim_size_field =\n      type_msg->GetDescriptor()->FindFieldByName(\"dim_size\");\n  type_msg->GetReflection()->SetInt32(type_msg, dim_size_field, dim_size);\n}\n\nstruct Objects {\n  std::unique_ptr<InitializerInterface> init;\n  std::unique_ptr<OptimizerInterface> opt;\n  std::unique_ptr<FloatCompressorInterface> comp;\n  std::unique_ptr<RetrieverInterface> retriever;\n};\n\ntemplate <class T, class F>\nvoid AssignOrCombine(T* t1, T t2, F combine_fn) {\n  if (*t1 == nullptr) {\n    *t1 = std::move(t2);\n    return;\n  }\n  *t1 = combine_fn(std::move(*t1), std::move(t2));\n}\n\nObjects GenerateObjFromSegments(\n    proto2::RepeatedPtrField<EntryConfig::Segment>* segments) {\n  Objects obj;\n  for (EntryConfig::Segment& segment : *segments) {\n    if (segment.has_comp_config() && segment.comp_config().has_fixed_r8()) {\n      auto retriever = NewFakeQuantRetriever(\n          segment.dim_size(),\n          FakeQuantizer(segment.comp_config().fixed_r8().r()));\n      AssignOrCombine(&obj.retriever, std::move(retriever), CombineRetrievers);\n    } else if (segment.has_comp_config() &&\n               segment.comp_config().has_one_bit()) {\n      auto hash_net_quantizer =\n          std::make_unique<HashNetQuantizer>(segment.comp_config().one_bit());\n      auto retriever = NewHashNetRetriever(segment.dim_size(),\n                                           std::move(hash_net_quantizer));\n      AssignOrCombine(&obj.retriever, std::move(retriever), CombineRetrievers);\n    } else {\n      auto retriever = NewRawRetriever(segment.dim_size());\n      AssignOrCombine(&obj.retriever, std::move(retriever), CombineRetrievers);\n    }\n    if (segment.has_opt_config()) {\n      WriteDimSize(segment.mutable_opt_config(), segment.dim_size());\n      auto new_opt = NewOptimizerFromConfig(segment.opt_config());\n      AssignOrCombine(&obj.opt, std::move(new_opt), CombineOptimizers);\n    }\n    if (segment.has_init_config()) {\n      WriteDimSize(segment.mutable_init_config(), segment.dim_size());\n      auto new_init = NewInitializerFromConfig(segment.init_config());\n      AssignOrCombine(&obj.init, std::move(new_init), CombineInitializers);\n    }\n    if (segment.has_comp_config()) {\n      WriteDimSize(segment.mutable_comp_config(), segment.dim_size());\n      auto new_comp = NewFloatCompressor(segment.comp_config());\n      AssignOrCombine(&obj.comp, std::move(new_comp), CombineFloatCompressor);\n    }\n  }\n  return obj;\n}\n\n}  // namespace\n\nstd::unique_ptr<EntryAccessorInterface> NewEntryAccessor(EntryConfig config) {\n  Objects obj = GenerateObjFromSegments(config.mutable_segments());\n  switch (config.entry_type()) {\n    case EntryConfig::TRAINING:\n      if (obj.init == nullptr || obj.opt == nullptr) {\n        throw std::invalid_argument(absl::StrFormat(\n            \"init or opt config is missing from entry config : %s\",\n            config.ShortDebugString()));\n      }\n\n      return std::make_unique<EntryAccessor>(\n          std::move(obj.init), std::move(obj.opt), std::move(obj.retriever));\n    case EntryConfig::SERVING:\n      if (obj.comp == nullptr) {\n        throw std::invalid_argument(\n            absl::StrFormat(\"comp config is missing form entry config: %s\",\n                            config.ShortDebugString()));\n      }\n      return std::make_unique<ServingEntryAccessor>(std::move(obj.comp));\n    default:\n      throw std::invalid_argument(\n          absl::StrFormat(\"Unknown entry type: %s\", config.ShortDebugString()));\n  }\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/entry_accessor.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_ENTRY_ACCESSOR\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_ENTRY_ACCESSOR\n\n#include <cassert>\n#include <cmath>\n#include <cstdint>\n#include <memory>\n\n#include \"absl/algorithm/container.h\"\n#include \"absl/types/span.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_interface.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n#include \"monolith/native_training/runtime/hash_table/utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nclass EntryAccessorInterface {\n public:\n  virtual ~EntryAccessorInterface() = default;\n\n  // Size bytes need to be allocated in this entry.\n  virtual int64_t SizeBytes() const = 0;\n\n  // Size bytes need to be allocated in this entry if not compressed.\n  virtual int64_t UncompressedSizeBytes() const = 0;\n\n  virtual std::string DebugString() const = 0;\n\n  // The dim that this entry accessor can support\n  virtual int DimSize() const = 0;\n\n  // The number of slices in this entry.\n  virtual int SliceSize() const = 0;\n\n  // Initialize the given entry.\n  virtual void Init(void* ctx) const = 0;\n\n  // Fills the num based on entry.\n  virtual void Fill(const void* ctx, absl::Span<float> num) const = 0;\n\n  // Assign the entry using num\n  virtual void Assign(absl::Span<const float> num, void* ctx) const = 0;\n\n  // AssignAdd the entry using num\n  virtual void AssignAdd(absl::Span<const float> num, void* ctx) const = 0;\n\n  // Optimizes the entry with |grad|.\n  virtual void Optimize(void* ctx, absl::Span<const float> grad,\n                        absl::Span<const float> learning_rates,\n                        const int64_t global_step = 0) const = 0;\n\n  // Converts an entry to EntryDump.\n  virtual EntryDump Save(const void* ctx, uint32_t timestamp_sec) const = 0;\n\n  // Restores the entry from |dump|.\n  virtual void Restore(void* ctx, uint32_t* timestamp_sec,\n                       EntryDump dump) const = 0;\n};\n\nstd::unique_ptr<EntryAccessorInterface> NewEntryAccessor(EntryConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_ENTRY_ACCESSOR\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/entry_accessor_decorator.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_ENTRY_ACCESSOR_DECORATOR_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_ENTRY_ACCESSOR_DECORATOR_H_\n\n#include \"monolith/native_training/runtime/hash_table/entry_accessor.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\n// DEPRECATED: Prefer using retriever\n//\n// The base class of decorator. By default, it delegates all requests to the\n// base entry accessor.\nclass EntryAccessorDecorator : public EntryAccessorInterface {\n public:\n  explicit EntryAccessorDecorator(\n      std::unique_ptr<EntryAccessorInterface> entry_accessor)\n      : entry_accessor_(std::move(entry_accessor)) {}\n\n  int64_t SizeBytes() const override { return entry_accessor_->SizeBytes(); }\n\n  int64_t UncompressedSizeBytes() const override {\n    return entry_accessor_->UncompressedSizeBytes();\n  }\n\n  std::string DebugString() const override {\n    return entry_accessor_->DebugString();\n  }\n\n  int DimSize() const override { return entry_accessor_->DimSize(); }\n\n  int SliceSize() const override { return entry_accessor_->SliceSize(); }\n\n  void Init(void* ctx) const override { entry_accessor_->Init(ctx); }\n\n  void Fill(const void* ctx, absl::Span<float> num) const override {\n    entry_accessor_->Fill(ctx, num);\n  }\n\n  void Assign(absl::Span<const float> num, void* ctx) const override {\n    entry_accessor_->Assign(num, ctx);\n  }\n\n  void AssignAdd(absl::Span<const float> num, void* ctx) const override {\n    entry_accessor_->AssignAdd(num, ctx);\n  }\n\n  void Optimize(void* ctx, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    entry_accessor_->Optimize(ctx, grad, learning_rates, global_step);\n  }\n\n  EntryDump Save(const void* ctx, uint32_t timestamp_sec) const override {\n    return entry_accessor_->Save(ctx, timestamp_sec);\n  }\n\n  void Restore(void* ctx, uint32_t* timestamp_sec,\n               EntryDump dump) const override {\n    entry_accessor_->Restore(ctx, timestamp_sec, dump);\n  }\n\n protected:\n  std::unique_ptr<EntryAccessorInterface> entry_accessor_;\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_ENTRY_ACCESSOR_DECORATOR_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/entry_accessor_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/entry_accessor.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"google/protobuf/text_format.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nnamespace proto2 = google::protobuf;\n\nusing ::testing::ElementsAre;\nusing ::testing::FloatEq;\nusing ::testing::FloatNear;\n\nTEST(EntryAccessorTest, FromConfig) {\n  EntryConfig config;\n  ASSERT_TRUE(proto2::TextFormat::ParseFromString(R\"(\n    segments {\n      dim_size: 1\n      init_config { zeros {} }\n      opt_config { sgd {} }\n    }\n    segments {\n      dim_size: 2\n      init_config { zeros {} }\n      opt_config { sgd {} }\n    }\n  )\",\n                                                  &config));\n  auto accessor = NewEntryAccessor(config);\n  auto entry = std::make_unique<char[]>(accessor->SizeBytes());\n  accessor->Init(entry.get());\n  accessor->Optimize(entry.get(), {1.0f, 2.0f, 3.0f}, {1.0f, 2.0f}, 0);\n  std::vector<float> num(3);\n  accessor->Fill(entry.get(), absl::MakeSpan(num));\n  EXPECT_THAT(absl::MakeSpan(num), ElementsAre(-1.0f, -4.0f, -6.0f));\n}\n\nTEST(EntryAccessorTest, SaveRestore) {\n  EntryConfig config;\n  ASSERT_TRUE(proto2::TextFormat::ParseFromString(R\"(\n    segments {\n      dim_size: 1\n      init_config { zeros {} }\n      opt_config { adagrad { initial_accumulator_value: 0.1\n      } }\n    }\n  )\",\n                                                  &config));\n  auto accessor = NewEntryAccessor(config);\n  auto entry1 = std::make_unique<char[]>(accessor->SizeBytes());\n  accessor->Init(entry1.get());\n  accessor->Optimize(entry1.get(), {1.0f}, {1.0f});\n  std::vector<float> num(1);\n  accessor->Fill(entry1.get(), absl::MakeSpan(num));\n  ASSERT_THAT(absl::MakeSpan(num), ElementsAre(FloatEq(-0.95346254f)));\n  EntryDump dump = accessor->Save(entry1.get(), 100);\n  auto entry2 = std::make_unique<char[]>(accessor->SizeBytes());\n  uint32_t timestamp_sec;\n  accessor->Restore(entry2.get(), &timestamp_sec, dump);\n  EXPECT_EQ(timestamp_sec, 100);\n  accessor->Optimize(entry2.get(), {1.0f}, {1.0f}, 0);\n  accessor->Fill(entry2.get(), absl::MakeSpan(num));\n  ASSERT_THAT(absl::MakeSpan(num), ElementsAre(FloatEq(-1.643528f)));\n}\n\nTEST(EntryAccessorTest, Update) {\n  std::unordered_map<std::string, std::string> configs = {{\"fp32\", R\"(\n        segments {\n          dim_size: 3\n          init_config { zeros {} }\n          opt_config { sgd {} }\n        }\n      )\"},\n                                                          {\"fp16\", R\"(\n        segments {\n          dim_size: 3\n          init_config { zeros {} }\n          opt_config { sgd {} }\n        }\n      )\"}};\n\n  for (const auto& kv : configs) {\n    EntryConfig config;\n    ASSERT_TRUE(proto2::TextFormat::ParseFromString(kv.second, &config));\n    auto accessor = NewEntryAccessor(config);\n    auto entry = std::make_unique<char[]>(accessor->SizeBytes());\n\n    std::vector<float> num = {0.1, 0.2, 0.3};\n    accessor->Assign(absl::MakeSpan(num), entry.get());\n    std::vector<float> embedding(3);\n    accessor->Fill(entry.get(), absl::MakeSpan(embedding));\n\n    if (kv.first == \"fp32\") {\n      EXPECT_THAT(embedding, ElementsAre(0.1, 0.2, 0.3));\n    }\n    if (kv.first == \"fp16\") {\n      float eps = 0.0001;\n      EXPECT_THAT(embedding,\n                  ElementsAre(FloatNear(0.1, eps), FloatNear(0.2, eps),\n                              FloatNear(0.3, eps)));\n    }\n  }\n}\n\nTEST(ServingEntryAccessorTest, Basic) {\n  EntryConfig config;\n  ASSERT_TRUE(proto2::TextFormat::ParseFromString(R\"(\n    segments {\n      dim_size: 1\n      comp_config { fp32 {} }\n    }\n    entry_type: SERVING\n  )\",\n                                                  &config));\n  auto accessor = NewEntryAccessor(config);\n  auto entry = std::make_unique<char[]>(accessor->SizeBytes());\n  EntryDump dump;\n  dump.add_num(1.0);\n  dump.set_last_update_ts_sec(100);\n  uint32_t timestamp_sec;\n  accessor->Restore(entry.get(), &timestamp_sec, dump);\n  EXPECT_THAT(timestamp_sec, timestamp_sec);\n  std::vector<float> out(1);\n  accessor->Fill(entry.get(), absl::MakeSpan(out));\n  EXPECT_THAT(out, ElementsAre(1.0));\n}\n\nTEST(ServingEntryAccessorTest, Update) {\n  std::unordered_map<std::string, std::string> configs = {{\"fp32\", R\"(\n        segments {\n          dim_size: 3\n          comp_config { fp32 {} }\n        }\n        entry_type: SERVING\n      )\"},\n                                                          {\"fp16\", R\"(\n        segments {\n          dim_size: 3\n          comp_config { fp16 {} }\n        }\n        entry_type: SERVING\n      )\"}};\n\n  for (const auto& kv : configs) {\n    EntryConfig config;\n    ASSERT_TRUE(proto2::TextFormat::ParseFromString(kv.second, &config));\n    auto accessor = NewEntryAccessor(config);\n    auto entry = std::make_unique<char[]>(accessor->SizeBytes());\n\n    std::vector<float> num = {0.1, 0.2, 0.3};\n    accessor->Assign(absl::MakeSpan(num), entry.get());\n    std::vector<float> embedding(3);\n    accessor->Fill(entry.get(), absl::MakeSpan(embedding));\n\n    if (kv.first == \"fp32\") {\n      EXPECT_THAT(embedding, ElementsAre(0.1, 0.2, 0.3));\n    }\n    if (kv.first == \"fp16\") {\n      float eps = 0.0001;\n      EXPECT_THAT(embedding,\n                  ElementsAre(FloatNear(0.1, eps), FloatNear(0.2, eps),\n                              FloatNear(0.3, eps)));\n    }\n  }\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/entry_defs.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/allocator/block_allocator.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\n// A wrapper for raw pointer. This helps utilize try_emplace in map.\n// TODO(leqi.zou): Essentailly we want to deprecate this. Will remove once\n// we find this is not useful.\n\nclass PackedEntry {\n public:\n  explicit PackedEntry(allocator::TSEmbeddingBlockAllocator* alloc)\n      : p_(alloc->AllocateOne()), timestamp_(0) {}\n\n  allocator::EntryAddress get_entry_addr() const { return p_; }\n\n  uint32_t GetTimestamp() const { return timestamp_; }\n\n  void SetTimestamp(uint32_t timestamp_sec) { timestamp_ = timestamp_sec; }\n\n private:\n  allocator::EntryAddress p_;\n\n  // Unix timestamp in seconds, UINT32_MAX means 2106-02-07 14:28:15+08:00\n  uint32_t timestamp_;\n};\n\nclass RawEntry {\n public:\n  RawEntry(size_t entry_size) : p_(new char[entry_size]) {}\n\n  void* get() const { return p_.get(); }\n\n  uint32_t GetTimestamp() const { return timestamp_; }\n\n  void SetTimestamp(uint32_t timestamp_sec) { timestamp_ = timestamp_sec; }\n\n private:\n  std::unique_ptr<char[]> p_;\n  // Unix timestamp in seconds, UINT32_MAX means 2106-02-07 14:28:15+08:00\n  uint32_t timestamp_;\n};\n\ntemplate <int64_t length>\nclass InlineEntry {\n public:\n  static_assert(length % 8 == 0 && length > 0,\n                \"InlineEntry's should be divisible by 8.\");\n\n  InlineEntry() {\n    static_assert(sizeof(InlineEntry<length>) == length,\n                  \"InlineEntry's implementation is wrong\");\n  }\n  static int capacity() { return length - 4; }\n  const void* get() const { return buffer_; }\n  void* get() { return buffer_; }\n\n  uint32_t GetTimestamp() const {\n    return *reinterpret_cast<const uint32_t*>(buffer_ + length - 4);\n  }\n\n  void SetTimestamp(uint32_t timestamp_sec) {\n    *reinterpret_cast<uint32_t*>(buffer_ + length - 4) = timestamp_sec;\n  }\n\n private:\n  char buffer_[length];\n};\n\n}  // namespace hash_table\n}  // namespace monolith"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/entry_defs_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/entry_defs.h\"\n\n#include \"gtest/gtest.h\"\n\n#include \"gmock/gmock.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nTEST(InlineEntryTest, Basic) {\n  InlineEntry<8> entry;\n  *reinterpret_cast<float*>(entry.get()) = 1.0;\n  EXPECT_THAT(entry.capacity(), 4);\n  entry.SetTimestamp(1234);\n  EXPECT_THAT(entry.GetTimestamp(), 1234);\n  EXPECT_THAT(*reinterpret_cast<float*>(entry.get()), 1.0);\n}\n\n}  // namespace hash_table\n}  // namespace monolith"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/hash_table_benchmark.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n//\n// Created by david on 2020-11-27.\n//\n\n#include <unordered_map>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/random/random.h\"\n#include \"absl/strings/str_format.h\"\n#include \"benchmark/benchmark.h\"\n#include \"glog/logging.h\"\n#include \"google/protobuf/text_format.h\"\n#include \"monolith/native_training/runtime/concurrency/thread_pool.h\"\n#include \"monolith/native_training/runtime/hash_table/cuckoohash/cuckoo_embedding_hash_table.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_factory.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nnamespace proto2 = ::google::protobuf;\n\nEmbeddingHashTableConfig SetupHashTableConfig(size_t dim) {\n  EmbeddingHashTableConfig config;\n  CHECK(proto2::TextFormat::ParseFromString(absl::StrFormat(R\"(\n    entry_config {\n      segments {\n        dim_size: %lu\n        init_config { zeros {} }\n        opt_config { sgd {} }\n      }\n    }\n    cuckoo {}\n  )\",\n                                                            dim),\n                                            &config));\n  return config;\n}\n\nvoid BM_Insert(benchmark::State& state) {  // NOLINT\n  auto entry_num = state.range(0);\n  auto thread_num = state.range(1);\n  auto dim = state.range(2);\n  monolith::concurrency::ThreadPool thread_pool(thread_num);\n  auto config = SetupHashTableConfig(dim);\n  auto table = NewEmbeddingHashTableFromConfig(config);\n\n  std::vector<float> vector(dim);\n  absl::BitGen bit_gen;\n  for (auto& val : vector) {\n    val = absl::Uniform<float>(bit_gen, -1.f, 1.f);\n  }\n\n  for (auto _ : state) {\n    std::atomic_int join(thread_num);\n    auto AssignAdd = [&]() {\n      for (size_t i = 0; i < entry_num; ++i) {\n        table->AssignAdd(i, absl::MakeSpan(vector), 0);\n      }\n      --join;\n    };\n\n    for (int64_t i = 0; i < thread_num; ++i) {\n      thread_pool.Schedule(AssignAdd);\n    }\n\n    while (join) {\n    }\n  }\n}\n\nvoid BM_Find(benchmark::State& state) {  // NOLINT\n  auto entry_num = state.range(0);\n  auto thread_num = state.range(1);\n  auto dim = state.range(2);\n  monolith::concurrency::ThreadPool thread_pool(thread_num);\n  auto config = SetupHashTableConfig(dim);\n  auto table = NewEmbeddingHashTableFromConfig(config);\n\n  std::vector<float> vector(dim);\n  absl::BitGen bit_gen;\n  for (auto& val : vector) {\n    val = absl::Uniform<float>(bit_gen, -1.f, 1.f);\n  }\n  for (size_t i = 0; i < entry_num; ++i) {\n    table->AssignAdd(i, absl::MakeSpan(vector), 0);\n  }\n\n  std::vector<int64_t> ids_to_find(entry_num / thread_num);\n  for (size_t i = 0; i < ids_to_find.size(); ++i) {\n    ids_to_find[i] = absl::Uniform<int64_t>(bit_gen, 0, 2 * entry_num);\n  }\n\n  for (auto _ : state) {\n    std::atomic_int join(thread_num);\n    auto Lookup = [&]() {\n      std::vector<float> vector(dim);\n      for (int64_t id : ids_to_find) {\n        table->Lookup(id, absl::MakeSpan(vector));\n      }\n      --join;\n    };\n\n    for (int64_t i = 0; i < thread_num; ++i) {\n      thread_pool.Schedule(Lookup);\n    }\n\n    while (join) {\n    }\n  }\n}\n\n/*\n  Run on (12 X 2592 MHz CPU s)\n  CPU Caches:\n    L1 Data 32 KiB (x12)\n    L1 Instruction 32 KiB (x12)\n    L2 Unified 256 KiB (x12)\n    L3 Unified 12288 KiB (x1)\n  Load Average: 1.74, 1.35, 0.68\n  ------------------------------------------------------------------\n  Benchmark                        Time             CPU   Iterations\n  ------------------------------------------------------------------\n  BM_Insert/10000000/1/32 7834986861 ns   7833447327 ns            1\n  BM_Insert/1000000/10/32 5021482710 ns   5019782037 ns            1\n  BM_Find/10000000/1/32   6015355836 ns   6015255039 ns            1\n  BM_Find/10000000/10/32  4677797500 ns   4677690867 ns            1\n*/\n\n// single thread, insert 10^7 entries\nBENCHMARK(BM_Insert)->Args({1000 * 10000, 1, 32});\n\n// 10 threads, insert 10^7 = 10^6 * 10 entries\nBENCHMARK(BM_Insert)->Args({100 * 10000, 10, 32});\n\n// single thread, find 10^7 times from 10^7 entries\nBENCHMARK(BM_Find)->Args({1000 * 10000, 1, 32});\n\n// 10 threads, find 10^7 = 10^6 * 10 times from 10^7 entries\nBENCHMARK(BM_Find)->Args({1000 * 10000, 10, 32});\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n\nBENCHMARK_MAIN();\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_proto_library\", \"cc_test\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime/hash_table:__subpackages__\"])\n\ncc_library(\n    name = \"initializer_interface\",\n    hdrs = [\"initializer_interface.h\"],\n    deps = [\n        \"@com_google_absl//absl/status\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\nproto_library(\n    name = \"initializer_config_proto\",\n    srcs = [\"initializer_config.proto\"],\n)\n\ncc_proto_library(\n    name = \"initializer_config_cc_proto\",\n    deps = [\":initializer_config_proto\"],\n)\n\npy_proto_library(\n    name = \"initializer_config_py_proto\",\n    srcs = [\"initializer_config.proto\"],\n    srcs_version = \"PY2AND3\",\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"initializer_factory\",\n    srcs = [\"initializer_factory.cc\"],\n    hdrs = [\"initializer_factory.h\"],\n    deps = [\n        \"random_uniform_initializer\",\n        \":constants_initializer\",\n        \":initializer_config_cc_proto\",\n        \":initializer_interface\",\n    ],\n)\n\ncc_library(\n    name = \"constants_initializer\",\n    srcs = [\"constants_initializer.cc\"],\n    hdrs = [\"constants_initializer.h\"],\n    deps = [\n        \":initializer_config_cc_proto\",\n        \":initializer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_library(\n    name = \"random_uniform_initializer\",\n    srcs = [\"random_uniform_initializer.cc\"],\n    hdrs = [\"random_uniform_initializer.h\"],\n    deps = [\n        \":initializer_config_cc_proto\",\n        \":initializer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"random_uniform_initializer_test\",\n    srcs = [\"random_uniform_initializer_test.cc\"],\n    deps = [\n        \":initializer_config_cc_proto\",\n        \":initializer_interface\",\n        \":random_uniform_initializer\",\n        \"@com_google_absl//absl/types:span\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"initializer_combination\",\n    srcs = [\"initializer_combination.cc\"],\n    hdrs = [\"initializer_combination.h\"],\n    deps = [\n        \":initializer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"initializer_combination_test\",\n    srcs = [\"initializer_combination_test.cc\"],\n    deps = [\n        \":constants_initializer\",\n        \":initializer_combination\",\n        \":initializer_interface\",\n        \"@com_google_absl//absl/types:span\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/constants_initializer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstring>\n\n#include \"monolith/native_training/runtime/hash_table/initializer/constants_initializer.h\"\n\n#include \"absl/strings/str_format.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass ConstantsInitializer : public InitializerInterface {\n public:\n  explicit ConstantsInitializer(int dim_size, float constant)\n      : dim_size_(dim_size), constant_(constant) {}\n\n  int DimSize() const override { return dim_size_; }\n\n  void Initialize(absl::Span<float> nums) const override {\n    for (int i = 0; i < dim_size_; ++i) {\n      nums[i] = constant_;\n    }\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Constants(D=%d, C=%f)\", dim_size_, constant_);\n  }\n\n private:\n  int dim_size_;\n  float constant_;\n};\n\n}  // namespace\n\nstd::unique_ptr<InitializerInterface> NewZerosInitializer(\n    ZerosInitializerConfig config) {\n  return std::make_unique<ConstantsInitializer>(config.dim_size(), 0);\n}\n\nstd::unique_ptr<InitializerInterface> NewZerosInitializer(int dim_size) {\n  return std::make_unique<ConstantsInitializer>(dim_size, 0);\n}\n\nstd::unique_ptr<InitializerInterface> NewOnesInitializer(\n    OnesInitializerConfig config) {\n  return std::make_unique<ConstantsInitializer>(config.dim_size(), 1);\n}\n\nstd::unique_ptr<InitializerInterface> NewConstantsInitializer(\n    ConstantsInitializerConfig config) {\n  return std::make_unique<ConstantsInitializer>(config.dim_size(),\n                                                config.constant());\n}\n\nstd::unique_ptr<InitializerInterface> NewConstantsInitializer(int dim_size,\n                                                              float constant) {\n  return std::make_unique<ConstantsInitializer>(dim_size, constant);\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/constants_initializer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_CONSTANTS_INITIALIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_CONSTANTS_INITIALIZER\n#include <memory>\n\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_config.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<InitializerInterface> NewZerosInitializer(\n    ZerosInitializerConfig config);\n\nstd::unique_ptr<InitializerInterface> NewZerosInitializer(int dim_size);\n\nstd::unique_ptr<InitializerInterface> NewOnesInitializer(\n    OnesInitializerConfig config);\n\nstd::unique_ptr<InitializerInterface> NewConstantsInitializer(\n    ConstantsInitializerConfig config);\n\nstd::unique_ptr<InitializerInterface> NewConstantsInitializer(int dim_size,\n                                                              float constant);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_CONSTANTS_INITIALIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/initializer_combination.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_combination.h\"\n\n#include \"absl/strings/str_format.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass CombinedInitializer : public InitializerInterface {\n public:\n  CombinedInitializer(std::unique_ptr<InitializerInterface> init1,\n                      std::unique_ptr<InitializerInterface> init2)\n      : init1_(std::move(init1)), init2_(std::move(init2)) {}\n\n  int DimSize() const override { return init1_->DimSize() + init2_->DimSize(); }\n\n  void Initialize(absl::Span<float> nums) const override {\n    init1_->Initialize(nums);\n    init2_->Initialize(nums.subspan(init1_->DimSize()));\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"%s|%s\", init1_->DebugString(),\n                           init2_->DebugString());\n  }\n\n private:\n  std::unique_ptr<InitializerInterface> init1_;\n  std::unique_ptr<InitializerInterface> init2_;\n};\n\n}  // namespace\n\nstd::unique_ptr<InitializerInterface> CombineInitializers(\n    std::unique_ptr<InitializerInterface> init1,\n    std::unique_ptr<InitializerInterface> init2) {\n  return std::make_unique<CombinedInitializer>(std::move(init1),\n                                               std::move(init2));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/initializer_combination.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_INITIALIZER_COMBINATION\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_INITIALIZER_COMBINATION\n#include <memory>\n\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\n// A entry may be initialized by different initializers so we need to combine\n// two initializers.\nstd::unique_ptr<InitializerInterface> CombineInitializers(\n    std::unique_ptr<InitializerInterface> init1,\n    std::unique_ptr<InitializerInterface> init2);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_INITIALIZER_COMBINATION\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/initializer_combination_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_combination.h\"\n\n#include <algorithm>\n\n#include \"absl/types/span.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/constants_initializer.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::ElementsAre;\n\nTEST(RandomUniformInitializer, Basic) {\n  std::vector<float> num(3, 1);\n  auto init1 = NewConstantsInitializer(1, 3);\n  auto init2 = NewConstantsInitializer(2, 4);\n  auto combined_init = CombineInitializers(std::move(init1), std::move(init2));\n  EXPECT_THAT(combined_init->DimSize(), 3);\n  combined_init->Initialize(absl::Span<float>(num));\n  EXPECT_THAT(num, ElementsAre(3, 4, 4));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/initializer_config.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax=\"proto2\";\n\npackage monolith.hash_table;\n\nmessage ZerosInitializerConfig {\n  optional int32 dim_size = 1;\n}\n\nmessage OnesInitializerConfig {\n  optional int32 dim_size = 1;\n}\n\nmessage ConstantsInitializerConfig {\n  optional int32 dim_size = 1;\n  optional float constant = 2;\n}\n\nmessage RandomUniformInitializerConfig {\n  optional int32 dim_size = 1;\n  optional float minval = 2 [default=-0.05];\n  optional float maxval = 3 [default=0.05];\n}\n\nmessage InitializerConfig {\n  oneof type {\n    ZerosInitializerConfig zeros = 1;\n    RandomUniformInitializerConfig random_uniform = 2;\n    OnesInitializerConfig ones = 3;\n\n    ConstantsInitializerConfig constants = 15;\n  }\n}"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/initializer_factory.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_factory.h\"\n\n#include <exception>\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/constants_initializer.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/random_uniform_initializer.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<InitializerInterface> NewInitializerFromConfig(\n    InitializerConfig config) {\n  switch (config.type_case()) {\n    case InitializerConfig::kZeros:\n      return NewZerosInitializer(std::move(*config.mutable_zeros()));\n    case InitializerConfig::kRandomUniform:\n      return NewRandomUniformInitializer(\n          std::move(*config.mutable_random_uniform()));\n    case InitializerConfig::kOnes:\n      return NewOnesInitializer(std::move(*config.mutable_ones()));\n    case InitializerConfig::kConstants:\n      return NewConstantsInitializer(std::move(*config.mutable_constants()));\n    default:\n      throw std::invalid_argument(absl::StrFormat(\"Unsupported initializer: %s\",\n                                                  config.ShortDebugString()));\n  }\n}\n\n}  // namespace hash_table\n}  // namespace monolith"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/initializer_factory.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_INITIALIZER_FACTORY\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_INITIALIZER_FACTORY\n#include <memory>\n\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_config.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<InitializerInterface> NewInitializerFromConfig(\n    InitializerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_INITIALIZER_FACTORY\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/initializer_interface.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_INTERFACE\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_INTERFACE\n\n#include \"absl/types/span.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nclass InitializerInterface {\n public:\n  virtual ~InitializerInterface() = default;\n\n  virtual int DimSize() const = 0;\n\n  virtual void Initialize(absl::Span<float> nums) const = 0;\n\n  virtual std::string DebugString() const = 0;\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_INTERFACE\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/random_uniform_initializer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/initializer/random_uniform_initializer.h\"\n\n#include <random>\n#include \"absl/strings/str_format.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass RandomUniformInitializer : public InitializerInterface {\n public:\n  explicit RandomUniformInitializer(RandomUniformInitializerConfig conf)\n      : conf_(std::move(conf)) {}\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  void Initialize(absl::Span<float> nums) const override {\n    thread_local std::mt19937 generator;\n    std::uniform_real_distribution<float> distribution(conf_.minval(),\n                                                       conf_.maxval());\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      nums[i] = distribution(generator);\n    }\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"RandomUniform(D=%d, min=%f, max=%f)\", DimSize(),\n                           conf_.minval(), conf_.maxval());\n  }\n\n private:\n  RandomUniformInitializerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<InitializerInterface> NewRandomUniformInitializer(\n    RandomUniformInitializerConfig config) {\n  return std::make_unique<RandomUniformInitializer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/random_uniform_initializer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_RANDOM_UNIFORM_INITIALIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_RANDOM_UNIFORM_INITIALIZER\n#include <memory>\n\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_config.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<InitializerInterface> NewRandomUniformInitializer(\n    RandomUniformInitializerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_INITIALIZER_RANDOM_UNIFORM_INITIALIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/initializer/random_uniform_initializer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/initializer/random_uniform_initializer.h\"\n\n#include <algorithm>\n\n#include \"absl/types/span.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/initializer/initializer_config.pb.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Gt;\nusing ::testing::Lt;\n\nTEST(RandomUniformInitializer, Basic) {\n  const int kDimSize = 1000;\n  std::vector<float> num(kDimSize, 0);\n  RandomUniformInitializerConfig config;\n  config.set_dim_size(kDimSize);\n  config.set_minval(-1);\n  config.set_maxval(1);\n  auto initializer = NewRandomUniformInitializer(config);\n  initializer->Initialize(absl::Span<float>(num));\n  EXPECT_THAT(*std::max_element(num.begin(), num.end()), Gt(0.9));\n  EXPECT_THAT(*std::min_element(num.begin(), num.end()), Lt(0.9));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_proto_library\", \"cc_test\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime:__subpackages__\"])\n\nproto_library(\n    name = \"optimizer_proto\",\n    srcs = [\"optimizer.proto\"],\n)\n\ncc_proto_library(\n    name = \"optimizer_cc_proto\",\n    deps = [\":optimizer_proto\"],\n)\n\npy_proto_library(\n    name = \"optimizer_py_proto\",\n    srcs = [\"optimizer.proto\"],\n    srcs_version = \"PY2AND3\",\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"optimizer_interface\",\n    hdrs = [\"optimizer_interface.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \"@com_google_absl//absl/base\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ncc_library(\n    name = \"optimizer_decorator\",\n    hdrs = [\"optimizer_decorator.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/base\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ncc_library(\n    name = \"stochastic_rounding\",\n    srcs = [\"stochastic_rounding.cc\"],\n    hdrs = [\"stochastic_rounding.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"//third_party/half_sourceforge_net:half\",\n    ],\n)\n\ncc_test(\n    name = \"stochastic_rounding_test\",\n    srcs = [\"stochastic_rounding_test.cc\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_factory\",\n        \":stochastic_rounding\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"optimizer_factory\",\n    srcs = [\"optimizer_factory.cc\"],\n    hdrs = [\"optimizer_factory.h\"],\n    deps = [\n        \":adadelta_optimizer\",\n        \":adagrad_optimizer\",\n        \":adam_optimizer\",\n        \":amsgrad_optimizer\",\n        \":batch_softmax_optimizer\",\n        \":dynamic_wd_adagrad_optimizer\",\n        \":ftrl_optimizer\",\n        \":group_ftrl_optimizer\",\n        \":group_adagrad_optimizer\",\n        \":momentum_optimizer\",\n        \":moving_average_optimizer\",\n        \":rmsprop_optimizer\",\n        \":sgd_optimizer\",\n        \":stochastic_rounding\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_library(\n    name = \"adagrad_optimizer_internal_deps\",\n)\n\ncc_library(\n    name = \"adagrad_optimizer\",\n    srcs = [\"adagrad_optimizer.cc\"],\n    hdrs = [\"adagrad_optimizer.h\"],\n    copts = [\n        \"-D_ENABLE_AVX\",\n    ],\n    deps = [\n        \":adagrad_optimizer_internal_deps\",\n        \":avx_utils\",\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_library(\n    name = \"batch_softmax_optimizer\",\n    srcs = [\"batch_softmax_optimizer.cc\"],\n    hdrs = [\"batch_softmax_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_library(\n    name = \"dynamic_wd_adagrad_optimizer_internal_deps\",\n)\n\ncc_library(\n    name = \"dynamic_wd_adagrad_optimizer\",\n    srcs = [\"dynamic_wd_adagrad_optimizer.cc\"],\n    hdrs = [\"dynamic_wd_adagrad_optimizer.h\"],\n    copts = [\n        \"-D_ENABLE_AVX\",\n    ],\n    deps = [\n        \":dynamic_wd_adagrad_optimizer_internal_deps\",\n        \":dynamic_wd_avx_utils\",\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_library(\n    name = \"ftrl_optimizer\",\n    srcs = [\"ftrl_optimizer.cc\"],\n    hdrs = [\"ftrl_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"ftrl_optimizer_test\",\n    srcs = [\"ftrl_optimizer_test.cc\"],\n    deps = [\n        \":ftrl_optimizer\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_test(\n    name = \"adagrad_optimizer_test\",\n    srcs = [\"adagrad_optimizer_test.cc\"],\n    deps = [\n        \":adagrad_optimizer\",\n        \":avx_utils\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_test(\n    name = \"batch_softmax_optimizer_test\",\n    srcs = [\"batch_softmax_optimizer_test.cc\"],\n    deps = [\n        \":batch_softmax_optimizer\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_test(\n    name = \"dynamic_wd_adagrad_optimizer_test\",\n    srcs = [\"dynamic_wd_adagrad_optimizer_test.cc\"],\n    deps = [\n        \":dynamic_wd_adagrad_optimizer\",\n        \":dynamic_wd_avx_utils\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"sgd_optimizer\",\n    srcs = [\"sgd_optimizer.cc\"],\n    hdrs = [\"sgd_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"sgd_optimizer_test\",\n    srcs = [\"sgd_optimizer_test.cc\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":sgd_optimizer\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"adadelta_optimizer\",\n    srcs = [\"adadelta_optimizer.cc\"],\n    hdrs = [\"adadelta_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"adadelta_optimizer_test\",\n    srcs = [\"adadelta_optimizer_test.cc\"],\n    deps = [\n        \":adadelta_optimizer\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"adam_optimizer\",\n    srcs = [\"adam_optimizer.cc\"],\n    hdrs = [\"adam_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"adam_optimizer_test\",\n    srcs = [\"adam_optimizer_test.cc\"],\n    deps = [\n        \":adam_optimizer\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"amsgrad_optimizer\",\n    srcs = [\"amsgrad_optimizer.cc\"],\n    hdrs = [\"amsgrad_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"amsgrad_optimizer_test\",\n    srcs = [\"amsgrad_optimizer_test.cc\"],\n    deps = [\n        \":amsgrad_optimizer\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"momentum_optimizer\",\n    srcs = [\"momentum_optimizer.cc\"],\n    hdrs = [\"momentum_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"momentum_optimizer_test\",\n    srcs = [\"momentum_optimizer_test.cc\"],\n    deps = [\n        \":momentum_optimizer\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"moving_average_optimizer\",\n    srcs = [\"moving_average_optimizer.cc\"],\n    hdrs = [\"moving_average_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"moving_average_optimizer_test\",\n    srcs = [\"moving_average_optimizer_test.cc\"],\n    deps = [\n        \":moving_average_optimizer\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"rmsprop_optimizer\",\n    srcs = [\"rmsprop_optimizer.cc\"],\n    hdrs = [\"rmsprop_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"rmsprop_optimizer_test\",\n    srcs = [\"rmsprop_optimizer_test.cc\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":rmsprop_optimizer\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"dc_optimizer\",\n    srcs = [\"dc_optimizer.cc\"],\n    hdrs = [\"dc_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_decorator\",\n        \":optimizer_interface\",\n    ],\n)\n\ncc_test(\n    name = \"dc_optimizer_test\",\n    srcs = [\"dc_optimizer_test.cc\"],\n    deps = [\n        \":adadelta_optimizer\",\n        \":dc_optimizer\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"optimizer_combination\",\n    srcs = [\"optimizer_combination.cc\"],\n    hdrs = [\"optimizer_combination.h\"],\n    deps = [\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/algorithm:container\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"optimizer_combination_test\",\n    srcs = [\"optimizer_combination_test.cc\"],\n    deps = [\n        \":adagrad_optimizer\",\n        \":optimizer_cc_proto\",\n        \":optimizer_combination\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"test_utils\",\n    testonly = 1,\n    hdrs = [\"test_utils.h\"],\n    deps = [\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ncc_library(\n    name = \"dynamic_wd_avx_utils\",\n    hdrs = [\"dynamic_wd_avx_utils.h\"],\n    copts = [\n        \"-D_ENABLE_AVX\",\n    ],\n    deps = [\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ncc_test(\n    name = \"dynamic_wd_avx_test\",\n    testonly = 1,\n    srcs = [\"dynamic_wd_avx_test.cc\"],\n    copts = [\n        \"-D_ENABLE_AVX\",\n    ],\n    deps = [\n        \":dynamic_wd_avx_utils\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"group_ftrl_optimizer\",\n    srcs = [\"group_ftrl_optimizer.cc\"],\n    hdrs = [\"group_ftrl_optimizer.h\"],\n    deps = [\n        \":avx_utils\",\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"group_ftrl_optimizer_test\",\n    srcs = [\"group_ftrl_optimizer_test.cc\"],\n    deps = [\n        \":avx_utils\",\n        \":group_ftrl_optimizer\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"group_adagrad_optimizer\",\n    srcs = [\"group_adagrad_optimizer.cc\"],\n    hdrs = [\"group_adagrad_optimizer.h\"],\n    deps = [\n        \":optimizer_cc_proto\",\n        \":optimizer_interface\",\n        \":avx_utils\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"group_adagrad_optimizer_test\",\n    srcs = [\"group_adagrad_optimizer_test.cc\"],\n    deps = [\n        \":group_adagrad_optimizer\",\n        \":optimizer_cc_proto\",\n        \":test_utils\",\n        \":avx_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"avx_utils\",\n    hdrs = [\"avx_utils.h\"],\n    copts = [\n        \"-D_ENABLE_AVX\",\n    ],\n    deps = [\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ncc_test(\n    name = \"avx_test\",\n    testonly = 1,\n    srcs = [\"avx_test.cc\"],\n    copts = [\n        \"-D_ENABLE_AVX\",\n    ],\n    deps = [\n        \":avx_utils\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_binary(\n    name = \"avx_benchmark\",\n    testonly = 1,\n    srcs = [\"avx_benchmark.cc\"],\n    copts = [\n        \"-D_ENABLE_AVX\",\n    ],\n    deps = [\n        \":avx_utils\",\n        \"//monolith/native_training/runtime/allocator:block_allocator\",\n        \"//monolith/native_training/runtime/common:cpu_info\",\n        \"@com_github_google_benchmark//:benchmark\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_glog//:glog\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/adadelta_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/adadelta_optimizer.h\"\n#include <cmath>\n#include <memory>\n#include \"absl/strings/str_format.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass AdadeltaOptimizer : public OptimizerInterface {\n public:\n  explicit AdadeltaOptimizer(AdadeltaOptimizerConfig config)\n      : conf_(std::move(config)) {}\n\n  int64_t SizeBytes() const override {\n    return 2 * conf_.dim_size() * sizeof(float);\n  }\n\n  int64_t UncompressedSizeBytes() const override { return SizeBytes(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Adadelta(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {\n    float* accum = static_cast<float*>(ctx);\n    float* accum_update = accum + conf_.dim_size();\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      accum[i] = accum_update[i] = 0;\n    }\n  }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    float* accum = static_cast<float*>(ctx);\n    float* accum_update = accum + conf_.dim_size();\n    float effective_lr = learning_rates[0];\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      float cur_grad = grad[i] + conf_.weight_decay_factor() * num[i];\n      float new_accum = accum[i] * conf_.averaging_ratio() +\n                        cur_grad * cur_grad * (1 - conf_.averaging_ratio());\n      float update = std::sqrt(accum_update[i] + conf_.epsilon()) /\n                     std::sqrt(new_accum + conf_.epsilon()) * cur_grad;\n      float new_w = num[i] - update * effective_lr;\n      float new_accum_update = accum_update[i] * conf_.averaging_ratio() +\n                               update * update * (1 - conf_.averaging_ratio());\n      // printf(\"%d: %f %f %f %f %f\\n\", i, cur_grad, new_accum, update, new_w,\n      // new_accum_update);\n      num[i] = new_w;\n      accum[i] = new_accum;\n      accum_update[i] = new_accum_update;\n    }\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    AdadeltaOptimizerDump* adadelta_dump = dump.add_dump()->mutable_adadelta();\n    const float* accum = static_cast<const float*>(ctx);\n    const float* accum_update = accum + conf_.dim_size();\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      adadelta_dump->add_accum(accum[i]);\n      adadelta_dump->add_accum_update(accum_update[i]);\n    }\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    const AdadeltaOptimizerDump& adadelta_dump = dump.dump(0).adadelta();\n    float* accum = static_cast<float*>(ctx);\n    float* accum_update = accum + conf_.dim_size();\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      accum[i] = adadelta_dump.accum(i);\n      accum_update[i] = adadelta_dump.accum_update(i);\n    }\n  }\n\n private:\n  AdadeltaOptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewAdadeltaOptimizer(\n    AdadeltaOptimizerConfig config) {\n  return std::make_unique<AdadeltaOptimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/adadelta_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_ADADELTA_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_ADADELTA_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewAdadeltaOptimizer(\n    AdadeltaOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_ADADELTA_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/adadelta_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/adadelta_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\nTEST(AdadeltaOptimizer, Basic) {\n  AdadeltaOptimizerConfig config;\n  config.set_dim_size(1);\n  auto opt = NewAdadeltaOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected = {-0.0031607f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).adadelta().accum(0), 10, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected2 = {-0.0064035f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\nTEST(AdadeltaOptimizer, ListUpdate) {\n  AdadeltaOptimizerConfig config;\n  config.set_dim_size(2);\n  auto opt = NewAdadeltaOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected = {-0.0031607f, -0.0030151f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).adadelta().accum(0), 10, 1e-4);\n  EXPECT_NEAR(dump.dump(0).adadelta().accum(1), .1, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected2 = {-0.0064035f, -0.0061047f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/adagrad_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n\n#include <cmath>\n#include <memory>\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/adagrad_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/avx_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass AdagradOptimizer : public OptimizerInterface {\n public:\n  explicit AdagradOptimizer(AdagradOptimizerConfig config)\n      : conf_(std::move(config)) {}\n\n  int64_t SizeBytes() const override {\n    return conf_.dim_size() * sizeof(float);\n  }\n\n  int64_t UncompressedSizeBytes() const override {\n    return conf_.dim_size() * sizeof(float);\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Adagrad(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {\n    float* norm = static_cast<float*>(ctx);\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      norm[i] = conf_.initial_accumulator_value();\n    }\n  }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    float* norm = static_cast<float*>(ctx);\n    AdagradOptimize(num.data(), norm, grad.data(), conf_.dim_size(),\n                    learning_rates[0], conf_.weight_decay_factor());\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    AdagradOptimizerDump* adagrad_dump = dump.add_dump()->mutable_adagrad();\n    const float* norm = static_cast<const float*>(ctx);\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      adagrad_dump->add_norm(norm[i]);\n    }\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    const AdagradOptimizerDump& adagrad_dump = dump.dump(0).adagrad();\n    float* norm = static_cast<float*>(ctx);\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      norm[i] = adagrad_dump.norm(i);\n    }\n  }\n\n private:\n  AdagradOptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewAdagradOptimizer(\n    AdagradOptimizerConfig config) {\n  return std::make_unique<AdagradOptimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/adagrad_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_ADAGRAD_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_ADAGRAD_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewAdagradOptimizer(\n    AdagradOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_ADAGRAD_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/adagrad_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/adagrad_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\nTEST(AdagradOptimizer, Basic) {\n  AdagradOptimizerConfig config;\n  config.set_dim_size(2);\n  config.set_initial_accumulator_value(1.0f);\n  auto opt = NewAdagradOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {1.0f, 2.0f},\n                {0.1f});\n  auto expected = {-0.07071067, -0.08944272};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {1.0f, 1.0f},\n                {0.1f});\n  opt->Optimize(mem2.mutable_ctx(), mem2.mutable_num_span(), {1.0f, 1.0f},\n                {0.1f});\n  EXPECT_THAT(mem.num(), ElementsAreArray(mem2.num()));\n}\n\nTEST(AdagradOptimizer, OptimizeWithWeightDecay) {\n  AdagradOptimizerConfig config;\n  config.set_dim_size(2);\n  config.set_initial_accumulator_value(1.0f);\n  config.set_weight_decay_factor(0.1f);\n\n  auto opt = NewAdagradOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {1.0f, 2.0f},\n                {0.1f});\n  auto expected = {-0.07071067, -0.08944272};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {1.0f, 2.0f},\n                {0.1f});\n  auto expected2 = {-0.128173, -0.155943};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {1.0f, 1.0f},\n                {0.1f});\n  opt->Optimize(mem2.mutable_ctx(), mem2.mutable_num_span(), {1.0f, 1.0f},\n                {0.1f});\n  EXPECT_THAT(mem.num(), ElementsAreArray(mem2.num()));\n}\n\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/adam_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cmath>\n#include <memory>\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/adam_optimizer.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass AdamOptimizer : public OptimizerInterface {\n public:\n  explicit AdamOptimizer(AdamOptimizerConfig config)\n      : conf_(std::move(config)) {}\n\n  int64_t SizeBytes() const override {\n    return (2 * conf_.dim_size() + 2) * sizeof(float);\n  }\n\n  int64_t UncompressedSizeBytes() const override { return SizeBytes(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Adam(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {\n    float* m = static_cast<float*>(ctx);\n    float* v = m + conf_.dim_size();\n    float& beta1_power = v[conf_.dim_size()];\n    float& beta2_power = v[conf_.dim_size() + 1];\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      m[i] = v[i] = 0;\n    }\n    beta1_power = conf_.beta1();\n    beta2_power = conf_.beta2();\n  }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    float* m = static_cast<float*>(ctx);\n    float* v = m + conf_.dim_size();\n    float& beta1_power = v[conf_.dim_size()];\n    float& beta2_power = v[conf_.dim_size() + 1];\n    float lr = learning_rates[0] * sqrt(1 - beta2_power) / (1 - beta1_power);\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      float cur_grad = grad[i] + conf_.weight_decay_factor() * num[i];\n      float new_m = m[i] + (cur_grad - m[i]) * (1 - conf_.beta1());\n      float new_v = v[i] + (cur_grad * cur_grad - v[i]) * (1 - conf_.beta2());\n      float new_w = num[i];\n      if (conf_.use_nesterov()) {\n        new_w -=\n            ((cur_grad * (1 - conf_.beta1()) + conf_.beta1() * new_m) * lr) /\n            (sqrt(new_v) + conf_.epsilon());\n      } else {\n        new_w -= (new_m * lr) / (sqrt(new_v) + conf_.epsilon());\n      }\n      num[i] = new_w;\n      m[i] = new_m;\n      v[i] = new_v;\n    }\n    beta1_power *= conf_.beta1();\n    beta2_power *= conf_.beta2();\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    AdamOptimizerDump* adam_dump = dump.add_dump()->mutable_adam();\n    const float* m = static_cast<const float*>(ctx);\n    const float* v = m + conf_.dim_size();\n    const float& beta1_power = v[conf_.dim_size()];\n    const float& beta2_power = v[conf_.dim_size() + 1];\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      adam_dump->add_m(m[i]);\n      adam_dump->add_v(v[i]);\n    }\n    adam_dump->set_beta1_power(beta1_power);\n    adam_dump->set_beta2_power(beta2_power);\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    const AdamOptimizerDump& adam_dump = dump.dump(0).adam();\n    float* m = static_cast<float*>(ctx);\n    float* v = m + conf_.dim_size();\n    float& beta1_power = v[conf_.dim_size()];\n    float& beta2_power = v[conf_.dim_size()];\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      m[i] = adam_dump.m(i);\n      v[i] = adam_dump.v(i);\n    }\n    beta1_power = adam_dump.beta1_power();\n    beta2_power = adam_dump.beta2_power();\n  }\n\n private:\n  AdamOptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewAdamOptimizer(\n    AdamOptimizerConfig config) {\n  return std::make_unique<AdamOptimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/adam_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_ADAM_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_ADAM_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewAdamOptimizer(\n    AdamOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_ADAM_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/adam_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/adam_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\nTEST(AdamOptimizer, Basic) {\n  AdamOptimizerConfig config;\n  config.set_dim_size(1);\n  auto opt = NewAdamOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected = {-0.00990099f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).adam().m(0), 1, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected2 = {-0.01983060f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\nTEST(AdamOptimizer, ListUpdate) {\n  AdamOptimizerConfig config;\n  config.set_dim_size(2);\n  auto opt = NewAdamOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected = {-0.00990099f, -0.00909091f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).adam().m(0), 1, 1e-4);\n  EXPECT_NEAR(dump.dump(0).adam().m(1), .1, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected2 = {-0.01983060f, -0.01842895f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/amsgrad_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cmath>\n#include <memory>\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/amsgrad_optimizer.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass AmsgradOptimizer : public OptimizerInterface {\n public:\n  explicit AmsgradOptimizer(AmsgradOptimizerConfig config)\n      : conf_(std::move(config)) {}\n\n  int64_t SizeBytes() const override {\n    return (3 * conf_.dim_size() + 2) * sizeof(float);\n  }\n\n  int64_t UncompressedSizeBytes() const override { return SizeBytes(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Amsgrad(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {\n    float* m = static_cast<float*>(ctx);\n    float* v = m + conf_.dim_size();\n    float* vhat = v + conf_.dim_size();\n    float& beta1_power = vhat[conf_.dim_size()];\n    float& beta2_power = vhat[conf_.dim_size() + 1];\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      m[i] = v[i] = vhat[i] = 0;\n    }\n    beta1_power = conf_.beta1();\n    beta2_power = conf_.beta2();\n  }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    float* m = static_cast<float*>(ctx);\n    float* v = m + conf_.dim_size();\n    float* vhat = v + conf_.dim_size();\n    float& beta1_power = vhat[conf_.dim_size()];\n    float& beta2_power = vhat[conf_.dim_size() + 1];\n    float lr = learning_rates[0] * sqrt(1 - beta2_power) / (1 - beta1_power);\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      float cur_grad = grad[i] + conf_.weight_decay_factor() * num[i];\n      float new_m = m[i] + (cur_grad - m[i]) * (1 - conf_.beta1());\n      float new_v = v[i] + (cur_grad * cur_grad - v[i]) * (1 - conf_.beta2());\n      float new_vhat = std::max(vhat[i], new_v);\n      float new_w = num[i];\n      if (conf_.use_nesterov()) {\n        new_w -=\n            ((cur_grad * (1 - conf_.beta1()) + conf_.beta1() * new_m) * lr) /\n            (sqrt(new_vhat) + conf_.epsilon());\n      } else {\n        new_w -= (new_m * lr) / (sqrt(new_vhat) + conf_.epsilon());\n      }\n      num[i] = new_w;\n      m[i] = new_m;\n      v[i] = new_v;\n      vhat[i] = new_vhat;\n    }\n    beta1_power *= conf_.beta1();\n    beta2_power *= conf_.beta2();\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    AmsgradOptimizerDump* amsgrad_dump = dump.add_dump()->mutable_amsgrad();\n    const float* m = static_cast<const float*>(ctx);\n    const float* v = m + conf_.dim_size();\n    const float* vhat = v + conf_.dim_size();\n    const float& beta1_power = vhat[conf_.dim_size()];\n    const float& beta2_power = vhat[conf_.dim_size() + 1];\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      amsgrad_dump->add_m(m[i]);\n      amsgrad_dump->add_v(v[i]);\n      amsgrad_dump->add_vhat(vhat[i]);\n    }\n    amsgrad_dump->set_beta1_power(beta1_power);\n    amsgrad_dump->set_beta2_power(beta2_power);\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    const AmsgradOptimizerDump& amsgrad_dump = dump.dump(0).amsgrad();\n    float* m = static_cast<float*>(ctx);\n    float* v = m + conf_.dim_size();\n    float* vhat = v + conf_.dim_size();\n    float& beta1_power = vhat[conf_.dim_size()];\n    float& beta2_power = vhat[conf_.dim_size() + 1];\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      m[i] = amsgrad_dump.m(i);\n      v[i] = amsgrad_dump.v(i);\n      vhat[i] = amsgrad_dump.vhat(i);\n    }\n    beta1_power = amsgrad_dump.beta1_power();\n    beta2_power = amsgrad_dump.beta2_power();\n  }\n\n private:\n  AmsgradOptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewAmsgradOptimizer(\n    AmsgradOptimizerConfig config) {\n  return std::make_unique<AmsgradOptimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/amsgrad_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_AMSGRAD_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_AMSGRAD_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewAmsgradOptimizer(\n    AmsgradOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_AMSGRAD_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/amsgrad_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/amsgrad_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\nTEST(AmsgradOptimizer, Basic) {\n  AmsgradOptimizerConfig config;\n  config.set_dim_size(1);\n  auto opt = NewAmsgradOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected = {-0.00990099f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).amsgrad().m(0), 1, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected2 = {-0.01983060f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\nTEST(AmsgradOptimizer, ListUpdate) {\n  AmsgradOptimizerConfig config;\n  config.set_dim_size(2);\n  auto opt = NewAmsgradOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected = {-0.00990099f, -0.00909091f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).amsgrad().m(0), 1, 1e-4);\n  EXPECT_NEAR(dump.dump(0).amsgrad().m(1), .1, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected2 = {-0.01983060f, -0.01842895f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/avx_benchmark.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n//\n// Created by david on 2020-11-27.\n//\n\n#include \"absl/random/random.h\"\n#include \"benchmark/benchmark.h\"\n#include \"monolith/native_training/runtime/common/cpu_info.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/avx_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nvoid BM_AdagradOptimize(benchmark::State& state) {  // NOLINT\n  size_t dim = state.range(0);\n  float lr = 0.01f;\n  std::vector<float> norm(dim, 0), grad(dim, 0);\n  absl::BitGen bit_gen;\n  for (size_t i = 0; i < dim; ++i) {\n    norm[i] = absl::Uniform<float>(bit_gen, .1f, 1.f);\n    grad[i] = absl::Uniform<float>(bit_gen, -1.f, 1.f);\n  }\n\n  std::vector<float> result(dim, 0);\n  for (auto _ : state) {\n    BaselineAdagradOptimize(result.data(), norm.data(), grad.data(), dim, lr,\n                            0.01);\n  }\n}\n\nvoid BM_AVXAdagradOptimize(benchmark::State& state) {  // NOLINT\n  RunCPUGuard();\n  size_t dim = state.range(0);\n  float lr = 0.01f;\n  std::vector<float> norm(dim, 0), grad(dim, 0);\n  absl::BitGen bit_gen;\n  for (size_t i = 0; i < dim; ++i) {\n    norm[i] = absl::Uniform<float>(bit_gen, .1f, 1.f);\n    grad[i] = absl::Uniform<float>(bit_gen, -1.f, 1.f);\n  }\n\n  std::vector<float> result(dim, 0);\n  for (auto _ : state) {\n    Avx256AdagradOptimize(result.data(), norm.data(), grad.data(), dim, lr,\n                          0.01);\n  }\n}\n\nBENCHMARK(BM_AdagradOptimize)->Arg(16)->Arg(64)->Arg(256);\nBENCHMARK(BM_AVXAdagradOptimize)->Arg(16)->Arg(64)->Arg(256);\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n\nBENCHMARK_MAIN();\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/avx_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n//\n// Created by david on 2020-11-27.\n//\n\n#include <vector>\n#include \"absl/random/random.h\"\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/avx_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\n\nvoid TestAdagradOptimize(size_t dim = 32) {\n  float lr = 0.01f;\n  std::vector<float> norm(dim, 0), grad(dim, 0);\n  absl::BitGen bit_gen;\n  for (size_t i = 0; i < dim; ++i) {\n    norm[i] = absl::Uniform<float>(bit_gen, .1f, 1.f);\n    grad[i] = absl::Uniform<float>(bit_gen, -1.f, 1.f);\n  }\n\n  std::vector<float> norm2(norm.begin(), norm.end()), grad2(grad.begin(), grad.end());\n  std::vector<float> result(dim, 0), result_avx(dim, 0);\n  BaselineAdagradOptimize(result.data(), norm.data(), grad.data(), dim, lr, 0.1f);\n\n#if defined(_ENABLE_AVX) && defined(__AVX__)\n  Avx256AdagradOptimize(result_avx.data(), norm2.data(), grad2.data(), dim, lr, 0.1f);\n#else\n  static_assert(false, \"AVX is not available, please check and recompile!\");\n#endif\n\n  for (size_t i = 0; i < dim; ++i) {\n    EXPECT_NEAR(result[i], result_avx[i], 1e-6);\n  }\n}\n\nTEST(AVX, Basic) {\n  TestAdagradOptimize(1);\n  TestAdagradOptimize(7);\n  TestAdagradOptimize(8);\n  TestAdagradOptimize(16);\n  TestAdagradOptimize(32);\n  TestAdagradOptimize(39);\n  TestAdagradOptimize(224);\n}\n\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/avx_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n//\n// Created by david on 2020-11-27.\n//\n\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_AVX_UTILS\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_AVX_UTILS\n\n#if defined(_ENABLE_AVX) && defined(__AVX__)\n#include <immintrin.h>\n#endif\n\nnamespace monolith {\nnamespace hash_table {\n\ninline void BaselineAdagradOptimize(float* num, float* norm, const float* grad,\n                                    size_t len, float lr, float w_decay) {\n  for (size_t i = 0; i < len; ++i) {\n    float g = grad[i] + w_decay * num[i];\n\n    norm[i] += g * g;\n    float effective_lr = lr / std::sqrt(norm[i]);\n    num[i] -= effective_lr * g;\n  }\n}\n\ninline float BaselineGetGroupNorm(float* num, float* norm, const float* grad,\n                                  float* zero, size_t len, float effective_lr) {\n  float group_zt_norm = 0;\n  for (size_t i = 0; i < len; ++i) {\n    auto norm_new = norm[i] + grad[i] * grad[i];\n    auto sigma = (std::sqrt(norm_new) - std::sqrt(norm[i])) / effective_lr;\n    zero[i] += (grad[i] - sigma * num[i]);\n    norm[i] = norm_new;\n    group_zt_norm += zero[i] * zero[i];\n  }\n  return group_zt_norm;\n}\n\ninline void BaselineSetWeightsWithGroupNorm(float group_zt_norm, float* num,\n                                            float* norm, float* zero,\n                                            size_t len, float effective_lr,\n                                            float l1_regularization_strength,\n                                            float l2_regularization_strength,\n                                            float beta) {\n  if (group_zt_norm < l1_regularization_strength) {\n    for (size_t i = 0; i < len; ++i) {\n      num[i] = 0;\n    }\n  } else {\n    float normwise =\n        (l1_regularization_strength - group_zt_norm) / group_zt_norm;\n    for (size_t i = 0; i < len; ++i) {\n      num[i] = effective_lr * zero[i] * normwise /\n               (beta + std::sqrt(norm[i]) +\n                l2_regularization_strength * effective_lr);\n    }\n  }\n}\n\ninline void BaselineGroupFTRLOptimize(float* num, float* norm,\n                                      const float* grad, float* zero,\n                                      size_t len, float effective_lr,\n                                      float l1_regularization_strength,\n                                      float l2_regularization_strength,\n                                      float beta) {\n  float group_zt_norm =\n      BaselineGetGroupNorm(num, norm, grad, zero, len, effective_lr);\n  group_zt_norm = std::abs(std::sqrt(group_zt_norm));\n  BaselineSetWeightsWithGroupNorm(group_zt_norm, num, norm, zero, len,\n                                  effective_lr, l1_regularization_strength,\n                                  l2_regularization_strength, beta);\n}\n\ninline void BaseReduceSum(const float* a, const float* b, float* output,\n                          size_t len) {\n  for (size_t i = 0; i < len; ++i) {\n    output[i] = a[i] + b[i];\n  }\n}\n\n#if defined(_ENABLE_AVX) && defined(__AVX__)\ninline void Avx256AdagradOptimize(float* num, float* norm, const float* grad,\n                                  size_t len, float lr, float w_decay) {\n  const __m256 lamda = _mm256_set1_ps(w_decay);\n  const __m256 _lr = _mm256_set1_ps(lr);\n\n  // OPTIMIZE: Loads floating-point vector from an aligned memory address\n  for (; len > 7; len -= 8, num += 8, norm += 8, grad += 8) {\n    const __m256 _num = _mm256_loadu_ps(num);\n    const __m256 _norm = _mm256_loadu_ps(norm);\n    const __m256 _grad = _mm256_loadu_ps(grad);\n    const __m256 updated_grad = _mm256_fmadd_ps(lamda, _num, _grad);\n    __m256 _norm_new = _mm256_fmadd_ps(updated_grad, updated_grad, _norm);\n    _mm256_storeu_ps(norm, _norm_new);\n\n    const __m256 _norm_new_sqrt = _mm256_sqrt_ps(_norm_new);\n    const __m256 effective_lr = _mm256_div_ps(_lr, _norm_new_sqrt);\n    __m256 _num_new = _mm256_fnmadd_ps(effective_lr, _grad, _num);\n    _mm256_storeu_ps(num, _num_new);\n  }\n\n  if (len) {\n    BaselineAdagradOptimize(num, norm, grad, len, lr, w_decay);\n  }\n}\n\n// horizontal sum of mm256\ninline float sum8(__m256 x) {\n  // hiQuad = ( x7, x6, x5, x4 )\n  const __m128 hiQuad = _mm256_extractf128_ps(x, 1);\n  // loQuad = ( x3, x2, x1, x0 )\n  const __m128 loQuad = _mm256_castps256_ps128(x);\n  // sumQuad = ( x3 + x7, x2 + x6, x1 + x5, x0 + x4 )\n  const __m128 sumQuad = _mm_add_ps(loQuad, hiQuad);\n  // loDual = ( -, -, x1 + x5, x0 + x4 )\n  const __m128 loDual = sumQuad;\n  // hiDual = ( -, -, x3 + x7, x2 + x6 )\n  const __m128 hiDual = _mm_movehl_ps(sumQuad, sumQuad);\n  // sumDual = ( -, -, x1 + x3 + x5 + x7, x0 + x2 + x4 + x6 )\n  const __m128 sumDual = _mm_add_ps(loDual, hiDual);\n  // lo = ( -, -, -, x0 + x2 + x4 + x6 )\n  const __m128 lo = sumDual;\n  // hi = ( -, -, -, x1 + x3 + x5 + x7 )\n  const __m128 hi = _mm_shuffle_ps(sumDual, sumDual, 0x1);\n  // sum = ( -, -, -, x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 )\n  const __m128 sum = _mm_add_ss(lo, hi);\n\n  return _mm_cvtss_f32(sum);\n}\n\ninline void Avx256GroupFTRLOptimize(float* num, float* norm, const float* grad,\n                                    float* zero, size_t len, float effective_lr,\n                                    float l1_regularization_strength,\n                                    float l2_regularization_strength,\n                                    float beta) {\n  const __m256 _lr = _mm256_set1_ps(effective_lr);\n  float group_zt_norm = 0.0;\n\n  float* numCopy = num;\n  size_t lenCopy = len;\n\n  float* normCopy = norm;\n  float* zeroCopy = zero;\n\n  for (; len > 7; len -= 8, num += 8, norm += 8, grad += 8, zero += 8) {\n    const __m256 _group_zt_norm = _mm256_set1_ps(0.0);\n\n    const __m256 _num = _mm256_loadu_ps(num);\n    const __m256 _norm = _mm256_loadu_ps(norm);\n    const __m256 _grad = _mm256_loadu_ps(grad);\n    const __m256 _zero = _mm256_loadu_ps(zero);\n\n    const __m256 _new_norm = _mm256_fmadd_ps(_grad, _grad, _norm);\n    const __m256 _norm_new_sqrt = _mm256_sqrt_ps(_new_norm);\n    const __m256 _norm_sqrt = _mm256_sqrt_ps(_norm);\n    __m256 _sigma = _mm256_sub_ps(_norm_new_sqrt, _norm_sqrt);\n    _sigma = _mm256_div_ps(_sigma, _lr);\n\n    const __m256 _add_zero = _mm256_fnmadd_ps(_sigma, _num, _grad);\n    const __m256 _new_zero = _mm256_add_ps(_zero, _add_zero);\n\n    _mm256_storeu_ps(zero, _new_zero);\n    _mm256_storeu_ps(norm, _new_norm);\n\n    group_zt_norm +=\n        sum8(_mm256_fmadd_ps(_new_zero, _new_zero, _group_zt_norm));\n  }\n\n  if (len) {\n    group_zt_norm +=\n        BaselineGetGroupNorm(num, norm, grad, zero, len, effective_lr);\n  }\n\n  group_zt_norm = std::abs(std::sqrt(group_zt_norm));\n\n  if (group_zt_norm < l1_regularization_strength) {\n    for (; lenCopy > 7; lenCopy -= 8, numCopy += 8) {\n      _mm256_storeu_ps(numCopy, _mm256_set1_ps(0.0));\n    }\n  } else {\n    const __m256 _normwise = _mm256_set1_ps(\n        (l1_regularization_strength - group_zt_norm) / group_zt_norm);\n    const __m256 _l2_regularization =\n        _mm256_set1_ps(l2_regularization_strength);\n    const __m256 _beta = _mm256_set1_ps(beta);\n\n    for (; lenCopy > 7;\n         lenCopy -= 8, numCopy += 8, normCopy += 8, zeroCopy += 8) {\n      const __m256 _norm = _mm256_loadu_ps(normCopy);\n      const __m256 _zero = _mm256_loadu_ps(zeroCopy);\n      const __m256 _sqrt_norm = _mm256_sqrt_ps(_norm);\n\n      __m256 _denom = _mm256_fnmadd_ps(_l2_regularization, _lr, _sqrt_norm);\n      _denom = _mm256_add_ps(_denom, _beta);\n\n      __m256 _numer = _mm256_mul_ps(_lr, _zero);\n      _numer = _mm256_mul_ps(_numer, _normwise);\n      __m256 _new_num = _mm256_div_ps(_numer, _denom);\n      _mm256_storeu_ps(numCopy, _new_num);\n    }\n  }\n\n  if (lenCopy) {\n    BaselineSetWeightsWithGroupNorm(\n        group_zt_norm, numCopy, normCopy, zeroCopy, lenCopy, effective_lr,\n        l1_regularization_strength, l2_regularization_strength, beta);\n  }\n}\n\ninline void Avx256ReduceSum(const float* a, const float* b, float* output,\n                            size_t len) {\n  for (; len > 7; len -= 8, a += 8, b += 8, output += 8) {\n    const __m256 _a = _mm256_loadu_ps(a);\n    const __m256 _b = _mm256_loadu_ps(b);\n    const __m256 _output = _mm256_add_ps(_a, _b);\n    _mm256_storeu_ps(output, _output);\n  }\n  if (len) {\n    BaseReduceSum(a, b, output, len);\n  }\n}\n#endif\n\ninline void AdagradOptimize(float* num, float* norm, const float* grad,\n                            size_t len, float lr, float w_decay) {\n#if defined(_ENABLE_AVX) && defined(__AVX__)\n  Avx256AdagradOptimize(num, norm, grad, len, lr, w_decay);\n#else\n  BaselineAdagradOptimize(num, norm, grad, len, lr, w_decay);\n#endif\n}\n\ninline void ReduceSum(const float* a, const float* b, float* output,\n                      size_t len) {\n#if defined(_ENABLE_AVX) && defined(__AVX__)\n  Avx256ReduceSum(a, b, output, len);\n#else\n  BaseReduceSum(a, b, output, len);\n#endif\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_AVX_UTILS\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/batch_softmax_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/batch_softmax_optimizer.h\"\n\n#include \"absl/strings/str_format.h\"\n#include \"glog/logging.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass BatchSoftmaxOptimizer : public OptimizerInterface {\n public:\n  explicit BatchSoftmaxOptimizer(BatchSoftmaxOptimizerConfig config)\n      : config_(std::move(config)) {\n    DCHECK_EQ(config_.dim_size(), 1);\n  }\n\n  int64_t SizeBytes() const override { return sizeof(int64_t); }\n\n  int64_t UncompressedSizeBytes() const override { return SizeBytes(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"BatchSoftmax(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return config_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {\n    auto* A = reinterpret_cast<int64_t*>(ctx);\n    *A = 0;\n  }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    float& B = num[0];\n    int64_t& A = *reinterpret_cast<int64_t*>(ctx);\n    float alpha = learning_rates[0];\n    B = (1 - alpha) * B + alpha * static_cast<float>(global_step - A);\n    if (global_step < 0) {\n      LOG(FATAL) << absl::StrFormat(\n          \"global_step=%ld is negative, please investigate!\", global_step);\n    }\n    A = global_step;\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    BatchSoftmaxOptimizerDump* batch_softmax_dump =\n        dump.add_dump()->mutable_batch_softmax();\n    int64_t A = *reinterpret_cast<const int64_t*>(ctx);\n    batch_softmax_dump->set_global_step(A);\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    const BatchSoftmaxOptimizerDump& batch_softmax_dump =\n        dump.dump(0).batch_softmax();\n    int64_t& A = *reinterpret_cast<int64_t*>(ctx);\n    A = batch_softmax_dump.global_step();\n  }\n\n private:\n  BatchSoftmaxOptimizerConfig config_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewBatchSoftmaxOptimizer(\n    BatchSoftmaxOptimizerConfig config) {\n  return std::make_unique<BatchSoftmaxOptimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/batch_softmax_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_BATCH_SOFTMAX_OPTIMIZER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_BATCH_SOFTMAX_OPTIMIZER_H_\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewBatchSoftmaxOptimizer(\n    BatchSoftmaxOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_BATCH_SOFTMAX_OPTIMIZER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/batch_softmax_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/batch_softmax_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::ElementsAreArray;\nusing ::testing::FloatNear;\nusing ::testing::Pointwise;\n\nTEST(BatchSoftmaxOptimizer, Basic) {\n  BatchSoftmaxOptimizerConfig config;\n  config.set_dim_size(1);\n  auto opt = NewBatchSoftmaxOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  int64_t global_step = 1;\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {2.0f}, {0.1f},\n                global_step);\n  EXPECT_FLOAT_EQ(mem.num().front(), 0.1f);\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {2.0f}, {0.1f});\n  opt->Optimize(mem2.mutable_ctx(), mem2.mutable_num_span(), {2.0f}, {0.1f});\n  EXPECT_THAT(mem.num(), ElementsAreArray(mem2.num()));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/dc_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cmath>\n#include <memory>\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/dc_optimizer.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\nclass DcOptimizer : public OptimizerDecorator {\n public:\n  explicit DcOptimizer(DcOptimizerConfig config,\n                       std::unique_ptr<OptimizerInterface> base_opt)\n      : OptimizerDecorator(std::move(base_opt)), conf_(std::move(config)) { }\n\n  void OptimizeWithLatestValue(void* ctx, absl::Span<float> num,\n                               absl::Span<const float> grad,\n                               absl::Span<const float> learning_rates,\n                               absl::Span<float> latest_value,\n                               const int64_t global_step) const override {\n    std::vector<float> compensated_g(conf_.dim_size());\n    // add in Float16 stuff later?\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      float new_grad = grad[i] + conf_.lambda_() * grad[i] * grad[i] *\n                        (num[i] - latest_value[i]);\n      compensated_g[i] = new_grad;\n    }\n    base_opt_.get()->Optimize(ctx, num, compensated_g, learning_rates, global_step);\n  }\n\n private:\n  DcOptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerDecorator> NewDcOptimizer(\n    DcOptimizerConfig config, std::unique_ptr<OptimizerInterface> base_opt) {\n  return std::make_unique<DcOptimizer>(std::move(config), std::move(base_opt));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/dc_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_DC_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_DC_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_decorator.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerDecorator> NewDcOptimizer(\n    DcOptimizerConfig config, std::unique_ptr<OptimizerInterface> base_opt);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_DC_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/dc_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/dc_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/adadelta_optimizer.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\nTEST(DcOptimizer, Basic) {\n  AdadeltaOptimizerConfig config1;\n  config1.set_dim_size(1);\n  auto opt1 = NewAdadeltaOptimizer(config1);\n  DcOptimizerConfig config2;\n  config2.set_dim_size(1);\n  config2.set_lambda_(0.1f);\n  auto opt2 = NewDcOptimizer(config2, std::move(opt1));\n  TestOptimizerEntry mem(opt2.get());\n  opt2->Init(mem.mutable_ctx());\n  float arr[] = {0.1f};\n  opt2->OptimizeWithLatestValue(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f}, arr);\n  auto expected = {-0.0031603f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt2->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).adadelta().accum(0), 8.1, 1e-4);\n  TestOptimizerEntry mem2(opt2.get());\n  opt2->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  arr[0] = 0.0f;\n  opt2->OptimizeWithLatestValue(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f}, arr);\n  auto expected2 = {-0.0065548f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\nTEST(DcOptimizer, ListUpdate) {\n  AdadeltaOptimizerConfig config1;\n  config1.set_dim_size(2);\n  auto opt1 = NewAdadeltaOptimizer(config1);\n  DcOptimizerConfig config2;\n  config2.set_dim_size(2);\n  config2.set_lambda_(0.1f);\n  auto opt2 = NewDcOptimizer(config2, std::move(opt1));\n  TestOptimizerEntry mem(opt2.get());\n  opt2->Init(mem.mutable_ctx());\n  float arr[] = {0.1f, 0.1f};\n  opt2->OptimizeWithLatestValue(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f}, arr);\n  auto expected = {-0.0031603f, -0.00301233f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt2->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).adadelta().accum(0), 8.1, 1e-4);\n  EXPECT_NEAR(dump.dump(0).adadelta().accum(1), .09801, 1e-4);\n  TestOptimizerEntry mem2(opt2.get());\n  opt2->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  arr[0] = 0.0f;\n  arr[1] = 0.0f;\n  opt2->OptimizeWithLatestValue(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f}, arr);\n  auto expected2 = {-0.0065548f, -0.00611400f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/dynamic_wd_adagrad_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/dynamic_wd_adagrad_optimizer.h\"\n\n#include <exception>\n\n#include \"absl/strings/str_format.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewDynamicWdAdagradOptimizer(\n    DynamicWdAdagradOptimizerConfig config) {\n  throw std::invalid_argument(absl::StrFormat(\n      \"optimizer is not implemented yet. %s\", config.ShortDebugString()));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/dynamic_wd_adagrad_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_DYNAMIC_WD_ADAGRAD_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_DYNAMIC_WD_ADAGRAD_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewDynamicWdAdagradOptimizer(\n    DynamicWdAdagradOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_DYNAMIC_WD_ADAGRAD_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/dynamic_wd_adagrad_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/dynamic_wd_adagrad_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/dynamic_wd_avx_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n//\n// Created by david on 2020-11-27.\n//\n\n#include <vector>\n#include \"absl/random/random.h\"\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/dynamic_wd_avx_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\n\nvoid TestDynamicWdAdagradOptimize(size_t dim = 32, int step = 1, bool decouple_wd = false) {\n  float lr = 0.01f;\n  std::vector<float> norm(dim, 0), grad(dim, 0);\n  absl::BitGen bit_gen;\n  for (size_t i = 0; i < dim; ++i) {\n    norm[i] = absl::Uniform<float>(bit_gen, .1f, 1.f);\n    grad[i] = absl::Uniform<float>(bit_gen, -1.f, 1.f);\n  }\n\n  std::vector<float> norm2(norm.begin(), norm.end()), grad2(grad.begin(), grad.end());\n  std::vector<float> result(dim, 0), result_avx(dim, 0);\n  for (int i = 0; i < step; ++i) {\n  BaselineDynamicWdAdagradOptimize(result.data(), norm.data(), grad.data(), dim, lr, 0.1f, decouple_wd);\n\n#if defined(_ENABLE_AVX) && defined(__AVX__)\n  if (decouple_wd) {\n    Avx256DynamicWdAdagradOptimizeDecoupleWd(result_avx.data(), norm2.data(), grad2.data(), dim, lr, 0.1f);\n  } else {\n    Avx256DynamicWdAdagradOptimize(result_avx.data(), norm2.data(), grad2.data(), dim, lr, 0.1f);\n  }\n#else\n  static_assert(false, \"AVX is not available, please check and recompile!\");\n#endif\n  }\n\n  for (size_t i = 0; i < dim; ++i) {\n    EXPECT_NEAR(result[i], result_avx[i], 1e-6);\n  }\n}\n\nTEST(AVX, Basic) {\n  TestDynamicWdAdagradOptimize(1);\n  TestDynamicWdAdagradOptimize(7);\n  TestDynamicWdAdagradOptimize(8);\n  TestDynamicWdAdagradOptimize(16);\n  TestDynamicWdAdagradOptimize(32);\n  TestDynamicWdAdagradOptimize(39);\n  TestDynamicWdAdagradOptimize(224);\n}\n\nTEST(AVX, DecoupleWd) {\n  TestDynamicWdAdagradOptimize(1, /*step=*/ 5, true);\n  TestDynamicWdAdagradOptimize(7, /*step=*/ 5, true);\n  TestDynamicWdAdagradOptimize(8, /*step=*/ 5, true);\n  TestDynamicWdAdagradOptimize(16, /*step=*/ 5, true);\n  TestDynamicWdAdagradOptimize(32, /*step=*/ 5, true);\n  TestDynamicWdAdagradOptimize(39, /*step=*/ 5, true);\n  TestDynamicWdAdagradOptimize(224, /*step=*/ 5, true);\n}\n\n\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/dynamic_wd_avx_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n//\n// Created by david on 2020-11-27.\n//\n\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_DYNAMIC_WD_AVX_UTILS\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_DYNAMIC_WD_AVX_UTILS\n\n#if defined(_ENABLE_AVX) && defined(__AVX__)\n#include <immintrin.h>\n#endif\n\nnamespace monolith {\nnamespace hash_table {\n\ninline void BaselineDynamicWdAdagradOptimize(float* num, float* norm, const float* grad,\n                                    size_t len, float lr, float w_decay,\n                                    bool decouple_wd) {\n  for (size_t i = 0; i < len; ++i) {\n    float g = grad[i];\n    if (!decouple_wd) {\n      g += w_decay * num[i];\n    }\n    norm[i] += g * g;\n    float effective_lr = lr / std::sqrt(norm[i]);\n    float grad_update = effective_lr * g;\n    if (decouple_wd) {\n      grad_update += lr * w_decay * num[i];\n    }\n\n    num[i] -= grad_update;\n  }\n}\n\ninline void BaseReduceSum(const float* a, const float* b, float* output,\n                          size_t len) {\n  for (size_t i = 0; i < len; ++i) {\n    output[i] = a[i] + b[i];\n  }\n}\n\n#if defined(_ENABLE_AVX) && defined(__AVX__)\ninline void Avx256DynamicWdAdagradOptimize(float* num, float* norm, const float* grad,\n                                  size_t len, float lr, float w_decay) {\n  const __m256 lamda = _mm256_set1_ps(w_decay);\n  float lrs[8] = {lr, lr, lr, lr, lr, lr, lr, lr};\n  const __m256 _lr = _mm256_loadu_ps(lrs);\n\n  // OPTIMIZE: Loads floating-point vector from an aligned memory address\n  for (; len > 7; len -= 8, num += 8, norm += 8, grad += 8) {\n    const __m256 _num = _mm256_loadu_ps(num);\n    const __m256 _norm = _mm256_loadu_ps(norm);\n    const __m256 _grad = _mm256_loadu_ps(grad);\n    const __m256 updated_grad = _mm256_fmadd_ps(lamda, _num, _grad);\n    __m256 _norm_new = _mm256_fmadd_ps(updated_grad, updated_grad, _norm);\n    _mm256_storeu_ps(norm, _norm_new);\n\n    const __m256 _norm_new_sqrt = _mm256_sqrt_ps(_norm_new);\n    const __m256 effective_lr = _mm256_div_ps(_lr, _norm_new_sqrt);\n    __m256 _num_new = _mm256_fnmadd_ps(effective_lr, _grad, _num);\n    _mm256_storeu_ps(num, _num_new);\n  }\n\n  if (len) {\n    BaselineDynamicWdAdagradOptimize(num, norm, grad, len, lr, w_decay, false);\n  }\n}\n\ninline void Avx256DynamicWdAdagradOptimizeDecoupleWd(float* num, float* norm, const float* grad,\n                                  size_t len, float lr, float w_decay) {\n  const __m256 lamda = _mm256_set1_ps(w_decay);\n  float lrs[8] = {lr, lr, lr, lr, lr, lr, lr, lr};\n  const __m256 _lr = _mm256_loadu_ps(lrs);\n\n  // OPTIMIZE: Loads floating-point vector from an aligned memory address\n  for (; len > 7; len -= 8, num += 8, norm += 8, grad += 8) {\n    const __m256 _num = _mm256_loadu_ps(num);\n    const __m256 _norm = _mm256_loadu_ps(norm);\n    const __m256 _grad = _mm256_loadu_ps(grad);\n    __m256 _norm_new = _mm256_fmadd_ps(_grad, _grad, _norm);\n    _mm256_storeu_ps(norm, _norm_new);\n\n    const __m256 _norm_new_sqrt = _mm256_sqrt_ps(_norm_new);\n    const __m256 effective_lr = _mm256_div_ps(_lr, _norm_new_sqrt);\n    __m256 _num_new = _mm256_fnmadd_ps(effective_lr, _grad, _num);\n    const __m256 effective_wd = _mm256_mul_ps(_lr, lamda);\n    __m256 _num_after_wd = _mm256_fnmadd_ps(effective_wd, _num, _num_new);\n    _mm256_storeu_ps(num, _num_after_wd);\n  }\n\n  if (len) {\n    BaselineDynamicWdAdagradOptimize(num, norm, grad, len, lr, w_decay, true);\n  }\n}\n\ninline void Avx256ReduceSum(const float* a, const float* b, float* output,\n                            size_t len) {\n  for (; len > 7; len -= 8, a += 8, b += 8, output += 8) {\n    const __m256 _a = _mm256_loadu_ps(a);\n    const __m256 _b = _mm256_loadu_ps(b);\n    const __m256 _output = _mm256_add_ps(_a, _b);\n    _mm256_storeu_ps(output, _output);\n  }\n  if (len) {\n    BaseReduceSum(a, b, output, len);\n  }\n}\n#endif\n\ninline void DynamicWdAdagradOptimize(float* num, float* norm, const float* grad,\n                            size_t len, float lr, float w_decay,\n                            bool decouple_wd = false) {\n#if defined(_ENABLE_AVX) && defined(__AVX__)\n  if (decouple_wd) {\n    Avx256DynamicWdAdagradOptimizeDecoupleWd(num, norm, grad, len, lr, w_decay);\n  } else {\n    Avx256DynamicWdAdagradOptimize(num, norm, grad, len, lr, w_decay);\n  }\n#else\n  BaselineDynamicWdAdagradOptimize(\n    num, norm, grad, len, lr, w_decay, decouple_wd);\n#endif\n}\n\ninline void ReduceSum(const float* a, const float* b, float* output,\n                      size_t len) {\n#if defined(_ENABLE_AVX) && defined(__AVX__)\n  Avx256ReduceSum(a, b, output, len);\n#else\n  BaseReduceSum(a, b, output, len);\n#endif\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_DYNAMIC_WD_AVX_UTILS\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/ftrl_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cmath>\n#include <memory>\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/ftrl_optimizer.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass FtrlOptimizer : public OptimizerInterface {\n public:\n  explicit FtrlOptimizer(FtrlOptimizerConfig config)\n      : conf_(std::move(config)) {}\n\n  // We need both Zero and Norm in the opt state.\n  int64_t SizeBytes() const override {\n    return 2 * conf_.dim_size() * sizeof(float);\n  }\n\n  int64_t UncompressedSizeBytes() const override { return SizeBytes(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Ftrl(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {\n    float* norm = static_cast<float*>(ctx);\n    float* zero = norm + conf_.dim_size();\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      norm[i] = conf_.initial_accumulator_value();\n      zero[i] = 0;\n    }\n  }\n\n  // Please refer to this link for the algorithm:\n  // https://www.eecs.tufts.edu/~dsculley/papers/ad-click-prediction.pdf\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    float* norm = static_cast<float*>(ctx);\n    float* zero = norm + conf_.dim_size();\n    float effective_lr = learning_rates[0];\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      auto norm_new = norm[i] + grad[i] * grad[i];\n      auto sigma = (std::sqrt(norm_new) - std::sqrt(norm[i])) / effective_lr;\n      zero[i] += (grad[i] - sigma * num[i]);\n      norm[i] = norm_new;\n      num[i] = (std::abs(zero[i]) > conf_.l1_regularization_strength())\n                   ? effective_lr *\n                         (std::signbit(zero[i]) *\n                              conf_.l1_regularization_strength() -\n                          zero[i]) /\n                         (std::sqrt(norm[i]) + conf_.beta() +\n                          conf_.l2_regularization_strength() * effective_lr)\n                   : 0.0;\n    }\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    FtrlOptimizerDump* ftrl_dump = dump.add_dump()->mutable_ftrl();\n    const float* norm = static_cast<const float*>(ctx);\n    const float* zero = norm + conf_.dim_size();\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      ftrl_dump->add_norm(norm[i]);\n      ftrl_dump->add_zero(zero[i]);\n    }\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    const FtrlOptimizerDump& ftrl_dump = dump.dump(0).ftrl();\n    float* norm = static_cast<float*>(ctx);\n    float* zero = norm + conf_.dim_size();\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      norm[i] = ftrl_dump.norm(i);\n      zero[i] = ftrl_dump.zero(i);\n    }\n  }\n\n private:\n  FtrlOptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewFtrlOptimizer(\n    FtrlOptimizerConfig config) {\n  return std::make_unique<FtrlOptimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/ftrl_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_FTRL_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_FTRL_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewFtrlOptimizer(\n    FtrlOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_FTRL_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/ftrl_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/ftrl_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\nTEST(FtrlOptimizer, Basic) {\n  FtrlOptimizerConfig config;\n  config.set_dim_size(1);\n  auto opt = NewFtrlOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected = {-0.009995f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).ftrl().norm(0), 1e-6, 100.1);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected2 = {-0.0170643f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\nTEST(FtrlOptimizer, ListUpdate) {\n  FtrlOptimizerConfig config;\n  config.set_dim_size(2);\n  auto opt = NewFtrlOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected = {-0.009995f, -0.00953463f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).ftrl().norm(0), 1e-6, 100.1);\n  EXPECT_NEAR(dump.dump(0).ftrl().norm(1), 1e-6, 1.1);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected2 = {-0.0170643f, -0.0164353f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/group_adagrad_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cmath>\n#include <memory>\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/group_adagrad_optimizer.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass GroupAdaGradOptimizer : public OptimizerInterface {\n public:\n  explicit GroupAdaGradOptimizer(GroupAdaGradOptimizerConfig config)\n      : conf_(std::move(config)) {}\n\n  // Only need 4 byte, grad_square_sum = sum(g_max^2)\n  int64_t SizeBytes() const override { return sizeof(float); }\n\n  int64_t UncompressedSizeBytes() const override {\n    return conf_.dim_size() * sizeof(float);\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"GroupAdaGrad(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {\n    float* grad_square_sum = static_cast<float*>(ctx);\n    *grad_square_sum = conf_.initial_accumulator_value();\n  }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    float* grad_square_sum = static_cast<float*>(ctx);\n    float effective_lr = learning_rates[0];\n\n    float max_grad_square = 0.0;\n    std::vector<float> g_decayed;\n    g_decayed.reserve(conf_.dim_size());\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      // weight_decay\n      float g = grad[i] + conf_.weight_decay_factor() * num[i];\n      if (g * g > max_grad_square) {\n        max_grad_square = g * g;\n      }\n      g_decayed.push_back(g);\n    }\n\n    *grad_square_sum = *grad_square_sum + max_grad_square;\n    float lr = effective_lr / (conf_.beta() + std::sqrt(*grad_square_sum));\n\n    float z_norm = 0.0;\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      num[i] = g_decayed[i] - num[i] / lr;\n      z_norm += num[i] * num[i];\n    }\n\n    z_norm = std::sqrt(z_norm);\n    if (z_norm < conf_.l2_regularization_strength()) {\n      for (int i = 0; i < conf_.dim_size(); ++i) {\n        num[i] = 0;\n      }\n    } else {\n      float coeffi =\n          -lr * (z_norm - conf_.l2_regularization_strength()) / z_norm;\n      for (int i = 0; i < conf_.dim_size(); ++i) {\n        num[i] = coeffi * num[i];\n      }\n    }\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    GroupAdaGradOptimizerDump* group_adagrad_dump =\n        dump.add_dump()->mutable_group_adagrad();\n    const float* grad_square_sum = static_cast<const float*>(ctx);\n    group_adagrad_dump->set_grad_square_sum(*grad_square_sum);\n\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    const GroupAdaGradOptimizerDump& group_adagrad_dump =\n        dump.dump(0).group_adagrad();\n    float* grad_square_sum = static_cast<float*>(ctx);\n    *grad_square_sum = group_adagrad_dump.grad_square_sum();\n  }\n\n private:\n  GroupAdaGradOptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewGroupAdaGradOptimizer(\n    GroupAdaGradOptimizerConfig config) {\n  return std::make_unique<GroupAdaGradOptimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/group_adagrad_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_GROUP_ADAGRAD_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_GROUP_ADAGRAD_OPTIMIZER\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewGroupAdaGradOptimizer(\n    GroupAdaGradOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_GROUP_ADAGRAD_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/group_adagrad_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/group_adagrad_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::ElementsAreArray;\nusing ::testing::FloatNear;\nusing ::testing::Pointwise;\n\nTEST(GroupAdaGradOptimizer, Basic) {\n  GroupAdaGradOptimizerConfig config;\n  config.set_dim_size(1);\n  config.set_l2_regularization_strength(1.0);\n  config.set_beta(1.0);\n  config.set_initial_accumulator_value(0.0);\n  config.set_weight_decay_factor(0.0);\n  auto opt = NewGroupAdaGradOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected = {-0.008182f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).group_adagrad().grad_square_sum(), 1e-6, 100.0);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem2.mutable_ctx(), mem2.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected2 = {-0.014125f};\n  ASSERT_THAT(mem2.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\nTEST(GroupAdaGradOptimizer, ListUpdate) {\n  GroupAdaGradOptimizerConfig config;\n  config.set_dim_size(2);\n  config.set_l2_regularization_strength(0.5);\n  config.set_beta(1.0);\n  config.set_initial_accumulator_value(0.0);\n  config.set_weight_decay_factor(0.0);\n  auto opt = NewGroupAdaGradOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f},\n                {0.01f});\n  auto expected = {-0.008639f, -0.000864f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).group_adagrad().grad_square_sum(), 1e-6, 100.0);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem2.mutable_ctx(), mem2.mutable_num_span(), {1.0f, 5.0f},\n                {0.01f});\n  auto expected2 = {-0.009096f, -0.004778f};\n  ASSERT_THAT(mem2.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\nTEST(GroupAdaGradOptimizer, ZeroLambda) {\n  GroupAdaGradOptimizerConfig config;\n  config.set_dim_size(2);\n  config.set_l2_regularization_strength(0);\n  config.set_beta(1.0);\n  config.set_initial_accumulator_value(0.0);\n  config.set_weight_decay_factor(0.0);\n  auto opt = NewGroupAdaGradOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f},\n                {0.01f});\n  auto expected = {-0.009091f, -0.000909f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n}\n\nTEST(GroupAdaGradOptimizer, SetZero) {\n  GroupAdaGradOptimizerConfig config;\n  config.set_dim_size(2);\n  config.set_l2_regularization_strength(1000);\n  config.set_beta(1.0);\n  config.set_initial_accumulator_value(0.0);\n  config.set_weight_decay_factor(0.0);\n  auto opt = NewGroupAdaGradOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f},\n                {0.01f});\n  auto expected = {0.0f, 0.0f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/group_ftrl_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/group_ftrl_optimizer.h\"\n\n#include <exception>\n\n#include \"absl/strings/str_format.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewGroupFtrlOptimizer(\n    GroupFtrlOptimizerConfig config) {\n  throw std::invalid_argument(absl::StrFormat(\n      \"optimizer is not implemented yet. %s\", config.ShortDebugString()));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/group_ftrl_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_GROUP_FTRL_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_GROUP_FTRL_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewGroupFtrlOptimizer(\n    GroupFtrlOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_GROUP_FTRL_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/group_ftrl_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/group_ftrl_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/momentum_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/momentum_optimizer.h\"\n#include <cmath>\n#include <memory>\n#include \"absl/strings/str_format.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass MomentumOptimizer : public OptimizerInterface {\n public:\n  explicit MomentumOptimizer(MomentumOptimizerConfig config)\n      : conf_(std::move(config)) {}\n\n  int64_t SizeBytes() const override {\n    return (conf_.dim_size()) * sizeof(float);\n  }\n\n  int64_t UncompressedSizeBytes() const override { return SizeBytes(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Momentum(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {\n    float* n = static_cast<float*>(ctx);\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      n[i] = 0;\n    }\n  }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    float* n = static_cast<float*>(ctx);\n    float g_total = 0;\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      float dx =\n          learning_rates[0] * (grad[i] + conf_.weight_decay_factor() * num[i]);\n      float new_n = n[i];\n      float new_w = num[i];\n      if (conf_.use_nesterov()) {\n        float prev_n = new_n;\n        new_n = conf_.momentum() * new_n - dx;\n        new_w += -conf_.momentum() * prev_n + (1 + conf_.momentum()) * new_n;\n      } else {\n        new_n = conf_.momentum() * new_n - dx;\n        new_w += new_n;\n      }\n      n[i] = new_n;\n      num[i] = new_w;\n      g_total += new_n;\n    }\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    MomentumOptimizerDump* momentum_dump = dump.add_dump()->mutable_momentum();\n    const float* n = static_cast<const float*>(ctx);\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      momentum_dump->add_n(n[i]);\n    }\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    const MomentumOptimizerDump& momentum_dump = dump.dump(0).momentum();\n    float* n = static_cast<float*>(ctx);\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      n[i] = momentum_dump.n(i);\n    }\n  }\n\n private:\n  MomentumOptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewMomentumOptimizer(\n    MomentumOptimizerConfig config) {\n  return std::make_unique<MomentumOptimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/momentum_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_MOMENTUM_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_MOMENTUM_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewMomentumOptimizer(\n    MomentumOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_MOMENTUM_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/momentum_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/momentum_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\nTEST(MomentumOptimizer, Basic) {\n  MomentumOptimizerConfig config;\n  config.set_dim_size(1);\n  auto opt = NewMomentumOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected = {-0.1f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).momentum().n(0), -0.1, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected2 = {-0.29f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\nTEST(MomentumOptimizer, ListUpdate) {\n  MomentumOptimizerConfig config;\n  config.set_dim_size(2);\n  auto opt = NewMomentumOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected = {-0.1f, -0.01f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).momentum().n(0), -0.1, 1e-4);\n  EXPECT_NEAR(dump.dump(0).momentum().n(1), -0.01, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected2 = {-0.29f, -0.029f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/moving_average_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/moving_average_optimizer.h\"\n#include <cmath>\n#include <memory>\n#include \"absl/strings/str_format.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass MovingAverageOptimizer : public OptimizerInterface {\n public:\n  explicit MovingAverageOptimizer(MovingAverageOptimizerConfig config)\n      : conf_(std::move(config)) {}\n\n  int64_t SizeBytes() const override { return 0; }\n\n  int64_t UncompressedSizeBytes() const override { return SizeBytes(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"MovingAverage(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {}\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      float new_w =\n          conf_.momentum() * num[i] + (1 - conf_.momentum()) * grad[i];\n      num[i] = new_w;\n    }\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {}\n\n private:\n  MovingAverageOptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewMovingAverageOptimizer(\n    MovingAverageOptimizerConfig config) {\n  return std::make_unique<MovingAverageOptimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/moving_average_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_MOVING_AVERAGE_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_MOVING_AVERAGE_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewMovingAverageOptimizer(\n    MovingAverageOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_MOVING_AVERAGE_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/moving_average_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/moving_average_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\nTEST(MovingAverageOptimizer, Basic) {\n  MovingAverageOptimizerConfig config;\n  config.set_dim_size(1);\n  auto opt = NewMovingAverageOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected = {1.0f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  // EXPECT_NEAR(dump.dump(0).momentum().n(0), -0.1, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected2 = {1.9f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\nTEST(MovingAverageOptimizer, ListUpdate) {\n  MovingAverageOptimizerConfig config;\n  config.set_dim_size(2);\n  auto opt = NewMovingAverageOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected = {1.0f, 0.1f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  // EXPECT_NEAR(dump.dump(0).momentum().n(0), -0.1, 1e-4);\n  // EXPECT_NEAR(dump.dump(0).momentum().n(1), -0.01, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected2 = {1.9f, 0.19f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/optimizer.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\n\npackage monolith.hash_table;\n\nmessage AdagradOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.001];\n  optional float initial_accumulator_value = 3 [default = 0.1];\n  optional int32 hessian_compression_times = 4 [default = 1];\n  optional float weight_decay_factor = 5 [default = 0.];\n  optional int64 warmup_steps = 6 [default = 0];\n}\n\nmessage AdagradOptimizerDump {\n  repeated float norm = 1;\n}\n\nmessage DynamicWdAdagradOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.001];\n  optional float initial_accumulator_value = 3 [default = 0.1];\n  optional int32 hessian_compression_times = 4 [default = 1];\n  optional float weight_decay_factor = 5 [default = 0.];\n  optional int64 warmup_steps = 6 [default = 0];\n  optional bool decouple_weight_decay = 7 [default = false];\n  optional bool enable_dynamic_wd = 8 [default = false];\n  optional float dynamic_wd_temperature = 9 [default = 1.0];\n  optional bool flip_direction = 10 [default = false];\n}\n\nmessage DynamicWdAdagradOptimizerDump {\n  repeated float norm = 1;\n  optional int64 last_update_step = 2;\n}\n\nmessage SgdOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.01];\n  optional int64 warmup_steps = 6 [default = 0];\n}\n\nmessage SgdOptimizerDump {\n}\n\nmessage FtrlOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.01];\n  optional float beta = 3 [default = 0.0];\n  optional float initial_accumulator_value = 4 [default = 0.1];\n  optional float l1_regularization_strength = 5 [default = 0.0];\n  optional float l2_regularization_strength = 6 [default = 0.0];\n  optional int64 warmup_steps = 7 [default = 0];\n}\n\nmessage FtrlOptimizerDump {\n  repeated float zero = 1;\n  repeated float norm = 2;\n}\n\nmessage GroupFtrlOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.01];\n  optional float beta = 3 [default = 1.0];\n  optional float initial_accumulator_value = 4 [default = 0.0];\n\n  optional float l1_regularization_strength = 5 [default = 0.0];\n  optional float l2_regularization_strength = 6 [default = 0.0];\n  optional int64 warmup_steps = 7 [default = 0];\n}\n\nmessage GroupFtrlOptimizerDump {\n  repeated float zero = 1;\n  repeated float norm = 2;\n}\n\nmessage GroupAdaGradOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.01];\n  optional float beta = 3 [default = 0.0];\n  optional float initial_accumulator_value = 4 [default = 0.1];\n  optional float l2_regularization_strength = 5 [default = 0.0];\n  optional float weight_decay_factor = 6 [default = 0.0];\n  optional int64 warmup_steps = 7 [default = 0];\n}\n\nmessage GroupAdaGradOptimizerDump {\n  optional float grad_square_sum = 1;\n}\n\nmessage AdadeltaOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.01];\n  optional float weight_decay_factor = 3 [default = 0.];\n  optional float averaging_ratio = 4 [default = 0.9];\n  optional float epsilon = 5 [default = 0.01];\n  optional int64 warmup_steps = 7 [default = 0];\n}\n\nmessage AdadeltaOptimizerDump {\n  repeated float accum = 1;\n  repeated float accum_update = 2;\n}\n\nmessage AdamOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.01];\n  optional float beta1 = 3 [default = 0.9];\n  optional float beta2 = 4 [default = 0.99];\n  optional bool use_beta1_warmup = 5 [default = false];\n  optional float weight_decay_factor = 6 [default = 0.];\n  optional bool use_nesterov = 7 [default = false];\n  optional float epsilon = 8 [default = 0.01];\n  optional int64 warmup_steps = 9 [default = 0];\n}\n\nmessage AdamOptimizerDump {\n  repeated float m = 1;\n  repeated float v = 2;\n  optional float beta1_power = 3;\n  optional float beta2_power = 4;\n}\n\nmessage AmsgradOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.01];\n  optional float beta1 = 3 [default = 0.9];\n  optional float beta2 = 4 [default = 0.99];\n  optional float weight_decay_factor = 6 [default = 0.];\n  optional bool use_nesterov = 7 [default = false];\n  optional float epsilon = 8 [default = 0.01];\n  optional int64 warmup_steps = 9 [default = 0];\n}\n\nmessage AmsgradOptimizerDump {\n  repeated float m = 1;\n  repeated float v = 2;\n  repeated float vhat = 3;\n  optional float beta1_power = 4;\n  optional float beta2_power = 5;\n}\n\nmessage MomentumOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.01];\n  optional float weight_decay_factor = 3 [default = 0.];\n  optional bool use_nesterov = 4 [default = false];\n  optional float momentum = 5 [default = 0.9];\n  optional int64 warmup_steps = 6 [default = 0];\n}\n\nmessage MomentumOptimizerDump {\n  repeated float n = 1;\n}\n\nmessage MovingAverageOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float momentum = 2 [default = 0.9];\n}\n\nmessage BatchSoftmaxOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.1];\n}\n\nmessage BatchSoftmaxOptimizerDump {\n  optional int64 global_step = 1;\n}\n\nmessage RmspropOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.01];\n  optional float weight_decay_factor = 3 [default = 0.];\n  optional float momentum = 4 [default = 0.9];\n}\n\nmessage RmspropOptimizerDump {\n  repeated float n = 1;\n}\n\nmessage RmspropV2OptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float learning_rate = 2 [default = 0.01];\n  optional float weight_decay_factor = 3 [default = 0.];\n  optional float momentum = 4 [default = 0.9];\n}\n\nmessage RmspropV2OptimizerDump {\n  repeated float n = 1;\n}\n\nmessage DcOptimizerConfig {\n  optional int32 dim_size = 1;\n  optional float lambda_ = 2 [default = 0.];\n}\n\nmessage OptimizerConfig {\n  oneof type {\n    AdagradOptimizerConfig adagrad = 1;\n    SgdOptimizerConfig sgd = 2;\n    FtrlOptimizerConfig ftrl = 3;\n    DynamicWdAdagradOptimizerConfig dynamic_wd_adagrad = 5;\n    AdadeltaOptimizerConfig adadelta = 6;\n    AdamOptimizerConfig adam = 7;\n    AmsgradOptimizerConfig amsgrad = 8;\n    MomentumOptimizerConfig momentum = 9;\n    MovingAverageOptimizerConfig moving_average = 10;\n    RmspropOptimizerConfig rmsprop = 11;\n    RmspropV2OptimizerConfig rmspropv2 = 12;\n    DcOptimizerConfig dc = 13;\n    GroupFtrlOptimizerConfig group_ftrl = 14;\n    BatchSoftmaxOptimizerConfig batch_softmax = 15;\n    GroupAdaGradOptimizerConfig group_adagrad = 16;\n  }\n  optional bool stochastic_rounding_float16 = 4;  // Default false.\n}\n\nmessage SingleOptimizerDump {\n  oneof type {\n    AdagradOptimizerDump adagrad = 1;\n    SgdOptimizerDump sgd = 2;\n    FtrlOptimizerDump ftrl = 3;\n    DynamicWdAdagradOptimizerDump dynamic_wd_adagrad = 4;\n    AdadeltaOptimizerDump adadelta = 6;\n    AdamOptimizerDump adam = 7;\n    AmsgradOptimizerDump amsgrad = 8;\n    MomentumOptimizerDump momentum = 9;\n    RmspropOptimizerDump rmsprop = 11;\n    RmspropV2OptimizerDump rmspropv2 = 12;\n    GroupFtrlOptimizerDump group_ftrl = 13;\n    BatchSoftmaxOptimizerDump batch_softmax = 14;\n    GroupAdaGradOptimizerDump group_adagrad = 15;\n  }\n}\n\n// TODO(leqi.zou): Consider about adding Arena to improve the performance.\nmessage OptimizerDump {\n  repeated SingleOptimizerDump dump = 1;\n}\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/optimizer_combination.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_combination.h\"\n\n#include \"absl/algorithm/container.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/types/span.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nnamespace proto2 = google::protobuf;\n\nclass CombinedOptimizer : public OptimizerInterface {\n public:\n  CombinedOptimizer(std::unique_ptr<OptimizerInterface> opt1,\n                    std::unique_ptr<OptimizerInterface> opt2)\n      : opt1_(std::move(opt1)),\n        size_bytes1_(opt1_->SizeBytes()),\n        dim_size1_(opt1_->DimSize()),\n        dump_size1_(GetOptDumpSize(opt1_.get())),\n        slice_size1_(opt1_->SliceSize()),\n        opt2_(std::move(opt2)) {}\n\n  int64_t SizeBytes() const override {\n    return opt1_->SizeBytes() + opt2_->SizeBytes();\n  }\n\n  int64_t UncompressedSizeBytes() const override {\n    return opt1_->UncompressedSizeBytes() + opt2_->UncompressedSizeBytes();\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"%s|%s\", opt1_->DebugString(), opt2_->DebugString());\n  }\n\n  int DimSize() const override { return opt1_->DimSize() + opt2_->DimSize(); }\n\n  int SliceSize() const override {\n    return opt1_->SliceSize() + opt2_->SliceSize();\n  }\n\n  void Init(void* ctx) const override {\n    void* ctx2 = static_cast<char*>(ctx) + size_bytes1_;\n    opt1_->Init(ctx);\n    opt2_->Init(ctx2);\n  }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    void* ctx2 = static_cast<char*>(ctx) + size_bytes1_;\n    auto num2 = num.subspan(dim_size1_);\n    auto grad2 = grad.subspan(dim_size1_);\n    auto learning_rates2 = learning_rates.subspan(slice_size1_);\n    opt1_->Optimize(ctx, num, grad, learning_rates, global_step);\n    opt2_->Optimize(ctx2, num2, grad2, learning_rates2, global_step);\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump combined_dump;\n    OptimizerDump dump1 = opt1_->Save(ctx);\n    const void* ctx2 = static_cast<const char*>(ctx) + size_bytes1_;\n    OptimizerDump dump2 = opt2_->Save(ctx2);\n    absl::c_move(*dump1.mutable_dump(), proto2::RepeatedFieldBackInserter(\n                                            combined_dump.mutable_dump()));\n    absl::c_move(*dump2.mutable_dump(), proto2::RepeatedFieldBackInserter(\n                                            combined_dump.mutable_dump()));\n    return combined_dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    OptimizerDump dump1;\n    for (int i = 0; i < dump_size1_; ++i) {\n      *dump1.add_dump() = std::move(*dump.mutable_dump(i));\n    }\n    OptimizerDump dump2;\n    for (int i = dump_size1_; i < dump.dump_size(); ++i) {\n      *dump2.add_dump() = std::move(*dump.mutable_dump(i));\n    }\n    opt1_->Restore(ctx, std::move(dump1));\n    void* ctx2 = static_cast<char*>(ctx) + size_bytes1_;\n    opt2_->Restore(ctx2, std::move(dump2));\n  }\n\n private:\n  int GetOptDumpSize(OptimizerInterface* opt) {\n    auto mem = std::make_unique<char[]>(opt->SizeBytes());\n    opt->Init(mem.get());\n    OptimizerDump dump = opt->Save(mem.get());\n    return dump.dump_size();\n  }\n\n  std::unique_ptr<OptimizerInterface> opt1_;\n  const int64_t size_bytes1_;\n  const int dim_size1_;\n  const int dump_size1_;\n  const int slice_size1_;\n  std::unique_ptr<OptimizerInterface> opt2_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> CombineOptimizers(\n    std::unique_ptr<OptimizerInterface> opt1,\n    std::unique_ptr<OptimizerInterface> opt2) {\n  return std::make_unique<CombinedOptimizer>(std::move(opt1), std::move(opt2));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/optimizer_combination.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_COMBINATION\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_COMBINATION\n#include <memory>\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\n// A entry may be optimized by different optimizers so we need to combine two\n// optimizers.\nstd::unique_ptr<OptimizerInterface> CombineOptimizers(\n    std::unique_ptr<OptimizerInterface> opt1,\n    std::unique_ptr<OptimizerInterface> opt2);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_COMBINATION\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/optimizer_combination_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_combination.h\"\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/adagrad_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\n\nTEST(CombineOptimizers, Basic) {\n  AdagradOptimizerConfig config1;\n  config1.set_dim_size(1);\n  config1.set_initial_accumulator_value(1);\n  auto opt1 = NewAdagradOptimizer(config1);\n\n  AdagradOptimizerConfig config2;\n  config2.set_dim_size(2);\n  config2.set_initial_accumulator_value(2);\n  auto opt2 = NewAdagradOptimizer(config2);\n\n  auto combined_opt = CombineOptimizers(std::move(opt1), std::move(opt2));\n  OptimizerDump dump;\n  {\n    TestOptimizerEntry mem(combined_opt.get());\n    combined_opt->Init(mem.mutable_ctx());\n    combined_opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(),\n                           {1.0f, 2.0f, 3.0f}, {1.0f, 2.0f});\n    auto expected = {-0.70710677, -1.6329931, -1.8090681};\n    EXPECT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n    dump = combined_opt->Save(mem.ctx());\n  }\n  TestOptimizerEntry mem(combined_opt.get());\n  combined_opt->Restore(mem.mutable_ctx(), dump);\n  combined_opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(),\n                         {1.0f, 2.0f, 3.0f}, {1.0f, 2.0f});\n  auto expected = {-0.57735026, -1.264911, -1.3416407};\n  EXPECT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/optimizer_decorator.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_DECORATOR\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_DECORATOR\n#include <cstdint>\n\n#include \"absl/types/span.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nclass OptimizerDecorator : public OptimizerInterface {\n public:\n  virtual ~OptimizerDecorator() = default;\n\n  explicit OptimizerDecorator(std::unique_ptr<OptimizerInterface> base_opt)\n      : base_opt_(std::move(base_opt)) {}\n\n  int64_t SizeBytes() const override { return base_opt_.get()->SizeBytes(); }\n\n  int64_t UncompressedSizeBytes() const override {\n    return base_opt_.get()->UncompressedSizeBytes();\n  }\n\n  std::string DebugString() const override {\n    return base_opt_.get()->DebugString();\n  }\n\n  int DimSize() const override { return base_opt_.get()->DimSize(); }\n\n  int SliceSize() const override { return base_opt_.get()->SliceSize(); }\n\n  void Init(void* ctx) const override { return base_opt_.get()->Init(ctx); }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step = 0) const override {\n    return base_opt_.get()->Optimize(ctx, num, grad, learning_rates);\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    return base_opt_.get()->Save(ctx);\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    return base_opt_.get()->Restore(ctx, dump);\n  }\n\n  virtual void OptimizeWithLatestValue(void* ctx, absl::Span<float> num,\n                                       absl::Span<const float> grad,\n                                       absl::Span<const float> learning_rates,\n                                       absl::Span<float> latest_value,\n                                       const int64_t global_step = 0) const = 0;\n\n protected:\n  std::unique_ptr<OptimizerInterface> base_opt_;\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_DECORATOR\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/optimizer_factory.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <exception>\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_factory.h\"\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/adadelta_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/adagrad_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/adam_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/amsgrad_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/batch_softmax_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/dynamic_wd_adagrad_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/ftrl_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/group_adagrad_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/group_ftrl_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/momentum_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/moving_average_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/rmsprop_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/sgd_optimizer.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/stochastic_rounding.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewOptimizerFromConfig(\n    OptimizerConfig config) {\n  std::unique_ptr<OptimizerInterface> opt = nullptr;\n  switch (config.type_case()) {\n    case OptimizerConfig::kAdagrad:\n      opt = NewAdagradOptimizer(std::move(*config.mutable_adagrad()));\n      break;\n    case OptimizerConfig::kSgd:\n      opt = NewSgdOptimizer(std::move(*config.mutable_sgd()));\n      break;\n    case OptimizerConfig::kFtrl:\n      opt = NewFtrlOptimizer(std::move(*config.mutable_ftrl()));\n      break;\n    case OptimizerConfig::kDynamicWdAdagrad:\n      opt = NewDynamicWdAdagradOptimizer(\n          std::move(*config.mutable_dynamic_wd_adagrad()));\n      break;\n    case OptimizerConfig::kAdadelta:\n      opt = NewAdadeltaOptimizer(std::move(*config.mutable_adadelta()));\n      break;\n    case OptimizerConfig::kAdam:\n      opt = NewAdamOptimizer(std::move(*config.mutable_adam()));\n      break;\n    case OptimizerConfig::kAmsgrad:\n      opt = NewAmsgradOptimizer(std::move(*config.mutable_amsgrad()));\n      break;\n    case OptimizerConfig::kMomentum:\n      opt = NewMomentumOptimizer(std::move(*config.mutable_momentum()));\n      break;\n    case OptimizerConfig::kMovingAverage:\n      opt = NewMovingAverageOptimizer(\n          std::move(*config.mutable_moving_average()));\n      break;\n    case OptimizerConfig::kRmsprop:\n      opt = NewRmspropOptimizer(std::move(*config.mutable_rmsprop()));\n      break;\n    case OptimizerConfig::kRmspropv2:\n      opt = NewRmspropV2Optimizer(std::move(*config.mutable_rmspropv2()));\n      break;\n    case OptimizerConfig::kGroupFtrl:\n      opt = NewGroupFtrlOptimizer(std::move(*config.mutable_group_ftrl()));\n      break;\n    case OptimizerConfig::kGroupAdagrad:\n      opt =\n          NewGroupAdaGradOptimizer(std::move(*config.mutable_group_adagrad()));\n      break;\n    case OptimizerConfig::kBatchSoftmax:\n      opt =\n          NewBatchSoftmaxOptimizer(std::move(*config.mutable_batch_softmax()));\n      break;\n    default:\n      throw std::invalid_argument(absl::StrFormat(\n          \"optimizer is not implemented yet. %s\", config.ShortDebugString()));\n  }\n  if (config.stochastic_rounding_float16()) {\n    opt = std::make_unique<StochasticRoundingFloat16OptimizerDecorator>(\n        std::move(opt));\n  }\n  return std::move(opt);\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/optimizer_factory.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_FACTORY\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_FACTORY\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewOptimizerFromConfig(\n    OptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_FACTORY\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_INTERFACE\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_INTERFACE\n#include <cstdint>\n\n#include \"absl/types/span.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nclass OptimizerInterface {\n public:\n  virtual ~OptimizerInterface() = default;\n\n  // How many bytes are required for the optimizer\n  virtual int64_t SizeBytes() const = 0;\n\n  // How many bytes are required for the optimizer if not compressed.\n  virtual int64_t UncompressedSizeBytes() const = 0;\n\n  virtual std::string DebugString() const = 0;\n\n  // The dim that this optimizer can support.\n  virtual int DimSize() const = 0;\n\n  // The slice size that this optimizer holds.\n  virtual int SliceSize() const = 0;\n\n  // Init optimizer ctx.\n  // |num| is at least DimSize() long.\n  virtual void Init(void* ctx) const = 0;\n\n  // optimize the num based on gradients and the optimizer's data.\n  // |num|, |grad| are float arrays whose length is at least DimSize().\n  virtual void Optimize(void* ctx, absl::Span<float> num,\n                        absl::Span<const float> grad,\n                        absl::Span<const float> learning_rates,\n                        const int64_t global_step = 0) const = 0;\n\n  // Save and restore the entry.\n  virtual OptimizerDump Save(const void* ctx) const = 0;\n  virtual void Restore(void* ctx, OptimizerDump dump) const = 0;\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_OPTIMIZER_INTERFACE\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/rmsprop_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/rmsprop_optimizer.h\"\n#include <cmath>\n#include <memory>\n#include \"absl/strings/str_format.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass RmspropOptimizer : public OptimizerInterface {\n public:\n  explicit RmspropOptimizer(RmspropOptimizerConfig config)\n      : conf_(std::move(config)) {}\n\n  int64_t SizeBytes() const override {\n    return (conf_.dim_size()) * sizeof(float);\n  }\n\n  int64_t UncompressedSizeBytes() const override { return SizeBytes(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Rmsprop(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {\n    float* n = static_cast<float*>(ctx);\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      n[i] = 0;\n    }\n  }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    float* n = static_cast<float*>(ctx);\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      const float& cur_grad = grad[i];\n      float new_n = n[i];\n      float new_w = num[i];\n      double dx =\n          cur_grad + static_cast<double>(conf_.weight_decay_factor()) * new_w;\n      new_n = static_cast<double>(conf_.momentum()) * new_n +\n              (1 - static_cast<double>(conf_.momentum())) * dx * dx;\n      double eta =\n          static_cast<double>(conf_.learning_rate()) / (std::sqrt(new_n) + 1);\n      new_w -= eta * dx;\n      n[i] = new_n;\n      num[i] = new_w;\n    }\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    RmspropOptimizerDump* rmsprop_dump = dump.add_dump()->mutable_rmsprop();\n    const float* n = static_cast<const float*>(ctx);\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      rmsprop_dump->add_n(n[i]);\n    }\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    const RmspropOptimizerDump& rmsprop_dump = dump.dump(0).rmsprop();\n    float* n = static_cast<float*>(ctx);\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      n[i] = rmsprop_dump.n(i);\n    }\n  }\n\n private:\n  RmspropOptimizerConfig conf_;\n};\n\nclass RmspropV2Optimizer : public OptimizerInterface {\n public:\n  explicit RmspropV2Optimizer(RmspropV2OptimizerConfig config)\n      : conf_(std::move(config)) {}\n\n  int64_t SizeBytes() const override {\n    return (conf_.dim_size()) * sizeof(float);\n  }\n\n  int64_t UncompressedSizeBytes() const override { return SizeBytes(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"RmspropV2(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {\n    float* n = static_cast<float*>(ctx);\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      n[i] = 0;\n    }\n  }\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    // TODO(eric.wei): implement RMSPropV2 using AVX\n    OptimizeNormal(ctx, num, grad, learning_rates, global_step);\n  }\n\n  void OptimizeNormal(void* ctx, absl::Span<float> num,\n                      absl::Span<const float> grad,\n                      absl::Span<const float> learning_rates,\n                      const int64_t global_step) const {\n    float* n = static_cast<float*>(ctx);\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      const float& cur_grad = grad[i];\n      float new_n = n[i];\n      float new_w = num[i];\n      double dx =\n          cur_grad + static_cast<double>(conf_.weight_decay_factor()) * new_w;\n      new_n = static_cast<double>(conf_.momentum()) * new_n + dx * dx;\n      double eta =\n          static_cast<double>(learning_rates[0]) / (std::sqrt(new_n) + 1);\n      new_w -= eta * dx;\n      n[i] = new_n;\n      num[i] = new_w;\n    }\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    RmspropV2OptimizerDump* rmspropv2_dump =\n        dump.add_dump()->mutable_rmspropv2();\n    const float* n = static_cast<const float*>(ctx);\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      rmspropv2_dump->add_n(n[i]);\n    }\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    const RmspropV2OptimizerDump& rmspropv2_dump = dump.dump(0).rmspropv2();\n    float* n = static_cast<float*>(ctx);\n\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      n[i] = rmspropv2_dump.n(i);\n    }\n  }\n\n private:\n  RmspropV2OptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewRmspropOptimizer(\n    RmspropOptimizerConfig config) {\n  return std::make_unique<RmspropOptimizer>(std::move(config));\n}\n\nstd::unique_ptr<OptimizerInterface> NewRmspropV2Optimizer(\n    RmspropV2OptimizerConfig config) {\n  return std::make_unique<RmspropV2Optimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/rmsprop_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_RMSPROP_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_RMSPROP_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewRmspropOptimizer(\n    RmspropOptimizerConfig config);\n\nstd::unique_ptr<OptimizerInterface> NewRmspropV2Optimizer(\n    RmspropV2OptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_RMSPROP_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/rmsprop_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/rmsprop_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\nusing ::testing::ElementsAreArray;\n\nTEST(RmspropOptimizer, Basic) {\n  RmspropOptimizerConfig config;\n  config.set_dim_size(1);\n  auto opt = NewRmspropOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected = {-0.024025f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).rmsprop().n(0), 10, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f}, {0.01f});\n  auto expected2 = {-0.042686f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\nTEST(RmspropV2Optimizer, ListUpdate) {\n  RmspropV2OptimizerConfig config;\n  config.set_dim_size(2);\n  auto opt = NewRmspropV2Optimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected = {-0.0090909f, -0.005f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n\n  // Test dump & restore\n  OptimizerDump dump = opt->Save(mem.ctx());\n  EXPECT_NEAR(dump.dump(0).rmspropv2().n(0), 100, 1e-4);\n  EXPECT_NEAR(dump.dump(0).rmspropv2().n(1), 1, 1e-4);\n  TestOptimizerEntry mem2(opt.get());\n  opt->Restore(mem2.mutable_ctx(), dump);\n  *mem2.mutable_num() = mem.num();\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {10.0f, 1.0f}, {0.01f});\n  auto expected2 = {-0.0158549f, -0.0092045f};\n  ASSERT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected2));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/sgd_optimizer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/sgd_optimizer.h\"\n#include <cmath>\n#include <memory>\n#include \"absl/strings/str_format.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass SgdOptimizer : public OptimizerInterface {\n public:\n  explicit SgdOptimizer(SgdOptimizerConfig config) : conf_(std::move(config)) {}\n\n  int64_t SizeBytes() const override { return 0; }\n\n  int64_t UncompressedSizeBytes() const override { return SizeBytes(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Sgd(D=%d)\", DimSize());\n  }\n\n  int DimSize() const override { return conf_.dim_size(); }\n\n  int SliceSize() const override { return 1; }\n\n  void Init(void* ctx) const override {}\n\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const {\n    float effective_lr = learning_rates[0];\n    for (int i = 0; i < conf_.dim_size(); ++i) {\n      num[i] -= effective_lr * grad[i];\n    }\n  }\n\n  OptimizerDump Save(const void* ctx) const override {\n    OptimizerDump dump;\n    dump.add_dump()->mutable_sgd();\n    return dump;\n  }\n\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    // Do nothing.\n  }\n\n private:\n  SgdOptimizerConfig conf_;\n};\n\n}  // namespace\n\nstd::unique_ptr<OptimizerInterface> NewSgdOptimizer(SgdOptimizerConfig config) {\n  return std::make_unique<SgdOptimizer>(std::move(config));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/sgd_optimizer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_SGD_OPTIMIZER\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_SGD_OPTIMIZER\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<OptimizerInterface> NewSgdOptimizer(SgdOptimizerConfig config);\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_SGD_OPTIMIZER\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/sgd_optimizer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/sgd_optimizer.h\"\n\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Pointwise;\nusing ::testing::FloatNear;\n\nTEST(SgdOptimizer, Basic) {\n  SgdOptimizerConfig config;\n  config.set_dim_size(1);\n  auto opt = NewSgdOptimizer(config);\n  TestOptimizerEntry mem(opt.get());\n  opt->Init(mem.mutable_ctx());\n  opt->Optimize(mem.mutable_ctx(), mem.mutable_num_span(), {1.0f}, {0.1f});\n  auto expected = {-0.1};\n  EXPECT_THAT(mem.num(), Pointwise(FloatNear(1e-6), expected));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/stochastic_rounding.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/optimizer/stochastic_rounding.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nthread_local std::vector<uint64_t> StochasticRoundingFloat16OptimizerDecorator::rng_ = {0, 1};\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/stochastic_rounding.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_STOCHASTIC_ROUNDING\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_STOCHASTIC_ROUNDING\n#include <cstdint>\n\n#include \"absl/types/span.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n#include \"third_party/half_sourceforge_net/half.hpp\"\n\nnamespace monolith {\nnamespace hash_table {\n\ninline float stochastic_round(float vf, float p) {\n  unsigned int half_up =\n      half_float::detail::float2half<std::round_toward_infinity>(vf);\n  unsigned int half_down =\n      half_float::detail::float2half<std::round_toward_neg_infinity>(vf);\n  float vf_up = half_float::detail::half2float<float>(half_up);\n  float vf_down = half_float::detail::half2float<float>(half_down);\n  if (p <= (vf - vf_down) / (vf_up - vf_down)) {\n    return vf_up;\n  } else {\n    return vf_down;\n  }\n}\n\nclass StochasticRoundingFloat16OptimizerDecorator : public OptimizerInterface {\n public:\n  explicit StochasticRoundingFloat16OptimizerDecorator(\n      std::unique_ptr<OptimizerInterface> optimizer)\n      : optimizer_(std::move(optimizer)) {}\n\n  // optimize the num based on gradients and the optimizer's data.\n  // |num|, |grad| are float arrays whose length is at least DimSize().\n  // Result `num` will be stochastically rounded.\n  void Optimize(void* ctx, absl::Span<float> num, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step = 0) const override {\n    optimizer_->Optimize(ctx, num, grad, learning_rates);\n    for (size_t i = 0; i < num.size(); ++i) {\n      num[i] = stochastic_round(num[i], rand());\n    }\n  }\n\n  // Forward all other class methods.\n  int64_t SizeBytes() const override { return optimizer_->SizeBytes(); }\n\n  int64_t UncompressedSizeBytes() const override {\n    return optimizer_->UncompressedSizeBytes();\n  }\n\n  std::string DebugString() const override { return optimizer_->DebugString(); }\n\n  // The dim that this optimizer can support.\n  int DimSize() const override { return optimizer_->DimSize(); }\n\n  // The slice size that this optimizer holds.\n  int SliceSize() const override { return optimizer_->SliceSize(); }\n\n  // Init optimizer ctx.\n  // |num| is at least DimSize() long.\n  void Init(void* ctx) const override { optimizer_->Init(ctx); }\n\n  // Save and restore the entry.\n  OptimizerDump Save(const void* ctx) const override {\n    return optimizer_->Save(ctx);\n  }\n  void Restore(void* ctx, OptimizerDump dump) const override {\n    optimizer_->Restore(ctx, dump);\n  }\n\n private:\n  std::unique_ptr<OptimizerInterface> optimizer_;\n\n  static thread_local std::vector<uint64_t> rng_;\n\n  static void update_rng() {\n    rng_[0] = (36969 * (rng_[0] & 65535) + (rng_[0] >> 16)) & 4294967295;\n    rng_[1] = (18000 * (rng_[1] & 65535) + (rng_[1] >> 16)) & 4294967295;\n  }\n  /*\n   * multiply-with-carry generator to generate a float number in [0, 1],\n   * for stochastic rounding from fp32 to fp16\n   *\n   * About Marsaglia's MWC generator, see\n   * [http://www.cs.yorku.ca/~oz/marsaglia-rng.html]\n   */\n  static float rand() {\n    update_rng();\n    return static_cast<float>((((rng_[0] & 65535) << 16) + rng_[1]) &\n                              4294967295) /\n           4294967296;\n  }\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_STOCHASTIC_ROUNDING\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/stochastic_rounding_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <memory>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_factory.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/stochastic_rounding.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/test_utils.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nTEST(StochasticRoundingFloat16OptimizerDecorator, Basic) {\n  OptimizerConfig config;\n  config.mutable_sgd()->set_dim_size(1);\n\n  // Float32 optimizer.\n  // By default, config.stochastic_rounding_float16() == false\n  auto opt = NewOptimizerFromConfig(config);\n\n  // Float16 optimizer.\n  config.set_stochastic_rounding_float16(true);\n  auto opt_float16 = NewOptimizerFromConfig(config);\n\n  EXPECT_NE(typeid(*(opt.get())), typeid(*(opt_float16.get())));\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/optimizer/test_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_TEST_UTILS\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_TEST_UTILS\n\n#include \"absl/types/span.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/optimizer_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\n// The pre allocated memory for the optimizer.\nclass TestOptimizerEntry {\n public:\n  explicit TestOptimizerEntry(OptimizerInterface* opt) : opt_(opt) {\n    ctx_ = NewCtx();\n    num_ = NewNum();\n  }\n\n  const void* ctx() { return ctx_.get(); }\n  void* mutable_ctx() { return ctx_.get(); }\n\n  std::vector<float>* mutable_num() { return &num_; }\n  const std::vector<float>& num() { return num_; }\n  absl::Span<float> mutable_num_span() { return absl::MakeSpan(num_); }\n\n private:\n  std::unique_ptr<char[]> NewCtx() {\n    return std::make_unique<char[]>(opt_->SizeBytes());\n  }\n\n  std::vector<float> NewNum() {\n    return std::vector<float>(opt_->DimSize(), 0.0f);\n  }\n\n  OptimizerInterface* opt_;\n  std::unique_ptr<char[]> ctx_;\n  std::vector<float> num_;\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_TEST_UTILS\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/quantized_entry_accessor.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_QUANTIZED_ENTRY_ACCESSOR_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_QUANTIZED_ENTRY_ACCESSOR_H_\n\n#include <vector>\n#include \"monolith/native_training/runtime/hash_table/compressor/fake_quantizer.h\"\n#include \"monolith/native_training/runtime/hash_table/compressor/float_compressor.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/entry_accessor_decorator.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstruct SegmentQatConfig {\n  explicit SegmentQatConfig(int dim_size, bool enable_qat = false,\n                            float r = 1.0f)\n      : dim_size(dim_size), enable_qat(enable_qat), r(r) {}\n\n  int dim_size;\n  bool enable_qat;\n  float r;\n};\n\n// Makes the entry accessor support quantized aware training.\nclass QuantizedEntryAccessor : public EntryAccessorDecorator {\n public:\n  QuantizedEntryAccessor(std::unique_ptr<EntryAccessorInterface> accessor,\n                         std::vector<SegmentQatConfig> segment_qat_configs)\n      : EntryAccessorDecorator(std::move(accessor)),\n        configs_(std::move(segment_qat_configs)) {\n    for (const auto &config : configs_) {\n      if (config.enable_qat) {\n        fake_quantizers_.emplace_back(\n            std::make_unique<FakeQuantizer>(config.r));\n      } else {\n        fake_quantizers_.emplace_back(nullptr);\n      }\n    }\n  }\n\n  void Fill(const void *ctx, absl::Span<float> num) const override {\n    int dim_size = entry_accessor_->DimSize();\n    std::vector<float> quantized(dim_size);\n    auto *ctx_float = static_cast<const float *>(ctx);\n\n    // Simulate the quantization on weights\n    int index = 0;\n    for (size_t i = 0; i < fake_quantizers_.size(); ++i) {\n      for (int j = 0; j < configs_[i].dim_size; ++j) {\n        quantized[index] =\n            fake_quantizers_[i] == nullptr\n                ? ctx_float[index]\n                : fake_quantizers_[i]->Quantize(ctx_float[index]);\n        ++index;\n      }\n    }\n\n    absl::c_copy(absl::MakeConstSpan(quantized), num.begin());\n  }\n\n  void Optimize(void *ctx, absl::Span<const float> grad,\n                absl::Span<const float> learning_rates,\n                const int64_t global_step) const override {\n    // Apply gradients to real weights\n    entry_accessor_->Optimize(ctx, grad, learning_rates, global_step);\n  }\n\n private:\n  std::vector<SegmentQatConfig> configs_;\n\n  std::vector<std::unique_ptr<FakeQuantizer>> fake_quantizers_;\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_OPTIMIZER_QUANTIZED_ENTRY_ACCESSOR_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/quantized_entry_accessor_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <memory>\n#include \"absl/strings/str_format.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"monolith/native_training/runtime/hash_table/entry_accessor_decorator.h\"\n#include \"monolith/native_training/runtime/hash_table/quantized_entry_accessor.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::_;\nusing ::testing::ElementsAre;\nusing ::testing::ExplainMatchResult;\nusing ::testing::FloatEq;\nusing ::testing::Invoke;\nusing ::testing::Le;\nusing ::testing::NiceMock;\nusing ::testing::Return;\nusing ::testing::WithArgs;\n\nclass MockEntryAccessor : public EntryAccessorInterface {\n public:\n  MOCK_CONST_METHOD0(SizeBytes, int64_t());\n  MOCK_CONST_METHOD0(UncompressedSizeBytes, int64_t());\n  MOCK_CONST_METHOD0(DebugString, std::string());\n  MOCK_CONST_METHOD0(DimSize, int());\n  MOCK_CONST_METHOD0(SliceSize, int());\n  MOCK_CONST_METHOD1(Init, void(void* ctx));\n  MOCK_CONST_METHOD2(Fill, void(const void* ctx, absl::Span<float>));\n  MOCK_CONST_METHOD2(Assign, void(absl::Span<const float> num, void* ctx));\n  MOCK_CONST_METHOD2(AssignAdd, void(absl::Span<const float> num, void* ctx));\n  MOCK_CONST_METHOD2(Save, EntryDump(const void* ctx, uint32_t));\n  MOCK_CONST_METHOD3(Restore, void(void* ctx, uint32_t*, EntryDump));\n  MOCK_CONST_METHOD4(Optimize, void(void* ctx, absl::Span<const float>,\n                                    absl::Span<const float>, const int64_t));\n};\n\nstruct MockEntryAccessorFakeOption {\n  float init_value = 1.0f;\n  int dim = 10;\n};\n\nvoid MakeMockEntryAccessor(\n    MockEntryAccessor* mock,\n    MockEntryAccessorFakeOption option = MockEntryAccessorFakeOption()) {\n  ON_CALL(*mock, DimSize()).WillByDefault(Return(option.dim));\n  ON_CALL(*mock, SizeBytes())\n      .WillByDefault(Return(option.dim * sizeof(float) * 2));\n  ON_CALL(*mock, Init(_))\n      .WillByDefault(Invoke([option](void* ctx) {\n        // Initialize embedding\n        auto* w = reinterpret_cast<float*>(ctx);\n        for (int i = 0; i < option.dim; ++i) {\n          w[i] = option.init_value;\n        }\n\n        // Initialize optimizer\n        auto* norm = w + option.dim;\n        for (int i = 0; i < option.dim; ++i) {\n          norm[i] = 1.0f;\n        }\n      }));\n\n  ON_CALL(*mock, Optimize(_, _, _, _))\n      .WillByDefault(WithArgs<0, 1, 2, 3>(Invoke([option](\n          void* ctx, absl::Span<const float> grad,\n          absl::Span<const float> learning_rates, const int64_t global_step) {\n        auto* embedding = reinterpret_cast<float*>(ctx);\n        auto* norm = embedding + option.dim;\n        for (int i = 0; i < option.dim; ++i) {\n          norm[i] += grad[i] * grad[i];\n          embedding[i] -= grad[i];\n        }\n      })));\n}\n\nTEST(QuantizedEntryAccessor, FixedRange) {\n  auto accessor = std::make_unique<NiceMock<MockEntryAccessor>>();\n  MakeMockEntryAccessor(accessor.get());\n  int dim_size = accessor->DimSize();\n  auto config1 = SegmentQatConfig(dim_size / 2, true, 1.0f);\n  auto config2 = SegmentQatConfig(dim_size / 2, true, 0.5f);\n  const float kStep1 = config1.r / 128, kStep2 = config2.r / 128;\n  QuantizedEntryAccessor quantized_accessor(std::move(accessor),\n                                            {config1, config2});\n  auto ctx = std::make_unique<char[]>(quantized_accessor.SizeBytes());\n\n  quantized_accessor.Init(ctx.get());\n  std::vector<float> num(dim_size), grad(dim_size, 1.0f);\n  quantized_accessor.Fill(ctx.get(), absl::MakeSpan(num));\n  for (int i = 0; i < dim_size / 2; ++i) {\n    EXPECT_THAT(std::abs(num[i] - 1.0f), Le(kStep1));\n  }\n  for (int i = dim_size / 2; i < dim_size; ++i) {\n    EXPECT_THAT(std::abs(num[i] - 0.5f), Le(kStep2));\n  }\n\n  quantized_accessor.Optimize(ctx.get(), absl::MakeConstSpan(grad), {.0f}, 0);\n  quantized_accessor.Fill(ctx.get(), absl::MakeSpan(num));\n  for (int i = 0; i < dim_size; ++i) {\n    EXPECT_FLOAT_EQ(num[i], 0.0f);\n  }\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_proto_library\", \"cc_test\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime/hash_table:__subpackages__\"])\n\ncc_library(\n    name = \"retriever_interface\",\n    hdrs = [\"retriever_interface.h\"],\n    deps = [\n        \"@com_google_absl//absl/base\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ncc_library(\n    name = \"retriever_base\",\n    hdrs = [\"retriever_base.h\"],\n    deps = [\n        \":retriever_interface\",\n        \"@com_google_absl//absl/base\",\n        \"@com_google_absl//absl/types:span\",\n    ],\n)\n\ncc_library(\n    name = \"raw_retriever\",\n    srcs = [\"raw_retriever.cc\"],\n    hdrs = [\"raw_retriever.h\"],\n    deps = [\n        \":retriever_base\",\n        \"@com_google_absl//absl/algorithm:container\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"raw_retriever_test\",\n    srcs = [\"raw_retriever_test.cc\"],\n    deps = [\n        \":raw_retriever\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"fake_quant_retriever\",\n    srcs = [\"fake_quant_retriever.cc\"],\n    hdrs = [\"fake_quant_retriever.h\"],\n    deps = [\n        \":retriever_base\",\n        \"//monolith/native_training/runtime/hash_table/compressor:fake_quantizer\",\n        \"@com_google_absl//absl/algorithm:container\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"fake_quant_retriever_test\",\n    srcs = [\"fake_quant_retriever_test.cc\"],\n    deps = [\n        \":fake_quant_retriever\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"retriever_combination\",\n    srcs = [\"retriever_combination.cc\"],\n    hdrs = [\"retriever_combination.h\"],\n    deps = [\n        \":retriever_base\",\n        \"@com_google_absl//absl/algorithm:container\",\n        \"@com_google_absl//absl/strings:str_format\",\n    ],\n)\n\ncc_test(\n    name = \"retriever_combination_test\",\n    srcs = [\"retriever_combination_test.cc\"],\n    deps = [\n        \":raw_retriever\",\n        \":fake_quant_retriever\",\n        \":retriever_combination\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"hash_net_retriever\",\n    srcs = [\"hash_net_retriever.cc\"],\n    hdrs = [\"hash_net_retriever.h\"],\n    deps = [\n        \":retriever_base\",\n        \"//monolith/native_training/runtime/hash_table/compressor:hash_net_quantizer\",\n        \"@com_google_absl//absl/algorithm:container\",\n    ],\n)\n\ncc_test(\n    name = \"hash_net_retriever_test\",\n    srcs = [\"hash_net_retriever_test.cc\"],\n    deps = [\n        \":hash_net_retriever\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/fake_quant_retriever.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/retriever/fake_quant_retriever.h\"\n\n#include <memory>\n#include \"absl/algorithm/container.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_base.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass FakeQuantRetriever final : public RetrieverBase {\n public:\n  FakeQuantRetriever(int dim_size, const FakeQuantizer& fake_quantizer)\n      : RetrieverBase(dim_size), fake_quantizer_(fake_quantizer) {}\n\n  void Retrieve(const void* ctx, absl::Span<float> num) const override {\n    absl::c_copy(GetNum(ctx), num.begin());\n    for (int i = 0; i < dim_size_; ++i) {\n      num[i] = fake_quantizer_.Quantize(num[i]);\n    }\n  }\n\n  void Backward(absl::Span<const float> num, absl::Span<float> grad,\n                int64_t global_step) const override {}\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"FakeQuant(D=%d)\", RetrieverBase::DimSize());\n  }\n\n private:\n  FakeQuantizer fake_quantizer_;\n};\n\n}  // namespace\n\nstd::unique_ptr<RetrieverInterface> NewFakeQuantRetriever(\n    int dim_size, const FakeQuantizer& fake_quantizer) {\n  return std::make_unique<FakeQuantRetriever>(dim_size, fake_quantizer);\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/fake_quant_retriever.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_FAKE_QUANT_RETRIEVER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_FAKE_QUANT_RETRIEVER_H_\n\n#include <memory>\n\n#include \"monolith/native_training/runtime/hash_table/compressor/fake_quantizer.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<RetrieverInterface> NewFakeQuantRetriever(int dim_size, const FakeQuantizer& fake_quantizer);\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_FAKE_QUANT_RETRIEVER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/fake_quant_retriever_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/retriever/fake_quant_retriever.h\"\n\n#include <memory>\n\n#include \"absl/random/random.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::ElementsAre;\nusing ::testing::Le;\n\nTEST(FakeQuantRetriever, Basic) {\n  int dim_size = 10;\n  float r = 1.0f;\n  const float kStep = r / 128;\n  FakeQuantizer fake_quantizer(1.0f);\n  auto retriever = NewFakeQuantRetriever(dim_size, fake_quantizer);\n  std::vector<float> entry(dim_size);\n  absl::BitGen bit_gen;\n  for (auto& val : entry) {\n    val = absl::Uniform<float>(bit_gen, -1.f, 1.f);\n  }\n\n  std::vector<float> num(dim_size, 0);\n  retriever->Retrieve(entry.data(), absl::MakeSpan(num));\n  for (int i = 0; i < dim_size; ++i) {\n    EXPECT_THAT(std::abs(entry[i] - num[i]), Le(kStep));\n  }\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/hash_net_retriever.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/retriever/hash_net_retriever.h\"\n\n#include <memory>\n#include <utility>\n#include \"absl/algorithm/container.h\"\n#include \"monolith/native_training/runtime/hash_table/compressor/hash_net_quantizer.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_base.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nclass HashNetRetriever final : public RetrieverBase {\n public:\n  HashNetRetriever(int dim_size,\n                   std::unique_ptr<HashNetQuantizer> hash_net_quantizer)\n      : RetrieverBase(dim_size),\n        hash_net_quantizer_(std::move(hash_net_quantizer)) {}\n  void Retrieve(const void* ctx, absl::Span<float> num) const override {\n    absl::c_copy(GetNum(ctx), num.begin());\n    for (int i = 0; i < dim_size_; ++i) {\n      num[i] = hash_net_quantizer_->Forward(num[i]);\n    }\n  }\n\n  void Backward(absl::Span<const float> num, absl::Span<float> grad,\n                int64_t global_step) const override {\n    for (int i = 0; i < dim_size_; ++i) {\n      hash_net_quantizer_->Backward(num[i], &grad[i], global_step);\n    }\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"HashNet(D=%d)\", RetrieverBase::DimSize());\n  }\n\n private:\n  std::unique_ptr<HashNetQuantizer> hash_net_quantizer_;\n};\n\nstd::unique_ptr<RetrieverInterface> NewHashNetRetriever(\n    int dim_size, std::unique_ptr<HashNetQuantizer> hash_net_quantizer) {\n  return std::make_unique<HashNetRetriever>(dim_size,\n                                            std::move(hash_net_quantizer));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/hash_net_retriever.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_HASH_NET_RETRIEVER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_HASH_NET_RETRIEVER_H_\n\n#include <memory>\n#include \"monolith/native_training/runtime/hash_table/compressor/hash_net_quantizer.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<RetrieverInterface> NewHashNetRetriever(\n    int dim_size, std::unique_ptr<HashNetQuantizer> hash_net_quantizer);\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_HASH_NET_RETRIEVER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/hash_net_retriever_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/retriever/hash_net_retriever.h\"\n\n#include <cmath>\n\n#include \"absl/random/random.h\"\n#include \"gtest/gtest.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nTEST(HashNetRetriever, Basic) {\n  FloatCompressorConfig_OneBit config;\n  config.set_dim_size(10);\n  config.set_step_size(100);\n  float amplitude = config.amplitude();\n  auto hash_net_quantizer = std::make_unique<HashNetQuantizer>(config);\n  HashNetQuantizer* quantizer = hash_net_quantizer.get();\n  auto retriever =\n      NewHashNetRetriever(config.dim_size(), std::move(hash_net_quantizer));\n  std::vector<float> entry(config.dim_size());\n  absl::BitGen bit_gen;\n  for (auto& val : entry) {\n    val = absl::Uniform<float>(bit_gen, -1.f, 1.f);\n  }\n\n  std::vector<float> num(config.dim_size(), 0);\n  retriever->Retrieve(entry.data(), absl::MakeSpan(num));\n  for (int i = 0; i < config.dim_size(); ++i) {\n    EXPECT_FLOAT_EQ(num[i], amplitude * std::tanh(entry[i]));\n  }\n\n  float grad = 1.0f;\n  int64_t global_step = 100;\n  quantizer->Backward(1.0f, &grad, global_step);\n  EXPECT_FLOAT_EQ(grad, 0.35840667f * amplitude);\n\n  float scale = quantizer->GetScale();\n  retriever->Retrieve(entry.data(), absl::MakeSpan(num));\n  for (int i = 0; i < config.dim_size(); ++i) {\n    EXPECT_FLOAT_EQ(num[i], amplitude * std::tanh(scale * entry[i]));\n  }\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/raw_retriever.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/retriever/raw_retriever.h\"\n\n#include <memory>\n#include \"absl/algorithm/container.h\"\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_base.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass RawRetriever final : public RetrieverBase {\n public:\n  explicit RawRetriever(int dim_size) : RetrieverBase(dim_size) {}\n\n  void Retrieve(const void* ctx, absl::Span<float> num) const override {\n    absl::c_copy(GetNum(ctx), num.begin());\n  }\n\n  void Backward(absl::Span<const float> num, absl::Span<float> grad,\n                int64_t global_step) const override {}\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Raw(D=%d)\", RetrieverBase::DimSize());\n  }\n};\n\n}  // namespace\n\nstd::unique_ptr<RetrieverInterface> NewRawRetriever(int dim_size) {\n  return std::make_unique<RawRetriever>(dim_size);\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/raw_retriever.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RAW_RETRIEVER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RAW_RETRIEVER_H_\n\n#include <memory>\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<RetrieverInterface> NewRawRetriever(int dim_size);\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RAW_RETRIEVER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/raw_retriever_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/retriever/raw_retriever.h\"\n\n#include <memory>\n\n#include \"absl/random/random.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::ElementsAre;\n\nTEST(RawRetriever, Basic) {\n  int dim_size = 10;\n  auto retriever = NewRawRetriever(dim_size);\n  std::vector<float> entry(dim_size);\n  absl::BitGen bit_gen;\n  for (auto& val : entry) {\n    val = absl::Uniform<float>(bit_gen, -1.f, 1.f);\n  }\n\n  std::vector<float> num(dim_size, 0);\n  retriever->Retrieve(entry.data(), absl::MakeSpan(num));\n  for (int i = 0; i < dim_size; ++i) {\n    EXPECT_EQ(entry[i], num[i]);\n  }\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/retriever_base.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RETRIEVER_BASE_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RETRIEVER_BASE_H_\n\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nclass RetrieverBase : public RetrieverInterface {\n public:\n  explicit RetrieverBase(int dim_size)\n      : dim_size_(dim_size), size_bytes_(sizeof(float) * dim_size) {}\n\n  int64_t SizeBytes() const override {\n    return size_bytes_;\n  }\n\n  int DimSize() const override {\n    return dim_size_;\n  }\n\n protected:\n  absl::Span<const float> GetNum(const void* ctx) const {\n    const auto* ctx_float = static_cast<const float*>(ctx);\n    return absl::MakeConstSpan(ctx_float, ctx_float + dim_size_);\n  }\n\n  int dim_size_;\n\n  int64_t size_bytes_;\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RETRIEVER_BASE_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/retriever_combination.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_combination.h\"\n\n#include <memory>\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_base.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nclass CombinedRetriever final : public RetrieverBase {\n public:\n  CombinedRetriever(std::unique_ptr<RetrieverInterface> retriever1,\n                    std::unique_ptr<RetrieverInterface> retriever2)\n      : RetrieverBase(retriever1->DimSize() + retriever2->DimSize()),\n        retriever1_(std::move(retriever1)),\n        retriever2_(std::move(retriever2)) {}\n\n  void Retrieve(const void* ctx, absl::Span<float> num) const override {\n    const void* ctx2 = static_cast<const char*>(ctx) + retriever1_->SizeBytes();\n    auto num2 = num.subspan(retriever1_->DimSize());\n    retriever1_->Retrieve(ctx, num);\n    retriever2_->Retrieve(ctx2, num2);\n  }\n\n  void Backward(absl::Span<const float> num, absl::Span<float> grad,\n                int64_t global_step) const override {\n    int dim_size1 = retriever1_->DimSize();\n    retriever1_->Backward(num.subspan(0, dim_size1), grad.subspan(0, dim_size1),\n                          global_step);\n    retriever2_->Backward(num.subspan(dim_size1), grad.subspan(dim_size1),\n                          global_step);\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"%s|%s\", retriever1_->DebugString(),\n                           retriever2_->DebugString());\n  }\n\n private:\n  int dim_size_;\n  std::unique_ptr<RetrieverInterface> retriever1_;\n  std::unique_ptr<RetrieverInterface> retriever2_;\n};\n\n}  // namespace\n\nstd::unique_ptr<RetrieverInterface> CombineRetrievers(\n    std::unique_ptr<RetrieverInterface> retriever1,\n    std::unique_ptr<RetrieverInterface> retriever2) {\n  return std::make_unique<CombinedRetriever>(std::move(retriever1),\n                                             std::move(retriever2));\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/retriever_combination.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RETRIEVER_COMBINATION_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RETRIEVER_COMBINATION_H_\n\n#include <memory>\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_interface.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nstd::unique_ptr<RetrieverInterface> CombineRetrievers(std::unique_ptr<RetrieverInterface> retriever1,\n                                                      std::unique_ptr<RetrieverInterface> retriever2);\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RETRIEVER_COMBINATION_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/retriever_combination_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_table/retriever/retriever_combination.h\"\n\n#include <memory>\n\n#include \"absl/random/random.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\n#include \"monolith/native_training/runtime/hash_table/retriever/raw_retriever.h\"\n#include \"monolith/native_training/runtime/hash_table/retriever/fake_quant_retriever.h\"\n\nnamespace monolith {\nnamespace hash_table {\nnamespace {\n\nusing ::testing::Le;\n\nTEST(CombinedRetriever, Basic) {\n  int dim_size1 = 10, dim_size2 = 20;\n  int dim_size = dim_size1 + dim_size2;\n  float r = 1.0f;\n  const float kStep = r / 128;\n  FakeQuantizer fake_quantizer(1.0f);\n  auto retriever1 = NewRawRetriever(dim_size1);\n  auto retriever2 = NewFakeQuantRetriever(dim_size2, fake_quantizer);\n  auto retriever = CombineRetrievers(std::move(retriever1), std::move(retriever2));\n  std::vector<float> entry(dim_size);\n  absl::BitGen bit_gen;\n  for (auto& val : entry) {\n    val = absl::Uniform<float>(bit_gen, -1.f, 1.f);\n  }\n\n  std::vector<float> num(dim_size, 0);\n  retriever->Retrieve(entry.data(), absl::MakeSpan(num));\n  for (int i = 0; i < dim_size1; ++i) {\n    EXPECT_EQ(entry[i], num[i]);\n  }\n  for (int i = dim_size1; i < dim_size; ++i) {\n    EXPECT_THAT(std::abs(entry[i] - num[i]), Le(kStep));\n  }\n}\n\n}  // namespace\n}  // namespace hash_table\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/retriever/retriever_interface.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RETRIEVER_INTERFACE_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RETRIEVER_INTERFACE_H_\n\n#include \"absl/types/span.h\"\n\nnamespace monolith {\nnamespace hash_table {\n\nclass RetrieverInterface {\n public:\n  virtual ~RetrieverInterface() = default;\n\n  // How many bytes could be accessed by the retriever\n  virtual int64_t SizeBytes() const = 0;\n\n  // The dim that this retriever can support.\n  virtual int DimSize() const = 0;\n\n  // Retrieve the num data accessed by the retriever.\n  // |num| is a float array whose length is DimSize().\n  virtual void Retrieve(const void* ctx, absl::Span<float> num) const = 0;\n\n  // Back propagation\n  virtual void Backward(absl::Span<const float> num, absl::Span<float> grad,\n                        int64_t global_step) const = 0;\n\n  virtual std::string DebugString() const = 0;\n};\n\n}  // namespace hash_table\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_RETRIEVER_RETRIEVER_INTERFACE_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_UTILS\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_UTILS\nnamespace monolith {\nnamespace hash_table {\n\ninline void* AddOffset(void* p, int offset) {\n  return reinterpret_cast<char*>(p) + offset;\n}\n\ninline const void* AddOffset(const void* p, int offset) {\n  return reinterpret_cast<const char*>(p) + offset;\n}\n\ntemplate <bool compute_keys_per_table = true>\nstd::pair<int, int> ComputeFusedOffsets(\n    const int* slot_size_vec,  // num_tables * num_shards\n    const int* table_dims,     // num_tables\n    int num_tables, int num_shards,\n    int* key_offsets,     // num_tables * num_shards + 1\n    int* emb_offsets,     // num_tables * num_shards + 1\n    int* keys_per_table,  // num_tables\n    int* emb_splits       // num_shards\n) {\n  if (compute_keys_per_table)\n    std::fill(keys_per_table, keys_per_table + num_tables, 0);\n  int total_keys = 0;\n  int total_embs = 0;\n  int prev_total_emb = 0;\n  key_offsets[0] = emb_offsets[0] = 0;\n  for (int shard_id = 0; shard_id < num_shards; shard_id++) {\n    for (int table_id = 0; table_id < num_tables; table_id++) {\n      int idx = num_tables * shard_id + table_id;\n      int slot_sz = slot_size_vec[idx];\n      int segment_dim = table_dims[table_id] * slot_sz;\n\n      if (compute_keys_per_table) keys_per_table[table_id] += slot_sz;\n      total_keys += slot_sz;\n      total_embs += segment_dim;\n\n      key_offsets[idx + 1] = key_offsets[idx] + slot_sz;\n      emb_offsets[idx + 1] = emb_offsets[idx] + segment_dim;\n    }\n    emb_splits[shard_id] = total_embs - prev_total_emb;\n    prev_total_emb = total_embs;\n  }\n  return std::make_pair(total_keys, total_embs);\n}\n\n}  // namespace hash_table\n}  // namespace monolith\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_HASH_TABLE_UTILS\n"
  },
  {
    "path": "monolith/native_training/runtime/hash_table/workspace.bzl",
    "content": ""
  },
  {
    "path": "monolith/native_training/runtime/hopscotch/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_proto_library\", \"cc_test\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime:__subpackages__\"])\n\ncc_library(\n    name = \"hopscotch_hash_set\",\n    srcs = [\"hopscotch_hash_set.cc\"],\n    hdrs = [\"hopscotch_hash_set.h\"],\n    deps = [\n        \"//monolith/native_training/runtime/concurrency:micro_one_bit_spin_lock\",\n        \"@com_google_absl//absl/container:flat_hash_set\",\n        \"@com_google_absl//absl/hash\",\n        \"@com_google_absl//absl/synchronization\",\n    ],\n)\n\n# diable this test since it is not runnable on TCE image.\n# cc_test(\n#     name = \"hopscotch_hash_set_test\",\n#     srcs = [\"hopscotch_hash_set_test.cc\"],\n#     deps = [\n#         \":hopscotch_hash_set\",\n#         \"@gperftools//:libtcmalloc\",\n#         \"@com_google_googletest//:gtest_main\",\n#     ],\n# )\n"
  },
  {
    "path": "monolith/native_training/runtime/hopscotch/hopscotch_hash_set.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hopscotch/hopscotch_hash_set.h\"\n\n#define likely(x) __builtin_expect(!!(x), 1)\n#define unlikely(x) __builtin_expect(!!(x), 0)\n\nnamespace monolith {\nnamespace hopscotch {\n\nusing FID = int64_t;\n\ninline static uint32_t NextPowerOfTwo(uint32_t n) {\n  --n;\n  n |= n >> 1;\n  n |= n >> 2;\n  n |= n >> 4;\n  n |= n >> 8;\n  n |= n >> 16;\n  return n + 1;\n}\n\ninline static int FirstLsbBitIndex(uint32_t x) { return __builtin_ffs(x) - 1; }\n\ntemplate <typename Key>\nHopscotchHashSet<Key>::HopscotchHashSet(uint32_t capacity,\n                                        uint32_t concurrency_level)\n    : capacity_(capacity) {\n  init_ = false;\n  lock_mask_ = NextPowerOfTwo(concurrency_level) - 1;\n  bucket_mask_ = NextPowerOfTwo(capacity * 1.2) - 1;\n  init_lock_.Init();\n  num_elements_.store(0, std::memory_order_seq_cst);  // 可能还没init就会获取\n}\n\ntemplate <typename Key>\nvoid HopscotchHashSet<Key>::DoInit() {\n  table_.resize(bucket_mask_ + kHopscotchHashInsertRange + 1);\n  locks_.resize(lock_mask_ + 1);\n  for (size_t i = 0; i <= lock_mask_; ++i) {\n    locks_[i].Init();\n  }\n  extra_lock_.Init();\n  clear_lock_.Init();\n  num_elements_.store(0, std::memory_order_seq_cst);\n  running_threads_.store(0, std::memory_order_seq_cst);\n  DoClear();\n}\n\ntemplate <typename Key>\nvoid HopscotchHashSet<Key>::FindCloserFreeBucket(\n    const concurrency::MicroOneBitSpinLock* lock, int* free_bucket,\n    int* free_dist) {\n  int move_bucket = *free_bucket - (kHopscotchHashHopRange - 1);\n  int move_free_dist;\n  for (move_free_dist = kHopscotchHashHopRange - 1; move_free_dist > 0;\n       --move_free_dist) {\n    auto new_lock = &locks_[move_bucket & lock_mask_];\n    uint32_t start_hop_info = table_[move_bucket].hop_info;\n    int move_new_free_dist = !start_hop_info ? kHopscotchHashHopRange\n                                             : __builtin_ctz(start_hop_info);\n    if (move_new_free_dist < move_free_dist) {\n      if (new_lock != lock) {\n        new_lock->Lock();\n      }\n      if (start_hop_info == table_[move_bucket].hop_info) {\n        // new_free_bucket -> free_bucket and empty new_free_bucket\n        int new_free_bucket = move_bucket + move_new_free_dist;\n        table_[*free_bucket].key = table_[new_free_bucket].key;\n        table_[*free_bucket].hash = table_[new_free_bucket].hash;\n        table_[move_bucket].hop_info |= 1u << move_free_dist;\n        table_[move_bucket].hop_info &= ~(1u << move_new_free_dist);\n\n        *free_bucket = new_free_bucket;\n        *free_dist -= move_free_dist - move_new_free_dist;\n        if (new_lock != lock) {\n          new_lock->Unlock();\n        }\n        return;\n      }\n      if (new_lock != lock) {\n        new_lock->Unlock();\n      }\n    }\n    ++move_bucket;\n  }\n  *free_bucket = -1;\n  *free_dist = 0;\n}\n\ntemplate <typename Key>\nsize_t HopscotchHashSet<Key>::insert(Key key) {\n  // we do lazy init here to save memory\n  if (!init_) {\n    init_lock_.Lock();\n    if (!init_) DoInit();\n    init_ = true;\n    init_lock_.Unlock();\n  }\n  size_t dropped_keys = 0;\n  if (unlikely(size() > capacity_)) {\n    clear_lock_.Lock();\n    if (likely(size() > capacity_)) {\n      for (int i = 0; i < locks_.size(); ++i) locks_[i].Lock();\n      dropped_keys = size();\n      this->DoClear();\n      for (int i = 0; i < locks_.size(); ++i) locks_[i].Unlock();\n    }\n    clear_lock_.Unlock();\n  }\n  uint32_t hash = HashFunc(key);\n  auto lock = &locks_[hash & lock_mask_];\n  lock->Lock();\n  int bucket = hash & bucket_mask_;\n  uint32_t hop_info = table_[bucket].hop_info;\n  // check if already exists\n  while (0 != hop_info) {\n    int i = FirstLsbBitIndex(hop_info);\n    int current = bucket + i;\n    if (key == table_[current].key) {\n      lock->Unlock();\n      return dropped_keys;\n    }\n    hop_info &= ~(1U << i);\n  }\n  // looking for free bucket\n  int free_bucket = bucket, free_dist = 0;\n  for (; free_dist < kHopscotchHashInsertRange; ++free_dist, ++free_bucket) {\n    if (kHopscotchHashEmpty == table_[free_bucket].hash &&\n        kHopscotchHashEmpty ==\n            __sync_val_compare_and_swap(&table_[free_bucket].hash,\n                                        kHopscotchHashEmpty, hash)) {\n      break;\n    }\n  }\n\n  // insert the new key\n  num_elements_.fetch_add(1, std::memory_order_relaxed);\n  if (free_dist < kHopscotchHashInsertRange) {\n    do {\n      if (free_dist < kHopscotchHashHopRange) {\n        table_[free_bucket].key = key;\n        table_[free_bucket].hash = hash;\n        table_[bucket].hop_info |= 1u << free_dist;\n        lock->Unlock();\n        return dropped_keys;\n      }\n      FindCloserFreeBucket(lock, &free_bucket, &free_dist);\n    } while (-1 != free_bucket);\n  } else {\n    // insert failed, insert into extra_ map\n    extra_lock_.Lock();\n    extra_.insert(key);\n    extra_lock_.Unlock();\n  }\n  lock->Unlock();\n  return dropped_keys;\n}\n\ntemplate <typename Key>\nstd::vector<Key> HopscotchHashSet<Key>::GetAndClear() {\n  if (!init_) return {};\n  clear_lock_.Lock();\n  for (int i = 0; i < locks_.size(); ++i) locks_[i].Lock();\n  std::vector<Key> results(size());\n  size_t index = 0;\n  for (auto&& entry : table_) {\n    if (entry.hash) {\n      results[index++] = entry.key;\n    }\n    entry.hash = 0;\n    entry.key = kEmptyKey;\n    entry.hop_info = 0;\n  }\n  for (auto&& key : extra_) {\n    results[index++] = key;\n  }\n  extra_.clear();\n  num_elements_.store(0, std::memory_order_seq_cst);\n  for (int i = 0; i < locks_.size(); ++i) locks_[i].Unlock();\n  clear_lock_.Unlock();\n  return results;\n}\n\ntemplate <typename Key>\nvoid HopscotchHashSet<Key>::DoClear() {\n  for (size_t i = 0; i < table_.size(); ++i) {\n    table_[i].hash = 0;\n    table_[i].key = kEmptyKey;\n    table_[i].hop_info = 0;\n  }\n  num_elements_.store(0, std::memory_order_seq_cst);\n  extra_.clear();\n}\n\ntemplate class HopscotchHashSet<FID>;\ntemplate class HopscotchHashSet<std::pair<int64_t, const void*>>;\n\ntemplate <>\nFID GetEmptyValue<FID>() {\n  return -1;\n}\n\ntemplate <>\nstd::pair<int64_t, const void*>\nGetEmptyValue<std::pair<int64_t, const void*>>() {\n  return std::make_pair(-1, nullptr);\n}\n\n}  // namespace hopscotch\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/hopscotch/hopscotch_hash_set.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HOPSCOTCH_HOPSCOTCH_HASH_SET_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HOPSCOTCH_HOPSCOTCH_HASH_SET_H_\n\n#include <atomic>\n#include <thread>\n#include <unordered_set>\n#include <vector>\n\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/hash/hash.h\"\n#include \"monolith/native_training/runtime/concurrency/micro_one_bit_spin_lock.h\"\n\nnamespace monolith {\nnamespace hopscotch {\n\nusing FID = int64_t;\n\n#pragma pack(push)\n#pragma pack(4)\n\ntemplate <typename Key>\nstruct hopscotch_entry_t {\n  Key key;\n  uint32_t hash;\n  uint32_t hop_info;\n};\n#pragma pack(pop)\n\ntemplate <class Key>\nKey GetEmptyValue() {\n  return Key();\n}\n\n// thread safe hopscotch hash set (insert only)\n// paper:\n// http://people.csail.mit.edu/shanir/publications/disc2008_submission_98.pdf\ntemplate <typename Key>\nclass HopscotchHashSet {\n public:\n  explicit HopscotchHashSet(uint32_t capacity, uint32_t concurrency_level);\n\n  // thread safe insert, return number keys cleared\n  size_t insert(Key key);\n\n  std::vector<Key> GetAndClear();\n\n  size_t size() const { return num_elements_.load(std::memory_order_relaxed); }\n\n  uint32_t capacity() const { return capacity_; }\n\n private:\n  uint32_t HashFunc(Key key) { return hash_func_(key) | 3; }\n\n  void FindCloserFreeBucket(const concurrency::MicroOneBitSpinLock* lock,\n                            int* free_bucket, int* free_dist);\n\n  void DoInit();\n\n  // clear the hash table, not thread safe\n  void DoClear();\n\n private:\n  static constexpr uint32_t kHopscotchHashInsertRange = 4096;\n  static constexpr uint32_t kHopscotchHashHopRange = 32;\n  static constexpr uint32_t kHopscotchHashEmpty = 0;\n  static constexpr uint32_t kHopscotchHashBusy = 1;\n  Key kEmptyKey = GetEmptyValue<Key>();\n\n  absl::Hash<Key> hash_func_;\n\n  // for those keys not insert into table\n  absl::flat_hash_set<Key> extra_;\n  concurrency::MicroOneBitSpinLock extra_lock_;\n  concurrency::MicroOneBitSpinLock clear_lock_;\n  concurrency::MicroOneBitSpinLock init_lock_;\n  std::vector<hopscotch_entry_t<Key>> table_;\n  std::vector<concurrency::MicroOneBitSpinLock> locks_;\n  uint32_t lock_mask_;\n  uint32_t bucket_mask_;\n  std::atomic_int running_threads_;  // number of thread doing insertion\n  std::atomic_int num_elements_;     // total number of elements\n  uint32_t capacity_;\n  bool init_;\n};\n\n}  // namespace hopscotch\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_HOPSCOTCH_HOPSCOTCH_HASH_SET_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/hopscotch/hopscotch_hash_set_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hopscotch/hopscotch_hash_set.h\"\n#include <sys/time.h>\n#include <algorithm>\n#include <set>\n#include <thread>\n#include <unordered_set>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n#include \"google/malloc_extension.h\"\n\n\nnamespace monolith {\nnamespace hopscotch {\nnamespace {\n\nusing FID = int64_t;\nconstexpr int kMaxNumKeys = 2097152;\nconstexpr int kConcurrencyLevel = 200;\nconstexpr int kSeed = 2233333;\n\nuint64_t GetTime() {\n  struct timeval tv;\n  gettimeofday(&tv, NULL);\n  return tv.tv_sec * 1000000ULL + tv.tv_usec;\n}\n\nstatic size_t MemoryUsage() {\n  size_t result = 0;\n  if (MallocExtension::instance()->GetNumericProperty(\n          \"generic.current_allocated_bytes\", &result)) {\n    return result;\n  }\n  return 0;\n}\n\nstatic size_t memory_last = 0;\nstatic uint64_t time_last = 0;\n\nvoid Reset() {\n  memory_last = MemoryUsage();\n  time_last = GetTime();\n}\n\nvoid Report() {\n  printf(\"time:%6.1f ms, memory:%6.1f M\\n\", (GetTime() - time_last) / 1000.0,\n         (MemoryUsage() - memory_last) / (1024.0 * 1024));\n  Reset();\n}\n\nTEST(HOPSCOTCH_HASH_SET, simple_test) {\n  HopscotchHashSet<FID> hash_set(1000, 1);\n  std::vector<FID> keys;\n  for (int i = 0; i < 1000; ++i) {\n    keys.emplace_back(std::rand());\n    hash_set.insert(keys.back());\n  }\n  auto all = hash_set.GetAndClear();\n  ASSERT_EQ(all.size(), 1000);\n  std::sort(all.begin(), all.end());\n  std::sort(keys.begin(), keys.end());\n  for (int i = 0; i < 1000; ++i) {\n    ASSERT_EQ(keys[i], all[i]);\n  }\n}\n\ntemplate<class MapType>\nvoid TestOneMap(MapType* map) {\n  srand(kSeed);\n  for (int i = 0; i < kMaxNumKeys; ++i) {\n    map->insert(std::rand());\n  }\n}\n\n// test google:dense_hash_set\n// 2096080\n// time:  94.3 ms, memory:  32.0 M\n//\n// test std::set\n// 2096080\n// time:1050.2 ms, memory:  96.0 M\n//\n// test std::unordered_set\n// 2096080\n// time: 333.8 ms, memory:  48.4 M\n//\n// test hopscotch_hash_set\n// 2096080\n// time: 188.3 ms, memory:  64.1 M\nTEST(HOPSCOTCH_HASH_SET, compare_test) {\n  Reset();\n\n  std::cout << \"test google:dense_hash_set\" << std::endl;\n  google::dense_hash_set<FID> dense_hash_set;\n  dense_hash_set.set_empty_key(-1);\n  TestOneMap(&dense_hash_set);\n  std::cout << dense_hash_set.size() << std::endl;\n  Report();\n\n  std::cout << \"test std::set\" << std::endl;\n  std::set<FID> std_set;\n  TestOneMap(&std_set);\n  std::cout << std_set.size() << std::endl;\n  Report();\n\n  std::cout << \"test std::unordered_set\" << std::endl;\n  std::unordered_set<FID> std_unordered_set;\n  TestOneMap(&std_unordered_set);\n  std::cout << std_unordered_set.size() << std::endl;\n  Report();\n\n  std::cout << \"test hopscotch_hash_set\" << std::endl;\n  HopscotchHashSet<FID> hash_set(kMaxNumKeys, 1);\n  TestOneMap(&hash_set);\n  std::cout << hash_set.size() << std::endl;\n  Report();\n}\n\nTEST(HOPSCOTCH_HASH_SET, multithread_test) {\n  HopscotchHashSet<FID> hash_set(kMaxNumKeys, 1000);\n  srand(kSeed);\n  for (int num_thread = 1; num_thread <= 10; ++num_thread) {\n    std::cout << \"test for \" << num_thread << \" threads\" << std::endl;\n    std::vector<FID> keys;\n    for (int i = 0; i < kMaxNumKeys; ++i) {\n      keys.emplace_back(std::rand());\n    }\n    std::vector<std::thread> writers(num_thread);\n    for (int i = 0; i < num_thread; ++i) {\n      writers[i] = std::thread(\n          [&](int index) {\n            for (int j = index; j < kMaxNumKeys; j += num_thread) {\n              EXPECT_EQ(0, hash_set.insert(keys[j]));\n            }\n          },\n          i);\n    }\n    for (int i = 0; i < num_thread; ++i) {\n      writers[i].join();\n    }\n    auto all = hash_set.GetAndClear();\n    std::sort(all.begin(), all.end());\n    std::sort(keys.begin(), keys.end());\n    keys.erase(std::unique(keys.begin(), keys.end()), keys.end());\n    std::cout << \"insert finished. total insert keys: \" << keys.size()\n              << std::endl;\n    ASSERT_EQ(all.size(), keys.size());\n    for (int i = 0; i < keys.size(); ++i) {\n      ASSERT_EQ(keys[i], all[i]);\n    }\n  }\n}\n\nTEST(HOPSCOTCH_HASH_SET, overflow_test) {\n  HopscotchHashSet<FID> hash_set(kMaxNumKeys, 1000);\n  const int num_thread = 10;\n  const int num_keys = kMaxNumKeys * 20 + 10000;\n  std::vector<FID> keys(num_keys);\n  for (int i = 0; i < num_keys; ++i) {\n    keys[i] = i;\n  }\n  std::vector<std::vector<int>> dropped_keys(num_thread);\n  std::vector<std::thread> writers(num_thread);\n  for (int i = 0; i < num_thread; ++i) {\n    writers[i] = std::thread(\n        [&](int index) {\n          for (int j = index; j < num_keys; j += num_thread) {\n            int result = hash_set.insert(keys[j]);\n            if (result != 0) {\n              dropped_keys[index].emplace_back(result);\n            }\n          }\n        },\n        i);\n  }\n  for (int i = 0; i < num_thread; ++i) {\n    writers[i].join();\n  }\n  int clear_times = 0;\n  for (int i = 0; i < num_thread; ++i) {\n    clear_times += dropped_keys[i].size();\n  }\n  EXPECT_EQ(clear_times, 20);\n}\n\n}  // namespace\n}  // namespace hopscotch\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_proto_library\", \"cc_test\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"cc_header_only_library\", \"tf_cc_test\", \"tf_custom_op_library\", \"tf_gen_op_wrapper_py\", \"tf_gpu_kernel_library_allow_except\", \"tf_kernel_library\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\nload(\"@rules_proto//proto:defs.bzl\", \"proto_library\")\nload(\"@bazel_skylib//lib:selects.bzl\", \"selects\")\n\npackage(default_visibility = [\n    \"//monolith:__subpackages__\",\n    \"@org_tensorflow//:__subpackages__\",\n])\n\ncc_header_only_library(\n    name = \"traceme\",\n    deps = [\n        \"@org_tensorflow//tensorflow/core/profiler/lib:traceme\",\n    ],\n)\n\ncc_library(\n    name = \"tracelib\",\n    deps = [\n        \":traceme\",\n        \"//monolith/native_training/runtime/common:metrics\",\n        \"@com_google_glog//:glog\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n)\n\ntf_gpu_kernel_library_allow_except(\n    name = \"embedding_hash_table_tf_bridge\",\n    srcs = [\"embedding_hash_table_tf_bridge.cc\"],\n    hdrs = [\"embedding_hash_table_tf_bridge.h\"],\n    deps = [\n        \":hash_filter_tf_bridge\",\n        \"//monolith/native_training/runtime/common:metrics\",\n        \"//monolith/native_training/runtime/hash_filter:filter\",\n        \"//monolith/native_training/runtime/hash_filter:probabilistic_filter\",\n        \"//monolith/native_training/runtime/hash_filter:sliding_hash_filter\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_factory\",\n        \"//monolith/native_training/runtime/hopscotch:hopscotch_hash_set\",\n        \"@com_google_absl//absl/memory\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:ops_util_hdrs\",\n    ],\n)\n\ncc_library(\n    name = \"hash_filter_tf_bridge\",\n    srcs = [\"hash_filter_tf_bridge.cc\"],\n    hdrs = [\"hash_filter_tf_bridge.h\"],\n    deps = [\n        \":file_utils\",\n        \"//monolith/native_training/data/training_instance:reader_util\",\n        \"//monolith/native_training/runtime/hash_filter:filter\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_cc_proto\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:ops_util_hdrs\",\n    ],\n)\n\ncc_library(\n    name = \"touched_key_set_tf_bridge\",\n    srcs = [],\n    hdrs = [\"touched_key_set_tf_bridge.h\"],\n    deps = [\n        \"//monolith/native_training/runtime/hopscotch:hopscotch_hash_set\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:ops_util_hdrs\",\n    ],\n)\n\ncc_library(\n    name = \"parameter_sync_tf_bridge\",\n    srcs = [\"parameter_sync_tf_bridge.cc\"],\n    hdrs = [\"parameter_sync_tf_bridge.h\"],\n    deps = [\n        \":embedding_hash_table_tf_bridge\",\n        \":multi_hash_table\",\n        \"//monolith/native_training/runtime/parameter_sync:dummy_sync_client\",\n        \"//monolith/native_training/runtime/parameter_sync:dummy_sync_server\",\n        \"//monolith/native_training/runtime/parameter_sync:sync_client_manager\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:ops_util_hdrs\",\n    ],\n)\n\ncc_library(\n    name = \"file_utils\",\n    srcs = [\"file_utils.cc\"],\n    hdrs = [\"file_utils.h\"],\n    deps = [\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_protobuf//:protobuf_lite\",\n        \"@com_googlesource_code_re2//:re2\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:ops_util_hdrs\",\n    ],\n)\n\ntf_cc_test(\n    name = \"file_utils_test\",\n    srcs = [\"file_utils_test.cc\"],\n    deps = [\n        \":file_utils\",\n        \"//monolith/native_training/data/training_instance:reader_util\",\n        \"@com_google_googletest//:gtest_main\",\n        \"@org_tensorflow//tensorflow/core:test\",\n    ],\n)\n\ntf_kernel_library(\n    name = \"clip_ops\",\n    srcs = [\n        \"clip_by_global_norm.h\",\n        \"clip_by_global_norm_op.cc\",\n    ],\n    copts = [\n        \"-D_ENABLE_AVX\",\n    ],\n    gpu_srcs = [\n        \"clip_by_global_norm.h\",\n        \"clip_by_global_norm.cu.cc\",\n        \"global_norm.cu.cc\",\n        \"clip_by_global_norm_fused.cu.cc\",\n        \"alloc_utils.h\",\n    ],\n    linkopts = [],\n    deps = [\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:gpu_device_array_for_custom_op\",\n        \"@org_tensorflow//tensorflow/core/kernels:gpu_prim_hdrs\",\n    ],\n)\n\ncc_library(\n    name = \"multi_hash_table\",\n    hdrs = [\"multi_hash_table.h\"],\n    deps = [\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/strings\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n)\n\ntf_gpu_kernel_library_allow_except(\n    name = \"hash_table_ops\",\n    srcs = [\n        \"gpu_multi_hash_table.h\",\n        \"hash_table/misc_ops.cc\",\n        \"hash_table_lookup_op.cc\",\n        \"hash_table_op.cc\",\n        \"hash_table_restore_op.cc\",\n        \"hash_table_save_op.cc\",\n        \"hash_table_update_op.cc\",\n        \"multi_hash_table.h\",\n        \"multi_hash_table_lookup_op.cc\",\n        \"multi_hash_table_op.cc\",\n        \"multi_hash_table_save_restore_ops.cc\",\n        \"multi_hash_table_update_op.cc\",\n    ],\n    deps = [\n        \":embedding_hash_table_tf_bridge\",\n        \":file_utils\",\n        \":hash_filter_tf_bridge\",\n        \":multi_hash_table\",\n        \":parameter_sync_tf_bridge\",\n        \"//monolith/native_training/data/training_instance:reader_util\",\n        \"//monolith/native_training/runtime/concurrency:queue\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/container:flat_hash_set\",\n        \"@com_google_absl//absl/random\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/time\",\n    ],\n)\n\ncc_library(\n    name = \"monolith_internal_ops\",\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"monolith_ops_additional_deps\",\n    deps = select({\n        \"@org_tensorflow//tensorflow:framework_shared_object\": [\"@org_tensorflow//tensorflow/core/platform/hadoop:hadoop_file_system\"],\n        \"//conditions:default\": [],\n    }),\n)\n\ncc_library(\n    name = \"monolith_ops\",\n    deps = [\n        \":clip_ops\",\n        \":deep_insight_ops\",\n        \":distribution_ops\",\n        \":file_ops\",\n        \":gen_seq_mask_op\",\n        \":hash_filter_ops\",\n        \":hash_table_ops\",\n        \":inbatch_auc_loss_ops\",\n        \":logging_ops\",\n        \":monolith_internal_ops\",\n        \":monolith_ops_additional_deps\",\n        \":parameter_sync_ops\",\n        \":remote_predict_op\",\n        \":touched_key_set_ops\",\n        \"//monolith/native_training/data:pb_data_ops\",\n        \"//monolith/native_training/data/training_instance:pb_datasource_ops\",\n        \"//monolith/native_training/layers:layer_tf_ops\",\n        \"//monolith/native_training/optimizers:training_ops\",\n    ],\n    alwayslink = 1,\n)\n\n# if framework_shared_object is true,\n# we shouldn't link the ops into tensorflow because\n# we don't separate the ops/kernels implementation.\n# Instead, we use dynamic load to solve this problem.\nselects.config_setting_group(\n    name = \"monolith_ops_for_tf_condition\",\n    match_any = [\"@org_tensorflow//tensorflow:framework_shared_object\", \":serving_gpu\"],\n)\n\ncc_library(\n    name = \"monolith_ops_for_tf\",\n    deps = select({\n        \":monolith_ops_for_tf_condition\": [],\n        \"//conditions:default\": [\n            \":monolith_ops\",\n        ],\n    }),\n    alwayslink = 1,\n)\n\ntf_kernel_library(\n    name = \"monolith_ops_for_load\",\n    deps = select({\n        \"@org_tensorflow//tensorflow:framework_shared_object\": [\":monolith_ops\"],\n        \"//conditions:default\": [],\n    }),\n)\n\ntf_gen_op_wrapper_py(\n    name = \"gen_monolith_ops_base\",\n    out = \"gen_monolith_ops_base.py\",\n    deps = [\":monolith_ops\"],\n)\n\npy_library(\n    name = \"gen_monolith_ops\",\n    srcs = [\"gen_monolith_ops.py\"],\n    data = [\":libtfkernel_monolith_ops_for_load.so\"],\n    deps = [\n        \":gen_monolith_ops_base\",\n        \"//monolith:utils\",\n        \"@org_tensorflow//tensorflow:tensorflow_py\",\n    ],\n)\n\ntf_kernel_library(\n    name = \"distribution_ops\",\n    srcs = [\n        \"alloc_utils.h\",\n        \"fused_embedding_to_layout.cc\",\n        \"fused_embedding_to_layout.h\",\n        \"fused_reorder_by_indices.cc\",\n        \"map_id_to_embedding_op.cc\",\n        \"reduce_op.cc\",\n        \"split_by_indices_op.cc\",\n        \"static_reshape_op.cc\",\n        \"unique_mapping_ops.cc\",\n        \"normalize_merged_split_op.cc\",\n    ],\n    copts = [\n        \"-D_ENABLE_AVX\",\n    ],\n    gpu_srcs = [\n        \"map_id_to_embedding.cu.cc\",\n        \"reduce_op.cu.cc\",\n        \"alloc_utils.h\",\n        \"fused_embedding_to_layout.h\",\n        \"fused_embedding_to_layout.cu.cc\",\n        \"aligned_concat_split.cu.cc\",\n    ],\n    # TODO: Figure out how to link \"@org_tensorflow//tensorflow/core/kernels:cwise_lib_hdrs\" for fill_functor.h\n    deps = [\n        \"//idl:example_cc_proto\",\n        \"//monolith/native_training/data/training_instance:data_reader\",\n        \"//monolith/native_training/data/training_instance:parse_instance_lib\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_factory\",\n        \"//monolith/native_training/runtime/ops:traceme\",\n        \"@com_google_absl//absl/algorithm:container\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:gpu_device_array_for_custom_op\",\n    ],\n)\n\ntf_gpu_kernel_library_allow_except(\n    name = \"hash_filter_ops\",\n    srcs = [\n        \"hash_filter_intercept_gradient_op.cc\",\n        \"hash_filter_op.cc\",\n        \"hash_filter_restore_op.cc\",\n        \"hash_filter_save_op.cc\",\n    ],\n    deps = [\n        \":file_utils\",\n        \":hash_filter_tf_bridge\",\n        \"//monolith/native_training/runtime/hash_filter\",\n        \"//monolith/native_training/runtime/hash_filter:dummy_hash_filter\",\n        \"//monolith/native_training/runtime/hash_filter:probabilistic_filter\",\n        \"//monolith/native_training/runtime/hash_filter:sliding_hash_filter\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n)\n\ncc_library(\n    name = \"file_ops\",\n    srcs = [\n        \"file_ops.cc\",\n    ],\n    deps = [\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"//monolith/native_training/runtime/hash_table:embedding_hash_table_cc_proto\",\n    ],\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"touched_key_set_ops\",\n    srcs = [\n        \"touched_key_set_insert_op.cc\",\n        \"touched_key_set_op.cc\",\n        \"touched_key_set_steal_op.cc\",\n    ],\n    deps = [\n        \":touched_key_set_tf_bridge\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"gen_seq_mask_op\",\n    srcs = [\n        \"gen_seq_mask.cc\",\n    ],\n    deps = [\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"inbatch_auc_loss_ops\",\n    srcs = [\n        \"inbatch_auc_loss.cc\",\n    ],\n    deps = [\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"remote_predict_op_lib\",\n    hdrs = [\"remote_predict_op.h\"],\n    deps = [\n        \":agent_heartbeat\",\n        \":tracelib\",\n        \"//monolith/native_training/runtime/common:metrics\",\n        \"@com_google_absl//absl/status\",\n        \"@com_google_absl//absl/time\",\n        \"@com_google_glog//:glog\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_service_proto\",\n    ],\n)\n\ncc_library(\n    name = \"prediction_service_grpc\",\n    srcs = [\n        \"prediction_service_grpc.cc\",\n    ],\n    hdrs = [\n        \"prediction_service_grpc.h\",\n    ],\n    deps = [\n        \"@com_google_absl//absl/status\",\n        \"@com_google_absl//absl/time\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_service_proto\",\n    ],\n)\n\ncc_library(\n    name = \"remote_predict_op_grpc\",\n    srcs = [\"remote_predict_op_grpc.cc\"],\n    deps = [\n        \":prediction_service_grpc\",\n        \":remote_predict_op_lib\",\n    ],\n    alwayslink = 1,\n)\nalias(\n    name = \"remote_predict_op\",\n    actual = \":remote_predict_op_grpc\",\n)\ntf_gpu_kernel_library_allow_except(\n    name = \"parameter_sync_ops\",\n    srcs = [\"parameter_sync_ops.cc\"],\n    deps = [\n        \":parameter_sync_tf_bridge\",\n        \"@com_github_grpc_grpc//:grpc++_reflection\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n)\n\nproto_library(\n    name = \"logging_ops_proto\",\n    srcs = [\"logging_ops.proto\"],\n)\n\ncc_proto_library(\n    name = \"logging_ops_cc_proto\",\n    visibility = [\"//visibility:public\"],\n    deps = [\":logging_ops_proto\"],\n)\n\npy_proto_library(\n    name = \"logging_ops_py_proto\",\n    srcs = [\"logging_ops.proto\"],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_library(\n    name = \"logging_ops\",\n    srcs = [\n        \"logging_ops.cc\",\n    ],\n    deps = [\n        \":logging_ops_cc_proto\",\n        \"//monolith/native_training/runtime/common:metrics\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"deep_insight_client_tf_bridge\",\n    hdrs = [\"deep_insight_client_tf_bridge.h\"],\n    deps = [\n        \":file_metric_writer\",\n        \"//monolith/native_training/runtime/deep_insight\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n        \"@org_tensorflow//tensorflow/core/kernels:ops_util_hdrs\",\n    ],\n)\n\ncc_library(\n    name = \"deep_insight_ops\",\n    srcs = [\n        \"deep_insight_ops.cc\",\n    ],\n    deps = [\n        \":deep_insight_client_tf_bridge\",\n    ],\n    alwayslink = 1,\n)\n\ncc_library(\n    name = \"agent_heartbeat\",\n    srcs = [\n        \"agent_heartbeat.cc\",\n    ],\n    hdrs = [\n        \"agent_heartbeat.h\",\n    ],\n    deps = [\n        \":net_utils\",\n        \"//monolith/agent_service:agent_service_cc_proto_grpc\",\n        \"@com_github_grpc_grpc//:grpc++\",\n        \"@com_google_absl//absl/container:flat_hash_map\",\n        \"@com_google_absl//absl/container:flat_hash_set\",\n        \"@com_google_absl//absl/status\",\n        \"@com_google_absl//absl/strings\",\n        \"@com_google_absl//absl/synchronization\",\n        \"@com_google_absl//absl/time\",\n        \"@com_google_glog//:glog\",\n        \"@org_tensorflow//tensorflow/core/platform:logging\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_service_proto\",\n    ],\n)\n\ntf_cc_test(\n    name = \"agent_heartbeat_test\",\n    srcs = [\"agent_heartbeat_test.cc\"],\n    extra_copts = [\n        \"-DTEST_USE_GRPC\",\n    ],\n    deps = [\n        \":agent_heartbeat\",\n        \":prediction_service_grpc\",\n        \"@com_google_googletest//:gtest_main\",\n        \"@org_tensorflow//tensorflow/core:test\",\n    ],\n)\n\ncc_library(\n    name = \"net_utils\",\n    srcs = [\"net_utils.cc\"],\n    hdrs = [\"net_utils.h\"],\n)\n\ncc_test(\n    name = \"net_utils_test\",\n    srcs = [\"net_utils_test.cc\"],\n    deps = [\n        \":net_utils\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"serving_deps_with_framework_shared_object\",\n    srcs = [\"@org_tensorflow//tensorflow:libtensorflow_framework.so.2\"],\n    deps = [\n        \"@org_tensorflow//tensorflow/core:distributed_tensorflow_dependencies\",\n        \"@org_tensorflow//tensorflow/core/distributed_runtime/rpc:grpc_runtime\",\n    ],\n)\n\ncc_library(\n    name = \"file_metric_writer\",\n    srcs = [\"file_metric_writer.cc\"],\n    hdrs = [\"file_metric_writer.h\"],\n    deps = [\n        \"//monolith/native_training/runtime/concurrency:queue\",\n        \"//monolith/native_training/runtime/concurrency:thread_pool\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_glog//:glog\",\n        \"@org_tensorflow//tensorflow/core:framework_headers_lib\",\n    ],\n)\n\ntf_cc_test(\n    name = \"file_metric_writer_test\",\n    srcs = [\"file_metric_writer_test.cc\"],\n    deps = [\n        \":file_metric_writer\",\n        \"@com_google_glog//:glog\",\n        \"@com_google_googletest//:gtest_main\",\n        \"@org_tensorflow//tensorflow/core:framework\",\n        \"@org_tensorflow//tensorflow/core:lib\",\n        \"@org_tensorflow//tensorflow/core:tensorflow\",\n    ],\n)\n\n# Expose monolith ops for tf serving\n# we may need to change it to tf_gpu_kernel_library_allow_except later\ncc_library(\n    name = \"serving_ops_cc\",\n    srcs = [\n    ],\n    visibility = [\n        \"//visibility:public\",\n    ],\n    deps = [\n        \":monolith_ops\",\n    ] + select({\n        \"@org_tensorflow//tensorflow:framework_shared_object\": [\":serving_deps_with_framework_shared_object\"],\n        \"//conditions:default\": [],\n    }),\n    alwayslink = 1,\n)\n\nconfig_setting(\n    name = \"serving_gpu\",\n    define_values = {\"using_cuda\": \"true\"},\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/agent_heartbeat.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/agent_heartbeat.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nconst char *const kAgentPortEnvVar = \"PORT2\";\n\nstd::unique_ptr<monolith::serving::agent_service::AgentService::Stub>\nNewAgentStub() {\n  const char *agent_port = getenv(kAgentPortEnvVar);\n  if (agent_port == nullptr) {\n    LOG(FATAL) << \"missing env \" << kAgentPortEnvVar;\n    return nullptr;\n  }\n  auto channel = grpc::CreateChannel(\"localhost:\" + std::string(agent_port),\n                                     grpc::InsecureChannelCredentials());\n  return monolith::serving::agent_service::AgentService::NewStub(channel);\n}\nvoid RemoveOtherAddrsIfThereIsLocalAddr(\n    const std::string &host,\n    google::protobuf::RepeatedPtrField<std::string> *addrs) {\n  std::string local_shard;\n  for (const std::string &addr : *addrs) {\n    if (addr.find(host) == 0) {\n      local_shard = addr;\n      break;\n    }\n  }\n  if (!local_shard.empty()) {\n    addrs->Clear();\n    addrs->Add(std::move(local_shard));\n  }\n}\nint GetApiVersion(\n    const absl::flat_hash_map<std::string, std::vector<std::string>>\n        &model_addrs) {\n  for (const auto it : model_addrs) {\n    const std::string &model = it.first;\n    if (model.find(\":ps\") != std::string::npos) {\n      return 1;\n    }\n  }\n  return 0;\n}\n\nstd::string GetModelKey(absl::string_view model_name,\n                        absl::string_view server_type, int index) {\n  return absl::StrCat(model_name, \":\", server_type, \":\", index);\n}\n\nstd::string GetModelPsKey(absl::string_view model_name, int index) {\n  return GetModelKey(model_name, \"ps\", index);\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/agent_heartbeat.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_AGENT_HEARTBEAT_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_AGENT_HEARTBEAT_H_\n\n#include <atomic>\n#include <chrono>\n#include <cstdlib>\n#include <mutex>\n#include <thread>\n\n#include \"absl/algorithm/container.h\"\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/status/status.h\"\n#include \"absl/strings/str_cat.h\"\n#include \"absl/strings/string_view.h\"\n#include \"absl/synchronization/mutex.h\"\n#include \"absl/synchronization/notification.h\"\n#include \"absl/time/clock.h\"\n#include \"absl/time/time.h\"\n#include \"glog/logging.h\"\n#include \"grpcpp/channel.h\"\n#include \"grpcpp/create_channel.h\"\n#include \"grpcpp/security/credentials.h\"\n#include \"monolith/agent_service/agent_service.grpc.pb.h\"\n#include \"monolith/agent_service/agent_service.pb.h\"\n#include \"monolith/native_training/runtime/ops/net_utils.h\"\n#include \"tensorflow/core/platform/default/logging.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nextern const char *const kAgentPortEnvVar;\n\nstd::unique_ptr<monolith::serving::agent_service::AgentService::Stub>\nNewAgentStub();\n\nvoid RemoveOtherAddrsIfThereIsLocalAddr(\n    const std::string &host,\n    google::protobuf::RepeatedPtrField<std::string> *addrs);\n\nint GetApiVersion(const absl::flat_hash_map<\n                  std::string, std::vector<std::string>> &model_addrs);\n\nstd::string GetModelKey(absl::string_view model_name,\n                        absl::string_view server_type, int index);\n\nstd::string GetModelPsKey(absl::string_view model_name, int index);\n\n// Provide getting PredictionServiceType by task,\n// while update cache data periodically by calling agent service.\ntemplate <typename PredictionServiceType>\nclass AgentHeartbeat {\n public:\n  using AgentService = monolith::serving::agent_service::AgentService;\n\n  AgentHeartbeat() : AgentHeartbeat(NewAgentStub(), absl::Seconds(15)) {}\n\n  ~AgentHeartbeat() {\n    stopped_.Notify();\n    heartbeat_thread_->join();\n  }\n\n  explicit AgentHeartbeat(\n      std::unique_ptr<AgentService::StubInterface> agent_stub,\n      absl::Duration heartbeat_interval)\n      : agent_stub_(std::move(agent_stub)),\n        heartbeat_interval_(heartbeat_interval) {\n    // Manual update once\n    UpdateAddrs();\n    {\n      absl::ReaderMutexLock l(&mu_);\n      api_version_ = GetApiVersion(model_addrs_);\n    }\n    heartbeat_thread_ = std::make_unique<std::thread>(HeartbeatFunc, this);\n  }\n\n  static const AgentHeartbeat &GetInstance() {\n    static AgentHeartbeat *instance = new AgentHeartbeat();\n    return *instance;\n  }\n\n  AgentHeartbeat(AgentHeartbeat const &) = delete;\n  void operator=(AgentHeartbeat const &) = delete;\n\n  // Old API encodes in this way:\n  // API version 0:\n  // model key: `ps:1`\n  // model_name: `ps_1`\n  //\n  // API version 1:\n  // model key: RealModel:ps:1\n  // model name: RealModel:ps:1\n  int api_version() const { return api_version_; }\n\n  // Old APIs. Going to be deprecated.\n  std::shared_ptr<PredictionServiceType> GetPredictionServiceByIdx(\n      int idx) const {\n    return GetPredictionService(absl::StrCat(\"ps:\", idx));\n  }\n\n  std::shared_ptr<PredictionServiceType> GetPredictionService(\n      absl::string_view model_key) const {\n    absl::ReaderMutexLock l(&mu_);\n    auto iter = service_by_model_.find(model_key);\n    if (iter == service_by_model_.end()) {\n      LOG(ERROR) << \"model key doesn't exist: \" << model_key;\n      return nullptr;\n    }\n    return iter->second;\n  }\n\n  void TestOnly_UpdateAddrs() { UpdateAddrs(); }\n  absl::flat_hash_map<std::string, std::vector<std::string>>\n  TestOnly_GetModelAddrs() {\n    absl::ReaderMutexLock l(&mu_);\n    return model_addrs_;\n  }\n\n private:\n  static void HeartbeatFunc(AgentHeartbeat *agent) {\n    absl::Time now = absl::Now();\n    while (!agent->stopped_.WaitForNotificationWithTimeout(\n        now + agent->heartbeat_interval_ - absl::Now())) {\n      now = absl::Now();\n      agent->UpdateAddrs();\n    }\n  }\n\n  // Updates the current model addresses.\n  void GetAddrs(monolith::serving::agent_service::ServerType server_type,\n                absl::flat_hash_map<std::string, std::vector<std::string>>& new_model_addrs) {\n    grpc::ClientContext context;\n    context.set_deadline(std::chrono::system_clock::now() +\n                         absl::ToChronoSeconds(absl::Seconds(5)));\n    monolith::serving::agent_service::HeartBeatRequest req;\n    req.set_server_type(server_type);\n    monolith::serving::agent_service::HeartBeatResponse resp;\n    grpc::Status status = agent_stub_->HeartBeat(&context, req, &resp);\n    if (!status.ok()) {\n      LOG(ERROR) << \"agent_service->HeartBeat error, code: \"\n                 << status.error_code() << \", msg: \" << status.error_message();\n      return;\n    }\n\n    const std::string my_host_ip = GetMyHostIp();\n\n    for (auto &kv : *resp.mutable_addresses()) {\n      const std::string &model = kv.first;\n      auto *resp_addrs = kv.second.mutable_address();\n      std::vector<std::string> addr_list;\n      addr_list.reserve(resp_addrs->size());\n      for (const std::string &addr : *resp_addrs) {\n        addr_list.push_back(addr);\n      }\n      new_model_addrs.insert({model, std::move(addr_list)});\n    }\n  }\n\n  void UpdateAddrs() {\n    absl::flat_hash_map<std::string, std::vector<std::string>> new_model_addrs;\n    GetAddrs(monolith::serving::agent_service::PS, new_model_addrs);\n    GetAddrs(monolith::serving::agent_service::DENSE, new_model_addrs);\n\n    bool same;\n    {\n      absl::ReaderMutexLock l(&mu_);\n      same = (new_model_addrs == model_addrs_);\n    }\n\n    if (!same) {\n      absl::flat_hash_map<std::string, std::shared_ptr<PredictionServiceType>>\n          new_service_by_model;\n      for (auto &kv : new_model_addrs) {\n        new_service_by_model.emplace(\n            kv.first, std::make_shared<PredictionServiceType>(kv.second));\n      }\n\n      {\n        absl::MutexLock l(&mu_);\n        model_addrs_.swap(new_model_addrs);\n        service_by_model_.swap(new_service_by_model);\n      }\n    }\n  }\n\n  absl::Notification stopped_;\n  std::unique_ptr<AgentService::StubInterface> agent_stub_;\n  absl::Duration heartbeat_interval_;\n  int api_version_;\n\n  // This is for the public API to use.\n  absl::flat_hash_map<std::string, std::vector<std::string>> model_addrs_;\n  mutable absl::Mutex mu_;\n  absl::flat_hash_map<std::string, std::shared_ptr<PredictionServiceType>>\n      service_by_model_ GUARDED_BY(mu_);\n\n  std::unique_ptr<std::thread> heartbeat_thread_;\n};\n\n// Gets the model key.\nstd::string GetModelPsKey(absl::string_view model_name, int index);\nstd::string GetModelKey(absl::string_view model_name,\n                        absl::string_view server_type, int index);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_AGENT_HEARTBEAT_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/agent_heartbeat_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n\n\n#ifdef TEST_USE_GRPC\n#include \"monolith/native_training/runtime/ops/prediction_service_grpc.h\"\n#else\n#include \"monolith/native_training/runtime/ops/prediction_service_archon.h\"\n#endif\n#include \"gmock/gmock.h\"\n#include \"grpcpp/server.h\"\n#include \"grpcpp/server_builder.h\"\n#include \"gtest/gtest.h\"\n#include \"monolith/agent_service/agent_service_mock.grpc.pb.h\"\n#include \"monolith/native_training/runtime/ops/agent_heartbeat.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nnamespace tf_serving = ::tensorflow::serving;\nusing DoneCallback = std::function<void()>;\n\n#ifdef TEST_USE_GRPC\nusing PredictionServiceType = PredictionServiceGrpc;\n#else\nusing PredictionServiceType = PredictionServiceArchon;\n#endif\n\nusing ::monolith::serving::agent_service::AddressList;\nusing ::monolith::serving::agent_service::AgentService;\nusing ::monolith::serving::agent_service::HeartBeatResponse;\nusing ::monolith::serving::agent_service::MockAgentServiceStub;\nusing ::testing::DoAll;\nusing ::testing::ElementsAre;\nusing ::testing::InSequence;\nusing ::testing::Pair;\nusing ::testing::Return;\nusing ::testing::SetArgPointee;\nusing ::testing::UnorderedElementsAre;\n\nconst absl::Duration kNoHeartbeat = absl::Hours(1000);\n\nTEST(AgentHeartbeatTest, Basic) {\n  auto stub = std::make_unique<MockAgentServiceStub>();\n  {\n    InSequence s;\n    HeartBeatResponse resp1;\n    (*resp1.mutable_addresses())[\"model_name\"].add_address(\"localhost:1\");\n    EXPECT_CALL(*stub, HeartBeat)\n        .WillOnce(DoAll(SetArgPointee<2>(resp1), Return(grpc::Status::OK)));\n\n    HeartBeatResponse resp2;\n    (*resp2.mutable_addresses())[\"model_name\"].add_address(\"localhost:2\");\n    EXPECT_CALL(*stub, HeartBeat)\n        .WillRepeatedly(DoAll(SetArgPointee<2>(resp2), Return(grpc::Status::OK)));\n  }\n\n  AgentHeartbeat<PredictionServiceType> agent(std::move(stub), kNoHeartbeat);\n  for (const auto &p : agent.TestOnly_GetModelAddrs()) {\n    printf(\"model_name = %s\\n\", p.first.c_str());\n    for (const auto addr : p.second) {\n      printf(\"%s \", addr.c_str());\n    }\n    puts(\"\");\n  }\n  EXPECT_THAT(\n      agent.TestOnly_GetModelAddrs(),\n      UnorderedElementsAre(Pair(\"model_name\", ElementsAre(\"localhost:1\"))));\n  agent.TestOnly_UpdateAddrs();\n  EXPECT_THAT(\n      agent.TestOnly_GetModelAddrs(),\n      UnorderedElementsAre(Pair(\"model_name\", ElementsAre(\"localhost:2\"))));\n}\n\nTEST(AgentHeartbeatTest, HeartBeat) {\n  auto stub = std::make_unique<MockAgentServiceStub>();\n  {\n    InSequence s;\n    HeartBeatResponse resp1;\n    (*resp1.mutable_addresses())[\"model_name\"].add_address(\"localhost:1\");\n    EXPECT_CALL(*stub, HeartBeat)\n        .WillOnce(DoAll(SetArgPointee<2>(resp1), Return(grpc::Status::OK)));\n\n    HeartBeatResponse resp2;\n    (*resp2.mutable_addresses())[\"model_name\"].add_address(\"localhost:2\");\n    EXPECT_CALL(*stub, HeartBeat)\n        .WillRepeatedly(\n            DoAll(SetArgPointee<2>(resp2), Return(grpc::Status::OK)));\n  }\n\n  AgentHeartbeat<PredictionServiceType> agent(std::move(stub),\n                                              absl::ZeroDuration());\n  // Waits for heartbeat update.\n  absl::SleepFor(absl::Seconds(0.2));\n  EXPECT_THAT(\n      agent.TestOnly_GetModelAddrs(),\n      UnorderedElementsAre(Pair(\"model_name\", ElementsAre(\"localhost:2\"))));\n}\n\nTEST(AgentHeartbeatTest, DefaultInstance) {\n  setenv(kAgentPortEnvVar, \"1234\", 1);\n  AgentHeartbeat<PredictionServiceType>::GetInstance();\n}\n\nclass MockPredictionService : public tf_serving::PredictionService::Service {\n public:\n  MOCK_METHOD(grpc::Status, Predict,\n              (grpc::ServerContext *, const tf_serving::PredictRequest *,\n               tf_serving::PredictResponse *));\n};\n\nstd::unique_ptr<grpc::Server> StartServer(\n    tf_serving::PredictionService::Service *service, int *port) {\n  grpc::ServerBuilder builder;\n  builder.AddListeningPort(absl::StrCat(GetMyHostIp(), \":0\"),\n                           grpc::InsecureServerCredentials(), port);\n  builder.RegisterService(service);\n  return builder.BuildAndStart();\n}\n\nTEST(AgentHeartbeatTest, StubTest) {\n  MockPredictionService service;\n  EXPECT_CALL(service, Predict);\n\n  int port;\n  auto server = StartServer(&service, &port);\n\n  auto stub = std::make_unique<MockAgentServiceStub>();\n  HeartBeatResponse resp;\n  (*resp.mutable_addresses())[\"model_name\"].add_address(\n      absl::StrCat(GetMyHostIp(), \":\", port));\n  EXPECT_CALL(*stub, HeartBeat)\n      .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK)));\n\n  AgentHeartbeat<PredictionServiceType> agent(std::move(stub), kNoHeartbeat);\n  std::shared_ptr<PredictionServiceType> predict =\n      agent.GetPredictionService(\"model_name\");\n  tf_serving::PredictRequest predict_req;\n  tf_serving::PredictResponse predict_resp;\n  absl::Notification notify;\n  predict->Predict(\n      &predict_req, &predict_resp,\n      [&notify](absl::Status s, DoneCallback &&op_done) { notify.Notify(); },\n      1000, [] {});\n  notify.WaitForNotification();\n}\n\nTEST(AgentHeartbeatTest, ApiVersion) {\n  auto stub = std::make_unique<MockAgentServiceStub>();\n  HeartBeatResponse resp;\n  (*resp.mutable_addresses())[\"ps:0\"].add_address(\"local_host:0\");\n  EXPECT_CALL(*stub, HeartBeat)\n      .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK)));\n  AgentHeartbeat<PredictionServiceType> agent(std::move(stub), kNoHeartbeat);\n  EXPECT_THAT(agent.api_version(), 0);\n}\n\nTEST(AgentHeartbeatTest2, ApiVersion) {\n  auto stub = std::make_unique<MockAgentServiceStub>();\n  HeartBeatResponse resp;\n  (*resp.mutable_addresses())[\"model_name:ps_0\"].add_address(\"local_host:0\");\n  EXPECT_CALL(*stub, HeartBeat)\n      .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK)));\n  AgentHeartbeat<PredictionServiceType> agent(std::move(stub), kNoHeartbeat);\n  EXPECT_THAT(agent.api_version(), 1);\n}\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/aligned_concat_split.cu.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#if GOOGLE_CUDA\n#define EIGEN_USE_GPU\n\n#include \"monolith/native_training/runtime/ops/alloc_utils.h\"\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/register_types.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/kernels/gpu_device_array.h\"\n#include \"tensorflow/core/kernels/gpu_device_array_gpu.h\"\n#include \"tensorflow/core/util/gpu_kernel_helper.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\n__global__ void flat_concat(\n    GpuDeviceArrayStruct<const float*> input_ptrs_da,  // length = 2N+1\n    int total, const float* _scale, float* out) {\n  float scale = *_scale;\n  auto _input_ptrs = GetGpuDeviceArrayOnDevice(&input_ptrs_da);\n  extern __shared__ const float* input_ptrs[];\n  for (int i = threadIdx.x; i < input_ptrs_da.size; i += blockDim.x)\n    input_ptrs[i] = _input_ptrs[i];\n  __syncthreads();\n  auto N = (input_ptrs_da.size - 1) / 2;\n  auto sizes = reinterpret_cast<const int*>(input_ptrs + N);\n  auto offsets = sizes + N;\n  auto tid = threadIdx.x + blockIdx.x * blockDim.x;\n  auto stride = blockDim.x * gridDim.x;\n  int work_id = 0;\n  for (int id = tid; id < total; id += stride) {\n    while (offsets[work_id + 1] <= id) work_id++;\n\n    int i = id - offsets[work_id];\n    if (i < sizes[work_id]) {\n      out[id] = input_ptrs[work_id][i] * scale;\n    } else {\n      out[id] = 0.0f;\n    }\n  }\n}\n\n// Flatten each input and then concatenate them. This op also ensures that the\n// start position of each input in the concat output is suitably aligned (as per\n// Tensorflow/Eigen's requirement), so that we can perform a split without\n// copying the underlying memory\nclass AlignedFlatConcat : public OpKernel {\n public:\n  explicit AlignedFlatConcat(OpKernelConstruction* context)\n      : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"N\", &N_));\n  }\n\n  void Compute(OpKernelContext* context) override {\n    const auto& gpu_device = context->eigen_gpu_device();\n    static_assert(sizeof(int) * 2 == sizeof(const float*));\n    GpuDeviceArrayOnHost<const float*> input_ptrs(context, 2 * N_ + 1);\n    OP_REQUIRES_OK(context, input_ptrs.Init());\n\n    FusedAlignedOutputAllocator<EIGEN_MAX_ALIGN_BYTES / sizeof(float)>\n        fao_alloc(context);\n    std::vector<int> offsets_sizes(2 * N_ + 2);\n    for (int i = 0; i < N_; ++i) {\n      auto sz = context->input(i).NumElements();\n      input_ptrs.Set(i, context->input(i).flat<float>().data());\n      offsets_sizes[i] = sz;\n      offsets_sizes[N_ + i] = fao_alloc.get_aligned_total();\n      fao_alloc.add_slice(sz);\n    }\n    int total = fao_alloc.get_aligned_total();\n    offsets_sizes[2 * N_] = total;\n    auto data = reinterpret_cast<const float**>(offsets_sizes.data());\n    for (int i = 0; i <= N_; ++i) input_ptrs.Set(N_ + i, data[i]);\n\n    OP_REQUIRES_OK(context, input_ptrs.Finalize());\n    OP_REQUIRES(context, 2 * N_ + 1 <= 2048,\n                errors::Unknown(\"Total size of \", 2 * N_ + 1,\n                                \" is greater than 2048 so is not supported. \"\n                                \"Please contact the developers.\"));\n\n    Tensor* out;\n    OP_REQUIRES_OK(context, context->allocate_output(0, {total}, &out));\n    auto config = GetGpuLaunchConfig(total, gpu_device);\n    TF_CHECK_OK(GpuLaunchKernel(\n        flat_concat, config.block_count, config.thread_per_block,\n        sizeof(const float*) * (2 * N_ + 1), gpu_device.stream(),\n        input_ptrs.data(), total, context->input(N_).flat<float>().data(),\n        out->flat<float>().data()));\n  }\n\n private:\n  int N_;\n};\n\nclass AlignedFlatSplit : public OpKernel {\n public:\n  explicit AlignedFlatSplit(OpKernelConstruction* context) : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"N\", &N_));\n  }\n  void Compute(OpKernelContext* context) override {\n    FusedAlignedOutputAllocator<EIGEN_MAX_ALIGN_BYTES / sizeof(float)>\n        fao_alloc(context);\n    const auto& flat = context->input(N_);\n    for (int i = 0; i < N_; ++i) {\n      context->set_output(i,\n                          fao_alloc.get_slice(context->input(i).shape(), flat));\n    }\n  }\n\n private:\n  int N_;\n};\nREGISTER_OP(\"MonolithAlignedFlatConcat\")\n    .Input(\"inputs: N * float\")\n    .Input(\"scale: float\")\n    .Output(\"concat: float\")\n    .Attr(\"N: int\")\n    .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) {\n      c->set_output(0, c->Vector(c->UnknownDim()));\n      return tensorflow::Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithAlignedFlatConcat\").Device(DEVICE_GPU),\n    AlignedFlatConcat);\n\nREGISTER_OP(\"MonolithAlignedFlatSplit\")\n    .Input(\"inputs: N * float\")  // for shape inference only, data not used\n    .Input(\"flat: float\")\n    .Output(\"concat: N * float\")\n    .Attr(\"N: int\")\n    .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) {\n      for (int i = 0; i < c->num_inputs() - 1; ++i) {\n        c->set_output(i, c->input(i));\n      }\n      return tensorflow::Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithAlignedFlatSplit\").Device(DEVICE_GPU),\n                        AlignedFlatSplit);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/alloc_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#pragma once\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\nnamespace tensorflow {\nnamespace monolith_tf {\n/**\n * A tiny, fast allocator that allocates aligned output tensors,\n * each having different shape.\n * \n * Useful when you have > 500 output tensors for your Op\n * and calls allocate_output become the bottleneck\n * \n * How to use:\n * First, initialize your allocator with the desired alignment.\n * Note that the alignment is specified in terms of the number of elements of \n * the corresponding dtype, not in bytes.\n * FusedAlignedOutputAllocator<EIGEN_MAX_ALIGN_BYTES / sizeof(your_dtype)> alloc;\n * \n * Then, tell the allocator the total size of your output by calling add_slice in a loop\n * for (int i = 0; i < num_outputs; i++) {\n *   alloc.add_slice(num_elements_in_this_output);\n * }\n * \n * Then, call .allocate. This will be a single call to ctx->allocate_temp\n * alloc.allocate(YOUR_DTYPE);\n * \n * Finally, get each output in the same order as you call add_slice.\n * You need to specify the shape for each output. \n * for (int i = 0; i < num_outputs; i++) {\n *   ctx->set_output(i, alloc.get_slice({DIM_SIZE1, ...}));\n * }\n * \n * There's also a get_unaligned_total that may come in handy\n * if you want to get the total size of your output without padding\n*/\ntemplate <size_t alignment>\nclass FusedAlignedOutputAllocator {\n public:\n  explicit FusedAlignedOutputAllocator(OpKernelContext* ctx): ctx_(ctx) {\n  }\n  inline void add_slice(int64 num_elements) {\n    total_ += num_elements;\n    aligned_total_ += round_up_to_align(num_elements);\n  }\n  inline void allocate(DataType dtype) {\n    // allocate_temp may seem suspicious here, but it's properly reference counted\n    // (including its slice), so we don't need to worry about its lifetime problem\n    OP_REQUIRES_OK(ctx_, ctx_->allocate_temp(dtype, {aligned_total_}, &flat_out_));\n    aligned_total_ = 0;\n  }\n  inline Tensor get_slice(const TensorShape& shape, const Tensor& flat) {\n    int64 num_elements = shape.num_elements();\n    Tensor reshaped;\n    // note: CopyFrom and Slice doesn't copy the underlying memory\n    (void)reshaped.CopyFrom(flat.Slice(aligned_total_, aligned_total_ + num_elements), shape);\n    aligned_total_ += round_up_to_align(num_elements);\n    return reshaped;\n  }\n  inline Tensor get_slice(const TensorShape& shape) {\n    return get_slice(shape, flat_out_);\n  }\n  inline int64 get_unaligned_total() const {\n    return total_;\n  }\n  inline int64 get_aligned_total() const {\n    return aligned_total_;\n  }\n\n private:\n  OpKernelContext* ctx_;\n  int64 aligned_total_ = 0;\n  int64 total_ = 0;\n  Tensor flat_out_;\n  static constexpr int64 round_up_to_align(int64 a) {\n    if (alignment == 0)\n      return a;\n    constexpr int64 temp = alignment - 1;\n    constexpr int64 temp2 = ~temp;\n    return (a + temp) & temp2;\n  }\n};\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/clip_by_global_norm.cu.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#if GOOGLE_CUDA\n#define EIGEN_USE_GPU\n\n#include \"monolith/native_training/runtime/ops/clip_by_global_norm.h\"\n\n#include \"monolith/native_training/runtime/ops/alloc_utils.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/kernels/gpu_device_array.h\"\n#include \"tensorflow/core/kernels/gpu_device_array_gpu.h\"\n#include \"tensorflow/core/util/gpu_kernel_helper.h\"\n\nnamespace tensorflow {\nnamespace monolith {\n\nnamespace {\n__global__ void element_wise_mul(\n    GpuDeviceArrayStruct<const float*> input_ptrs_da,\n    GpuDeviceArrayStruct<float*> output_ptrs_da,\n    GpuDeviceArrayStruct<int> offsets_da, int size, float scale) {\n  const float** input_ptrs = GetGpuDeviceArrayOnDevice(&input_ptrs_da);\n  int* offsets = GetGpuDeviceArrayOnDevice(&offsets_da);\n  float** output_ptrs = GetGpuDeviceArrayOnDevice(&output_ptrs_da);\n\n  // if using shared memory\n  // Ref:\n  // https://github.com/tensorflow/tensorflow/blob/v2.4.0/tensorflow/core/kernels/split_lib_gpu.cu.cc#L124\n  GPU_DYNAMIC_SHARED_MEM_DECL(sizeof(int), unsigned char, smem);\n  int* smem_offsets = reinterpret_cast<int*>(smem);\n  for (int x = threadIdx.x; x < offsets_da.size; x += blockDim.x) {\n    smem_offsets[x] = offsets[x];\n  }\n  __syncthreads();\n  offsets = smem_offsets;\n\n  int i = 0;\n  GPU_1D_KERNEL_LOOP(idx, size) {\n    // safe offsets read: when idx == size - 1, i+1 == num_inputs\n    while (offsets[i + 1] <= idx) ++i;\n    int j = idx - offsets[i];\n    output_ptrs[i][j] = ldg(input_ptrs[i] + j) * scale;\n  }\n}\n}  // namespace\n\ntypedef Eigen::GpuDevice GPUDevice;\n\ntemplate <>\nstruct ClipByGlobalNormImpl<GPUDevice> {\n  static void Compute(OpKernelContext* context, float scale) {\n    const auto& gpu_device = context->eigen_gpu_device();\n    auto N_ = context->num_inputs() - 2;\n    GpuDeviceArrayOnHost<const float*> input_ptrs_da(context, N_);\n    GpuDeviceArrayOnHost<int> offsets(context, N_ + 1);\n    OP_REQUIRES_OK(context, input_ptrs_da.Init());\n    OP_REQUIRES_OK(context, offsets.Init());\n    monolith_tf::FusedAlignedOutputAllocator<EIGEN_MAX_ALIGN_BYTES /\n                                             sizeof(float)>\n        fao_alloc(context);\n    for (int i = 0; i < N_; ++i) {\n      input_ptrs_da.Set(i, context->input(i).flat<float>().data());\n      offsets.Set(i, fao_alloc.get_unaligned_total());\n      fao_alloc.add_slice(context->input(i).NumElements());\n    }\n    int total = fao_alloc.get_unaligned_total();\n    offsets.Set(N_, total);\n\n    OP_REQUIRES_OK(context, input_ptrs_da.Finalize());\n    OP_REQUIRES_OK(context, offsets.Finalize());\n\n    GpuDeviceArrayOnHost<float*> output_ptrs_da(context, N_);\n    OP_REQUIRES_OK(context, output_ptrs_da.Init());\n    fao_alloc.allocate(DT_FLOAT);\n    for (int i = 0; i < N_; ++i) {\n      auto t = fao_alloc.get_slice(context->input(i).shape());\n      output_ptrs_da.Set(i, t.flat<float>().data());\n      context->set_output(i, std::move(t));\n    }\n    OP_REQUIRES_OK(context, output_ptrs_da.Finalize());\n    auto config = GetGpuLaunchConfig(total, gpu_device);\n\n    const int smem_usage = sizeof(int) * (N_ + 1);\n    TF_CHECK_OK(GpuLaunchKernel(\n        element_wise_mul, config.block_count, config.thread_per_block,\n        smem_usage, gpu_device.stream(), input_ptrs_da.data(),\n        output_ptrs_da.data(), offsets.data(), total, scale));\n  }\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithClipByGlobalNorm\")\n                            .Device(DEVICE_GPU)\n                            .HostMemory(\"global_norm\")\n                            .HostMemory(\"clip_norm\"),\n                        ClipByGlobalNorm<GPUDevice>);\n}  // namespace monolith\n}  // namespace tensorflow\n\n#endif  // GOOGLE_CUDA\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/clip_by_global_norm.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_CLIP_BY_GLOBAL_NORM\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_CLIP_BY_GLOBAL_NORM\n\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/register_types.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/util/work_sharder.h\"\n\nnamespace tensorflow {\nnamespace monolith {\n\ntemplate <typename Device>\nstruct ClipByGlobalNormImpl {\n  static void Compute(OpKernelContext* context, float scale);\n};\n\ntemplate <typename Device>\nclass ClipByGlobalNorm : public OpKernel {\n public:\n  explicit ClipByGlobalNorm(OpKernelConstruction* context)\n      : OpKernel(context) {}\n\n  void Compute(OpKernelContext* context) override {\n    int num_inputs = context->num_inputs() - 2;\n    float global_norm = context->input(num_inputs).scalar<float>()();\n    float clip_norm = context->input(num_inputs + 1).scalar<float>()();\n    if (global_norm > clip_norm) {\n      ClipByGlobalNormImpl<Device>::Compute(context, clip_norm / global_norm);\n    } else {\n      // If no clip, output as input.\n      for (int i = 0; i < num_inputs; ++i) {\n        context->set_output(i, context->input(i));\n      }\n    }\n  }\n};\n\n}  // namespace monolith\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_CLIP_BY_GLOBAL_NORM\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/clip_by_global_norm_fused.cu.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#if GOOGLE_CUDA\n#define EIGEN_USE_GPU\n\n#include \"monolith/native_training/runtime/ops/alloc_utils.h\"\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/register_types.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/kernels/gpu_device_array.h\"\n#include \"tensorflow/core/kernels/gpu_device_array_gpu.h\"\n#include \"tensorflow/core/kernels/gpu_prim.h\"\n#include \"tensorflow/core/util/gpu_kernel_helper.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\ntemplate <int BLOCK_THREADS>\n__global__ void globalReduceSum(\n    GpuDeviceArrayStruct<const float*> input_ptrs_da,\n    GpuDeviceArrayStruct<int> offsets_da, float* out, int size) {\n  const float** input_ptrs = GetGpuDeviceArrayOnDevice(&input_ptrs_da);\n  int* offsets = GetGpuDeviceArrayOnDevice(&offsets_da);\n\n  extern __shared__ int smem[];\n  for (int x = threadIdx.x; x < offsets_da.size; x += blockDim.x) {\n    smem[x] = offsets[x];\n  }\n  __syncthreads();\n  offsets = smem;\n\n  float thread_sum = 0;\n  int i = 0;\n  GPU_1D_KERNEL_LOOP(idx, size) {\n    // safe offsets read: when idx == size - 1, i+1 == N_\n    while (offsets[i + 1] <= idx) ++i;\n    int j = idx - offsets[i];\n    float v = ldg(input_ptrs[i] + j);\n    thread_sum += v * v;  // l2\n  }\n  // thread reduce sum to block reduce sum\n  typedef gpuprim::BlockReduce<float, BLOCK_THREADS> BlockReduce;\n  __shared__ typename BlockReduce::TempStorage temp_storage;\n  float block_sum = BlockReduce(temp_storage).Sum(thread_sum);\n  if (threadIdx.x == 0)\n    // block reduce sum to global reduce sum\n    atomicAdd(out, block_sum);\n}\n\n__global__ void element_wise_mul(\n    GpuDeviceArrayStruct<const float*> input_ptrs_da,\n    GpuDeviceArrayStruct<float*> output_ptrs_da,\n    GpuDeviceArrayStruct<int> offsets_da, int size, float scale) {\n  const float** input_ptrs = GetGpuDeviceArrayOnDevice(&input_ptrs_da);\n  int* offsets = GetGpuDeviceArrayOnDevice(&offsets_da);\n  float** output_ptrs = GetGpuDeviceArrayOnDevice(&output_ptrs_da);\n\n  extern __shared__ int smem[];\n  for (int x = threadIdx.x; x < offsets_da.size; x += blockDim.x) {\n    smem[x] = offsets[x];\n  }\n  __syncthreads();\n  offsets = smem;\n\n  int i = 0;\n  GPU_1D_KERNEL_LOOP(idx, size) {\n    // safe offsets read: when idx == size - 1, i+1 == num_inputs\n    while (offsets[i + 1] <= idx) ++i;\n    int j = idx - offsets[i];\n    output_ptrs[i][j] = ldg(input_ptrs[i] + j) * scale;\n  }\n}\n}  // namespace\n\nclass ClipByGlobalNormFused : public OpKernel {\n public:\n  explicit ClipByGlobalNormFused(OpKernelConstruction* context)\n      : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"N\", &N_));\n  }\n\n  void Compute(OpKernelContext* context) override {\n    const auto& gpu_device = context->eigen_gpu_device();\n    GpuDeviceArrayOnHost<const float*> input_ptrs_da(context, N_);\n    GpuDeviceArrayOnHost<int> offsets(context, N_ + 1);\n    OP_REQUIRES_OK(context, input_ptrs_da.Init());\n    OP_REQUIRES_OK(context, offsets.Init());\n    FusedAlignedOutputAllocator<EIGEN_MAX_ALIGN_BYTES / sizeof(float)>\n        fao_alloc(context);\n    for (int i = 0; i < N_; ++i) {\n      input_ptrs_da.Set(i, context->input(i).flat<float>().data());\n      offsets.Set(i, fao_alloc.get_unaligned_total());\n      fao_alloc.add_slice(context->input(i).NumElements());\n    }\n    int total = fao_alloc.get_unaligned_total();\n    offsets.Set(N_, total);\n\n    OP_REQUIRES_OK(context, input_ptrs_da.Finalize());\n    OP_REQUIRES_OK(context, offsets.Finalize());\n\n    Tensor d_norm;\n    OP_REQUIRES_OK(context, context->allocate_temp(DT_FLOAT, {}, &d_norm));\n    gpu_device.memset(d_norm.data(), 0, sizeof(float));\n\n    constexpr int block_sz = 1024;\n    const int smem_usage = sizeof(int) * (N_ + 1);\n    TF_CHECK_OK(GpuLaunchKernel(\n        globalReduceSum<block_sz>,\n        std::min(gpu_device.maxGpuThreadsPerMultiProcessor() / block_sz, 1) *\n            gpu_device.getNumGpuMultiProcessors(),\n        block_sz, smem_usage, gpu_device.stream(), input_ptrs_da.data(),\n        offsets.data(), d_norm.flat<float>().data(), total));\n\n    // async kernel launch above can hide some latency of the code below until\n    // synchronize\n    Tensor* h_norm_out;\n    OP_REQUIRES_OK(context, context->allocate_output(N_, {}, &h_norm_out));\n    gpu_device.memcpyDeviceToHost(h_norm_out->data(), d_norm.data(),\n                                  sizeof(float));\n\n    GpuDeviceArrayOnHost<float*> output_ptrs_da(context, N_);\n    OP_REQUIRES_OK(context, output_ptrs_da.Init());\n\n    float clip_norm = context->input(N_).scalar<float>()();\n    fao_alloc.allocate(DT_FLOAT);\n    for (int i = 0; i < N_; ++i) {\n      auto t = fao_alloc.get_slice(context->input(i).shape());\n      output_ptrs_da.Set(i, t.flat<float>().data());\n      // if this ends up unused, it will be overwritten\n      context->set_output(i, std::move(t));\n    }\n    OP_REQUIRES_OK(context, output_ptrs_da.Finalize());\n    auto config = GetGpuLaunchConfig(total, gpu_device);\n\n    gpu_device.synchronize();\n\n    float global_norm = std::sqrt(h_norm_out->scalar<float>()());\n    if (global_norm > clip_norm) {\n      TF_CHECK_OK(GpuLaunchKernel(element_wise_mul, config.block_count,\n                                  config.thread_per_block, smem_usage,\n                                  gpu_device.stream(), input_ptrs_da.data(),\n                                  output_ptrs_da.data(), offsets.data(), total,\n                                  clip_norm / global_norm));\n    } else {\n      for (int i = 0; i < N_; ++i) {\n        *context->mutable_output(i) = context->input(i);\n      }\n    }\n  }\n\n private:\n  int N_;\n};\n\nREGISTER_OP(\"MonolithClipByGlobalNormFused\")\n    .Input(\"grad_list: N * float\")\n    .Input(\"clip_norm: float\")\n    .Output(\"clipped: N * float\")\n    .Output(\"global_norm: float\")\n    .Attr(\"N: int\")\n    .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) {\n      for (int i = 0; i < c->num_inputs(); ++i) {\n        c->set_output(i, c->input(i));\n      }\n      return tensorflow::Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithClipByGlobalNormFused\")\n                            .Device(DEVICE_GPU)\n                            .HostMemory(\"global_norm\")\n                            .HostMemory(\"clip_norm\"),\n                        ClipByGlobalNormFused);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/clip_by_global_norm_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"clip_by_global_norm.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/register_types.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/util/work_sharder.h\"\n\nnamespace tensorflow {\nnamespace monolith {\n\ntypedef Eigen::ThreadPoolDevice CPUDevice;\n\ntemplate <>\nstruct ClipByGlobalNormImpl<CPUDevice> {\n  static void Compute(OpKernelContext* context, float scale) {\n    int num_inputs = context->num_inputs() - 2;\n    bool user_parallel = num_inputs > 4;\n    auto func = [context, scale](int64 start, int64 end) {\n      for (int64 i = start; i < end; ++i) {\n        Tensor* temp;\n        context->allocate_output(i, context->input(i).shape(), &temp);\n        temp->flat<float>() = context->input(i).flat<float>() * scale;\n      }\n    };\n    if (user_parallel) {\n      auto worker_threads =\n          *(context->device()->tensorflow_cpu_worker_threads());\n      Shard(worker_threads.num_threads, worker_threads.workers, num_inputs,\n            (num_inputs + worker_threads.num_threads - 1) /\n                worker_threads.num_threads,\n            func);\n    } else {\n      func(0, num_inputs);\n    }\n  }\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithClipByGlobalNorm\").Device(DEVICE_CPU),\n                        ClipByGlobalNorm<CPUDevice>);\n\n// End: Kernel Definition\nREGISTER_OP(\"MonolithClipByGlobalNorm\")\n    .Input(\"grad_list: N * float\")\n    .Input(\"global_norm: float\")\n    .Input(\"clip_norm: float\")\n    .Output(\"clip_grad_list: N * float\")\n    .Attr(\"N: int\")\n    .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) {\n      int input_n = c->num_inputs() - 2;\n      for (int i = 0; i < input_n; ++i) {\n        c->set_output(i, c->input(i));\n      }\n      return tensorflow::Status::OK();\n    });\n\n}  // namespace monolith\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/deep_insight_client_tf_bridge.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_DEEP_INSIGHT_CLIENT_TF_BRIDGE\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_DEEP_INSIGHT_CLIENT_TF_BRIDGE\n\n#include <string>\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/deep_insight/deep_insight.h\"\n#include \"monolith/native_training/runtime/ops/file_metric_writer.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n\nusing monolith::deep_insight::ExtraField;\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass DeepInsightClientTfBridge : public ResourceBase {\n public:\n  explicit DeepInsightClientTfBridge(\n      std::unique_ptr<monolith::deep_insight::DeepInsight> deep_insight_client,\n      std::unique_ptr<monolith::deep_insight::FileMetricWriter>\n          file_metric_writer)\n      : deep_insight_client_(std::move(deep_insight_client)),\n        file_metric_writer_(std::move(file_metric_writer)) {}\n\n  std::string SendV2(\n      const std::string& model_name, const std::vector<std::string>& targets,\n      uint64_t uid, int64_t req_time, int64_t train_time,\n      const std::vector<float>& labels, const std::vector<float>& preds,\n      const std::vector<float>& sample_rates, float sample_ratio,\n      const std::vector<std::shared_ptr<ExtraField>>& extra_fields,\n      bool return_msgs) {\n    std::string msg = deep_insight_client_->SendV2(\n        model_name, targets, uid, req_time, train_time, labels, preds,\n        sample_rates, sample_ratio, extra_fields, true);\n    file_metric_writer_->Write(msg);\n    return return_msgs ? msg : \"\";\n  }\n\n  int64_t GenerateTrainingTime() {\n    return deep_insight_client_->GenerateTrainingTime();\n  }\n\n  uint64_t GetTotalSendCounter() {\n    return deep_insight_client_->GetTotalSendCounter();\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Total send counter: %d\",\n                           deep_insight_client_->GetTotalSendCounter());\n  }\n\n private:\n  std::unique_ptr<monolith::deep_insight::DeepInsight> deep_insight_client_;\n  std::unique_ptr<monolith::deep_insight::FileMetricWriter> file_metric_writer_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_DEEP_INSIGHT_CLIENT_TF_BRIDGE\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/deep_insight_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/deep_insight_client_tf_bridge.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\nusing monolith::deep_insight::ExtraField;\nusing monolith::deep_insight::FloatExtraField;\nusing monolith::deep_insight::Int64ExtraField;\nusing monolith::deep_insight::StringExtraField;\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass MonolithCreateDeepInsightClientOp\n    : public ResourceOpKernel<DeepInsightClientTfBridge> {\n public:\n  explicit MonolithCreateDeepInsightClientOp(OpKernelConstruction* ctx)\n      : ResourceOpKernel(ctx) {\n    OP_REQUIRES_OK(\n        ctx, ctx->GetAttr(\"enable_metrics_counter\", &enable_metrics_counter_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"is_fake\", &is_fake_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"filename\", &filename_));\n  }\n\n  ~MonolithCreateDeepInsightClientOp() override {}\n\n private:\n  Status CreateResource(DeepInsightClientTfBridge** deep_insight_client_bridge)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    auto deep_insight_client =\n        std::make_unique<monolith::deep_insight::DeepInsight>(\n            enable_metrics_counter_, is_fake_);\n    auto file_metric_writer =\n        std::make_unique<monolith::deep_insight::FileMetricWriter>(filename_);\n    *deep_insight_client_bridge = new DeepInsightClientTfBridge(\n        std::move(deep_insight_client), std::move(file_metric_writer));\n    return Status::OK();\n  }\n\n  bool enable_metrics_counter_;\n  bool is_fake_;\n  std::string filename_;\n};\n\nclass MonolithWriteDeepInsightOp : public OpKernel {\n public:\n  explicit MonolithWriteDeepInsightOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"model_name\", &model_name_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"target\", &target_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"sample_ratio\", &sample_ratio_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"return_msgs\", &return_msgs_));\n    OP_REQUIRES_OK(ctx,\n                   ctx->GetAttr(\"use_zero_train_time\", &use_zero_train_time_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    DeepInsightClientTfBridge* deep_insight_client = nullptr;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0),\n                                       &deep_insight_client));\n    core::ScopedUnref unref(deep_insight_client);\n\n    auto uids_vec = ctx->input(1).vec<int64_t>();\n    auto req_times_vec = ctx->input(2).vec<int64_t>();\n    auto labels_vec = ctx->input(3).vec<float>();\n    auto preds_vec = ctx->input(4).vec<float>();\n    auto sample_rates_vec = ctx->input(5).vec<float>();\n\n    int64_t train_time =\n        use_zero_train_time_ ? 0 : deep_insight_client->GenerateTrainingTime();\n\n    int64_t batch_size = labels_vec.dimension(0);\n    Tensor* msgs;\n    ctx->allocate_output(0, {batch_size}, &msgs);\n    auto msgs_vec = msgs->vec<tstring>();\n\n    std::vector<std::string> targets;\n    targets.push_back(target_);\n\n    for (uint32_t i = 0; i < batch_size; i++) {\n      std::vector<float> labels, preds, sample_rates;\n      std::vector<std::shared_ptr<ExtraField>> extra_fields;\n      labels.push_back(labels_vec(i));\n      preds.push_back(preds_vec(i));\n      sample_rates.push_back(sample_rates_vec(i));\n      std::string msg = deep_insight_client->SendV2(\n          model_name_, targets, uids_vec(i), req_times_vec(i), train_time,\n          labels, preds, sample_rates, sample_ratio_, extra_fields,\n          return_msgs_);\n\n      msgs_vec(i) = msg;\n    }\n  };\n\n private:\n  std::string model_name_;\n  std::string target_;\n  float sample_ratio_;\n  bool return_msgs_;\n  bool use_zero_train_time_;\n};\n\nclass MonolithWriteDeepInsightV2 : public OpKernel {\n public:\n  explicit MonolithWriteDeepInsightV2(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"model_name\", &model_name_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"targets\", &targets_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"sample_ratio\", &sample_ratio_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"return_msgs\", &return_msgs_));\n    OP_REQUIRES_OK(ctx,\n                   ctx->GetAttr(\"use_zero_train_time\", &use_zero_train_time_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"extra_fields_keys\", &extra_fields_keys_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"Tfields\", &extra_fields_dtypes_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    DeepInsightClientTfBridge* deep_insight_client = nullptr;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0),\n                                       &deep_insight_client));\n    core::ScopedUnref unref(deep_insight_client);\n\n    auto req_times_vec = ctx->input(1).vec<int64_t>();      // batch\n    auto labels_mat = ctx->input(2).matrix<float>();        // num_heads x batch\n    auto preds_mat = ctx->input(3).matrix<float>();         // num_heads x batch\n    auto sample_rates_mat = ctx->input(4).matrix<float>();  // num_heads x batch\n\n    OpInputList extra_fields_values;\n    OP_REQUIRES_OK(\n        ctx, ctx->input_list(\"extra_fields_values\", &extra_fields_values));\n\n    int64_t train_time =\n        use_zero_train_time_ ? 0 : deep_insight_client->GenerateTrainingTime();\n\n    int64_t batch_size = labels_mat.dimension(1);\n\n    std::vector<std::vector<std::shared_ptr<ExtraField>>>\n        batched_extra_fields;  // batch x num_keys\n    std::vector<int64_t> uids_vec;\n    for (uint32_t b = 0; b < batch_size; b++) {\n      batched_extra_fields.emplace_back();\n      auto& extra_fields = batched_extra_fields.back();\n      for (size_t i = 0; i < extra_fields_dtypes_.size(); i++) {\n        if (extra_fields_dtypes_.at(i) == tensorflow::DT_FLOAT) {\n          extra_fields.push_back(std::make_shared<FloatExtraField>(\n              extra_fields_keys_.at(i),\n              extra_fields_values[i].vec<float>()(b)));\n        } else if (extra_fields_dtypes_.at(i) == tensorflow::DT_INT64) {\n          if (extra_fields_keys_.at(i) == \"uid\") {\n            uids_vec.push_back(extra_fields_values[i].vec<int64_t>()(b));\n          } else {\n            extra_fields.push_back(std::make_shared<Int64ExtraField>(\n                extra_fields_keys_.at(i),\n                extra_fields_values[i].vec<int64_t>()(b)));\n          }\n        } else if (extra_fields_dtypes_.at(i) == tensorflow::DT_STRING) {\n          extra_fields.push_back(std::make_shared<StringExtraField>(\n              extra_fields_keys_.at(i),\n              extra_fields_values[i].vec<tstring>()(b)));\n        }\n      }\n    }\n\n    Tensor* msgs;\n    ctx->allocate_output(0, {batch_size}, &msgs);\n    auto msgs_vec = msgs->vec<tstring>();\n\n    for (uint32_t i = 0; i < batch_size; i++) {\n      std::vector<float> labels, preds, sample_rates;\n      for (int j = 0; j < targets_.size(); j++) {\n        labels.push_back(labels_mat(j, i));\n        preds.push_back(preds_mat(j, i));\n        sample_rates.push_back(sample_rates_mat(j, i));\n      }\n      std::string msg = deep_insight_client->SendV2(\n          model_name_, targets_, uids_vec.at(i), req_times_vec(i), train_time,\n          labels, preds, sample_rates, sample_ratio_,\n          batched_extra_fields.at(i), return_msgs_);\n\n      msgs_vec(i) = msg;\n    }\n  };\n\n private:\n  std::string model_name_;\n  std::vector<std::string> targets_;\n  float sample_ratio_;\n  bool return_msgs_;\n  bool use_zero_train_time_;\n  std::vector<tstring> extra_fields_keys_;\n  std::vector<DataType> extra_fields_dtypes_;\n};\n\nREGISTER_OP(\"MonolithCreateDeepInsightClient\")\n    .Output(\"handle: resource\")\n    .Attr(\"enable_metrics_counter: bool = false\")\n    .Attr(\"is_fake: bool = false\")\n    .Attr(\"filename: string = ''\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithCreateDeepInsightClient\").Device(DEVICE_CPU),\n    MonolithCreateDeepInsightClientOp);\n\nREGISTER_OP(\"MonolithWriteDeepInsight\")\n    .Input(\"deep_insight_client_handle: resource\")\n    .Input(\"uids: int64\")\n    .Input(\"req_times: int64\")\n    .Input(\"labels: float\")\n    .Input(\"preds: float\")\n    .Input(\"sample_rates: float\")\n    .Output(\"msgs: string\")\n    .Attr(\"model_name: string\")\n    .Attr(\"target: string = 'ctr_head'\")\n    .Attr(\"sample_ratio: float = 0.01\")\n    .Attr(\"return_msgs: bool = false\")\n    .Attr(\"use_zero_train_time: bool = false\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      ctx->set_output(0, ctx->input(1));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithWriteDeepInsight\").Device(DEVICE_CPU),\n                        MonolithWriteDeepInsightOp);\n\nREGISTER_OP(\"MonolithWriteDeepInsightV2\")\n    .Input(\"deep_insight_client_handle: resource\")\n    .Input(\"req_times: int64\")\n    .Input(\"labels: float\")\n    .Input(\"preds: float\")\n    .Input(\"sample_rates: float\")\n    .Input(\"extra_fields_values: Tfields\")\n    .Output(\"msgs: string\")\n    .Attr(\"model_name: string\")\n    .Attr(\"extra_fields_keys: list(string)\")\n    .Attr(\"Tfields: list(type)\")\n    .Attr(\"targets: list(string)\")\n    .Attr(\"sample_ratio: float = 0.01\")\n    .Attr(\"return_msgs: bool = false\")\n    .Attr(\"use_zero_train_time: bool = false\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      ctx->set_output(0, ctx->input(1));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithWriteDeepInsightV2\").Device(DEVICE_CPU),\n                        MonolithWriteDeepInsightV2);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n\n#include <algorithm>\n#include <chrono>\n#include <cstring>\n#include <exception>\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/memory/memory.h\"\n#include \"absl/types/span.h\"\n#include \"monolith/native_training/runtime/common/metrics.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_factory.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/avx_utils.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nnamespace hash_table = ::monolith::hash_table;\nusing ::monolith::hash_table::EmbeddingHashTableConfig;\n\nconstexpr int64_t kSecPerHour = 60 * 60;\n\nStatus ValidateDim(const Tensor& t, int64 expected_dim) {\n  if (TF_PREDICT_FALSE(t.NumElements() != expected_dim)) {\n    return errors::InvalidArgument(\"The dim doesn't match expectation. \",\n                                   t.NumElements(), \" vs \", expected_dim);\n  }\n  return Status::OK();\n}\n\n}  // namespace\n\nStatus EmbeddingHashTableTfBridge::New(\n    monolith::hash_table::EmbeddingHashTableConfig config,\n    HashFilterTfBridge* hash_filter, EmbeddingHashTableTfBridge** new_bridge,\n    const std::string& name, hash_table::GpuExtraArgs args) {\n  auto bridge = core::RefCountPtr<EmbeddingHashTableTfBridge>(\n      new EmbeddingHashTableTfBridge(hash_filter));\n  bridge->config_ = config;\n  bridge->name_ = name;\n  bridge->dim_size_ = 0;\n  for (const auto& segment : config.entry_config().segments()) {\n    bridge->dim_size_ += segment.dim_size();\n  }\n  try {\n    bridge->table_ =\n        hash_table::NewEmbeddingHashTableFromConfig(config, std::move(args));\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n\n  bridge->max_update_ts_sec_ = std::make_unique<std::atomic<int64_t>>(0);\n  bridge->last_evict_ts_sec_ = std::make_unique<std::atomic<int64_t>>(0);\n\n  auto& bridge_ref = *bridge;\n  bridge_ref.evict_finished_ = true;\n  if (config.enable_feature_eviction()) {\n    bridge_ref.evict_finished_ = false;\n    const int evict_features_every_n_hours =\n        config.feature_evict_every_n_hours();\n    auto evict_func = [&bridge_ref, evict_features_every_n_hours]() {\n      while (!bridge_ref.evict_finished_) {\n        const int64_t last_evict_ts_sec = bridge_ref.last_evict_ts_sec();\n        const int64_t current_ts_sec =\n            std::chrono::duration_cast<std::chrono::seconds>(\n                std::chrono::system_clock::now().time_since_epoch())\n                .count();\n        if (last_evict_ts_sec == 0) {\n          bridge_ref.set_last_evict_ts_sec(current_ts_sec);\n        }\n        if (last_evict_ts_sec != 0 &&\n            current_ts_sec - last_evict_ts_sec >=\n                evict_features_every_n_hours * kSecPerHour) {\n          LOG_EVERY_N_SEC(INFO, 60)\n              << \"embedding_hash_table_tf_bridge: started feature eviction, \"\n                 \"current_ts_sec : \"\n              << current_ts_sec << \" last_evict_ts_sec : \" << last_evict_ts_sec\n              << \" max_update_ts_sec: \" << bridge_ref.max_update_ts_sec();\n          bridge_ref.table_->Evict(bridge_ref.max_update_ts_sec());\n          bridge_ref.set_last_evict_ts_sec(current_ts_sec);\n          LOG_EVERY_N_SEC(INFO, 60)\n              << \"embedding_hash_table_tf_bridge: finished feature eviction\";\n        }\n        std::this_thread::sleep_for(std::chrono::seconds(10));\n      }\n    };\n    bridge_ref.evict_thread_ = std::make_unique<std::thread>(evict_func);\n  }\n\n  *new_bridge = bridge.release();\n  return Status::OK();\n}\n\nStatus EmbeddingHashTableTfBridge::BatchLookup(OpKernelContext* ctx,\n                                               const int num_ids, int64_t* ids,\n                                               float* out_embedding,\n                                               int64_t* hit_fid_count) const {\n  try {\n    std::vector<absl::Span<float>> out_embeddings;\n    out_embeddings.reserve(num_ids);\n    for (int i = 0; i < num_ids; ++i) {\n      out_embeddings.push_back(\n          absl::MakeSpan(out_embedding + i * dim_size(), dim_size()));\n    }\n\n    *hit_fid_count = table_->BatchLookup(absl::MakeSpan(ids, num_ids),\n                                         absl::MakeSpan(out_embeddings));\n\n    if (IsServingEntryType() && num_ids) {\n      const std::string tagkv = absl::StrFormat(\"name=%s\", name_);\n      float hit_rate = *hit_fid_count / static_cast<float>(num_ids);\n      monolith::GetMetrics()->emit_timer(\"lookup_fid_hit_rate\", hit_rate,\n                                         tagkv);\n    }\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::BatchLookupEntry(\n    OpKernelContext* ctx, const int num_ids, int64_t* ids,\n    EntryDump* out_entries) const {\n  try {\n    table_->BatchLookupEntry(absl::MakeSpan(ids, num_ids),\n                             absl::MakeSpan(out_entries, num_ids));\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::Lookup(OpKernelContext* ctx, int64 id,\n                                          float* out_embedding) const {\n  try {\n    table_->Lookup(id, absl::MakeSpan(out_embedding, dim_size()));\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::LookupEntry(OpKernelContext* ctx, int64 id,\n                                               EntryDump* out_entry) const {\n  try {\n    table_->LookupEntry(id, absl::MakeSpan(out_entry, 1));\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::Assign(OpKernelContext* ctx, int num_ids,\n                                          const int64_t* ids,\n                                          const float* embeddings,\n                                          int64_t update_time) const {\n  try {\n    int64_t max_value = std::max(update_time, max_update_ts_sec_->load());\n    max_update_ts_sec_->store(max_value);\n    std::vector<int64_t> ids_after_filter;\n    std::vector<absl::Span<const float>> embeddings_after_filter;\n    ids_after_filter.reserve(num_ids);\n    embeddings_after_filter.reserve(num_ids);\n    for (int i = 0; i < num_ids; ++i) {\n      int64_t id = ids[i];\n      if (!table_->Contains(id) &&\n          hash_filter_->ShouldBeFiltered(id, table_.get())) {\n        continue;\n      }\n      ids_after_filter.push_back(id);\n      embeddings_after_filter.emplace_back(\n          absl::MakeSpan(embeddings + i * dim_size(), dim_size()));\n    }\n\n    table_->Assign(absl::MakeSpan(ids_after_filter),\n                   absl::MakeSpan(embeddings_after_filter), update_time);\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::AssignAdd(OpKernelContext* ctx, int64 id,\n                                             const Tensor& tensor,\n                                             int64_t update_time) const {\n  // Here max_update_ts_sec_ only need a fuzzy maximum value.\n  // We don't need use strict locking or compare_and_change\n  // to change this max_update_ts_sec_. And we can save some performance here.\n  int64_t max_value = std::max(update_time, max_update_ts_sec_->load());\n  max_update_ts_sec_->store(max_value);\n\n  if (!table_->Contains(id) &&\n      hash_filter_->ShouldBeFiltered(id, table_.get())) {\n    return Status::OK();\n  }\n\n  try {\n    TF_RETURN_IF_ERROR(ValidateDim(tensor, dim_size_));\n    auto span =\n        absl::MakeConstSpan(static_cast<float*>(tensor.data()), dim_size());\n    table_->AssignAdd(id, span, update_time);\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::AssignAdd2(int64 id,\n                                              absl::Span<const float> value,\n                                              int64_t update_time) {\n  int64_t max_value = std::max(update_time, max_update_ts_sec_->load());\n  max_update_ts_sec_->store(max_value);\n\n  if (hash_filter_->ShouldBeFiltered(id, table_.get())) {\n    return Status::OK();\n  }\n\n  try {\n    table_->AssignAdd(id, value, update_time);\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::Reinitialize(const int64_t* ids,\n                                                int64_t num_ids, int* status) {\n  auto id_vec = absl::MakeConstSpan(ids, num_ids);\n  try {\n    table_->Reinitialize(id_vec, absl::MakeSpan(status, num_ids));\n    if (hash_set_ != nullptr) {\n      for (int64_t id : id_vec) {\n        hash_set_->insert(std::make_pair(id, this));\n      }\n    }\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::BatchOptimize(\n    OpKernelContext* ctx, size_t num_ids, const int64_t* ids,\n    const float* tensor, absl::Span<const float> learning_rates,\n    int64_t update_time, bool enable_dedup, const int64_t global_step) const {\n  int max_value = std::max(update_time, max_update_ts_sec_->load());\n  max_update_ts_sec_->store(max_value);\n\n  try {\n    std::vector<int64_t> ids_after_filter;\n    std::vector<absl::Span<const float>> grads_after_filter;\n    // TODO(zouxuan): add theadlocal cache instead of allocating everytime.\n    std::unique_ptr<float[]> cache_grads;\n    if (enable_dedup) {\n      // To avoid repeated alloc, we do a conservative block allocation.\n      cache_grads = std::make_unique<float[]>(num_ids * dim_size());\n\n      // The first step we do a dedup, where all grads and occurences are\n      // grouped by IDs.\n      absl::flat_hash_map<int64_t, float*> ids_to_grads;\n      absl::flat_hash_map<int64_t, uint32_t> ids_to_counts;\n      ids_to_grads.reserve(num_ids);\n      ids_to_counts.reserve(num_ids);\n      for (int i = 0; i < num_ids; ++i) {\n        int64_t id = ids[i];\n        if (!ids_to_grads.count(id)) {\n          ids_to_counts[id] = 1;\n          const float* grad_src = tensor + i * dim_size();\n          float* grad_dest =\n              cache_grads.get() + ids_to_grads.size() * dim_size();\n          std::memcpy(grad_dest, grad_src, dim_size() * sizeof(float));\n          ids_to_grads[id] = grad_dest;\n        } else {\n          const float* grad_src = tensor + i * dim_size();\n          float* grad_dest = ids_to_grads[id];\n          hash_table::ReduceSum(grad_dest, grad_src, grad_dest, dim_size());\n          ++(ids_to_counts[id]);\n        }\n      }\n      // The second step is to perform a filtering, and creates the vect of IDs\n      // and grads for update.\n      ids_after_filter.reserve(num_ids);\n      grads_after_filter.reserve(num_ids);\n      for (const auto& entry : ids_to_counts) {\n        int64_t id = entry.first;\n        uint32_t filter_count = entry.second;\n        if (!table_->Contains(id) &&\n            hash_filter_->ShouldBeFiltered(id, filter_count, table_.get())) {\n          continue;\n        }\n        ids_after_filter.emplace_back(id);\n        grads_after_filter.emplace_back(\n            absl::MakeSpan(ids_to_grads[id], dim_size()));\n      }\n    } else {\n      // We do simple increments (by 1) on the hash filters.\n      ids_after_filter.reserve(num_ids);\n      grads_after_filter.reserve(num_ids);\n      for (int i = 0; i < num_ids; ++i) {\n        int64_t id = ids[i];\n        if (!table_->Contains(id) &&\n            hash_filter_->ShouldBeFiltered(id, table_.get())) {\n          continue;\n        }\n\n        ids_after_filter.emplace_back(id);\n        grads_after_filter.emplace_back(\n            absl::MakeSpan(tensor + i * dim_size(), dim_size()));\n      }\n    }\n\n    // The final step is to perform an update based on the optimizer it uses.\n    table_->BatchOptimize(absl::MakeSpan(ids_after_filter),\n                          absl::MakeSpan(grads_after_filter), learning_rates,\n                          update_time, global_step);\n    if (hash_set_ != nullptr) {\n      for (int64_t id : ids_after_filter) {\n        hash_set_->insert(std::make_pair(id, this));\n      }\n    }\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::Optimize(\n    OpKernelContext* ctx, int64 id, absl::Span<const float> grads,\n    absl::Span<const float> learning_rates, int64_t update_time,\n    int64_t global_step) const {\n  int max_value = std::max(update_time, max_update_ts_sec_->load());\n  max_update_ts_sec_->store(max_value);\n\n  try {\n    table_->Optimize(id, grads, learning_rates, update_time, global_step);\n    if (hash_set_ != nullptr) {\n      hash_set_->insert(std::make_pair(id, this));\n    }\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::LockAll(std::unique_ptr<LockCtx>* ctx) {\n  try {\n    *ctx = table_->LockAll();\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::ResourceExhausted(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::Save(OpKernelContext* ctx, DumpShard shard,\n                                        WriteFn write_fn,\n                                        DumpIterator* iter) const {\n  try {\n    table_->Save(shard, std::move(write_fn), iter);\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::ResourceExhausted(e.what());\n  }\n}\n\nStatus EmbeddingHashTableTfBridge::Restore(\n    OpKernelContext* ctx, DumpShard shard,\n    std::function<bool(EntryDump*, int64_t*)> get_fn) const {\n  try {\n    int64_t update_time = table_->Restore(shard, std::move(get_fn));\n\n    // Here we make sure max value is updated correctly when there are\n    // multiple threads to update this value simultaneously.\n    // There is no overhead since this operation is called once for each shard.\n    while (true) {\n      int64_t old_value = max_update_ts_sec_->load();\n      int64_t new_value = std::max(old_value, update_time);\n      bool ret =\n          max_update_ts_sec_->compare_exchange_weak(old_value, new_value);\n      if (ret == true) {\n        break;\n      }\n    }\n\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::ResourceExhausted(e.what());\n  }\n}\n\nstd::string EmbeddingHashTableTfBridge::DebugString() const {\n  return config_.DebugString();\n}\n\nstd::string EmbeddingHashTableTfBridge::Summary() const {\n  return table_->DebugString();\n}\n\nint32 EmbeddingHashTableTfBridge::dim_size() const { return dim_size_; }\nint32 EmbeddingHashTableTfBridge::slice_size() const {\n  return table_->SliceSize();\n}\nint64 EmbeddingHashTableTfBridge::max_update_ts_sec() const {\n  return max_update_ts_sec_->load();\n}\n\nint64 EmbeddingHashTableTfBridge::last_evict_ts_sec() const {\n  return last_evict_ts_sec_->load();\n}\n\nvoid EmbeddingHashTableTfBridge::set_last_evict_ts_sec(\n    const int64_t last_evict_ts_sec) {\n  *last_evict_ts_sec_ = last_evict_ts_sec;\n}\n\nbool EmbeddingHashTableTfBridge::IsServingEntryType() const {\n  return config_.entry_config().entry_type() ==\n         hash_table::EntryConfig_EntryType_SERVING;\n}\n\nstd::vector<std::pair<int64_t, const void*>>\nEmbeddingHashTableTfBridge::TouchedKeySet() const {\n  if (hash_set_) {\n    return hash_set_->GetAndClear();\n  }\n  return {};\n}\n\nvoid EmbeddingHashTableTfBridge::SetHopscotchHashSet(\n    HopscotchHashSet<std::pair<int64_t, const void*>>* hash_set) {\n  CHECK(hash_set_ == nullptr);\n  hash_set_ = hash_set;\n}\n\nconst EmbeddingHashTableConfig& EmbeddingHashTableTfBridge::GetConfig() const {\n  return config_;\n}\n\nEmbeddingHashTableTfBridge::~EmbeddingHashTableTfBridge() {\n  // Let the eviction thread stop\n  evict_finished_ = true;\n  if (evict_thread_) {\n    evict_thread_->join();\n  }\n  hash_set_ = nullptr;\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_EMBEDDING_HASH_TABLE_TF_BRIDGE_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_EMBEDDING_HASH_TABLE_TF_BRIDGE_H_\n#include <atomic>\n#include <memory>\n#include <mutex>\n#include <thread>\n\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_interface.h\"\n#include \"monolith/native_training/runtime/hopscotch/hopscotch_hash_set.h\"\n#include \"monolith/native_training/runtime/ops/hash_filter_tf_bridge.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/platform/status.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntemplate <class T>\nusing HopscotchHashSet = monolith::hopscotch::HopscotchHashSet<T>;\n\n// A hash table which can be used in TF runtime.\n// It captures all potential exceptions and convert them into error.\nclass EmbeddingHashTableTfBridge : public ResourceBase {\n public:\n  using EntryDump = monolith::hash_table::EntryDump;\n\n  static Status New(monolith::hash_table::EmbeddingHashTableConfig config,\n                    HashFilterTfBridge* hash_filter,\n                    EmbeddingHashTableTfBridge** new_bridge,\n                    const std::string& name,\n                    monolith::hash_table::GpuExtraArgs args = {});\n\n  ~EmbeddingHashTableTfBridge();\n\n  // BatchLookup |ids| and write it into |embeddings|\n  Status BatchLookup(OpKernelContext* ctx, const int num_ids, int64_t* ids,\n                     float* out_embedding, int64_t* hit_fid_count) const;\n  Status BatchLookupEntry(OpKernelContext* ctx, const int num_ids, int64_t* ids,\n                          EntryDump* out_entries) const;\n  // Lookup |id| and write it into |embedding|\n  Status Lookup(OpKernelContext* ctx, int64 id, float* out_embedding) const;\n  Status LookupEntry(OpKernelContext* ctx, int64 id,\n                     EntryDump* out_entry) const;\n  // TODO(leqi.zou): Unify the API here.\n  // 1. Remove all batch APIs.\n  // 2. Replace int64_t to int64\n  Status Assign(OpKernelContext* ctx, int num_ids, const int64_t* ids,\n                const float* embeddings, int64_t update_time) const;\n  // TODO(leqi.zou): Replace this API by AssignAdd2.\n  Status AssignAdd(OpKernelContext* ctx, int64 id, const Tensor& tensor,\n                   int64_t update_time) const;\n  Status AssignAdd2(int64 id, absl::Span<const float> value,\n                    int64_t update_time);\n  Status Reinitialize(const int64_t* ids, int64_t num_ids, int* status);\n  Status BatchOptimize(OpKernelContext* ctx, size_t num_ids, const int64_t* ids,\n                       const float* tensor,\n                       absl::Span<const float> learning_rates,\n                       int64_t update_time, bool enable_dedup,\n                       const int64_t global_step) const;\n  Status Optimize(OpKernelContext* ctx, int64 id, absl::Span<const float> grads,\n                  absl::Span<const float> learning_rates, int64_t update_time,\n                  int64_t global_step) const;\n\n  using DumpShard =\n      monolith::hash_table::EmbeddingHashTableInterface::DumpShard;\n  using DumpIterator =\n      monolith::hash_table::EmbeddingHashTableInterface::DumpIterator;\n  using WriteFn = monolith::hash_table::EmbeddingHashTableInterface::WriteFn;\n  using LockCtx = monolith::hash_table::EmbeddingHashTableInterface::LockCtx;\n\n  // For the functor injected, it is ok to throw exceptions.\n  Status LockAll(std::unique_ptr<LockCtx>* ctx);\n  Status Save(OpKernelContext* ctx, DumpShard shard, WriteFn write_fn,\n              DumpIterator* iter) const;\n  Status Restore(OpKernelContext* ctx, DumpShard shard,\n                 std::function<bool(EntryDump*, int64_t*)> get_fn) const;\n  void Clear() const { table_->Clear(); }\n  int64_t Size() const { return table_->Size(); }\n\n  int32 dim_size() const;\n  int32 slice_size() const;\n  int64 max_update_ts_sec() const;\n  int64 last_evict_ts_sec() const;\n  void set_last_evict_ts_sec(const int64_t last_evict_ts_sec);\n  bool IsServingEntryType() const;\n  std::string DebugString() const override;\n  std::string Summary() const;\n\n  void SetHopscotchHashSet(\n      HopscotchHashSet<std::pair<int64_t, const void*>>* hash_set);\n\n  HopscotchHashSet<std::pair<int64_t, const void*>>* GetHashSet() const {\n    return hash_set_;\n  }\n\n  std::vector<std::pair<int64_t, const void*>> TouchedKeySet() const;\n\n  const monolith::hash_table::EmbeddingHashTableConfig& GetConfig() const;\n  monolith::hash_table::EmbeddingHashTableInterface* GetTable() const {\n    return table_.get();\n  }\n\n private:\n  explicit EmbeddingHashTableTfBridge(HashFilterTfBridge* hash_filter)\n      : hash_filter_(hash_filter) {}\n\n  std::string name_;\n  std::unique_ptr<monolith::hash_table::EmbeddingHashTableInterface> table_;\n  monolith::hash_table::EmbeddingHashTableConfig config_;\n  int64 dim_size_ = 0;\n  std::unique_ptr<std::atomic<int64_t>> max_update_ts_sec_;\n  HashFilterTfBridge* hash_filter_;\n\n  std::unique_ptr<std::thread> evict_thread_;\n  std::unique_ptr<std::atomic<int64_t>> last_evict_ts_sec_;\n  mutex evict_mu_;\n  bool evict_finished_ TF_GUARDED_BY(evict_mu_);\n\n  HopscotchHashSet<std::pair<int64_t, const void*>>* hash_set_ = nullptr;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_EMBEDDING_HASH_TABLE_TF_BRIDGE_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/file_metric_writer.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/file_metric_writer.h\"\n\n#include \"absl/strings/str_format.h\"\n#include \"tensorflow/core/lib/io/record_writer.h\"\n#include \"tensorflow/core/platform/path.h\"\n\nnamespace monolith {\nnamespace deep_insight {\n\nusing tensorflow::Env;\nusing tensorflow::Status;\nusing tensorflow::io::RecordWriter;\nusing tensorflow::io::RecordWriterOptions;\n\nFileMetricWriter::FileMetricWriter(std::string filename)\n    : filename_(std::move(filename)),\n      finished_(false),\n      total_produce_(0),\n      total_consume_(0),\n      total_dump_(0) {\n  LOG(INFO) << \"deepinsight dump filename: \" << filename_;\n\n  if (!filename_.empty()) {\n    Env* env = Env::Default();\n    std::string dirname(tensorflow::io::Dirname(filename_));\n    TF_CHECK_OK(env->RecursivelyCreateDir(dirname));\n    TF_CHECK_OK(env->NewWritableFile(filename_, &fp_));\n\n    queue_ = std::make_unique<monolith::concurrency::Queue<std::string>>(8192);\n    thread_pool_ = std::make_unique<monolith::concurrency::ThreadPool>(1);\n    thread_pool_->Schedule([this]() {\n      RecordWriterOptions options;\n      RecordWriter writer(fp_.get(), options);\n\n      while (!finished_ || !queue_->empty()) {\n        std::string msg;\n        bool ok = queue_->try_pop(msg, std::chrono::milliseconds(10));\n        if (ok) {\n          ++total_consume_;\n          LOG_EVERY_N_SEC(INFO, 300)\n              << absl::StrFormat(\"Consume %ld records\", total_consume_);\n\n          Status s = writer.WriteRecord(msg);\n          if (s.ok()) {\n            ++total_dump_;\n            LOG_EVERY_N_SEC(INFO, 300)\n                << absl::StrFormat(\"Dump %ld records\", total_dump_);\n          } else {\n            LOG(ERROR) << absl::StrFormat(\n                \"Failed to write record: %s, status=%s\", msg,\n                s.error_message());\n          }\n        } else {\n          LOG_EVERY_N_SEC(INFO, 300) << \"Failed to try pop, queue maybe empty!\";\n        }\n      }\n\n      LOG(INFO) << absl::StrFormat(\"Totally produce %ld records\",\n                                   total_produce_);\n      LOG(INFO) << absl::StrFormat(\"Totally consume %ld records\",\n                                   total_consume_);\n      LOG(INFO) << absl::StrFormat(\"Totally dump %ld records\", total_dump_);\n      TF_CHECK_OK(writer.Close());\n      TF_CHECK_OK(fp_->Close());\n    });\n  }\n}\n\nFileMetricWriter::~FileMetricWriter() { finished_ = true; }\n\nvoid FileMetricWriter::Write(const std::string& msg) {\n  if (fp_) {\n    while (true) {\n      bool ok = queue_->try_push(msg, std::chrono::milliseconds(10));\n      if (ok) {\n        ++total_produce_;\n        LOG_EVERY_N_SEC(INFO, 300)\n            << absl::StrFormat(\"Produce %ld records\", total_produce_);\n        break;\n      } else {\n        LOG_EVERY_N_SEC(INFO, 60) << \"Failed to try push, queue maybe full!\";\n      }\n    }\n  }\n}\n\n}  // namespace deep_insight\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/file_metric_writer.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_DEEP_INSIGHT_FILE_METRIC_WRITER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_DEEP_INSIGHT_FILE_METRIC_WRITER_H_\n\n#include <atomic>\n#include <memory>\n#include <string>\n\n#include \"tensorflow/core/platform/file_system.h\"\n\n#include \"monolith/native_training/runtime/concurrency/queue.h\"\n#include \"monolith/native_training/runtime/concurrency/thread_pool.h\"\n\nnamespace monolith {\nnamespace deep_insight {\n\nclass FileMetricWriter {\n public:\n  explicit FileMetricWriter(std::string filename);\n\n  ~FileMetricWriter();\n\n  void Write(const std::string& msg);\n\n private:\n  std::string filename_;\n  std::atomic_bool finished_;\n  int64_t total_produce_;\n  int64_t total_consume_;\n  int64_t total_dump_;\n  std::unique_ptr<tensorflow::WritableFile> fp_;\n  std::unique_ptr<monolith::concurrency::Queue<std::string>> queue_;\n  std::unique_ptr<monolith::concurrency::ThreadPool> thread_pool_;\n};\n\n}  // namespace deep_insight\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_DEEP_INSIGHT_FILE_METRIC_WRITER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/file_metric_writer_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/file_metric_writer.h\"\n\n#include <string>\n#include <vector>\n\n#include \"glog/logging.h\"\n#include \"gtest/gtest.h\"\n\n#include \"tensorflow/core/lib/io/record_writer.h\"\n\nnamespace monolith {\nnamespace deep_insight {\n\nusing tensorflow::Env;\nusing tensorflow::Status;\nusing tensorflow::io::RecordWriter;\nusing tensorflow::io::RecordWriterOptions;\n\nTEST(FileMetricWriterTest, Basic) {\n  Env* env = Env::Default();\n  std::string filename;\n  CHECK(env->LocalTempFilename(&filename));\n  FileMetricWriter writer(filename);\n  writer.Write(\"hello\");\n  writer.Write(\"world\");\n}\n\n}  // namespace deep_insight\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/file_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"absl/synchronization/mutex.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n#include \"tensorflow/core/lib/io/record_writer.h\"\n#include \"tensorflow/core/platform/path.h\"\n\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\n// It is a thin wrapper of GFile. Make it compatible with ResourceKernelOp\n// and thread safe.\nclass FileResource : public ResourceBase {\n public:\n  explicit FileResource(std::unique_ptr<WritableFile> f,\n                        absl::string_view debugging_info)\n      : f_(std::move(f)), debugging_info_(debugging_info), closed_(false) {}\n\n  std::string DebugString() const override { return debugging_info_; }\n\n  Status Close() {\n    absl::MutexLock l(&mu_);\n    closed_ = true;\n    return f_->Close();\n  }\n\n  Status Append(StringPiece data) {\n    absl::MutexLock l(&mu_);\n    return f_->Append(data);\n  }\n\n  Status AppendRecord(const string& serialized) {\n    absl::MutexLock l(&mu_);\n    if (!record_writer_) {\n      record_writer_.reset(new tensorflow::io::RecordWriter(\n          f_.get(),\n          tensorflow::io::RecordWriterOptions::CreateRecordWriterOptions(\"\")));\n    }\n    return record_writer_->WriteRecord(serialized);\n  }\n\n  ~FileResource() {\n    absl::MutexLock l(&mu_);\n    if (!closed_) {\n      auto s = f_->Close();\n      if (!s.ok()) {\n        LOG(ERROR) << \"Unable to close file \" << debugging_info_ << \" :\"\n                   << s.ToString();\n      }\n    }\n  }\n\n private:\n  absl::Mutex mu_;\n  std::unique_ptr<WritableFile> f_ ABSL_GUARDED_BY(mu_);\n  std::unique_ptr<tensorflow::io::RecordWriter> record_writer_\n      ABSL_GUARDED_BY(mu_);\n  const std::string debugging_info_;\n  bool closed_ ABSL_GUARDED_BY(mu_);\n};\n\n}  // namespace\n\nclass MonolithWritableFileOp : public ResourceOpKernel<FileResource> {\n public:\n  explicit MonolithWritableFileOp(OpKernelConstruction* c)\n      : ResourceOpKernel(c) {\n    OP_REQUIRES_OK(c, c->GetAttr(\"filename\", &filename_));\n    env_ = c->env();\n  }\n\n  ~MonolithWritableFileOp() override {}\n\n private:\n  Status CreateResource(FileResource** file_wrapper)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    const std::string dir = std::string(io::Dirname(filename_));\n    if (!env_->FileExists(dir).ok()) {\n      TF_RETURN_IF_ERROR(env_->RecursivelyCreateDir(dir));\n    }\n    std::unique_ptr<WritableFile> f;\n    TF_RETURN_IF_ERROR(env_->NewWritableFile(filename_, &f));\n    *file_wrapper = new FileResource(std::move(f), filename_);\n    return Status::OK();\n  }\n\n  std::string filename_;\n  Env* env_;\n};\n\nREGISTER_OP(\"MonolithWritableFile\")\n    .Output(\"handle: resource\")\n    .Attr(\"filename: string\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithWritableFile\").Device(DEVICE_CPU),\n                        MonolithWritableFileOp);\n\nclass MonolithWritableFileCloseOp : public OpKernel {\n public:\n  explicit MonolithWritableFileCloseOp(OpKernelConstruction* c) : OpKernel(c) {}\n\n  void Compute(OpKernelContext* c) override {\n    FileResource* f;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 0), &f));\n    core::ScopedUnref unref(f);\n    OP_REQUIRES_OK(c, f->Close());\n  }\n};\n\nREGISTER_OP(\"MonolithWritableFileClose\")\n    .Input(\"handle: resource\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::UnknownShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithWritableFileClose\").Device(DEVICE_CPU),\n                        MonolithWritableFileCloseOp);\n\nclass MonolithWritableFileAppendOp : public OpKernel {\n public:\n  explicit MonolithWritableFileAppendOp(OpKernelConstruction* c)\n      : OpKernel(c) {}\n\n  void Compute(OpKernelContext* c) override {\n    FileResource* f;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 0), &f));\n    core::ScopedUnref unref(f);\n    const auto& content = c->input(1).scalar<tstring>()();\n    OP_REQUIRES_OK(c, f->Append(content));\n  }\n};\n\nREGISTER_OP(\"MonolithWritableFileAppend\")\n    .Input(\"handle: resource\")\n    .Input(\"content: string\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::UnknownShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithWritableFileAppend\").Device(DEVICE_CPU),\n                        MonolithWritableFileAppendOp);\n\nclass MonolithEntryDumpFileAppendOp : public OpKernel {\n public:\n  explicit MonolithEntryDumpFileAppendOp(OpKernelConstruction* c)\n      : OpKernel(c) {}\n\n  void Compute(OpKernelContext* c) override {\n    FileResource* f;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 0), &f));\n    core::ScopedUnref unref(f);\n    const auto& item_id = c->input(1).flat<int64>();\n    const auto& bias = c->input(2).flat<float>();\n    const auto& embedding = c->input(3).flat<float>();\n\n    size_t batch_size = item_id.size();\n    CHECK_GT(batch_size, 0);\n    CHECK_EQ(embedding.size() % batch_size, 0);\n    size_t embedding_len = embedding.size() / batch_size;\n    for (size_t batch_id = 0; batch_id < batch_size; batch_id++) {\n      monolith::hash_table::EntryDump d;\n      d.set_id(item_id(batch_id));\n      d.add_num(bias(batch_id));\n      for (size_t i = 0; i < embedding_len; i++) {\n        d.add_num(embedding(batch_id * embedding_len + i));\n      }\n      OP_REQUIRES_OK(c, f->AppendRecord(d.SerializeAsString()));\n    }\n  }\n};\n\nREGISTER_OP(\"MonolithEntryDumpFileAppend\")\n    .Input(\"handle: resource\")\n    .Input(\"item_id: int64\")\n    .Input(\"bias: float\")\n    .Input(\"embedding: float\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::UnknownShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEntryDumpFileAppend\").Device(DEVICE_CPU),\n                        MonolithEntryDumpFileAppendOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/file_utils.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/file_utils.h\"\n\n#include \"absl/strings/str_format.h\"\n#include \"re2/re2.h\"\n#include \"tensorflow/core/platform/errors.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nnamespace {\n\nconst char* const kShardedFileFormat = \"%s-%05d-of-%05d\";\n}\n\nstd::string GetShardedFileName(absl::string_view basename, int shard,\n                               int nshards) {\n  return absl::StrFormat(kShardedFileFormat, basename, shard, nshards);\n}\n\nStatus ValidateShardedFiles(absl::string_view basename,\n                            absl::Span<const std::string> filenames,\n                            FileSpec* spec) {\n  std::vector<bool> show;\n  for (absl::string_view filename : filenames) {\n    if (filename.substr(0, basename.size()) != basename) {\n      return errors::InvalidArgument(\"Filename \", filename,\n                                     \" doesn't belong to \", basename);\n    }\n    absl::string_view suffix = filename.substr(basename.size());\n    int shard, nshards;\n    // Ignore invalid files.\n    if (!RE2::FullMatch(suffix, R\"raw(-(\\d{5})?-of-(\\d{5})?)raw\", &shard,\n                        &nshards)) {\n      continue;\n    }\n    if (show.empty()) {\n      show.resize(nshards);\n    }\n    if (nshards != (int)show.size()) {\n      return errors::InvalidArgument(\"Filename \", filename,\n                                     \" doesn't match nshards. \", show.size());\n    }\n    if (shard >= nshards) {\n      return errors::InvalidArgument(\"Shard \", shard, \"exceeds \", nshards,\n                                     \" for \", filename);\n    }\n    show[shard] = true;\n  }\n  if (show.empty()) {\n    return errors::InvalidArgument(\"There is no valid sharded files for \",\n                                   basename);\n  }\n  for (int i = 0; i < (int)show.size(); ++i) {\n    if (!show[i]) {\n      return errors::InvalidArgument(\"Shard \", i, \" doesn't show up for \",\n                                     basename);\n    }\n  }\n\n  if (spec != nullptr) {\n    *spec = FileSpec::ShardedFileSpec(basename, show.size());\n  }\n  return Status::OK();\n}\n\nFileSpec FileSpec::ShardedFileSpec(absl::string_view prefix, int nshards) {\n  FileSpec spec;\n  spec.type_ = FileSpec::SHARDED_FILES;\n  spec.prefix_ = std::string(prefix);\n  spec.nshards_ = nshards;\n  return spec;\n}\n\nstd::vector<std::string> FileSpec::GetFilenames() const {\n  std::vector<std::string> filenames;\n  switch (type_) {\n    case FileSpec::SHARDED_FILES:\n      for (int i = 0; i < nshards_; ++i) {\n        filenames.push_back(GetShardedFileName(prefix_, i, nshards_));\n      }\n      break;\n    default:\n      break;\n  }\n  return filenames;\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/file_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_FILE_UTILS\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_FILE_UTILS\n\n#include \"absl/strings/string_view.h\"\n#include \"absl/types/span.h\"\n#include \"tensorflow/core/platform/status.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\n// Returns sharded file name.\nstd::string GetShardedFileName(absl::string_view basename, int shard,\n                               int nshards);\n\n// A spec reprsents a set of files.\nclass FileSpec final {\n public:\n  FileSpec() {}\n\n  static FileSpec ShardedFileSpec(absl::string_view prefix, int nshards);\n\n  std::vector<std::string> GetFilenames() const;\n\n  int nshards() const { return nshards_; }\n\n private:\n  enum Type { UNKNOWN, SHARDED_FILES };\n\n  Type type_ = UNKNOWN;\n  std::string prefix_;\n  int nshards_ = 0;\n};\n\n// Validates if filenames construct a valid file spec for base name.\nStatus ValidateShardedFiles(absl::string_view basename,\n                            absl::Span<const std::string> filenames,\n                            FileSpec* spec = nullptr);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_FILE_UTILS\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/file_utils_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/file_utils.h\"\n\n#include <string>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"tensorflow/core/lib/core/status_test_util.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nusing ::testing::ElementsAre;\n\nTEST(ValidateShardedFilesTest, Basic) {\n  FileSpec spec;\n  TF_EXPECT_OK(ValidateShardedFiles(\"a/b\", {\"a/b-00000-of-00001\"}));\n  TF_EXPECT_OK(ValidateShardedFiles(\n      \"a/b\", {\"a/b-00000-of-00002\", \"a/b-00001-of-00002\"}, &spec));\n  EXPECT_THAT(spec.nshards(), 2);\n  TF_EXPECT_OK(ValidateShardedFiles(\n      \"a\", {\"a-00000-of-00001\", \"a-00000-of-00001-tmp-1234\"}));\n  TF_EXPECT_OK(ValidateShardedFiles(\n      \"a\", {\"a-00000-of-00001\", \"a-00000-of-00002-tmp-1234\"}));\n  EXPECT_FALSE(ValidateShardedFiles(\"a/b\", {\"a/b-00000-of-00002\"}).ok());\n  EXPECT_FALSE(\n      ValidateShardedFiles(\"a/b\", {\"a/b-00000-of-00001\", \"a/b-00001-of-00001\"})\n          .ok());\n  EXPECT_FALSE(\n      ValidateShardedFiles(\"a/b\", {\"a/b-00000-of-00001\", \"a/b-00000-of-00002\",\n                                   \"a/b-00001-of-00002\"})\n          .ok());\n  EXPECT_FALSE(ValidateShardedFiles(\"a/b\", {\"random-string\"}).ok());\n  EXPECT_FALSE(ValidateShardedFiles(\"a/b\", {\"a/b-random-string\"}).ok());\n}\n\nTEST(ValidateShardedFilesTest, FileSpecTest) {\n  auto spec = FileSpec::ShardedFileSpec(\"a/b\", 2);\n  EXPECT_THAT(spec.GetFilenames(),\n              ElementsAre(\"a/b-00000-of-00002\", \"a/b-00001-of-00002\"));\n}\n\nTEST(ValidateShardedFilesTest, LargeFileSet) {\n  std::vector<std::string> filenames;\n  for (int i = 0; i < 100; ++i) {\n    filenames.push_back(GetShardedFileName(\"/a\", i, 100));\n  }\n  TF_EXPECT_OK(ValidateShardedFiles(\"/a\", filenames));\n}\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/fused_embedding_to_layout.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/fused_embedding_to_layout.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nnamespace fused_layout {\n\nvoid *MemCopy(float *dest, const float *src, std::size_t count) {\n  return std::memcpy(dest, src, count * sizeof(float));\n}\n\ntemplate <class TInit>\nvoid OptimizedSumpooling(const float *src, const int dim_num, void *init_ptr,\n                         float *dst, void *one_mutex_ptr = nullptr,\n                         int mean_pool_fid_num = 0) {\n  std::mutex *one_mutex = static_cast<std::mutex *>(one_mutex_ptr);\n  TInit *init = static_cast<TInit *>(init_ptr);\n  if (one_mutex) {\n    one_mutex->lock();\n  }\n  if (init && *init) {\n    if (mean_pool_fid_num) {\n      for (size_t i = 0; i < dim_num; ++i) {\n        dst[i] = (src[i] / mean_pool_fid_num);\n      }\n    } else {\n      MemCopy(dst, src, dim_num);\n    }\n    *init = false;\n  } else {\n    // ::monolith::hash_table::ReduceSum(src, dst, dst, dim_num);\n    if (mean_pool_fid_num) {\n      for (size_t i = 0; i < dim_num; ++i) {\n        dst[i] += (src[i] / mean_pool_fid_num);\n      }\n    } else {\n      for (size_t i = 0; i < dim_num; ++i) {\n        dst[i] += src[i];\n      }\n    }\n  }\n  if (one_mutex) {\n    one_mutex->unlock();\n  }\n}\n\nNoneLayout::NoneLayout(const std::string &name, const OutConfig &out_conf,\n                       OpInputList &tensor_list, int &start_idx)\n    : Layout(name, out_conf) {\n  int offset = 0;\n  CHECK(out_conf.slice_configs_size() == out_conf.shape_size());\n  for (const SliceConfig &slice_conf : out_conf.slice_configs()) {\n    slice_to_tensor_.insert(\n        {GetKey(slice_conf), {&tensor_list[start_idx++], offset++}});\n  }\n}\n\n// op output\nNoneLayout::NoneLayout(const std::string &name, const OutConfig &out_conf,\n                       OpOutputList &tensor_list, int &start_idx)\n    : Layout(name, out_conf) {\n  int offset = 0;\n  CHECK(out_conf.slice_configs_size() == out_conf.shape_size());\n  for (const SliceConfig &slice_conf : out_conf.slice_configs()) {\n    slice_to_tensor_.insert(\n        {GetKey(slice_conf), {tensor_list[start_idx++], offset++}});\n  }\n}\n\nPtrWrapper NoneLayout::GetSlice(int row_id, const SliceConfig &slice_conf) {\n  auto key = GetKey(slice_conf);\n  auto it = slice_to_tensor_.find(key);\n  if (it != slice_to_tensor_.end()) {\n    auto &layout_info = it->second;\n    const LayoutShape &shape = out_config_.shape(layout_info.second);\n    if (slice_conf.pooling_type() == PoolingType::FIRSTN) {\n      CHECK_EQ(shape.dims_size(), 3);\n      // none seq [batch_size, max_seq_len, num_dim]\n      const auto tensor = layout_info.first->tensor<float, 3>();\n      return PtrWrapper{&tensor(row_id, 0, 0), shape.dims(1) * shape.dims(2),\n                        layout_info.first->NumElements()};\n\n    } else {\n      CHECK_EQ(shape.dims_size(), 2);  // none [batch_size, num_dim]\n      const auto mat = layout_info.first->matrix<float>();\n      return PtrWrapper{&mat(row_id, 0), shape.dims(1),\n                        layout_info.first->NumElements()};\n    }\n  }\n}\n\nDefaultLayout::DefaultLayout(const std::string &name, const OutConfig &out_conf,\n                             OpInputList &tensor_list, int &start_idx)\n    : Layout(name, out_conf) {\n  int offset = 0;\n  CHECK_EQ(out_conf.shape_size(), 1);\n  CHECK_NE(out_conf.out_type(), OutType::NONE);\n\n  for (const SliceConfig &slice_conf : out_conf.slice_configs()) {\n    slice_to_tensor_.insert(\n        {GetKey(slice_conf), {&tensor_list[start_idx], offset}});\n    if (out_conf.out_type() == OutType::STACK) {\n      offset += 1;\n    } else if (out_conf.out_type() == OutType::CONCAT) {\n      offset += slice_conf.end() - slice_conf.start();\n    } else {\n      CHECK(out_conf.out_type() == OutType::ADDN);\n    }\n  }\n\n  start_idx++;\n}\n\nDefaultLayout::DefaultLayout(const std::string &name, const OutConfig &out_conf,\n                             OpOutputList &tensor_list, int &start_idx)\n    : Layout(name, out_conf) {\n  int offset = 0;\n  CHECK_EQ(out_conf.shape_size(), 1);\n  CHECK_NE(out_conf.out_type(), OutType::NONE);\n\n  for (const SliceConfig &slice_conf : out_conf.slice_configs()) {\n    slice_to_tensor_.insert(\n        {GetKey(slice_conf), {tensor_list[start_idx], offset}});\n    if (out_conf.out_type() == OutType::STACK) {\n      offset += 1;\n    } else if (out_conf.out_type() == OutType::CONCAT) {\n      offset += slice_conf.end() - slice_conf.start();\n    } else {\n      CHECK(out_conf.out_type() == OutType::ADDN);\n    }\n  }\n\n  start_idx++;\n}\n\nPtrWrapper DefaultLayout::GetSlice(int row_id, const SliceConfig &slice_conf) {\n  auto key = GetKey(slice_conf);\n  auto it = slice_to_tensor_.find(key);\n  if (it != slice_to_tensor_.end()) {\n    auto &layout_info = it->second;\n    CHECK_EQ(out_config_.shape_size(), 1);\n    const LayoutShape &shape = out_config_.shape(0);\n\n    // TODO(zhangru): support concat/stack seq\n    if (slice_conf.pooling_type() == PoolingType::FIRSTN) {\n      CHECK(shape.dims_size() > 2 && shape.dims_size() < 5);\n      if (shape.dims_size() == 3) {\n        // concat [batch_size, max_seq_len, num_dims];\n        // add_n [batch_size, , num_dim];\n        const auto tensor = layout_info.first->tensor<float, 3>();\n        return PtrWrapper{&tensor(row_id, 0, layout_info.second),\n                          shape.dims(1) * shape.dims(2),\n                          layout_info.first->NumElements()};\n      } else {  // if (shape.dims_size() == 4) {\n        // stack [batch_size, features_size, max_seq_len , num_dim];\n        const auto tensor = layout_info.first->tensor<float, 4>();\n        return PtrWrapper{&tensor(row_id, 0, 0, layout_info.second),\n                          shape.dims(1) * shape.dims(2) * shape.dims(3),\n                          layout_info.first->NumElements()};\n      }\n    } else {\n      CHECK(shape.dims_size() > 1 && shape.dims_size() < 4);\n      if (shape.dims_size() == 2) {\n        // concat [batch_size, num_dims];\n        // add_n [batch_size, num_dim];\n        const auto mat = layout_info.first->matrix<float>();\n        return PtrWrapper{&mat(row_id, layout_info.second), shape.dims(1),\n                          layout_info.first->NumElements()};\n      } else {  // if (shape.dims_size() == 3) {\n        // stack [batch_size, features_size , num_dim];\n        const auto tensor = layout_info.first->tensor<float, 3>();\n        return PtrWrapper{&tensor(row_id, layout_info.second, 0),\n                          shape.dims(1) * shape.dims(2),\n                          layout_info.first->NumElements()};\n      }\n    }\n  }\n}\n\nMonolithEmbeddingToLayoutBase::MonolithEmbeddingToLayoutBase(\n    OpKernelConstruction *ctx, int version)\n    : OpKernel(ctx), version_(version) {\n  std::string serialized;\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(\"feature_cfgs\", &serialized));\n  OP_REQUIRES(\n      ctx, feature_cfgs_.ParseFromArray(serialized.data(), serialized.size()),\n      errors::FailedPrecondition(\"Failed to parse the feature_cfgs_.\"));\n  OP_REQUIRES_OK(ctx, ctx->GetAttr(\"variant_type\", &variant_type_));\n  if (version_ >= 2) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"ps_num\", &ps_num_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"parallel_flag\", &parallel_flag_));\n  }\n\n  // set max_sequence_length/pooling_type/slice_idx/feature_idx here:\n  // use the index in the sorted feature_names_used as feature_idx.\n  const auto &feature_names_used = feature_cfgs_.feature_configs();\n  std::vector<std::string> feature_names;\n  std::map<std::string, std::map<std::string, int>> table_feature_dim_map;\n  for (const auto &feature_conf_pair : feature_names_used) {\n    feature_names.push_back(feature_conf_pair.first);\n    int dims_sum = 0;\n    for (size_t slice_idx = 0;\n         slice_idx < feature_conf_pair.second.slice_dims_size(); slice_idx++) {\n      dims_sum += feature_conf_pair.second.slice_dims(slice_idx);\n    }\n    table_feature_dim_map[feature_conf_pair.second.table()]\n                         [feature_conf_pair.first] = dims_sum;\n  }\n  std::sort(feature_names.begin(), feature_names.end());\n\n  {\n    table_feature_dim_.resize(table_feature_dim_map.size());\n    int i = 0;\n    // table_feature_dim_map is map, already sort\n    for (auto &iter : table_feature_dim_map) {\n      auto &table_name = iter.first;\n      auto &record_dims = table_feature_dim_[i];\n      std::vector<std::string> feature_name_tmp;\n      auto &feature_dim_map = iter.second;\n      for (auto &sub_iter : feature_dim_map) {\n        feature_name_tmp.push_back(sub_iter.first);\n      }\n      std::sort(feature_name_tmp.begin(), feature_name_tmp.end());\n      record_dims.resize(feature_name_tmp.size());\n      for (size_t j = 0; j < feature_name_tmp.size(); ++j) {\n        record_dims[j] = feature_dim_map[feature_name_tmp[j]];\n      }\n      ++i;\n    }\n  }\n  std::vector<std::unordered_map<int, int>>\n      slice_idx_per_feature;  // feature_index: {start: slice_idx}\n  slice_idx_per_feature.resize(feature_names_used.size());\n  for (size_t feature_idx = 0; feature_idx < feature_names.size();\n       feature_idx++) {\n    std::unordered_map<int, int> start2slice_idx;\n    const auto &feature_name = feature_names[feature_idx];\n    const auto &feat_conf = feature_names_used.at(feature_name);\n    int slice_prefix_sum_ = 0;\n    for (size_t slice_idx = 0; slice_idx < feat_conf.slice_dims_size();\n         slice_idx++) {\n      start2slice_idx[slice_prefix_sum_] = slice_idx;\n      slice_prefix_sum_ += feat_conf.slice_dims(slice_idx);\n    }\n    max_slice_num_ = std::max(max_slice_num_, feat_conf.slice_dims_size());\n    slice_idx_per_feature[feature_idx] = start2slice_idx;\n  }\n\n  auto *out_configs = feature_cfgs_.mutable_out_configs();\n  for (auto &pair : *out_configs) {\n    layout_names_.push_back(pair.first);\n    for (auto &slice_config : *pair.second.mutable_slice_configs()) {\n      const auto &feature_name = slice_config.feature_name();\n      const auto &feat_conf = feature_names_used.at(feature_name);\n      slice_config.set_max_sequence_length(feat_conf.max_sequence_length());\n      slice_config.set_pooling_type(feat_conf.pooling_type());\n      CHECK(!(pair.second.out_type() == OutType::ADDN &&\n              slice_config.pooling_type() == PoolingType::FIRSTN));\n      auto it =\n          std::find(feature_names.begin(), feature_names.end(), feature_name);\n      if (it != feature_names.end()) {\n        int feature_idx = it - feature_names.begin();\n        slice_config.set_feature_idx(feature_idx);\n        slice_config.set_slice_idx(\n            slice_idx_per_feature[feature_idx][slice_config.start()]);\n      }\n    }\n  }\n  std::sort(layout_names_.begin(), layout_names_.end());\n}\n\nMonolithEmbeddingToLayoutOp::MonolithEmbeddingToLayoutOp(\n    OpKernelConstruction *ctx, int version /* = 1*/)\n    : MonolithEmbeddingToLayoutBase(ctx, version) {}\n\nvoid MonolithEmbeddingToLayoutOp::Compute(OpKernelContext *ctx) {\n  // Grab the input tensor\n  OpInputList embeddings_list;\n  OP_REQUIRES_OK(ctx, ctx->input_list(\"embeddings_list\", &embeddings_list));\n\n  const Tensor *fids_offset_input;\n  OP_REQUIRES_OK(ctx, ctx->input(\"fid_offset\", &fids_offset_input));\n\n  const Tensor *feature_offset_input;\n  OP_REQUIRES_OK(ctx, ctx->input(\"feature_offset\", &feature_offset_input));\n\n  const Tensor *nfl_offset_input;\n  OP_REQUIRES_OK(ctx, ctx->input(\"nfl_offset\", &nfl_offset_input));\n\n  const Tensor *batch_size_tensor;\n  OP_REQUIRES_OK(ctx, ctx->input(\"batch_size\", &batch_size_tensor));\n\n  const Tensor *nfl_size_tensor;\n  const Tensor *feature_size_tensor;\n  const Tensor *fid_size_tensor;\n  const Tensor *emb_size_tensor;\n  if (GetVersion() == 5) {\n    OP_REQUIRES_OK(ctx, ctx->input(\"nfl_size\", &nfl_size_tensor));\n    OP_REQUIRES_OK(ctx, ctx->input(\"feature_size\", &feature_size_tensor));\n    OP_REQUIRES_OK(ctx, ctx->input(\"fid_size\", &fid_size_tensor));\n    OP_REQUIRES_OK(ctx, ctx->input(\"emb_size\", &emb_size_tensor));\n  }\n\n  const auto fids_offset_vec = fids_offset_input->flat<uint64>();\n  int total_fid_num = fids_offset_input->dim_size(0);\n  const auto feature_offset_vec = feature_offset_input->flat<int32>();\n  int total_feature_num = feature_offset_input->dim_size(0);\n  const auto nfl_offset_vec = nfl_offset_input->flat<uint32>();\n  int total_nfl_num = nfl_offset_input->dim_size(0);\n  int req_num = 1;\n  int32 max_batch_size = 0;\n  std::vector<int> each_req_batch_size_offset(1, 0);\n  std::vector<int> each_req_nfl_offset(1, 0);\n  std::vector<int> each_req_feature_offset(1, 0);\n  std::vector<int> each_req_fid_offset(1, 0);\n  if (GetVersion() == 5) {\n    const auto batch_size_vec = batch_size_tensor->flat<int32>();\n    req_num = batch_size_tensor->dim_size(0);\n\n    for (size_t i = 0; i < req_num; ++i) {\n      each_req_batch_size_offset.push_back(\n          each_req_batch_size_offset[i] + batch_size_vec(i));\n      max_batch_size = std::max(batch_size_vec(i), max_batch_size);\n    }\n\n    const auto nfl_size_vec = nfl_size_tensor->flat<int32>();\n    for (size_t i = 0; i < req_num; ++i) {\n      each_req_nfl_offset.push_back(each_req_nfl_offset[i] + nfl_size_vec(i));\n    }\n    CHECK_EQ(each_req_nfl_offset.back(), total_nfl_num);\n\n    const auto feature_size_vec = feature_size_tensor->flat<int32>();\n    for (size_t i = 0; i < req_num; ++i) {\n      each_req_feature_offset.push_back(\n          each_req_feature_offset[i] + feature_size_vec(i));\n    }\n    CHECK_EQ(each_req_feature_offset.back(), total_feature_num);\n\n    const auto fid_size_vec = fid_size_tensor->flat<int32>();\n    for (size_t i = 0; i < req_num; ++i) {\n      each_req_fid_offset.push_back(each_req_fid_offset[i] + fid_size_vec(i));\n    }\n    CHECK_EQ(each_req_fid_offset.back(), total_fid_num);\n  } else {\n    max_batch_size = batch_size_tensor->scalar<int32>()();\n    each_req_batch_size_offset.push_back(max_batch_size);\n    each_req_nfl_offset.push_back(total_nfl_num);\n    each_req_feature_offset.push_back(total_feature_num);\n    each_req_fid_offset.push_back(total_fid_num);\n  }\n  req_sum_ += req_num;\n  process_num_++;\n  LOG_EVERY_N_SEC(INFO, 60) << \"input avg req num: \"\n                            << req_sum_ * 1.0 / process_num_;\n\n  OpOutputList layout_tensor_list;\n  OP_REQUIRES_OK(ctx, ctx->output_list(\"tensors\", &layout_tensor_list));\n\n  std::vector<PtrWrapper> embeddings_data;\n  if (GetVersion() == 2) {\n    CHECK_EQ(req_num, 1);\n    OpInputList fid_list_row_split;\n    OP_REQUIRES_OK(ctx,\n                   ctx->input_list(\"fid_list_row_split\", &fid_list_row_split));\n\n    int ps_num = GetPsNum();\n    const std::vector<std::vector<int>> &table_feature_dim =\n        GetFeatureInTableDim();\n    embeddings_data.reserve(GetFeatureCfgs().feature_configs_size() * ps_num);\n    CHECK_EQ(embeddings_list.size(), ps_num * table_feature_dim.size());\n    CHECK_EQ(embeddings_list.size(), fid_list_row_split.size());\n    for (size_t table_i = 0; table_i < table_feature_dim.size(); ++table_i) {\n      auto &feature_dims = table_feature_dim[table_i];\n      for (size_t ps_i = 0; ps_i < ps_num; ++ps_i) {\n        int emb_index = table_i * ps_num + ps_i;\n        auto embeddings_flat = embeddings_list[emb_index].flat<float>();\n        auto embeddings_size = embeddings_flat.size();\n        auto embeddings_ptr = embeddings_flat.data();\n\n        auto fid_list_row_split_flat =\n            fid_list_row_split[emb_index].flat<int64_t>();\n\n        CHECK_EQ(static_cast<int>(feature_dims.size() + 1),\n                 fid_list_row_split_flat.size());\n        int pre_offset = 0;\n        int pre_emb_offset = 0;\n        for (size_t feature_i = 0; feature_i < feature_dims.size();\n             ++feature_i) {\n          int dim = feature_dims[feature_i];\n          int offset = fid_list_row_split_flat(feature_i + 1);\n          int fid_count = (offset - pre_offset);\n          embeddings_data.push_back(PtrWrapper{embeddings_ptr + pre_emb_offset,\n                                               dim, fid_count * dim});\n          pre_offset = offset;\n          pre_emb_offset += fid_count * dim;\n          CHECK(pre_emb_offset <= embeddings_size);\n        }\n      }\n    }\n  } else if (GetVersion() == 3) {\n    embeddings_data.reserve(embeddings_list.size());\n    for (size_t i = 0; i < embeddings_list.size(); ++i) {\n      const auto &embeddings_mat_ptr_ = embeddings_list[i].flat<float>().data();\n      embeddings_data.push_back(PtrWrapper(\n          {embeddings_mat_ptr_, 1, embeddings_list[i].flat<float>().size()}));\n    }\n  } else if (GetVersion() == 4) {\n    int ps_num = GetPsNum();\n    const std::vector<std::vector<int>> &table_feature_dim =\n        GetFeatureInTableDim();\n\n    CHECK_EQ(embeddings_list.size(), 1);\n    const auto embeddings_list_flat = embeddings_list[0].flat<float>();\n\n    const Tensor *fid_list_emb_row_lenth_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"fid_list_emb_row_lenth\",\n                                   &fid_list_emb_row_lenth_tensor));\n    const auto fid_list_emb_row_lenth_flat =\n        fid_list_emb_row_lenth_tensor->flat<int32_t>();\n    CHECK_EQ(fid_list_emb_row_lenth_flat.size(),\n             table_feature_dim.size() * ps_num);\n\n    embeddings_data.resize(req_num * fid_list_emb_row_lenth_flat.size());\n    int pre_count = 0;\n    for (size_t i = 0; i < req_num * fid_list_emb_row_lenth_flat.size(); ++i) {\n      int req_i = i / fid_list_emb_row_lenth_flat.size();\n      int table_idx = (i % fid_list_emb_row_lenth_flat.size()) % table_feature_dim.size();\n      int ps_index = (i % fid_list_emb_row_lenth_flat.size()) / table_feature_dim.size();\n      int index = req_i * fid_list_emb_row_lenth_flat.size() + table_idx * ps_num + ps_index;\n\n      embeddings_data[index].ptr = embeddings_list_flat.data() + pre_count;\n      embeddings_data[index].offset = 1;\n      embeddings_data[index].count = fid_list_emb_row_lenth_flat(i);\n\n      pre_count += fid_list_emb_row_lenth_flat(i);\n    }\n    CHECK_EQ(pre_count, req_num * embeddings_list_flat.size());\n  } else if (GetVersion() == 5) {\n    const auto emb_size_vec = emb_size_tensor->flat<int32>();\n    std::vector<std::vector<int>> each_req_emb_offset(\n        embeddings_list.size(), std::vector<int>(req_num + 1, 0));\n    for (size_t i = 0; i < embeddings_list.size(); ++i) {\n      for (size_t req_i = 0; req_i < req_num; ++req_i) {\n        each_req_emb_offset[i][req_i + 1] =\n            each_req_emb_offset[i][req_i] +\n            emb_size_vec(i + req_i * embeddings_list.size());\n      }\n      CHECK_EQ(each_req_emb_offset[i].back(),\n               embeddings_list[i].flat<float>().size());\n    }\n\n    embeddings_data.reserve(req_num * embeddings_list.size());\n    for (size_t req_i = 0; req_i < req_num; req_i++) {\n      for (size_t i = 0; i < embeddings_list.size(); ++i) {\n        const auto &embeddings_mat_ptr_ =\n            embeddings_list[i].flat<float>().data();\n        embeddings_data.push_back(\n            PtrWrapper({embeddings_mat_ptr_ + each_req_emb_offset[i][req_i], 1,\n                        each_req_emb_offset[i][req_i + 1] -\n                            each_req_emb_offset[i][req_i]}));\n      }\n    }\n  } else {\n    CHECK_EQ(req_num, 1);\n    embeddings_data.reserve(embeddings_list.size());\n    for (size_t i = 0; i < embeddings_list.size(); ++i) {\n      const auto &embeddings_mat_ptr_ = embeddings_list[i].flat<float>().data();\n      embeddings_data.push_back(PtrWrapper(\n          {embeddings_mat_ptr_, embeddings_list[i].dim_size(1),\n           embeddings_list[i].dim_size(0) * embeddings_list[i].dim_size(1)}));\n    }\n  }\n\n  {\n    auto activity =\n        std::make_unique<profiler::TraceMe>([]() { return \"AllocateTensors\"; });\n    int offset = 0;\n    const auto &out_configs = GetFeatureCfgs().out_configs();\n    for (const auto &layout_name : GetLayoutNames()) {\n      const OutConfig &out_conf = out_configs.at(layout_name);\n      for (const auto shape : out_conf.shape()) {\n        Tensor *tensor;\n        TensorShape tensor_shape;\n        for (size_t i = 0; i < shape.dims_size(); ++i) {\n          if (i == 0) {\n            tensor_shape.AddDim(shape.dims(i) == -1\n                                    ? each_req_batch_size_offset.back()\n                                    : shape.dims(i));\n          } else {\n            CHECK_GT(shape.dims(i), 0);\n            tensor_shape.AddDim(shape.dims(i));\n          }\n        }\n        OP_REQUIRES_OK(\n            ctx, layout_tensor_list.allocate(offset++, tensor_shape, &tensor));\n      }\n    }\n  }\n\n  int offset = 0;\n  std::vector<std::shared_ptr<Layout>> layouts;\n  {\n    auto activity =\n        std::make_unique<profiler::TraceMe>([]() { return \"CreateLayout\"; });\n    for (const auto &layout_name : GetLayoutNames()) {\n      const OutConfig &out_conf =\n          GetFeatureCfgs().out_configs().at(layout_name);\n      switch (out_conf.out_type()) {\n        case OutType::NONE:\n          layouts.push_back(std::make_shared<NoneLayout>(\n              layout_name, out_conf, layout_tensor_list, offset));\n          break;\n        default:\n          layouts.push_back(std::make_shared<DefaultLayout>(\n              layout_name, out_conf, layout_tensor_list, offset));\n          break;\n      }\n    }\n  }\n\n  TaskRun(layouts, embeddings_data, fids_offset_vec.data(), total_fid_num,\n          feature_offset_vec.data(), total_feature_num, nfl_offset_vec.data(),\n          total_nfl_num, max_batch_size, each_req_batch_size_offset,\n          each_req_nfl_offset, each_req_feature_offset, each_req_fid_offset,\n          req_num, ctx, &layout_tensor_list);\n}\n\nvoid ForwardTaskRunImpl(int slice_conf_i, int dim_num, int64 nfl_idx,\n                        ::monolith::io::proto::OutType out_type,\n                        ::monolith::io::proto::PoolingType pooling_type,\n                        int max_sequence_length, int start,\n                        const uint64 *fids_offset_vec, int total_fid_num,\n                        const int32 *feature_offset_vec, int total_feature_num,\n                        const uint32 *nfl_offset_vec, int total_nfl_num,\n                        int batch_size, const PtrWrapper *embeddings_data,\n                        int embeddings_data_size, PtrWrapper *ptr_info_ptr) {\n  PtrWrapper &ptr_info = *ptr_info_ptr;\n  bool is_shared;\n  int nfl_offset, feature_num;\n  GetFeatureInfo(nfl_idx, nfl_offset_vec, total_nfl_num, total_feature_num,\n                 &is_shared, &nfl_offset, &feature_num);\n  if (!feature_num) return;  // nfl exits\n\n  std::unique_ptr<float[]> tmp;\n  if (is_shared && (out_type == OutType::ADDN)) {\n    tmp.reset(new float[dim_num]());\n  }\n  int feature_idx = nfl_offset + 0;\n  for (size_t index = 0; index < batch_size; ++index) {\n    int temp_offset = index * ptr_info.offset;\n    if (pooling_type == PoolingType::FIRSTN) {\n      CHECK(temp_offset + max_sequence_length * dim_num <= ptr_info.count);\n    } else {\n      CHECK(temp_offset + dim_num <= ptr_info.count);\n    }\n    if (!is_shared || index == 0) {\n      bool init = (out_type != OutType::ADDN) || tmp;\n      GatherEmb(\n          feature_idx, max_sequence_length, pooling_type, dim_num, start,\n          embeddings_data, embeddings_data_size, fids_offset_vec, total_fid_num,\n          feature_offset_vec, total_feature_num,\n          const_cast<float *>(tmp ? tmp.get() : ptr_info.ptr + temp_offset),\n          OptimizedSumpooling<bool>, MemCopy, nullptr, nullptr,\n          DefaultGetInitFunc, &init);\n      if (tmp) {\n        bool init_tmp = (out_type != OutType::ADDN) || (slice_conf_i == 0);\n        OptimizedSumpooling<bool>(\n            tmp.get(), dim_num, &init_tmp,\n            const_cast<float *>(ptr_info.ptr + temp_offset));\n      }\n      feature_idx++;\n    } else {\n      if (tmp) {\n        bool init_tmp = (slice_conf_i == 0);  // && index == 0\n        OptimizedSumpooling<bool>(\n            tmp.get(), dim_num, &init_tmp,\n            const_cast<float *>(ptr_info.ptr + temp_offset));\n      } else {\n        switch (pooling_type) {\n          case PoolingType::SUM:\n          case PoolingType::MEAN:\n            MemCopy(const_cast<float *>(ptr_info.ptr + temp_offset),\n                    ptr_info.ptr, dim_num);\n            break;\n          case PoolingType::FIRSTN:\n            MemCopy(const_cast<float *>(ptr_info.ptr + temp_offset),\n                    ptr_info.ptr, dim_num * max_sequence_length);\n            break;\n          default:\n            break;\n        }\n      }\n    }\n  }\n}\n\nvoid MonolithEmbeddingToLayoutOp::TaskRun(\n    const std::vector<std::shared_ptr<Layout>> &layouts,\n    const std::vector<PtrWrapper> &embeddings_data,\n    const uint64 *fids_offset_vec, int total_fid_num,\n    const int32 *feature_offset_vec, int total_feature_num,\n    const uint32 *nfl_offset_vec, int total_nfl_num, int batch_size,\n    const std::vector<int> &each_req_batch_size_offset,\n    const std::vector<int> &each_req_nfl_offset,\n    const std::vector<int> &each_req_feature_offset,\n    const std::vector<int> &each_req_fid_offset, int req_num,\n    OpKernelContext *ctx, OpOutputList *layout_tensor_list) {\n  CHECK_EQ(req_num, 1);\n  for (int32 idx = 0; idx < layout_tensor_list->size(); ++idx) {\n    (*layout_tensor_list)[idx]->flat<float>().setZero();\n  }\n  auto gather_emb_fn = [&, this](int start, int end) {\n    for (int64 para_i = start; para_i < end; ++para_i) {\n      auto &layout = layouts.at(para_i);\n      // CHECK(end - start == 1);\n      const ::google::protobuf::RepeatedPtrField<SliceConfig>\n          &layout_slice_configs = layout->GetSliceConfig();\n      for (uint slice_conf_i = 0; slice_conf_i < layout_slice_configs.size();\n           ++slice_conf_i) {\n        const SliceConfig &slice_conf = layout_slice_configs[slice_conf_i];\n        int dim_num = slice_conf.end() - slice_conf.start();\n        PtrWrapper ptr_info = layout->GetSlice(0, slice_conf);\n        const int64 nfl_idx = slice_conf.feature_idx();\n\n        ForwardTaskRunImpl(slice_conf_i, dim_num, nfl_idx, layout->out_type(),\n                           slice_conf.pooling_type(),\n                           slice_conf.max_sequence_length(), slice_conf.start(),\n                           fids_offset_vec, total_fid_num, feature_offset_vec,\n                           total_feature_num, nfl_offset_vec, total_nfl_num,\n                           batch_size, embeddings_data.data(),\n                           embeddings_data.size(), &ptr_info);\n      }\n    }\n  };\n\n  {\n    auto activity =\n        std::make_unique<profiler::TraceMe>([]() { return \"GatherEmbFn\"; });\n    int parallel_flag = GetParallelFlag();\n    if (parallel_flag == 0) {\n      for (int i = 0; i < layouts.size(); ++i) {\n        gather_emb_fn(i, i + 1);\n      }\n    } else {\n      auto workers = ctx->device()->tensorflow_cpu_worker_threads()->workers;\n      workers->ParallelFor(\n          layouts.size(),\n          thread::ThreadPool::SchedulingParams(\n              thread::ThreadPool::SchedulingStrategy::kFixedBlockSize,\n              absl::nullopt,\n              1),  // block_size\n          gather_emb_fn);\n    }\n  }\n}\n\nclass MonolithEmbeddingToLayoutOpV2 : public MonolithEmbeddingToLayoutOp {\n public:\n  explicit MonolithEmbeddingToLayoutOpV2(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutOp(ctx, 2) {}\n};\n\nclass MonolithEmbeddingToLayoutOpV3 : public MonolithEmbeddingToLayoutOp {\n public:\n  explicit MonolithEmbeddingToLayoutOpV3(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutOp(ctx, 3) {}\n};\n\nclass MonolithEmbeddingToLayoutOpV4 : public MonolithEmbeddingToLayoutOp {\n public:\n  explicit MonolithEmbeddingToLayoutOpV4(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutOp(ctx, 4) {}\n};\n\nclass MonolithEmbeddingToLayoutOpV5 : public MonolithEmbeddingToLayoutOp {\n public:\n  explicit MonolithEmbeddingToLayoutOpV5(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutOp(ctx, 5) {}\n};\n\nMonolithEmbeddingToLayoutGradOp::MonolithEmbeddingToLayoutGradOp(\n    OpKernelConstruction *ctx, int version /* = 1*/)\n    : MonolithEmbeddingToLayoutBase(ctx, version) {}\n\nvoid MonolithEmbeddingToLayoutGradOp::Compute(OpKernelContext *ctx) {\n  // Grab the input tensor\n  OpInputList embeddings_list;\n  OP_REQUIRES_OK(ctx, ctx->input_list(\"embeddings_list\", &embeddings_list));\n\n  const Tensor *fids_offset_input;\n  OP_REQUIRES_OK(ctx, ctx->input(\"fid_offset\", &fids_offset_input));\n\n  const Tensor *feature_offset_input;\n  OP_REQUIRES_OK(ctx, ctx->input(\"feature_offset\", &feature_offset_input));\n\n  const Tensor *nfl_offset_input;\n  OP_REQUIRES_OK(ctx, ctx->input(\"nfl_offset\", &nfl_offset_input));\n\n  const Tensor *batch_size_tensor;\n  OP_REQUIRES_OK(ctx, ctx->input(\"batch_size\", &batch_size_tensor));\n\n  OpInputList tensors_grad;\n  OP_REQUIRES_OK(ctx, ctx->input_list(\"tensors_grad\", &tensors_grad));\n\n  const auto fids_offset_vec = fids_offset_input->flat<uint64>();\n  int total_fid_num = fids_offset_input->dim_size(0);\n  const auto feature_offset_vec = feature_offset_input->flat<int32>();\n  int total_feature_num = feature_offset_input->dim_size(0);\n  const auto nfl_offset_vec = nfl_offset_input->flat<uint32>();\n  int total_nfl_num = nfl_offset_input->dim_size(0);\n  int32 batch_size = batch_size_tensor->scalar<int32>()();\n\n  std::vector<std::pair<int, int>> ufid_grads_info;\n\n  OpOutputList embeddings_grad_list;\n  OP_REQUIRES_OK(\n      ctx, ctx->output_list(\"embeddings_grad_list\", &embeddings_grad_list));\n  std::vector<PtrWrapper> embeddings_grads_data;\n  int init_counter = 0;\n  if (GetVersion() == 2) {\n    OpInputList fid_list_row_split;\n    OP_REQUIRES_OK(ctx,\n                   ctx->input_list(\"fid_list_row_split\", &fid_list_row_split));\n    int ps_num = GetPsNum();\n    const std::vector<std::vector<int>> &table_feature_dim =\n        GetFeatureInTableDim();\n    ufid_grads_info.reserve(GetFeatureCfgs().feature_configs_size() * ps_num);\n    embeddings_grads_data.reserve(GetFeatureCfgs().feature_configs_size() *\n                                  ps_num);\n    CHECK_EQ(embeddings_list.size(), ps_num * table_feature_dim.size());\n    CHECK_EQ(embeddings_list.size(), fid_list_row_split.size());\n    for (size_t table_i = 0; table_i < table_feature_dim.size(); ++table_i) {\n      auto &feature_dims = table_feature_dim[table_i];\n      for (size_t ps_i = 0; ps_i < ps_num; ++ps_i) {\n        int emb_index = table_i * ps_num + ps_i;\n        Tensor *tensor;\n        OP_REQUIRES_OK(\n            ctx, embeddings_grad_list.allocate(\n                     emb_index, embeddings_list[emb_index].shape(), &tensor));\n\n        auto embeddings_grad_flat =\n            embeddings_grad_list[emb_index]->flat<float>();\n        auto embeddings_grad_size = embeddings_grad_flat.size();\n        auto embeddings_grad_ptr = embeddings_grad_flat.data();\n\n        auto fid_list_row_split_flat =\n            fid_list_row_split[emb_index].flat<int64_t>();\n\n        CHECK_EQ(static_cast<int>(feature_dims.size() + 1),\n                 fid_list_row_split_flat.size());\n\n        int pre_offset = 0;\n        int pre_emb_offset = 0;\n        for (size_t feature_i = 0; feature_i < feature_dims.size();\n             ++feature_i) {\n          int dim = feature_dims[feature_i];\n          int offset = fid_list_row_split_flat(feature_i + 1);\n          int fid_count = (offset - pre_offset);\n          embeddings_grads_data.push_back(PtrWrapper{\n              embeddings_grad_ptr + pre_emb_offset, dim, fid_count * dim});\n          ufid_grads_info.emplace_back(std::make_pair(init_counter, fid_count));\n          pre_offset = offset;\n          pre_emb_offset += fid_count * dim;\n          CHECK(pre_emb_offset <= embeddings_grad_size);\n          init_counter += fid_count;\n        }\n      }\n    }\n  } else if (GetVersion() == 3 || GetVersion() == 5) {\n    embeddings_grads_data.reserve(embeddings_list.size());\n    for (size_t i = 0; i < embeddings_list.size(); ++i) {\n      Tensor *tensor;\n      OP_REQUIRES_OK(ctx, embeddings_grad_list.allocate(\n                              i, embeddings_list[i].shape(), &tensor));\n      embeddings_grads_data.push_back(\n          PtrWrapper({embeddings_grad_list[i]->flat<float>().data(), 1,\n                      embeddings_grad_list[i]->flat<float>().size()}));\n    }\n  } else if (GetVersion() == 4) {\n    int ps_num = GetPsNum();\n    const std::vector<std::vector<int>> &table_feature_dim =\n        GetFeatureInTableDim();\n\n    CHECK_EQ(embeddings_list.size(), 1);\n    Tensor *embeddings_grad_list_tensor;\n    OP_REQUIRES_OK(\n        ctx, embeddings_grad_list.allocate(0, {embeddings_list[0].shape()},\n                                           &embeddings_grad_list_tensor));\n    const auto embeddings_grad_list_flat =\n        embeddings_grad_list_tensor->flat<float>();\n\n    const Tensor *fid_list_emb_row_lenth_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"fid_list_emb_row_lenth\",\n                                   &fid_list_emb_row_lenth_tensor));\n    const auto fid_list_emb_row_lenth_flat =\n        fid_list_emb_row_lenth_tensor->flat<int32_t>();\n    CHECK_EQ(fid_list_emb_row_lenth_flat.size(),\n             table_feature_dim.size() * ps_num);\n\n    embeddings_grads_data.resize(fid_list_emb_row_lenth_flat.size());\n    int pre_count = 0;\n    for (size_t i = 0; i < fid_list_emb_row_lenth_flat.size(); ++i) {\n      int table_idx = i % table_feature_dim.size();\n      int ps_index = i / table_feature_dim.size();\n      int index = table_idx * ps_num + ps_index;\n\n      embeddings_grads_data[index].ptr =\n          embeddings_grad_list_flat.data() + pre_count;\n      embeddings_grads_data[index].offset = 1;\n      embeddings_grads_data[index].count = fid_list_emb_row_lenth_flat(i);\n\n      pre_count += fid_list_emb_row_lenth_flat(i);\n    }\n    CHECK_EQ(pre_count, embeddings_grad_list_flat.size());\n  } else {\n    embeddings_grads_data.reserve(embeddings_list.size());\n    for (size_t i = 0; i < embeddings_list.size(); ++i) {\n      Tensor *tensor;\n      OP_REQUIRES_OK(ctx, embeddings_grad_list.allocate(\n                              i, embeddings_list[i].shape(), &tensor));\n      int dim = embeddings_list[i].dim_size(1);\n      int fid_count = embeddings_list[i].dim_size(0);\n      embeddings_grads_data.push_back(PtrWrapper{\n          embeddings_grad_list[i]->flat<float>().data(), dim, fid_count * dim});\n      ufid_grads_info.emplace_back(std::make_pair(init_counter, fid_count));\n      init_counter += fid_count;\n    }\n  }\n\n  // wrapper of bool for avoid :\n  // invalid initialization of non-const reference of type 'bool&' from an\n  // rvalue of type 'bool'\n  GroupA init(init_counter, GetMaxSliceNum());\n\n  int offset = 0;\n  std::vector<std::shared_ptr<Layout>> layouts;\n  for (const auto &layout_name : GetLayoutNames()) {\n    const OutConfig &out_conf = GetFeatureCfgs().out_configs().at(layout_name);\n\n    switch (out_conf.out_type()) {\n      case OutType::NONE:\n        layouts.push_back(std::make_shared<NoneLayout>(layout_name, out_conf,\n                                                       tensors_grad, offset));\n        break;\n      default:\n        layouts.push_back(std::make_shared<DefaultLayout>(\n            layout_name, out_conf, tensors_grad, offset));\n        break;\n    }\n  }\n\n  TaskRun(layouts, &ufid_grads_info, fids_offset_vec.data(), total_fid_num,\n          feature_offset_vec.data(), total_feature_num, nfl_offset_vec.data(),\n          total_nfl_num, batch_size, ctx, &embeddings_grad_list,\n          &embeddings_grads_data,\n          (GetVersion() == 3 || GetVersion() == 4 || GetVersion() == 5) ? nullptr : &init);\n}\n\nstatic constexpr int NUM_LOCKS = 512;\nvoid *ScatterGradGetMutexFuncFunc(void *main_params, int32 index1,\n                                  int32 index2) {\n  std::mutex *mutex_list = static_cast<std::mutex *>(main_params);\n  int lock_idx = index1 * index2;\n  int mutex_idx = lock_idx % NUM_LOCKS;\n  auto one_mutex = mutex_list + mutex_idx;\n  return one_mutex;\n}\nstruct ScatterGradGetInitFuncParams {\n  int slice_conf_slice_idx;\n  const std::vector<std::pair<int, int>> *ufid_grads_info;\n  GroupA *init;\n};\nvoid *ScatterGradGetInitFunc(void *main_params, int32 index1, int32 index2) {\n  ScatterGradGetInitFuncParams *params =\n      static_cast<ScatterGradGetInitFuncParams *>(main_params);\n  const auto &fid_info = params->ufid_grads_info->at(index1);\n  CUSTOM_CHECK(index2 < fid_info.second);\n  int real_ufid_idx = fid_info.first + index2;\n  auto init_p = params->init->Get(real_ufid_idx, params->slice_conf_slice_idx);\n  return init_p;\n}\n\nvoid MonolithEmbeddingToLayoutGradOp::TaskRun(\n    const std::vector<std::shared_ptr<Layout>> &layouts,\n    const std::vector<std::pair<int, int>> *ufid_grads_info,\n    const uint64 *fids_offset_vec, int total_fid_num,\n    const int32 *feature_offset_vec, int total_feature_num,\n    const uint32 *nfl_offset_vec, int total_nfl_num, int batch_size,\n    OpKernelContext *ctx, OpOutputList *embeddings_grad_list,\n    std::vector<PtrWrapper> *embeddings_grads_data, GroupA *init) {\n  for (int32 idx = 0; idx < embeddings_grad_list->size(); ++idx) {\n    (*embeddings_grad_list)[idx]->flat<float>().setConstant(0);\n  }\n  int parallel_flag = GetParallelFlag();\n\n  // mutex/init per op compute, because there are several(>1) grad op\n  // calculated togather.\n  std::unique_ptr<mutex[]> mutex_list;\n  if (parallel_flag != 0) {\n    mutex_list = std::make_unique<mutex[]>(NUM_LOCKS);\n  }\n  auto scatter_grad_fn = [&, this](int start, int end) {\n    for (int64 para_i = start; para_i < end; ++para_i) {\n      auto &layout = layouts.at(para_i);\n      // CHECK(end - start == 1);\n      const ::google::protobuf::RepeatedPtrField<SliceConfig>\n          &layout_slice_configs = layout->GetSliceConfig();\n      for (const SliceConfig &slice_conf : layout_slice_configs) {\n        int dim_num = slice_conf.end() - slice_conf.start();\n        PtrWrapper ptr_info = layout->GetSlice(0, slice_conf);\n        const int64 &nfl_idx = slice_conf.feature_idx();\n        bool is_shared;\n        int nfl_offset, feature_num;\n        GetFeatureInfo(nfl_idx, nfl_offset_vec, total_nfl_num,\n                       total_feature_num, &is_shared, &nfl_offset,\n                       &feature_num);\n        if (!feature_num) continue;  // nfl exits\n        int feature_idx = nfl_offset + 0;\n        for (size_t index = 0; index < batch_size; ++index) {\n          int temp_offset = index * ptr_info.offset;\n          if (slice_conf.pooling_type() == PoolingType::FIRSTN) {\n            CHECK(temp_offset + slice_conf.max_sequence_length() * dim_num <=\n                  ptr_info.count);\n          } else {\n            CHECK(temp_offset + dim_num <= ptr_info.count);\n          }\n          ScatterGradGetInitFuncParams init_params(\n              {slice_conf.slice_idx(), ufid_grads_info, init});\n          ScatterGrad(feature_idx, slice_conf.max_sequence_length(),\n                      slice_conf.pooling_type(), ptr_info.ptr + temp_offset,\n                      dim_num, slice_conf.start(), fids_offset_vec,\n                      total_fid_num, feature_offset_vec, total_feature_num,\n                      embeddings_grads_data->size(),\n                      embeddings_grads_data->data(), OptimizedSumpooling<char>,\n                      (mutex_list ? ScatterGradGetMutexFuncFunc : nullptr),\n                      (mutex_list ? mutex_list.get() : nullptr),\n                      (init ? ScatterGradGetInitFunc : nullptr), &init_params);\n          if (!is_shared) {  // train don't have shared feature\n            feature_idx++;\n          }\n        }\n      }\n    }\n  };\n\n  if (parallel_flag == 0) {\n    for (int i = 0; i < layouts.size(); ++i) {\n      scatter_grad_fn(i, i + 1);\n    }\n  } else {\n    auto worker_threads = ctx->device()->tensorflow_cpu_worker_threads();\n    worker_threads->workers->ParallelFor(\n        layouts.size(),\n        thread::ThreadPool::SchedulingParams(\n            thread::ThreadPool::SchedulingStrategy::kFixedBlockSize,\n            absl::nullopt,\n            1),  // block_size\n        scatter_grad_fn);\n  }\n}\n\nclass MonolithEmbeddingToLayoutGradOpV2\n    : public MonolithEmbeddingToLayoutGradOp {\n public:\n  explicit MonolithEmbeddingToLayoutGradOpV2(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutGradOp(ctx, 2) {}\n};\n\nclass MonolithEmbeddingToLayoutGradOpV3\n    : public MonolithEmbeddingToLayoutGradOp {\n public:\n  explicit MonolithEmbeddingToLayoutGradOpV3(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutGradOp(ctx, 3) {}\n};\n\nclass MonolithEmbeddingToLayoutGradOpV4\n    : public MonolithEmbeddingToLayoutGradOp {\n public:\n  explicit MonolithEmbeddingToLayoutGradOpV4(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutGradOp(ctx, 4) {}\n};\n\nclass MonolithEmbeddingToLayoutGradOpV5\n    : public MonolithEmbeddingToLayoutGradOp {\n public:\n  explicit MonolithEmbeddingToLayoutGradOpV5(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutGradOp(ctx, 5) {}\n};\n\nauto forward_shape_inference_fn = [](shape_inference::InferenceContext *ctx) {\n  std::string serialized;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"feature_cfgs\", &serialized));\n  FeatureConfigs feature_cfgs;\n  CHECK(feature_cfgs.ParseFromArray(serialized.data(), serialized.size()));\n\n  std::vector<std::string> layout_names;\n  const auto &out_configs = feature_cfgs.out_configs();\n  for (const auto &pair : out_configs) {\n    layout_names.push_back(pair.first);\n  }\n  std::sort(layout_names.begin(), layout_names.end());\n\n  std::vector<shape_inference::ShapeHandle> tensors_shape;\n  for (const auto &layout_name : layout_names) {\n    const OutConfig &out_conf = out_configs.at(layout_name);\n    for (const auto shape : out_conf.shape()) {\n      std::vector<shape_inference::DimensionHandle> dims;\n      for (size_t i = 0; i < shape.dims_size(); ++i) {\n        if (i == 0) {\n          dims.push_back(ctx->UnknownDim());\n        } else {\n          CHECK_GT(shape.dims(i), 0);\n          dims.push_back(ctx->MakeDim(shape.dims(i)));\n        }\n      }\n      tensors_shape.push_back(ctx->MakeShape(dims));\n    }\n  }\n  TF_RETURN_IF_ERROR(ctx->set_output(\"tensors\", tensors_shape));\n  return Status::OK();\n};\n\nREGISTER_OP(\"MonolithEmbeddingToLayout\")\n    .Input(\"embeddings_list: M * float\")\n    .Input(\"fid_offset: uint64\")\n    .Input(\"feature_offset: int32\")\n    .Input(\"nfl_offset: uint32\")\n    .Input(\"batch_size: int32\")\n    .Output(\"tensors: num_out * float\")\n    .Attr(\"M: int\")  // num of fids_list (shard x subtable)\n    .Attr(\"num_out: int\")\n    .Attr(\"variant_type: string\")\n    .Attr(\"feature_cfgs: string\")\n    .SetDoNotOptimize()\n    .SetShapeFn(forward_shape_inference_fn);\n\nauto backward_shape_inference_fn = [](shape_inference::InferenceContext *ctx) {\n  std::vector<shape_inference::ShapeHandle> embeddings_list_shape;\n  TF_RETURN_IF_ERROR(ctx->input(\"embeddings_list\", &embeddings_list_shape));\n  TF_RETURN_IF_ERROR(\n      ctx->set_output(\"embeddings_grad_list\", embeddings_list_shape));\n  return Status::OK();\n};\n\nREGISTER_OP(\"MonolithEmbeddingToLayoutGrad\")\n    .Input(\"embeddings_list: M * float\")\n    .Input(\"fid_offset: uint64\")\n    .Input(\"feature_offset: int32\")\n    .Input(\"nfl_offset: uint32\")\n    .Input(\"batch_size: int32\")\n    .Input(\"tensors_grad: num_input * float\")\n    .Output(\"embeddings_grad_list: M * float\")\n    .Attr(\"M: int\")          // num of fids_list (shard x subtable)\n    .Attr(\"num_input: int\")  // num of tensors_grad input\n    .Attr(\"variant_type: string\")\n    .Attr(\"feature_cfgs: string\")\n    .SetDoNotOptimize()\n    .SetShapeFn(backward_shape_inference_fn);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayout\").Device(DEVICE_CPU),\n                        MonolithEmbeddingToLayoutOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithEmbeddingToLayoutGrad\").Device(DEVICE_CPU),\n    MonolithEmbeddingToLayoutGradOp);\n\nREGISTER_OP(\"MonolithEmbeddingToLayoutV2\")\n    .Input(\"embeddings_list: M * float\")\n    .Input(\"fid_list_row_split: M * int64\")\n    .Input(\"fid_offset: uint64\")\n    .Input(\"feature_offset: int32\")\n    .Input(\"nfl_offset: uint32\")\n    .Input(\"batch_size: int32\")\n    .Output(\"tensors: num_out * float\")\n    .Attr(\"M: int\")  // num of fids_list (shard x subtable)\n    .Attr(\"num_out: int\")\n    .Attr(\"variant_type: string\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"parallel_flag: int = 0\")\n    .SetDoNotOptimize()\n    .SetShapeFn(forward_shape_inference_fn);\n\nREGISTER_OP(\"MonolithEmbeddingToLayoutGradV2\")\n    .Input(\"embeddings_list: M * float\")\n    .Input(\"fid_list_row_split: M * int64\")\n    .Input(\"fid_offset: uint64\")\n    .Input(\"feature_offset: int32\")\n    .Input(\"nfl_offset: uint32\")\n    .Input(\"batch_size: int32\")\n    .Input(\"tensors_grad: num_input * float\")\n    .Output(\"embeddings_grad_list: M * float\")\n    .Attr(\"M: int\")          // num of fids_list (shard x subtable)\n    .Attr(\"num_input: int\")  // num of tensors_grad input\n    .Attr(\"variant_type: string\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"parallel_flag: int = 0\")\n    .SetDoNotOptimize()\n    .SetShapeFn(backward_shape_inference_fn);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayoutV2\").Device(DEVICE_CPU),\n                        MonolithEmbeddingToLayoutOpV2);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithEmbeddingToLayoutGradV2\").Device(DEVICE_CPU),\n    MonolithEmbeddingToLayoutGradOpV2);\n\nREGISTER_OP(\"MonolithEmbeddingToLayoutV3\")\n    .Input(\"embeddings_list: M * float\")\n    .Input(\"fid_offset: uint64\")\n    .Input(\"feature_offset: int32\")\n    .Input(\"nfl_offset: uint32\")\n    .Input(\"batch_size: int32\")\n    .Output(\"tensors: num_out * float\")\n    .Attr(\"M: int\")  // num of fids_list (shard x subtable)\n    .Attr(\"num_out: int\")\n    .Attr(\"variant_type: string\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"parallel_flag: int = 0\")\n    .SetDoNotOptimize()\n    .SetShapeFn(forward_shape_inference_fn);\n\nREGISTER_OP(\"MonolithEmbeddingToLayoutGradV3\")\n    .Input(\"embeddings_list: M * float\")\n    .Input(\"fid_offset: uint64\")\n    .Input(\"feature_offset: int32\")\n    .Input(\"nfl_offset: uint32\")\n    .Input(\"batch_size: int32\")\n    .Input(\"tensors_grad: num_input * float\")\n    .Output(\"embeddings_grad_list: M * float\")\n    .Attr(\"M: int\")          // num of fids_list (shard x subtable)\n    .Attr(\"num_input: int\")  // num of tensors_grad input\n    .Attr(\"variant_type: string\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"parallel_flag: int = 0\")\n    .SetDoNotOptimize()\n    .SetShapeFn(backward_shape_inference_fn);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayoutV3\").Device(DEVICE_CPU),\n                        MonolithEmbeddingToLayoutOpV3);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithEmbeddingToLayoutGradV3\").Device(DEVICE_CPU),\n    MonolithEmbeddingToLayoutGradOpV3);\n\nREGISTER_OP(\"MonolithEmbeddingToLayoutV4\")\n    .Input(\"embeddings_list: M * float\")\n    .Input(\"fid_list_emb_row_lenth: int32\")\n    .Input(\"fid_offset: uint64\")\n    .Input(\"feature_offset: int32\")\n    .Input(\"nfl_offset: uint32\")\n    .Input(\"batch_size: int32\")\n    .Output(\"tensors: num_out * float\")\n    .Attr(\"M: int\")  // num of fids_list (shard x subtable)\n    .Attr(\"num_out: int\")\n    .Attr(\"variant_type: string\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"parallel_flag: int = 0\")\n    .SetDoNotOptimize()\n    .SetShapeFn(forward_shape_inference_fn);\n\nREGISTER_OP(\"MonolithEmbeddingToLayoutGradV4\")\n    .Input(\"embeddings_list: M * float\")\n    .Input(\"fid_list_emb_row_lenth: int32\")\n    .Input(\"fid_offset: uint64\")\n    .Input(\"feature_offset: int32\")\n    .Input(\"nfl_offset: uint32\")\n    .Input(\"batch_size: int32\")\n    .Input(\"tensors_grad: num_input * float\")\n    .Output(\"embeddings_grad_list: M * float\")\n    .Attr(\"M: int\")          // num of fids_list (shard x subtable)\n    .Attr(\"num_input: int\")  // num of tensors_grad input\n    .Attr(\"variant_type: string\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"parallel_flag: int = 0\")\n    .SetDoNotOptimize()\n    .SetShapeFn(backward_shape_inference_fn);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayoutV4\").Device(DEVICE_CPU),\n                        MonolithEmbeddingToLayoutOpV4);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithEmbeddingToLayoutGradV4\").Device(DEVICE_CPU),\n    MonolithEmbeddingToLayoutGradOpV4);\n\nREGISTER_OP(\"MonolithEmbeddingToLayoutV5\")\n    .Input(\"embeddings_list: M * float\")\n    .Input(\"fid_offset: uint64\")\n    .Input(\"feature_offset: int32\")\n    .Input(\"nfl_offset: uint32\")\n    .Input(\"batch_size: int32\")\n    .Input(\"nfl_size: int32\")\n    .Input(\"feature_size: int32\")\n    .Input(\"fid_size: int32\")\n    .Input(\"emb_size: int32\")\n    .Output(\"tensors: num_out * float\")\n    .Attr(\"M: int\")  // num of fids_list (shard x subtable)\n    .Attr(\"num_out: int\")\n    .Attr(\"variant_type: string\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"parallel_flag: int = 0\")\n    .SetDoNotOptimize()\n    .SetShapeFn(forward_shape_inference_fn);\n\nREGISTER_OP(\"MonolithEmbeddingToLayoutGradV5\")\n    .Input(\"embeddings_list: M * float\")\n    .Input(\"fid_offset: uint64\")\n    .Input(\"feature_offset: int32\")\n    .Input(\"nfl_offset: uint32\")\n    .Input(\"batch_size: int32\")\n    .Input(\"tensors_grad: num_input * float\")\n    .Output(\"embeddings_grad_list: M * float\")\n    .Attr(\"M: int\")          // num of fids_list (shard x subtable)\n    .Attr(\"num_input: int\")  // num of tensors_grad input\n    .Attr(\"variant_type: string\")\n    .Attr(\"feature_cfgs: string\")\n    .Attr(\"ps_num: int\")\n    .Attr(\"parallel_flag: int = 0\")\n    .SetDoNotOptimize()\n    .SetShapeFn(backward_shape_inference_fn);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayoutV5\").Device(DEVICE_CPU),\n                        MonolithEmbeddingToLayoutOpV5);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithEmbeddingToLayoutGradV5\").Device(DEVICE_CPU),\n    MonolithEmbeddingToLayoutGradOpV5);\n\n\n}  // namespace fused_layout\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/fused_embedding_to_layout.cu.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n\n#if GOOGLE_CUDA\n#define EIGEN_USE_GPU\n\n#include \"monolith/native_training/runtime/ops/fused_embedding_to_layout.h\"\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/register_types.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/kernels/gpu_device_array.h\"\n#include \"tensorflow/core/kernels/gpu_device_array_gpu.h\"\n#include \"tensorflow/core/util/gpu_kernel_helper.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace fused_layout {\n\ntypedef Eigen::GpuDevice GPUDevice;\n\nstruct ForwardTaskInfo {\n  int dim_num;\n  PtrWrapper ptr_info;\n  int64 nfl_idx;\n\n  ::monolith::io::proto::OutType out_type;\n  ::monolith::io::proto::PoolingType pooling_type;\n  int max_sequence_length;\n  int start;\n  int req_i;\n};\n\n__device__ void *MemCopyGPU(float *dest, const float *src, std::size_t count) {\n  for (int32 idx = 0; idx < count; ++idx) {\n    *(dest + idx) = *(src + idx);\n  }\n  return dest;\n}\n\n__device__ void OptimizedSumpoolingGPU(const float *src, const int dim_num,\n                                       void *init_ptr, float *dst,\n                                       void *one_mutex = nullptr,\n                                       int mean_pool_fid_num = 0) {\n  bool *init = static_cast<bool *>(init_ptr);\n  if (init && *init) {\n    if (mean_pool_fid_num) {\n      for (size_t i = 0; i < dim_num; ++i) {\n        dst[i] = (src[i] / mean_pool_fid_num);\n      }\n    } else {\n      MemCopyGPU(dst, src, dim_num);\n    }\n    *init = false;\n  } else {\n    if (mean_pool_fid_num) {\n      for (size_t i = 0; i < dim_num; ++i) {\n        dst[i] += (src[i] / mean_pool_fid_num);\n      }\n    } else {\n      for (size_t i = 0; i < dim_num; ++i) {\n        dst[i] += src[i];\n      }\n    }\n  }\n}\n\n__device__ void OptimizedSumpoolingGPUWithLock(const float *src,\n                                               const int dim_num,\n                                               void *init_ptr, float *dst,\n                                               void *one_mutex = nullptr,\n                                               int mean_pool_fid_num = 0) {\n  if (mean_pool_fid_num) {\n    for (int32 idx = 0; idx < dim_num; ++idx) {\n      GpuAtomicAdd(dst + idx, (*(src + idx)) / mean_pool_fid_num);\n    }\n  } else {\n    for (int32 idx = 0; idx < dim_num; ++idx) {\n      GpuAtomicAdd(dst + idx, *(src + idx));\n    }\n  }\n}\n\n__global__ void ForwardBatchKernel(\n    const Gpu2DLaunchConfig config,\n    GpuDeviceArrayStruct<PtrWrapper> embeddings_data_list,\n    const uint64 *fids_offset_vec, int total_fid_num,\n    const int32 *feature_offset_vec, int total_feature_num,\n    const uint32 *nfl_offset_vec, int total_nfl_num,\n    GpuDeviceArrayStruct<ForwardTaskInfo> task_info_list,\n    GpuDeviceArrayStruct<int> each_req_batch_size_list,\n    GpuDeviceArrayStruct<int> each_req_nfl_list,\n    GpuDeviceArrayStruct<int> each_req_feature_list,\n    GpuDeviceArrayStruct<int> each_req_fid_list,\n    GpuDeviceArrayStruct<int> each_req_emb_list) {\n  ForwardTaskInfo *task_info_list_ptr =\n      GetGpuDeviceArrayOnDevice(&task_info_list);\n  int *each_req_batch_size_offset =\n      GetGpuDeviceArrayOnDevice(&each_req_batch_size_list);\n  int *each_req_nfl_offset = GetGpuDeviceArrayOnDevice(&each_req_nfl_list);\n  int *each_req_feature_offset =\n      GetGpuDeviceArrayOnDevice(&each_req_feature_list);\n  int *each_req_fid_offset = GetGpuDeviceArrayOnDevice(&each_req_fid_list);\n  int *each_req_emb_offset = GetGpuDeviceArrayOnDevice(&each_req_emb_list);\n\n  const PtrWrapper *embeddings_data_list_ptr =\n      GetGpuDeviceArrayOnDevice(&embeddings_data_list);\n\n  bool is_shared;\n  int nfl_offset;\n  int feature_num;\n  ForwardTaskInfo *task_info = nullptr;\n  int feature_idx;\n  int temp_offset;\n  bool init;\n\n  GPU_AXIS_KERNEL_LOOP(task_idx, config.virtual_thread_count.y, Y) {\n    task_info = task_info_list_ptr + task_idx;\n    GetFeatureInfo(task_info->nfl_idx,\n                   nfl_offset_vec + *(each_req_nfl_offset + task_info->req_i),\n                   *(each_req_nfl_offset + task_info->req_i + 1) -\n                       *(each_req_nfl_offset + task_info->req_i),\n                   *(each_req_feature_offset + task_info->req_i + 1) -\n                       *(each_req_feature_offset + task_info->req_i),\n                   &is_shared, &nfl_offset, &feature_num);\n    if (!feature_num) return;  // nfl exits\n\n    GPU_AXIS_KERNEL_LOOP(batch_idx, config.virtual_thread_count.x, X) {\n      if (batch_idx >= *(each_req_batch_size_offset + task_info->req_i + 1) -\n                           *(each_req_batch_size_offset + task_info->req_i))\n        return;                  // out of range\n      feature_idx = nfl_offset;  // in single req scope\n      if (!is_shared) {\n        feature_idx += batch_idx;\n      }\n      temp_offset =\n          (batch_idx + *(each_req_batch_size_offset + task_info->req_i)) *\n          task_info->ptr_info.offset;\n\n      if (task_info->out_type == OutType::ADDN) {\n        if (task_info->pooling_type == PoolingType::FIRSTN) {  // not support\n        } else {\n          GatherEmb(feature_idx, task_info->max_sequence_length,\n                    task_info->pooling_type, task_info->dim_num,\n                    task_info->start,\n                    embeddings_data_list_ptr +\n                        *(each_req_emb_offset + task_info->req_i),\n                    *(each_req_emb_offset + task_info->req_i + 1) -\n                        *(each_req_emb_offset + task_info->req_i),\n                    fids_offset_vec + *(each_req_fid_offset + task_info->req_i),\n                    *(each_req_fid_offset + task_info->req_i + 1) -\n                        *(each_req_fid_offset + task_info->req_i),\n                    feature_offset_vec +\n                        *(each_req_feature_offset + task_info->req_i),\n                    *(each_req_feature_offset + task_info->req_i + 1) -\n                        *(each_req_feature_offset + task_info->req_i),\n                    const_cast<float *>(task_info->ptr_info.ptr + temp_offset),\n                    OptimizedSumpoolingGPUWithLock, MemCopyGPU, nullptr,\n                    nullptr, nullptr, nullptr);\n        }\n      } else {\n        init = true;\n        GatherEmb(\n            feature_idx, task_info->max_sequence_length,\n            task_info->pooling_type, task_info->dim_num, task_info->start,\n            embeddings_data_list_ptr +\n                *(each_req_emb_offset + task_info->req_i),\n            *(each_req_emb_offset + task_info->req_i + 1) -\n                *(each_req_emb_offset + task_info->req_i),\n            fids_offset_vec + *(each_req_fid_offset + task_info->req_i),\n            *(each_req_fid_offset + task_info->req_i + 1) -\n                *(each_req_fid_offset + task_info->req_i),\n            feature_offset_vec + *(each_req_feature_offset + task_info->req_i),\n            *(each_req_feature_offset + task_info->req_i + 1) -\n                *(each_req_feature_offset + task_info->req_i),\n            const_cast<float *>(task_info->ptr_info.ptr + temp_offset),\n            OptimizedSumpoolingGPU, MemCopyGPU, nullptr, nullptr,\n            DefaultGetInitFunc, &init);\n      }\n    }\n  }\n}\n\ntemplate <typename T>\nstruct SetZeroFunctor {\n  void operator()(const GPUDevice &d, typename TTypes<T>::Flat out) {\n    To32Bit(out).device(d) = To32Bit(out).constant(T(0));\n  }\n};\n\nclass MonolithEmbeddingToLayoutOpV3GPU : public MonolithEmbeddingToLayoutOp {\n public:\n  explicit MonolithEmbeddingToLayoutOpV3GPU(OpKernelConstruction *ctx,\n                                            int verison = 3)\n      : MonolithEmbeddingToLayoutOp(ctx, verison) {}\n  virtual void TaskRun(const std::vector<std::shared_ptr<Layout>> &layouts,\n                       const std::vector<PtrWrapper> &embeddings_data,\n                       const uint64 *fids_offset_vec, int total_fid_num,\n                       const int32 *feature_offset_vec, int total_feature_num,\n                       const uint32 *nfl_offset_vec, int total_nfl_num,\n                       int batch_size,\n                       const std::vector<int> &each_req_batch_size_offset,\n                       const std::vector<int> &each_req_nfl_offset,\n                       const std::vector<int> &each_req_feature_offset,\n                       const std::vector<int> &each_req_fid_offset, int req_num,\n                       OpKernelContext *ctx, OpOutputList *layout_tensor_list) {\n    GPUDevice gpu_device = ctx->eigen_device<GPUDevice>();\n    SetZeroFunctor<float> zero_functor;\n    for (int32 idx = 0; idx < layout_tensor_list->size(); ++idx) {\n      zero_functor(gpu_device, (*layout_tensor_list)[idx]->flat<float>());\n    }\n\n    int each_req_emb_num = embeddings_data.size() / req_num;\n    std::vector<ForwardTaskInfo> task_info_vec;\n    {\n      auto activity =\n          std::make_unique<profiler::TraceMe>([]() { return \"BuildGPUTask\"; });\n      for (int req_i = 0; req_i < req_num; req_i++) {\n        for (int para_i = 0; para_i < layouts.size(); ++para_i) {\n          auto &layout = layouts.at(para_i);\n          // CHECK(end - start == 1);\n          const ::google::protobuf::RepeatedPtrField<SliceConfig>\n              &layout_slice_configs = layout->GetSliceConfig();\n          for (uint slice_conf_i = 0;\n               slice_conf_i < layout_slice_configs.size(); ++slice_conf_i) {\n            const SliceConfig &slice_conf = layout_slice_configs[slice_conf_i];\n            int dim_num = slice_conf.end() - slice_conf.start();\n            PtrWrapper ptr_info = layout->GetSlice(0, slice_conf);\n            const int64 nfl_idx = slice_conf.feature_idx();\n            task_info_vec.push_back(ForwardTaskInfo(\n                {dim_num, ptr_info, nfl_idx, layout->out_type(),\n                 slice_conf.pooling_type(), slice_conf.max_sequence_length(),\n                 slice_conf.start(), req_i}));\n          }\n        }\n      }\n    }\n\n    GpuDeviceArrayOnHost<ForwardTaskInfo> task_info_list(ctx,\n                                                         task_info_vec.size());\n    GpuDeviceArrayOnHost<PtrWrapper> embeddings_data_list(\n        ctx, embeddings_data.size());\n    GpuDeviceArrayOnHost<int> each_req_batch_size_list(\n        ctx, each_req_batch_size_offset.size());\n    GpuDeviceArrayOnHost<int> each_req_nfl_list(ctx,\n                                                each_req_nfl_offset.size());\n    GpuDeviceArrayOnHost<int> each_req_feature_list(\n        ctx, each_req_feature_offset.size());\n    GpuDeviceArrayOnHost<int> each_req_fid_list(ctx,\n                                                each_req_fid_offset.size());\n    GpuDeviceArrayOnHost<int> each_req_emb_list(ctx, req_num + 1);\n    {\n      auto activity = std::make_unique<profiler::TraceMe>(\n          []() { return \"CopyHostValueToDevice\"; });\n      OP_REQUIRES_OK(ctx, task_info_list.Init());\n      for (int i = 0; i < task_info_vec.size(); ++i) {\n        task_info_list.Set(i, task_info_vec[i]);\n      }\n      OP_REQUIRES_OK(ctx, task_info_list.Finalize());\n\n      OP_REQUIRES_OK(ctx, embeddings_data_list.Init());\n      for (int i = 0; i < embeddings_data.size(); ++i) {\n        embeddings_data_list.Set(i, embeddings_data[i]);\n      }\n      OP_REQUIRES_OK(ctx, embeddings_data_list.Finalize());\n\n      OP_REQUIRES_OK(ctx, each_req_batch_size_list.Init());\n      for (int i = 0; i < each_req_batch_size_offset.size(); ++i) {\n        each_req_batch_size_list.Set(i, each_req_batch_size_offset[i]);\n      }\n      OP_REQUIRES_OK(ctx, each_req_batch_size_list.Finalize());\n\n      OP_REQUIRES_OK(ctx, each_req_nfl_list.Init());\n      for (int i = 0; i < each_req_nfl_offset.size(); ++i) {\n        each_req_nfl_list.Set(i, each_req_nfl_offset[i]);\n      }\n      OP_REQUIRES_OK(ctx, each_req_nfl_list.Finalize());\n\n      OP_REQUIRES_OK(ctx, each_req_feature_list.Init());\n      for (int i = 0; i < each_req_feature_offset.size(); ++i) {\n        each_req_feature_list.Set(i, each_req_feature_offset[i]);\n      }\n      OP_REQUIRES_OK(ctx, each_req_feature_list.Finalize());\n\n      OP_REQUIRES_OK(ctx, each_req_fid_list.Init());\n      for (int i = 0; i < each_req_fid_offset.size(); ++i) {\n        each_req_fid_list.Set(i, each_req_fid_offset[i]);\n      }\n      OP_REQUIRES_OK(ctx, each_req_fid_list.Finalize());\n\n      OP_REQUIRES_OK(ctx, each_req_emb_list.Init());\n      for (int i = 0; i < req_num + 1; ++i) {\n        each_req_emb_list.Set(i, i * each_req_emb_num);\n      }\n      OP_REQUIRES_OK(ctx, each_req_emb_list.Finalize());\n    }\n\n    auto config =\n        GetGpu2DLaunchConfig(batch_size, task_info_vec.size(), gpu_device);\n    GpuLaunchKernel(ForwardBatchKernel, config.block_count,\n                    config.thread_per_block, 0, gpu_device.stream(), config,\n                    embeddings_data_list.data(), fids_offset_vec, total_fid_num,\n                    feature_offset_vec, total_feature_num, nfl_offset_vec,\n                    total_nfl_num, task_info_list.data(),\n                    each_req_batch_size_list.data(), each_req_nfl_list.data(),\n                    each_req_feature_list.data(), each_req_fid_list.data(),\n                    each_req_emb_list.data());\n  }\n};\n\nclass MonolithEmbeddingToLayoutOpV4GPU\n    : public MonolithEmbeddingToLayoutOpV3GPU {\n public:\n  explicit MonolithEmbeddingToLayoutOpV4GPU(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutOpV3GPU(ctx, 4) {}\n};\n\nclass MonolithEmbeddingToLayoutOpV5GPU\n    : public MonolithEmbeddingToLayoutOpV3GPU {\n public:\n  explicit MonolithEmbeddingToLayoutOpV5GPU(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutOpV3GPU(ctx, 5) {}\n};\n\n__global__ void BackwardBatchKernel(\n    const Gpu2DLaunchConfig config,\n    GpuDeviceArrayStruct<ForwardTaskInfo> task_info_list,\n    const uint64 *fids_offset_vec, int total_fid_num,\n    const int32 *feature_offset_vec, int total_feature_num,\n    const uint32 *nfl_offset_vec, int total_nfl_num, int batch_size,\n    GpuDeviceArrayStruct<PtrWrapper> embeddings_grads_data) {\n  const ForwardTaskInfo *task_info_list_ptr =\n      GetGpuDeviceArrayOnDevice(&task_info_list);\n\n  PtrWrapper *embeddings_grads_data_list_ptr =\n      GetGpuDeviceArrayOnDevice(&embeddings_grads_data);\n\n  bool is_shared;\n  int nfl_offset;\n  int feature_num;\n  const ForwardTaskInfo *task_info = nullptr;\n  int feature_idx;\n  int temp_offset;\n\n  GPU_AXIS_KERNEL_LOOP(task_idx, config.virtual_thread_count.y, Y) {\n    task_info = task_info_list_ptr + task_idx;\n\n    GetFeatureInfo(task_info->nfl_idx, nfl_offset_vec, total_nfl_num,\n                   total_feature_num, &is_shared, &nfl_offset, &feature_num);\n\n    if (!feature_num) return;  // nfl exits\n\n    GPU_AXIS_KERNEL_LOOP(batch_idx, config.virtual_thread_count.x, X) {\n      feature_idx = nfl_offset;\n      if (!is_shared) {\n        feature_idx += batch_idx;\n      }\n      temp_offset = batch_idx * task_info->ptr_info.offset;\n\n      ScatterGrad(\n          feature_idx, task_info->max_sequence_length, task_info->pooling_type,\n          task_info->ptr_info.ptr + temp_offset, task_info->dim_num,\n          task_info->start, fids_offset_vec, total_fid_num, feature_offset_vec,\n          total_feature_num, embeddings_grads_data.size,\n          embeddings_grads_data_list_ptr, OptimizedSumpoolingGPUWithLock,\n          nullptr, nullptr, nullptr, nullptr);\n    }\n  }\n}\n\nclass MonolithEmbeddingToLayoutGradOpV3GPU\n    : public MonolithEmbeddingToLayoutGradOp {\n public:\n  explicit MonolithEmbeddingToLayoutGradOpV3GPU(OpKernelConstruction *ctx,\n                                                int verison = 3)\n      : MonolithEmbeddingToLayoutGradOp(ctx, verison) {}\n  void TaskRun(const std::vector<std::shared_ptr<Layout>> &layouts,\n               const std::vector<std::pair<int, int>> *ufid_grads_info,\n               const uint64 *fids_offset_vec, int total_fid_num,\n               const int32 *feature_offset_vec, int total_feature_num,\n               const uint32 *nfl_offset_vec, int total_nfl_num, int batch_size,\n               OpKernelContext *ctx, OpOutputList *embeddings_grad_list,\n               std::vector<PtrWrapper> *embeddings_grads_data, GroupA *init) {\n    GPUDevice gpu_device = ctx->eigen_device<GPUDevice>();\n    SetZeroFunctor<float> zero_functor;\n    for (int32 idx = 0; idx < embeddings_grad_list->size(); ++idx) {\n      zero_functor(gpu_device, (*embeddings_grad_list)[idx]->flat<float>());\n    }\n    std::vector<ForwardTaskInfo> task_info_vec;\n    for (int64 para_i = 0; para_i < layouts.size(); ++para_i) {\n      auto &layout = layouts.at(para_i);\n      // CHECK(end - start == 1);\n      const ::google::protobuf::RepeatedPtrField<SliceConfig>\n          &layout_slice_configs = layout->GetSliceConfig();\n      for (const SliceConfig &slice_conf : layout_slice_configs) {\n        int dim_num = slice_conf.end() - slice_conf.start();\n        PtrWrapper ptr_info = layout->GetSlice(0, slice_conf);\n        const int64 nfl_idx = slice_conf.feature_idx();\n        task_info_vec.push_back(ForwardTaskInfo(\n            {dim_num, ptr_info, nfl_idx, layout->out_type(),\n             slice_conf.pooling_type(), slice_conf.max_sequence_length(),\n             slice_conf.start()}));\n      }\n    }\n    GpuDeviceArrayOnHost<ForwardTaskInfo> task_info_list(ctx,\n                                                         task_info_vec.size());\n    OP_REQUIRES_OK(ctx, task_info_list.Init());\n    for (int i = 0; i < task_info_vec.size(); ++i) {\n      task_info_list.Set(i, task_info_vec[i]);\n    }\n    OP_REQUIRES_OK(ctx, task_info_list.Finalize());\n\n    GpuDeviceArrayOnHost<PtrWrapper> embeddings_grads_data_list(\n        ctx, embeddings_grads_data->size());\n    OP_REQUIRES_OK(ctx, embeddings_grads_data_list.Init());\n    for (int i = 0; i < embeddings_grads_data->size(); ++i) {\n      embeddings_grads_data_list.Set(i, (*embeddings_grads_data)[i]);\n    }\n    OP_REQUIRES_OK(ctx, embeddings_grads_data_list.Finalize());\n\n    auto config =\n        GetGpu2DLaunchConfig(batch_size, task_info_vec.size(), gpu_device);\n    GpuLaunchKernel(\n        BackwardBatchKernel, config.block_count, config.thread_per_block, 0,\n        gpu_device.stream(), config, task_info_list.data(), fids_offset_vec,\n        total_fid_num, feature_offset_vec, total_feature_num, nfl_offset_vec,\n        total_nfl_num, batch_size, embeddings_grads_data_list.data());\n  }\n};\n\nclass MonolithEmbeddingToLayoutGradOpV4GPU\n    : public MonolithEmbeddingToLayoutGradOpV3GPU {\n public:\n  explicit MonolithEmbeddingToLayoutGradOpV4GPU(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutGradOpV3GPU(ctx, 4) {}\n};\n\nclass MonolithEmbeddingToLayoutGradOpV5GPU\n    : public MonolithEmbeddingToLayoutGradOpV3GPU {\n public:\n  explicit MonolithEmbeddingToLayoutGradOpV5GPU(OpKernelConstruction *ctx)\n      : MonolithEmbeddingToLayoutGradOpV3GPU(ctx, 5) {}\n};\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayoutV3\")\n                            .Device(DEVICE_GPU)\n                            .HostMemory(\"batch_size\"),\n                        MonolithEmbeddingToLayoutOpV3GPU);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayoutGradV3\")\n                            .Device(DEVICE_GPU)\n                            .HostMemory(\"batch_size\"),\n                        MonolithEmbeddingToLayoutGradOpV3GPU);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayoutV4\")\n                            .Device(DEVICE_GPU)\n                            .HostMemory(\"batch_size\")\n                            .HostMemory(\"fid_list_emb_row_lenth\"),\n                        MonolithEmbeddingToLayoutOpV4GPU);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayoutGradV4\")\n                            .Device(DEVICE_GPU)\n                            .HostMemory(\"batch_size\")\n                            .HostMemory(\"fid_list_emb_row_lenth\"),\n                        MonolithEmbeddingToLayoutGradOpV4GPU);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayoutV5\")\n                            .Device(DEVICE_GPU)\n                            .HostMemory(\"batch_size\")\n                            .HostMemory(\"nfl_size\")\n                            .HostMemory(\"feature_size\")\n                            .HostMemory(\"fid_size\")\n                            .HostMemory(\"emb_size\"),\n                        MonolithEmbeddingToLayoutOpV5GPU);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithEmbeddingToLayoutGradV5\")\n                            .Device(DEVICE_GPU)\n                            .HostMemory(\"batch_size\"),\n                        MonolithEmbeddingToLayoutGradOpV5GPU);\n\n}  // namespace fused_layout\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // GOOGLE_CUDA\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/fused_embedding_to_layout.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstring>\n#include <tuple>  // for tuple\n#include \"absl/strings/str_cat.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n#include \"tensorflow/core/util/work_sharder.h\"\n\n#include \"idl/matrix/proto/example.pb.h\"\n#include \"monolith/native_training/data/training_instance/cc/pb_variant.h\"\n#include \"monolith/native_training/runtime/hash_table/optimizer/avx_utils.h\"\n#include \"tensorflow/core/profiler/lib/traceme.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nnamespace fused_layout {\nusing FidList = ::monolith::io::proto::FidList;\nusing Example = ::monolith::io::proto::Example;\nusing ExampleBatch = ::monolith::io::proto::ExampleBatch;\nusing FeatureConfigs = ::monolith::io::proto::FeatureConfigs;\nusing PoolingType = ::monolith::io::proto::PoolingType;\nusing OutType = ::monolith::io::proto::OutType;\nusing SliceConfig = ::monolith::io::proto::SliceConfig;\nusing LayoutShape = ::monolith::io::proto::TensorShape;\nusing OutConfig = ::monolith::io::proto::OutConfig;\nusing Feature = ::monolith::io::proto::Feature;\nusing FeatureListType = ::monolith::io::proto::FeatureListType;\nusing NamedFeatureList = ::monolith::io::proto::NamedFeatureList;\n\nusing MiniBatch = std::unordered_map<std::string, std::vector<const Feature *>>;\nusing Fid2EmbIdxMap = std::unordered_map<int64, std::pair<int, int>>;\nusing Fid2EmbMap = std::unordered_map<int64, const float *>;\n\nEIGEN_ALWAYS_INLINE EIGEN_DEVICE_FUNC void ParseFidOffset(\n    const uint64 &fids_offset, int32 *index1, int32 *index2) {\n  *index1 = fids_offset >> 32;\n  *index2 = fids_offset << 32 >> 32;\n}\n\nEIGEN_ALWAYS_INLINE EIGEN_DEVICE_FUNC void ParseNflOffset(\n    const uint32 nfl_offset_encode, bool *is_shared, int *nfl_offset) {\n  *is_shared = nfl_offset_encode >> 31;\n  *nfl_offset = nfl_offset_encode & 0x7fffffff;\n}\n\nEIGEN_ALWAYS_INLINE EIGEN_DEVICE_FUNC void GetFeatureInfo(\n    const int64 nfl_idx, const uint32 *nfl_offset_vec, const int total_nfl_num,\n    const int total_feature_num, bool *is_shared, int *nfl_offset,\n    int *feature_num) {\n  ParseNflOffset(*(nfl_offset_vec + nfl_idx), is_shared, nfl_offset);\n  if (nfl_idx < total_nfl_num - 1) {\n    bool is_shared_later;\n    int nfl_offset_later;\n    ParseNflOffset(*(nfl_offset_vec + nfl_idx + 1), &is_shared_later,\n                   &nfl_offset_later);\n    *feature_num = nfl_offset_later - *nfl_offset;\n  } else {\n    *feature_num = total_feature_num - *nfl_offset;\n  }\n}\n\nstruct PtrWrapper {\n  const float *ptr;\n  uint offset;\n  uint count;\n};\n\nstruct GroupA {\n  GroupA(int dim1, int dim2) : b(dim1 * dim2, true), dim_1(dim1), dim_2(dim2) {}\n  char *Get(int dim1, int dim2) { return &(b.at(dim1 * dim_2 + dim2)); }\n  std::vector<char> b;\n  int dim_1;\n  int dim_2;\n};\n\nclass Layout {\n public:\n  Layout(const std::string &name, const OutConfig &out_conf)\n      : name_(name), out_config_(out_conf) {}\n\n  virtual ~Layout() {}\n\n  virtual PtrWrapper GetSlice(int row_id, const SliceConfig &slice_conf) = 0;\n\n  const SliceConfig *GetKey(const SliceConfig &slice_conf) {\n    return &slice_conf;\n    // return absl::StrCat(name_, \"_\", slice_conf.feature_name(), \"_\",\n    //                     slice_conf.start(), \"_\", slice_conf.end());\n  }\n\n  const ::google::protobuf::RepeatedPtrField<SliceConfig> &GetSliceConfig() {\n    return out_config_.slice_configs();\n  }\n\n  const OutType out_type() { return out_config_.out_type(); }\n\n protected:\n  const std::string &name_;\n  const OutConfig &out_config_;\n};\n\nclass NoneLayout : public Layout {\n public:\n  // op input TODO\n  NoneLayout(const std::string &name, const OutConfig &out_conf,\n             OpInputList &tensor_list, int &start_idx);\n\n  // op output\n  NoneLayout(const std::string &name, const OutConfig &out_conf,\n             OpOutputList &tensor_list, int &start_idx);\n\n  virtual ~NoneLayout() {}\n\n  PtrWrapper GetSlice(int row_id, const SliceConfig &slice_conf) override;\n\n private:\n  absl::flat_hash_map<const SliceConfig *, std::pair<const Tensor *, const int>>\n      slice_to_tensor_;\n};\n\nclass DefaultLayout : public Layout {\n public:\n  DefaultLayout(const std::string &name, const OutConfig &out_conf,\n                OpInputList &tensor_list, int &start_idx);\n\n  DefaultLayout(const std::string &name, const OutConfig &out_conf,\n                OpOutputList &tensor_list, int &start_idx);\n\n  virtual ~DefaultLayout() {}\n\n  PtrWrapper GetSlice(int row_id, const SliceConfig &slice_conf) override;\n\n private:\n  absl::flat_hash_map<const SliceConfig *, std::pair<const Tensor *, const int>>\n      slice_to_tensor_;\n};\n\nclass MonolithEmbeddingToLayoutBase : public OpKernel {\n public:\n  explicit MonolithEmbeddingToLayoutBase(OpKernelConstruction *ctx,\n                                         int version);\n\n private:\n  std::string variant_type_;\n  FeatureConfigs feature_cfgs_;\n  std::vector<std::string> layout_names_;\n  int max_slice_num_ = 0;\n  std::vector<std::vector<int>> table_feature_dim_;\n  int ps_num_ = 0;\n  int parallel_flag_ = 0;\n  int version_ = 1;\n\n protected:\n  int GetMaxSliceNum() { return max_slice_num_; }\n  const std::string &GetVariantType() { return variant_type_; }\n  const std::vector<std::string> &GetLayoutNames() { return layout_names_; }\n  const FeatureConfigs &GetFeatureCfgs() { return feature_cfgs_; }\n  int GetPsNum() { return ps_num_; }\n  int GetParallelFlag() { return parallel_flag_; }\n  int GetVersion() { return version_; }\n  const std::vector<std::vector<int>> &GetFeatureInTableDim() {\n    return table_feature_dim_;\n  }\n};\n\n#ifdef EIGEN_USE_GPU\n#define CUSTOM_CHECK(cond)                                               \\\n  if (!(cond)) {                                                         \\\n    printf(\"ERROR %s %s:%d CHECK Fail\\n\", __FILE__, __func__, __LINE__); \\\n    return;                                                              \\\n  }\n#else\n#define CUSTOM_CHECK(cond) CHECK(cond)\n#endif\n\ntypedef void (*OptimizedSumpoolingFunc)(const float *src, const int dim_num,\n                                        void *init, float *dst, void *one_mutex,\n                                        int mean_pool_fid_num);\n\ntypedef void *(MemCopyFunc)(float *dest, const float *src, std::size_t count);\ntypedef void *(GetMutexFunc)(void *main_params, int32 index1, int32 index2);\ntypedef void *(GetInitFunc)(void *main_params, int32 index1, int32 index2);\nEIGEN_ALWAYS_INLINE EIGEN_DEVICE_FUNC void *DefaultGetInitFunc(\n    void *main_params, int32 index1, int32 index2) {\n  return main_params;\n}\n\nEIGEN_ALWAYS_INLINE EIGEN_DEVICE_FUNC void GatherEmb(\n    const int feature_idx, const int max_sequence_length,\n    const PoolingType pooling_type, int dims, int slice_conf_start,\n    const PtrWrapper *embeddings_data, int embeddings_data_size,\n    const uint64 *fids_offset_vec, const int total_fid_num,\n    const int32 *feature_offset_vec, const int total_feature_num,\n    float *out_ptr, OptimizedSumpoolingFunc opt_sumpool_fn,\n    MemCopyFunc mem_copy_fn, GetMutexFunc get_mutex_func,\n    void *get_mutex_func_main_params, GetInitFunc get_init_func,\n    void *get_init_func_main_params) {\n  CUSTOM_CHECK(feature_idx < total_feature_num);\n  int fid_num = (feature_idx < total_feature_num - 1)\n                    ? *(feature_offset_vec + feature_idx + 1) -\n                          *(feature_offset_vec + feature_idx)\n                    : total_fid_num - *(feature_offset_vec + feature_idx);\n  CUSTOM_CHECK(fid_num >= 0);\n  if (fid_num == 0) return;\n  const auto start_fid_offset_idx = *(feature_offset_vec + feature_idx);\n\n  int seq_idx = 0;\n\n  for (int fid_idx = 0; fid_idx < fid_num; fid_idx++) {\n    auto fid_offset_idx = start_fid_offset_idx + fid_idx;\n    int32 index1, index2;\n    CUSTOM_CHECK(fid_offset_idx < total_fid_num);\n    const uint64 fids_offset = *(fids_offset_vec + fid_offset_idx);\n    ParseFidOffset(fids_offset, &index1, &index2);\n    CUSTOM_CHECK(index1 < embeddings_data_size);\n    const auto &ptr_info = *(embeddings_data + index1);\n    int tmp_offset = index2 * ptr_info.offset + slice_conf_start;\n    CUSTOM_CHECK(tmp_offset + dims <= ptr_info.count);\n    const float *src = ptr_info.ptr + tmp_offset;\n    void *one_mutex = nullptr;\n    if (get_mutex_func) {\n      one_mutex = get_mutex_func(get_mutex_func_main_params, index1, index2);\n    }\n    void *init = nullptr;\n    if (get_init_func) {\n      init = get_init_func(get_init_func_main_params, index1, index2);\n    }\n    switch (pooling_type) {\n      case PoolingType::SUM:\n        opt_sumpool_fn(src, dims, init, out_ptr, one_mutex, 0);\n        break;\n      case PoolingType::MEAN:\n        opt_sumpool_fn(src, dims, init, out_ptr, one_mutex, fid_num);\n        break;\n      case PoolingType::FIRSTN:\n        if (seq_idx < max_sequence_length) {\n          mem_copy_fn(out_ptr + seq_idx * dims, src, dims);\n        }\n        seq_idx++;\n        break;\n      default:\n        break;\n    }\n  }\n}\n\nclass MonolithEmbeddingToLayoutOp : public MonolithEmbeddingToLayoutBase {\n public:\n  explicit MonolithEmbeddingToLayoutOp(OpKernelConstruction *ctx,\n                                       int version = 1);\n\n  void Compute(OpKernelContext *ctx) override;\n  virtual void TaskRun(const std::vector<std::shared_ptr<Layout>> &layouts,\n                       const std::vector<PtrWrapper> &embeddings_data,\n                       const uint64 *fids_offset_vec, int total_fid_num,\n                       const int32 *feature_offset_vec, int total_feature_num,\n                       const uint32 *nfl_offset_vec, int total_nfl_num,\n                       int batch_size,\n                       const std::vector<int> &each_req_batch_size_offset,\n                       const std::vector<int> &each_req_nfl_offset,\n                       const std::vector<int> &each_req_feature_offset,\n                       const std::vector<int> &each_req_fid_offset, int req_num,\n                       OpKernelContext *ctx, OpOutputList *layout_tensor_list);\n\n private:\n  int req_sum_ = 0;\n  int process_num_ = 0;\n};\n\nEIGEN_ALWAYS_INLINE EIGEN_DEVICE_FUNC void ScatterGrad(\n    const int feature_idx, const int max_sequence_length,\n    const PoolingType pooling_type, const float *grad_ptr, int dims,\n    int slice_conf_start, const uint64 *fids_offset_vec,\n    const int total_fid_num, const int32 *feature_offset_vec,\n    const int total_feature_num, const int embeddings_grads_data_num,\n    PtrWrapper *embeddings_grads_data, OptimizedSumpoolingFunc opt_sumpool_fn,\n    GetMutexFunc get_mutex_func, void *get_mutex_func_main_params,\n    GetInitFunc get_init_func, void *get_init_func_main_params) {\n  CUSTOM_CHECK(feature_idx < total_feature_num);\n  int fid_num = (feature_idx < total_feature_num - 1)\n                    ? *(feature_offset_vec + feature_idx + 1) -\n                          *(feature_offset_vec + feature_idx)\n                    : total_fid_num - *(feature_offset_vec + feature_idx);\n  CUSTOM_CHECK(fid_num >= 0);\n  if (fid_num == 0) return;\n  const auto start_fid_offset_idx = *(feature_offset_vec + feature_idx);\n  int seq_idx = 0;\n\n  for (int fid_idx = 0; fid_idx < fid_num; fid_idx++) {\n    int32 fid_offset_idx = start_fid_offset_idx + fid_idx;\n    int32 index1, index2;\n    CUSTOM_CHECK(fid_offset_idx < total_fid_num);\n    const uint64 fids_offset = *(fids_offset_vec + fid_offset_idx);\n    ParseFidOffset(fids_offset, &index1, &index2);\n    CUSTOM_CHECK(index1 < embeddings_grads_data_num);\n    // embeddings_grads_data: fid grad data\n    const auto &ptr_info = *(embeddings_grads_data + index1);\n    int tmp_offset = index2 * ptr_info.offset + slice_conf_start;\n    CUSTOM_CHECK(tmp_offset + dims <= ptr_info.count);\n    void *one_mutex = nullptr;\n    if (get_mutex_func) {\n      one_mutex = get_mutex_func(get_mutex_func_main_params, index1, index2);\n    }\n    void *init_p = nullptr;\n    if (get_init_func) {\n      init_p = get_init_func(get_init_func_main_params, index1, index2);\n    }\n    float *dst = const_cast<float *>(ptr_info.ptr) + tmp_offset;\n    switch (pooling_type) {\n      case PoolingType::SUM: {\n        opt_sumpool_fn(grad_ptr, dims, init_p, dst, one_mutex, 0);\n        break;\n      }\n      case PoolingType::MEAN: {\n        opt_sumpool_fn(grad_ptr, dims, init_p, dst, one_mutex, fid_num);\n        break;\n      }\n      case PoolingType::FIRSTN: {\n        if (seq_idx < max_sequence_length) {\n          opt_sumpool_fn(grad_ptr + seq_idx * dims, dims, init_p, dst,\n                         one_mutex, false);\n        }\n        seq_idx++;\n        break;\n      }\n\n      default:\n        break;\n    }\n  }\n}\n\nclass MonolithEmbeddingToLayoutGradOp : public MonolithEmbeddingToLayoutBase {\n public:\n  explicit MonolithEmbeddingToLayoutGradOp(OpKernelConstruction *ctx,\n                                           int version = 1);\n\n  void Compute(OpKernelContext *ctx) override;\n  virtual void TaskRun(const std::vector<std::shared_ptr<Layout>> &layouts,\n                       const std::vector<std::pair<int, int>> *ufid_grads_info,\n                       const uint64 *fids_offset_vec, int total_fid_num,\n                       const int32 *feature_offset_vec, int total_feature_num,\n                       const uint32 *nfl_offset_vec, int total_nfl_num,\n                       int batch_size, OpKernelContext *ctx,\n                       OpOutputList *embeddings_grad_list,\n                       std::vector<PtrWrapper> *embeddings_grads_data,\n                       GroupA *init);\n};\n\n}  // namespace fused_layout\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/fused_reorder_by_indices.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <chrono>\n#include <cstring>\n\n#include \"absl/algorithm/container.h\"\n#include \"absl/container/flat_hash_map.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/lib/core/threadpool.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntemplate <class T>\nclass FusedReorderByIndicesOp : public OpKernel {\n public:\n  explicit FusedReorderByIndicesOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"rank0_empty\", &rank0_empty_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"num_of_shards\", &num_shards_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"M\", &num_tables_));\n    OP_REQUIRES_OK(ctx,\n                   ctx->GetAttr(\"slot_embedding_dims\", &slot_embedding_dims_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    // auto start = std::chrono::steady_clock::now();\n    std::vector<typename absl::flat_hash_map<T, int>> ids_sets(num_tables_);\n    std::vector<std::vector<T>> ids_for_splits(num_tables_ * num_shards_);\n    int total_fids = 0;\n    for (int m = 0; m < num_tables_; ++m) {\n      auto data = ctx->input(m).vec<T>().data();\n      auto sz = ctx->input(m).NumElements();\n      total_fids += sz;\n      // Performance critical: reserve enough space so ids_sets won't rehash\n      ids_sets[m].reserve(sz);\n      for (int n = 0; n < num_shards_; n++)\n        // reserve so ids_for_splits will **most likely** not reallocate\n        ids_for_splits[n * num_tables_ + m].reserve((sz + sz / 4) /\n                                                    num_shards_);\n\n      int dim = slot_embedding_dims_[m];\n      for (int i = 0; i < sz; ++i) {\n        auto val = data[i];\n        auto& vec = ids_for_splits[shard_func(val) * num_tables_ + m];\n        if (ids_sets[m].insert({val, vec.size() * dim}).second)\n          vec.push_back(val);\n      }\n    }\n\n    Tensor *output, *shard_sizes, *sharded_slot_sizes;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(1, {num_shards_}, &shard_sizes));\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(2, {num_shards_ * num_tables_},\n                                            &sharded_slot_sizes));\n    auto shard_sizes_vec = shard_sizes->vec<int32>();\n    auto sharded_slot_sizes_vec = sharded_slot_sizes->vec<int32>();\n    shard_sizes_vec.setZero();\n\n    int uniq_id_size = 0;\n    int emb_offset = 0;\n    // compute a column major order emb_offsets for better cache\n    std::vector<int> emb_offsets_cm(num_tables_ * num_shards_);\n    for (int n = 0; n < num_shards_; n++) {\n      for (int m = 0; m < num_tables_; ++m) {\n        auto idx = n * num_tables_ + m;\n        auto sz = ids_for_splits[idx].size();\n        sharded_slot_sizes_vec(idx) = sz;\n        shard_sizes_vec(n) += sz;\n        uniq_id_size += sz;\n        emb_offsets_cm[m * num_shards_ + n] = emb_offset;\n        emb_offset += sz * slot_embedding_dims_[m];\n      }\n    }\n\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {uniq_id_size}, &output));\n    auto output_ptr = output->vec<T>().data();\n    for (const auto& vec : ids_for_splits) {\n      std::memcpy(output_ptr, vec.data(), sizeof(T) * vec.size());\n      output_ptr += vec.size();\n    }\n\n    Tensor *emb_offset_sz, *fused_emb_offset;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(3, {num_tables_}, &emb_offset_sz));\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(4, {total_fids}, &fused_emb_offset));\n    auto emb_offset_sz_vec = emb_offset_sz->vec<int32>().data();\n    auto fused_emb_offset_vec = fused_emb_offset->vec<int32>().data();\n\n    total_fids = 0;\n    for (int m = 0; m < num_tables_; ++m) {\n      auto sz = ctx->input(m).NumElements();\n      auto data = ctx->input(m).vec<T>().data();\n      emb_offset_sz_vec[m] = sz;\n      for (int i = 0; i < sz; ++i) {\n        auto val = data[i];\n        fused_emb_offset_vec[total_fids + i] =\n            ids_sets[m][val] +\n            emb_offsets_cm[shard_func(val) + m * num_shards_];\n      }\n      total_fids += sz;\n    }\n    // std::cout << \"fused reorder took \"\n    //           << (std::chrono::steady_clock::now() - start).count() * 1e-9\n    //           << std::endl;\n  }\n  // TODO(hanzhizhou): consider precompute this or add specialization for\n  // rank0_empty=false and num_shards=power of 2. Currently 10% of the total\n  // time is spent on this function during the computation of this OP\n  inline int shard_func(int64 val) {\n    return val % (num_shards_ - rank0_empty_) + rank0_empty_;\n  }\n\n private:\n  bool rank0_empty_;\n  int num_shards_;\n  int num_tables_;\n  std::vector<int32> slot_embedding_dims_;\n};\n\nREGISTER_OP(\"FusedReorderByIndices\")\n    .Input(\"input: M * T\")\n    .Output(\"output: T\")\n    .Output(\"shard_sizes: int32\")\n    .Output(\"sharded_slot_sizes: int32\")\n    .Output(\"emb_offset_sz: int32\")\n    .Output(\"fused_emb_offset: int32\")\n    .Attr(\"num_of_shards: int\")\n    .Attr(\"slot_embedding_dims: list(int)\")\n    .Attr(\"rank0_empty: bool\")\n    .Attr(\"M: int\")\n    .Attr(\"T: type\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle input;\n      std::vector<shape_inference::DimensionHandle> dim_handles;\n      dim_handles.push_back(c->UnknownDim());\n\n      // The WithRank call validates that the input shape c->input(0)\n      //  has a shape with exactly one dimension.\n      TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 1, &input));\n\n      // The first output is the tensor in shape (?,)\n      // It contains deduped reordered ids.\n      c->set_output(0, c->MakeShape(dim_handles));\n\n      // The second output is the tensor in shape (num_of_shards,)\n      // It contains shard sizes.\n      int num_of_shards;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"num_of_shards\", &num_of_shards));\n      c->set_output(1, c->MakeShape({num_of_shards}));\n\n      // The third output is the tensor in shape (num_of_shards*M,)\n      //  where M is the number of type T input.\n      // It contains sharded (merged) slot sizes.\n      int M;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"M\", &M));\n      c->set_output(2, c->MakeShape({num_of_shards * M}));\n\n      // The fourth output is an array of offsets to the fifth output\n      c->set_output(3, c->Vector(M));\n      c->set_output(4, c->Vector(c->UnknownDim()));\n      return Status::OK();\n    });\n\n#define REGISTER_KERNEL_FUSED_REORDER_BY_INDICES(type)    \\\n  REGISTER_KERNEL_BUILDER(Name(\"FusedReorderByIndices\")   \\\n                              .Device(DEVICE_CPU)         \\\n                              .TypeConstraint<type>(\"T\"), \\\n                          FusedReorderByIndicesOp<type>)\n\nREGISTER_KERNEL_FUSED_REORDER_BY_INDICES(int64);\n\n#undef REGISTER_KERNEL_FUSED_REORDER_BY_INDICES\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/gen_monolith_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training.runtime.ops.gen_monolith_ops_base import *\nfrom monolith import utils\n\ntf.load_library(\n    utils.get_libops_path(\n        \"monolith/native_training/runtime/ops/libtfkernel_monolith_ops_for_load.so\"\n    ))\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/gen_seq_mask.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstring>\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\n// The difference between this reduce sum op and tf.sparse.reduce_sum is that\n// this supports sparse values which are vectors.\ntemplate <typename T>\nclass GenSeqMaskOp : public OpKernel {\n public:\n  explicit GenSeqMaskOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"max_seq_length\", &max_seq_length_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& splits = ctx->input(0);\n    int64 batch_size = splits.dim_size(0) - 1;\n    Tensor* mask = nullptr;\n    OP_REQUIRES_OK(\n        ctx, ctx->allocate_output(0, {batch_size, max_seq_length_}, &mask));\n    std::memset(mask->data(), 0, mask->AllocatedBytes());\n\n    auto splits_flat = splits.flat<T>();\n    auto mask_mat = mask->matrix<T>();\n    for (int64 i = 0; i < batch_size; ++i) {\n      T size = splits_flat(i + 1) - splits_flat(i);\n      size = size > max_seq_length_ ? max_seq_length_ : size;\n      for (size_t j = 0; j < size; ++j) mask_mat(i, j) = 1;\n    }\n  }\n\n private:\n  int max_seq_length_;\n};\n\nREGISTER_OP(\"GenSeqMask\")\n    .Input(\"splits: T\")\n    .Output(\"mask: T\")\n    .Attr(\"max_seq_length: int\")\n    .Attr(\"T: {int32, int64}\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      int max_seq_length;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"max_seq_length\", &max_seq_length));\n      if (ctx->FullyDefined(ctx->input(0))) {\n        tensorflow::shape_inference::DimensionHandle batch_size;\n        tensorflow::shape_inference::DimensionHandle input_dim =\n            ctx->Dim(ctx->input(0), 0);\n        TF_RETURN_IF_ERROR(\n            ctx->Subtract(input_dim, ctx->MakeDim(1), &batch_size));\n        ctx->set_output(\n            0, ctx->MakeShape({batch_size, ctx->MakeDim(max_seq_length)}));\n      } else {\n        ctx->set_output(0, ctx->MakeShape({ctx->UnknownDim(),\n                                           ctx->MakeDim(max_seq_length)}));\n      }\n\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"GenSeqMask\").Device(DEVICE_CPU).TypeConstraint<int32>(\"T\"),\n    GenSeqMaskOp<int32>);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"GenSeqMask\").Device(DEVICE_CPU).TypeConstraint<int64>(\"T\"),\n    GenSeqMaskOp<int64>);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/global_norm.cu.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#if GOOGLE_CUDA\n#define EIGEN_USE_GPU\n\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/register_types.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/kernels/gpu_device_array.h\"\n#include \"tensorflow/core/kernels/gpu_device_array_gpu.h\"\n#include \"tensorflow/core/kernels/gpu_prim.h\"\n#include \"tensorflow/core/util/gpu_kernel_helper.h\"\n\nnamespace tensorflow {\nnamespace monolith {\n\nnamespace {  // Seperate for CUDA Kernel Def\n\ntemplate <int BLOCK_THREADS>\n__global__ void globalReduceSum(\n    GpuDeviceArrayStruct<const float*> input_ptrs_da,\n    GpuDeviceArrayStruct<int> offsets_da, float* out, int size) {\n  const float** input_ptrs = GetGpuDeviceArrayOnDevice(&input_ptrs_da);\n  int* offsets = GetGpuDeviceArrayOnDevice(&offsets_da);\n\n  // if using shared memory\n  // Ref:\n  // https://github.com/tensorflow/tensorflow/blob/v2.4.0/tensorflow/core/kernels/concat_lib_gpu_impl.cu.cc#L73\n  GPU_DYNAMIC_SHARED_MEM_DECL(sizeof(int), unsigned char, smem);\n  int* smem_offsets = reinterpret_cast<int*>(smem);\n  for (int x = threadIdx.x; x < offsets_da.size; x += blockDim.x) {\n    smem_offsets[x] = offsets[x];\n  }\n  __syncthreads();\n  offsets = smem_offsets;\n\n  float thread_sum = 0;\n  int i = 0;\n  GPU_1D_KERNEL_LOOP(idx, size) {\n    // safe offsets read: when idx == size - 1, i+1 == num_inputs\n    while (offsets[i + 1] <= idx) ++i;\n    int j = idx - offsets[i];\n    float v = ldg(input_ptrs[i] + j);\n    thread_sum += v * v;  // l2\n  }\n  // thread reduce sum to block reduce sum\n  typedef gpuprim::BlockReduce<float, BLOCK_THREADS> BlockReduce;\n  __shared__ typename BlockReduce::TempStorage temp_storage;\n  float block_sum = BlockReduce(temp_storage).Sum(thread_sum);\n  __syncthreads();\n  if (threadIdx.x == 0)\n    // block reduce sum to global reduce sum\n    atomicAdd(out, block_sum);\n}\n\n}  // namespace\n\ntemplate <typename Device>\nstruct GlobalReduceImpl {\n  static void Compute(OpKernelContext* context,\n                      const std::vector<const float*>& input_ptrs,\n                      const std::vector<int>& input_lens,\n                      const std::vector<float*>& output_ptrs, float global_norm,\n                      float clip_norm);\n};\n\ntypedef Eigen::GpuDevice GPUDevice;\n\ntemplate <typename T>\nstruct SetZeroFunctor {\n  void operator()(const GPUDevice& d, typename TTypes<T>::Scalar out) {\n    To32Bit(out).device(d) = To32Bit(out).constant(T(0));\n  }\n};\n\ntemplate <>\nstruct GlobalReduceImpl<GPUDevice> {\n  static void Compute(OpKernelContext* context,\n                      const std::vector<const float*>& input_ptrs,\n                      const std::vector<int>& input_lens,\n                      TTypes<float>::Scalar output) {\n    GPUDevice gpu_device = context->eigen_device<GPUDevice>();\n    int num_inputs = input_ptrs.size();\n\n    GpuDeviceArrayOnHost<const float*> input_ptrs_da(context, num_inputs);\n    OP_REQUIRES_OK(context, input_ptrs_da.Init());\n    for (int i = 0; i < num_inputs; ++i) {\n      input_ptrs_da.Set(i, input_ptrs[i]);\n    }\n    OP_REQUIRES_OK(context, input_ptrs_da.Finalize());\n\n    int offset = 0;\n    GpuDeviceArrayOnHost<int> offsets(context, num_inputs + 1);\n    int smem_usage = sizeof(int) * (num_inputs + 1);\n    OP_REQUIRES_OK(context, offsets.Init());\n    for (int i = 0; i < num_inputs; ++i) {\n      offsets.Set(i, offset);\n      offset += input_lens[i];\n    }\n    offsets.Set(num_inputs, offset);  // offset val here is total workload\n    OP_REQUIRES_OK(context, offsets.Finalize());\n\n    SetZeroFunctor<float> zero_functor;\n    zero_functor(gpu_device, output);\n\n    const int thread_per_block = 1024;  // const int for globalReduceSum\n    const int physical_thread_count =\n        std::min(gpu_device.getNumGpuMultiProcessors() *\n                     gpu_device.maxGpuThreadsPerMultiProcessor(),\n                 offset);\n    const int block_count =\n        std::min(DivUp(physical_thread_count, thread_per_block),\n                 gpu_device.getNumGpuMultiProcessors());\n    TF_CHECK_OK(GpuLaunchKernel(globalReduceSum<thread_per_block>, block_count,\n                                thread_per_block, smem_usage,\n                                gpu_device.stream(), input_ptrs_da.data(),\n                                offsets.data(), output.data(), offset));\n  }\n};\n\ntemplate <typename Device>\nclass GlobalReduce : public OpKernel {\n public:\n  explicit GlobalReduce(OpKernelConstruction* context) : OpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"N\", &num_inputs_));\n  }\n\n  void Compute(OpKernelContext* context) override {\n    VLOG(1) << \"In GlobalReduce Computation\";\n\n    auto num_inputs = context->num_inputs();\n    std::vector<const float*> input_ptrs(num_inputs);\n    std::vector<int> input_lens(num_inputs);\n    for (int i = 0; i < num_inputs; ++i) {\n      input_ptrs[i] = context->input(i).flat<float>().data();\n      input_lens[i] = context->input(i).NumElements();\n    }\n\n    Tensor* out;\n    OP_REQUIRES_OK(context, context->allocate_output(0, TensorShape({}), &out));\n    GlobalReduceImpl<Device>::Compute(context, input_ptrs, input_lens,\n                                      out->scalar<float>().data());\n  }\n\n private:\n  int num_inputs_;\n};\n\nREGISTER_OP(\"GlobalL2Reduce\")\n    .Input(\"grad_list: N * float\")\n    .Output(\"global_norm: float\")\n    .Attr(\"N: int\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"GlobalL2Reduce\").Device(DEVICE_GPU),\n                        GlobalReduce<GPUDevice>);\n\n}  // namespace monolith\n}  // namespace tensorflow\n\n#endif  // GOOGLE_CUDA\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/gpu_multi_hash_table.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_GPU_MULTI_HASH_TABLE\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_GPU_MULTI_HASH_TABLE\n#ifdef GOOGLE_CUDA\n#define EIGEN_USE_GPU\n#include \"monolith/native_training/runtime/hash_table/GPUcucohash/cuco_multi_table_ops.cuh.h\"\n#include \"monolith/native_training/runtime/ops/multi_hash_table.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass GpuMultiHashTable : public MultiHashTable {\n public:\n  ::monolith::hash_table::CucoMultiHashTableOp op;\n  explicit GpuMultiHashTable(\n      absl::string_view shared_name, std::vector<int> slot_occ = {},\n      ::monolith::hash_table::GpucucoEmbeddingHashTableConfig config = {},\n      cudaStream_t stream = 0)\n      : MultiHashTable(shared_name),\n        op(std::move(slot_occ), std::move(config), stream) {}\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_GPU_MULTI_HASH_TABLE\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_filter_intercept_gradient_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/hash_filter/sliding_hash_filter.h\"\n#include \"monolith/native_training/runtime/ops/hash_filter_tf_bridge.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass HashFilterInterceptGradientOp : public OpKernel {\n public:\n  explicit HashFilterInterceptGradientOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    ctx->set_output(0, ctx->input(2));\n  }\n\n  int threshold_;\n};\n\nclass HashFilterInterceptGradientGradientOp : public OpKernel {\n public:\n  explicit HashFilterInterceptGradientGradientOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    HashFilterTfBridge* filter = nullptr;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &filter));\n    core::ScopedUnref unref(filter);\n    const Tensor& ids = ctx->input(1);\n    auto ids_vec = ids.vec<int64>();\n    const Tensor& grad = ctx->input(2);\n    TensorShape grad_shape(\n        {ids.NumElements(), grad.NumElements() / ids.NumElements()});\n    auto grad_mat = ctx->input(2).shaped<float, 2>(grad_shape.dim_sizes());\n    Tensor* filtered_grad;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, grad_shape, &filtered_grad));\n    auto filtered_grad_mat =\n        filtered_grad->shaped<float, 2>(grad_shape.dim_sizes());\n    for (int i = 0; i < ids_vec.dimension(0); ++i) {\n      if (filter->ShouldBeFiltered(ids_vec(i))) {\n        for (int j = 0; j < grad_shape.dim_size(1); ++j) {\n          filtered_grad_mat(i, j) = 0;\n        }\n      } else {\n        filtered_grad_mat.chip<0>(i) = grad_mat.chip<0>(i);\n      }\n    }\n  }\n};\n\nREGISTER_OP(\"MonolithHashFilterInterceptGradient\")\n    .Input(\"filter_handle: resource\")\n    .Input(\"ids: int64\")\n    .Input(\"embeddings: float\")\n    .Output(\"same_embeddings: float\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      c->set_output(0, c->input(2));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithHashFilterInterceptGradient\").Device(DEVICE_CPU),\n    HashFilterInterceptGradientOp);\n\nREGISTER_OP(\"MonolithHashFilterInterceptGradientGradient\")\n    .Input(\"filter_handle: resource\")\n    .Input(\"ids: int64\")\n    .Input(\"grad: float\")\n    .Output(\"filted_grad: float\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      c->set_output(0, c->input(2));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithHashFilterInterceptGradientGradient\").Device(DEVICE_CPU),\n    HashFilterInterceptGradientGradientOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_filter_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <memory>\n\n#include \"monolith/native_training/runtime/hash_filter/dummy_hash_filter.h\"\n#include \"monolith/native_training/runtime/hash_filter/probabilistic_filter.h\"\n#include \"monolith/native_training/runtime/hash_filter/sliding_hash_filter.h\"\n#include \"monolith/native_training/runtime/ops/hash_filter_tf_bridge.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing ::monolith::hash_filter::DummyHashFilter;\nusing ::monolith::hash_filter::ProbabilisticFilter;\nusing ::monolith::hash_filter::SlidingHashFilter;\n\nclass DummyFilterOp : public ResourceOpKernel<HashFilterTfBridge> {\n public:\n  explicit DummyFilterOp(OpKernelConstruction* ctx) : ResourceOpKernel(ctx) {}\n\n  ~DummyFilterOp() override = default;\n\n private:\n  Status CreateResource(HashFilterTfBridge** filter_bridge)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    auto filter = std::make_unique<DummyHashFilter>();\n    *filter_bridge = new HashFilterTfBridge(std::move(filter), config_);\n    return Status::OK();\n  };\n\n  monolith::hash_table::SlotOccurrenceThresholdConfig config_;\n};\n\nclass HashFilterOp : public ResourceOpKernel<HashFilterTfBridge> {\n public:\n  explicit HashFilterOp(OpKernelConstruction* ctx) : ResourceOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"capacity\", &capacity_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"split_num\", &split_num_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"config\", &config_serialized_));\n    if (!config_serialized_.empty()) {\n      OP_REQUIRES(\n          ctx, config_.ParseFromString(config_serialized_),\n          errors::InvalidArgument(\"Unable to parse config. Make sure it \"\n                                  \"is serialized version of \"\n                                  \"SlotOccurrenceThresholdConfig.\"));\n    }\n  }\n\n  ~HashFilterOp() override {}\n\n private:\n  Status CreateResource(HashFilterTfBridge** filter_bridge)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    auto filter = std::make_unique<SlidingHashFilter>(capacity_, split_num_);\n    // TODO(leqi.zou): We know this is NOT thread safe. But let's keep it as it\n    // is because we may remove HashFilter in the future.\n    *filter_bridge = new HashFilterTfBridge(std::move(filter), config_);\n    return Status::OK();\n  };\n\n  int64 capacity_;\n  int split_num_;\n  std::string config_serialized_;\n  monolith::hash_table::SlotOccurrenceThresholdConfig config_;\n};\n\nclass ProbabilisticFilterOp : public ResourceOpKernel<HashFilterTfBridge> {\n public:\n  explicit ProbabilisticFilterOp(OpKernelConstruction* ctx)\n      : ResourceOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"equal_probability\", &equal_probability_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"config\", &config_serialized_));\n    if (!config_serialized_.empty()) {\n      OP_REQUIRES(\n          ctx, config_.ParseFromString(config_serialized_),\n          errors::InvalidArgument(\"Unable to parse config. Make sure it \"\n                                  \"is serialized version of \"\n                                  \"SlotOccurrenceThresholdConfig.\"));\n    }\n  }\n\n  ~ProbabilisticFilterOp() override = default;\n\n private:\n  Status CreateResource(HashFilterTfBridge** filter_bridge)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    auto filter = std::make_unique<ProbabilisticFilter>(equal_probability_);\n    *filter_bridge = new HashFilterTfBridge(std::move(filter), config_, true);\n    return Status::OK();\n  };\n\n  bool equal_probability_;\n  std::string config_serialized_;\n  monolith::hash_table::SlotOccurrenceThresholdConfig config_;\n};\n\nREGISTER_OP(\"MonolithHashFilter\")\n    .Output(\"handle: resource\")\n    .Attr(\"capacity: int = 300000000\")\n    .Attr(\"split_num: int = 7\")\n    // Config contains a string of pb message SlotOccurrenceThresholdConfig.\n    .Attr(\"config: string = ''\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashFilter\").Device(DEVICE_CPU),\n                        HashFilterOp);\n\nREGISTER_OP(\"MonolithProbabilisticFilter\")\n    .Output(\"handle: resource\")\n    .Attr(\"equal_probability: bool = false\")\n    // Config contains a string of pb message SlotOccurrenceThresholdConfig.\n    .Attr(\"config: string = ''\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_OP(\"MonolithDummyHashFilter\")\n    .Output(\"handle: resource\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithDummyHashFilter\").Device(DEVICE_CPU),\n                        DummyFilterOp);\nREGISTER_KERNEL_BUILDER(Name(\"MonolithProbabilisticFilter\").Device(DEVICE_CPU),\n                        ProbabilisticFilterOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_filter_restore_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <memory>\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n#include \"tensorflow/core/lib/io/record_writer.h\"\n#include \"tensorflow/core/platform/path.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n\n#include \"monolith/native_training/runtime/hash_filter/dummy_hash_filter.h\"\n#include \"monolith/native_training/runtime/hash_filter/sliding_hash_filter.h\"\n#include \"monolith/native_training/runtime/ops/file_utils.h\"\n#include \"monolith/native_training/runtime/ops/hash_filter_tf_bridge.h\"\n#include \"tensorflow/core/lib/io/record_reader.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing ::monolith::hash_table::SlidingHashFilterMetaDump;\nusing ::monolith::hash_table::HashFilterSplitMetaDump;\nusing ::monolith::hash_table::HashFilterSplitDataDump;\n\nclass HashFilterRestoreOp : public AsyncOpKernel {\n public:\n  explicit HashFilterRestoreOp(OpKernelConstruction* ctx)\n      : AsyncOpKernel(ctx) {}\n\n  void ComputeAsync(OpKernelContext* ctx, DoneCallback done) override {\n    HashFilterTfBridge* hash_filter = nullptr;\n    OP_REQUIRES_OK_ASYNC(\n        ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &hash_filter), done);\n    core::ScopedUnref unref(hash_filter);\n    const Tensor& basename_tensor = ctx->input(1);\n    const std::string basename = basename_tensor.scalar<tstring>()();\n    std::vector<std::string> files;\n    OP_REQUIRES_OK_ASYNC(\n        ctx, ctx->env()->GetMatchingPaths(absl::StrCat(basename, \"-*\"), &files),\n        done);\n\n    FileSpec file_spec;\n    OP_REQUIRES_OK_ASYNC(ctx, ValidateShardedFiles(basename, files, &file_spec),\n                         done);\n    OP_REQUIRES_ASYNC(ctx, file_spec.nshards() > 0,\n                      errors::NotFound(\"Unable to find the dump files for: \",\n                                       name(), \" in \", basename),\n                      done);\n    ctx->set_output(0, ctx->input(0));\n    int nsplits = file_spec.nshards();\n    auto pack = new HashFilterAsyncPack(ctx, hash_filter, basename,\n                                        std::move(done), nsplits);\n    for (int i = 0; i < nsplits; ++i) {\n      ctx->device()->tensorflow_cpu_worker_threads()->workers->Schedule(\n          [this, pack, i, nsplits] { WorkerThread(i, nsplits, pack); });\n    }\n  }\n\n private:\n  void WorkerThread(int split_idx, int nsplits, HashFilterAsyncPack* p) {\n    p->status[split_idx] = RestoreOneSplit(split_idx, nsplits, p);\n    if (p->finish_num.fetch_add(1) == p->thread_num - 1) {\n      Cleanup(p);\n    }\n  }\n\n  Status RestoreOneSplit(int split_idx, int nsplits, HashFilterAsyncPack* p) {\n    std::string filename = GetShardedFileName(p->basename, split_idx, nsplits);\n    std::unique_ptr<RandomAccessFile> f;\n\n    TF_RETURN_IF_ERROR(p->ctx->env()->NewRandomAccessFile(filename, &f));\n    io::RecordReaderOptions opts;\n    opts.buffer_size = 10 * 1024 * 1024;\n    io::SequentialRecordReader reader(f.get(), opts);\n    Status restore_status;\n    auto get_meta_fn = [&reader,\n                        &restore_status](HashFilterSplitMetaDump* dump) {\n      Status s = GetMetaRecord(&reader, dump);\n      if (TF_PREDICT_FALSE(!s.ok())) {\n        if (!errors::IsOutOfRange(s)) {\n          restore_status = s;\n        }\n        return false;\n      }\n      return true;\n    };\n    auto get_data_fn = [&reader,\n                        &restore_status](HashFilterSplitDataDump* dump) {\n      Status s = GetDataRecord(&reader, dump);\n      if (TF_PREDICT_FALSE(!s.ok())) {\n        if (!errors::IsOutOfRange(s)) {\n          restore_status = s;\n        }\n        return false;\n      }\n      return true;\n    };\n\n    TF_RETURN_IF_ERROR(\n        p->hash_filter->Restore(split_idx, get_meta_fn, get_data_fn));\n    TF_RETURN_IF_ERROR(restore_status);\n    return Status::OK();\n  }\n\n  static Status GetMetaRecord(io::SequentialRecordReader* reader,\n                              HashFilterSplitMetaDump* dump) {\n    tstring s;\n    TF_RETURN_IF_ERROR(reader->ReadRecord(&s));\n    if (!dump->ParseFromArray(s.data(), s.size())) {\n      return errors::FailedPrecondition(\n          \"Unable to parse data. Data might be corrupted\");\n    }\n    return Status::OK();\n  }\n\n  static Status GetDataRecord(io::SequentialRecordReader* reader,\n                              HashFilterSplitDataDump* dump) {\n    tstring s;\n    TF_RETURN_IF_ERROR(reader->ReadRecord(&s));\n    if (!dump->ParseFromArray(s.data(), s.size())) {\n      return errors::FailedPrecondition(\n          \"Unable to parse data. Data might be corrupted\");\n    }\n    return Status::OK();\n  }\n\n  // Clean up when all shards are done.\n  void Cleanup(HashFilterAsyncPack* p) {\n    auto done = [p]() {\n      // We want to delete p first and then call done.\n      auto done = std::move(p->done);\n      delete p;\n      done();\n    };\n    for (int i = 0; i < p->thread_num; ++i) {\n      OP_REQUIRES_OK_ASYNC(p->ctx, p->status[i], done);\n    }\n    done();\n  }\n};\n\nREGISTER_OP(\"MonolithHashFilterRestore\")\n    .Input(\"handle: resource\")\n    .Input(\"basename: string\")\n    .Output(\"output_handle: resource\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashFilterRestore\").Device(DEVICE_CPU),\n                        HashFilterRestoreOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_filter_save_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <memory>\n\n#include \"monolith/native_training/runtime/hash_filter/dummy_hash_filter.h\"\n#include \"monolith/native_training/runtime/hash_filter/sliding_hash_filter.h\"\n#include \"monolith/native_training/runtime/ops/file_utils.h\"\n#include \"monolith/native_training/runtime/ops/hash_filter_tf_bridge.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n#include \"tensorflow/core/lib/io/record_writer.h\"\n#include \"tensorflow/core/platform/path.h\"\n#include \"tensorflow/core/platform/random.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing ::monolith::hash_table::SlidingHashFilterMetaDump;\nusing ::monolith::hash_table::HashFilterSplitMetaDump;\nusing ::monolith::hash_table::HashFilterSplitDataDump;\n\nclass HashFilterSaveOp : public AsyncOpKernel {\n public:\n  explicit HashFilterSaveOp(OpKernelConstruction* ctx) : AsyncOpKernel(ctx) {}\n\n  void ComputeAsync(OpKernelContext* ctx, DoneCallback done) override {\n    HashFilterTfBridge* hash_filter = nullptr;\n    OP_REQUIRES_OK_ASYNC(\n        ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &hash_filter), done);\n    core::ScopedUnref unref(hash_filter);\n    const Tensor& basename_tensor = ctx->input(1);\n    const std::string basename = basename_tensor.scalar<tstring>()();\n    const std::string dirname = std::string(io::Dirname(basename));\n    OP_REQUIRES_OK_ASYNC(ctx, ctx->env()->RecursivelyCreateDir(dirname), done);\n    ctx->set_output(0, ctx->input(0));\n    int nsplits = hash_filter->GetSplitNum();\n    if (nsplits == 0) {\n      done();\n      return;\n    }\n    auto pack = new HashFilterAsyncPack(ctx, hash_filter, basename,\n                                        std::move(done), nsplits);\n    for (int i = 0; i < nsplits; ++i) {\n      ctx->device()->tensorflow_cpu_worker_threads()->workers->Schedule(\n          [this, i, nsplits, pack] { WorkerThread(i, nsplits, pack); });\n    }\n  }\n\n private:\n  void WorkerThread(int split_idx, int nsplits, HashFilterAsyncPack* p) {\n    p->status[split_idx] = SaveOneSplit(split_idx, nsplits, p);\n    if (p->finish_num.fetch_add(1) == p->thread_num - 1) {\n      Cleanup(p);\n    }\n  }\n\n  Status SaveOneSplit(int split_idx, int nsplits, HashFilterAsyncPack* p) {\n    std::string filename = GetShardedFileName(p->basename, split_idx, nsplits);\n    std::string tmp_filename = absl::StrCat(filename, \"-tmp-\", random::New64());\n    std::unique_ptr<WritableFile> f;\n    TF_RETURN_IF_ERROR(p->ctx->env()->NewWritableFile(tmp_filename, &f));\n    io::RecordWriter writer(f.get());\n    Status write_status;\n    // In theory, we should stop writing once write failed.\n    // But this requires a lot of refactoring and currently we only do 2 writes.\n    // So we keep it as it is here.\n    auto write_data_fn = [this, &writer,\n                          &write_status](HashFilterSplitDataDump dump) {\n      Status s = writer.WriteRecord(dump.SerializeAsString());\n      if (TF_PREDICT_FALSE(!s.ok())) {\n        write_status.Update(s);\n      }\n    };\n    auto write_meta_fn = [this, &writer,\n                          &write_status](HashFilterSplitMetaDump dump) {\n      Status s = writer.WriteRecord(dump.SerializeAsString());\n      if (TF_PREDICT_FALSE(!s.ok())) {\n        write_status.Update(s);\n      }\n    };\n    TF_RETURN_IF_ERROR(\n        p->hash_filter->Save(split_idx, write_meta_fn, write_data_fn));\n    TF_RETURN_IF_ERROR(write_status);\n    TF_RETURN_IF_ERROR(writer.Close());\n    TF_RETURN_IF_ERROR(f->Close());\n    TF_RETURN_IF_ERROR(p->ctx->env()->RenameFile(tmp_filename, filename));\n    return Status::OK();\n  }\n\n  // Clean up when all shards are done.\n  void Cleanup(HashFilterAsyncPack* p) {\n    auto done = [p]() {\n      // We want to delete p first and then call done.\n      auto done = std::move(p->done);\n      delete p;\n      done();\n    };\n    for (int i = 0; i < p->thread_num; ++i) {\n      OP_REQUIRES_OK_ASYNC(p->ctx, p->status[i], done);\n    }\n    done();\n  }\n};\n\nREGISTER_OP(\"MonolithHashFilterSave\")\n    .Input(\"handle: resource\")\n    .Input(\"basename: string\")\n    .Output(\"output_handle: resource\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashFilterSave\").Device(DEVICE_CPU),\n                        HashFilterSaveOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_filter_tf_bridge.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/hash_filter_tf_bridge.h\"\n\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing ::monolith::hash_filter::Filter;\nusing ::monolith::hash_table::HashFilterSplitDataDump;\nusing ::monolith::hash_table::HashFilterSplitMetaDump;\nusing ::monolith::hash_table::SlotOccurrenceThresholdConfig;\n\nHashFilterTfBridge::HashFilterTfBridge(\n    std::unique_ptr<Filter> filter, const SlotOccurrenceThresholdConfig& config,\n    bool is_probabilistic)\n    : filter_(std::move(filter)), is_probabilistic_(is_probabilistic) {\n  slot_to_occurrence_threshold_.resize(get_max_slot_number(),\n                                       config.default_occurrence_threshold());\n  for (const auto& slot_occurrence_threshold :\n       config.slot_occurrence_thresholds()) {\n    slot_to_occurrence_threshold_[slot_occurrence_threshold.slot()] =\n        slot_occurrence_threshold.occurrence_threshold();\n  }\n}\n\nint HashFilterTfBridge::GetSlotOccurrenceThreshold(int64_t fid) const {\n  return slot_to_occurrence_threshold_[slot_id_v2(fid)];\n}\n\nStatus HashFilterTfBridge::Save(\n    int split_idx, std::function<void(HashFilterSplitMetaDump)> write_meta_fn,\n    std::function<void(HashFilterSplitDataDump)> write_data_fn) const {\n  try {\n    filter_->Save(split_idx, std::move(write_meta_fn),\n                  std::move(write_data_fn));\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::ResourceExhausted(e.what());\n  }\n}\n\nStatus HashFilterTfBridge::Restore(\n    int split_idx, std::function<bool(HashFilterSplitMetaDump*)> get_meta_fn,\n    std::function<bool(HashFilterSplitDataDump*)> get_data_fn) const {\n  try {\n    filter_->Restore(split_idx, std::move(get_meta_fn), std::move(get_data_fn));\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::ResourceExhausted(e.what());\n  }\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_filter_tf_bridge.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_HASH_FILTER_TF_BRIDGE_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_HASH_FILTER_TF_BRIDGE_H_\n#include <cstdint>\n#include <memory>\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_filter/filter.h\"\n#include \"monolith/native_training/runtime/ops/file_utils.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass HashFilterTfBridge : public ResourceBase {\n public:\n  explicit HashFilterTfBridge(\n      std::unique_ptr<monolith::hash_filter::Filter> filter,\n      const monolith::hash_table::SlotOccurrenceThresholdConfig& config,\n      bool is_probabilistic = false);\n\n  bool ShouldBeFiltered(\n      int64_t id, int64_t count,\n      monolith::hash_table::EmbeddingHashTableInterface* table) {\n    return filter_->ShouldBeFiltered(id, count, GetSlotOccurrenceThreshold(id),\n                                     table);\n  }\n\n  bool ShouldBeFiltered(\n      int64_t id,\n      monolith::hash_table::EmbeddingHashTableInterface* table = nullptr) {\n    return ShouldBeFiltered(id, 1, table);\n  }\n\n  int GetSplitNum() { return filter_->split_num(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"Filter with capacity: %d\", filter_->capacity());\n  }\n\n  // For the functor injected, it is ok to throw exceptions.\n  Status Save(\n      int split_idx,\n      std::function<void(::monolith::hash_table::HashFilterSplitMetaDump)>\n          write_meta_fn,\n      std::function<void(::monolith::hash_table::HashFilterSplitDataDump)>\n          write_data_fn) const;\n\n  Status Restore(\n      int split_idx,\n      std::function<bool(::monolith::hash_table::HashFilterSplitMetaDump*)>\n          get_meta_fn,\n      std::function<bool(::monolith::hash_table::HashFilterSplitDataDump*)>\n          get_data_fn) const;\n  const std::vector<int>& GetOccuranceThresholdArray() const {\n    return slot_to_occurrence_threshold_;\n  }\n  bool IsProbabilistic() const { return is_probabilistic_; }\n\n private:\n  int GetSlotOccurrenceThreshold(int64_t fid) const;\n\n  std::unique_ptr<monolith::hash_filter::Filter> filter_;\n  std::vector<int> slot_to_occurrence_threshold_;\n  const bool is_probabilistic_;\n};\n\n// Carries the data through async process.\n// It will ref and unref |p_hash_filter|\nstruct HashFilterAsyncPack {\n  HashFilterAsyncPack(OpKernelContext* p_ctx, HashFilterTfBridge* p_hash_filter,\n                      std::string p_basename, std::function<void()> p_done,\n                      int p_thread_num)\n      : ctx(p_ctx),\n        hash_filter(p_hash_filter),\n        basename(p_basename),\n        done(std::move(p_done)),\n        thread_num(p_thread_num),\n        finish_num(0),\n        status(thread_num) {\n    hash_filter->Ref();\n  }\n\n  ~HashFilterAsyncPack() { hash_filter->Unref(); }\n\n  OpKernelContext* ctx;\n  HashFilterTfBridge* hash_filter;\n  std::string basename;\n  std::function<void()> done;\n  const int thread_num;\n  std::atomic_int finish_num;\n  std::vector<Status> status;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_HASH_FILTER_TF_BRIDGE_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_table/misc_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table_interface.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nclass HashTableSizeOp : public OpKernel {\n public:\n  explicit HashTableSizeOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    EmbeddingHashTableTfBridge* table = nullptr;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &table));\n    Tensor* size_tensor;\n\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {}, &size_tensor));\n    size_tensor->scalar<int64>()() = table->Size();\n  }\n};\n\nREGISTER_OP(\"MonolithHashTableSize\")\n    .Input(\"table_handle: resource\")\n    .Output(\"size: int64\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashTableSize\").Device(DEVICE_CPU),\n                        HashTableSizeOp);\n\nclass SaveAsTensorOp : public OpKernel {\n public:\n  explicit SaveAsTensorOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* c) override {\n    EmbeddingHashTableTfBridge* table = nullptr;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 0), &table));\n    auto shard = GetShard(c);\n    auto iter = GetIter(c);\n    std::vector<EmbeddingHashTableTfBridge::EntryDump> dumps;\n    dumps.reserve(shard.limit);\n    auto write_fn = [&dumps](EmbeddingHashTableTfBridge::EntryDump dump) {\n      dumps.push_back(std::move(dump));\n      return true;\n    };\n    OP_REQUIRES_OK(c, table->Save(c, shard, write_fn, &iter));\n\n    Tensor* new_offset;\n    OP_REQUIRES_OK(c, c->allocate_output(0, {}, &new_offset));\n    new_offset->scalar<int64>()() = iter.offset;\n    Tensor* out_dump;\n    OP_REQUIRES_OK(c, c->allocate_output(1, {(int64)dumps.size()}, &out_dump));\n    auto out_dump_vec = out_dump->vec<tstring>();\n    for (int i = 0; i < dumps.size(); ++i) {\n      out_dump_vec(i) = dumps[i].SerializeAsString();\n    }\n  }\n\n private:\n  EmbeddingHashTableTfBridge::DumpShard GetShard(OpKernelContext* c) {\n    EmbeddingHashTableTfBridge::DumpShard shard;\n    shard.idx = c->input(1).scalar<int32>()();\n    shard.total = c->input(2).scalar<int32>()();\n    shard.limit = c->input(3).scalar<int64>()();\n    return shard;\n  }\n\n  EmbeddingHashTableTfBridge::DumpIterator GetIter(OpKernelContext* c) {\n    EmbeddingHashTableTfBridge::DumpIterator iter;\n    iter.offset = c->input(4).scalar<int64>()();\n    return iter;\n  }\n\n private:\n  int num_shard_;\n  int shard_id_;\n};\n\nREGISTER_OP(\"MonolithHashTableSaveAsTensor\")\n    .Input(\"table_handle: resource\")\n    .Input(\"shard_idx: int32\")\n    .Input(\"num_shards: int32\")\n    .Input(\"limit: int64\")\n    .Input(\"offset: int64\")\n    .Output(\"new_offset: int64\")\n    .Output(\"entry: string\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      ctx->set_output(0, ctx->Scalar());\n      ctx->set_output(1, ctx->Vector({ctx->UnknownDim()}));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithHashTableSaveAsTensor\").Device(DEVICE_CPU), SaveAsTensorOp);\n\n// In the future, probably we want to convert EntryDump to a set of tensors\n// before we extract useful information.\nclass ExtractSlotFromEntryOp : public OpKernel {\n public:\n  explicit ExtractSlotFromEntryOp(OpKernelConstruction* c) : OpKernel(c) {\n    c->GetAttr(\"fid_v2\", &fid_v2_);\n  }\n\n  void Compute(OpKernelContext* c) {\n    const Tensor& dump = c->input(0);\n    auto dump_vec = dump.vec<tstring>();\n    const int len = dump.NumElements();\n    Tensor* slot;\n    OP_REQUIRES_OK(c, c->allocate_output(0, {len}, &slot));\n    auto slot_vec = slot->vec<int32>();\n    for (int i = 0; i < len; ++i) {\n      const tstring& s = dump_vec(i);\n      monolith::hash_table::EntryDump d;\n      if (!d.ParseFromArray(s.data(), s.size())) {\n        LOG_EVERY_N_SEC(WARNING, 10) << \"Fail to parse entry dump.\";\n      }\n      if (fid_v2_) {\n        slot_vec(i) = slot_id_v2(d.id());\n      } else {\n        slot_vec(i) = slot_id_v1(d.id());\n      }\n    }\n  }\n\n private:\n  bool fid_v2_;\n};\n\nREGISTER_OP(\"MonolithExtractSlotFromEntry\")\n    .Input(\"entry: string\")\n    .Output(\"slot: int32\")\n    .Attr(\"fid_v2: bool\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      ctx->set_output(0, ctx->input(0));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithExtractSlotFromEntry\").Device(DEVICE_CPU),\n                        ExtractSlotFromEntryOp);\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_table_lookup_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/hash_table/utils.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/types.h\"\n#include \"tensorflow/core/platform/default/integral_types.h\"\n#include \"tensorflow/core/util/work_sharder.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing CPUDevice = Eigen::ThreadPoolDevice;\n\nclass HashTableLookupOp : public OpKernel {\n public:\n  explicit HashTableLookupOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"dim_size\", &dim_size_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"use_multi_threads\", &use_multi_threads_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    EmbeddingHashTableTfBridge* hash_table = nullptr;\n    OP_REQUIRES_OK(ctx,\n                   LookupResource(ctx, HandleFromInput(ctx, 0), &hash_table));\n    core::ScopedUnref unref(hash_table);\n    const Tensor& ids = ctx->input(1);\n    const int64 len_ids = ids.NumElements();\n    OP_REQUIRES(\n        ctx, dim_size_ == hash_table->dim_size(),\n        errors::InvalidArgument(absl::StrFormat(\n            \"dim_size should match hash table size. %d vs %d. Node name: %s\",\n            dim_size_, hash_table->dim_size(), def().name())));\n    Tensor* embeddings;\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(0, {len_ids, dim_size_}, &embeddings));\n    auto embeddings_mat = embeddings->matrix<float>();\n    auto ids_flat = ids.flat<int64_t>();\n\n    if (use_multi_threads_) {\n      auto lookup = [&](const int64 begin, const int64 end) {\n        int64_t hit_fid_count = 0;\n        hash_table->BatchLookup(\n            ctx, (end - begin), const_cast<int64_t*>(ids_flat.data() + begin),\n            embeddings_mat.data() + begin * dim_size_, &hit_fid_count);\n      };\n\n      // TODO(zhangbiao.david, youlong.cheng): tweak this number for\n      // optimization.\n      const int64 kCostPerUnit = 8 * dim_size_;\n      const DeviceBase::CpuWorkerThreads& worker_threads =\n          *ctx->device()->tensorflow_cpu_worker_threads();\n      Shard(worker_threads.num_threads, worker_threads.workers, len_ids,\n            kCostPerUnit, lookup);\n    } else {\n      int64_t hit_fid_count = 0;\n      hash_table->BatchLookup(ctx, len_ids,\n                              const_cast<int64_t*>(ids_flat.data()),\n                              embeddings_mat.data(), &hit_fid_count);\n    }\n  }\n\n  int64 dim_size_;\n  bool use_multi_threads_;\n};\n\nclass HashTableLookupEntryOp : public OpKernel {\n public:\n  explicit HashTableLookupEntryOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    EmbeddingHashTableTfBridge* hash_table = nullptr;\n    OP_REQUIRES_OK(ctx,\n                   LookupResource(ctx, HandleFromInput(ctx, 0), &hash_table));\n    core::ScopedUnref unref(hash_table);\n    const Tensor& ids = ctx->input(1);\n    const int64 len_ids = ids.NumElements();\n    auto ids_flat = ids.flat<int64_t>();\n\n    std::vector<EmbeddingHashTableTfBridge::EntryDump> entries(len_ids);\n    if (entries.size() > 0) {\n      hash_table->BatchLookupEntry(\n          ctx, len_ids, const_cast<int64_t*>(ids_flat.data()), &entries[0]);\n    }\n\n    Tensor* entry_strs;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {len_ids}, &entry_strs));\n    auto entry_str_vec = entry_strs->vec<tstring>();\n    for (int i = 0; i < entries.size(); ++i) {\n      entry_str_vec(i) = entries[i].SerializeAsString();\n    }\n  }\n};\n\nclass HashTableLookupGradientOp : public OpKernel {\n public:\n  explicit HashTableLookupGradientOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& id_indices = ctx->input(0);\n    const Tensor& id_values = ctx->input(1);\n    const Tensor& input_grads = ctx->input(2);\n\n    OP_REQUIRES(\n        ctx, id_indices.dim_size(0) == id_values.dim_size(0),\n        errors::InvalidArgument(\n            \"id_indices's first dim and id_values dim should be same. Got \",\n            id_indices.dim_size(0), \"v.s. \", id_values.dim_size(0)));\n    const int64 batch_size = id_indices.dim_size(0);\n    const int64 embedding_dim = input_grads.dim_size(1);\n\n    Tensor* output_ids;\n    ctx->allocate_output(0, {batch_size}, &output_ids);\n    Tensor* output_grads;\n    ctx->allocate_output(1, {batch_size, embedding_dim}, &output_grads);\n\n    auto id_indices_mat = id_indices.matrix<int64>();\n    auto id_values_vec = id_values.vec<int64>();\n    auto input_grads_mat = input_grads.matrix<float>();\n    auto output_ids_vec = output_ids->vec<int64>();\n    auto output_grads_mat = output_grads->matrix<float>();\n    for (int64 i = 0; i < batch_size; ++i) {\n      const int64 batch = id_indices_mat(i, 0);\n      const int64 id = id_values_vec(i);\n      output_ids_vec(i) = id;\n      for (int64 j = 0; j < embedding_dim; ++j) {\n        output_grads_mat(i, j) = input_grads_mat(batch, j);\n      }\n    }\n  }\n};\n\ntemplate <typename Device>\nclass HashTableFusedLookupOp : public OpKernel {\n public:\n  explicit HashTableFusedLookupOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"N\", &num_tables_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"num_of_shards\", &num_shards_));\n  }\n\n  void ComputeH(OpKernelContext* ctx);\n  void Compute(OpKernelContext* ctx) override { ComputeH(ctx); }\n\n private:\n  int num_shards_;\n  int num_tables_;\n};\n\ntemplate <>\nvoid HashTableFusedLookupOp<CPUDevice>::ComputeH(OpKernelContext* ctx) {\n  auto ids_flat = ctx->input(num_tables_ + 0).flat<int64_t>().data();\n  auto slot_size_vec = ctx->input(num_tables_ + 1).vec<int>().data();\n  auto slot_size_cnt = num_tables_ * num_shards_;\n\n  Tensor *embeddings_ts, *emb_splits_ts, *key_offsets_ts, *emb_offsets_ts;\n  OP_REQUIRES_OK(ctx, ctx->allocate_output(1, {num_shards_}, &emb_splits_ts));\n  OP_REQUIRES_OK(ctx,\n                 ctx->allocate_output(2, {slot_size_cnt + 1}, &key_offsets_ts));\n  OP_REQUIRES_OK(ctx,\n                 ctx->allocate_output(3, {slot_size_cnt + 1}, &emb_offsets_ts));\n  ctx->set_output(4, ctx->input(num_tables_ + 0));\n  auto key_offsets = key_offsets_ts->vec<int>().data();\n  auto emb_offsets = emb_offsets_ts->vec<int>().data();\n  auto emb_splits = emb_splits_ts->vec<int>().data();\n\n  std::vector<EmbeddingHashTableTfBridge*> hash_tables(num_tables_, nullptr);\n  std::vector<int> hash_table_dims(num_tables_, 0);\n  for (int table_id = 0; table_id < num_tables_; table_id++) {\n    EmbeddingHashTableTfBridge* hash_table = nullptr;\n    OP_REQUIRES_OK(\n        ctx, LookupResource(ctx, HandleFromInput(ctx, table_id), &hash_table));\n    core::ScopedUnref unref(hash_table);\n    hash_tables[table_id] = hash_table;\n    hash_table_dims[table_id] = hash_table->dim_size();\n  }\n\n  int total_keys, total_embs;\n  std::tie(total_keys, total_embs) =\n      monolith::hash_table::ComputeFusedOffsets<false>(\n          slot_size_vec, hash_table_dims.data(), num_tables_, num_shards_,\n          key_offsets, emb_offsets, nullptr, emb_splits);\n\n  OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {total_embs}, &embeddings_ts));\n  auto embeddings = embeddings_ts->vec<float>().data();\n\n  auto lookup = [&](const int begin, const int end) {\n    for (int shard_id = begin; shard_id < end; shard_id++) {\n      for (int table_id = 0; table_id < num_tables_; table_id++) {\n        int curr_idx = shard_id * num_tables_ + table_id;\n        int64_t hit_fid_count = 0;\n        hash_tables[table_id]->BatchLookup(\n            ctx, slot_size_vec[curr_idx],\n            const_cast<int64_t*>(ids_flat) + key_offsets[curr_idx],\n            embeddings + emb_offsets[curr_idx], &hit_fid_count);\n      }\n    }\n  };\n\n  // TODO(zouxuan): tweak this number for optimization.\n  const int64 kCostPerUnit = 1000000;\n  const DeviceBase::CpuWorkerThreads& worker_threads =\n      *ctx->device()->tensorflow_cpu_worker_threads();\n  Shard(worker_threads.num_threads, worker_threads.workers, num_shards_,\n        kCostPerUnit, lookup);\n}\n\n\nREGISTER_OP(\"MonolithHashTableLookup\")\n    .Input(\"table_handle: resource\")\n    .Input(\"ids: int64\")\n    .Output(\"embeddings: float32\")\n    .Attr(\"dim_size: int\")\n    .Attr(\"use_multi_threads: bool = false\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int64 dim_size;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"dim_size\", &dim_size));\n      shape_inference::DimensionHandle len_ids = c->Dim(c->input(1), 0);\n      c->set_output(0, c->Matrix(len_ids, dim_size));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashTableLookup\").Device(DEVICE_CPU),\n                        HashTableLookupOp);\n\nREGISTER_OP(\"MonolithHashTableLookupEntry\")\n    .Input(\"table_handle: resource\")\n    .Input(\"ids: int64\")\n    .Output(\"entry_str: string\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::DimensionHandle len_ids = c->Dim(c->input(1), 0);\n      c->set_output(0, c->Vector(len_ids));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashTableLookupEntry\").Device(DEVICE_CPU),\n                        HashTableLookupEntryOp);\n\nREGISTER_OP(\"MonolithHashTableLookupGradient\")\n    .Input(\"id_indices: int64\")\n    .Input(\"id_values: int64\")\n    .Input(\"input_grads : float\")\n    .Output(\"ids: int64\")\n    .Output(\"output_grads: float\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::DimensionHandle batch_size = c->Dim(c->input(0), 0);\n      shape_inference::DimensionHandle embedding_size = c->Dim(c->input(2), 1);\n      c->set_output(0, c->MakeShape({batch_size}));\n      c->set_output(1, c->MakeShape({batch_size, embedding_size}));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithHashTableLookupGradient\").Device(DEVICE_CPU),\n    HashTableLookupGradientOp);\n\nREGISTER_OP(\"MonolithHashTableFusedLookup\")\n    .Input(\"table_handles: N * resource\")\n    .Input(\"ids: int64\")\n    .Input(\"fused_slot_size: int32\")\n    .Input(\"req_time: int64\")\n    .Output(\"embeddings: float32\")\n    .Output(\"embedding_splits: int32\")\n    .Output(\"id_offsets: int32\")\n    .Output(\"embedding_offsets: int32\")\n    .Output(\"indices: int64\")\n    .Attr(\"N: int\")\n    .Attr(\"num_of_shards: int\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int num_tables, num_shards;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"num_of_shards\", &num_shards));\n      TF_RETURN_IF_ERROR(c->GetAttr(\"N\", &num_tables));\n      c->set_output(0, c->Vector(c->UnknownDim()));\n      c->set_output(1, c->Vector(num_shards));\n      c->set_output(2, c->Vector(num_tables * num_shards + 1));\n      c->set_output(3, c->Vector(num_tables * num_shards + 1));\n      c->set_output(4, c->input(num_tables));\n      auto shape = c->input(num_tables + 1);\n      TF_RETURN_IF_ERROR(c->WithRank(shape, 1, &shape));\n      auto dim = c->Dim(shape, 0);\n      TF_RETURN_IF_ERROR(c->WithValue(dim, num_tables * num_shards, &dim));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashTableFusedLookup\").Device(DEVICE_CPU),\n                        HashTableFusedLookupOp<CPUDevice>);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_table_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"absl/strings/str_format.h\"\n#include \"absl/synchronization/mutex.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"monolith/native_training/runtime/ops/parameter_sync_tf_bridge.h\"\n#include \"monolith/native_training/runtime/parameter_sync/parameter_sync_client.h\"\n#include \"tensorflow/core/framework/lookup_interface.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/types.h\"\n#include \"tensorflow/core/platform/default/integral_types.h\"\n#include \"tensorflow/core/platform/mutex.h\"\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing ::monolith::hash_table::EmbeddingHashTableConfig;\nusing ::monolith::hash_table::GpuExtraArgs;\n// using ::monolith::hopscotch::HopscotchHashSet;\nusing ::monolith::parameter_sync::ParameterSyncClient;\n\nusing CPUDevice = Eigen::ThreadPoolDevice;\n\ntemplate <typename Device>\nclass HashTableOp : public OpKernel {\n public:\n  explicit HashTableOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    std::string config_serialized;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"config\", &config_serialized));\n    OP_REQUIRES(ctx, config_.ParseFromString(config_serialized),\n                errors::InvalidArgument(\"Unable to parse config. Make sure it \"\n                                        \"is serialized version of \"\n                                        \"EmbeddingHashTableConfig.\"));\n  }\n\n  ~HashTableOp() override {\n    if (hash_table_ != nullptr) {\n      if (cinfo_.resource_is_private_to_kernel()) {\n        cinfo_.resource_manager()\n            ->Delete<EmbeddingHashTableTfBridge>(cinfo_.container(),\n                                                 cinfo_.name())\n            .IgnoreError();\n      }\n      // here we use different way than ResourceKernelOp. Otherwise,\n      // we got crash and I believe it is our compiler's problem.\n      hash_table_->Unref();\n    }\n\n    if (hash_filter_ != nullptr) {\n      hash_filter_->Unref();\n    }\n  }\n\n  void ComputeH(OpKernelContext* ctx);\n\n  void Compute(OpKernelContext* ctx) override {\n    absl::MutexLock l(&mu_);\n    if (hash_filter_ == nullptr) {\n      OP_REQUIRES_OK(\n          ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &hash_filter_));\n    }\n    if (hash_table_ == nullptr) {\n      ComputeH(ctx);\n    }\n    OP_REQUIRES_OK(ctx, MakeResourceHandleToOutput(\n                            ctx, 0, cinfo_.container(), cinfo_.name(),\n                            TypeIndex::Make<EmbeddingHashTableTfBridge>()));\n  }\n\n private:\n  EmbeddingHashTableConfig config_;\n  absl::Mutex mu_;\n  EmbeddingHashTableTfBridge* hash_table_ ABSL_GUARDED_BY(mu_) = nullptr;\n  HashFilterTfBridge* hash_filter_ ABSL_GUARDED_BY(mu_) = nullptr;\n  ContainerInfo cinfo_ ABSL_GUARDED_BY(mu_);\n};\n\ntemplate <>\nvoid HashTableOp<CPUDevice>::ComputeH(OpKernelContext* ctx) {\n  ResourceMgr* rmgr = ctx->resource_manager();\n  OP_REQUIRES_OK(ctx, cinfo_.Init(rmgr, def()));\n\n  core::RefCountPtr<ParameterSyncClientTfBridge> sync_client = nullptr;\n  OP_REQUIRES_OK(ctx,\n                 LookupResource(ctx, HandleFromInput(ctx, 1), &sync_client));\n  auto sync_client_ptr = sync_client.get();\n\n  auto creator = [&, this](EmbeddingHashTableTfBridge** out_hash_table) {\n    TF_RETURN_IF_ERROR(EmbeddingHashTableTfBridge::New(\n        config_, hash_filter_, out_hash_table, cinfo_.name()));\n\n    if (sync_client_ptr->IsDummySyncClient()) {\n      LOG(INFO) << absl::StrFormat(\n          \"Hash table %s will not be attached to the sync client\",\n          cinfo_.name());\n    } else {\n      // TODO(zhangbiao.david) Make hopscotch hash set configurable\n      auto* touched_key_set = sync_client_ptr->GetTouchedKeySet();\n      (*out_hash_table)->SetHopscotchHashSet(touched_key_set);\n      sync_client_ptr->AddHashTableResource(cinfo_.name(), *out_hash_table);\n      LOG(INFO) << absl::StrFormat(\n          \"Hash table %s will be attached to the sync client\", cinfo_.name());\n    }\n    return Status::OK();\n  };\n  OP_REQUIRES_OK(ctx,\n                 rmgr->LookupOrCreate<EmbeddingHashTableTfBridge>(\n                     cinfo_.container(), cinfo_.name(), &hash_table_, creator));\n}\n\n\nREGISTER_OP(\"MonolithHashTable\")\n    .Input(\"filter_handle: resource\")\n    .Input(\"sync_client_handle: resource\")\n    .Output(\"handle: resource\")\n    .Attr(\"config: string\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashTable\").Device(DEVICE_CPU),\n                        HashTableOp<CPUDevice>);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_table_restore_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <atomic>\n#include <cstring>\n#include <string>\n#include <utility>\n\n#include \"absl/strings/str_cat.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"monolith/native_training/runtime/ops/file_utils.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/kernels/ops_util.h\"\n#include \"tensorflow/core/lib/io/path.h\"\n#include \"tensorflow/core/lib/io/record_reader.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\n// Carries the data through async process.\n// It will ref and unref |p_hash_table|.\nstruct AsyncPack {\n  AsyncPack(OpKernelContext* p_ctx, EmbeddingHashTableTfBridge* p_hash_table,\n            std::string p_basename, std::function<void()> p_done,\n            int p_thread_num)\n      : ctx(p_ctx),\n        basename(std::move(p_basename)),\n        record_count(0),\n        hash_table(p_hash_table),\n        done(std::move(p_done)),\n        thread_num(p_thread_num),\n        finish_num(0),\n        status(thread_num) {\n    hash_table->Ref();\n  }\n\n  ~AsyncPack() { hash_table->Unref(); }\n\n  OpKernelContext* ctx;\n  std::string basename;\n  std::atomic_long record_count;\n  EmbeddingHashTableTfBridge* hash_table;\n  std::function<void()> done;\n  const int thread_num;\n  std::atomic_int finish_num;\n  std::vector<Status> status;\n};\n\n}  // namespace\n\nclass HashTableRestoreOp : public AsyncOpKernel {\n public:\n  explicit HashTableRestoreOp(OpKernelConstruction* ctx) : AsyncOpKernel(ctx) {}\n\n  void ComputeAsync(OpKernelContext* ctx, DoneCallback done) override {\n    EmbeddingHashTableTfBridge* hash_table = nullptr;\n    OP_REQUIRES_OK_ASYNC(\n        ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &hash_table), done);\n    core::ScopedUnref unref(hash_table);\n    const Tensor& basename_tensor = ctx->input(1);\n    const std::string basename = basename_tensor.scalar<tstring>()();\n    std::vector<std::string> files;\n    OP_REQUIRES_OK_ASYNC(\n        ctx, ctx->env()->GetMatchingPaths(absl::StrCat(basename, \"-*\"), &files),\n        done);\n\n    FileSpec file_spec;\n    OP_REQUIRES_OK_ASYNC(ctx, ValidateShardedFiles(basename, files, &file_spec),\n                         done);\n    OP_REQUIRES_ASYNC(ctx, file_spec.nshards() > 0,\n                      errors::NotFound(\"Unable to find the dump files for: \",\n                                       name(), \" in \", basename),\n                      done);\n    ctx->set_output(0, ctx->input(0));\n    hash_table->Clear();\n    int nshards = files.size();\n    auto pack =\n        new AsyncPack(ctx, hash_table, basename, std::move(done), nshards);\n    for (int i = 0; i < nshards; ++i) {\n      ctx->device()->tensorflow_cpu_worker_threads()->workers->Schedule(\n          [this, pack, i, nshards] {\n            WorkerThread({i, nshards}, pack);\n          });\n    }\n  }\n\n private:\n  void WorkerThread(EmbeddingHashTableTfBridge::DumpShard shard, AsyncPack* p) {\n    p->status[shard.idx] = RestoreOneShard(shard, p);\n    if (p->finish_num.fetch_add(1) == p->thread_num - 1) {\n      auto summary = p->hash_table->Summary();\n      auto basename = tensorflow::io::Basename(p->basename);\n      LOG(INFO) << absl::StrFormat(\n          \"Hash table: %s, summary: %s, restore read %ld records, skip %ld \"\n          \"zero embeddings\",\n          basename, summary, p->record_count,\n          p->record_count - p->hash_table->Size());\n      Cleanup(p);\n    }\n  }\n\n  Status RestoreOneShard(EmbeddingHashTableTfBridge::DumpShard shard,\n                         AsyncPack* p) {\n    std::string filename =\n        GetShardedFileName(p->basename, shard.idx, shard.total);\n    std::unique_ptr<RandomAccessFile> f;\n\n    TF_RETURN_IF_ERROR(p->ctx->env()->NewRandomAccessFile(filename, &f));\n    io::RecordReaderOptions opts;\n    opts.buffer_size = 10 * 1024 * 1024;\n    io::SequentialRecordReader reader(f.get(), opts);\n    Status restore_status;\n    auto get_fn = [&reader, &restore_status, &p](\n        EmbeddingHashTableTfBridge::EntryDump* dump, int64_t* max_update_ts) {\n      Status s = GetRecord(&reader, dump);\n      if (TF_PREDICT_FALSE(!s.ok())) {\n        if (!errors::IsOutOfRange(s)) {\n          restore_status = s;\n        }\n        return false;\n      }\n      p->record_count.fetch_add(1);\n      if (!dump->has_last_update_ts_sec()) {\n        dump->set_last_update_ts_sec(0);\n      }\n\n      *max_update_ts = std::max(dump->last_update_ts_sec(), *max_update_ts);\n      return true;\n    };\n\n    TF_RETURN_IF_ERROR(p->hash_table->Restore(p->ctx, shard, get_fn));\n    TF_RETURN_IF_ERROR(restore_status);\n    return Status::OK();\n  }\n\n  static Status GetRecord(io::SequentialRecordReader* reader,\n                          EmbeddingHashTableTfBridge::EntryDump* dump) {\n    tstring s;\n    TF_RETURN_IF_ERROR(reader->ReadRecord(&s));\n    if (!dump->ParseFromArray(s.data(), s.size())) {\n      return errors::FailedPrecondition(\n          \"Unable to parse data. Data might be corrupted\");\n    }\n\n    return Status::OK();\n  }\n\n  // Clean up when all shards are done.\n  void Cleanup(AsyncPack* p) {\n    auto done = [p]() {\n      // We want to delete p first and then call done.\n      auto done = std::move(p->done);\n      delete p;\n      done();\n    };\n    for (int i = 0; i < p->thread_num; ++i) {\n      OP_REQUIRES_OK_ASYNC(p->ctx, p->status[i], done);\n    }\n    done();\n  }\n};\n\nREGISTER_OP(\"MonolithHashTableRestore\")\n    .Input(\"handle: resource\")\n    .Input(\"basename: string\")\n    .Output(\"output_handle: resource\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashTableRestore\").Device(DEVICE_CPU),\n                        HashTableRestoreOp);\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_table_save_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <atomic>\n#include <cstring>\n#include <string>\n\n#include \"absl/random/random.h\"\n#include \"absl/strings/str_cat.h\"\n#include \"absl/time/clock.h\"\n#include \"absl/time/time.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"monolith/native_training/runtime/ops/file_utils.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/kernels/ops_util.h\"\n#include \"tensorflow/core/lib/io/record_writer.h\"\n#include \"tensorflow/core/platform/path.h\"\n#include \"tensorflow/core/platform/random.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\n// Carries the data through async process.\n// It will ref and unref |p_hash_table|.\nstruct AsyncPack {\n  AsyncPack(OpKernelContext* p_ctx, EmbeddingHashTableTfBridge* p_hash_table,\n            std::string p_basename,\n            std::unique_ptr<EmbeddingHashTableTfBridge::LockCtx> p_lock_ctx,\n            std::function<void()> p_done, int p_thread_num)\n      : ctx(p_ctx),\n        basename(p_basename),\n        hash_table(p_hash_table),\n        lock_ctx(std::move(p_lock_ctx)),\n        done(std::move(p_done)),\n        thread_num(p_thread_num),\n        finish_num(0),\n        status(thread_num) {\n    hash_table->Ref();\n  }\n\n  ~AsyncPack() { hash_table->Unref(); }\n\n  OpKernelContext* ctx;\n  std::string basename;\n  EmbeddingHashTableTfBridge* hash_table;\n  std::unique_ptr<EmbeddingHashTableTfBridge::LockCtx> lock_ctx;\n  std::function<void()> done;\n  const int thread_num;\n  std::atomic_int finish_num;\n  std::vector<Status> status;\n};\n\nconst int kAutoTune = -1;\n\n}  // namespace\n\nclass HashTableSaveOp : public AsyncOpKernel {\n public:\n  explicit HashTableSaveOp(OpKernelConstruction* ctx) : AsyncOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"nshards\", &nshards_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"random_sleep_ms\", &random_sleep_ms_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"slot_expire_time_config\",\n                                     &slot_expire_time_config_serialized_));\n    if (!slot_expire_time_config_serialized_.empty()) {\n      OP_REQUIRES(\n          ctx, slot_expire_time_config_.ParseFromString(\n                   slot_expire_time_config_serialized_),\n          errors::InvalidArgument(\"Unable to parse config. Make sure it \"\n                                  \"is serialized version of \"\n                                  \"SlotExpireTimeConfig.\"));\n    }\n\n    slot_to_expire_time_.resize(get_max_slot_number(),\n                                slot_expire_time_config_.default_expire_time());\n    for (const auto& slot_expire_time :\n         slot_expire_time_config_.slot_expire_times()) {\n      slot_to_expire_time_[slot_expire_time.slot()] =\n          slot_expire_time.expire_time();\n    }\n  }\n\n  void ComputeAsync(OpKernelContext* ctx, DoneCallback done) override {\n    EmbeddingHashTableTfBridge* hash_table = nullptr;\n    OP_REQUIRES_OK_ASYNC(\n        ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &hash_table), done);\n    core::ScopedUnref unref(hash_table);\n    const Tensor& basename_tensor = ctx->input(1);\n    const std::string basename = basename_tensor.scalar<tstring>()();\n    const std::string dirname = std::string(io::Dirname(basename));\n    OP_REQUIRES_OK_ASYNC(ctx, ctx->env()->RecursivelyCreateDir(dirname), done);\n    ctx->set_output(0, ctx->input(0));\n    int real_nshards = PickNshards(hash_table);\n    std::unique_ptr<EmbeddingHashTableTfBridge::LockCtx> lock_ctx;\n    OP_REQUIRES_OK_ASYNC(ctx, hash_table->LockAll(&lock_ctx), done);\n    auto pack = new AsyncPack(ctx, hash_table, basename, std::move(lock_ctx),\n                              std::move(done), real_nshards);\n    for (int i = 0; i < real_nshards; ++i) {\n      // !important: When using GPU, tensorflow_cpu_worker_threads' are bound to\n      // device 0 regardless of the correct device id of the current process.\n      // That means one has to use the CUDA_VISIBLE_DEVICES\n      // environment vairable to make sure that only one GPU is visible each\n      // process. Otherwise something like this will happen: some device memory\n      // is allocated on device 3, but accessing that memory in this thread as\n      // device 0 will cause illegal memory errors\n      ctx->device()->tensorflow_cpu_worker_threads()->workers->Schedule(\n          [this, pack, i, real_nshards] {\n            WorkerThread({i, real_nshards}, pack);\n          });\n    }\n  }\n\n  int PickNshards(EmbeddingHashTableTfBridge* table) {\n    if (nshards_ >= 0) return nshards_;\n    const int64 size = table->Size();\n    const int64 kBaseline = 1000000ll;\n    return std::min(4LL, std::max(1LL, size / kBaseline));\n  }\n\n private:\n  void WorkerThread(EmbeddingHashTableTfBridge::DumpShard shard, AsyncPack* p) {\n    absl::BitGen bitgen;\n    absl::SleepFor(\n        absl::Milliseconds(absl::Uniform(bitgen, 0, random_sleep_ms_)));\n    p->status[shard.idx] = SaveOneShard(shard, p);\n    if (p->finish_num.fetch_add(1) == p->thread_num - 1) {\n      Cleanup(p);\n    }\n  }\n\n  Status SaveOneShard(EmbeddingHashTableTfBridge::DumpShard shard,\n                      AsyncPack* p) {\n    std::string filename =\n        GetShardedFileName(p->basename, shard.idx, shard.total);\n    std::string tmp_filename = absl::StrCat(filename, \"-tmp-\", random::New64());\n    std::unique_ptr<WritableFile> f;\n    TF_RETURN_IF_ERROR(p->ctx->env()->NewWritableFile(tmp_filename, &f));\n    io::RecordWriter writer(f.get());\n    Status write_status;\n    int64_t max_update_ts_sec = p->hash_table->max_update_ts_sec();\n    auto write_fn = [this, &max_update_ts_sec, &writer, &write_status](\n        EmbeddingHashTableTfBridge::EntryDump dump) {\n      int64_t slot_id = slot_id_v2(dump.id());\n      // Elements of slot_to_expire_time_ are in days.\n      // last_update_ts_sec is seconds since the Epoch.\n      if (max_update_ts_sec - dump.last_update_ts_sec() >=\n          slot_to_expire_time_[slot_id] * 24 * 3600) {\n        return true;\n      }\n      Status s = writer.WriteRecord(dump.SerializeAsString());\n      if (TF_PREDICT_FALSE(!s.ok())) {\n        // OK to throw here since it will be catched.\n        write_status = s;\n        return false;\n      }\n      return true;\n    };\n    EmbeddingHashTableTfBridge::DumpIterator iter;\n    TF_RETURN_IF_ERROR(p->hash_table->Save(p->ctx, shard, write_fn, &iter));\n    TF_RETURN_IF_ERROR(writer.Close());\n    TF_RETURN_IF_ERROR(f->Close());\n    TF_RETURN_IF_ERROR(p->ctx->env()->RenameFile(tmp_filename, filename));\n    return Status::OK();\n  }\n\n  // Clean up when all shards are done.\n  void Cleanup(AsyncPack* p) {\n    auto done = [p]() {\n      // We want to delete p first and then call done.\n      auto done = std::move(p->done);\n      delete p;\n      done();\n    };\n    for (int i = 0; i < p->thread_num; ++i) {\n      OP_REQUIRES_OK_ASYNC(p->ctx, p->status[i], done);\n    }\n    done();\n  }\n\n  int nshards_;\n  int64 random_sleep_ms_;\n  std::string slot_expire_time_config_serialized_;\n  monolith::hash_table::SlotExpireTimeConfig slot_expire_time_config_;\n  std::vector<int64_t> slot_to_expire_time_;\n};\n\nREGISTER_OP(\"MonolithHashTableSave\")\n    .Input(\"handle: resource\")\n    .Input(\"basename: string\")\n    .Output(\"output_handle: resource\")\n    .Attr(\"nshards: int=-1\")\n    .Attr(\"random_sleep_ms: int=0\")\n    .Attr(\"slot_expire_time_config: string = ''\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashTableSave\").Device(DEVICE_CPU),\n                        HashTableSaveOp);\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/hash_table_update_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/concurrency/queue.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"monolith/native_training/runtime/ops/hash_filter_tf_bridge.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/types.h\"\n#include \"tensorflow/core/util/work_sharder.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing monolith::concurrency::Queue;\nusing CPUDevice = Eigen::ThreadPoolDevice;\nclass HashTableAssignOp : public OpKernel {\n public:\n  explicit HashTableAssignOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    EmbeddingHashTableTfBridge* hash_table = nullptr;\n    OP_REQUIRES_OK(ctx,\n                   LookupResource(ctx, HandleFromInput(ctx, 0), &hash_table));\n    core::ScopedUnref unref(hash_table);\n    const Tensor& id_values = ctx->input(1);\n    const Tensor& id_updates = ctx->input(2);\n    const Tensor& update_time_tensor = ctx->input(3);\n    int64_t update_time = update_time_tensor.scalar<int64_t>()();\n    auto id_values_vec = id_values.vec<int64>();\n    const int num_updates = id_values_vec.dimension(0);\n    OP_REQUIRES_OK(\n        ctx, hash_table->Assign(\n                 ctx, num_updates, static_cast<int64_t*>(id_values.data()),\n                 static_cast<float*>(id_updates.data()), update_time));\n\n    ctx->set_output(0, ctx->input(0));\n  }\n};\n\nclass HashTableAssignAddOp : public OpKernel {\n public:\n  explicit HashTableAssignAddOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    EmbeddingHashTableTfBridge* hash_table = nullptr;\n    OP_REQUIRES_OK(ctx,\n                   LookupResource(ctx, HandleFromInput(ctx, 0), &hash_table));\n    core::ScopedUnref unref(hash_table);\n    const Tensor& id_values = ctx->input(1);\n    const Tensor& id_updates = ctx->input(2);\n    const Tensor& update_time_tensor = ctx->input(3);\n    int64_t update_time = update_time_tensor.scalar<int64_t>()();\n    auto id_values_vec = id_values.vec<int64>();\n    const int64 num_updates = id_values_vec.dimension(0);\n    for (int64 i = 0; i < num_updates; ++i) {\n      OP_REQUIRES_OK(\n          ctx, hash_table->AssignAdd(ctx, id_values_vec(i),\n                                     id_updates.SubSlice(i), update_time));\n    }\n\n    ctx->set_output(0, ctx->input(0));\n  }\n};\n\nclass HashTableOptimizeOp : public OpKernel {\n public:\n  explicit HashTableOptimizeOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"use_multi_threads\", &use_multi_threads_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"enable_dedup\", &enable_dedup_));\n\n    int queue_size = 0;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"queue_size\", &queue_size));\n    CHECK_GE(queue_size, 0);\n\n    queue_ =\n        queue_size > 0\n            ? std::make_unique<\n                  Queue<std::tuple<Tensor, Tensor, absl::Span<const float>>>>(\n                  queue_size)\n            : nullptr;\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    EmbeddingHashTableTfBridge* hash_table = nullptr;\n    OP_REQUIRES_OK(ctx,\n                   LookupResource(ctx, HandleFromInput(ctx, 0), &hash_table));\n    core::ScopedUnref unref(hash_table);\n    const Tensor& id_values = ctx->input(1);\n    const Tensor& id_updates = ctx->input(2);\n    const Tensor& learning_rate_tensor = ctx->input(3);\n    const Tensor& update_time_tensor = ctx->input(4);\n    const Tensor& global_step = ctx->input(5);\n\n    int64_t update_time = update_time_tensor.scalar<int64_t>()();\n    size_t num_updates = id_values.NumElements();\n    auto ids_flat = id_values.flat<int64_t>();\n    auto* ids = const_cast<int64_t*>(ids_flat.data());\n    absl::Span<const float> learning_rate_values =\n        absl::MakeSpan(static_cast<float*>(learning_rate_tensor.data()),\n                       learning_rate_tensor.NumElements());\n    int64_t global_step_value = global_step.scalar<int64_t>()();\n    if (use_multi_threads_) {\n      auto dim_size = hash_table->dim_size();\n      auto update = [&](const int64 begin, const int64 end) {\n        OP_REQUIRES_OK(\n            ctx, hash_table->BatchOptimize(\n                     ctx, (end - begin), (ids + begin),\n                     static_cast<float*>(id_updates.data()) + begin * dim_size,\n                     learning_rate_values, update_time, enable_dedup_,\n                     global_step_value));\n      };\n\n      // TODO(zhangbiao.david, youlong.cheng): tweak this number for\n      // optimization.\n      const int64 kCostPerUnit = 20 * dim_size;\n      const DeviceBase::CpuWorkerThreads& worker_threads =\n          *ctx->device()->tensorflow_cpu_worker_threads();\n      Shard(worker_threads.num_threads, worker_threads.workers, num_updates,\n            kCostPerUnit, update);\n    } else {\n      std::chrono::milliseconds timeout(1);\n\n      // Optimize using this thread if operation timing out\n      if (queue_ &&\n          queue_->try_push({id_values, id_updates, learning_rate_values},\n                           timeout)) {\n        auto thread_pool =\n            ctx->device()->tensorflow_cpu_worker_threads()->workers;\n        thread_pool->Schedule(\n            [this, ctx, update_time, hash_table, global_step_value]() {\n              auto ids_and_grads = queue_->pop();\n              const auto& id_values = std::get<0>(ids_and_grads);\n              const auto& tensor = std::get<1>(ids_and_grads);\n              auto& learning_rate_values = std::get<2>(ids_and_grads);\n              size_t num_updates = id_values.NumElements();\n              auto ids_flat = id_values.flat<int64_t>();\n              hash_table->BatchOptimize(\n                  ctx, num_updates, const_cast<int64_t*>(ids_flat.data()),\n                  static_cast<float*>(tensor.data()), learning_rate_values,\n                  update_time, enable_dedup_, global_step_value);\n            });\n      } else {\n        OP_REQUIRES_OK(ctx, hash_table->BatchOptimize(\n                                ctx, num_updates, ids,\n                                static_cast<float*>(id_updates.data()),\n                                learning_rate_values, update_time,\n                                enable_dedup_, global_step_value));\n      }\n    }\n\n    ctx->set_output(0, ctx->input(0));\n  }\n\n private:\n  bool use_multi_threads_;\n  bool enable_dedup_;\n  mutable std::unique_ptr<\n      Queue<std::tuple<Tensor, Tensor, absl::Span<const float>>>>\n      queue_;\n};\n\ntemplate <typename Device>\nclass HashTableFusedOptimizeOp : public OpKernel {\n public:\n  explicit HashTableFusedOptimizeOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"N\", &num_tables_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"num_of_shards\", &num_shards_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"enable_grad_accumulation\",\n                                     &enable_grad_accumulation_));\n  }\n\n  void ComputeH(OpKernelContext* ctx);\n  void Compute(OpKernelContext* ctx) override {\n    ComputeH(ctx);\n    for (int table_id = 0; table_id < num_tables_; table_id++) {\n      ctx->set_output(table_id, ctx->input(table_id));\n    }\n  }\n\n private:\n  bool enable_grad_accumulation_;\n  int num_tables_;\n  int num_shards_;\n};\n\ntemplate <>\nvoid HashTableFusedOptimizeOp<CPUDevice>::ComputeH(OpKernelContext* ctx) {\n  auto ids = ctx->input(num_tables_).vec<int64_t>().data();\n  auto num_ids = ctx->input(num_tables_).NumElements();\n  auto indices = ctx->input(num_tables_ + 1).vec<int64_t>().data();\n  auto slot_size_vec = ctx->input(num_tables_ + 2).vec<int32>().data();\n  auto id_grads = ctx->input(num_tables_ + 3).vec<float>().data();\n  auto num_grads = ctx->input(num_tables_ + 3).NumElements();\n  auto key_offsets = ctx->input(num_tables_ + 4).vec<int32>().data();\n  auto emb_offsets = ctx->input(num_tables_ + 5).vec<int32>().data();\n  auto learning_rates = ctx->input(num_tables_ + 6).vec<float>().data();\n  auto update_time = ctx->input(num_tables_ + 7).scalar<int64_t>()();\n  auto global_step = ctx->input(num_tables_ + 8).scalar<int64_t>()();\n\n  std::vector<EmbeddingHashTableTfBridge*> hash_tables(num_tables_, nullptr);\n  for (int table_id = 0; table_id < num_tables_; table_id++) {\n    EmbeddingHashTableTfBridge* hash_table = nullptr;\n    OP_REQUIRES_OK(\n        ctx, LookupResource(ctx, HandleFromInput(ctx, table_id), &hash_table));\n    core::ScopedUnref unref(hash_table);\n    hash_tables[table_id] = hash_table;\n  }\n  auto optimize = [&](const int begin, const int end) {\n    for (int shard_id = begin; shard_id < end; shard_id++) {\n      int learning_rate_offset = 0;\n      for (int table_id = 0; table_id < num_tables_; table_id++) {\n        int curr_idx = shard_id * num_tables_ + table_id;\n        auto table = hash_tables[table_id];\n        auto learning_rate = absl::MakeConstSpan(\n            learning_rates + learning_rate_offset, table->slice_size());\n        learning_rate_offset += table->slice_size();\n        table->BatchOptimize(\n            ctx, slot_size_vec[curr_idx], ids + key_offsets[curr_idx],\n            id_grads + emb_offsets[curr_idx], learning_rate, update_time,\n            enable_grad_accumulation_, global_step);\n      }\n    }\n  };\n  // TODO(zouxuan): tweak this number for optimization.\n  const int64 kCostPerUnit = 10000000;\n  const DeviceBase::CpuWorkerThreads& worker_threads =\n      *ctx->device()->tensorflow_cpu_worker_threads();\n  Shard(worker_threads.num_threads, worker_threads.workers, num_shards_,\n        kCostPerUnit, optimize);\n}\n\n\nREGISTER_OP(\"MonolithHashTableAssign\")\n    .Input(\"table_handle: resource\")\n    .Input(\"id_values: int64\")\n    .Input(\"id_updates: float\")\n    .Input(\"req_time: int64\")\n    .Output(\"table_handle_output: resource\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashTableAssign\").Device(DEVICE_CPU),\n                        HashTableAssignOp);\n\nREGISTER_OP(\"MonolithHashTableAssignAdd\")\n    .Input(\"table_handle: resource\")\n    .Input(\"id_values: int64\")\n    .Input(\"id_updates: float\")\n    .Input(\"req_time: int64\")\n    .Output(\"table_handle_output: resource\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashTableAssignAdd\").Device(DEVICE_CPU),\n                        HashTableAssignAddOp);\n\nREGISTER_OP(\"MonolithHashTableOptimize\")\n    .Input(\"table_handle: resource\")\n    .Input(\"id_values: int64\")\n    .Input(\"id_updates: float\")\n    .Input(\"learning_rate_tensor: float\")\n    .Input(\"req_time: int64\")\n    .Input(\"global_step: int64\")\n    .Output(\"table_handle_output: resource\")\n    .Attr(\"use_multi_threads: bool = false\")\n    .Attr(\"queue_size: int = 0\")\n    .Attr(\"enable_dedup: bool = false\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithHashTableOptimize\").Device(DEVICE_CPU),\n                        HashTableOptimizeOp);\n\nREGISTER_OP(\"MonolithHashTableFusedOptimize\")\n    .Input(\"table_handles: N * resource\")\n    .Input(\"ids: int64\")\n    .Input(\"indices: int64\")\n    .Input(\"fused_slot_size: int32\")\n    .Input(\"id_grads: float\")\n    .Input(\"id_offsets: int32\")\n    .Input(\"grad_offsets: int32\")\n    .Input(\"learning_rate_tensors: float\")\n    .Input(\"req_time: int64\")\n    .Input(\"global_step: int64\")\n    .Output(\"table_handles_output: N * resource\")\n    .Attr(\"N: int\")\n    .Attr(\"num_of_shards: int\")\n    .Attr(\"enable_grad_accumulation: bool = false\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int num_tables, num_shards;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"N\", &num_tables));\n      TF_RETURN_IF_ERROR(c->GetAttr(\"num_of_shards\", &num_shards));\n      for (int i = 0; i < num_tables; ++i) {\n        c->set_output(i, c->Scalar());\n      }\n      auto shape = c->input(num_tables + 2);\n      TF_RETURN_IF_ERROR(c->WithRank(shape, 1, &shape));\n      auto dim = c->Dim(shape, 0);\n      TF_RETURN_IF_ERROR(c->WithValue(dim, num_tables * num_shards, &dim));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithHashTableFusedOptimize\").Device(DEVICE_CPU),\n    HashTableFusedOptimizeOp<CPUDevice>);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/inbatch_auc_loss.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cmath>\n#include <cstdio>\n#include <ctime>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass InbatchAucLossOp : public OpKernel {\n public:\n  explicit InbatchAucLossOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    float neg_weight;\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"neg_weight\", &neg_weight));\n    CHECK_GT(neg_weight, 0);\n    CHECK_LE(neg_weight, 1.0);\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    const Tensor *label_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"label\", &label_tensor));\n    const Tensor *logit_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"logit\", &logit_tensor));\n    OP_REQUIRES(ctx, label_tensor->NumElements() == logit_tensor->NumElements(),\n                errors::InvalidArgument(\"the label and logit not match\"));\n\n    std::vector<size_t> positive, negative;\n    auto label_flat = label_tensor->flat<float>();\n    for (size_t i = 0; i < label_flat.size(); ++i) {\n      if (label_flat(i) > 0) {\n        positive.push_back(i);\n      } else if (label_flat(i) > -10000) {\n        negative.push_back(i);\n      }\n    }\n\n    float loss = 0;\n    auto logit_flat = logit_tensor->flat<float>();\n    for (const size_t &i : positive) {\n      float pos_logit = logit_flat(i);\n      for (const size_t &j : negative) {\n        float diff = pos_logit - logit_flat(j);\n        if (diff > -87 && diff < 88) {\n          loss += diff - log(1.0 + exp(diff));\n        } else if (diff <= -87) {\n          loss += diff;\n        }\n      }\n    }\n\n    Tensor *loss_tensor = nullptr;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {}, &loss_tensor));\n    loss_tensor->scalar<float>()() = loss;\n  }\n};\n\nclass InbatchAucLossGradOp : public OpKernel {\n public:\n  explicit InbatchAucLossGradOp(OpKernelConstruction *ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"neg_weight\", &neg_weight_));\n  }\n\n  void Compute(OpKernelContext *ctx) override {\n    const Tensor *label_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"label\", &label_tensor));\n    const Tensor *logit_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"logit\", &logit_tensor));\n    OP_REQUIRES(ctx, label_tensor->NumElements() == logit_tensor->NumElements(),\n                errors::InvalidArgument(\"the label and logit not match\"));\n    const Tensor *grad_tensor;\n    OP_REQUIRES_OK(ctx, ctx->input(\"grad\", &grad_tensor));\n    float grad = grad_tensor->scalar<float>()();\n\n    Tensor *logit_grad_tensor = nullptr;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, logit_tensor->shape(),\n                                             &logit_grad_tensor));\n    auto logit_grad_float = logit_grad_tensor->flat<float>();\n    logit_grad_float.setZero();\n\n    std::vector<size_t> positive, negative;\n    auto label_flat = label_tensor->flat<float>();\n    for (size_t i = 0; i < label_flat.size(); ++i) {\n      if (label_flat(i) > 0) {\n        positive.push_back(i);\n      } else if (label_flat(i) > -10000) {\n        negative.push_back(i);\n      }\n    }\n\n    auto logit_flat = logit_tensor->flat<float>();\n    for (const size_t &i : positive) {\n      float pos_logit = logit_flat(i);\n      for (const size_t &j : negative) {\n        float diff = pos_logit - logit_flat(j);\n        float grad_ij;\n        if (diff > -87 && diff < 88) {\n          grad_ij = 1.0 - 1.0 / (1.0 + exp(-diff));\n        } else if (diff <= -87) {\n          grad_ij = 1;\n        } else {\n          grad_ij = 0;\n        }\n\n        logit_grad_float(i) += grad_ij;\n        logit_grad_float(j) -= neg_weight_ * grad_ij;\n      }\n    }\n\n    if (grad != 1) {\n      for (size_t i = 0; i < logit_grad_float.size(); ++i) {\n        logit_grad_float(i) *= grad;\n      }\n    }\n  }\n\n private:\n  float neg_weight_;\n};\n\nnamespace {\n\nREGISTER_KERNEL_BUILDER(Name(\"InbatchAucLoss\").Device(DEVICE_CPU),\n                        InbatchAucLossOp)\n\nREGISTER_KERNEL_BUILDER(Name(\"InbatchAucLossGrad\").Device(DEVICE_CPU),\n                        InbatchAucLossGradOp)\n\nREGISTER_OP(\"InbatchAucLoss\")\n    .Input(\"label: float\")\n    .Input(\"logit: float\")\n    .Attr(\"neg_weight: float\")\n    .Output(\"loss: float\")\n    .SetDoNotOptimize()\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->Scalar());\n      return Status::OK();\n    });\n\nREGISTER_OP(\"InbatchAucLossGrad\")\n    .Input(\"label: float\")\n    .Input(\"logit: float\")\n    .Input(\"grad: float\")\n    .Attr(\"neg_weight: float\")\n    .Output(\"logit_grad: float\")\n    .SetDoNotOptimize()\n    .SetShapeFn([](shape_inference::InferenceContext *ctx) {\n      ctx->set_output(0, ctx->input(1));\n      return Status::OK();\n    });\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/logging_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstdlib>\n#include <string>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/string_view.h\"\n#include \"absl/synchronization/mutex.h\"\n#include \"absl/time/clock.h\"\n#include \"monolith/native_training/runtime/common/metrics.h\"\n#include \"monolith/native_training/runtime/ops/logging_ops.pb.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nclass TensorTimestampOp : public OpKernel {\n public:\n  explicit TensorTimestampOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"T\", &types_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    for (int i = 0; i < static_cast<int>(types_.size()); ++i) {\n      ctx->set_output(i, ctx->input(i));\n    }\n    Tensor* ts;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(types_.size(), {}, &ts));\n    auto ts_scalar = ts->scalar<int64>();\n    ts_scalar() = absl::ToUnixMicros(absl::Now());\n  }\n\n private:\n  std::vector<DataType> types_;\n};\n\nREGISTER_OP(\"MonolithTensorsTimestamp\")\n    .Attr(\"T: list(type)\")\n    .Input(\"tensors_in: T\")\n    .Output(\"tensors_out: T\")\n    .Output(\"timestamp: int64\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      std::vector<DataType> types;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"T\", &types));\n      for (int i = 0; i < static_cast<int>(types.size()); ++i) {\n        ctx->set_output(i, ctx->input(i));\n      }\n      ctx->set_output(types.size(), ctx->Scalar());\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithTensorsTimestamp\").Device(DEVICE_CPU),\n                        TensorTimestampOp);\n\n// Deprecated.\nclass MetricOp : public OpKernel {\n public:\n  explicit MetricOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"T\", &types_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"key\", &key_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"tags\", &tags_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    for (int i = 0; i < static_cast<int>(types_.size()); ++i) {\n      ctx->set_output(i, ctx->input(i));\n    }\n    const Tensor& value_tensor = ctx->input(types_.size());\n    const float value = value_tensor.scalar<float>()();\n    monolith::GetMetrics()->emit_timer(key_, value, tags_);\n  }\n\n private:\n  std::vector<DataType> types_;\n  std::string key_;\n  std::string tags_;\n};\n\nREGISTER_OP(\"MonolithMetric\")\n    .Attr(\"T: list(type)\")\n    .Attr(\"key: string\")\n    .Attr(\"tags: string\")\n    .Input(\"tensors_in: T\")\n    .Input(\"value: float\")\n    .Output(\"tensors_out: T\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      std::vector<DataType> types;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"T\", &types));\n      for (int i = 0; i < static_cast<int>(types.size()); ++i) {\n        ctx->set_output(i, ctx->input(i));\n      }\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithMetric\").Device(DEVICE_CPU), MetricOp);\n\nclass MetricV2Op : public OpKernel {\n public:\n  explicit MetricV2Op(OpKernelConstruction* ctx)\n      : OpKernel(ctx),\n        stat_last_1_min_(60),\n        stat_last_5_min_(300),\n        stat_last_15_min_(900) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"key\", &key_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"tags\", &tags_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& value_tensor = ctx->input(0);\n    const float value = value_tensor.scalar<float>()();\n    monolith::GetMetrics()->emit_timer(key_, value, tags_);\n    tensorflow::mutex_lock l(mu_);\n    auto now = absl::Now();\n    stat_last_1_min_.PushOne(value, now);\n    stat_last_5_min_.PushOne(value, now);\n    stat_last_15_min_.PushOne(value, now);\n\n    LOG_EVERY_N_SEC(INFO, 600) << absl::StrFormat(\n        \"%s last_1_min: %s\", key_, stat_last_1_min_.DebugString());\n    LOG_EVERY_N_SEC(INFO, 600) << absl::StrFormat(\n        \"%s last_5_min: %s\", key_, stat_last_5_min_.DebugString());\n    LOG_EVERY_N_SEC(INFO, 600) << absl::StrFormat(\n        \"%s last_15_min: %s\", key_, stat_last_15_min_.DebugString());\n  }\n\n private:\n  class MovingStat {\n   public:\n    explicit MovingStat(int64_t time_window_in_sec)\n        : time_window_in_sec_(time_window_in_sec),\n          min_(std::numeric_limits<float>::max()),\n          max_(std::numeric_limits<float>::min()),\n          sum_(0.f) {}\n\n    void PushOne(float value, absl::Time t) {\n      min_ = std::min(min_, value);\n      max_ = std::max(max_, value);\n      sum_ += value;\n      buffer_.emplace_back(value, t);\n\n      while (!buffer_.empty()) {\n        auto start = buffer_.front().second;\n        int64_t delta = absl::ToInt64Seconds(t - start);\n        if (delta > time_window_in_sec_) {\n          sum_ -= buffer_.front().first;\n          buffer_.pop_front();\n        } else {\n          break;\n        }\n      }\n    }\n\n    std::string DebugString() const {\n      float avg = 0, p99 = 0;\n      if (!buffer_.empty()) {\n        avg = sum_ / buffer_.size();\n\n        std::vector<float> values;\n        values.reserve(buffer_.size());\n        for (const auto& p : buffer_) {\n          values.push_back(p.first);\n        }\n        std::sort(values.begin(), values.end());\n        int64_t p99_index = values.size() * 0.99f;\n        p99 = values[p99_index];\n      }\n\n      return absl::StrFormat(\"min: %f, max: %f, avg: %f, p99: %f\", min_, max_,\n                             avg, p99);\n    }\n\n   private:\n    int64_t time_window_in_sec_;\n    float min_;\n    float max_;\n    float sum_;\n    std::deque<std::pair<float, absl::Time>> buffer_;\n  };\n\n private:\n  std::string key_;\n  std::string tags_;\n  tensorflow::mutex mu_;\n  MovingStat stat_last_1_min_ TF_GUARDED_BY(mu_);\n  MovingStat stat_last_5_min_ TF_GUARDED_BY(mu_);\n  MovingStat stat_last_15_min_ TF_GUARDED_BY(mu_);\n};\n\nREGISTER_OP(\"MonolithMetricV2\")\n    .Attr(\"key: string\")\n    .Attr(\"tags: string\")\n    .SetIsStateful()\n    .Input(\"value: float\")\n    .SetShapeFn(shape_inference::NoOutputs);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithMetricV2\").Device(DEVICE_CPU),\n                        MetricV2Op);\n\nstruct MachineInfo : ResourceBase {\n  int64 mem_limit = 0;\n\n  std::string DebugString() const {\n    return absl::StrFormat(\"mem_limit: %lld\", mem_limit);\n  }\n};\n\nclass MachineInfoOp : public ResourceOpKernel<MachineInfo> {\n public:\n  explicit MachineInfoOp(OpKernelConstruction* c) : ResourceOpKernel(c) {\n    OP_REQUIRES_OK(c, c->GetAttr(\"mem_limit\", &mem_limit_));\n  }\n\n private:\n  Status CreateResource(MachineInfo** info_out)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    auto* info = new MachineInfo();\n    info->mem_limit = mem_limit_;\n    *info_out = info;\n    return Status::OK();\n  }\n\n  int64 mem_limit_;  // Unit bytes\n};\n\nREGISTER_OP(\"MonolithMachineInfo\")\n    .Output(\"handle: resource\")\n    .Attr(\"mem_limit: int\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithMachineInfo\").Device(DEVICE_CPU),\n                        MachineInfoOp);\n\nclass MonolithCheckMachineHealthOp : public OpKernel {\n public:\n  explicit MonolithCheckMachineHealthOp(OpKernelConstruction* c)\n      : OpKernel(c) {}\n\n  void Compute(OpKernelContext* c) override {\n    int64 current_mem = GetCurrentUsage();\n    OP_REQUIRES(c, current_mem > 0,\n                errors::Internal(\"Unable to get the current process usage.\"));\n    MachineInfo* info = nullptr;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 0), &info));\n    core::ScopedUnref unref(info);\n    Tensor* result_tensor;\n    OP_REQUIRES_OK(c, c->allocate_output(0, {}, &result_tensor));\n    auto result_scalar = result_tensor->scalar<tstring>();\n    MachineHealthResult result;\n    if (current_mem >= info->mem_limit) {\n      result.set_status(MachineHealthResult::OUT_OF_MEMORY);\n      result.set_message(\n          absl::StrFormat(\"Memory limit exceeded. Current: %lld, Limit: %lld\",\n                          current_mem, info->mem_limit));\n    }\n    result_scalar() = result.SerializeAsString();\n  }\n\n  int64_t GetCurrentUsage() {\n    FILE* file = fopen(\"/proc/self/status\", \"r\");\n    int64_t result = 0;\n    char line[128];\n    while (fgets(line, 128, file) != NULL) {\n      if (std::strncmp(line, \"VmRSS:\", 6) == 0) {\n        // The line is like `VmRSS:       708 kB`\n        result = std::strtol(line + 6, nullptr, 10);\n        break;\n      }\n    }\n    fclose(file);\n    return result * 1024;\n  }\n};\n\nREGISTER_OP(\"MonolithCheckMachineHealth\")\n    .Input(\"machine_info_handle: resource\")\n    .Output(\"serialized_result: string\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      ctx->set_output(0, ctx->Scalar());\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithCheckMachineHealth\").Device(DEVICE_CPU),\n                        MonolithCheckMachineHealthOp);\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/logging_ops.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax=\"proto2\";\n\npackage tensorflow.monolith_tf;\n\n\n// To make it faster, we need to guarantee that\n// it is empty message when it is OK.\nmessage MachineHealthResult {\n  enum MachineHealthStatus {\n    OK = 0;\n    OUT_OF_MEMORY = 1;\n  }\n  optional MachineHealthStatus status = 1;\n  optional string message = 2;\n}"
  },
  {
    "path": "monolith/native_training/runtime/ops/map_id_to_embedding.cu.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#if GOOGLE_CUDA\n#define EIGEN_USE_GPU\n#include \"monolith/native_training/runtime/ops/alloc_utils.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/register_types.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/kernels/gpu_device_array.h\"\n#include \"tensorflow/core/kernels/gpu_device_array_gpu.h\"\n#include \"tensorflow/core/util/gpu_kernel_helper.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntypedef Eigen::GpuDevice GPUDevice;\n\ntemplate <typename T>\n__global__ void FusedGatherKernel(\n    const T* __restrict__ fused_embeddings,\n    GpuDeviceArrayStruct<const int32*> input_ptr_data,\n    GpuDeviceArrayStruct<T*> output_ptr_data,\n    GpuDeviceArrayStruct<int32> embedding_dims_data,\n    GpuDeviceArrayStruct<int32> offsets_data, int32 size) {\n  const int32** input_ptrs = GetGpuDeviceArrayOnDevice(&input_ptr_data);\n  T** output_ptrs = GetGpuDeviceArrayOnDevice(&output_ptr_data);\n\n  int32* offsets = GetGpuDeviceArrayOnDevice(&offsets_data);\n  int32* embedding_dims = GetGpuDeviceArrayOnDevice(&embedding_dims_data);\n\n  // if using shared memory\n  // Ref:\n  // https://github.com/tensorflow/tensorflow/blob/v2.4.0/tensorflow/core/kernels/split_lib_gpu.cu.cc#L124\n  GPU_DYNAMIC_SHARED_MEM_DECL(sizeof(int32), unsigned char, smem);\n  int32* smem_offsets = reinterpret_cast<int32*>(smem);\n  int32* smem_embedding_dims = smem_offsets + offsets_data.size;\n  for (int x = threadIdx.x; x < offsets_data.size; x += blockDim.x) {\n    smem_offsets[x] = offsets[x];\n  }\n  for (int x = threadIdx.x; x < embedding_dims_data.size; x += blockDim.x) {\n    smem_embedding_dims[x] = embedding_dims[x];\n  }\n  __syncthreads();\n  offsets = smem_offsets;\n  embedding_dims = smem_embedding_dims;\n\n  int i = 0;\n  for (int32 idx : GpuGridRangeX<int32>(size)) {\n    // safe offsets read: when idx == size - 1, i+1 == num_inputs\n    // since num_inputs := number of merged slot < 100,\n    // linear search would be sufficient here\n    while (offsets[i + 1] <= idx) ++i;\n    int32 local_idx = idx - offsets[i];\n    int32 dim = embedding_dims[i];\n    int j = local_idx / dim;\n    int k = local_idx % dim;\n\n    int32 emb_offset = input_ptrs[i][j];\n    output_ptrs[i][local_idx] = ldg(fused_embeddings + emb_offset + k);\n  }\n}\n\ntemplate <typename T>\n__global__ void FusedGatherGradKernel(\n    T* output_ptr, GpuDeviceArrayStruct<const T*> input_ptr_data,\n    GpuDeviceArrayStruct<const int32*> offset_ptr_data,\n    GpuDeviceArrayStruct<int32> embedding_dims_data,\n    GpuDeviceArrayStruct<int32> offsets_data, int32 size, const float* _scale) {\n  float scale = *_scale;\n  const T** input_ptrs = GetGpuDeviceArrayOnDevice(&input_ptr_data);\n  const int32** offset_ptrs = GetGpuDeviceArrayOnDevice(&offset_ptr_data);\n\n  int32* offsets = GetGpuDeviceArrayOnDevice(&offsets_data);\n  int32* embedding_dims = GetGpuDeviceArrayOnDevice(&embedding_dims_data);\n\n  // if using shared memory\n  // Ref:\n  // https://github.com/tensorflow/tensorflow/blob/v2.4.0/tensorflow/core/kernels/split_lib_gpu.cu.cc#L124\n  GPU_DYNAMIC_SHARED_MEM_DECL(sizeof(int32), unsigned char, smem);\n  int32* smem_offsets = reinterpret_cast<int32*>(smem);\n  int32* smem_embedding_dims = smem_offsets + offsets_data.size;\n  for (int x = threadIdx.x; x < offsets_data.size; x += blockDim.x) {\n    smem_offsets[x] = offsets[x];\n  }\n  for (int x = threadIdx.x; x < embedding_dims_data.size; x += blockDim.x) {\n    smem_embedding_dims[x] = embedding_dims[x];\n  }\n  __syncthreads();\n  offsets = smem_offsets;\n  embedding_dims = smem_embedding_dims;\n\n  int i = 0;\n  for (int32 idx : GpuGridRangeX<int32>(size)) {\n    // safe offsets read: when idx == size - 1, i+1 == num_inputs\n    // since num_inputs := number of merged slot < 100,\n    // linear search would be sufficient here\n    while (offsets[i + 1] <= idx) ++i;\n    int32 local_idx = idx - offsets[i];\n    int32 dim = embedding_dims[i];\n    int j = local_idx / dim;\n    int k = local_idx % dim;\n\n    const int32 emb_offset = offset_ptrs[i][j];\n    GpuAtomicAdd(output_ptr + emb_offset + k, input_ptrs[i][local_idx] * scale);\n  }\n}\n\ntemplate <typename T>\nstruct SetZeroFunctor {\n  void operator()(const GPUDevice& d, typename TTypes<T>::Flat out) {\n    To32Bit(out).device(d) = To32Bit(out).constant(T(0));\n  }\n};\n\ntemplate <typename T>\nclass FusedGatherEmbeddingsByInputOpGPU : public OpKernel {\n public:\n  explicit FusedGatherEmbeddingsByInputOpGPU(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"M\", &num_of_inputs_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"embedding_dims\", &embedding_dims_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    auto fused_embeddings_flat = ctx->input(0).flat<T>();\n    OpInputList inputs;\n    OP_REQUIRES_OK(ctx, ctx->input_list(\"embedding_offsets\", &inputs));\n\n    GpuDeviceArrayOnHost<const int32*> input_ptrs(ctx, num_of_inputs_);\n    GpuDeviceArrayOnHost<int32> embedding_dims(ctx, num_of_inputs_);\n    GpuDeviceArrayOnHost<int32> offsets(ctx, num_of_inputs_ + 1);\n    OP_REQUIRES_OK(ctx, input_ptrs.Init());\n    OP_REQUIRES_OK(ctx, embedding_dims.Init());\n    OP_REQUIRES_OK(ctx, offsets.Init());\n    int smem_usage = sizeof(int32) * (num_of_inputs_ + 1 + num_of_inputs_);\n    // smem: offsets + embedding_dims\n    FusedAlignedOutputAllocator<EIGEN_MAX_ALIGN_BYTES / sizeof(T)> fao_alloc(\n        ctx);\n    for (int i = 0; i < num_of_inputs_; ++i) {\n      auto dim = embedding_dims_[i];\n      auto s = inputs[i].NumElements();  // == input[i].shape().dim_size(0)\n      embedding_dims.Set(i, dim);\n      offsets.Set(i, fao_alloc.get_unaligned_total());\n      input_ptrs.Set(i, inputs[i].flat<int32>().data());\n      fao_alloc.add_slice(s * dim);\n    }\n    // offset val here is total workload\n    offsets.Set(num_of_inputs_, fao_alloc.get_unaligned_total());\n    OP_REQUIRES_OK(ctx, offsets.Finalize());\n    OP_REQUIRES_OK(ctx, input_ptrs.Finalize());\n    OP_REQUIRES_OK(ctx, embedding_dims.Finalize());\n\n    GpuDeviceArrayOnHost<T*> output_ptrs(ctx, num_of_inputs_);\n    OP_REQUIRES_OK(ctx, output_ptrs.Init());\n    fao_alloc.allocate(ctx->expected_output_dtype(0));\n    for (int i = 0; i < num_of_inputs_; ++i) {\n      auto dim = embedding_dims_[i];\n      auto s = inputs[i].NumElements();  // == input[i].shape().dim_size(0)\n      Tensor out = fao_alloc.get_slice({s, dim});\n      output_ptrs.Set(i, out.flat<T>().data());\n      ctx->set_output(i, std::move(out));\n    }\n    OP_REQUIRES_OK(ctx, output_ptrs.Finalize());\n\n    GPUDevice gpu_device = ctx->eigen_device<GPUDevice>();\n\n    // We use a 2D LaunchConfig here to make thread (x, y) of every\n    // input tensor y better benefit from the ldg local cache read\n    // for multiple x of x + n * grid_stride.\n    // >>> auto config = GetGpu2DLaunchConfig(max_input_size, num_of_inputs_,\n    //                                        gpu_device);\n    //\n    // However, across inputs the distribution of elements thus thread workload\n    // can be imbalanced in this implementation.\n    //\n    /// One alternative implmentation for this Op is based on ComputeAsync +\n    // Multiple Kernel Calls, for example similar to\n    // https://github.com/tensorflow/tensorflow/blob/v2.4.0/tensorflow/core/kernels/dynamic_partition_op_gpu.cu.cc#L454-L469\n    //\n    // The chosen implementation is to distribute the output workload balanced\n    // on threads,\n    // while searching the idx input bucket to which the output val belongs to.\n    auto config =\n        GetGpuLaunchConfig(fao_alloc.get_unaligned_total(), gpu_device);\n    auto grid_offset = 24;\n    char* ptr = std::getenv(\"MONOLITH_GT_OVERSUB_SM\");\n    if (ptr) grid_offset = std::atoi(ptr);\n    grid_offset += 2;\n    GpuLaunchKernel(\n        FusedGatherKernel<T>, config.block_count - grid_offset, config.thread_per_block,\n        /*shared_memory_size_bytes=*/smem_usage, gpu_device.stream(),\n        fused_embeddings_flat.data(), input_ptrs.data(), output_ptrs.data(),\n        embedding_dims.data(), offsets.data(), fao_alloc.get_unaligned_total());\n  }\n\n private:\n  int num_of_inputs_;\n  std::vector<int32> embedding_dims_;\n};\n\ntemplate <typename T>\nclass FusedGatherEmbeddingsByInputGradientOpGPU : public OpKernel {\n public:\n  explicit FusedGatherEmbeddingsByInputGradientOpGPU(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"M\", &num_of_inputs_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"embedding_dims\", &embedding_dims_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    GPUDevice gpu_device = ctx->eigen_device<GPUDevice>();\n\n    OpInputList inputs;\n    OP_REQUIRES_OK(ctx, ctx->input_list(\"grads\", &inputs));\n    OpInputList embedding_offsets;\n    OP_REQUIRES_OK(ctx,\n                   ctx->input_list(\"embedding_offsets\", &embedding_offsets));\n\n    GpuDeviceArrayOnHost<const T*> input_ptrs(ctx, num_of_inputs_);\n    GpuDeviceArrayOnHost<const int32*> emb_offset_ptrs(ctx, num_of_inputs_);\n    OP_REQUIRES_OK(ctx, input_ptrs.Init());\n    OP_REQUIRES_OK(ctx, emb_offset_ptrs.Init());\n\n    GpuDeviceArrayOnHost<int32> embedding_dims(ctx, num_of_inputs_);\n    OP_REQUIRES_OK(ctx, embedding_dims.Init());\n\n    int32 offset = 0;\n    GpuDeviceArrayOnHost<int32> offsets(ctx,\n                                        num_of_inputs_ + 1);  // input_offsets\n    OP_REQUIRES_OK(ctx, offsets.Init());\n    int smem_usage = sizeof(int32) * (num_of_inputs_ + 1 + num_of_inputs_);\n    // smem: offsets + embedding_dims\n    for (int i = 0; i < num_of_inputs_; ++i) {\n      input_ptrs.Set(i, inputs[i].flat<T>().data());\n      emb_offset_ptrs.Set(i, embedding_offsets[i].flat<int32>().data());\n      auto s = embedding_offsets[i].NumElements();\n      auto dim = embedding_dims_[i];\n      embedding_dims.Set(i, dim);\n      offsets.Set(i, offset);\n      offset += s * dim;\n    }\n    offsets.Set(num_of_inputs_, offset);  // offset val here is total workload\n    OP_REQUIRES_OK(ctx, offsets.Finalize());\n    OP_REQUIRES_OK(ctx, input_ptrs.Finalize());\n    OP_REQUIRES_OK(ctx, emb_offset_ptrs.Finalize());\n    OP_REQUIRES_OK(ctx, embedding_dims.Finalize());\n\n    int32 fused_embeddings_size = ctx->input(0).scalar<int32>().data()[0];\n    Tensor* output_tensor;\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(0, TensorShape({fused_embeddings_size}),\n                                        &output_tensor));\n\n    SetZeroFunctor<T> zero_functor;\n    auto output = output_tensor->flat<T>();\n    zero_functor(gpu_device, output);\n\n    auto config = GetGpuLaunchConfig(offset, gpu_device);\n    GpuLaunchKernel(\n        FusedGatherGradKernel<T>, config.block_count, config.thread_per_block,\n        /*shared_memory_size_bytes=*/smem_usage, gpu_device.stream(),\n        output.data(), input_ptrs.data(), emb_offset_ptrs.data(),\n        embedding_dims.data(), offsets.data(), offset,\n        ctx->input(2 * num_of_inputs_ + 1).flat<float>().data());\n  }\n\n private:\n  int num_of_inputs_;\n  std::vector<int32> embedding_dims_;\n};\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithFusedGatherEmbeddingsByInput\").Device(DEVICE_GPU),\n    FusedGatherEmbeddingsByInputOpGPU<float>);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithFusedGatherEmbeddingsByInputGradient\")\n                            .Device(DEVICE_GPU)\n                            .HostMemory(\"fused_embeddings_size\"),\n                        FusedGatherEmbeddingsByInputGradientOpGPU<float>);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // GOOGLE_CUDA\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/map_id_to_embedding_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstring>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n#include \"tensorflow/core/util/work_sharder.h\"\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/avx_utils.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\n// The input embeddings are a list of 2D tensors.\n// This represents the embedding: embeddings[index].chip<0>(pos)\nstruct EmbeddingLocation {\n  int64 index;\n  int64 pos;\n};\n\n}  // namespace\n\n// Maps input ids into embeddings.\nclass MapIdToEmbeddingOp : public OpKernel {\n public:\n  explicit MapIdToEmbeddingOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"num_splits\", &num_splits_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"use_multi_threads\", &use_multi_threads_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    absl::flat_hash_map<int64, EmbeddingLocation> id_to_loc;\n    int64 total_split_ids = 0;\n    for (int i = 0; i < num_splits_; ++i) {\n      total_split_ids += ctx->input(i).flat<int64>().dimension(0);\n    }\n    id_to_loc.reserve(total_split_ids);\n\n    for (int i = 0; i < num_splits_; ++i) {\n      auto ids = ctx->input(i).flat<int64>();\n      for (int64 j = 0; j < ids.dimension(0); ++j) {\n        id_to_loc.insert({ids(j), {i, j}});\n      }\n    }\n\n    std::vector<TTypes<const float>::Matrix> embeddings;\n    embeddings.reserve(num_splits_);\n    for (int i = 0; i < num_splits_; ++i) {\n      embeddings.emplace_back(ctx->input(num_splits_ + i).matrix<float>());\n    }\n    int64 embedding_dim = embeddings[0].dimension(1);\n\n    const Tensor& input = ctx->input(2 * num_splits_);\n    auto input_flat = input.flat<int64>();\n    Tensor* output;\n    TensorShape output_shape = input.shape();\n    output_shape.AddDim(embedding_dim);\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, output_shape, &output));\n    auto output_mat =\n        output->shaped<float, 2>({input.NumElements(), embedding_dim});\n    auto map_fn = [&](const int64 begin, const int64 end) {\n      for (int64 k = begin; k < end; ++k) {\n        auto iter = id_to_loc.find(input_flat(k));\n        if (iter == id_to_loc.end()) {\n          return ctx->SetStatus(\n              errors::InvalidArgument(\"Unable to map id \", input_flat(k)));\n        }\n        const EmbeddingLocation& loc = iter->second;\n        output_mat.chip<0>(k) = embeddings[loc.index].chip<0>(loc.pos);\n      }\n    };\n\n    int64 total = input_flat.dimension(0);\n    if (use_multi_threads_) {\n      auto worker_threads = ctx->device()->tensorflow_cpu_worker_threads();\n      auto workers = worker_threads->workers;\n      int num_shards = std::min(5LL, std::max(1LL, total / 10000));\n      int64 block_size = total / num_shards;\n      workers->ParallelFor(\n          total, thread::ThreadPool::SchedulingParams(\n                     thread::ThreadPool::SchedulingStrategy::kFixedBlockSize,\n                     absl::nullopt, block_size),\n          map_fn);\n    } else {\n      map_fn(0, total);\n    }\n  }\n\n private:\n  int num_splits_;\n  bool use_multi_threads_;\n};\n\nclass MapIdToEmbeddingGradientOp : public OpKernel {\n public:\n  explicit MapIdToEmbeddingGradientOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"num_splits\", &num_splits_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& input = ctx->input(num_splits_);\n    const Tensor& grads = ctx->input(num_splits_ + 1);\n    const int64 embedding_size = grads.dim_size(grads.dims() - 1);\n    absl::flat_hash_map<int64, EmbeddingLocation> id_to_loc;\n    std::vector<TTypes<float>::Matrix> embedding_grads_mats;\n    embedding_grads_mats.reserve(num_splits_);\n    for (int i = 0; i < num_splits_; ++i) {\n      auto ids = ctx->input(i).flat<int64>();\n      int64 len_ids = ids.dimension(0);\n      for (int64 j = 0; j < ids.dimension(0); ++j) {\n        id_to_loc.insert({ids(j), {i, j}});\n      }\n      Tensor* output;\n      OP_REQUIRES_OK(\n          ctx, ctx->allocate_output(i, {len_ids, embedding_size}, &output));\n      std::memset(output->data(), 0, output->AllocatedBytes());\n      embedding_grads_mats.emplace_back(output->matrix<float>());\n    }\n    auto input_flat = input.flat<int64>();\n    auto grads_mat =\n        grads.shaped<float, 2>({input.NumElements(), embedding_size});\n    for (int64 k = 0; k < input_flat.dimension(0); ++k) {\n      const EmbeddingLocation& loc = id_to_loc.find(input_flat(k))->second;\n      embedding_grads_mats[loc.index].chip<0>(loc.pos) += grads_mat.chip<0>(k);\n    }\n  }\n\n private:\n  int num_splits_;\n};\n\n// Maps input ids into embeddings with only 1 tensor.\nclass GatherEmbeddingsByInputOp : public OpKernel {\n public:\n  explicit GatherEmbeddingsByInputOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"use_multi_threads\", &use_multi_threads_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    absl::flat_hash_map<int64, int64> id_to_loc;\n    auto ids = ctx->input(0).flat<int64>();\n    for (int i = 0; i < ids.dimension(0); ++i) {\n      id_to_loc.insert({ids(i), i});\n    }\n\n    TTypes<const float>::Matrix embeddings = ctx->input(1).matrix<float>();\n    OP_REQUIRES(ctx, embeddings.dimension(0) == ids.dimension(0),\n                errors::InvalidArgument(\"See unmatched embedding dim \",\n                                        embeddings.dimension(0), \" and id dim \",\n                                        ids.dimension(0)));\n    int64 embedding_dim = embeddings.dimension(1);\n\n    const Tensor& input = ctx->input(2);\n    auto input_flat = input.flat<int64>();\n    Tensor *output, *output_index_mapping;\n    TensorShape output_shape = input.shape();\n    output_shape.AddDim(embedding_dim);\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, output_shape, &output));\n    OP_REQUIRES_OK(\n        ctx, ctx->allocate_output(1, input.shape(), &output_index_mapping));\n    auto output_mat =\n        output->shaped<float, 2>({input.NumElements(), embedding_dim});\n    auto output_index_mapping_flat = output_index_mapping->flat<int64>();\n\n    auto fill_fn = [&](const int64 begin, const int64 end) {\n      for (int64 k = begin; k < end; ++k) {\n        auto iter = id_to_loc.find(input_flat(k));\n        if (iter == id_to_loc.end()) {\n          return ctx->SetStatus(\n              errors::InvalidArgument(\"Unable to map id \", input_flat(k)));\n        }\n        const int64& pos = iter->second;\n        output_mat.chip<0>(k) = embeddings.chip<0>(pos);\n        output_index_mapping_flat(k) = pos;\n      }\n    };\n    if (use_multi_threads_) {\n      // TODO(zouxuan): tune this for performance.\n      const int64 kCostPerUnit = 4 * embedding_dim;\n      const DeviceBase::CpuWorkerThreads& worker_threads =\n          *ctx->device()->tensorflow_cpu_worker_threads();\n      Shard(worker_threads.num_threads, worker_threads.workers,\n            input_flat.dimension(0), kCostPerUnit, fill_fn);\n    } else {\n      fill_fn(0, input_flat.dimension(0));\n    }\n  }\n\n private:\n  bool use_multi_threads_;\n};\n\nclass GatherEmbeddingsByInputGradientOp : public OpKernel {\n public:\n  explicit GatherEmbeddingsByInputGradientOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& ids = ctx->input(0);\n    const Tensor& grads = ctx->input(1);\n    auto index_mapping_flat = ctx->input(2).flat<int64>();\n    auto ids_flat = ids.flat<int64>();\n    // Reshape it to len(input):embedding_size shape.\n    const int64 embedding_size = grads.dim_size(grads.dims() - 1);\n    const int64 input_size = index_mapping_flat.dimension(0);\n    auto grads_mat = grads.shaped<float, 2>({input_size, embedding_size});\n    const int64 len_ids = ids_flat.dimension(0);\n\n    Tensor* output;\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(0, {len_ids, embedding_size}, &output));\n    std::memset(output->data(), 0, output->AllocatedBytes());\n    TTypes<float>::Matrix embedding_grads_mats = output->matrix<float>();\n\n    for (int64 k = 0; k < input_size; ++k) {\n      const int64 loc = index_mapping_flat(k);\n      embedding_grads_mats.chip<0>(loc) += grads_mat.chip<0>(k);\n    }\n  }\n};\n\nclass FusedGatherEmbeddingsByInputOp : public OpKernel {\n public:\n  explicit FusedGatherEmbeddingsByInputOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"M\", &num_of_inputs_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"embedding_dims\", &embedding_dims_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    auto fused_embeddings_flat = ctx->input(0).flat<float>();\n\n    OpInputList inputs;\n    OP_REQUIRES_OK(ctx, ctx->input_list(\"embedding_offsets\", &inputs));\n    OpOutputList outputs;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"output\", &outputs));\n\n    std::vector<float*> output_ptrs(outputs.size());\n    DCHECK_EQ(num_of_inputs_, outputs.size());\n    for (int i = 0; i < num_of_inputs_; ++i) {\n      TensorShape output_shape = inputs[i].shape();\n      output_shape.AddDim(embedding_dims_[i]);\n      Tensor* out;\n      OP_REQUIRES_OK(ctx, outputs.allocate(i, output_shape, &out));\n      output_ptrs[i] = out->flat<float>().data();\n    }\n\n    auto fill_fn = [&](const int64 begin, const int64 end) {\n      for (int i = begin; i < end; ++i) {\n        auto embedding_offset_vec = inputs[i].vec<int32>();\n        int embedding_dim = embedding_dims_[i];\n\n        for (int j = 0; j < embedding_offset_vec.size(); ++j) {\n          auto offset = embedding_offset_vec(j);\n          std::memcpy(output_ptrs[i] + j * embedding_dim,\n                      fused_embeddings_flat.data() + offset,\n                      embedding_dim * sizeof(float));\n        }\n      }\n    };\n\n    auto worker_threads = ctx->device()->tensorflow_cpu_worker_threads();\n    worker_threads->workers->ParallelFor(\n        num_of_inputs_,\n        thread::ThreadPool::SchedulingParams(\n            thread::ThreadPool::SchedulingStrategy::kFixedBlockSize,\n            absl::nullopt,\n            1),  // block_size\n        fill_fn);\n  }\n\n private:\n  int num_of_inputs_;\n  std::vector<int> embedding_dims_;\n};\n\nclass FusedGatherEmbeddingsByInputGradientOp : public OpKernel {\n public:\n  explicit FusedGatherEmbeddingsByInputGradientOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"embedding_dims\", &embedding_dims_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"M\", &num_of_inputs_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    int32 fused_embeddings_size = ctx->input(0).flat<int32>()(0);\n    Tensor* output;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(\n                            0, TensorShape({fused_embeddings_size}), &output));\n    auto output_flat = output->flat<float>();\n    std::memset(output->data(), 0, output->AllocatedBytes());\n    // By design, different inputs from num_of_inputs_ are sharded into\n    // different positions in the flattened gradients, and thus simply do a\n    // parallel fill function.\n    auto fill_fn = [&](const int64 begin, const int64 end) {\n      for (int i = begin; i < end; ++i) {\n        auto input_flat = ctx->input(1 + i).flat<float>();\n        auto embedding_offset_vec =\n            ctx->input(num_of_inputs_ + 1 + i).vec<int32>();\n        int embedding_dim = embedding_dims_[i];\n        for (int j = 0; j < embedding_offset_vec.dimension(0); ++j) {\n          int32 offset = embedding_offset_vec(j);\n          const float* input_a =\n              const_cast<float*>(input_flat.data()) + j * embedding_dim;\n          float* output_b = static_cast<float*>(output_flat.data()) + offset;\n          // Use AVX acceleration for reducesum.\n          ::monolith::hash_table::ReduceSum(input_a, output_b, output_b,\n                                            embedding_dim);\n        }\n      }\n    };\n\n    auto worker_threads = ctx->device()->tensorflow_cpu_worker_threads();\n    worker_threads->workers->ParallelFor(\n        num_of_inputs_,\n        thread::ThreadPool::SchedulingParams(\n            thread::ThreadPool::SchedulingStrategy::kFixedBlockSize,\n            absl::nullopt,\n            1),  // block_size\n        fill_fn);\n  }\n\n private:\n  std::vector<int> embedding_dims_;\n  int num_of_inputs_;\n};\n\nREGISTER_OP(\"MonolithMapIdToEmbedding\")\n    .Input(\"ids: num_splits * int64\")\n    .Input(\"embeddings: num_splits * float\")\n    .Input(\"input: int64\")\n    .Output(\"output: float\")\n    .Attr(\"num_splits: int\")\n    .Attr(\"use_multi_threads: bool = true\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int num_splits;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"num_splits\", &num_splits));\n      shape_inference::ShapeHandle embedding_shape =\n          c->MakeShape({c->Dim(c->input(num_splits), -1)});\n      shape_inference::ShapeHandle input_shape = c->input(2 * num_splits);\n      shape_inference::ShapeHandle output_shape;\n      TF_RETURN_IF_ERROR(\n          c->Concatenate(input_shape, embedding_shape, &output_shape));\n      c->set_output(0, output_shape);\n      return Status::OK();\n    });\n\nREGISTER_OP(\"MonolithMapIdToEmbeddingGradient\")\n    .Input(\"ids: num_splits * int64\")\n    .Input(\"input: int64\")\n    .Input(\"grads: float\")\n    .Output(\"embedding_grads: num_splits * float\")\n    .Attr(\"num_splits: int\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      int num_splits;\n      TF_RETURN_IF_ERROR(ctx->GetAttr(\"num_splits\", &num_splits));\n      shape_inference::DimensionHandle embedding_size =\n          ctx->Dim(ctx->input(num_splits + 1), -1);\n      for (int i = 0; i < num_splits; ++i) {\n        shape_inference::DimensionHandle len_ids = ctx->Dim(ctx->input(i), 0);\n        ctx->set_output(i, ctx->MakeShape({len_ids, embedding_size}));\n      }\n      return Status::OK();\n    });\n\nREGISTER_OP(\"MonolithGatherEmbeddingsByInput\")\n    .Input(\"ids: int64\")\n    .Input(\"embeddings: float\")\n    .Input(\"input: int64\")\n    .Output(\"output: float\")\n    .Output(\"output_index_mapping: int64\")\n    .SetDoNotOptimize()  // Crash with grappler.\n    .Attr(\"use_multi_threads: bool = false\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      shape_inference::ShapeHandle embedding_shape =\n          c->MakeShape({c->Dim(c->input(1), -1)});\n      shape_inference::ShapeHandle input_shape = c->input(2);\n      shape_inference::ShapeHandle output_shape;\n      TF_RETURN_IF_ERROR(\n          c->Concatenate(input_shape, embedding_shape, &output_shape));\n      c->set_output(0, output_shape);\n      c->set_output(1, input_shape);\n      return Status::OK();\n    });\n\nREGISTER_OP(\"MonolithGatherEmbeddingsByInputGradient\")\n    .Input(\"ids: int64\")\n    .Input(\"grads: float\")\n    .Input(\"index_mapping: int64\")\n    .Output(\"embedding_grads: float\")\n    .SetDoNotOptimize()  // Crash with grappler.\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      shape_inference::DimensionHandle embedding_size =\n          ctx->Dim(ctx->input(1), -1);\n      shape_inference::DimensionHandle len_ids = ctx->Dim(ctx->input(0), 0);\n      ctx->set_output(0, ctx->MakeShape({len_ids, embedding_size}));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"MonolithFusedGatherEmbeddingsByInput\")\n    .Input(\"fused_embeddings: float\")\n    .Input(\"embedding_offsets: M * int32\")\n    .Output(\"output: M * float\")\n    .Attr(\"embedding_dims: list(int)\")\n    .Attr(\"M: int\")\n    .SetDoNotOptimize()  // Crash with grappler.\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int M;\n      std::vector<int> embedding_dims;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"embedding_dims\", &embedding_dims));\n      TF_RETURN_IF_ERROR(c->GetAttr(\"M\", &M));\n      for (int i = 0; i < M; ++i) {\n        shape_inference::DimensionHandle dim = c->Dim(c->input(1 + i), 0);\n        c->set_output(i, c->MakeShape({dim, embedding_dims[i]}));\n      }\n      return Status::OK();\n    });\n\nREGISTER_OP(\"MonolithFusedGatherEmbeddingsByInputGradient\")\n    .Input(\"fused_embeddings_size: int32\")\n    .Input(\"grads: M * float\")\n    .Input(\"embedding_offsets: M * int32\")\n    .Input(\"scale: float\")\n    .Output(\"output: float\")\n    .Attr(\"embedding_dims: list(int)\")\n    .Attr(\"M: int\")\n    .SetDoNotOptimize()  // Crash with grappler.\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      shape_inference::DimensionHandle output_dim;\n      TF_RETURN_IF_ERROR(ctx->MakeDimForScalarInput(0, &output_dim));\n      ctx->set_output(0, ctx->Vector(output_dim));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithMapIdToEmbedding\").Device(DEVICE_CPU),\n                        MapIdToEmbeddingOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithMapIdToEmbeddingGradient\").Device(DEVICE_CPU),\n    MapIdToEmbeddingGradientOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithGatherEmbeddingsByInput\").Device(DEVICE_CPU),\n    GatherEmbeddingsByInputOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithGatherEmbeddingsByInputGradient\").Device(DEVICE_CPU),\n    GatherEmbeddingsByInputGradientOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithFusedGatherEmbeddingsByInput\").Device(DEVICE_CPU),\n    FusedGatherEmbeddingsByInputOp);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithFusedGatherEmbeddingsByInputGradient\").Device(DEVICE_CPU),\n    FusedGatherEmbeddingsByInputGradientOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/multi_hash_table.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_MULTI_HASH_TABLE_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_MULTI_HASH_TABLE_H_\n\n#include <vector>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_cat.h\"\n#include \"absl/strings/string_view.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass MultiHashTable : public ResourceBase {\n public:\n  explicit MultiHashTable(absl::string_view shared_name)\n      : shared_name_(std::string(shared_name)) {}\n\n  void add_table(absl::string_view name,\n                 core::RefCountPtr<EmbeddingHashTableTfBridge> table) {\n    names_.push_back(std::string(name));\n    tables_.push_back(std::move(table));\n  }\n\n  EmbeddingHashTableTfBridge* table(int i) const { return tables_[i].get(); }\n\n  const std::vector<std::string>& names() const { return names_; }\n\n  const std::string& name(int i) { return names_[i]; }\n\n  int size() const { return names_.size(); }\n\n  const std::string& shared_name() const { return shared_name_; }\n\n  std::string DebugString() const override {\n    std::string ret;\n    for (int i = 0; i < size(); ++i) {\n      ret += absl::StrCat(\"name: \", names_[i], \":\", tables_[i]->DebugString(),\n                          \";\");\n    }\n    return ret;\n  }\n\n  int64 MemoryUsed() const override {\n    int64 ret = 0;\n    for (const auto& table : tables_) {\n      ret += table->MemoryUsed();\n    }\n    return ret;\n  }\n\n private:\n  std::string shared_name_;\n  std::vector<core::RefCountPtr<EmbeddingHashTableTfBridge>> tables_;\n  std::vector<std::string> names_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_MULTI_HASH_TABLE_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/multi_hash_table_lookup_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <vector>\n\n#include \"absl/strings/str_format.h\"\n#include \"monolith/native_training/runtime/common/metrics.h\"\n#include \"monolith/native_training/runtime/hash_table/utils.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"monolith/native_training/runtime/ops/multi_hash_table.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/types.h\"\n#include \"tensorflow/core/platform/default/integral_types.h\"\n#include \"tensorflow/core/util/work_sharder.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing CPUDevice = Eigen::ThreadPoolDevice;\nclass MultiHashTableLookupOp : public OpKernel {\n public:\n  explicit MultiHashTableLookupOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    core::RefCountPtr<MultiHashTable> mtable;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &mtable));\n    auto id_vec = ctx->input(1).flat<int64_t>();\n    auto id_split_vec = ctx->input(2).flat<int64>();\n    // OP_REQUIRES(ctx, id_split_vec.size() == mtable->size() + 1,\n    //             errors::InvalidArgument(\"id_split must be \", mtable->size() + 1,\n    //                                     \". Current: \", id_split_vec.size()));\n    int req_size = (id_split_vec.size() - 1) / mtable->size();\n    OP_REQUIRES(ctx, id_split_vec.size() == mtable->size() * req_size + 1,\n                errors::InvalidArgument(\"table size: \", mtable->size(),\n                                        \". Error id_split size: \", id_split_vec.size()));\n    std::vector<int> each_emb_offset(req_size);\n    int emb_size = 0;\n    for (int req_i = 0; req_i < req_size; ++req_i) {\n      each_emb_offset[req_i] = emb_size;\n      for (int i = 0; i < mtable->size(); ++i) {\n        int id_split_idx = req_i * mtable->size() + i;\n        const int num_ids = id_split_vec(id_split_idx + 1) - id_split_vec(id_split_idx);\n        emb_size += num_ids * mtable->table(i)->dim_size();\n      }\n    }\n    Tensor* emb_tensor;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {emb_size}, &emb_tensor));\n    int64_t* id_ptr = const_cast<int64_t*>(id_vec.data());\n    // int emb_offset = 0;\n    float* emb_data = reinterpret_cast<float*>(emb_tensor->data());\n    int64_t total_hit_fid_count = 0, total_num_ids = 0;\n    for (int i = 0; i < mtable->size(); ++i) {\n      EmbeddingHashTableTfBridge* table = mtable->table(i);\n      for (int req_i = 0; req_i < req_size; ++req_i) {\n        int id_split_idx = req_i * mtable->size() + i;\n        const int num_ids = id_split_vec(id_split_idx + 1) - id_split_vec(id_split_idx);\n        total_num_ids += num_ids;\n        int64_t hit_fid_count = 0;\n        OP_REQUIRES_OK(ctx,\n                       table->BatchLookup(ctx, num_ids, id_ptr + id_split_vec(i),\n                                          emb_data + each_emb_offset[req_i],\n                                          &hit_fid_count));\n        total_hit_fid_count += hit_fid_count;\n        each_emb_offset[req_i] += num_ids * table->dim_size();\n      }\n    }\n\n    if (mtable->size() && mtable->table(0)->IsServingEntryType() &&\n        total_num_ids) {\n      const std::string tagkv = \"name=all\";\n      float hit_rate = total_hit_fid_count / static_cast<float>(total_num_ids);\n      monolith::GetMetrics()->emit_timer(\"lookup_fid_hit_rate\", hit_rate,\n                                         tagkv);\n    }\n  }\n};\n\nclass MultiHashTableLookupEntryOp : public OpKernel {\n public:\n  explicit MultiHashTableLookupEntryOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    core::RefCountPtr<MultiHashTable> mtable;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &mtable));\n    auto id_vec = ctx->input(1).flat<int64_t>();\n    auto id_split_vec = ctx->input(2).flat<int64>();\n    OP_REQUIRES(ctx, id_split_vec.size() == mtable->size() + 1,\n                errors::InvalidArgument(\"id_split must be \", mtable->size() + 1,\n                                        \". Current: \", id_split_vec.size()));\n    Tensor* entry_tensor;\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(0, {id_vec.size()}, &entry_tensor));\n    auto entry = entry_tensor->vec<tstring>();\n    int64_t* id_ptr = const_cast<int64_t*>(id_vec.data());\n    for (int i = 0; i < mtable->size(); ++i) {\n      EmbeddingHashTableTfBridge* table = mtable->table(i);\n      const int num_ids = id_split_vec(i + 1) - id_split_vec(i);\n      std::vector<EmbeddingHashTableTfBridge::EntryDump> entries(num_ids);\n      OP_REQUIRES_OK(\n          ctx, table->BatchLookupEntry(ctx, num_ids, id_ptr + id_split_vec(i),\n                                       entries.data()));\n      for (int j = 0; j < num_ids; ++j) {\n        entry(j + id_split_vec(i)) = entries[j].SerializeAsString();\n      }\n    }\n  }\n\n private:\n  int num_tables_;\n  bool enable_inter_table_parallelism_;\n  int64 cost_per_table_;\n};\n\ntemplate <typename Device>\nclass MultiHashTableFusedLookupOp : public OpKernel {\n public:\n  explicit MultiHashTableFusedLookupOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"num_of_shards\", &num_shards_));\n  }\n\n  void ComputeH(OpKernelContext* ctx);\n  void Compute(OpKernelContext* ctx) override { ComputeH(ctx); }\n\n private:\n  int num_shards_;\n};\n\ntemplate <>\nvoid MultiHashTableFusedLookupOp<CPUDevice>::ComputeH(OpKernelContext* ctx) {\n  auto ids_flat = ctx->input(1).flat<int64_t>().data();\n  auto slot_size_vec = ctx->input(2).vec<int>().data();\n  auto slot_size_cnt = ctx->input(2).NumElements();\n\n  Tensor *embeddings_ts, *emb_splits_ts, *key_offsets_ts, *emb_offsets_ts;\n  OP_REQUIRES_OK(ctx, ctx->allocate_output(1, {num_shards_}, &emb_splits_ts));\n  OP_REQUIRES_OK(ctx,\n                 ctx->allocate_output(2, {slot_size_cnt + 1}, &key_offsets_ts));\n  OP_REQUIRES_OK(ctx,\n                 ctx->allocate_output(3, {slot_size_cnt + 1}, &emb_offsets_ts));\n  ctx->set_output(4, ctx->input(1));\n  auto key_offsets = key_offsets_ts->vec<int>().data();\n  auto emb_offsets = emb_offsets_ts->vec<int>().data();\n  auto emb_splits = emb_splits_ts->vec<int>().data();\n\n  core::RefCountPtr<MultiHashTable> mtable;\n  OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &mtable));\n\n  int num_tables_ = mtable->size();\n  std::vector<int> hash_table_dims(num_tables_, 0);\n  for (int table_id = 0; table_id < num_tables_; table_id++) {\n    hash_table_dims[table_id] = mtable->table(table_id)->dim_size();\n  }\n\n  int total_keys, total_embs;\n  std::tie(total_keys, total_embs) =\n      monolith::hash_table::ComputeFusedOffsets<false>(\n          slot_size_vec, hash_table_dims.data(), num_tables_, num_shards_,\n          key_offsets, emb_offsets, nullptr, emb_splits);\n\n  OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {total_embs}, &embeddings_ts));\n  auto embeddings = embeddings_ts->vec<float>().data();\n\n  auto lookup = [&](const int begin, const int end) {\n    for (int shard_id = begin; shard_id < end; shard_id++) {\n      for (int table_id = 0; table_id < num_tables_; table_id++) {\n        int curr_idx = shard_id * num_tables_ + table_id;\n        int64_t hit_fid_count = 0;\n        mtable->table(table_id)->BatchLookup(\n            ctx, slot_size_vec[curr_idx],\n            const_cast<int64_t*>(ids_flat) + key_offsets[curr_idx],\n            embeddings + emb_offsets[curr_idx], &hit_fid_count);\n      }\n    }\n  };\n\n  // TODO(zouxuan): tweak this number for optimization.\n  const int64 kCostPerUnit = 1000000;\n  const DeviceBase::CpuWorkerThreads& worker_threads =\n      *ctx->device()->tensorflow_cpu_worker_threads();\n  Shard(worker_threads.num_threads, worker_threads.workers, num_shards_,\n        kCostPerUnit, lookup);\n}\n\n\nREGISTER_OP(\"MonolithMultiHashTableLookup\")\n    .Input(\"mtable: resource\")\n    .Input(\"id: int64\")\n    .Input(\"id_split: int64\")\n    .Output(\"embedding: float\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      c->set_output(0, c->Vector(c->UnknownDim()));\n\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithMultiHashTableLookup\").Device(DEVICE_CPU),\n                        MultiHashTableLookupOp);\n\nREGISTER_OP(\"MonolithMultiHashTableLookupEntry\")\n    .Input(\"mtable: resource\")\n    .Input(\"id: int64\")\n    .Input(\"id_split: int64\")\n    .Output(\"serialized_entries: string\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      c->set_output(0, c->input(0));\n\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithMultiHashTableLookupEntry\").Device(DEVICE_CPU),\n    MultiHashTableLookupEntryOp);\n\nREGISTER_OP(\"MonolithMultiHashTableFusedLookup\")\n    .Input(\"mtable: resource\")\n    .Input(\"ids: int64\")\n    .Input(\"fused_slot_size: int32\")\n    .Input(\"req_time: int64\")\n    .Output(\"embeddings: float32\")\n    .Output(\"embedding_splits: int32\")\n    .Output(\"id_offsets: int32\")\n    .Output(\"embedding_offsets: int32\")\n    .Output(\"indices: int64\")\n    .Attr(\"num_of_shards: int\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int num_shards;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"num_of_shards\", &num_shards));\n      c->set_output(0, c->Vector(c->UnknownDim()));\n      c->set_output(1, c->Vector(num_shards));\n\n      auto shape = c->input(2);\n      TF_RETURN_IF_ERROR(c->WithRank(shape, 1, &shape));\n      auto dim = c->Dim(shape, 0);\n      shape_inference::DimensionHandle out;\n      TF_RETURN_IF_ERROR(c->Add(dim, 1, &out));\n      c->set_output(2, c->Vector(out));\n      c->set_output(3, c->Vector(out));\n      c->set_output(4, c->input(1));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithMultiHashTableFusedLookup\").Device(DEVICE_CPU),\n    MultiHashTableFusedLookupOp<CPUDevice>);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/multi_hash_table_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstring>\n#include <string>\n#include \"absl/strings/str_format.h\"\n#include \"absl/synchronization/mutex.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"monolith/native_training/runtime/ops/multi_hash_table.h\"\n#include \"monolith/native_training/runtime/ops/parameter_sync_tf_bridge.h\"\n#include \"monolith/native_training/runtime/parameter_sync/parameter_sync_client.h\"\n#include \"tensorflow/core/framework/lookup_interface.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/types.h\"\n#include \"tensorflow/core/platform/default/integral_types.h\"\n#include \"tensorflow/core/platform/mutex.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing ::monolith::hash_table::MultiEmbeddingHashTableConfig;\nusing ::monolith::parameter_sync::ParameterSyncClient;\n\ntemplate <typename T>\nclass CreateMultiHashTableOp : public ResourceOpKernel<T> {\n public:\n  explicit CreateMultiHashTableOp(OpKernelConstruction* ctx)\n      : ResourceOpKernel<T>(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    absl::MutexLock l(&mu_);\n    if (ResourceOpKernel<T>::resource_ == nullptr) {\n      const tstring& serialized_config = ctx->input(0).scalar<tstring>()();\n      OP_REQUIRES(ctx, config_.ParseFromArray(serialized_config.data(),\n                                              serialized_config.size()),\n                  errors::InvalidArgument(\"Unable to parse config.\"));\n      n_ = config_.names_size();\n      OP_REQUIRES(\n          ctx, config_.configs_size() == n_,\n          errors::InvalidArgument(\n              \"`table_configs` size must equal to `N`, got filter_handles (\",\n              config_.names_size(), \") vs N (\", n_, \")\"));\n\n      const auto& filter_handle = ctx->input(1).scalar<ResourceHandle>()();\n      OP_REQUIRES_OK(ctx, LookupResource(ctx, filter_handle, &hash_filter_));\n\n      const auto& sync_client_handle = ctx->input(2).scalar<ResourceHandle>()();\n      OP_REQUIRES_OK(ctx,\n                     LookupResource(ctx, sync_client_handle, &sync_client_));\n      SetupStream(ctx);\n    }\n    ResourceOpKernel<T>::Compute(ctx);\n  }\n\n  void SetupStream(OpKernelContext* ctx);\n\n  Status CreateResource(T** resource) TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override;\n\n private:\n  absl::Mutex mu_;\n  int n_ ABSL_GUARDED_BY(mu_);\n  MultiEmbeddingHashTableConfig config_ ABSL_GUARDED_BY(mu_);\n  core::RefCountPtr<HashFilterTfBridge> hash_filter_ ABSL_GUARDED_BY(mu_);\n  core::RefCountPtr<ParameterSyncClientTfBridge> sync_client_\n      ABSL_GUARDED_BY(mu_);\n};\n\ntemplate <>\nvoid CreateMultiHashTableOp<MultiHashTable>::SetupStream(OpKernelContext* ctx) {\n}\n\ntemplate <>\nStatus CreateMultiHashTableOp<MultiHashTable>::CreateResource(\n    MultiHashTable** resource) {\n  auto* mtable = new MultiHashTable(cinfo_.name());\n  for (int i = 0; i < n_; i++) {\n    EmbeddingHashTableTfBridge* hash_table;\n    TF_RETURN_IF_ERROR(EmbeddingHashTableTfBridge::New(\n        config_.configs(i), hash_filter_.get(), &hash_table, config_.names(i)));\n    hash_table->SetHopscotchHashSet(sync_client_->GetTouchedKeySet());\n    mtable->add_table(\n        config_.names(i),\n        core::RefCountPtr<EmbeddingHashTableTfBridge>(hash_table));\n  }\n  sync_client_->SetMultiHashTableResource(mtable);\n  *resource = mtable;\n  return Status::OK();\n}\n\nREGISTER_OP(\"CreateMonolithMultiHashTable\")\n    .Input(\"config: string\")\n    .Input(\"filter_handle: resource\")\n    .Input(\"sync_client_handle: resource\")\n    .Output(\"multi_hash_table_handle: resource\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"CreateMonolithMultiHashTable\").Device(DEVICE_CPU),\n                        CreateMultiHashTableOp<MultiHashTable>);\n\ntemplate <typename T>\nclass ReadMultiHashTableOp : public OpKernel {\n public:\n  explicit ReadMultiHashTableOp(OpKernelConstruction* c) : OpKernel(c) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    absl::MutexLock l(&mu_);\n    if (cinfo_.name().empty()) {\n      OP_REQUIRES_OK(ctx, cinfo_.Init(ctx->resource_manager(), def()));\n    }\n    OP_REQUIRES_OK(\n        ctx, MakeResourceHandleToOutput(ctx, 0, cinfo_.container(),\n                                        cinfo_.name(), TypeIndex::Make<T>()));\n  }\n\n private:\n  absl::Mutex mu_;\n  ContainerInfo cinfo_ TF_GUARDED_BY(mu_);\n};\n\nREGISTER_OP(\"ReadMonolithMultiHashTable\")\n    .Output(\"multi_hash_table_handle: resource\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"ReadMonolithMultiHashTable\").Device(DEVICE_CPU),\n                        ReadMultiHashTableOp<MultiHashTable>);\n\ntemplate <typename T>\nclass IsHashTableInitializedOp : public OpKernel {\n public:\n  explicit IsHashTableInitializedOp(OpKernelConstruction* c) : OpKernel(c) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    core::RefCountPtr<T> mtable;\n    Status s = LookupResource(ctx, HandleFromInput(ctx, 0), &mtable);\n\n    Tensor* output_tensor;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {}, &output_tensor));\n    output_tensor->scalar<bool>()() = s.ok();\n  }\n};\n\nREGISTER_OP(\"IsHashTableInitialized\")\n    .Input(\"handle: resource\")\n    .Output(\"is_initialized: bool\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"IsHashTableInitialized\").Device(DEVICE_CPU),\n                        IsHashTableInitializedOp<MultiHashTable>);\n\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/multi_hash_table_save_restore_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <atomic>\n#include <cstring>\n#include <memory>\n#include <string>\n\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/container/flat_hash_set.h\"\n#include \"absl/random/random.h\"\n#include \"absl/strings/str_cat.h\"\n#include \"absl/time/clock.h\"\n#include \"absl/time/time.h\"\n#include \"monolith/native_training/data/training_instance/cc/reader_util.h\"\n#include \"monolith/native_training/runtime/hash_table/embedding_hash_table.pb.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"monolith/native_training/runtime/ops/file_utils.h\"\n#include \"monolith/native_training/runtime/ops/multi_hash_table.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/kernels/ops_util.h\"\n#include \"tensorflow/core/lib/io/record_reader.h\"\n#include \"tensorflow/core/lib/io/record_writer.h\"\n#include \"tensorflow/core/platform/path.h\"\n#include \"tensorflow/core/platform/random.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n#include \"third_party/nlohmann/json.hpp\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nusing tensorflow::strings::HumanReadableNumBytes;\n\n// Carries the data through async process.\ntemplate <typename TableType>\nstruct AsyncPack {\n  AsyncPack(OpKernelContext* p_ctx, core::RefCountPtr<TableType> p_mtable,\n            std::string p_basename,\n            std::vector<std::unique_ptr<EmbeddingHashTableTfBridge::LockCtx>>\n                p_lock_ctxs,\n            std::function<void()> p_done, int p_thread_num)\n      : ctx(p_ctx),\n        basename(std::move(p_basename)),\n        record_count(0),\n        mtable(std::move(p_mtable)),\n        lock_ctxs(std::move(p_lock_ctxs)),\n        done(std::move(p_done)),\n        thread_num(p_thread_num),\n        finish_num(0),\n        status(p_thread_num) {}\n\n  ~AsyncPack() {\n    for (const auto& s : status) {\n      OP_REQUIRES_OK_ASYNC(ctx, s, done);\n    }\n    done();\n  }\n\n  OpKernelContext* ctx;\n  std::string basename;\n  mutable std::atomic_long record_count;\n  core::RefCountPtr<TableType> mtable;\n  std::vector<std::unique_ptr<EmbeddingHashTableTfBridge::LockCtx>> lock_ctxs;\n  std::function<void()> done;\n  const int thread_num;\n  mutable std::atomic_int finish_num;\n  mutable std::vector<Status> status;\n};\n\ntemplate <typename TableType>\nstruct EntryDumpIter {\n  explicit EntryDumpIter(io::SequentialRecordReader* reader_, int64_t limit_)\n      : reader(reader_), limit(limit_), offset(0) {}\n  bool GetNext(const AsyncPack<TableType>* p,\n               EmbeddingHashTableTfBridge::EntryDump* dump, Status* status) {\n    *status = Status::OK();\n    if (offset >= limit) return false;\n    tstring s;\n    *status = reader->ReadRecord(&s);\n    if (!status->ok() || !dump->ParseFromArray(s.data(), s.size())) {\n      *status = errors::DataLoss(\"Parse entry failed!\");\n      return false;\n    }\n    offset++;\n    p->record_count.fetch_add(1);\n\n    return true;\n  }\n\n  io::SequentialRecordReader* reader;\n  int64_t limit;\n  int64_t offset;\n};\n\nconst char* const kShardedMetadataFileFormat = \"%s.meta-%05d-of-%05d\";\n\nstd::string GetShardedMetadataFileName(absl::string_view basename, int shard,\n                                       int nshards) {\n  return absl::StrFormat(kShardedMetadataFileFormat, basename, shard, nshards);\n}\n\n}  // namespace\n\ntemplate <typename TableType>\nclass MultiHashTableSaveOp : public AsyncOpKernel {\n public:\n  explicit MultiHashTableSaveOp(OpKernelConstruction* ctx)\n      : AsyncOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"nshards\", &nshards_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"slot_expire_time_config\",\n                                     &slot_expire_time_config_serialized_));\n    if (!slot_expire_time_config_serialized_.empty()) {\n      OP_REQUIRES(\n          ctx, slot_expire_time_config_.ParseFromString(\n                   slot_expire_time_config_serialized_),\n          errors::InvalidArgument(\"Unable to parse config. Make sure it \"\n                                  \"is serialized version of \"\n                                  \"SlotExpireTimeConfig.\"));\n    }\n\n    slot_to_expire_time_.resize(get_max_slot_number(),\n                                slot_expire_time_config_.default_expire_time());\n    for (const auto& slot_expire_time :\n         slot_expire_time_config_.slot_expire_times()) {\n      slot_to_expire_time_[slot_expire_time.slot()] =\n          slot_expire_time.expire_time();\n    }\n  }\n\n  void ComputeAsync(OpKernelContext* ctx, DoneCallback done) override {\n    core::RefCountPtr<TableType> mtable;\n    OP_REQUIRES_OK_ASYNC(\n        ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &mtable), done);\n    const Tensor& basename_tensor = ctx->input(1);\n    const std::string basename = basename_tensor.scalar<tstring>()();\n    const std::string dirname = std::string(io::Dirname(basename));\n    OP_REQUIRES_OK_ASYNC(ctx, ctx->env()->RecursivelyCreateDir(dirname), done);\n\n    int real_nshards = PickNshards(*mtable);\n    std::vector<std::unique_ptr<EmbeddingHashTableTfBridge::LockCtx>> lock_ctxs;\n    for (int i = 0; i < mtable->size(); ++i) {\n      std::unique_ptr<EmbeddingHashTableTfBridge::LockCtx> lock_ctx;\n      OP_REQUIRES_OK_ASYNC(ctx, mtable->table(i)->LockAll(&lock_ctx), done);\n      lock_ctxs.push_back(std::move(lock_ctx));\n    }\n    auto pack = std::make_shared<const AsyncPack<TableType>>(\n        ctx, std::move(mtable), basename, std::move(lock_ctxs), std::move(done),\n        real_nshards);\n    for (int i = 0; i < real_nshards; ++i) {\n      ctx->device()->tensorflow_cpu_worker_threads()->workers->Schedule(\n          [this, pack, i, real_nshards] {\n            WorkerThread({i, real_nshards}, pack);\n          });\n    }\n    ctx->set_output(0, ctx->input(0));\n  }\n\n private:\n  void WorkerThread(EmbeddingHashTableTfBridge::DumpShard shard,\n                    std::shared_ptr<const AsyncPack<TableType>> p) {\n    p->status[shard.idx] = SaveOneShard(shard, p.get());\n  }\n\n  Status SaveOneShard(EmbeddingHashTableTfBridge::DumpShard shard,\n                      const AsyncPack<TableType>* p) {\n    const std::string filename =\n        GetShardedFileName(p->basename, shard.idx, shard.total);\n    const std::string meta_filename =\n        GetShardedMetadataFileName(p->basename, shard.idx, shard.total);\n    const std::string tmp_filename =\n        absl::StrCat(filename, \"-tmp-\", random::New64());\n    const std::string tmp_meta_filename =\n        absl::StrCat(meta_filename, \"-tmp-\", random::New64());\n    std::unique_ptr<WritableFile> fp;\n    TF_RETURN_IF_ERROR(p->ctx->env()->NewWritableFile(tmp_filename, &fp));\n    std::unique_ptr<WritableFile> fp_meta;\n    TF_RETURN_IF_ERROR(\n        p->ctx->env()->NewWritableFile(tmp_meta_filename, &fp_meta));\n\n    io::RecordWriterOptions options;\n    options.compression_type = io::RecordWriterOptions::SNAPPY_COMPRESSION;\n    io::RecordWriterOptions options_meta;\n    io::RecordWriter writer(fp.get(), options);\n    io::RecordWriter meta_writer(fp_meta.get(), options_meta);\n    Status write_status;\n\n    for (int table_idx = 0; table_idx < p->mtable->size(); table_idx++) {\n      int64_t num_entries = 0;\n      const EmbeddingHashTableTfBridge* table = p->mtable->table(table_idx);\n      const std::string& table_name = p->mtable->name(table_idx);\n      int64_t max_update_ts_sec = table->max_update_ts_sec();\n      auto write_fn = [&](EmbeddingHashTableTfBridge::EntryDump dump) {\n        int64_t slot_id = slot_id_v2(dump.id());\n        // Elements of slot_to_expire_time_ are in days.\n        // last_update_ts_sec is seconds since the Epoch.\n        if (max_update_ts_sec - dump.last_update_ts_sec() >=\n            slot_to_expire_time_[slot_id] * 24 * 3600) {\n          return true;\n        }\n        Status s = writer.WriteRecord(dump.SerializeAsString());\n        if (TF_PREDICT_FALSE(!s.ok())) {\n          // OK to throw here since it will be catched.\n          write_status = s;\n          return false;\n        }\n        num_entries++;\n        return true;\n      };\n      EmbeddingHashTableTfBridge::DumpIterator iter;\n      TF_RETURN_IF_ERROR(table->Save(p->ctx, shard, write_fn, &iter));\n\n      monolith::hash_table::MultiHashTableMetadata meta;\n      meta.set_table_name(table_name);\n      meta.set_num_entries(num_entries);\n      TF_RETURN_IF_ERROR(meta_writer.WriteRecord(meta.SerializeAsString()));\n    }\n\n    TF_RETURN_IF_ERROR(writer.Close());\n    TF_RETURN_IF_ERROR(meta_writer.Close());\n    TF_RETURN_IF_ERROR(fp->Close());\n    TF_RETURN_IF_ERROR(fp_meta->Close());\n    TF_RETURN_IF_ERROR(p->ctx->env()->RenameFile(tmp_filename, filename));\n    TF_RETURN_IF_ERROR(\n        p->ctx->env()->RenameFile(tmp_meta_filename, meta_filename));\n    return Status::OK();\n  }\n\n  int PickNshards(const TableType& mtable) {\n    if (nshards_ >= 0) return nshards_;\n    int64 total_size = 0;\n    const int64 kBaseline = 1000000ll;\n    for (size_t i = 0; i < mtable.size(); i++) {\n      total_size += mtable.table(i)->Size();\n    }\n    return std::min(4LL, std::max(1LL, total_size / kBaseline));\n  }\n\n  int nshards_;\n  std::string slot_expire_time_config_serialized_;\n  monolith::hash_table::SlotExpireTimeConfig slot_expire_time_config_;\n  std::vector<int64_t> slot_to_expire_time_;\n};\n\ntemplate <typename TableType>\nclass MultiHashTableRestoreOp : public AsyncOpKernel {\n public:\n  explicit MultiHashTableRestoreOp(OpKernelConstruction* ctx)\n      : AsyncOpKernel(ctx) {}\n\n  void ComputeAsync(OpKernelContext* ctx, DoneCallback done) override {\n    core::RefCountPtr<TableType> mtable;\n    OP_REQUIRES_OK_ASYNC(\n        ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &mtable), done);\n\n    const Tensor& basename_tensor = ctx->input(1);\n    const std::string basename = basename_tensor.scalar<tstring>()();\n    std::vector<std::string> files;\n    OP_REQUIRES_OK_ASYNC(\n        ctx, ctx->env()->GetMatchingPaths(absl::StrCat(basename, \"-*\"), &files),\n        done);\n\n    FileSpec file_spec;\n    OP_REQUIRES_OK_ASYNC(ctx, ValidateShardedFiles(basename, files, &file_spec),\n                         done);\n    OP_REQUIRES_ASYNC(ctx, file_spec.nshards() > 0,\n                      errors::NotFound(\"Unable to find the dump files for: \",\n                                       name(), \" in \", basename),\n                      done);\n\n    int nshards = file_spec.nshards();\n    auto pack = std::make_shared<const AsyncPack<TableType>>(\n        ctx, std::move(mtable), basename,\n        std::vector<std::unique_ptr<EmbeddingHashTableTfBridge::LockCtx>>(),\n        std::move(done), nshards);\n    for (int i = 0; i < nshards; ++i) {\n      ctx->device()->tensorflow_cpu_worker_threads()->workers->Schedule(\n          [this, pack, i, nshards] {\n            WorkerThread({i, nshards}, pack);\n          });\n    }\n    ctx->set_output(0, ctx->input(0));\n  }\n\n private:\n  void WorkerThread(EmbeddingHashTableTfBridge::DumpShard shard,\n                    std::shared_ptr<const AsyncPack<TableType>> p) {\n    p->status[shard.idx] = RestoreOneShard(shard, p.get());\n    if (p->finish_num.fetch_add(1) == p->thread_num - 1) {\n      int64_t total_byte_size = 0, total_uncompressed_byte_size = 0,\n              total_size = 0;\n      for (int i = 0; i < p->mtable->size(); ++i) {\n        auto t = p->mtable->table(i);\n        auto name = p->mtable->name(i);\n        auto summary = t->Summary();\n        LOG(INFO) << absl::StrFormat(\"Hash table: %s, summary: %s\", name,\n                                     summary);\n        LogSummary(summary, &total_byte_size, &total_uncompressed_byte_size);\n        total_size += t->Size();\n      }\n\n      LOG(INFO) << absl::StrFormat(\n          \"Restore read %ld records, skip %ld zero embeddings\", p->record_count,\n          p->record_count - total_size);\n      LOG(INFO) << absl::StrFormat(\n          \"total memory: %s, total memory if not compressed: %s\",\n          HumanReadableNumBytes(total_byte_size),\n          HumanReadableNumBytes(total_uncompressed_byte_size));\n    }\n  }\n\n  Status RestoreOneShard(EmbeddingHashTableTfBridge::DumpShard shard,\n                         const AsyncPack<TableType>* p) {\n    std::string filename =\n        GetShardedFileName(p->basename, shard.idx, shard.total);\n    std::string meta_filename =\n        GetShardedMetadataFileName(p->basename, shard.idx, shard.total);\n    std::unique_ptr<RandomAccessFile> fp;\n    std::unique_ptr<RandomAccessFile> fp_meta;\n    TF_RETURN_IF_ERROR(p->ctx->env()->NewRandomAccessFile(filename, &fp));\n    TF_RETURN_IF_ERROR(\n        p->ctx->env()->NewRandomAccessFile(meta_filename, &fp_meta));\n\n    io::RecordReaderOptions options;\n    options.compression_type = io::RecordReaderOptions::SNAPPY_COMPRESSION;\n    options.buffer_size = 10 * 1024 * 1024;\n    io::SequentialRecordReader reader(fp.get(), options);\n    io::RecordReaderOptions options_meta;\n    io::SequentialRecordReader meta_reader(fp_meta.get(), options_meta);\n\n    absl::flat_hash_set<std::string> tables_in_shard;\n    absl::flat_hash_map<std::string, int> name_to_idx;\n    for (int i = 0; i < p->mtable->size(); ++i) {\n      name_to_idx.insert({p->mtable->name(i), i});\n    }\n    bool eof = false;\n    Status restore_status;\n    while (!eof) {\n      tstring meta_pb;\n      Status meta_status = meta_reader.ReadRecord(&meta_pb);\n      if (!meta_status.ok()) {\n        if (errors::IsOutOfRange(meta_status)) {\n          eof = true;\n          break;\n        } else {\n          return errors::DataLoss(\"Read table metadata failed!\");\n        }\n      }\n\n      monolith::hash_table::MultiHashTableMetadata meta;\n      if (!meta.ParseFromArray(meta_pb.data(), meta_pb.size())) {\n        return errors::DataLoss(\"Parse table metadata failed!\");\n      }\n      auto name_iter = name_to_idx.find(meta.table_name());\n      if (name_iter == name_to_idx.end()) {\n        if (shard.idx == 0) {\n          LOG(INFO) << \"Table \" << meta.table_name()\n                    << \" in checkpoint. skipped.\";\n        }\n        tstring dummy_str;\n        for (int64_t i = 0; i < meta.num_entries(); i++) {\n          TF_RETURN_IF_ERROR(reader.ReadRecord(&dummy_str));\n        }\n        continue;\n      }\n      tables_in_shard.insert(meta.table_name());\n      EmbeddingHashTableTfBridge* table = p->mtable->table(name_iter->second);\n\n      EntryDumpIter<TableType> entry_iter(&reader, meta.num_entries());\n      auto get_fn = [&](EmbeddingHashTableTfBridge::EntryDump* dump,\n                        int64_t* max_update_ts) {\n        if (!entry_iter.GetNext(p, dump, &restore_status)) return false;\n        if (!dump->has_last_update_ts_sec()) {\n          dump->set_last_update_ts_sec(0);\n        }\n        *max_update_ts = std::max(dump->last_update_ts_sec(), *max_update_ts);\n        return true;\n      };\n      TF_RETURN_IF_ERROR(table->Restore(p->ctx, shard, get_fn));\n      TF_RETURN_IF_ERROR(restore_status);\n    }\n    if (shard.idx == 0) {\n      for (const std::string& table_name : p->mtable->names()) {\n        if (!tables_in_shard.contains(table_name)) {\n          LOG(WARNING) << \"Table \" << table_name << \" not found checkpoint.\";\n        }\n      }\n    }\n    if (!eof)\n      return errors::DataLoss(\"Couldn't read all of checkpoint shard \",\n                              shard.idx);\n    return Status::OK();\n  }\n\n private:\n  template <bool enabled = std::is_same<TableType, MultiHashTable>::value>\n  inline typename std::enable_if<enabled, void>::type LogSummary(\n      const std::string& summary, int64_t* total_byte_size,\n      int64_t* total_uncompressed_byte_size) {\n    nlohmann::json json = nlohmann::json::parse(summary);\n    CHECK(json.contains(\"memory\"));\n    CHECK(json.contains(\"memory_if_not_compressed\"));\n    *total_byte_size += int64_t(json[\"memory\"]);\n    *total_uncompressed_byte_size += int64_t(json[\"memory_if_not_compressed\"]);\n  }\n\n  template <bool enabled = std::is_same<TableType, MultiHashTable>::value>\n  inline typename std::enable_if<!enabled, void>::type LogSummary(\n      const std::string& summary, int64_t* total_byte_size,\n      int64_t* total_uncompressed_byte_size) {}\n};\n\nclass MultiHashTableFeatureStatOp : public OpKernel {\n public:\n  explicit MultiHashTableFeatureStatOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& basename_tensor = ctx->input(0);\n    const std::string basename = basename_tensor.scalar<tstring>()();\n    std::vector<std::string> files;\n    OP_REQUIRES_OK(ctx, ctx->env()->GetMatchingPaths(\n                            absl::StrCat(basename, \"-*\"), &files));\n\n    OP_REQUIRES_OK(ctx, ValidateShardedFiles(basename, files));\n    OP_REQUIRES(ctx, !files.empty(),\n                errors::NotFound(\"Unable to find the dump files for: \", name(),\n                                 \" in \", basename));\n\n    absl::flat_hash_map<std::string, uint64_t> feature_count;\n    int nshards = files.size();\n    for (int idx = 0; idx < nshards; ++idx) {\n      std::string filename = GetShardedMetadataFileName(basename, idx, nshards);\n      std::unique_ptr<RandomAccessFile> fp;\n      OP_REQUIRES_OK(ctx, ctx->env()->NewRandomAccessFile(filename, &fp));\n\n      io::RecordReaderOptions options;\n      io::SequentialRecordReader reader(fp.get(), options);\n\n      bool eof = false;\n      while (!eof) {\n        tstring meta_pb;\n        Status s = reader.ReadRecord(&meta_pb);\n        if (!s.ok()) {\n          if (errors::IsOutOfRange(s)) {\n            eof = true;\n            break;\n          } else {\n            OP_REQUIRES(ctx, s.ok(),\n                        errors::DataLoss(\"Read table metadata failed!\"));\n          }\n        }\n\n        monolith::hash_table::MultiHashTableMetadata meta;\n        OP_REQUIRES(ctx, meta.ParseFromArray(meta_pb.data(), meta_pb.size()),\n                    errors::DataLoss(\"Parse table metadata failed!\"));\n        if (!feature_count.contains(meta.table_name())) {\n          feature_count[meta.table_name()] = 0;\n        }\n        feature_count[meta.table_name()] += meta.num_entries();\n      }\n\n      OP_REQUIRES(ctx, eof, errors::DataLoss(\n                                \"Couldn't read all of checkpoint shard \", idx));\n    }\n\n    int num_tables = feature_count.size();\n    Tensor* features;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, TensorShape({\n                                                    num_tables,\n                                                }),\n                                             &features));\n    auto features_vec = features->vec<tstring>();\n    Tensor* counts;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(1, TensorShape({\n                                                    num_tables,\n                                                }),\n                                             &counts));\n    auto counts_vec = counts->vec<uint64_t>();\n    int feature_iter = 0;\n    for (const auto& it : feature_count) {\n      features_vec(feature_iter) = it.first;\n      counts_vec(feature_iter) = it.second;\n      feature_iter++;\n    }\n  }\n};\n\nREGISTER_OP(\"MonolithMultiHashTableSave\")\n    .Input(\"mtable: resource\")\n    .Input(\"basename: string\")\n    .Output(\"output_mtable: resource\")\n    .Attr(\"nshards: int=-1\")\n    .Attr(\"slot_expire_time_config: string = ''\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithMultiHashTableSave\").Device(DEVICE_CPU),\n                        MultiHashTableSaveOp<MultiHashTable>);\n\nREGISTER_OP(\"MonolithMultiHashTableRestore\")\n    .Input(\"mtable: resource\")\n    .Input(\"basename: string\")\n    .Output(\"output_mtable: resource\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithMultiHashTableRestore\").Device(DEVICE_CPU),\n    MultiHashTableRestoreOp<MultiHashTable>);\n\n\nREGISTER_OP(\"MonolithMultiHashTableFeatureStat\")\n    .Input(\"basename: string\")\n    .Output(\"features: string\")\n    .Output(\"counts: uint64\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      ctx->set_output(0, ctx->Vector(ctx->UnknownDim()));\n      ctx->set_output(1, ctx->Vector(ctx->UnknownDim()));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithMultiHashTableFeatureStat\").Device(DEVICE_CPU),\n    MultiHashTableFeatureStatOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/multi_hash_table_update_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"absl/types/span.h\"\n#include \"monolith/native_training/runtime/concurrency/queue.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"monolith/native_training/runtime/ops/hash_filter_tf_bridge.h\"\n#include \"monolith/native_training/runtime/ops/multi_hash_table.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/tensor_types.h\"\n#include \"tensorflow/core/framework/types.h\"\n#include \"tensorflow/core/util/work_sharder.h\"\nnamespace tensorflow {\nnamespace monolith_tf {\n\nusing CPUDevice = Eigen::ThreadPoolDevice;\n\nnamespace {\n\nusing monolith::concurrency::Queue;\n\nStatus MismatchLength(absl::string_view tensor_name, int tensor_size,\n                      int expected_size) {\n  return errors::InvalidArgument(\"The length of tensor `\", tensor_name,\n                                 \"` doesn't equal to table num. \", tensor_size,\n                                 \"v.s.\", expected_size);\n}\n\nStatus LengthTooShort(absl::string_view tensor_name, int tensor_size) {\n  return errors::InvalidArgument(\"The length of tensor `\", tensor_name,\n                                 \"` is too short. Currently value\",\n                                 tensor_size);\n}\n\nclass MultiHashTableOptimizeOp : public OpKernel {\n public:\n  explicit MultiHashTableOptimizeOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* c) override {\n    core::RefCountPtr<MultiHashTable> mtable;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 0), &mtable));\n    auto id_vec = c->input(1).flat<int64>();\n    auto id_split = c->input(2).flat<int64>();\n    OP_REQUIRES(c, id_split.size() - 1 == mtable->size(),\n                MismatchLength(\"id\", id_split.size() - 1, mtable->size()));\n    auto value_vec = c->input(3).flat<float>();\n    auto learning_rate_vec = c->input(4).flat<float>();\n    int64 update_time = c->input(5).scalar<int64>()();\n    int64 global_step = c->input(6).scalar<int64>()();\n    int n = mtable->size();\n    int value_offset = 0;\n    int learning_rate_offset = 0;\n    for (int i = 0; i < n; ++i) {\n      EmbeddingHashTableTfBridge* table = mtable->table(i);\n      const int num_ids = id_split(i + 1) - id_split(i);\n      const int value_size =\n          (id_split(i + 1) - id_split(i)) * table->dim_size();\n      OP_REQUIRES(c, value_offset + value_size <= value_vec.size(),\n                  LengthTooShort(\"value\", value_vec.size()));\n      auto learning_rate = absl::MakeConstSpan(\n          learning_rate_vec.data() + learning_rate_offset, table->slice_size());\n      learning_rate_offset += table->slice_size();\n      OP_REQUIRES(c, learning_rate_offset <= learning_rate_vec.size(),\n                  LengthTooShort(\"learning_rate\", learning_rate_vec.size()));\n\n      OP_REQUIRES_OK(\n          c, table->BatchOptimize(\n                 c, num_ids,\n                 reinterpret_cast<const int64_t*>(id_vec.data() + id_split(i)),\n                 value_vec.data() + value_offset, learning_rate, update_time,\n                 false, global_step));\n      value_offset += value_size;\n    }\n    c->set_output(0, c->input(0));\n  }\n};\n\nREGISTER_OP(\"MonolithMultiHashTableOptimize\")\n    .Input(\"mtable: resource\")\n    .Input(\"id: int64\")\n    .Input(\"id_split: int64\")\n    .Input(\"value: float\")\n    .Input(\"learning_rate: float\")\n    .Input(\"update_time: int64\")\n    .Input(\"global_step: int64\")\n    .Output(\"updated_table: resource\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithMultiHashTableOptimize\").Device(DEVICE_CPU),\n    MultiHashTableOptimizeOp);\n\nclass MultiHashTableAssignOp : public OpKernel {\n public:\n  explicit MultiHashTableAssignOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* c) override {\n    core::RefCountPtr<MultiHashTable> mtable;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 0), &mtable));\n    auto id_vec = c->input(1).flat<int64>();\n    auto id_split = c->input(2).flat<int64>();\n    OP_REQUIRES(c, id_split.size() - 1 == mtable->size(),\n                MismatchLength(\"id\", id_split.size() - 1, mtable->size()));\n    auto value_vec = c->input(3).flat<float>();\n    int64 update_time = c->input(4).scalar<int64>()();\n    int n = mtable->size();\n    int value_offset = 0;\n    for (int i = 0; i < n; ++i) {\n      EmbeddingHashTableTfBridge* table = mtable->table(i);\n      const int num_ids = id_split(i + 1) - id_split(i);\n      const int value_size = num_ids * table->dim_size();\n      OP_REQUIRES(c, value_offset + value_size <= value_vec.size(),\n                  LengthTooShort(\"value\", value_vec.size()));\n      OP_REQUIRES_OK(\n          c, table->Assign(\n                 c, num_ids,\n                 reinterpret_cast<const int64_t*>(id_vec.data() + id_split(i)),\n                 value_vec.data() + value_offset, update_time));\n      value_offset += value_size;\n    }\n    c->set_output(0, c->input(0));\n  }\n};\n\nREGISTER_OP(\"MonolithMultiHashTableAssign\")\n    .Input(\"mtable: resource\")\n    .Input(\"id: int64\")\n    .Input(\"id_split: int64\")\n    .Input(\"value: float\")\n    .Input(\"update_time: int64\")\n    .Output(\"updated_table: resource\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithMultiHashTableAssign\").Device(DEVICE_CPU),\n                        MultiHashTableAssignOp);\n\nclass MultiHashTableAssignAddOp : public OpKernel {\n public:\n  explicit MultiHashTableAssignAddOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* c) override {\n    core::RefCountPtr<MultiHashTable> mtable;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 0), &mtable));\n    auto id_vec = c->input(1).flat<int64>();\n    auto id_split = c->input(2).flat<int64>();\n    OP_REQUIRES(c, id_split.size() - 1 == mtable->size(),\n                MismatchLength(\"id\", id_split.size() - 1, mtable->size()));\n    auto value_vec = c->input(3).flat<float>();\n    int64 update_time = c->input(4).scalar<int64>()();\n    int n = mtable->size();\n    int value_offset = 0;\n    for (int i = 0; i < n; ++i) {\n      EmbeddingHashTableTfBridge* table = mtable->table(i);\n      const int num_ids = id_split(i + 1) - id_split(i);\n      const int value_size = num_ids * table->dim_size();\n      OP_REQUIRES(c, value_offset + value_size <= value_vec.size(),\n                  LengthTooShort(\"value\", value_vec.size()));\n      for (int j = id_split(i); j < id_split(i + 1); ++j) {\n        auto value = absl::MakeConstSpan(value_vec.data() + value_offset,\n                                         table->dim_size());\n        OP_REQUIRES_OK(c, table->AssignAdd2(id_vec(j), value, update_time));\n        value_offset += table->dim_size();\n      }\n    }\n    c->set_output(0, c->input(0));\n  }\n};\n\nREGISTER_OP(\"MonolithMultiHashTableAssignAdd\")\n    .Input(\"mtable: resource\")\n    .Input(\"id: int64\")\n    .Input(\"id_split: int64\")\n    .Input(\"value: float\")\n    .Input(\"update_time: int64\")\n    .Output(\"updated_table: resource\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithMultiHashTableAssignAdd\").Device(DEVICE_CPU),\n    MultiHashTableAssignAddOp);\n\nclass MultiHashTableReinitializeOp : public OpKernel {\n public:\n  explicit MultiHashTableReinitializeOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* c) override {\n    core::RefCountPtr<MultiHashTable> mtable;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 0), &mtable));\n    auto table_name = c->input(1).scalar<tstring>()();\n    auto id_vec = c->input(2).flat<int64>();\n    Tensor* status_tensor;\n    OP_REQUIRES_OK(c, c->allocate_output(1, {id_vec.size()}, &status_tensor));\n    auto status_vec = status_tensor->vec<int32>();\n    // -1: table_name does not exist, and the id will not be processed\n    //  0: the id was inserted and is initialized\n    //  1: the id was already in the table and is reinitialized\n    status_vec.setConstant(-1);\n    int* status = reinterpret_cast<int*>(status_tensor->data());\n    std::vector<std::string> names = mtable->names();\n    auto it = std::find_if(\n        names.begin(), names.end(),\n        [&table_name](const std::string& name) { return name == table_name; });\n    if (it == names.end()) {\n      LOG(ERROR) << \"table \" << table_name << \" does not exist!\";\n    } else {\n      int index = std::distance(names.begin(), it);\n      EmbeddingHashTableTfBridge* table = mtable->table(index);\n      OP_REQUIRES_OK(c, table->Reinitialize(\n                            reinterpret_cast<const int64_t*>(id_vec.data()),\n                            id_vec.size(), status));\n    }\n    c->set_output(0, c->input(0));\n  }\n};\n\nREGISTER_OP(\"MonolithMultiHashTableReinitialize\")\n    .Input(\"mtable: resource\")\n    .Input(\"table_name: string\")\n    .Input(\"id: int64\")\n    .Output(\"updated_table: resource\")\n    .Output(\"id_status: int32\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      ctx->set_output(0, ctx->Scalar());\n      ctx->set_output(1, ctx->input(2));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithMultiHashTableReinitialize\").Device(DEVICE_CPU),\n    MultiHashTableReinitializeOp);\n\ntemplate <typename Device>\nclass MultiHashTableFusedOptimizeOp : public OpKernel {\n public:\n  explicit MultiHashTableFusedOptimizeOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"num_of_shards\", &num_shards_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"enable_grad_accumulation\",\n                                     &enable_grad_accumulation_));\n  }\n\n  void ComputeH(OpKernelContext* ctx);\n  void Compute(OpKernelContext* ctx) override {\n    ComputeH(ctx);\n    ctx->set_output(0, ctx->input(0));\n  }\n\n private:\n  bool enable_grad_accumulation_;\n  int num_shards_;\n};\n\ntemplate <>\nvoid MultiHashTableFusedOptimizeOp<CPUDevice>::ComputeH(OpKernelContext* ctx) {\n  auto ids = ctx->input(1).vec<int64_t>().data();\n  auto num_ids = ctx->input(1).NumElements();\n  auto indices = ctx->input(2).vec<int64_t>().data();\n  auto slot_size_vec = ctx->input(3).vec<int32>().data();\n  auto id_grads = ctx->input(4).vec<float>().data();\n  auto num_grads = ctx->input(4).NumElements();\n  auto key_offsets = ctx->input(5).vec<int32>().data();\n  auto emb_offsets = ctx->input(6).vec<int32>().data();\n  auto learning_rates = ctx->input(7).vec<float>().data();\n  auto req_time = ctx->input(8).scalar<int64_t>()();\n  auto global_step = ctx->input(9).scalar<int64_t>()();\n\n  core::RefCountPtr<MultiHashTable> mtable;\n  OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &mtable));\n\n  int num_tables_ = mtable->size();\n  auto optimize = [&](const int begin, const int end) {\n    for (int shard_id = begin; shard_id < end; shard_id++) {\n      int learning_rate_offset = 0;\n      for (int table_id = 0; table_id < num_tables_; table_id++) {\n        int curr_idx = shard_id * num_tables_ + table_id;\n        auto table = mtable->table(table_id);\n        auto learning_rate = absl::MakeConstSpan(\n            learning_rates + learning_rate_offset, table->slice_size());\n        learning_rate_offset += table->slice_size();\n        table->BatchOptimize(ctx, slot_size_vec[curr_idx],\n                             ids + key_offsets[curr_idx],\n                             id_grads + emb_offsets[curr_idx], learning_rate,\n                             req_time, enable_grad_accumulation_, global_step);\n      }\n    }\n  };\n  // TODO(zouxuan): tweak this number for optimization.\n  const int64 kCostPerUnit = 10000000;\n  const DeviceBase::CpuWorkerThreads& worker_threads =\n      *ctx->device()->tensorflow_cpu_worker_threads();\n  Shard(worker_threads.num_threads, worker_threads.workers, num_shards_,\n        kCostPerUnit, optimize);\n}\n\n\nREGISTER_OP(\"MonolithMultiHashTableFusedOptimize\")\n    .Input(\"mtable: resource\")\n    .Input(\"ids: int64\")\n    .Input(\"indices: int64\")\n    .Input(\"fused_slot_size: int32\")\n    .Input(\"id_grads: float\")\n    .Input(\"id_offsets: int32\")\n    .Input(\"grad_offsets: int32\")\n    .Input(\"learning_rate_tensors: float\")\n    .Input(\"req_time: int64\")\n    .Input(\"global_step: int64\")\n    .Output(\"mtable_out: resource\")\n    .Attr(\"num_of_shards: int\")\n    .Attr(\"enable_grad_accumulation: bool = false\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithMultiHashTableFusedOptimize\").Device(DEVICE_CPU),\n    MultiHashTableFusedOptimizeOp<CPUDevice>);\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/net_utils.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <arpa/inet.h>\n#include <ifaddrs.h>\n#include <netinet/in.h>\n#include <sys/types.h>\n#include <cstdio>\n#include <cstring>\n#include <map>\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nstd::multimap<std::string, std::string> GetLocalIpAddreeses() {\n  std::multimap<std::string, std::string> addresses;\n  ifaddrs *ifaddrs_list = nullptr;\n  getifaddrs(&ifaddrs_list);\n\n  for (ifaddrs *ifa = ifaddrs_list; ifa != nullptr; ifa = ifa->ifa_next) {\n    if (!ifa->ifa_addr) {\n      continue;\n    }\n    void *tmp_addr = nullptr;\n    if (ifa->ifa_addr->sa_family == AF_INET) {  // check it is IP4\n      tmp_addr = &(reinterpret_cast<sockaddr_in *>(ifa->ifa_addr)->sin_addr);\n      char buffer[INET_ADDRSTRLEN];\n      inet_ntop(AF_INET, tmp_addr, buffer, INET_ADDRSTRLEN);\n      addresses.insert({ifa->ifa_name, buffer});\n    } else if (ifa->ifa_addr->sa_family == AF_INET6) {  // check it is IP6\n      // is a valid IP6 Address\n      tmp_addr = &(reinterpret_cast<sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr);\n      char buffer[INET6_ADDRSTRLEN];\n      inet_ntop(AF_INET6, tmp_addr, buffer, INET6_ADDRSTRLEN);\n      addresses.insert({ifa->ifa_name, buffer});\n    }\n  }\n  if (ifaddrs_list != nullptr) freeifaddrs(ifaddrs_list);\n  return addresses;\n}\n\nstd::string GetMyHostIp() {\n  // If we are in TCE, env var will provide ip to us.\n  char *ip = getenv(\"MY_HOST_IP\");\n  if (ip != nullptr) {\n    return ip;\n  }\n  auto addresses = GetLocalIpAddreeses();\n  auto it = addresses.find(\"eth0\");\n  if (it == addresses.end()) {\n    return \"\";\n  }\n  return it->second;\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/net_utils.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_NET_UTILS_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_NET_UTILS_H_\n\n#include <map>\n#include <string>\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\n// Gets network interface name to ip addresses mapping.\nstd::multimap<std::string, std::string> GetLocalIpAddreeses();\n\n// Gets a string represents ip address of eth0.\nstd::string GetMyHostIp();\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_NET_UTILS_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/net_utils_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/net_utils.h\"\n#include \"gtest/gtest.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nTEST(NetUtilsTest, Basic) {\n  GetLocalIpAddreeses();\n  GetMyHostIp();\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/normalize_merged_split_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstring>\n\n#include \"absl/algorithm/container.h\"\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_format.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nclass NormalizeMergedSplitOp : public OpKernel {\n public:\n  explicit NormalizeMergedSplitOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor *row_split_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"row_split\", &row_split_input));\n\n    const Tensor *row_split_size_input;\n    OP_REQUIRES_OK(ctx, ctx->input(\"row_split_size\", &row_split_size_input));\n\n    const auto row_split_vec = row_split_input->flat<int64>();\n    int split_num = row_split_input->dim_size(0);\n\n    const auto row_split_size_vec = row_split_size_input->flat<int32>();\n    int merge_num = row_split_size_input->dim_size(0);\n\n    int output_size = split_num + 1 - merge_num;\n    Tensor *normed_row_split_tensor;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(\"normed_row_split\",\n                                             TensorShape({\n                                                 output_size,\n                                             }),\n                                             &normed_row_split_tensor));\n    auto normed_row_split_flat = normed_row_split_tensor->flat<int64_t>();\n\n    int offset = 0;\n    int pre_size = 0;\n    int output_idx = 0;\n    /*\n     row_split: 0, 2, 5, 5, 9, 0, 0, 3, 4\n     row_split_size: 5, 4\n     offset = 0, pre_size = 0\n     output: 0, 2, 5, 5, 9\n     offset = 5, pre_size = 9\n     output: 0, 2, 5, 5, 9, 9, 12, 13\n     offset = 9, pre_size = 13\n     */\n    for (size_t i = 0; i < merge_num; ++i) {\n      if (i == 0) {\n        for (int j = offset; j < offset + row_split_size_vec(i); ++j) {\n          normed_row_split_flat(output_idx) = pre_size + row_split_vec(j);\n          output_idx++;\n        }\n      } else {\n        for (int j = offset + 1; j < offset + row_split_size_vec(i); ++j) {\n          normed_row_split_flat(output_idx) = pre_size + row_split_vec(j);\n          output_idx++;\n        }\n      }\n      offset += row_split_size_vec(i);\n      pre_size += row_split_vec(offset - 1);\n    }\n  }\n};\n\nREGISTER_OP(\"MonolithNormalizeMergedSplit\")\n    .Input(\"row_split: int64\")\n    .Input(\"row_split_size: int32\")\n    .Output(\"normed_row_split: int64\")\n    .SetShapeFn([](shape_inference::InferenceContext* ctx) {\n      ctx->set_output(0, ctx->Vector(ctx->UnknownDim()));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithNormalizeMergedSplit\").Device(DEVICE_CPU),\n                        NormalizeMergedSplitOp);\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/parameter_sync_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <memory>\n\n#include \"grpcpp/ext/proto_server_reflection_plugin.h\"\n#include \"grpcpp/grpcpp.h\"\n#include \"grpcpp/health_check_service_interface.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n#include \"tensorflow/core/platform/tstring.h\"\n\n#include \"monolith/native_training/runtime/ops/parameter_sync_tf_bridge.h\"\n#include \"monolith/native_training/runtime/parameter_sync/dummy_sync_client.h\"\n#include \"monolith/native_training/runtime/parameter_sync/parameter_sync.pb.h\"\n#include \"monolith/native_training/runtime/parameter_sync/parameter_sync_client.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass DummySyncServerOp : public ResourceOpKernel<DummySyncServerTfBridge> {\n public:\n  explicit DummySyncServerOp(OpKernelConstruction* ctx)\n      : ResourceOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"address\", &address_));\n  }\n\n  ~DummySyncServerOp() override = default;\n\n private:\n  Status CreateResource(DummySyncServerTfBridge** server_bridge)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    *server_bridge = new DummySyncServerTfBridge(address_);\n    return Status::OK();\n  };\n\n  std::string address_;\n};\n\nclass DummySyncServerShutdownOp : public OpKernel {\n public:\n  explicit DummySyncServerShutdownOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    DummySyncServerTfBridge* server = nullptr;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &server));\n    core::ScopedUnref unref(server);\n\n    server->Shutdown();\n    // TODO(zhangbiao.david): remove\n    LOG(INFO) << server->DebugString() << \" has been shutdown\";\n\n    Tensor* output;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {1}, &output));\n    auto output_vec = output->vec<int64>();\n    output_vec(0) = 100;\n  }\n};\n\nclass DummySyncServerGetPortOp : public OpKernel {\n public:\n  explicit DummySyncServerGetPortOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    DummySyncServerTfBridge* server = nullptr;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &server));\n    core::ScopedUnref unref(server);\n\n    int port = server->GetSelectedPort();\n    Tensor* output;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {1}, &output));\n    output->scalar<int32>()() = port;\n  }\n};\n\nclass DummySyncClientOp : public ResourceOpKernel<ParameterSyncClientTfBridge> {\n public:\n  explicit DummySyncClientOp(OpKernelConstruction* ctx)\n      : ResourceOpKernel(ctx) {}\n\n  ~DummySyncClientOp() override = default;\n\n private:\n  Status CreateResource(ParameterSyncClientTfBridge** client_bridge)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    *client_bridge =\n        new ParameterSyncClientTfBridge(true, [](const std::string& target) {\n          return std::make_unique<monolith::parameter_sync::DummySyncClient>(\n              target);\n        });\n    return Status::OK();\n  };\n};\n\nclass ParameterSyncClientOp\n    : public ResourceOpKernel<ParameterSyncClientTfBridge> {\n public:\n  explicit ParameterSyncClientOp(OpKernelConstruction* ctx)\n      : ResourceOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"config\", &config_serialized_));\n    OP_REQUIRES(ctx, config_.ParseFromString(config_serialized_),\n                errors::InvalidArgument(\"Unable to parse config. Make \"\n                                        \"sure it is serialized version of \"\n                                        \"ClientConfig\"));\n  }\n\n  ~ParameterSyncClientOp() override = default;\n\n private:\n  Status CreateResource(ParameterSyncClientTfBridge** client_bridge)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    *client_bridge =\n        new ParameterSyncClientTfBridge(false, [](const std::string& target) {\n          return std::make_unique<\n              monolith::parameter_sync::ParameterSyncClient>(target);\n        });\n    (*client_bridge)\n        ->TryReplace(config_.targets(), config_.targets_extra_info());\n\n    return Status::OK();\n  };\n\n  std::string config_serialized_;\n\n  monolith::parameter_sync::ClientConfig config_;\n};\n\nclass ParameterSyncOp : public OpKernel {\n public:\n  explicit ParameterSyncOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    ParameterSyncClientTfBridge* client = nullptr;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &client));\n    core::ScopedUnref unref(client);\n\n    const Tensor* config_str;\n    monolith::parameter_sync::ClientConfig config;\n    OP_REQUIRES_OK(ctx, ctx->input(\"config_str\", &config_str));\n    OP_REQUIRES(ctx, config.ParseFromString(config_str->flat<tstring>()(0)),\n                errors::InvalidArgument(\"Unable to parse config. Make \"\n                                        \"sure it is serialized version of \"\n                                        \"ClientConfig\"));\n\n    client->TryReplace(config.targets(), config.targets_extra_info());\n    LOG_EVERY_N_SEC(INFO, 600) << client->DebugString();\n    LOG_EVERY_N_SEC(INFO, 600)\n        << \"ClientConfig: \" << config.ShortDebugString() << std::endl;\n    monolith::parameter_sync::PushResult result;\n    OP_REQUIRES_OK(ctx,\n                   client->Push(config.model_name(), config.signature_name(),\n                                config.timeout_in_ms(), &result));\n\n    std::string json;\n    auto option = google::protobuf::util::JsonOptions();\n    option.add_whitespace = true;\n    option.preserve_proto_field_names = true;\n    google::protobuf::util::MessageToJsonString(result, &json, option);\n    Tensor* output;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {1}, &output));\n    auto result_vec = output->vec<tstring>();\n    result_vec(0) = json;\n  }\n};\n\nREGISTER_OP(\"MonolithDummySyncServer\")\n    .Output(\"handle: resource\")\n    .Attr(\"address: string\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithDummySyncServer\").Device(DEVICE_CPU),\n                        DummySyncServerOp);\n\nREGISTER_OP(\"MonolithDummySyncServerShutdown\")\n    .Input(\"handle: resource\")\n    .Output(\"size: int64\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithDummySyncServerShutdown\").Device(DEVICE_CPU),\n    DummySyncServerShutdownOp);\n\nREGISTER_OP(\"MonolithDummySyncServerGetPort\")\n    .Input(\"handle: resource\")\n    .Output(\"size: int32\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithDummySyncServerGetPort\").Device(DEVICE_CPU),\n    DummySyncServerGetPortOp);\n\nREGISTER_OP(\"MonolithParameterSyncClient\")\n    .Output(\"handle: resource\")\n    .Attr(\"config: string\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithParameterSyncClient\").Device(DEVICE_CPU),\n                        ParameterSyncClientOp);\n\nREGISTER_OP(\"MonolithDummySyncClient\")\n    .Output(\"handle: resource\")\n    .Attr(\"config: string = ''\")\n    .Attr(\"container: string = ''\")\n    .Attr(\"shared_name: string = ''\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithDummySyncClient\").Device(DEVICE_CPU),\n                        DummySyncClientOp);\nREGISTER_OP(\"MonolithParameterSync\")\n    .Input(\"handle: resource\")\n    .Input(\"config_str: string\")\n    .Output(\"result: string\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithParameterSync\").Device(DEVICE_CPU),\n                        ParameterSyncOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/parameter_sync_tf_bridge.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/ops/parameter_sync_tf_bridge.h\"\n\n#include \"absl/strings/str_cat.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nusing ::monolith::parameter_sync::ClientConfig_TargetExtraInfo;\nusing ::monolith::parameter_sync::PushRequest;\nusing ::monolith::parameter_sync::PushResult;\n\nvoid AddIdToDelta(\n    const std::string& name, const EmbeddingHashTableTfBridge& table,\n    const std::vector<int64_t>& ids,\n    monolith::parameter_sync::PushRequest_DeltaEmbeddingHashTable* delta) {\n  int dim_size = static_cast<int>(table.dim_size());\n  std::vector<float> embedding(dim_size);\n  delta->set_unique_id(name);\n  delta->set_dim_size(dim_size);\n\n  int delta_size = static_cast<int>(ids.size());\n  auto* mutable_fids = delta->mutable_fids();\n  auto* embeddings = delta->mutable_embeddings();\n  mutable_fids->Reserve(delta_size);\n  embeddings->Reserve(delta_size * dim_size);\n\n  for (int64_t id : ids) {\n    mutable_fids->Add(id);\n    table.Lookup(nullptr, id, embedding.data());\n    embeddings->Add(embedding.data(), embedding.data() + dim_size);\n  }\n}\n\n}  // namespace\n\nStatus ParameterSyncClientTfBridge::Push(const std::string& model_name,\n                                         const std::string& signature_name,\n                                         int64_t timeout_in_ms,\n                                         PushResult* result) const {\n  try {\n    PushRequest request;\n    const bool is_mtable = mtable_ != nullptr;\n    request.set_model_name(model_name);\n    if (is_mtable) {\n      // TODO(leqi.zou): Currently it is hard coded.\n      // Will revisit this part later.\n      request.set_signature_name(\n          absl::StrCat(mtable_->shared_name(), \"/raw_assign\"));\n      request.mutable_delta_multi_hash_tables()->Reserve(mtable_->size());\n    } else {\n      request.set_signature_name(signature_name);\n      request.mutable_delta_hash_tables()->Reserve(hash_tables_.size());\n    }\n    request.set_timeout_in_ms(timeout_in_ms);\n    std::vector<std::pair<int64_t, const void*>> fids_and_tables =\n        touched_key_set_->GetAndClear();\n    std::unordered_map<const void*, std::vector<int64_t>> table_to_fids;\n    for (const auto& fid_and_table : fids_and_tables) {\n      table_to_fids[fid_and_table.second].push_back(fid_and_table.first);\n    }\n\n    if (is_mtable) {\n      for (int i = 0; i < mtable_->size(); ++i) {\n        AddIdToDelta(mtable_->name(i), *mtable_->table(i),\n                     table_to_fids[mtable_->table(i)],\n                     request.mutable_delta_multi_hash_tables()->Add());\n      }\n    } else {\n      for (const auto& kv : hash_tables_) {\n        const std::string& name = kv.first;\n        const auto* table = kv.second;\n        AddIdToDelta(name, *table, table_to_fids[table],\n                     request.mutable_delta_hash_tables()->Add());\n      }\n    }\n\n    if (fids_and_tables.size() > 0) {\n      *result = sync_client_manager_->Push(request, model_name, signature_name);\n      LOG_EVERY_N_SEC(INFO, 600) << \"Response: \" << result->ShortDebugString();\n    } else {\n      LOG_EVERY_N_SEC(INFO, 600) << \"No updated FIDs!\";\n    }\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\nStatus ParameterSyncClientTfBridge::TryReplace(\n    const google::protobuf::RepeatedPtrField<std::string>& targets,\n    const google::protobuf::RepeatedPtrField<ClientConfig_TargetExtraInfo>&\n        targets_extra_info) {\n  try {\n    sync_client_manager_->TryReplace(targets, targets_extra_info);\n    return Status::OK();\n  } catch (const std::exception& e) {\n    return errors::InvalidArgument(e.what());\n  }\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/parameter_sync_tf_bridge.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_PARAMETER_SYNC_TF_BRIDGE_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_PARAMETER_SYNC_TF_BRIDGE_H_\n\n#include <memory>\n#include <utility>\n\n#include \"absl/strings/str_format.h\"\n#include \"absl/synchronization/mutex.h\"\n#include \"monolith/native_training/runtime/ops/embedding_hash_table_tf_bridge.h\"\n#include \"monolith/native_training/runtime/ops/multi_hash_table.h\"\n#include \"monolith/native_training/runtime/parameter_sync/dummy_sync_server.h\"\n#include \"monolith/native_training/runtime/parameter_sync/sync_client_interface.h\"\n#include \"monolith/native_training/runtime/parameter_sync/sync_client_manager.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\n// 64MB\nconst size_t MAX_TOUCHED_KEYS = 64 * 1024 * 1024 / (8 * 4);\n\nclass DummySyncServerTfBridge : public ResourceBase {\n public:\n  using DummySyncServer = monolith::parameter_sync::DummySyncServer;\n\n  explicit DummySyncServerTfBridge(const std::string& target) {\n    server_ = std::make_unique<DummySyncServer>(target);\n  }\n\n  void Shutdown() const { server_->Shutdown(); }\n\n  std::string GetTarget() const { return server_->GetTarget(); }\n\n  int GetSelectedPort() const { return server_->GetSelectedPort(); }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"DummySyncServerTfBridge target = %s\",\n                           server_->GetTarget());\n  }\n\n private:\n  std::unique_ptr<DummySyncServer> server_;\n};\n\nclass ParameterSyncClientTfBridge : public ResourceBase {\n public:\n  using SyncClientInterface = monolith::parameter_sync::SyncClientInterface;\n  using PushResult = monolith::parameter_sync::PushResult;\n  using SyncClientManager = monolith::parameter_sync::SyncClientManager;\n\n  ParameterSyncClientTfBridge(\n      bool is_dummy_sync_client,\n      std::function<std::unique_ptr<SyncClientInterface>(const std::string&)>\n          client_factory)\n      : is_dummy_sync_client_(is_dummy_sync_client) {\n    sync_client_manager_ =\n        std::make_unique<SyncClientManager>(std::move(client_factory));\n    if (!IsDummySyncClient()) {\n      touched_key_set_ = std::move(\n          std::make_unique<HopscotchHashSet<std::pair<int64_t, const void*>>>(\n              MAX_TOUCHED_KEYS, 1024));\n    }\n  }\n\n  Status Push(const std::string& model_name, const std::string& signature_name,\n              int64_t timeout_in_ms, PushResult* result) const;\n\n  Status TryReplace(\n      const google::protobuf::RepeatedPtrField<std::string>& targets,\n      const google::protobuf::RepeatedPtrField<\n          monolith::parameter_sync::ClientConfig_TargetExtraInfo>&\n          targets_extra_info);\n\n  Status AddHashTableResource(const std::string& name,\n                              EmbeddingHashTableTfBridge* hash_table)\n      ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {\n    absl::WriterMutexLock l(&mu_);\n    DCHECK(!hash_tables_.count(name));\n    if (mtable_ != nullptr) {\n      return errors::InvalidArgument(\n          \"Only one type of tables can be set. MultiHashTable is set.\");\n    }\n    hash_tables_[name] = hash_table;\n    return Status::OK();\n  }\n\n  Status SetMultiHashTableResource(MultiHashTable* mtable)\n      ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {\n    absl::WriterMutexLock l(&mu_);\n    if (mtable_ != nullptr) {\n      return errors::AlreadyExists(\n          \"The sync client is set a mtable resource already.\");\n    }\n    if (hash_tables_.size() > 0) {\n      return errors::InvalidArgument(\n          \"Only one type of tables can be set. HashTable is set.\");\n    }\n    mtable_ = mtable;\n    return Status::OK();\n  }\n\n  std::string DebugString() const override {\n    std::vector<std::string> hash_table_names;\n    hash_table_names.reserve(hash_tables_.size());\n    std::transform(hash_tables_.begin(), hash_tables_.end(),\n                   std::back_inserter(hash_table_names),\n                   [](const auto& kv) { return kv.first; });\n    return absl::StrFormat(\"hash tables = [%s]\",\n                           absl::StrJoin(hash_table_names, \", \"));\n  }\n\n  bool IsDummySyncClient() const { return is_dummy_sync_client_; }\n\n  HopscotchHashSet<std::pair<int64_t, const void*>>* GetTouchedKeySet() {\n    return touched_key_set_.get();\n  }\n\n private:\n  // hash table name -> hash table resource\n  std::map<std::string, EmbeddingHashTableTfBridge*> hash_tables_\n      ABSL_GUARDED_BY(mu_);\n\n  MultiHashTable* mtable_ = nullptr;\n  std::unique_ptr<SyncClientManager> sync_client_manager_;\n  std::unique_ptr<HopscotchHashSet<std::pair<int64_t, const void*>>>\n      touched_key_set_;\n\n  mutable absl::Mutex mu_;\n\n  bool is_dummy_sync_client_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_PARAMETER_SYNC_TF_BRIDGE_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/prediction_service_grpc.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2020 Google Inc. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n#include \"monolith/native_training/runtime/ops/prediction_service_grpc.h\"\n\n#include \"absl/time/clock.h\"\n#include \"grpcpp/create_channel.h\"\n#include \"grpcpp/security/credentials.h\"\n#include \"grpcpp/support/channel_arguments.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nabsl::Status FromGrpcStatus(const ::grpc::Status& s) {\n  if (s.ok()) {\n    return absl::Status();\n  }\n  return absl::Status(static_cast<absl::StatusCode>(s.error_code()),\n                      s.error_message());\n}\n\nint GetCallbackThreadNum() {\n  const char* thread_num_str =\n      std::getenv(\"MONOLITH_GRPC_REMOTE_CALLBACK_THREADS\");\n  if (thread_num_str == nullptr) {\n    return 10;\n  }\n  return std::stoi(std::string(thread_num_str));\n}\n\n}  // namespace\n\n::grpc::CompletionQueue* GetSharedCompletionQueue() {\n  static CompletionQueueWithThreads* cq_with_threads =\n      new CompletionQueueWithThreads(GetCallbackThreadNum());\n  return cq_with_threads->GetCompletionQueue();\n}\n\nCompletionQueueWithThreads::CompletionQueueWithThreads(\n    const size_t thread_num) {\n  queues_ = std::vector<::grpc::CompletionQueue>(thread_num);\n  for (size_t i = 0; i < thread_num; ++i) {\n    auto* cq = &queues_[i];\n    auto pooling_fn = [cq]() {\n      void* p_tag;\n      bool ok;\n      while (cq->Next(&p_tag, &ok)) {\n        RemotePredictCQTag* cq_tag = static_cast<RemotePredictCQTag*>(p_tag);\n        cq_tag->OnCompleted(ok);\n      }\n    };\n    cq_threads_.emplace_back(std::make_unique<std::thread>(pooling_fn));\n  }\n}\n\nCompletionQueueWithThreads::~CompletionQueueWithThreads() {\n  for (size_t i = 0; i < queues_.size(); ++i) {\n    queues_[i].Shutdown();\n  }\n  for (size_t i = 0; i < cq_threads_.size(); ++i) {\n    cq_threads_[i]->join();\n  }\n}\n\n::grpc::CompletionQueue* CompletionQueueWithThreads::GetCompletionQueue() {\n  return &queues_[queue_idx_++ % queues_.size()];\n}\n\nPredictionServiceGrpcPerAddress::PredictionServiceGrpcPerAddress(\n    const std::string& target_address) {\n  // TODO(b/159739577): Set security channel from incoming rpc request.\n  // auto channel = ::grpc::CreateChannel(target_address,\n  //                                      ::grpc::InsecureChannelCredentials());\n  ::grpc::ChannelArguments arg;\n  arg.SetMaxReceiveMessageSize(INT32_MAX);\n  arg.SetMaxSendMessageSize(INT32_MAX);\n  auto channel = ::grpc::CreateCustomChannel(\n      target_address, ::grpc::InsecureChannelCredentials(), arg);\n  stub_ = tensorflow::serving::PredictionService::NewStub(channel);\n}\n\nvoid PredictionServiceGrpcPerAddress::Predict(\n    tensorflow::serving::PredictRequest* request,\n    tensorflow::serving::PredictResponse* response,\n    std::function<void(absl::Status status, DoneCallback&&)> callback,\n    int64_t max_rpc_deadline_millis, DoneCallback op_done) {\n  ::grpc::ClientContext* rpc = new ::grpc::ClientContext;\n  DoneCallback rpc_done = [rpc, done = op_done]() {\n    delete rpc;\n    done();\n  };\n  std::function<void(::grpc::Status)> wrapped_callback =\n      [callback,\n       rpc_done = std::move(rpc_done)](::grpc::Status status) mutable {\n        callback(FromGrpcStatus(status), std::forward<DoneCallback>(rpc_done));\n      };\n\n  new RemotePredictCQTag(GetSharedCompletionQueue(), rpc, &stub_, request,\n                         response, std::move(wrapped_callback));\n}\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/prediction_service_grpc.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2020 Google Inc. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_PREDICTION_SERVICE_GRPC_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_PREDICTION_SERVICE_GRPC_H_\n#include <string>\n#include <thread>\n\n#include \"absl/status/status.h\"\n#include \"absl/time/time.h\"\n#include \"tensorflow_serving/apis/prediction_service.grpc.pb.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass RemotePredictCQTag {\n public:\n  RemotePredictCQTag(\n      ::grpc::CompletionQueue *cq, ::grpc::ClientContext *rpc,\n      std::unique_ptr<::tensorflow::serving::PredictionService::Stub> *stub_,\n      ::tensorflow::serving::PredictRequest *request,\n      ::tensorflow::serving::PredictResponse *response,\n      std::function<void(grpc::Status)> callback)\n      : response_(response), callback_(std::move(callback)) {\n    std::unique_ptr<\n        grpc::ClientAsyncResponseReader<::tensorflow::serving::PredictResponse>>\n        rpc_call = (*stub_)->AsyncPredict(rpc, *request, cq);\n    rpc_call->Finish(response, &status_, reinterpret_cast<void *>(this));\n  };\n  ~RemotePredictCQTag() {}\n\n  // OnCompleted is invoked when the RPC has finished.\n  // Implementations of OnCompleted can delete *this.\n  void OnCompleted(bool ok) {\n    callback_(status_);\n    delete this;\n  }\n\n private:\n  ::tensorflow::serving::PredictResponse *response_;\n  std::function<void(grpc::Status)> callback_;\n  grpc::Status status_;\n};\n\nclass CompletionQueueWithThreads {\n public:\n  explicit CompletionQueueWithThreads(const size_t thread_num);\n\n  ~CompletionQueueWithThreads();\n\n  ::grpc::CompletionQueue *GetCompletionQueue();\n\n private:\n  std::atomic_ullong queue_idx_;\n  std::vector<::grpc::CompletionQueue> queues_;\n  std::vector<std::unique_ptr<std::thread>> cq_threads_;\n};\n\n::grpc::CompletionQueue *GetSharedCompletionQueue();\n\n// gRPC based communication point with PredictionService.\nclass PredictionServiceGrpcPerAddress {\n public:\n  using DoneCallback = std::function<void()>;\n  explicit PredictionServiceGrpcPerAddress(const std::string &target_address);\n\n  void Predict(\n      ::tensorflow::serving::PredictRequest *request,\n      ::tensorflow::serving::PredictResponse *response,\n      std::function<void(absl::Status status, DoneCallback &&)> callback,\n      int64_t max_rpc_deadline_millis, DoneCallback op_done);\n\n private:\n  std::unique_ptr<::tensorflow::serving::PredictionService::Stub> stub_;\n};\n\nclass PredictionServiceGrpc {\n public:\n  using DoneCallback = std::function<void()>;\n\n  void Predict(\n      tensorflow::serving::PredictRequest *request,\n      tensorflow::serving::PredictResponse *response,\n      std::function<void(absl::Status status, DoneCallback &&)> callback,\n      int64_t max_rpc_deadline_millis, DoneCallback op_done) {\n    size_t idx = std::rand() % services_.size();\n    services_[idx]->Predict(request, response, callback,\n                            max_rpc_deadline_millis, op_done);\n  }\n\n  explicit PredictionServiceGrpc(const std::vector<std::string> &address_list) {\n    size_t n = address_list.size();\n    services_.reserve(n);\n    for (const auto &addr : address_list) {\n      services_.push_back(\n          std::make_unique<PredictionServiceGrpcPerAddress>(addr));\n    }\n  }\n\n private:\n  std::vector<std::unique_ptr<PredictionServiceGrpcPerAddress>> services_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_PREDICTION_SERVICE_GRPC_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/reduce_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstring>\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n#include \"tensorflow/core/util/work_sharder.h\"\n\n#include \"monolith/native_training/runtime/hash_table/optimizer/avx_utils.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\n// The difference between this reduce sum op and tf.sparse.reduce_sum is that\n// this supports sparse values which are vectors.\nclass ReduceSumOp : public OpKernel {\n public:\n  explicit ReduceSumOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& id_indices = ctx->input(0);\n    auto id_indices_mat = id_indices.matrix<int64>();\n    const Tensor& id_values = ctx->input(1);\n    const int64 value_size = id_values.shape().dim_size(1);\n    auto id_values_mat = id_values.matrix<float>();\n    const Tensor& id_dense_shape = ctx->input(2);\n    const int64 batch_size = id_dense_shape.flat<int64>()(0);\n    Tensor* reduced;\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(0, {batch_size, value_size}, &reduced));\n    std::memset(reduced->data(), 0, reduced->AllocatedBytes());\n    auto reduced_mat = reduced->matrix<float>();\n    for (int64 i = 0; i < id_indices_mat.dimension(0); ++i) {\n      int64 batch = id_indices_mat(i, 0);\n      reduced_mat.chip<0>(batch) += id_values_mat.chip<0>(i);\n    }\n  }\n};\n\n// The difference between this reduce mean op and tf.sparse.reduce_mean is that\n// this supports sparse values which are vectors.\nclass ReduceMeanOp : public OpKernel {\n public:\n  explicit ReduceMeanOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& id_indices = ctx->input(0);\n    auto id_indices_mat = id_indices.matrix<int64>();\n    const Tensor& id_values = ctx->input(1);\n    const int64 value_size = id_values.shape().dim_size(1);\n    auto id_values_mat = id_values.matrix<float>();\n    const Tensor& id_dense_shape = ctx->input(2);\n    const int64 batch_size = id_dense_shape.flat<int64>()(0);\n\n    Tensor* reduced;\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(0, {batch_size, value_size}, &reduced));\n    std::memset(reduced->data(), 0, reduced->AllocatedBytes());\n    auto reduced_mat = reduced->matrix<float>();\n    std::vector<size_t> counter(batch_size, 0);\n\n    for (int64 i = 0; i < id_indices_mat.dimension(0); ++i) {\n      int64 batch = id_indices_mat(i, 0);\n      reduced_mat.chip<0>(batch) += id_values_mat.chip<0>(i);\n      counter[batch] += 1;\n    }\n    for (int64 i = 0; i < batch_size; ++i) {\n      float multiply = 1.0 / static_cast<float>(counter[i]);\n      for (int64 j = 0; j < value_size; ++j) {\n        reduced_mat(i, j) *= multiply;\n      }\n    }\n  }\n};\n\n// The difference between this reduce square norm and tf.sparse.segment_sqrt_n\n// is that this supports sparse values which are vectors.\nclass ReduceSquareNormOp : public OpKernel {\n public:\n  explicit ReduceSquareNormOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& id_indices = ctx->input(0);\n    auto id_indices_mat = id_indices.matrix<int64>();\n    const Tensor& id_values = ctx->input(1);\n    const int64 value_size = id_values.shape().dim_size(1);\n    auto id_values_mat = id_values.matrix<float>();\n    const Tensor& id_dense_shape = ctx->input(2);\n    const int64 batch_size = id_dense_shape.flat<int64>()(0);\n\n    Tensor* reduced;\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(0, {batch_size, value_size}, &reduced));\n    std::memset(reduced->data(), 0, reduced->AllocatedBytes());\n    auto reduced_mat = reduced->matrix<float>();\n\n    for (int64 i = 0; i < id_indices_mat.dimension(0); ++i) {\n      int64 batch = id_indices_mat(i, 0);\n      for (int64 j = 0; j < value_size; ++j) {\n        reduced_mat(batch, j) += (id_values_mat(i, j) * id_values_mat(i, j));\n      }\n    }\n    for (int64 i = 0; i < batch_size; ++i) {\n      for (int64 j = 0; j < value_size; ++j) {\n        reduced_mat(i, j) = std::sqrt(reduced_mat(i, j));\n      }\n    }\n  }\n};\n\nclass ReduceSumGradientOp : public OpKernel {\n public:\n  explicit ReduceSumGradientOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& id_indices = ctx->input(0);\n    auto id_indices_mat = id_indices.matrix<int64>();\n    const int64 len_ids = id_indices_mat.dimension(0);\n    const Tensor& grads = ctx->input(1);\n    auto grads_mat = grads.matrix<float>();\n    const int64 grad_size = grads_mat.dimension(1);\n    Tensor* id_value_grads;\n    OP_REQUIRES_OK(\n        ctx, ctx->allocate_output(0, {len_ids, grad_size}, &id_value_grads));\n    auto id_value_grads_flat = id_value_grads->flat<float>();\n    // Single thread is actually more efficient.\n    for (int64 i = 0; i < len_ids; ++i) {\n      int64 batch = id_indices_mat(i, 0);\n      std::memcpy(\n          static_cast<float*>(id_value_grads_flat.data()) + i * grad_size,\n          const_cast<float*>(grads_mat.data()) + batch * grad_size,\n          sizeof(float) * grad_size);\n    }\n  }\n};\n\nclass ReduceMeanGradientOp : public OpKernel {\n public:\n  explicit ReduceMeanGradientOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& id_indices = ctx->input(0);\n    auto id_indices_mat = id_indices.matrix<int64>();\n    const int64 len_ids = id_indices_mat.dimension(0);\n    const Tensor& grads = ctx->input(1);\n    auto grads_mat = grads.matrix<float>();\n    const int64 grad_size = grads_mat.dimension(1);\n    Tensor* id_value_grads;\n    OP_REQUIRES_OK(\n        ctx, ctx->allocate_output(0, {len_ids, grad_size}, &id_value_grads));\n    auto id_value_grads_mat = id_value_grads->matrix<float>();\n    // grad_size equals to indices's batch size.\n    std::vector<size_t> counter(grad_size, 0);\n    for (int64 i = 0; i < len_ids; ++i) {\n      int64 batch = id_indices_mat(i, 0);\n      counter[batch] += 1;\n    }\n\n    for (int64 i = 0; i < len_ids; ++i) {\n      int64 batch = id_indices_mat(i, 0);\n      float multiply = 1.0 / static_cast<float>(counter[batch]);\n      for (int64 j = 0; j < grad_size; ++j) {\n        id_value_grads_mat(i, j) = grads_mat(batch, j) * multiply;\n      }\n    }\n  }\n};\n\nclass ReduceSquareNormGradientOp : public OpKernel {\n public:\n  explicit ReduceSquareNormGradientOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& id_indices = ctx->input(0);\n    auto id_indices_mat = id_indices.matrix<int64>();\n    const int64 len_ids = id_indices_mat.dimension(0);\n    const Tensor& id_values = ctx->input(1);\n    auto id_values_mat = id_values.matrix<float>();\n    const Tensor& grads = ctx->input(2);\n    auto grads_mat = grads.matrix<float>();\n    const int64 batch_size = grads_mat.dimension(0);\n    const int64 grad_size = grads_mat.dimension(1);\n    Tensor* id_value_grads;\n    OP_REQUIRES_OK(\n        ctx, ctx->allocate_output(0, {len_ids, grad_size}, &id_value_grads));\n    auto id_value_grads_mat = id_value_grads->matrix<float>();\n\n    Tensor reduced_values(DT_FLOAT, TensorShape({batch_size, grad_size}));\n    std::memset(reduced_values.data(), 0, reduced_values.AllocatedBytes());\n    auto reduced_mat = reduced_values.matrix<float>();\n\n    for (int64 i = 0; i < len_ids; ++i) {\n      int64 batch = id_indices_mat(i, 0);\n      for (int64 j = 0; j < grad_size; ++j) {\n        reduced_mat(batch, j) += (id_values_mat(i, j) * id_values_mat(i, j));\n      }\n    }\n    for (int64 i = 0; i < batch_size; ++i) {\n      for (int64 j = 0; j < grad_size; ++j) {\n        reduced_mat(i, j) = std::sqrt(reduced_mat(i, j));\n      }\n    }\n\n    for (int64 i = 0; i < len_ids; ++i) {\n      int64 batch = id_indices_mat(i, 0);\n      for (int64 j = 0; j < grad_size; ++j) {\n        // dl/dx = x/sqrt(sum(x)) * dl/dy\n        float multiply = (reduced_mat(batch, j) == 0)\n                             ? 0.0\n                             : id_values_mat(i, j) / reduced_mat(batch, j);\n        id_value_grads_mat(i, j) = grads_mat(batch, j) * multiply;\n      }\n    }\n  }\n};\n\n// This is an extended op that supports reducesum and split in one fused op.\nclass ReduceSumAndSplitOp : public OpKernel {\n public:\n  explicit ReduceSumAndSplitOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"M\", &M_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"split_dims\", &split_dims_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& id_indices = ctx->input(0);\n    auto id_indices_mat = id_indices.matrix<int64>();\n    const Tensor& id_values = ctx->input(1);\n    const int64 value_size = id_values.shape().dim_size(1);\n    auto id_values_mat = id_values.matrix<float>();\n    const Tensor& id_dense_shape = ctx->input(2);\n    const int64 batch_size = id_dense_shape.flat<int64>()(0);\n\n    std::vector<Tensor*> reduced_list(M_);\n    for (int i = 0; i < M_; ++i) {\n      OP_REQUIRES_OK(ctx,\n                     ctx->allocate_output(i, {batch_size, split_dims_[i]},\n                                          &reduced_list[i]));\n      std::memset(reduced_list[i]->data(), 0,\n                  reduced_list[i]->AllocatedBytes());\n    }\n\n    for (int64 i = 0; i < id_indices_mat.dimension(0); ++i) {\n      int64 batch = id_indices_mat(i, 0);\n      int emb_offset = 0;\n      for (int j = 0; j < M_; ++j) {\n        auto reduced_mat = reduced_list[j]->matrix<float>();\n        int embedding_dim = split_dims_[j];\n        float* input_a = const_cast<float*>(id_values_mat.data()) +\n                         i * value_size + emb_offset;\n        float* output_b =\n            static_cast<float*>(reduced_mat.data()) + batch * split_dims_[j];\n        ::monolith::hash_table::ReduceSum(input_a, output_b, output_b,\n                                          split_dims_[j]);\n        emb_offset += split_dims_[j];\n      }\n    }\n  }\n\n private:\n  int M_;\n  std::vector<int> split_dims_;\n};\n\nclass ReduceSumAndSplitGradientOp : public OpKernel {\n public:\n  explicit ReduceSumAndSplitGradientOp(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"M\", &M_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"split_dims\", &split_dims_));\n    grad_dim_ = 0;\n    for (int i = 0; i < M_; i++) {\n      grad_dim_ += split_dims_[i];\n    }\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& id_indices = ctx->input(0);\n    auto id_indices_mat = id_indices.matrix<int64>();\n    const int64 len_ids = id_indices_mat.dimension(0);\n    Tensor* id_value_grads;\n    OP_REQUIRES_OK(\n        ctx, ctx->allocate_output(0, {len_ids, grad_dim_}, &id_value_grads));\n    auto id_value_grads_flat = id_value_grads->flat<float>();\n    int offset = 0;\n    for (int i = 0; i < M_; ++i) {\n      const Tensor& grads = ctx->input(i + 1);\n      auto grads_mat = grads.matrix<float>();\n      const int64 grad_size = grads_mat.dimension(1);\n      CHECK(grad_size == split_dims_[i]);\n      auto block_size = sizeof(float) * grad_size;\n      for (int64 j = 0; j < len_ids; ++j) {\n        int64 batch = id_indices_mat(j, 0);\n        std::memcpy(static_cast<float*>(id_value_grads_flat.data()) +\n                        j * grad_dim_ + offset,\n                    const_cast<float*>(grads_mat.data()) + batch * grad_size,\n                    block_size);\n      }\n      offset += grad_size;\n    }\n  }\n\n private:\n  int M_;\n  std::vector<int> split_dims_;\n  int grad_dim_;\n};\n\nStatus ReduceShape(shape_inference::InferenceContext* ctx) {\n  shape_inference::ShapeHandle dense_handle;\n  TF_RETURN_IF_ERROR(ctx->MakeShapeFromShapeTensor(2, &dense_handle));\n  shape_inference::DimensionHandle dim0 = ctx->Dim(dense_handle, 0);\n  shape_inference::DimensionHandle dim1 = ctx->Dim(ctx->input(1), 1);\n  ctx->set_output(0, ctx->MakeShape({dim0, dim1}));\n  return Status::OK();\n}\n\nStatus GradientReduceShape(shape_inference::InferenceContext* ctx) {\n  shape_inference::DimensionHandle len_id = ctx->Dim(ctx->input(0), 0);\n  shape_inference::DimensionHandle grad_size = ctx->Dim(ctx->input(1), 1);\n  ctx->set_output(0, ctx->MakeShape({len_id, grad_size}));\n  return Status::OK();\n}\n\nStatus FusedReduceShape(shape_inference::InferenceContext* ctx) {\n  int M;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"M\", &M));\n  std::vector<int> split_dims;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"split_dims\", &split_dims));\n  CHECK_EQ(split_dims.size(), M);\n  shape_inference::ShapeHandle dense_handle;\n  TF_RETURN_IF_ERROR(ctx->MakeShapeFromShapeTensor(2, &dense_handle));\n  for (int i = 0; i < M; i++) {\n    shape_inference::DimensionHandle dim0 = ctx->Dim(dense_handle, 0);\n    ctx->set_output(i, ctx->MakeShape({dim0, split_dims[i]}));\n  }\n\n  return Status::OK();\n}\n\nStatus FusedGradientReduceShape(shape_inference::InferenceContext* ctx) {\n  int M;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"M\", &M));\n  std::vector<int> split_dims;\n  TF_RETURN_IF_ERROR(ctx->GetAttr(\"split_dims\", &split_dims));\n  CHECK_EQ(split_dims.size(), M);\n  int grad_dim = 0;\n  for (int i = 0; i < M; i++) {\n    grad_dim += split_dims[i];\n  }\n  shape_inference::DimensionHandle len_id = ctx->Dim(ctx->input(0), 0);\n  ctx->set_output(0, ctx->MakeShape({len_id, grad_dim}));\n  return Status::OK();\n}\n\nREGISTER_OP(\"MonolithReduceSum\")\n    .Input(\"id_indices: int64\")\n    .Input(\"id_values: float\")\n    .Input(\"id_dense_shape: int64\")\n    .Output(\"reduced: float\")\n    .SetShapeFn(ReduceShape);\n\nREGISTER_OP(\"MonolithReduceMean\")\n    .Input(\"id_indices: int64\")\n    .Input(\"id_values: float\")\n    .Input(\"id_dense_shape: int64\")\n    .Output(\"reduced: float\")\n    .SetShapeFn(ReduceShape);\n\nREGISTER_OP(\"MonolithReduceSquareNorm\")\n    .Input(\"id_indices: int64\")\n    .Input(\"id_values: float\")\n    .Input(\"id_dense_shape: int64\")\n    .Output(\"reduced: float\")\n    .SetShapeFn(ReduceShape);\n\nREGISTER_OP(\"MonolithReduceSumGradient\")\n    .Input(\"id_indices: int64\")\n    .Input(\"grads: float\")\n    .Output(\"id_values_grads: float\")\n    .SetShapeFn(GradientReduceShape);\n\nREGISTER_OP(\"MonolithReduceMeanGradient\")\n    .Input(\"id_indices: int64\")\n    .Input(\"grads: float\")\n    .Output(\"id_values_grads: float\")\n    .SetShapeFn(GradientReduceShape);\n\nREGISTER_OP(\"MonolithReduceSquareNormGradient\")\n    .Input(\"id_indices: int64\")\n    .Input(\"id_values: float\")\n    .Input(\"grads: float\")\n    .Output(\"id_values_grads: float\")\n    .SetShapeFn(GradientReduceShape);\n\nREGISTER_OP(\"MonolithFusedReduceSumAndSplit\")\n    .Input(\"id_indices: int64\")\n    .Input(\"id_values: float\")\n    .Input(\"id_dense_shape: int64\")\n    .Output(\"reduced: M * float\")\n    .Attr(\"M: int\")\n    .Attr(\"split_dims: list(int)\")\n    .SetShapeFn(FusedReduceShape);\n\nREGISTER_OP(\"MonolithFusedReduceSumAndSplitGradient\")\n    .Input(\"id_indices: int64\")\n    .Input(\"grads: M * float\")\n    .Output(\"output_grad: float\")\n    .Attr(\"M: int\")\n    .Attr(\"split_dims: list(int)\")\n    .SetShapeFn(FusedGradientReduceShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithReduceSum\").Device(DEVICE_CPU),\n                        ReduceSumOp);\nREGISTER_KERNEL_BUILDER(Name(\"MonolithReduceMean\").Device(DEVICE_CPU),\n                        ReduceMeanOp);\nREGISTER_KERNEL_BUILDER(Name(\"MonolithReduceSquareNorm\").Device(DEVICE_CPU),\n                        ReduceSquareNormOp);\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithFusedReduceSumAndSplit\").Device(DEVICE_CPU),\n    ReduceSumAndSplitOp);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithReduceSumGradient\").Device(DEVICE_CPU),\n                        ReduceSumGradientOp);\nREGISTER_KERNEL_BUILDER(Name(\"MonolithReduceMeanGradient\").Device(DEVICE_CPU),\n                        ReduceMeanGradientOp);\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithReduceSquareNormGradient\").Device(DEVICE_CPU),\n    ReduceSquareNormGradientOp);\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithFusedReduceSumAndSplitGradient\").Device(DEVICE_CPU),\n    ReduceSumAndSplitGradientOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow"
  },
  {
    "path": "monolith/native_training/runtime/ops/reduce_op.cu.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#if GOOGLE_CUDA\n#define EIGEN_USE_GPU\n#include \"monolith/native_training/runtime/ops/alloc_utils.h\"\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/register_types.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/framework/types.h\"\n#include \"tensorflow/core/kernels/gpu_device_array.h\"\n#include \"tensorflow/core/kernels/gpu_device_array_gpu.h\"\n#include \"tensorflow/core/profiler/lib/traceme.h\"\n#include \"tensorflow/core/util/gpu_kernel_helper.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntypedef Eigen::GpuDevice GPUDevice;\n// To run mnay segment_sum ops on various input lengths and emb dims,\n// in one single GPU kernel. We define input group i:\n//  * indices with n_i length and s_i segments,\n//    s_i <= n_i as input_outer_dim_size;\n//    s_i <= output_outer_dim_size also;\n//    For example, [1,1,1,2,2,4] with n_i = 5, s_i = 3\n//    where output_outer_dim_size >= 4 >= s_i\n//  * values with n_i input_outer_dim_size and d_i dims\n// The total computation workload is sum n_i * d_i on i.\n// For all n_i, we stride with a fixed length k_n, so that\n// the same stride can have chance to reduce in local thread.\n// The total gpu workload is now the sum on i of\n//  [(n_i // k_n) + 1] * d_i\ntemplate <typename T, typename Index, int OuterDimTileSize>\n__global__ void FusedSortedSegmentSumCustomKernel(\n    GpuDeviceArrayStruct<Index> input_outer_dim_sizes_data,\n    GpuDeviceArrayStruct<Index> inner_dim_sizes_data,\n    GpuDeviceArrayStruct<Index> output_outer_dim_sizes_data,\n    GpuDeviceArrayStruct<const Index*> segment_idss_data,  // __restrict__\n    GpuDeviceArrayStruct<const T*> inputs_data,            // __restrict__\n    GpuDeviceArrayStruct<T*> outputs_data,                 // __restrict__\n    GpuDeviceArrayStruct<Index> stripe_offsets_data,\n    const Index total_stripe_count) {\n  Index* input_outer_dim_sizes =\n      GetGpuDeviceArrayOnDevice(&input_outer_dim_sizes_data);\n  Index* inner_dim_sizes = GetGpuDeviceArrayOnDevice(&inner_dim_sizes_data);\n  Index* output_outer_dim_sizes =\n      GetGpuDeviceArrayOnDevice(&output_outer_dim_sizes_data);\n\n  const Index* __restrict__* segment_idss =\n      GetGpuDeviceArrayOnDevice(&segment_idss_data);\n  const T* __restrict__* inputs = GetGpuDeviceArrayOnDevice(&inputs_data);\n  T* __restrict__* outputs = GetGpuDeviceArrayOnDevice(&outputs_data);\n  Index* stripe_offsets = GetGpuDeviceArrayOnDevice(&stripe_offsets_data);\n\n  // if using shared memory\n  // Ref:\n  // https://github.com/tensorflow/tensorflow/blob/v2.4.0/tensorflow/core/kernels/split_lib_gpu.cu.cc#L124\n  GPU_DYNAMIC_SHARED_MEM_DECL(sizeof(Index), unsigned char, smem);\n  Index N = input_outer_dim_sizes_data.size;\n  Index* ptr = reinterpret_cast<Index*>(smem);\n  Index* smem_input_outer_dim_sizes = ptr;\n  ptr += N;\n  Index* smem_inner_dim_sizes = ptr;\n  ptr += N;\n  Index* smem_output_outer_dim_sizes = ptr;\n  ptr += N;\n  Index* smem_stripe_offsets = ptr;\n  for (int x = threadIdx.x; x < N; x += blockDim.x) {\n    smem_input_outer_dim_sizes[x] = input_outer_dim_sizes[x];\n    smem_inner_dim_sizes[x] = inner_dim_sizes[x];\n    smem_output_outer_dim_sizes[x] = output_outer_dim_sizes[x];\n  }\n  for (int x = threadIdx.x; x < N + 1 /*stripe_offsets_data.size*/;\n       x += blockDim.x) {\n    smem_stripe_offsets[x] = stripe_offsets[x];\n  }\n  __syncthreads();\n  stripe_offsets = smem_stripe_offsets;\n  input_outer_dim_sizes = smem_input_outer_dim_sizes;\n  inner_dim_sizes = smem_inner_dim_sizes;\n  output_outer_dim_sizes = smem_output_outer_dim_sizes;\n\n  Index i = 0;\n  for (Index stripe_index : GpuGridRangeX(total_stripe_count)) {\n    // Determine the abstract computation unit amd local_stripe_index\n    while (stripe_offsets[i + 1] <= stripe_index) ++i;\n    Index local_stripe_index = stripe_index - stripe_offsets[i];\n\n    auto input_outer_dim_size = input_outer_dim_sizes[i];\n    auto inner_dim_size = inner_dim_sizes[i];\n    auto output_outer_dim_size = output_outer_dim_sizes[i];\n    if (input_outer_dim_size == 0 || inner_dim_size == 0 ||\n        output_outer_dim_size == 0)\n      continue;\n    auto segment_ids = segment_idss[i];\n    auto input = inputs[i];\n    auto output = outputs[i];\n\n    // Start computation: segment sum\n    const Index segment_offset = local_stripe_index % inner_dim_size;\n    const Index input_outer_dim_index_base =\n        local_stripe_index / inner_dim_size * Index(OuterDimTileSize);\n\n    T sum = T(0);\n    Index first_segment_id = segment_ids[input_outer_dim_index_base];\n    Index last_output_segment_id = output_outer_dim_size;\n\n    const Index actual_stripe_height =\n        min(Index(OuterDimTileSize),\n            input_outer_dim_size - input_outer_dim_index_base);\n    // #pragma unroll\n    for (Index j = 0; j < actual_stripe_height; j++) {\n      Index current_output_segment_id =\n          segment_ids[input_outer_dim_index_base + j];\n      // Decide whether to write result to global memory.\n      // Result is only written to global memory if we move\n      // to another segment. Otherwise we can keep accumulating\n      // locally.\n      if (current_output_segment_id > last_output_segment_id) {\n        const Index output_index =\n            last_output_segment_id * inner_dim_size + segment_offset;\n        // decide whether to write result to global memory using atomic\n        // operations\n        if (last_output_segment_id == first_segment_id) {\n          GpuAtomicAdd(output + output_index, sum);\n        } else {\n          *(output + output_index) = sum;\n        }\n        sum = T(0);\n      }\n      sum += ldg(input + (input_outer_dim_index_base + j) * inner_dim_size +\n                 segment_offset);\n      last_output_segment_id = current_output_segment_id;\n    }\n    // For the last result in a strip, always write using atomic operations\n    // due to possible race conditions with threads computing\n    // the following strip.\n    const Index output_index =\n        last_output_segment_id * inner_dim_size + segment_offset;\n    GpuAtomicAdd(output + output_index, sum);\n  }\n}\n\n// Returns true if the three tensors have valid number of elements\n// If shape_input has 0 elements, then we need to have indices and updates with\n// exactly 0 elements too, otherwise we should error. If indices has 0 elements\n// then updates should also have 0 elements, otherwise we should error.\nbool ValidEmptyOutputShape(int64 num_inputs, int64 num_indices,\n                           int64 num_updates) {\n  if (num_indices == 0 && num_updates == 0) {\n    return true;  // regardless of num_inputs ?= 0, covers both cases\n  }\n  // now we want all 3 tensors to have values\n  return (num_inputs != 0 && num_indices != 0 && num_updates != 0);\n}\n\ntemplate <typename T, typename Index>\nclass FusedSegmentSumGPU : public OpKernel {\n public:\n  explicit FusedSegmentSumGPU(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"N\", &N_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    GPUDevice gpu_device = ctx->eigen_device<GPUDevice>();\n    const int OuterDimTileSize = 8;\n    Index stripe_offset = 0;  // max as total_stripe_count\n    GpuDeviceArrayOnHost<Index> stripe_offsets(ctx, N_ + 1);\n    OP_REQUIRES_OK(ctx, stripe_offsets.Init());\n\n    OpInputList indices_list;\n    OP_REQUIRES_OK(ctx, ctx->input_list(\"indices\", &indices_list));\n    OpInputList updates_list;\n    OP_REQUIRES_OK(ctx, ctx->input_list(\"updates\", &updates_list));\n    OpInputList shape_list;\n    OP_REQUIRES_OK(ctx, ctx->input_list(\"shape\", &shape_list));\n    OpOutputList outputs;\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"outputs\", &outputs));\n\n    GpuDeviceArrayOnHost<const Index*> indices_ptrs(ctx, N_);\n    // TODO(peng): concat then memcpy if necessary\n    OP_REQUIRES_OK(ctx, indices_ptrs.Init());\n    GpuDeviceArrayOnHost<const T*> updates_ptrs(ctx, N_);\n    OP_REQUIRES_OK(ctx, updates_ptrs.Init());\n    GpuDeviceArrayOnHost<T*> output_ptrs(ctx, N_);\n    OP_REQUIRES_OK(ctx, output_ptrs.Init());\n\n    GpuDeviceArrayOnHost<Index> input_outer_dim_sizes(ctx, N_);\n    OP_REQUIRES_OK(ctx, input_outer_dim_sizes.Init());\n    GpuDeviceArrayOnHost<Index> inner_dim_sizes(ctx, N_);\n    OP_REQUIRES_OK(ctx, inner_dim_sizes.Init());\n    GpuDeviceArrayOnHost<Index> output_outer_dim_sizes(ctx, N_);\n    OP_REQUIRES_OK(ctx, output_outer_dim_sizes.Init());\n    // Shared memory used by four <Index> typed Device array.\n    int smem_usage = sizeof(Index) * (4 * N_ + 1);\n\n    for (int i = 0; i < N_; ++i) {\n      const Tensor& indices = indices_list[i];\n      const Tensor& updates = updates_list[i];\n      const Tensor& shape_input = shape_list[i];\n\n      OP_REQUIRES(ctx, indices.shape().dims() >= 1,\n                  errors::InvalidArgument(\n                      \"Indices shape must have rank at least one. Found:\",\n                      indices.shape().DebugString()));\n      OP_REQUIRES(ctx, updates.shape().dims() >= 1,\n                  errors::InvalidArgument(\n                      \"Updates shape must have rank at least one. Found:\",\n                      updates.shape().DebugString()));\n\n      auto vec = shape_input.flat<Index>();\n      TensorShape output_shape;\n      OP_REQUIRES_OK(ctx, TensorShapeUtils::MakeShape(vec.data(), vec.size(),\n                                                      &output_shape));\n\n      OP_REQUIRES(ctx,\n                  ValidEmptyOutputShape(shape_input.NumElements(),\n                                        indices.shape().num_elements(),\n                                        updates.shape().num_elements()),\n                  errors::InvalidArgument(\n                      \"Indices and updates specified for empty output shape\"));\n\n      OP_REQUIRES(ctx, shape_input.dims() == 1,\n                  errors::InvalidArgument(\"Shape must be a vector\"));\n\n      //\n      Index input_total_size = updates.NumElements();\n      auto input_shape = updates.shape();\n      Index input_outer_dim_size = input_shape.dim_size(0);\n      Index inner_dim_size = 1;\n      for (int j = 1; j < input_shape.dims(); ++j)\n        inner_dim_size *= input_shape.dim_size(j);\n      input_outer_dim_sizes.Set(i, input_outer_dim_size);\n      inner_dim_sizes.Set(i, inner_dim_size);\n      output_outer_dim_sizes.Set(i, output_shape.dim_size(0));\n\n      stripe_offsets.Set(i, stripe_offset);\n      Index input_outer_dim_num_stripe =\n          Eigen::divup(input_outer_dim_size, Index(OuterDimTileSize));\n      stripe_offset += input_outer_dim_num_stripe * inner_dim_size;\n\n      //\n      Tensor* out;\n      OP_REQUIRES_OK(ctx, outputs.allocate(i, output_shape, &out));\n      gpu_device.memset(out->flat<T>().data(), T(0),\n                        sizeof(T) * out->NumElements());\n      output_ptrs.Set(i, out->flat<T>().data());\n      updates_ptrs.Set(i, updates.flat<T>().data());\n      indices_ptrs.Set(i, indices.flat<Index>().data());\n    }\n    const Index total_stripe_count = stripe_offset;\n    stripe_offsets.Set(N_, stripe_offset);\n    OP_REQUIRES_OK(ctx, stripe_offsets.Finalize());\n\n    OP_REQUIRES_OK(ctx, input_outer_dim_sizes.Finalize());\n    OP_REQUIRES_OK(ctx, inner_dim_sizes.Finalize());\n    OP_REQUIRES_OK(ctx, output_outer_dim_sizes.Finalize());\n\n    OP_REQUIRES_OK(ctx, indices_ptrs.Finalize());\n    OP_REQUIRES_OK(ctx, updates_ptrs.Finalize());\n    OP_REQUIRES_OK(ctx, output_ptrs.Finalize());\n\n    auto config = GetGpuLaunchConfig(total_stripe_count, gpu_device);\n    GpuLaunchKernel(\n        FusedSortedSegmentSumCustomKernel<T, Index, OuterDimTileSize>,\n        config.block_count, config.thread_per_block,\n        /*shared_memory_size_bytes=*/smem_usage, gpu_device.stream(),\n        input_outer_dim_sizes.data(), inner_dim_sizes.data(),\n        output_outer_dim_sizes.data(), indices_ptrs.data(), updates_ptrs.data(),\n        output_ptrs.data(), stripe_offsets.data(), total_stripe_count);\n  }\n\n private:\n  int N_;\n};\n\n// a struct that stores a mapping from the current column to the base address in\n// the tensor after split\ntemplate <typename T>\nstruct __align__(16) ColumnInfo {\n  T* __restrict__ base_address;  // this includes the current slice index\n  int slice_len;\n};\n\ntemplate <typename T>\nstruct __align__(16) TableInfo {\n  int c_offset;\n  int r_offset;\n  T* __restrict__ embs;\n};\n\n// given the row and column index in output,\n// sum up the corresponding column of the rows that need to be reduced\ntemplate <typename T>\n__device__ __forceinline__ void fused_reduce_and_split(\n    int r_group_id, const int* __restrict__ row_prefix, ColumnInfo<T> col_info,\n    const float* __restrict__ emb, int emb_len) {\n  int row_end = ldg(row_prefix + r_group_id + 1);\n  T sum = T(0);\n  for (int r = ldg(row_prefix + r_group_id); r < row_end; r++)\n    sum += ldg(emb + r * emb_len);\n  col_info.base_address[r_group_id * col_info.slice_len] = sum;\n}\n\n// initialize shared memory so that\n// first (N + 1) * sizeof(TableInfo<T>) byte is the array of TableInfo<T>\n// and the following (N + 1) * sizeof(int) byte is the array of int (table\n// splits)\ntemplate <typename T>\n__device__ __forceinline__ const int* init_shared_mem(\n    GpuDeviceArrayStruct<int, 0>& _table_splits,          // N + 1\n    GpuDeviceArrayStruct<TableInfo<T>, 0>& _table_infos,  // N + 1\n    int* shared_mem) {\n  int table_info_sz = _table_splits.size * (sizeof(TableInfo<T>) / sizeof(int));\n  auto s_table_infos = shared_mem;\n  auto s_table_splits = shared_mem + table_info_sz;\n  auto g_table_splits = GetGpuDeviceArrayOnDevice(&_table_splits);\n  auto g_table_infos =\n      reinterpret_cast<int*>(GetGpuDeviceArrayOnDevice(&_table_infos));\n  int total_shared_sz = table_info_sz + _table_splits.size;\n  for (int i = threadIdx.x; i < total_shared_sz; i += blockDim.x) {\n    if (i < table_info_sz) {\n      s_table_infos[i] = g_table_infos[i];\n    } else {\n      int j = i - table_info_sz;\n      s_table_splits[j] = g_table_splits[j];\n    }\n  }\n  __syncthreads();\n  return s_table_splits;\n}\n\n// GpuDeviceArrayStruct<int, 0> guarantees stroing address in global memory\n// so __ldg can works properly\n// this kernel works the best when the number of rows to be reduced is\n// relatively even across the batch dimension, so that each threads' workload is\n// about the same\ntemplate <typename T>\n__global__ void FusedReduceSumAndSplitKernel(\n    GpuDeviceArrayStruct<int, 0> _table_splits,                // N + 1\n    GpuDeviceArrayStruct<TableInfo<const T>, 0> _table_infos,  // N + 1\n    const ColumnInfo<T>* __restrict__ col_infos,\n    const int* __restrict__ row_splits,\n    int total  // =total number of elements in output (tables * n_rows after\n               // reduction * emb_len)\n) {\n  extern __shared__ int shared_mem[];\n  auto table_infos = reinterpret_cast<const TableInfo<const T>*>(shared_mem);\n  auto table_splits = init_shared_mem(_table_splits, _table_infos, shared_mem);\n\n  int table_idx = 1;\n  for (int outer_tid = threadIdx.x + blockIdx.x * blockDim.x; outer_tid < total;\n       outer_tid += blockDim.x * gridDim.x) {\n    while (outer_tid >= table_splits[table_idx]) table_idx++;\n    table_idx -= 1;\n    int idx = outer_tid - table_splits[table_idx];\n    auto table = table_infos[table_idx];\n    auto next_table = table_infos[table_idx + 1];\n    int emb_len = next_table.c_offset - table.c_offset;\n    int row_idx = idx / emb_len;\n    int col_idx = idx % emb_len;\n    fused_reduce_and_split(row_idx, row_splits + table.r_offset,\n                           col_infos[table.c_offset + col_idx],  // 16-byte load\n                           table.embs + col_idx, emb_len);\n  }\n}\n\ntemplate <typename ValueType, int MaxInlineValues = 8>\nconst ValueType* GetGpuDeviceArrayOnHost(\n    const GpuDeviceArrayStruct<ValueType, MaxInlineValues>* data) {\n  if (data->size <= MaxInlineValues) {\n    return data->inline_values;\n  } else {\n    return data->out_of_line_values;\n  }\n}\n\ntemplate <typename T>\nclass FusedReduceAndSplitGPU : public OpKernel {\n public:\n  explicit FusedReduceAndSplitGPU(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"N\", &N_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"slice_dims\", &slice_dims_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"row_split_splits\", &row_split_splits_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    profiler::TraceMe activity(\n        []() { return \"FusedReduceAndSplitGPUPreprocessing\"; });\n    const auto& gpu_device = ctx->eigen_gpu_device();\n    OpInputList updates_list;\n    OpOutputList outputs;\n    OP_REQUIRES_OK(ctx, ctx->input_list(\"updates\", &updates_list));\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"outputs\", &outputs));\n\n    GpuDeviceArrayOnHost<int, 0> table_splits(ctx, N_ + 1);\n    GpuDeviceArrayOnHost<TableInfo<const T>, 0> table_infos(ctx, N_ + 1);\n    OP_REQUIRES_OK(ctx, table_splits.Init());\n    OP_REQUIRES_OK(ctx, table_infos.Init());\n\n    // precalculate the size we need for storing row splits and column infos\n    int col_info_size = 0;\n    // number of rows after reduction = batch_size + 1\n    int batch_size = row_split_splits_[1] - row_split_splits_[0] - 1;\n    FusedAlignedOutputAllocator<EIGEN_MAX_ALIGN_BYTES / sizeof(T)> fao_alloc(\n        ctx);\n    for (int i = 0; i < N_; i++) {\n      const Tensor& updates = updates_list[i];\n      table_splits.Set(i, fao_alloc.get_unaligned_total());\n      table_infos.Set(\n          i, {col_info_size, row_split_splits_[i], updates.flat<T>().data()});\n      int emb_len = updates.dim_size(1);\n      // number of rows after reduction * embedding length\n      fao_alloc.add_slice(batch_size * emb_len);\n      col_info_size += emb_len;\n    }\n    table_splits.Set(N_, fao_alloc.get_unaligned_total());\n    table_infos.Set(N_, {col_info_size, row_split_splits_[N_], nullptr});\n    OP_REQUIRES_OK(ctx, table_splits.Finalize());\n    OP_REQUIRES_OK(ctx, table_infos.Finalize());\n\n    // since these arrays are used as the backing storage,\n    // we don't allow them to store values inline\n    // because that will be on the host's stack and will not be\n    // device-accessible\n    GpuDeviceArrayOnHost<ColumnInfo<T>, 0> col_infos(ctx, col_info_size);\n    OP_REQUIRES_OK(ctx, col_infos.Init());\n\n    // hide some latency by the h2ds above\n    fao_alloc.allocate(outputs.expected_output_dtype(0));\n\n    col_info_size = 0;\n    for (int i = 0; i < slice_dims_.size(); i++) {\n      int slice_len = slice_dims_[i];\n      Tensor out = fao_alloc.get_slice({batch_size, slice_len});\n      auto data = out.flat<T>().data();\n      outputs.set(i, std::move(out));\n      for (int k = 0; k < slice_len; k++) {\n        col_infos.Set(col_info_size++, {data + k, slice_len});\n      }\n    }\n    OP_REQUIRES_OK(ctx, col_infos.Finalize());\n    auto smem_sz = (sizeof(TableInfo<const T>) + sizeof(int)) * (N_ + 1);\n    auto config =\n        GetGpuLaunchConfig(fao_alloc.get_unaligned_total(), gpu_device,\n                           FusedReduceSumAndSplitKernel<T>, smem_sz, 0);\n    auto grid_offset = 24;\n    char* ptr = std::getenv(\"MONOLITH_GT_OVERSUB_SM\");\n    if (ptr) grid_offset = std::atoi(ptr);\n    grid_offset += 2;\n    GpuLaunchKernel(FusedReduceSumAndSplitKernel<T>, config.block_count - grid_offset,\n                    config.thread_per_block, smem_sz, gpu_device.stream(),\n                    table_splits.data(), table_infos.data(),\n                    GetGpuDeviceArrayOnHost(&col_infos.data()),\n                    ctx->input(0).vec<int32>().data(),  // row_splits base ptr\n                    fao_alloc.get_unaligned_total());\n  }\n\n private:\n  int N_;\n  std::vector<int> slice_dims_, row_split_splits_;\n};\n\ntemplate <typename T>\n__device__ __forceinline__ void fused_reduce_and_split_grad(\n    int r_group_id, const int* __restrict__ row_splits, int count,\n    ColumnInfo<const T> col_info, float* __restrict__ emb, int emb_len) {\n  // from https://en.cppreference.com/w/cpp/algorithm/upper_bound\n  int it, step, first = 0;\n  while (count > 0) {\n    it = first;\n    step = count / 2;\n    it += step;\n\n    // ldg will not be helpful here due to irregular pattern\n    if (!(r_group_id < row_splits[it])) {\n      first = ++it;\n      count -= step + 1;\n    } else {\n      count = step;\n    }\n  }\n  // find the largest first element in row_splits >= r_group_id\n  emb[r_group_id * emb_len] =\n      col_info.base_address[(first - 1) * col_info.slice_len];\n}\n\n// workload of threads are perfectly balanced: (1 load + 1 store) * (total /\n// num_threads) the bottleneck of this kernel is the binary search in the device\n// function above\ntemplate <typename T>\n__global__ void FusedReduceSumAndSplitKernelGrad(\n    GpuDeviceArrayStruct<int, 0> _table_splits,          // N + 1\n    GpuDeviceArrayStruct<TableInfo<T>, 0> _table_infos,  // N + 1\n    const ColumnInfo<const T>* __restrict__ col_infos,\n    const int* __restrict__ row_splits,\n    int total  // =total number of elements in output (tables * n_rows before\n               // reduction * emb_len)\n) {\n  extern __shared__ int shared_mem[];\n  auto table_infos = reinterpret_cast<const TableInfo<T>*>(shared_mem);\n  auto table_splits = init_shared_mem(_table_splits, _table_infos, shared_mem);\n\n  int table_idx = 1;\n  for (int outer_tid = threadIdx.x + blockIdx.x * blockDim.x; outer_tid < total;\n       outer_tid += blockDim.x * gridDim.x) {\n    while (outer_tid >= table_splits[table_idx]) table_idx++;\n    table_idx -= 1;\n    int idx = outer_tid - table_splits[table_idx];\n    auto table = table_infos[table_idx];\n    auto next_table = table_infos[table_idx + 1];\n    int emb_len = next_table.c_offset - table.c_offset;\n    int row_len = next_table.r_offset - table.r_offset;\n    int row_idx = idx / emb_len;\n    int col_idx = idx % emb_len;\n    fused_reduce_and_split_grad(\n        row_idx, row_splits + table.r_offset, row_len,\n        col_infos[table.c_offset + col_idx],  // 16-byte load\n        table.embs + col_idx, emb_len);\n  }\n}\n\ntemplate <typename T>\nclass FusedReduceAndSplitGPUGrad : public OpKernel {\n public:\n  explicit FusedReduceAndSplitGPUGrad(OpKernelConstruction* ctx)\n      : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"slice_dims\", &slice_dims_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"row_split_splits\", &row_split_splits_));\n    N_ = row_split_splits_.size() - 1;  // =num_tables\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    profiler::TraceMe activity(\n        []() { return \"FusedReduceAndSplitGPUGradPreprocessing\"; });\n    const auto& gpu_device = ctx->eigen_gpu_device();\n    OpInputList slice_list, updates_list;\n    OpOutputList outputs;\n    OP_REQUIRES_OK(ctx, ctx->input_list(\"updates\", &updates_list));\n    OP_REQUIRES_OK(ctx, ctx->input_list(\"slices\", &slice_list));\n    OP_REQUIRES_OK(ctx, ctx->output_list(\"outputs\", &outputs));\n\n    GpuDeviceArrayOnHost<int, 0> table_splits(ctx, N_ + 1);\n    GpuDeviceArrayOnHost<TableInfo<T>, 0> table_infos(ctx, N_ + 1);\n    OP_REQUIRES_OK(ctx, table_splits.Init());\n    OP_REQUIRES_OK(ctx, table_infos.Init());\n\n    FusedAlignedOutputAllocator<EIGEN_MAX_ALIGN_BYTES / sizeof(T)> fao_alloc(\n        ctx);\n    int col_info_size = 0;\n    for (int i = 0; i < N_; i++) {\n      // number of rows before reduction\n      int num_rows = updates_list[i].dim_size(0);\n      int emb_len = updates_list[i].dim_size(1);\n\n      table_splits.Set(i, fao_alloc.get_unaligned_total());\n      fao_alloc.add_slice(num_rows * emb_len);\n      col_info_size += emb_len;\n    }\n    // check for overflow\n    OP_REQUIRES(ctx, fao_alloc.get_unaligned_total() <= INT_MAX,\n                errors::InvalidArgument(\"There are too many elements to be \"\n                                        \"processed by fused reduce and split\"));\n    table_splits.Set(N_, fao_alloc.get_unaligned_total());\n    OP_REQUIRES_OK(ctx, table_splits.Finalize());\n\n    // col_info stores the base src address of each slice\n    GpuDeviceArrayOnHost<ColumnInfo<const T>, 0> col_infos(ctx, col_info_size);\n    OP_REQUIRES_OK(ctx, col_infos.Init());\n\n    col_info_size = 0;\n    for (int i = 0; i < slice_dims_.size(); i++) {\n      int slice_len = slice_dims_[i];\n      auto data = slice_list[i].flat<T>().data();\n      for (int j = 0; j < slice_len; j++) {\n        col_infos.Set(col_info_size++, {data + j, slice_len});\n      }\n    }\n    OP_REQUIRES_OK(ctx, col_infos.Finalize());\n\n    // hide some latency by the h2ds above\n    fao_alloc.allocate(outputs.expected_output_dtype(0));\n\n    col_info_size = 0;\n    for (int i = 0; i < N_; i++) {\n      int num_rows = updates_list[i].dim_size(0);\n      int emb_len = updates_list[i].dim_size(1);\n      Tensor out = fao_alloc.get_slice({num_rows, emb_len});\n      table_infos.Set(\n          i, {col_info_size, row_split_splits_[i], out.flat<T>().data()});\n      outputs.set(i, std::move(out));\n      col_info_size += emb_len;\n    }\n    table_infos.Set(N_, {col_info_size, row_split_splits_[N_], nullptr});\n    OP_REQUIRES_OK(ctx, table_infos.Finalize());\n\n    auto smem_sz = (sizeof(TableInfo<const T>) + sizeof(int)) * (N_ + 1);\n    auto config =\n        GetGpuLaunchConfig(fao_alloc.get_unaligned_total(), gpu_device,\n                           FusedReduceSumAndSplitKernelGrad<T>, smem_sz, 0);\n    GpuLaunchKernel(FusedReduceSumAndSplitKernelGrad<T>, config.block_count,\n                    config.thread_per_block, smem_sz, gpu_device.stream(),\n                    table_splits.data(), table_infos.data(),\n                    GetGpuDeviceArrayOnHost(&col_infos.data()),\n                    ctx->input(0).vec<int32>().data(),\n                    fao_alloc.get_unaligned_total());\n  }\n\n private:\n  int N_;\n  std::vector<int> slice_dims_;  // fused array of slice dimensions\n  std::vector<int> row_split_splits_;\n};\n\n#define REGISTER_FUSED_REDUCE_AND_SPLIT_GRAD(type)                   \\\n  REGISTER_KERNEL_BUILDER(Name(\"MonolithFusedReduceAndSplitGPUGrad\") \\\n                              .Device(DEVICE_GPU)                    \\\n                              .TypeConstraint<type>(\"T\"),            \\\n                          FusedReduceAndSplitGPUGrad<type>)\nTF_CALL_float(REGISTER_FUSED_REDUCE_AND_SPLIT_GRAD);\n\nREGISTER_OP(\"MonolithFusedReduceAndSplitGPUGrad\")\n    .Input(\"splits: int32\")   // input of the forward op\n    .Input(\"updates: M * T\")  // input of the forward op, needed to do shape\n                              // inference\n    .Input(\"slices: N * T\")   // output of the forward op\n    .Output(\"outputs: M * T\")\n    .Attr(\"slice_dims: list(int)\")        // from the forward op\n    .Attr(\"row_split_splits: list(int)\")  // from the forward op\n    .Attr(\"T: type\")\n    .Attr(\"N: int\")\n    .Attr(\"M: int\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int N, M;\n      std::vector<int> slice_dims, row_split_splits;\n\n      TF_RETURN_IF_ERROR(c->GetAttr(\"N\", &N));\n      TF_RETURN_IF_ERROR(c->GetAttr(\"M\", &M));\n      TF_RETURN_IF_ERROR(c->GetAttr(\"slice_dims\", &slice_dims));\n      TF_RETURN_IF_ERROR(c->GetAttr(\"row_split_splits\", &row_split_splits));\n\n      // simple sanity checks\n      if (slice_dims.size() != N)\n        return errors::InvalidArgument(\n            \"len(slice_dims) must equal to the number of input slices\");\n      if (row_split_splits.size() != M + 1)\n        return errors::InvalidArgument(\n            \"len(row_split_splits) must equal to M + 1\");\n\n      for (int i = 0; i < M; i++) {\n        c->set_output(i, c->input(1 + i));\n      }\n      return Status::OK();\n    });\n\n#define REGISTER_FUSED_REDUCE_AND_SPLIT(type)                    \\\n  REGISTER_KERNEL_BUILDER(Name(\"MonolithFusedReduceAndSplitGPU\") \\\n                              .Device(DEVICE_GPU)                \\\n                              .TypeConstraint<type>(\"T\"),        \\\n                          FusedReduceAndSplitGPU<type>)\nTF_CALL_float(REGISTER_FUSED_REDUCE_AND_SPLIT);\n\nREGISTER_OP(\"MonolithFusedReduceAndSplitGPU\")\n    .Input(\"splits: int32\")\n    .Input(\"updates: N * T\")\n    .Output(\"outputs: num_slices * T\")\n    .Attr(\"num_slices: int >= 1\")\n    .Attr(\"slice_dims: list(int)\")\n    .Attr(\"row_split_splits: list(int)\")\n    .Attr(\"T: type\")\n    .Attr(\"N: int\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int N;\n      std::vector<int> slice_dims, row_split_splits;\n\n      TF_RETURN_IF_ERROR(c->GetAttr(\"N\", &N));\n      TF_RETURN_IF_ERROR(c->GetAttr(\"slice_dims\", &slice_dims));\n      TF_RETURN_IF_ERROR(c->GetAttr(\"row_split_splits\", &row_split_splits));\n\n      // simple sanity checks\n      int num_outputs = c->num_outputs();\n      if (slice_dims.size() != num_outputs)\n        return errors::InvalidArgument(\n            \"len(slice_dims) must equal to num_slices\");\n\n      int batch_size = row_split_splits[1] - row_split_splits[0] - 1;\n      for (int i = 0; i < num_outputs; i++) {\n        auto output_shape = c->MakeShape({batch_size, slice_dims[i]});\n        c->set_output(i, output_shape);\n      }\n      return Status::OK();\n    });\n\n#define REGISTER_FUSED_SCATTER_ND_KERNEL_INDEX(type, index_type)      \\\n  REGISTER_KERNEL_BUILDER(Name(\"MonolithFusedSegmentSum\")             \\\n                              .Device(DEVICE_GPU)                     \\\n                              .TypeConstraint<type>(\"T\")              \\\n                              .TypeConstraint<index_type>(\"Tindices\") \\\n                              .HostMemory(\"shape\"),                   \\\n                          FusedSegmentSumGPU<type, index_type>)\n\n#define REGISTER_FUSED_SCATTER_ND_KERNEL(type)         \\\n  REGISTER_FUSED_SCATTER_ND_KERNEL_INDEX(type, int32); \\\n  REGISTER_FUSED_SCATTER_ND_KERNEL_INDEX(type, int64);\n\nTF_CALL_float(REGISTER_FUSED_SCATTER_ND_KERNEL);\n// TF_CALL_GPU_NUMBER_TYPES(REGISTER_FUSED_SCATTER_ND_KERNEL);\n\n#undef REGISTER_FUSED_SCATTER_ND_KERNEL\n#undef REGISTER_FUSED_SCATTER_ND_KERNEL_INDEX\n\nREGISTER_OP(\"MonolithFusedSegmentSum\")\n    .Input(\"indices: N * Tindices\")\n    .Input(\"updates: N * T\")\n    .Input(\"shape: N * Tindices\")\n    .Output(\"outputs: N * T\")\n    .Attr(\"T: type\")\n    .Attr(\"Tindices: {int32, int64}\")\n    .Attr(\"N: int\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int N;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"N\", &N));\n      for (int i = N - 1; i >= 0; --i) {\n        shape_inference::ShapeHandle indices_shape;\n        TF_RETURN_IF_ERROR(c->WithRankAtLeast(c->input(i), 1, &indices_shape));\n        shape_inference::ShapeHandle updates_shape;\n        TF_RETURN_IF_ERROR(\n            c->WithRankAtLeast(c->input(N + i), 1, &updates_shape));\n        shape_inference::ShapeHandle output_shape;\n        TF_RETURN_IF_ERROR(\n            c->MakeShapeFromShapeTensor(2 * N + i, &output_shape));\n        shape_inference::ShapeHandle\n            expanded_indices_shape;  // mimic expand_dims(indices, -1)\n        TF_RETURN_IF_ERROR(c->Concatenate(indices_shape, c->Vector(1),\n                                          &expanded_indices_shape));\n        TF_RETURN_IF_ERROR(shape_inference::ScatterNdShapeHelper(\n            c, expanded_indices_shape, updates_shape,\n            output_shape));  // set shape to output 0\n        if (c->input_handle_shapes_and_types(0) == nullptr &&\n            c->num_outputs() > 0) {\n          c->set_output(i, c->output(0));\n        }\n      }\n      return Status::OK();\n    });\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // GOOGLE_CUDA\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/remote_predict_op.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n// forked from:\n//   https://github.com/tensorflow/serving/blob/2.4.0/tensorflow_serving/experimental/tensorflow/ops/remote_predict/kernels/remote_predict_op_kernel.h\n//   https://github.com/tensorflow/serving/blob/2.4.0/tensorflow_serving/experimental/tensorflow/ops/remote_predict/kernels/remote_predict_op_kernel.cc\n//   https://github.com/tensorflow/serving/blob/2.4.0/tensorflow_serving/experimental/tensorflow/ops/remote_predict/ops/remote_predict_op.cc\n// with:\n//   \"#ifndef\n//   TENSORFLOW_SERVING_EXPERIMENTAL_TENSORFLOW_OPS_REMOTE_PREDICT_KERNELS_REMOTE_PREDICT_OP_KERNEL_H_\n//   ...\" removed\n\n#include \"absl/status/status.h\"\n#include \"absl/time/time.h\"\n#include \"glog/logging.h\"\n#include \"google/protobuf/map.h\"\n#include \"google/protobuf/wrappers.pb.h\"\n#include \"monolith/native_training/runtime/common/metrics.h\"\n#include \"monolith/native_training/runtime/ops/agent_heartbeat.h\"\n#include \"tensorflow/core/framework/common_shape_fns.h\"\n#include \"tensorflow/core/framework/op.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/register_types.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/tensor.pb.h\"\n#include \"tensorflow/core/lib/core/threadpool.h\"\n#include \"tensorflow/core/lib/gtl/cleanup.h\"\n#include \"tensorflow/core/profiler/lib/traceme.h\"\n#include \"tensorflow/core/protobuf/named_tensor.pb.h\"\n#include \"tensorflow_serving/apis/model.pb.h\"\n#include \"tensorflow_serving/apis/predict.pb.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\ntypedef google::protobuf::Map<tensorflow::string, tensorflow::TensorProto>\n    AliasTensorMap;\nusing ::tensorflow::serving::PredictRequest;\nusing ::tensorflow::serving::PredictResponse;\n\n// Remote Predict Op kernel implementation class templated on different\n// PredictionServiceStubTypes.\ntemplate <typename PredictionServiceStubType, typename AgentHeartbeat>\nclass RemotePredictOp : public AsyncOpKernel {\n public:\n  explicit RemotePredictOp(OpKernelConstruction *context)\n      : AsyncOpKernel(context) {\n    OP_REQUIRES_OK(context, context->GetAttr(\"model_name\", &model_name_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"model_version\", &model_version_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"max_rpc_deadline_millis\",\n                                             &max_rpc_deadline_millis_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"fail_op_on_rpc_error\",\n                                             &fail_op_on_rpc_error_));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"signature_name\", &signature_name_));\n    OP_REQUIRES_OK(context,\n                   context->GetAttr(\"old_model_name\", &old_model_name_));\n    OP_REQUIRES_OK(context, context->GetAttr(\"task\", &task_));\n\n    if (AgentHeartbeat::GetInstance().api_version() == 0 &&\n        old_model_name_.size() > 0) {\n      req_model_name_ = old_model_name_;\n    } else {\n      req_model_name_ = model_name_;\n    }\n  }\n\n  void ComputeAsync(OpKernelContext *context, DoneCallback done) override {\n    auto activity =\n        std::make_shared<profiler::TraceMe>([this]() { return name(); });\n    auto remote_predict_op_latency_start = std::chrono::system_clock::now();\n    // Get the input tensor alias names.\n    const auto &input_tensor_aliases = context->input(0).flat<tstring>();\n\n    // Get the input tensors.\n    OpInputList input_tensors;\n    OP_REQUIRES_OK_ASYNC(\n        context, context->input_list(\"input_tensors\", &input_tensors), done);\n    // Get the output tensor alias names.\n    // Directly index to output_tensor_aliases by moving past all the input\n    // before it, including the input_tensor_aliases and input_tensors.\n    auto output_tensor_aliases =\n        context->input(1 + input_tensors.size()).flat<tstring>();\n\n    // Build the PredictRequest.\n    std::shared_ptr<PredictRequest> request(new PredictRequest);\n\n    request->mutable_model_spec()->set_name(req_model_name_);\n\n    request->mutable_model_spec()->set_signature_name(signature_name_);\n\n    if (model_version_ >= 0) {\n      request->mutable_model_spec()->mutable_version()->set_value(\n          model_version_);\n    }\n\n    AliasTensorMap &inputs = *request->mutable_inputs();\n    for (int i = 0; i < input_tensor_aliases.size(); ++i) {\n      tensorflow::TensorProto proto;\n      input_tensors[i].AsProtoField(&proto);\n      inputs[input_tensor_aliases(i)] = proto;\n    }\n\n    for (int i = 0; i < output_tensor_aliases.size(); ++i) {\n      request->add_output_filter(tensorflow::string(output_tensor_aliases(i)));\n    }\n\n    std::shared_ptr<PredictResponse> response(new PredictResponse());\n\n    std::shared_ptr<PredictionServiceStubType> prediction_service = nullptr;\n    if (AgentHeartbeat::GetInstance().api_version() == 0) {\n      if (old_model_name_.size() > 0) {\n        size_t pos = old_model_name_.find(\"_\");\n        std::string real_model_name = old_model_name_;\n        if (pos != std::string::npos) {\n          real_model_name.replace(pos, 1, \":\");\n        }\n        LOG_FIRST_N(INFO, 3) << \"GetPredictionService by old_model_name_:\"\n                             << real_model_name;\n        prediction_service =\n            AgentHeartbeat::GetInstance().GetPredictionService(real_model_name);\n      } else {\n        LOG_FIRST_N(INFO, 3) << \"GetPredictionService by task_:\" << task_;\n        prediction_service =\n            AgentHeartbeat::GetInstance().GetPredictionServiceByIdx(task_);\n      }\n    } else {\n      prediction_service =\n          AgentHeartbeat::GetInstance().GetPredictionService(model_name_);\n    }\n\n    OP_REQUIRES_ASYNC(\n        context, prediction_service != nullptr,\n        errors::Unavailable(\"No available remote servers. model_name=\",\n                            model_name_, \",signature_name=\", signature_name_),\n        done);\n\n    auto serving_latency_start = std::chrono::system_clock::now();\n    auto callback = [this, context, request, response, activity,\n                     output_tensor_aliases, done, serving_latency_start,\n                     remote_predict_op_latency_start, prediction_service](\n        const absl::Status &status, DoneCallback &&rpc_done) {\n      std::ostringstream tagkv;\n      tagkv << \"model_name=\" << model_name_\n            << \"|signature_name=\" << signature_name_;\n\n      auto serving_latency_end = std::chrono::system_clock::now();\n      std::chrono::duration<double> serving_latency_diff =\n          std::chrono::duration_cast<std::chrono::microseconds>(\n              serving_latency_end - serving_latency_start);\n      monolith::GetMetrics()->emit_timer(\n          \"serving_latency\", serving_latency_diff.count(), tagkv.str());\n      LOG_EVERY_N(INFO, 1000) << \"emit_timer serving_latency \" << tagkv.str();\n      PostProcessResponse(context, response.get(), status,\n                          fail_op_on_rpc_error_, output_tensor_aliases,\n                          std::forward<DoneCallback>(rpc_done));\n      auto remote_predict_op_latency_end = std::chrono::system_clock::now();\n      std::chrono::duration<double> remote_predict_op_latency_diff =\n          std::chrono::duration_cast<std::chrono::microseconds>(\n              remote_predict_op_latency_end - remote_predict_op_latency_start);\n      monolith::GetMetrics()->emit_timer(\"remote_predict_op_latency\",\n                                         remote_predict_op_latency_diff.count(),\n                                         tagkv.str());\n      monolith::GetMetrics()->emit_counter(\"remote_predict_op_throughput\", 1,\n                                           tagkv.str());\n      LOG_EVERY_N(INFO, 1000) << \"emit_timer remote_predict_op_latency \"\n                              << tagkv.str();\n    };\n    // Make the RPC call.\n    prediction_service->Predict(request.get(), response.get(), callback,\n                                max_rpc_deadline_millis_, done);\n  }\n\n  void PostProcessResponse(OpKernelContext *context, PredictResponse *response,\n                           const absl::Status &rpc_status,\n                           bool fail_op_on_rpc_error,\n                           TTypes<const tstring>::Flat output_tensor_aliases,\n                           DoneCallback rpc_done) {\n    auto rpc_cleaner = gtl::MakeCleanup([&] { rpc_done(); });\n    Tensor *status_code;\n    OP_REQUIRES_OK_ASYNC(\n        context, context->allocate_output(0, TensorShape({}), &status_code),\n        rpc_cleaner.release());\n    status_code->scalar<int>()() = static_cast<int>(rpc_status.code());\n    Tensor *status_error_message;\n    OP_REQUIRES_OK_ASYNC(\n        context,\n        context->allocate_output(1, TensorShape({}), &status_error_message),\n        rpc_cleaner.release());\n    status_error_message->scalar<tstring>()() = rpc_status.message();\n    OpOutputList output_tensors_list;\n    OP_REQUIRES_OK_ASYNC(\n        context, context->output_list(\"output_tensors\", &output_tensors_list),\n        rpc_cleaner.release());\n    // Process the response.\n    if (!rpc_status.ok()) {\n      if (fail_op_on_rpc_error) {\n        OP_REQUIRES_OK_ASYNC(\n            context, tensorflow::Status(static_cast<tensorflow::error::Code>(\n                                            rpc_status.code()),\n                                        rpc_status.message()),\n            rpc_cleaner.release());\n      } else {\n        // Allocate some empty output for the output_tensors.\n        for (int i = 0; i < output_tensors_list.size(); ++i) {\n          Tensor *unused;\n          OP_REQUIRES_OK_ASYNC(context, output_tensors_list.allocate(\n                                            i, TensorShape({}), &unused),\n                               rpc_cleaner.release());\n        }\n        return;\n      }\n    }\n    OP_REQUIRES_ASYNC(\n        context, output_tensors_list.size() == output_tensor_aliases.size(),\n        errors::Internal(\n            \"Response doesn't have the right number of outputs; actual: \",\n            output_tensors_list.size(), \" expected: \",\n            output_tensor_aliases.size()),\n        rpc_cleaner.release());\n    AliasTensorMap &outputs = *response->mutable_outputs();\n    for (int i = 0; i < output_tensor_aliases.size(); i++) {\n      Tensor output_tensor;\n      OP_REQUIRES_ASYNC(\n          context, output_tensor.FromProto(outputs[output_tensor_aliases(i)]),\n          errors::Internal(\"Response tensor proto: \",\n                           tensorflow::string(output_tensor_aliases(i)),\n                           \" cannot be converted back to a tensor.\"),\n          rpc_cleaner.release());\n      output_tensors_list.set(i, output_tensor);\n    }\n  }\n\n private:\n  std::string model_name_;\n  int64 model_version_;\n  bool fail_op_on_rpc_error_;\n  int64 max_rpc_deadline_millis_;\n  std::string signature_name_;\n  std::string old_model_name_;\n  int task_;\n\n  std::string req_model_name_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/remote_predict_op_grpc.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n==============================================================================*/\n\n#include \"monolith/native_training/runtime/ops/remote_predict_op.h\"\n#include \"monolith/native_training/runtime/ops/prediction_service_grpc.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"TfServingRemotePredict\").Device(DEVICE_CPU),\n    RemotePredictOp<::tensorflow::monolith_tf::PredictionServiceGrpc,\n                    ::tensorflow::monolith_tf::AgentHeartbeat<\n                        ::tensorflow::monolith_tf::PredictionServiceGrpc>>);\n\nREGISTER_OP(\"TfServingRemotePredict\")\n    .Attr(\"T: list(type)\")\n    .Attr(\"model_name: string = ''\")\n    .Attr(\"model_version: int = -1\")\n    .Attr(\"fail_op_on_rpc_error: bool = true\")\n    .Attr(\"max_rpc_deadline_millis: int = 30\")\n    .Attr(\"signature_name: string = 'serving_default'\")\n    .Attr(\"old_model_name: string = ''\")\n    .Attr(\"task: int = -1\")\n    .Input(\"input_tensor_aliases: string\")\n    .Input(\"input_tensors: T\")\n    .Input(\"output_tensor_aliases: string\")\n    .Output(\"status_code: int32\")\n    .Output(\"status_error_message: string\")\n    .Output(\"output_tensors: output_types\")\n    .Attr(\"output_types: list(type)\")\n    .SetShapeFn([](shape_inference::InferenceContext *c) {\n      shape_inference::ShapeHandle unused;\n      // Checks the length of input_tensor_aliases with that of input_tensors.\n      std::vector<shape_inference::ShapeHandle> input_aliases_handle;\n      TF_RETURN_IF_ERROR(\n          c->input(\"input_tensor_aliases\", &input_aliases_handle));\n      TF_RETURN_IF_ERROR(c->WithRank(input_aliases_handle[0], 1, &unused));\n      std::vector<shape_inference::ShapeHandle> inputs_handle;\n      TF_RETURN_IF_ERROR(c->input(\"input_tensors\", &inputs_handle));\n      if (c->Value(c->NumElements(input_aliases_handle[0])) !=\n          inputs_handle.size()) {\n        return errors::InvalidArgument(\n            \"'input_tensors' should be equal in length to \"\n            \"'input_tensor_aliases'. Length of 'input_tensors': \",\n            inputs_handle.size(), \", length of 'input_tensor_aliases': \",\n            c->Value(c->NumElements(input_aliases_handle[0])));\n      }\n\n      // Checks the length of output_tensor_aliases with that of output_types.\n      DataTypeVector output_types;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"output_types\", &output_types));\n      std::vector<shape_inference::ShapeHandle> output_aliases_handle;\n      TF_RETURN_IF_ERROR(\n          c->input(\"output_tensor_aliases\", &output_aliases_handle));\n      if (c->Value(c->NumElements(output_aliases_handle[0])) !=\n          output_types.size()) {\n        return errors::InvalidArgument(\n            \"'output_types' should be equal in length to \"\n            \"'output_tensor_aliases'. Length of 'output_types': \",\n            output_types.size(), \", length of 'output_tensor_aliases': \",\n            c->Value(c->NumElements(output_aliases_handle[0])));\n      }\n\n      // We know the shape of the first 2 outputs, but not the rest.\n      TF_RETURN_IF_ERROR(c->set_output(\"status_code\", {c->Scalar()}));\n      TF_RETURN_IF_ERROR(c->set_output(\"status_error_message\", {c->Scalar()}));\n      for (int i = 2; i < c->num_outputs(); ++i) {\n        c->set_output(i, c->UnknownShape());\n      }\n\n      return Status::OK();\n    })\n    .SetIsStateful()\n    .Doc(R\"doc(\nInvokes Predict on a remote graph.\nfail_op_on_rpc_error: If set true, the Op fails if the rpc fails, and returns\n  the status code as 0 and an empty status_message. Otherwise the\n  Op returns the status of the rpc call, along with the output tensors, if any.\n  Set true by default.\nmax_rpc_deadline_millis: The rpc deadline for remote predict. The actual\ndeadline is min(incoming_rpc_deadline, max_rpc_deadline_millis).\ntask: The task id of ps/entry, if the server_type is entry, it would be 0.\nsignature_name: the signature def for remote graph inference, defaulting to \n\"serving_default\".\nmodel_name: Model name of the remote TF graph.\nmodel_version: the target version for the Predict call. When unset, the\n  default value (-1) implies the latest available version should be used.\ninput_tensor_aliases: Tensor of strings for the input tensor alias names to supply\n  to the RemotePredict call.\ninput_tensors: List of tensors to provide as input. Should be equal in length\n  to 'input_tensor_aliases'.\noutput_tensor_aliases: Tensor of strings for the output tensor alias names to\n  supply to the Predict call.\nstatus_code: Returns the status code of the rpc call; basically converting\n  tensorflow::error::Code to it's int value, so 0 means OK.\nstatus_error_message: Returns the error message in the rpc status.\noutput_tensors: Tensors returned by the Predict call on the remote graph, which \n  are in the same order as output_tensor_aliases.\noutput_types: A list of types of the output tensors. Length of this list should\n  be equal to the length of 'output_tensor_aliases'.\nold_model_name: the model name used in previous monolith version (deprecated).\ntask: the index of old_model_name (deprecated).\n)doc\");\n\n}  // namespace\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/split_by_indices_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstring>\n\n#include \"absl/algorithm/container.h\"\n#include \"absl/container/flat_hash_set.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\n// Given an int64 tensor, split it into multiple tensors based on the value.\ntemplate <class T>\nclass SplitByIndicesOp : public OpKernel {\n public:\n  explicit SplitByIndicesOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"num_splits\", &num_splits_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& indices = ctx->input(0);\n    auto indices_vec = indices.vec<int64>();\n    const int64 num_elements = indices.NumElements();\n    const Tensor& input = ctx->input(1);\n    const int64 element_size =\n        num_elements == 0 ? 0 : input.NumElements() / num_elements;\n    auto input_mat = input.shaped<T, 2>({num_elements, element_size});\n\n    // Here we use a naive implementation (No parallel)\n    std::vector<int64> split_sizes(num_splits_, 0);\n    for (int i = 0; i < num_elements; ++i) {\n      ++split_sizes[indices_vec(i)];\n    }\n\n    std::vector<Tensor*> splitted(num_splits_);\n    for (int i = 0; i < num_splits_; ++i) {\n      TensorShape output_shape;\n      output_shape.AddDim(split_sizes[i]);\n      const TensorShape& shape = input.shape();\n      for (int j = 1; j < shape.dims(); ++j) {\n        output_shape.AddDim(shape.dim_size(j));\n      }\n      OP_REQUIRES_OK(ctx, ctx->allocate_output(i, output_shape, &splitted[i]));\n    }\n    std::vector<typename TTypes<T>::Matrix> splitted_mat;\n    splitted_mat.reserve(num_splits_);\n    for (int i = 0; i < num_splits_; ++i) {\n      splitted_mat.emplace_back(\n          splitted[i]->shaped<T, 2>({split_sizes[i], element_size}));\n    }\n    for (int64 i = num_elements - 1; i >= 0; --i) {\n      int64 index = indices_vec(i);\n      splitted_mat[index].template chip<0>(--split_sizes[index]) =\n          input_mat.template chip<0>(i);\n    }\n  }\n\n private:\n  int num_splits_;\n};\n\nclass SplitByIndicesGradientOp : public OpKernel {\n public:\n  explicit SplitByIndicesGradientOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"num_splits\", &num_splits_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& indices = ctx->input(0);\n    auto indices_vec = indices.vec<int64>();\n    const int64 num_elements = indices.NumElements();\n    std::vector<int64> split_sizes(num_splits_, 0);\n    for (int64 i = 0; i < num_elements; ++i) {\n      ++split_sizes[indices_vec(i)];\n    }\n    const Tensor& input = ctx->input(1);\n    const int64 element_size =\n        num_elements == 0 ? 0 : input.NumElements() / num_elements;\n    const int grads_offset = 2;\n    std::vector<TTypes<const float>::Matrix> grads_mat;\n    grads_mat.reserve(num_splits_);\n    for (int i = 0; i < num_splits_; ++i) {\n      const Tensor& grad = ctx->input(grads_offset + i);\n      grads_mat.emplace_back(\n          grad.shaped<float, 2>({split_sizes[i], element_size}));\n    }\n    Tensor* input_grads;\n    TensorShape input_grads_shape = ctx->input(grads_offset).shape();\n    input_grads_shape.set_dim(0, num_elements);\n    OP_REQUIRES_OK(ctx,\n                   ctx->allocate_output(0, input_grads_shape, &input_grads));\n    auto input_grads_mat =\n        input_grads->shaped<float, 2>({num_elements, element_size});\n    for (int64 i = num_elements - 1; i >= 0; --i) {\n      int64 index = indices_vec(i);\n      input_grads_mat.chip<0>(i) =\n          grads_mat[index].chip<0>(--split_sizes[index]);\n    }\n  }\n\n private:\n  int num_splits_;\n};\n\ntemplate <class T>\nclass ReorderByIndicesOp : public OpKernel {\n public:\n  explicit ReorderByIndicesOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"num_of_shards\", &num_of_shards_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    const Tensor& input = ctx->input(0);\n    const Tensor& shard_ids = ctx->input(1);\n    auto shard_id_vec = shard_ids.vec<int32>();\n    const int64 num_elements = shard_ids.NumElements();\n    // NOTE: element_size should always be 1 except for the test where we use\n    // assign-add for initial assignment.\n    const int64 element_size =\n        num_elements == 0 ? 0 : input.NumElements() / num_elements;\n    auto input_mat = input.shaped<T, 2>({num_elements, element_size});\n\n    // We first count the number of unique FIDs, and also calculate the size for\n    // each shard.\n    typename absl::flat_hash_set<T> id_set;\n    std::vector<int64> splits_offsets(num_of_shards_, 0);\n    std::vector<std::vector<int64>> ids_for_splits(num_of_shards_,\n                                                   std::vector<int64>{});\n    int64 uniq_id_size = 0;\n    for (int i = 0; i < num_elements; ++i) {\n      // First insertion if never sees it before.\n      if (id_set.insert(input_mat(i, 0)).second) {\n        auto index = shard_id_vec(i);\n        ids_for_splits[index].emplace_back(i);\n        ++(splits_offsets[index]);\n        ++uniq_id_size;\n      }\n    }\n\n    // We allocate the buffer here.\n    TensorShape output_shape;\n    output_shape.AddDim(uniq_id_size);\n    const TensorShape& shape = input.shape();\n    for (int j = 1; j < shape.dims(); ++j) {\n      output_shape.AddDim(shape.dim_size(j));\n    }\n    Tensor *outputs, *output_sizes;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, output_shape, &outputs));\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(1, TensorShape{num_of_shards_},\n                                             &output_sizes));\n\n    // We assign the split sizes here.\n    auto output_shape_vec = output_sizes->vec<int32>();\n    for (int i = 0; i < num_of_shards_; i++) {\n      output_shape_vec(i) = splits_offsets[i];\n      if (i > 0) splits_offsets[i] += splits_offsets[i - 1];\n    }\n    // We assign the reordered IDs here.\n    typename TTypes<T>::Matrix splitted_mat =\n        outputs->shaped<T, 2>({uniq_id_size, element_size});\n    for (int i = num_of_shards_ - 1; i >= 0; --i) {\n      for (const int64& j : ids_for_splits[i]) {\n        splitted_mat.template chip<0>(--splits_offsets[i]) =\n            input_mat.template chip<0>(j);\n      }\n    }\n  }\n\n private:\n  int num_of_shards_;\n};\n\nclass RaggedSplitByIndicesOp : public OpKernel {\n public:\n  explicit RaggedSplitByIndicesOp(OpKernelConstruction* c) : OpKernel(c) {\n    OP_REQUIRES_OK(c, c->GetAttr(\"num_splits\", &num_splits_));\n  }\n\n  void Compute(OpKernelContext* c) override {\n    const auto indices = c->input(0).vec<int64>();\n    const auto num = c->input(1).vec<int64>();\n    const auto num_split = c->input(2).vec<int64>();\n    OP_REQUIRES(c, num.size() == indices.size(),\n                errors::InvalidArgument(\n                    \"Ragged tensor values size must match indices size. Got \",\n                    num.size(), \" v.s. \", indices.size()));\n    std::vector<int64> split_sizes(num_splits_, 0);\n    for (int64 i = 0; i < indices.size(); ++i) {\n      ++split_sizes[indices(i)];\n    }\n    std::vector<int64> splitted_offsets(num_splits_, 0);\n    std::vector<TTypes<int64>::Vec> splitted_nums;\n    std::vector<TTypes<int64>::Vec> splitted_num_splits;\n    std::vector<TTypes<int64>::Vec> splitted_pos;\n    for (int i = 0; i < num_splits_; ++i) {\n      Tensor* t;\n      OP_REQUIRES_OK(c, c->allocate_output(i, {split_sizes[i]}, &t));\n      splitted_nums.push_back(t->vec<int64>());\n      OP_REQUIRES_OK(\n          c, c->allocate_output(i + num_splits_, {num_split.size()}, &t));\n      auto splitted_num_split = t->vec<int64>();\n      splitted_num_split(0) = 0;\n      splitted_num_splits.push_back(splitted_num_split);\n      OP_REQUIRES_OK(\n          c, c->allocate_output(i + 2 * num_splits_, {split_sizes[i]}, &t));\n      splitted_pos.push_back(t->vec<int64>());\n    }\n    int split_offset = 1;\n    for (int64 i = 0;; ++i) {\n      while (split_offset < num_split.size() && i == num_split(split_offset)) {\n        for (int index = 0; index < num_splits_; ++index) {\n          splitted_num_splits[index](split_offset) = splitted_offsets[index];\n        }\n        ++split_offset;\n      }\n      if (i >= indices.size()) break;\n      int64 index = indices(i);\n      splitted_pos[index](splitted_offsets[index]) = i;\n      splitted_nums[index](splitted_offsets[index]) = num(i);\n      ++splitted_offsets[index];\n    }\n    OP_REQUIRES(c, split_offset == num_split.size(),\n                errors::InvalidArgument(\"The input ragged tensor is invalid.\"));\n  }\n\n private:\n  int num_splits_;\n};\n\nREGISTER_OP(\"MonolithRaggedSplitByIndices\")\n    .Input(\"indices: int64\")\n    .Input(\"num: int64\")\n    .Input(\"num_split: int64\")\n    .Output(\"splitted_nums: num_splits * int64\")\n    .Output(\"splitted_num_splits: num_splits * int64\")\n    .Output(\"splitted_pos: num_splits * int64\")\n    .Attr(\"num_splits: int\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int num_splits;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"num_splits\", &num_splits));\n      int offset = 0;\n      for (int i = 0; i < num_splits; ++i) {\n        c->set_output(i, c->Vector(c->UnknownDim()));\n      }\n      offset += num_splits;\n      for (int i = 0; i < num_splits; ++i) {\n        c->set_output(offset + i, c->input(2));\n      }\n      offset += num_splits;\n      for (int i = 0; i < num_splits; ++i) {\n        c->set_output(offset + i, c->Vector(c->UnknownDim()));\n      }\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithRaggedSplitByIndices\").Device(DEVICE_CPU),\n                        RaggedSplitByIndicesOp);\n\nREGISTER_OP(\"MonolithSplitByIndices\")\n    .Input(\"indices: int64\")\n    .Input(\"input: T\")\n    .Output(\"splitted: num_splits * T\")\n    .Attr(\"num_splits: int\")\n    .Attr(\"T: type\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int num_splits;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"num_splits\", &num_splits));\n      shape_inference::ShapeHandle shape_handle = c->input(1);\n      int rank = c->Rank(shape_handle);\n      std::vector<shape_inference::DimensionHandle> dim_handles;\n      dim_handles.push_back(c->UnknownDim());\n      for (int i = 1; i < rank; ++i) {\n        dim_handles.push_back(c->Dim(shape_handle, i));\n      }\n      for (int i = 0; i < num_splits; ++i) {\n        c->set_output(i, c->MakeShape(dim_handles));\n      }\n      return Status::OK();\n    });\n\nREGISTER_OP(\"MonolithSplitByIndicesGradient\")\n    .Input(\"indices: int64\")\n    .Input(\"input: float\")\n    .Input(\"grads: num_splits * float\")\n    .Output(\"input_grads: float\")\n    .Attr(\"num_splits: int\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int num_splits;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"num_splits\", &num_splits));\n      shape_inference::DimensionHandle num_elements = c->Dim(c->input(0), 0);\n      shape_inference::ShapeHandle shape_handle = c->input(1);\n      int rank = c->Rank(shape_handle);\n      std::vector<shape_inference::DimensionHandle> dim_handles;\n      dim_handles.push_back(num_elements);\n      for (int i = 1; i < rank; ++i) {\n        dim_handles.push_back(c->Dim(shape_handle, i));\n      }\n      c->set_output(0, c->MakeShape(dim_handles));\n      return Status::OK();\n    });\n\nREGISTER_OP(\"MonolithReorderByIndices\")\n    .Input(\"input: T\")\n    .Input(\"shard_ids: int32\")\n    .Output(\"reordered_tensor: T\")\n    .Output(\"shard_sizes: int32\")\n    .Attr(\"num_of_shards: int\")\n    .Attr(\"T: type\")\n    .SetDoNotOptimize()  // Crash with grappler.\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      int num_of_shards;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"num_of_shards\", &num_of_shards));\n      shape_inference::ShapeHandle shape_handle = c->input(0);\n      int rank = c->Rank(shape_handle);\n      std::vector<shape_inference::DimensionHandle> dim_handles;\n      dim_handles.push_back(c->UnknownDim());\n      for (int i = 1; i < rank; ++i) {\n        dim_handles.push_back(c->Dim(shape_handle, i));\n      }\n      // The first output is for the reshuffled first element.\n      c->set_output(0, c->MakeShape(dim_handles));\n      // The second output is for the all2all sizes.\n      c->set_output(1, c->MakeShape({num_of_shards}));\n      return Status::OK();\n    });\n\n#define REGISTER_KERNEL(type)                             \\\n  REGISTER_KERNEL_BUILDER(Name(\"MonolithSplitByIndices\")  \\\n                              .Device(DEVICE_CPU)         \\\n                              .TypeConstraint<type>(\"T\"), \\\n                          SplitByIndicesOp<type>)\n\nREGISTER_KERNEL(float);\nREGISTER_KERNEL(int64);\n\n#undef REGISTER_KERNEL\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithSplitByIndicesGradient\").Device(DEVICE_CPU),\n    SplitByIndicesGradientOp);\n\n#define REGISTER_KERNEL_REORDER_BY_INDICES(type)           \\\n  REGISTER_KERNEL_BUILDER(Name(\"MonolithReorderByIndices\") \\\n                              .Device(DEVICE_CPU)          \\\n                              .TypeConstraint<type>(\"T\"),  \\\n                          ReorderByIndicesOp<type>)\n\nREGISTER_KERNEL_REORDER_BY_INDICES(float);\nREGISTER_KERNEL_REORDER_BY_INDICES(int64);\n\n#undef REGISTER_KERNEL_REORDER_BY_INDICES\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/static_reshape_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <string>\n#include <vector>\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/tensor.h\"\n#include \"tensorflow/core/framework/tensor_shape.h\"\n#include \"tensorflow/core/framework/types.h\"\n#include \"tensorflow/core/platform/status.h\"\n#include \"tensorflow/core/util/work_sharder.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass StaticReshapeNOp : public OpKernel {\n public:\n  explicit StaticReshapeNOp(OpKernelConstruction* ctx) : OpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"shapes\", &shapes_));\n    OP_REQUIRES_OK(ctx,\n                   ctx->GetAttr(\"enable_parallelism\", &enable_parallelism_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"cost_per_tensor\", &cost_per_tensor_));\n  }\n\n  void Compute(OpKernelContext* ctx) override {\n    int num_inputs = ctx->num_inputs();\n    OP_REQUIRES(ctx, shapes_.size() == num_inputs,\n                errors::InvalidArgument(\n                    \"`shapes` size must equal to `inputs`, got shapes (\",\n                    shapes_.size(), \") vs `inputs` (\", num_inputs, \")\"));\n\n    Tensor* tensor_sizes = nullptr;\n    OP_REQUIRES_OK(\n        ctx, ctx->allocate_output(num_inputs, {num_inputs}, &tensor_sizes));\n    auto sizes_vec = tensor_sizes->vec<int64_t>();\n\n    auto thread_pool = ctx->device()->tensorflow_cpu_worker_threads()->workers;\n    auto reshape = [&](int64_t begin, int64_t end) {\n      for (int idx = static_cast<int>(begin); idx < static_cast<int>(end);\n           idx++) {\n        const Tensor& input = ctx->input(idx);\n        int tensor_size = input.NumElements();\n        TensorShape shape;\n        const PartialTensorShape& partial = shapes_.at(idx);\n\n        // Maybe infer unk dim.\n        int64_t product = 1;\n        int unknown_index = -1;\n        OP_REQUIRES(ctx, partial.dims() > 0,\n                    errors::InvalidArgument(\n                        \"Shape cannot be unknown rank for input [\", idx, \"]!\"));\n        for (int d = 0; d < partial.dims(); d++) {\n          int dim = partial.dim_size(d);\n          if (dim == -1) {\n            OP_REQUIRES(\n                ctx, unknown_index == -1,\n                errors::InvalidArgument(\n                    \"Only one input size may be -1, not both \", unknown_index,\n                    \" and \", d, \"for input [\", idx, \"]!\"));\n            unknown_index = d;\n            shape.AddDim(1);\n          } else {\n            shape.AddDim(dim);\n            product *= dim;\n          }\n        }\n        if (unknown_index != -1) {\n          if (product == 0) {\n            // In this case, tensor_size should be 0.\n            // Check will perform later.\n            shape.set_dim(unknown_index, 0);\n          } else {\n            OP_REQUIRES(ctx, tensor_size % product == 0,\n                        errors::InvalidArgument(\n                            \"Input[\", idx, \"] of size \", tensor_size,\n                            \" cannot be reshaped as \", shape.DebugString()));\n            shape.set_dim(unknown_index, tensor_size / product);\n          }\n        }\n        OP_REQUIRES(\n            ctx, input.NumElements() == shape.num_elements(),\n            errors::InvalidArgument(\n                \"Input[\", idx, \"] to reshape is a tensor with \",\n                input.NumElements(), \" values, but the requested shape has \",\n                shape.num_elements()));\n        Tensor output(input.dtype());\n        CHECK(output.CopyFrom(input, shape));\n        ctx->set_output(idx, output);\n        sizes_vec(idx) = shape.num_elements();\n      }\n    };\n\n    if (enable_parallelism_) {\n      thread_pool->ParallelFor(num_inputs, cost_per_tensor_, reshape);\n    } else {\n      reshape(0, num_inputs);\n    }\n  }\n\n private:\n  std::vector<PartialTensorShape> shapes_;\n  bool enable_parallelism_;\n  int64 cost_per_tensor_;\n};\n\nREGISTER_OP(\"MonolithStaticReshapeN\")\n    .Input(\"inputs: dtypes\")\n    .Output(\"outputs: dtypes\")\n    .Output(\"sizes: int64\")\n    .Attr(\"dtypes: list(type)\")\n    .Attr(\"shapes: list(shape)\")\n    .Attr(\"enable_parallelism: bool = true\")\n    .Attr(\"cost_per_tensor: int = 10000000\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      std::vector<PartialTensorShape> shapes;\n      TF_RETURN_IF_ERROR(c->GetAttr(\"shapes\", &shapes));\n      for (int i = 0; i < shapes.size(); i++) {\n        shape_inference::ShapeHandle shape;\n        TF_RETURN_IF_ERROR(\n            c->MakeShapeFromPartialTensorShape(shapes[i], &shape));\n        c->set_output(i, shape);\n      }\n      c->set_output(shapes.size(), c->Vector(shapes.size()));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithStaticReshapeN\").Device(DEVICE_CPU),\n                        StaticReshapeNOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/touched_key_set_insert_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <memory>\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\n#include \"monolith/native_training/runtime/ops/touched_key_set_tf_bridge.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass TouchedKeySetInsertOp : public OpKernel {\n public:\n  explicit TouchedKeySetInsertOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    TouchedKeySetTfBridge* touched_key_set = nullptr;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &touched_key_set));\n    core::ScopedUnref unref(touched_key_set);\n    const Tensor& tensor = ctx->input(1);\n    const int64 num_elements = tensor.NumElements();\n    auto ids = tensor.vec<int64>();\n    int64 total_dropped_num = 0;\n    for (int64 i = 0; i < num_elements; ++i) {\n      total_dropped_num += touched_key_set->Insert(ids(i));\n    }\n\n    Tensor* output;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {1}, &output));\n    auto output_vec = output->vec<int64>();\n    output_vec(0) = total_dropped_num;\n  }\n};\n\nREGISTER_OP(\"MonolithTouchedKeySetInsert\")\n        .Input(\"handle: resource\")\n        .Input(\"ids: int64\")\n        .Output(\"size: int64\")\n        .SetIsStateful()\n        .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithTouchedKeySetInsert\").Device(DEVICE_CPU),\n                        TouchedKeySetInsertOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/touched_key_set_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <memory>\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\n#include \"monolith/native_training/runtime/ops/touched_key_set_tf_bridge.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass TouchedKeySetOp : public ResourceOpKernel<TouchedKeySetTfBridge> {\n public:\n  explicit TouchedKeySetOp(OpKernelConstruction* ctx) : ResourceOpKernel(ctx) {\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"capacity\", &capacity_));\n    OP_REQUIRES_OK(ctx, ctx->GetAttr(\"concurrency_level\", &concurrency_level_));\n  }\n\n  ~TouchedKeySetOp() override = default;\n\n private:\n  Status CreateResource(TouchedKeySetTfBridge** touched_key_set_bridge)\n      TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) override {\n    auto touched_key_set =\n        std::make_unique<monolith::hopscotch::HopscotchHashSet<int64_t>>(\n            capacity_,\n            concurrency_level_);\n    *touched_key_set_bridge =\n        new TouchedKeySetTfBridge(std::move(touched_key_set));\n    return Status::OK();\n  };\n\n  int64 capacity_;\n\n  int concurrency_level_;\n};\n\nREGISTER_OP(\"MonolithTouchedKeySet\")\n        .Output(\"handle: resource\")\n        .Attr(\"capacity: int = 2097152\")\n        .Attr(\"concurrency_level: int = 1024\")\n        .Attr(\"container: string = ''\")\n        .Attr(\"shared_name: string = ''\")\n        .SetIsStateful()\n        .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithTouchedKeySet\").Device(DEVICE_CPU),\n                        TouchedKeySetOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/touched_key_set_steal_op.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <memory>\n\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\n#include \"monolith/native_training/runtime/ops/touched_key_set_tf_bridge.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass TouchedKeySetStealOp : public OpKernel {\n public:\n  explicit TouchedKeySetStealOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}\n\n  void Compute(OpKernelContext* ctx) override {\n    TouchedKeySetTfBridge* touched_key_set = nullptr;\n    OP_REQUIRES_OK(ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &touched_key_set));\n    core::ScopedUnref unref(touched_key_set);\n\n    auto ids = touched_key_set->Steal();\n    Tensor* output;\n    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, {static_cast<int64>(ids.size())}, &output));\n    auto output_vec = output->vec<int64>();\n    for (size_t i = 0; i < ids.size(); ++i) {\n      output_vec(i) = ids[i];\n    }\n  }\n};\n\nREGISTER_OP(\"MonolithTouchedKeySetSteal\")\n        .Input(\"handle: resource\")\n        .Output(\"ids: int64\")\n        .SetIsStateful()\n        .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithTouchedKeySetSteal\").Device(DEVICE_CPU),\n                        TouchedKeySetStealOp);\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/touched_key_set_tf_bridge.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_TOUCHED_KEY_SET_TF_BRIDGE_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_TOUCHED_KEY_SET_TF_BRIDGE_H_\n\n#include <memory>\n\n#include \"absl/strings/str_format.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/resource_op_kernel.h\"\n\n#include \"monolith/native_training/runtime/hopscotch/hopscotch_hash_set.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\n\nclass TouchedKeySetTfBridge : public ResourceBase {\n public:\n  explicit TouchedKeySetTfBridge(std::unique_ptr<monolith::hopscotch::HopscotchHashSet<\n      int64_t>> touched_key_set)\n      : touched_key_set_(std::move(touched_key_set)) {}\n\n  size_t Insert(int64_t key) {\n    return touched_key_set_->insert(key);\n  }\n\n  std::vector<int64_t> Steal() {\n    return touched_key_set_->GetAndClear();\n  }\n\n  size_t Size() const {\n    return touched_key_set_->size();\n  }\n\n  std::string DebugString() const override {\n    return absl::StrFormat(\"TouchedKeySet with capacity: %d\",\n                           touched_key_set_->capacity());\n  }\n\n private:\n  std::unique_ptr<monolith::hopscotch::HopscotchHashSet<int64_t>>\n      touched_key_set_;\n};\n\n}  // namespace monolith_tf\n}  // namespace tensorflow\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_OPS_TOUCHED_KEY_SET_TF_BRIDGE_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/ops/unique_mapping_ops.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include <cstring>\n\n#include \"absl/algorithm/container.h\"\n#include \"absl/container/flat_hash_map.h\"\n#include \"absl/strings/str_format.h\"\n#include \"tensorflow/core/framework/op_kernel.h\"\n#include \"tensorflow/core/framework/resource_mgr.h\"\n#include \"tensorflow/core/framework/shape_inference.h\"\n#include \"tensorflow/core/platform/threadpool.h\"\n\nnamespace tensorflow {\nnamespace monolith_tf {\nnamespace {\n\n// We want to create a tensor, while its lifecycle going with the step.\n// So the idea here is to use step_container.\n//\n// The following code is borrowed from variable_ops.cc in Tensorflow.\nstring SharedTensorName(const string& tensor_name,\n                        const FrameAndIter& control_frame) {\n  if (control_frame.frame_id != kIllegalFrameId &&\n      control_frame.iter_id != kIllegalIterId) {\n    return strings::StrCat(tensor_name, \"/frame:\", control_frame.frame_id,\n                           \"/iter:\", control_frame.iter_id);\n  }\n  return tensor_name;\n}\n\nstruct SharedTensor : public ResourceBase {\n  // Maybe we can add a mutex here if needed.\n  std::string name;\n  Tensor val;\n  string DebugString() const override { return name; }\n  int64 MemoryUsed() const override { return val.AllocatedBytes(); }\n};\n\nclass UniqueKeyWitValueAndOffsetOp : public OpKernel {\n public:\n  explicit UniqueKeyWitValueAndOffsetOp(OpKernelConstruction* c) : OpKernel(c) {\n    OP_REQUIRES_OK(c, c->GetAttr(\"dims\", &dims_));\n    OP_REQUIRES_OK(c, c->GetAttr(\"generate_buffer\", &generate_buffer_));\n    dims_size_ = dims_.size();\n  }\n\n  void Compute(OpKernelContext* c) override {\n    auto key = c->input(0).vec<int64>();\n    auto key_split = c->input(1).vec<int64>();\n    OP_REQUIRES(c, key_split.size() == dims_size_ + 1,\n                errors::InvalidArgument(\"RaggedKey should have \", dims_size_,\n                                        \" but got \", key_split.size() - 1));\n    Tensor* t;\n\n    std::vector<int64> unique_key;\n    unique_key.reserve(key.size());\n\n    OP_REQUIRES_OK(c, c->allocate_output(1, {dims_size_ + 1}, &t));\n    auto unique_key_split_vec = t->vec<int64>();\n    unique_key_split_vec(0) = 0;\n\n    OP_REQUIRES_OK(c, c->allocate_output(2, {key.size()}, &t));\n    auto value_offset_vec = t->vec<int64>();\n    int64 value_offset_vec_offset = 0;\n\n    std::vector<int64> value_offset_split;\n    value_offset_split.reserve(key.size());\n    value_offset_split.push_back(0);\n\n    int64 value_offset = 0;\n    absl::flat_hash_map<int64, absl::InlinedVector<int64, 4>> m;\n    int j = 0;\n    m.reserve(2 * (key_split(1) - key_split(0)));\n    for (int i = 0;; ++i) {\n      while (i == key_split(j + 1)) {\n        unique_key_split_vec(j + 1) = unique_key.size();\n        for (int k = unique_key_split_vec(j); k < unique_key_split_vec(j + 1);\n             ++k) {\n          auto it = m.find(unique_key[k]);\n          for (int64 value_offset_for_key : it->second) {\n            value_offset_vec(value_offset_vec_offset++) = value_offset_for_key;\n          }\n          value_offset_split.push_back(value_offset_vec_offset);\n        }\n        ++j;\n        if (j < dims_size_) {\n          m.clear();\n          m.reserve(2 * (key_split(j + 1) - key_split(j)));\n        } else {\n          break;\n        }\n      }\n      if (i == key.size()) break;\n      auto it = m.find(key(i));\n      if (it == m.end()) {\n        m.insert({key(i), {value_offset}});\n        unique_key.push_back(key(i));\n      } else {\n        it->second.push_back(value_offset);\n      }\n      value_offset += dims_[j];\n    }\n    OP_REQUIRES_OK(c, c->allocate_output(0, {unique_key.size()}, &t));\n    auto unique_key_vec = t->vec<int64>();\n    std::memcpy(unique_key_vec.data(), unique_key.data(),\n                sizeof(int64) * unique_key.size());\n    OP_REQUIRES_OK(c, c->allocate_output(3, {value_offset_split.size()}, &t));\n    auto value_offset_split_vec = t->vec<int64>();\n    std::memcpy(value_offset_split_vec.data(), value_offset_split.data(),\n                sizeof(int64) * value_offset_split.size());\n    OP_REQUIRES_OK(c, CreateSharedTensor(c, {value_offset}));\n  }\n\n  Status CreateSharedTensor(OpKernelContext* c, TensorShape shape) {\n    if (!generate_buffer_) {\n      Tensor* handle;\n      TF_RETURN_IF_ERROR(c->allocate_output(4, TensorShape({}), &handle));\n      handle->scalar<ResourceHandle>()() = ResourceHandle();\n      return Status::OK();\n    }\n    const std::string unique_name =\n        SharedTensorName(def().name(), c->frame_iter());\n    Tensor t;\n    TF_RETURN_IF_ERROR(c->allocate_temp(DataType::DT_FLOAT, shape, &t));\n    SharedTensor* st = new SharedTensor();\n    st->name = unique_name;\n    st->val = std::move(t);\n    auto* container = c->step_container();\n\n    TF_RETURN_IF_ERROR(\n        container->Create(c->resource_manager(), unique_name, st));\n    Tensor* handle;\n    TF_RETURN_IF_ERROR(c->allocate_output(4, TensorShape({}), &handle));\n    handle->scalar<ResourceHandle>()() =\n        container->MakeResourceHandle<SharedTensor>(unique_name, *c->device());\n    return Status::OK();\n  }\n\n private:\n  std::vector<int> dims_;\n  int dims_size_;\n  bool generate_buffer_;\n};\n\nREGISTER_OP(\"MonolithUniqueKeyWithValueAndOffset\")\n    .Input(\"key: int64\")\n    .Input(\"key_split: int64\")\n    .Output(\"unique_key: int64\")\n    .Output(\"unique_key_split: int64\")\n    .Output(\"value_offset: int64\")\n    .Output(\"value_offset_split: int64\")\n    .Output(\"value_buffer: resource\")\n    .Attr(\"dims: list(int)\")\n    .Attr(\"generate_buffer: bool\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      c->set_output(0, c->Vector(c->UnknownDim()));\n      c->set_output(1, c->input(1));\n      c->set_output(2, c->input(0));\n      c->set_output(3, c->Vector(c->UnknownDim()));\n      c->set_output(4, c->Scalar());\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithUniqueKeyWithValueAndOffset\").Device(DEVICE_CPU),\n    UniqueKeyWitValueAndOffsetOp);\n\nclass FinallizeSharedTensorOp : public OpKernel {\n public:\n  explicit FinallizeSharedTensorOp(OpKernelConstruction* c) : OpKernel(c) {}\n\n  void Compute(OpKernelContext* c) override {\n    core::RefCountPtr<SharedTensor> st = nullptr;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 0), &st));\n    c->set_output(0, st->val);\n    OP_REQUIRES_OK(c, DeleteResource(c, HandleFromInput(c, 0)));\n  }\n};\n\nREGISTER_OP(\"MonolithFinalizeSharedTensor\")\n    .Input(\"handle: num_tensors * resource\")\n    .Output(\"t: dtype\")\n    .Attr(\"shape: shape\")\n    .Attr(\"dtype: type\")\n    .Attr(\"num_tensors: int\")\n    .SetIsStateful()\n    .SetShapeFn(shape_inference::ExplicitShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithFinalizeSharedTensor\").Device(DEVICE_CPU),\n                        FinallizeSharedTensorOp);\n\nclass FillWithOffsetMapOp : public OpKernel {\n public:\n  explicit FillWithOffsetMapOp(OpKernelConstruction* c) : OpKernel(c) {\n    OP_REQUIRES_OK(c, c->GetAttr(\"dims\", &dims_));\n  }\n\n  void Compute(OpKernelContext* c) override {\n    auto pos = c->input(0).vec<int64>();\n    auto pos_split = c->input(1).vec<int64>();\n    auto value = c->input(2).vec<float>();\n    auto value_offset_map = c->input(3).vec<int64>();\n    auto value_offset_map_split = c->input(4).vec<int64>();\n    core::RefCountPtr<SharedTensor> st = nullptr;\n    OP_REQUIRES_OK(c, LookupResource(c, HandleFromInput(c, 5), &st));\n    auto value_buffer = st->val.vec<float>();\n    int64 value_offset = 0;\n    OP_REQUIRES(\n        c, pos_split.size() == dims_.size() + 1,\n        errors::InvalidArgument(\"Pos's first dim doesn't match dim size. \",\n                                pos_split.size() - 1, \" v.s. \", dims_.size()));\n    int j = 0;\n    for (int i = 0; i < pos.size(); ++i) {\n      while (i == pos_split(j + 1)) {\n        ++j;\n      }\n      OP_REQUIRES(\n          c, pos(i) < value_offset_map.size(),\n          errors::InvalidArgument(\"pos is bigger than offset size. \", pos(i),\n                                  \" v.s. \", value_offset_map.size()));\n      const int64 value_offset_end = value_offset + dims_[j];\n      OP_REQUIRES(c, value_offset_end <= value.size(),\n                  errors::InvalidArgument(FormatValueError(pos_split, value)));\n      for (int64 offset_pos = value_offset_map_split(pos(i));\n           offset_pos < value_offset_map_split(pos(i) + 1); ++offset_pos) {\n        std::memcpy(value_buffer.data() + value_offset_map(offset_pos),\n                    value.data() + value_offset, dims_[j] * sizeof(float));\n      }\n      value_offset = value_offset_end;\n    }\n    c->set_output(0, c->input(5));\n  }\n\n private:\n  std::string FormatValueError(TTypes<const int64>::Vec pos_split,\n                               TTypes<const float>::Vec value) {\n    std::string s;\n    std::vector<int64> pos_split_vec;\n    for (int i = 0; i < pos_split.size(); ++i) {\n      pos_split_vec.push_back(pos_split(i));\n    }\n    int64 expected_size = 0;\n    for (int i = 0; i < dims_.size(); ++i) {\n      expected_size += (pos_split_vec[i + 1] - pos_split_vec[i]) * dims_[i];\n    }\n    absl::StrAppend(&s,\n                    absl::StrFormat(\"Value size doesn't match expected size. \"\n                                    \"expected: %d, actual: %d. \\n\",\n                                    expected_size, value.size()));\n    absl::StrAppend(&s, absl::StrFormat(\"Pos split: %s\",\n                                        absl::StrJoin(pos_split_vec, \",\")));\n    return s;\n  }\n\n  std::vector<int> dims_;\n};\n\nREGISTER_OP(\"MonolithFillWithOffsetMap\")\n    .Input(\"pos: int64\")\n    .Input(\"pos_split: int64\")\n    .Input(\"value : float\")\n    .Input(\"value_offset_map: int64\")\n    .Input(\"value_offset_map_split: int64\")\n    .Input(\"value_buffer: resource\")\n    .Output(\"out_value_buffer: resource\")\n    .Attr(\"dims: list(int)\")\n    .SetShapeFn(shape_inference::ScalarShape);\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithFillWithOffsetMap\").Device(DEVICE_CPU),\n                        FillWithOffsetMapOp);\n\nclass FillWithOffsetMapGradientOp : public OpKernel {\n public:\n  explicit FillWithOffsetMapGradientOp(OpKernelConstruction* c) : OpKernel(c) {\n    OP_REQUIRES_OK(c, c->GetAttr(\"dims\", &dims_));\n  }\n\n  void Compute(OpKernelContext* c) override {\n    auto pos = c->input(0).vec<int64>();\n    auto pos_split = c->input(1).vec<int64>();\n    auto grad = c->input(2).vec<float>();\n    auto grad_offset_map = c->input(3).vec<int64>();\n    auto grad_offset_map_split = c->input(4).vec<int64>();\n\n    int64 bgrad_size = 0;\n    for (int j = 0; j < dims_.size(); ++j) {\n      bgrad_size += dims_[j] * (pos_split(j + 1) - pos_split(j));\n    }\n    Tensor* t;\n    OP_REQUIRES_OK(c, c->allocate_output(0, {bgrad_size}, &t));\n    auto bgrad = t->vec<float>();\n    bgrad.setZero();\n    int64 bgrad_offset = 0;\n\n    int j = 0;\n    for (int i = 0; i < pos.size(); ++i) {\n      while (i == pos_split(j + 1)) {\n        ++j;\n      }\n      OP_REQUIRES(\n          c, pos(i) < grad_offset_map.size(),\n          errors::InvalidArgument(\"pos is bigger than offset size. \", pos(i),\n                                  \" v.s. \", grad_offset_map.size()));\n      for (int64 offset_pos = grad_offset_map_split(pos(i));\n           offset_pos < grad_offset_map_split(pos(i) + 1); ++offset_pos) {\n        const int64 grad_offset = grad_offset_map(offset_pos);\n        for (int k = 0; k < dims_[j]; ++k) {\n          bgrad(bgrad_offset + k) += grad(grad_offset + k);\n        }\n      }\n      bgrad_offset += dims_[j];\n    }\n  }\n\n private:\n  std::vector<int> dims_;\n};\n\nREGISTER_OP(\"MonolithFillWithOffsetMapGradient\")\n    .Input(\"pos: int64\")\n    .Input(\"pos_split: int64\")\n    .Input(\"grad: float\")\n    .Input(\"grad_offset_map: int64\")\n    .Input(\"grad_offset_map_split: int64\")\n    .Output(\"backprop_grad: float\")\n    .Attr(\"dims: list(int)\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      c->set_output(0, c->Vector(c->UnknownDim()));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(\n    Name(\"MonolithFillWithOffsetMapGradient\").Device(DEVICE_CPU),\n    FillWithOffsetMapGradientOp);\n\nclass FusedValueRowidsOp : public OpKernel {\n public:\n  explicit FusedValueRowidsOp(OpKernelConstruction* c) : OpKernel(c) {}\n\n  void Compute(OpKernelContext* c) override {\n    auto splits = c->input(0).vec<int64>();\n    Tensor* t;\n    const int len = splits.size() - 1;\n    OP_REQUIRES_OK(c, c->allocate_output(0, {splits(len)}, &t));\n    auto rowids = t->vec<int64>();\n    for (int64 i = 0; i < len; ++i) {\n      for (int64 j = splits(i); j < splits(i + 1); ++j) {\n        rowids(j) = i;\n      }\n    }\n  }\n};\n\nREGISTER_OP(\"MonolithFusedValueRowids\")\n    .Input(\"splits: int64\")\n    .Output(\"rowids: int64\")\n    .SetShapeFn([](shape_inference::InferenceContext* c) {\n      c->set_output(0, c->Vector(c->UnknownDim()));\n      return Status::OK();\n    });\n\nREGISTER_KERNEL_BUILDER(Name(\"MonolithFusedValueRowids\").Device(DEVICE_CPU),\n                        FusedValueRowidsOp);\n\n}  // namespace\n}  // namespace monolith_tf\n}  // namespace tensorflow\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\", \"cc_proto_library\", \"cc_test\")\nload(\"@com_github_grpc_grpc//bazel:cc_grpc_library.bzl\", \"cc_grpc_library\")\nload(\"@com_google_protobuf//:protobuf.bzl\", \"py_proto_library\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_cc_test\")\nload(\"@com_github_grpc_grpc//bazel:cc_grpc_library.bzl\", \"cc_grpc_library\")\n\npackage(default_visibility = [\"//monolith/native_training/runtime:__subpackages__\"])\n\ncc_library(\n    name = \"sync_client_interface\",\n    hdrs = [\"sync_client_interface.h\"],\n    deps = [\n        \"@com_github_grpc_grpc//:grpc++\",\n    ],\n)\n\ncc_library(\n    name = \"dummy_sync_client\",\n    hdrs = [\"dummy_sync_client.h\"],\n    deps = [\n        \":sync_client_interface\",\n    ],\n)\n\nproto_library(\n    name = \"parameter_sync_proto\",\n    srcs = [\"parameter_sync.proto\"],\n)\n\ncc_proto_library(\n    name = \"parameter_sync_cc_proto\",\n    deps = [\":parameter_sync_proto\"],\n)\n\npy_proto_library(\n    name = \"parameter_sync_py_proto\",\n    srcs = [\"parameter_sync.proto\"],\n    srcs_version = \"PY2AND3\",\n    visibility = [\"//visibility:public\"],\n    deps = [],\n)\n\ncc_grpc_library(\n    name = \"parameter_sync_cc_grpc\",\n    srcs = [\":parameter_sync_proto\"],\n    grpc_only = True,\n    deps = [\":parameter_sync_cc_proto\"],\n)\n\ncc_library(\n    name = \"parameter_sync_client\",\n    srcs = [\"parameter_sync_client.cc\"],\n    hdrs = [\"parameter_sync_client.h\"],\n    deps = [\n        \":parameter_sync_cc_grpc\",\n        \":sync_client_interface\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_glog//:glog\",\n        \"@org_tensorflow//tensorflow/core/platform:logging\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_service_proto\",\n    ],\n)\n\ntf_cc_test(\n    name = \"parameter_sync_client_test\",\n    srcs = [\"parameter_sync_client_test.cc\"],\n    deps = [\n        \":parameter_sync_client\",\n        \"@com_google_googletest//:gtest_main\",\n        \"@org_tensorflow//tensorflow/core:test\",\n    ],\n)\n\ncc_library(\n    name = \"dummy_sync_server\",\n    srcs = [\"dummy_sync_server.cc\"],\n    hdrs = [\"dummy_sync_server.h\"],\n    deps = [\n        \":parameter_sync_cc_grpc\",\n        \"@com_github_grpc_grpc//:grpc++\",\n        \"@com_github_grpc_grpc//:grpc++_reflection\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_glog//:glog\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_service_proto\",\n    ],\n)\n\ncc_library(\n    name = \"request_splitter\",\n    srcs = [\"request_splitter.cc\"],\n    hdrs = [\"request_splitter.h\"],\n    deps = [\n        \":parameter_sync_cc_grpc\",\n        \"@com_github_grpc_grpc//:grpc++\",\n        \"@com_github_grpc_grpc//:grpc++_reflection\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_glog//:glog\",\n    ],\n)\n\ncc_test(\n    name = \"request_splitter_test\",\n    srcs = [\"request_splitter_test.cc\"],\n    deps = [\n        \":request_splitter\",\n        \"@com_google_googletest//:gtest_main\",\n    ],\n)\n\ncc_library(\n    name = \"sync_client_manager\",\n    srcs = [\"sync_client_manager.cc\"],\n    hdrs = [\"sync_client_manager.h\"],\n    deps = [\n        \":parameter_sync_cc_grpc\",\n        \":sync_client_interface\",\n        \"//monolith/native_training/runtime/common:metrics\",\n        \"//monolith/native_training/runtime/parameter_sync:parameter_sync_client\",\n        \"//monolith/native_training/runtime/parameter_sync:request_splitter\",\n        \"@com_google_absl//absl/strings:str_format\",\n        \"@com_google_absl//absl/synchronization\",\n        \"@com_google_glog//:glog\",\n        \"@org_tensorflow//tensorflow/core/platform:logging\",\n        \"@org_tensorflow_serving//tensorflow_serving/apis:prediction_service_proto\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/dummy_sync_client.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_DUMMY_SYNC_CLIENT_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_DUMMY_SYNC_CLIENT_H_\n\n#include \"monolith/native_training/runtime/parameter_sync/sync_client_interface.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\n\nclass DummySyncClient : public SyncClientInterface {\n public:\n  explicit DummySyncClient(const std::string& target) {}\n\n  grpc::Status Push(const PushRequest&, PushResponse*) const override {\n    return grpc::Status::OK;\n  }\n};\n\n}  // namespace parameter_sync\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_DUMMY_SYNC_CLIENT_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/dummy_sync_server.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/parameter_sync/dummy_sync_server.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\n\nusing grpc::Server;\nusing grpc::ServerBuilder;\nusing grpc::ServerContext;\nusing grpc::Status;\n\nDummySyncServer::DummySyncServer(std::string target)\n    : target_(std::move(target)), selected_port_(0) {\n  grpc::EnableDefaultHealthCheckService(true);\n  grpc::reflection::InitProtoReflectionServerBuilderPlugin();\n  ServerBuilder builder;\n\n  // Listen on the given address without any authentication mechanism.\n  builder.AddListeningPort(target_, grpc::InsecureServerCredentials(),\n                           &selected_port_);\n\n  // Register \"service\" as the instance through which we'll communicate with\n  // clients. In this case it corresponds to an *synchronous* service.\n  builder.RegisterService(&service_);\n\n  // Finally assemble the server.\n  server_ = builder.BuildAndStart();\n  LOG(INFO) << \"Server listening on \" << target_ << \", selecting port \"\n            << selected_port_ << std::endl;\n}\n\nvoid DummySyncServer::Shutdown() const { server_->Shutdown(); }\n\nconst std::string& DummySyncServer::GetTarget() const { return target_; }\n\nint DummySyncServer::GetSelectedPort() const { return selected_port_; }\n\n}  // namespace parameter_sync\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/dummy_sync_server.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_DUMMY_SYNC_SERVER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_DUMMY_SYNC_SERVER_H_\n\n#include \"absl/strings/match.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_join.h\"\n#include \"glog/logging.h\"\n#include \"grpcpp/ext/proto_server_reflection_plugin.h\"\n#include \"grpcpp/grpcpp.h\"\n#include \"grpcpp/health_check_service_interface.h\"\n#include \"tensorflow_serving/apis/prediction_service.grpc.pb.h\"\n\n#include \"monolith/native_training/runtime/parameter_sync/parameter_sync.grpc.pb.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\n\n// Test only\nclass PredictionServiceImpl final\n    : public tensorflow::serving::PredictionService::Service {\n  grpc::Status Predict(\n      grpc::ServerContext* context,\n      const tensorflow::serving::PredictRequest* request,\n      tensorflow::serving::PredictResponse* response) override {\n    // TODO(zhangbiao.david): remove\n    LOG(INFO) << \"PredictionServiceImpl\" << std::endl;\n    for (const auto& kv : request->inputs()) {\n      std::vector<std::string> output;\n      if (absl::EndsWith(kv.first, \"_id\")) {\n        std::transform(kv.second.int64_val().begin(),\n                       kv.second.int64_val().end(), std::back_inserter(output),\n                       [](int64_t id) { return std::to_string(id); });\n      } else if (absl::EndsWith(kv.first, \"_value\")) {\n        std::transform(kv.second.float_val().begin(),\n                       kv.second.float_val().end(), std::back_inserter(output),\n                       [](float value) { return std::to_string(value); });\n      } else {\n        LOG(FATAL) << \"Inputs' key should end with '_id' or '_value'\";\n      }\n\n      LOG(INFO) << absl::StrFormat(\"%s: %s\", kv.first,\n                                   absl::StrJoin(output, \" \"));\n    }\n\n    response->mutable_model_spec()->CopyFrom(request->model_spec());\n    return grpc::Status::OK;\n  }\n};\n\nclass DummySyncServer {\n public:\n  explicit DummySyncServer(std::string target);\n\n  void Shutdown() const;\n\n  const std::string& GetTarget() const;\n\n  int GetSelectedPort() const;\n\n private:\n  std::string target_;\n\n  int selected_port_;\n\n  PredictionServiceImpl service_;\n\n  std::unique_ptr<grpc::Server> server_;\n};\n\n}  // namespace parameter_sync\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_DUMMY_SYNC_SERVER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/parameter_sync.proto",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\nsyntax = \"proto2\";\n\npackage monolith.parameter_sync;\n\n\n// The request message containing delta fids and embeddings.\nmessage PushRequest {\n  message DeltaEmbeddingHashTable {\n    optional string unique_id = 1;\n    optional int32 dim_size = 2;\n    repeated int64 fids = 3;\n    repeated float embeddings = 4;\n  }\n\n  optional string model_name = 1;\n\n  optional string signature_name = 2;\n\n  repeated DeltaEmbeddingHashTable delta_hash_tables = 3;\n\n  // The embedding changes from multi hash tables\n  // The size of this field should equal to number of hash tables included in\n  // multi hash table.\n  repeated DeltaEmbeddingHashTable delta_multi_hash_tables = 5;\n\n  optional int64 timeout_in_ms = 4 [default = 1000];\n}\n\n// The response message\nmessage PushResponse {\n  // gRPC's server address\n  optional string target = 3;\n\n  // gRPC's status code, 0 means OK.\n  optional int32 status_code = 1;\n\n  // gRPC's error message\n  optional string error_message = 2;\n\n  // Number of fids successfully assigned\n  optional int32 update_num = 4;\n}\n\nmessage PushResult {\n  repeated PushResponse responses = 1;\n}\n\nmessage ClientConfig {\n  optional string model_name = 1;\n\n  optional string signature_name = 2;\n\n  repeated string targets = 3;\n\n  optional int64 timeout_in_ms = 4 [default = 1000];\n\n  message TargetExtraInfo {\n    optional string idc = 1;\n    optional string cluster = 2;\n    optional int64 replica_id = 3 [default = -1];\n  }\n  repeated TargetExtraInfo targets_extra_info = 5;\n}"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/parameter_sync_client.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/parameter_sync/parameter_sync_client.h\"\n\n#include \"absl/strings/match.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_join.h\"\n#include \"grpc/impl/codegen/gpr_types.h\"\n#include \"tensorflow/core/platform/default/logging.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\n\nusing grpc::Channel;\nusing grpc::ClientContext;\nusing grpc::Status;\nusing tensorflow::serving::PredictionService;\nusing tensorflow::serving::PredictRequest;\nusing tensorflow::serving::PredictResponse;\n\nParameterSyncClient::ConvertResult ParameterSyncClient::Convert(\n    const PushRequest& request) {\n  ConvertResult result;\n  PredictRequest& predict_request = result.req;\n  predict_request.mutable_model_spec()->set_name(request.model_name());\n  predict_request.mutable_model_spec()->set_signature_name(\n      request.signature_name());\n  auto& inputs = *predict_request.mutable_inputs();\n  int& total = result.total;\n  if (request.delta_multi_hash_tables_size() > 0) {\n    tensorflow::TensorProto id_tensor, id_split_tensor, emb_tensor;\n    id_tensor.set_dtype(tensorflow::DataType::DT_INT64);\n    id_split_tensor.set_dtype(tensorflow::DataType::DT_INT64);\n    emb_tensor.set_dtype(tensorflow::DataType::DT_FLOAT);\n    int64_t split = 0;\n    id_split_tensor.add_int64_val(split);\n    for (const PushRequest::DeltaEmbeddingHashTable& table :\n         request.delta_multi_hash_tables()) {\n      total += table.fids_size();\n      split += table.fids_size();\n      id_split_tensor.add_int64_val(split);\n      for (int64_t id : table.fids()) {\n        id_tensor.add_int64_val(id);\n      }\n      for (float value : table.embeddings()) {\n        emb_tensor.add_float_val(value);\n      }\n    }\n    id_tensor.mutable_tensor_shape()->add_dim()->set_size(\n        id_tensor.int64_val_size());\n    id_split_tensor.mutable_tensor_shape()->add_dim()->set_size(\n        id_split_tensor.int64_val_size());\n    emb_tensor.mutable_tensor_shape()->add_dim()->set_size(\n        emb_tensor.float_val_size());\n    // names here should match what we write in `saved_model_exporters`\n    inputs[\"id\"] = std::move(id_tensor);\n    inputs[\"id_split\"] = std::move(id_split_tensor);\n    inputs[\"flat_value\"] = std::move(emb_tensor);\n  } else {\n    for (const auto& delta : request.delta_hash_tables()) {\n      int num_update = delta.fids().size();\n      total += num_update;\n      tensorflow::TensorProto proto_fid, proto_emb;\n      proto_fid.set_dtype(tensorflow::DataType::DT_INT64);\n      proto_emb.set_dtype(tensorflow::DataType::DT_FLOAT);\n      for (int64_t id : delta.fids()) {\n        proto_fid.add_int64_val(id);\n      }\n      for (float value : delta.embeddings()) {\n        proto_emb.add_float_val(value);\n      }\n\n      int dimension = delta.dim_size();\n      proto_fid.mutable_tensor_shape()->add_dim()->set_size(num_update);\n      proto_emb.mutable_tensor_shape()->add_dim()->set_size(num_update);\n      proto_emb.mutable_tensor_shape()->add_dim()->set_size(dimension);\n      inputs[delta.unique_id() + \"_id\"] = proto_fid;\n      inputs[delta.unique_id() + \"_value\"] = proto_emb;\n    }\n  }\n  return result;\n}\n\ngrpc::Status ParameterSyncClient::Push(const PushRequest& request,\n                                       PushResponse* response) const {\n  // Context for the client. It could be used to convey extra information to\n  // the server and/or tweak certain RPC behaviors.\n  ConvertResult convert_result = Convert(request);\n  const PredictRequest& predict_request = convert_result.req;\n  PredictResponse predict_response;\n  ClientContext context;\n  gpr_timespec ts;\n  ts.tv_sec = request.timeout_in_ms() / 1000;\n  ts.tv_nsec = (request.timeout_in_ms() % 1000) * 1000 * 1000;\n  ts.clock_type = GPR_TIMESPAN;\n  context.set_deadline(ts);\n\n  // TODO(zhangbiao.david): predict_request.DebugString() causes a segment\n  //  fault, but I have no idea about it.\n  // LOG(INFO) << \"PredictRequest\\n\" << predict_request.DebugString() <<\n  // std::endl;\n\n  // The actual RPC.\n  Status status;\n  status = stub_->Predict(&context, predict_request, &predict_response);\n  response->set_status_code(status.error_code());\n  response->set_error_message(status.error_message());\n\n  // Act upon its status.\n  if (status.ok()) {\n    response->set_update_num(convert_result.total);\n    return status;\n  } else {\n    response->set_update_num(0);\n    LOG_EVERY_N_SEC(ERROR, 10) << status.error_code() << \": \"\n                               << status.error_message();\n    return status;\n  }\n}\n\n}  // namespace parameter_sync\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/parameter_sync_client.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_PARAMETER_SYNC_CLIENT_H_\n#define MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_PARAMETER_SYNC_CLIENT_H_\n\n#include \"glog/logging.h\"\n#include \"grpcpp/grpcpp.h\"\n#include \"monolith/native_training/runtime/parameter_sync/parameter_sync.grpc.pb.h\"\n#include \"monolith/native_training/runtime/parameter_sync/sync_client_interface.h\"\n#include \"tensorflow_serving/apis/prediction_service.grpc.pb.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\n\nclass ParameterSyncClient final : public SyncClientInterface {\n public:\n  explicit ParameterSyncClient(std::string target)\n      : ParameterSyncClient(CreateStub(target)) {\n    target_ = std::move(target);\n  }\n\n  explicit ParameterSyncClient(\n      std::unique_ptr<tensorflow::serving::PredictionService::StubInterface>\n          stub)\n      : stub_(std::move(stub)) {}\n\n  // Assembles the client's payload, sends it and presents the response back\n  // from the server.\n  grpc::Status Push(const PushRequest& request,\n                    PushResponse* response) const override;\n\n  // Ideally we should mock stub to simulate the behavior of this class.\n  // However, there are some problems to generate mock class.\n  // We just verify request conversion here.\n  struct ConvertResult {\n    tensorflow::serving::PredictRequest req;\n    int total = 0;\n  };\n  static ConvertResult Convert(const PushRequest& req);\n\n private:\n  static std::unique_ptr<tensorflow::serving::PredictionService::Stub>\n  CreateStub(const std::string& target) {\n    // 32M\n    const int MAX_MESSAGE_LENGTH = 32 * 1024 * 1024;\n    grpc::ChannelArguments arguments;\n    arguments.SetMaxSendMessageSize(MAX_MESSAGE_LENGTH);\n    arguments.SetMaxReceiveMessageSize(MAX_MESSAGE_LENGTH);\n    auto channel = grpc::CreateCustomChannel(\n        target, grpc::InsecureChannelCredentials(), arguments);\n    return tensorflow::serving::PredictionService::NewStub(channel);\n  }\n\n  std::string target_;\n\n  std::unique_ptr<tensorflow::serving::PredictionService::StubInterface> stub_;\n};\n\n}  // namespace parameter_sync\n}  // namespace monolith\n\n#endif  // MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_PARAMETER_SYNC_CLIENT_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/parameter_sync_client_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/parameter_sync/parameter_sync_client.h\"\n\n#include \"gmock/gmock.h\"\n#include \"google/protobuf/text_format.h\"\n#include \"google/protobuf/util/message_differencer.h\"\n#include \"grpcpp/grpcpp.h\"\n#include \"gtest/gtest.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\nnamespace {\n\nusing ::tensorflow::serving::PredictRequest;\n\nTEST(MultiHashTableTest, Basic) {\n  PushRequest req;\n  ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(R\"(\n    model_name: \"test_model\"\n    signature_name: \"table/raw_assign\"\n    delta_multi_hash_tables: [\n      {\n        fids: [1, 2]\n        embeddings: [1.0, 2.0, 3.0, 4.0]\n      },\n      {\n        fids: [3]\n        embeddings: [5.0]\n      }\n    ]\n  )\",\n                                                            &req));\n  auto result = ParameterSyncClient::Convert(req);\n  PredictRequest expected_predict_req;\n  ASSERT_TRUE(\n      google::protobuf::TextFormat::ParseFromString(R\"(\n    model_spec {\n      name: \"test_model\"\n      signature_name: \"table/raw_assign\"\n    }\n    inputs {\n      key: \"flat_value\"\n      value {\n        dtype: DT_FLOAT\n        tensor_shape {\n          dim {\n            size: 5\n          }\n        }\n        float_val: [1.0, 2.0, 3.0, 4.0, 5.0]\n      }\n    }\n    inputs {\n      key: \"id\"\n      value {\n        dtype: DT_INT64\n        tensor_shape {\n          dim {\n            size: 3\n          }\n        }\n        int64_val: [1, 2, 3]\n      }\n    }\n    inputs {\n      key: \"id_split\"\n      value {\n        dtype: DT_INT64\n        tensor_shape {\n          dim {\n            size: 3\n          }\n        }\n        int64_val: [0, 2, 3]\n      }\n    }\n  )\",\n                                                    &expected_predict_req));\n  EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(\n      result.req, expected_predict_req));\n}\n\nTEST(HashTableTest, Basic) {\n  PushRequest req;\n  ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(R\"(\n    model_name: \"test_model\"\n    signature_name: \"hashtable_assign\"\n    delta_hash_tables: [\n      {\n        unique_id: \"table1\"\n        dim_size: 2\n        fids: [1, 2]\n        embeddings: [1.0, 2.0, 3.0, 4.0]\n      },\n      {\n        unique_id: \"table2\"\n        dim_size: 1\n        fids: [3]\n        embeddings: [5.0]\n      }\n    ]\n  )\",\n                                                            &req));\n  auto result = ParameterSyncClient::Convert(req);\n  PredictRequest expected_predict_req;\n  ASSERT_TRUE(\n      google::protobuf::TextFormat::ParseFromString(R\"(\n    model_spec {\n      name: \"test_model\"\n      signature_name: \"hashtable_assign\"\n    }\n    inputs {\n      key: \"table1_id\"\n      value {\n        dtype: DT_INT64\n        tensor_shape {\n          dim {\n            size: 2\n          }\n        }\n        int64_val: [1, 2]\n      }\n    }\n    inputs {\n      key: \"table1_value\"\n      value {\n        dtype: DT_FLOAT\n        tensor_shape {\n          dim {\n            size: 2\n          }\n          dim {\n            size: 2\n          }\n        }\n        float_val: [1.0, 2.0, 3.0, 4.0]\n      }\n    }\n    inputs {\n      key: \"table2_id\"\n      value {\n        dtype: DT_INT64\n        tensor_shape {\n          dim {\n            size: 1\n          }\n        }\n        int64_val: [3]\n      }\n    }\n    inputs {\n      key: \"table2_value\"\n      value {\n        dtype: DT_FLOAT\n        tensor_shape {\n          dim {\n            size: 1\n          }\n          dim {\n            size: 1\n          }\n        }\n        float_val: [5.0]\n      }\n    }\n  )\",\n                                                    &expected_predict_req));\n  EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals(\n      result.req, expected_predict_req));\n}\n\n}  // namespace\n}  // namespace parameter_sync\n}  // namespace monolith"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/request_splitter.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/parameter_sync/request_splitter.h\"\n\n#include \"glog/logging.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\nnamespace {\n\nvoid SplitTable(const PushRequest::DeltaEmbeddingHashTable& table,\n                int split_num, int i,\n                PushRequest::DeltaEmbeddingHashTable* target_table) {\n  size_t delta_size = table.fids_size();\n  int dim_size = table.dim_size();\n  size_t q = delta_size / split_num;\n  size_t part_size = i + 1 == split_num ? q + delta_size % split_num : q;\n  target_table->set_unique_id(table.unique_id());\n  target_table->set_dim_size(table.dim_size());\n  auto* mutable_fids = target_table->mutable_fids();\n  auto* mutable_embeddings = target_table->mutable_embeddings();\n\n  // TODO(leqi.zou): This seems not very mem efficient.\n  for (size_t j = 0; j < part_size; ++j) {\n    int index = i * q + j;\n    int64_t fid = table.fids(index);\n    mutable_fids->Add(fid);\n    const float* embedding = table.embeddings().data() + index * dim_size;\n    mutable_embeddings->Add(embedding, embedding + dim_size);\n  }\n}\n\n}  // namespace\n\nstd::vector<PushRequest> RequestSplitter::Split(\n    const PushRequest& push_request, int64_t max_message_length) const {\n  DCHECK_GT(max_message_length, 0);\n  size_t byte_size = push_request.ByteSizeLong();\n  if (byte_size <= max_message_length) {\n    return {push_request};\n  }\n\n  size_t split_num = (byte_size + max_message_length - 1) / max_message_length;\n  const std::string& model_name = push_request.model_name();\n  const std::string& signature_name = push_request.signature_name();\n  int64_t timeout_in_ms = push_request.timeout_in_ms();\n\n  std::vector<PushRequest> requests;\n  requests.reserve(split_num);\n  for (size_t i = 0; i < split_num; ++i) {\n    requests.emplace_back();\n    PushRequest* request = &requests.back();\n    request->set_model_name(model_name);\n    request->set_signature_name(signature_name);\n    request->mutable_delta_hash_tables()->Reserve(\n        push_request.delta_hash_tables_size());\n    request->mutable_delta_multi_hash_tables()->Reserve(\n        push_request.delta_multi_hash_tables_size());\n    request->set_timeout_in_ms(timeout_in_ms);\n\n    for (const auto& table : push_request.delta_hash_tables()) {\n      auto* delta_hash_table = request->mutable_delta_hash_tables()->Add();\n      SplitTable(table, split_num, i, delta_hash_table);\n    }\n\n    for (const auto& table : push_request.delta_multi_hash_tables()) {\n      auto* delta_multi_hash_table =\n          request->mutable_delta_multi_hash_tables()->Add();\n      SplitTable(table, split_num, i, delta_multi_hash_table);\n    }\n  }\n\n  return requests;\n}\n}  // namespace parameter_sync\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/request_splitter.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_REQUEST_SPLITTER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_REQUEST_SPLITTER_H_\n\n#include <vector>\n\n#include \"monolith/native_training/runtime/parameter_sync/parameter_sync.grpc.pb.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\n\nclass RequestSplitter {\n public:\n  std::vector<PushRequest> Split(const PushRequest& push_request,\n                                 int64_t max_message_length) const;\n};\n\n}  // namespace parameter_sync\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_REQUEST_SPLITTER_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/request_splitter_test.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/parameter_sync/request_splitter.h\"\n\n#include <numeric>\n\n#include \"glog/logging.h\"\n#include \"google/protobuf/util/message_differencer.h\"\n#include \"gtest/gtest.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\nnamespace {\n\nusing ::google::protobuf::util::MessageDifferencer;\n\nPushRequest_DeltaEmbeddingHashTable SetUpOneDeltaHashTable(\n    const std::string& unique_id, size_t fid_num, size_t dim,\n    int fid_value_offset = 0) {\n  PushRequest_DeltaEmbeddingHashTable table;\n  table.set_unique_id(unique_id);\n  table.set_dim_size(dim);\n\n  std::vector<int64_t> fids(fid_num);\n  std::iota(fids.begin(), fids.end(), fid_value_offset);\n  std::vector<float> embeddings(fid_num * dim);\n  for (size_t i = 0; i < fid_num; ++i) {\n    for (size_t j = 0; j < dim; ++j) {\n      embeddings[i * dim + j] = static_cast<float>(i + fid_value_offset);\n    }\n  }\n\n  table.mutable_fids()->Add(fids.begin(), fids.end());\n  table.mutable_embeddings()->Add(embeddings.begin(), embeddings.end());\n  return table;\n}\n\nTEST(RequestSplitter, NoSplit) {\n  PushRequest request;\n  request.set_model_name(\"hello\");\n  request.set_signature_name(\"hashtable_assign\");\n  auto t0 = SetUpOneDeltaHashTable(\"table0\", 0, 1);\n  auto t1 = SetUpOneDeltaHashTable(\"table1\", 2, 1);\n  auto t2 = SetUpOneDeltaHashTable(\"table2\", 3, 2);\n  request.mutable_delta_hash_tables()->Add(std::move(t0));\n  request.mutable_delta_hash_tables()->Add(std::move(t1));\n  request.mutable_delta_hash_tables()->Add(std::move(t2));\n\n  RequestSplitter splitter;\n  std::vector<PushRequest> requests = splitter.Split(request, 4 * 1024 * 1024);\n  EXPECT_EQ(requests.size(), 1);\n  EXPECT_TRUE(MessageDifferencer::Equals(request, requests.front()));\n}\n\n// Test case(byte size = 111)\nTEST(RequestSplitter, SplitIntoTwo) {\n  PushRequest request, request1, request2;\n  request.set_model_name(\"hello\");\n  request.set_signature_name(\"hashtable_assign\");\n  auto t0 = SetUpOneDeltaHashTable(\"table0\", 0, 1);\n  auto t1 = SetUpOneDeltaHashTable(\"table1\", 2, 1);\n  auto t2 = SetUpOneDeltaHashTable(\"table2\", 3, 2);\n  request.mutable_delta_hash_tables()->Add(std::move(t0));\n  request.mutable_delta_hash_tables()->Add(std::move(t1));\n  request.mutable_delta_hash_tables()->Add(std::move(t2));\n  request.set_timeout_in_ms(100);\n\n  RequestSplitter splitter;\n  std::vector<PushRequest> requests = splitter.Split(request, 60);\n  EXPECT_EQ(requests.size(), 2);\n\n  // part 1\n  request1.set_model_name(\"hello\");\n  request1.set_signature_name(\"hashtable_assign\");\n  t0 = SetUpOneDeltaHashTable(\"table0\", 0, 1);\n  t1 = SetUpOneDeltaHashTable(\"table1\", 1, 1);\n  t2 = SetUpOneDeltaHashTable(\"table2\", 1, 2);\n  request1.mutable_delta_hash_tables()->Add(std::move(t0));\n  request1.mutable_delta_hash_tables()->Add(std::move(t1));\n  request1.mutable_delta_hash_tables()->Add(std::move(t2));\n  request1.set_timeout_in_ms(100);\n  EXPECT_TRUE(MessageDifferencer::Equals(requests.front(), request1));\n\n  // part 2\n  request2.set_model_name(\"hello\");\n  request2.set_signature_name(\"hashtable_assign\");\n  t0 = SetUpOneDeltaHashTable(\"table0\", 0, 1);\n  t1 = SetUpOneDeltaHashTable(\"table1\", 1, 1, 1.0f);\n  t2 = SetUpOneDeltaHashTable(\"table2\", 2, 2, 1.0f);\n  request2.mutable_delta_hash_tables()->Add(std::move(t0));\n  request2.mutable_delta_hash_tables()->Add(std::move(t1));\n  request2.mutable_delta_hash_tables()->Add(std::move(t2));\n  request2.set_timeout_in_ms(100);\n  EXPECT_TRUE(MessageDifferencer::Equals(requests.back(), request2));\n}\n\n// Test case(byte size = 113)\nTEST(RequestSplitter, MultiHashTableSplitIntoTwo) {\n  PushRequest request, request1, request2;\n  request.set_model_name(\"hello\");\n  request.set_signature_name(\"table/raw_assign\");\n  auto t0 = SetUpOneDeltaHashTable(\"table0\", 0, 1);\n  auto t1 = SetUpOneDeltaHashTable(\"table1\", 2, 1);\n  auto t2 = SetUpOneDeltaHashTable(\"table2\", 3, 2);\n  request.mutable_delta_multi_hash_tables()->Add(std::move(t0));\n  request.mutable_delta_multi_hash_tables()->Add(std::move(t1));\n  request.mutable_delta_multi_hash_tables()->Add(std::move(t2));\n  request.set_timeout_in_ms(100);\n\n  RequestSplitter splitter;\n  std::vector<PushRequest> requests = splitter.Split(request, 60);\n  EXPECT_EQ(requests.size(), 2);\n\n  // part 1\n  request1.set_model_name(\"hello\");\n  request1.set_signature_name(\"table/raw_assign\");\n  t0 = SetUpOneDeltaHashTable(\"table0\", 0, 1);\n  t1 = SetUpOneDeltaHashTable(\"table1\", 1, 1);\n  t2 = SetUpOneDeltaHashTable(\"table2\", 1, 2);\n  request1.mutable_delta_multi_hash_tables()->Add(std::move(t0));\n  request1.mutable_delta_multi_hash_tables()->Add(std::move(t1));\n  request1.mutable_delta_multi_hash_tables()->Add(std::move(t2));\n  request1.set_timeout_in_ms(100);\n  EXPECT_TRUE(MessageDifferencer::Equals(requests[0], request1));\n\n  // part 2\n  request2.set_model_name(\"hello\");\n  request2.set_signature_name(\"table/raw_assign\");\n  t0 = SetUpOneDeltaHashTable(\"table0\", 0, 1);\n  t1 = SetUpOneDeltaHashTable(\"table1\", 1, 1, 1.0f);\n  t2 = SetUpOneDeltaHashTable(\"table2\", 2, 2, 1.0f);\n  request2.mutable_delta_multi_hash_tables()->Add(std::move(t0));\n  request2.mutable_delta_multi_hash_tables()->Add(std::move(t1));\n  request2.mutable_delta_multi_hash_tables()->Add(std::move(t2));\n  request2.set_timeout_in_ms(100);\n  EXPECT_TRUE(MessageDifferencer::Equals(requests[1], request2));\n}\n\n}  // namespace\n}  // namespace parameter_sync\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/sync_client_interface.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_SYNC_CLIENT_INTERFACE_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_SYNC_CLIENT_INTERFACE_H_\n\n#include \"grpcpp/grpcpp.h\"\n#include \"monolith/native_training/runtime/parameter_sync/parameter_sync.pb.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\n\nclass SyncClientInterface {\n public:\n  virtual grpc::Status Push(const PushRequest&, PushResponse*) const = 0;\n};\n\n}  // namespace parameter_sync\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_SYNC_CLIENT_INTERFACE_H_\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/sync_client_manager.cc",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#include \"monolith/native_training/runtime/parameter_sync/sync_client_manager.h\"\n\n#include \"absl/strings/match.h\"\n#include \"absl/strings/str_format.h\"\n#include \"absl/strings/str_join.h\"\n#include \"glog/logging.h\"\n#include \"tensorflow/core/platform/default/logging.h\"\n\n#include \"monolith/native_training/runtime/common/metrics.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\n\n// 4M\nconst int MAX_MESSAGE_LENGTH = 4 * 1024 * 1024;\n\nSyncClientManager::SyncClientManager(\n    std::function<std::unique_ptr<SyncClientInterface>(const std::string&)>\n        client_factory)\n    : client_factory_(std::move(client_factory)) {}\n\nstd::string SyncClientManager::PushRequestDebugString(\n    const PushRequest& request, int index, int total) const {\n  std::vector<std::string> delta_hash_table_info;\n  delta_hash_table_info.reserve(request.delta_hash_tables_size());\n  std::string prefix = \"MonolithHashTable_\";\n  for (const auto& table : request.delta_hash_tables()) {\n    std::string simple_id = table.unique_id();\n    if (absl::StartsWith(simple_id, prefix)) {\n      simple_id = simple_id.substr(prefix.length());\n    }\n    delta_hash_table_info.push_back(absl::StrFormat(\n        \"(unique_id: %s, fid_num: %d)\", simple_id, table.fids().size()));\n  }\n\n  for (const auto& table : request.delta_multi_hash_tables()) {\n    std::string simple_id = table.unique_id();\n    delta_hash_table_info.push_back(absl::StrFormat(\n        \"(unique_id: %s, fid_num: %d)\", simple_id, table.fids().size()));\n  }\n\n  return absl::StrFormat(\n      \"PushRequest[%d/%d]: model_name = %s, signature_name = %s, \"\n      \"delta_hash_table = [%s]\",\n      index, total, request.model_name(), request.signature_name(),\n      absl::StrJoin(delta_hash_table_info, \", \"));\n}\n\nPushResult SyncClientManager::Push(const PushRequest& request,\n                                   const std::string& model_name,\n                                   const std::string& signature_name) const {\n  LOG_EVERY_N_SEC(INFO, 60) << PushRequestDebugString(request, -1, -1);\n  std::vector<PushRequest> requests =\n      request_splitter_.Split(request, MAX_MESSAGE_LENGTH);\n  int total = static_cast<int>(requests.size());\n  std::vector<std::string> debug_string(total);\n  auto split_log = [&]() {\n    for (int i = 0; i < total; ++i) {\n      debug_string[i] = PushRequestDebugString(requests[i], i, total);\n    }\n    return absl::StrJoin(debug_string, \"\\n\");\n  };\n  LOG_EVERY_N_SEC(INFO, 60) << split_log();\n\n  std::vector<int64_t> request_fid_count;\n  request_fid_count.reserve(requests.size());\n  std::transform(requests.begin(), requests.end(),\n                 std::back_inserter(request_fid_count),\n                 [](const PushRequest& req) {\n                   int64_t count = 0;\n                   for (const auto& t : req.delta_hash_tables()) {\n                     count += t.fids_size();\n                   }\n                   for (const auto& t : req.delta_multi_hash_tables()) {\n                     count += t.fids_size();\n                   }\n                   return count;\n                 });\n\n  auto MakeTagKV = [&](const std::string& target, const std::string& status) {\n    auto it = caddr_to_extra_info_map_.find(target);\n    int replica_id = -1;\n    std::string idc_cluster = \"NA\";\n    if (it != caddr_to_extra_info_map_.end()) {\n      replica_id = it->second.replica_id();\n      idc_cluster =\n          absl::StrFormat(\"%s:%s\", it->second.idc(), it->second.cluster());\n    }\n    return absl::StrFormat(\n        \"model_name=%s|signature_name=%s|target=%s|status=%s|idc=%s|replica_id=\"\n        \"%d\",\n        model_name, signature_name, target, status, idc_cluster, replica_id);\n  };\n\n  PushResult result;\n  {\n    absl::ReaderMutexLock l(&mu_);\n    for (const auto& kv : clients_) {\n      std::unordered_map<std::string, int64_t> fid_count = {{\"OK\", 0},\n                                                            {\"KO\", 0}};\n      std::unordered_map<std::string, int64_t> byte_size = {{\"OK\", 0},\n                                                            {\"KO\", 0}};\n      for (size_t i = 0; i < requests.size(); ++i) {\n        const auto& req = requests[i];\n        auto response = result.add_responses();\n        if (!req.delta_hash_tables().empty() ||\n            !req.delta_multi_hash_tables().empty()) {\n          int64_t start = absl::ToUnixMicros(absl::Now());\n          auto status = kv.second->Push(req, response);\n          int64_t end = absl::ToUnixMicros(absl::Now());\n          std::string status_key = status.ok() ? \"OK\" : \"KO\";\n          std::string tag_kv = MakeTagKV(kv.first, status_key);\n          monolith::GetMetrics()->emit_timer(\"parameter_sync_latency\",\n                                             end - start, tag_kv);\n          fid_count[status_key] += request_fid_count[i];\n          byte_size[status_key] += static_cast<int64_t>(req.ByteSizeLong());\n        }\n        response->set_target(kv.first);\n      }\n\n      for (const auto& p : fid_count) {\n        if (p.second) {\n          std::string tag_kv = MakeTagKV(kv.first, p.first);\n          monolith::GetMetrics()->emit_counter(\"parameter_sync_fid_count\",\n                                               p.second, tag_kv);\n        }\n      }\n      for (const auto& p : byte_size) {\n        if (p.second) {\n          std::string tag_kv = MakeTagKV(kv.first, p.first);\n          monolith::GetMetrics()->emit_counter(\"parameter_sync_byte_size\",\n                                               p.second, tag_kv);\n        }\n      }\n    }\n  }\n\n  return result;\n}\n\nbool SyncClientManager::TryReplace(\n    const google::protobuf::RepeatedPtrField<std::string>& targets,\n    const google::protobuf::RepeatedPtrField<\n        monolith::parameter_sync::ClientConfig_TargetExtraInfo>&\n        targets_extra_info) {\n  absl::WriterMutexLock l(&mu_);\n  std::unordered_set<std::string> unique_targets;\n  for (const auto& target : targets) {\n    unique_targets.insert(target);\n  }\n  caddr_to_extra_info_map_.clear();\n  if (targets.size() == targets_extra_info.size()) {\n    for (int i = 0; i < targets.size(); i++) {\n      caddr_to_extra_info_map_.emplace(targets[i], targets_extra_info[i]);\n    }\n  }\n\n  // Remove invalid targets\n  std::vector<std::string> invalid_targets;\n  for (const auto& kv : clients_) {\n    if (!unique_targets.count(kv.first)) {\n      invalid_targets.emplace_back(kv.first);\n    }\n  }\n  for (const auto& target : invalid_targets) {\n    clients_.erase(target);\n  }\n\n  // Add new targets\n  for (const auto& target : unique_targets) {\n    if (!clients_.count(target)) {\n      auto client = client_factory_(target);\n      clients_[target] = std::move(client);\n    }\n  }\n\n  return true;\n}\n\n}  // namespace parameter_sync\n}  // namespace monolith\n"
  },
  {
    "path": "monolith/native_training/runtime/parameter_sync/sync_client_manager.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n#ifndef MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_SYNC_CLIENT_MANAGER_H_\n#define MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_SYNC_CLIENT_MANAGER_H_\n\n#include \"absl/synchronization/mutex.h\"\n\n#include \"monolith/native_training/runtime/parameter_sync/request_splitter.h\"\n#include \"monolith/native_training/runtime/parameter_sync/sync_client_interface.h\"\n\nnamespace monolith {\nnamespace parameter_sync {\n\nclass SyncClientManager {\n public:\n  SyncClientManager(\n      std::function<std::unique_ptr<SyncClientInterface>(const std::string&)>\n          client_factory);\n\n  PushResult Push(const PushRequest& request, const std::string& model_name,\n                  const std::string& signature_name) const\n      ABSL_SHARED_LOCKS_REQUIRED(mu_);\n\n  bool TryReplace(\n      const google::protobuf::RepeatedPtrField<std::string>& targets,\n      const google::protobuf::RepeatedPtrField<\n          monolith::parameter_sync::ClientConfig_TargetExtraInfo>&\n          targets_extra_info) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);\n\n private:\n  std::string PushRequestDebugString(const PushRequest& request, int index,\n                                     int total) const;\n\n private:\n  RequestSplitter request_splitter_;\n\n  // We create an individual ParameterSyncClient for each target, which is an\n  // online ps shard replica. Typically, each target has corresponding hash\n  // tables like hash_tables_.\n  std::map<std::string, std::unique_ptr<SyncClientInterface>> clients_\n      ABSL_GUARDED_BY(mu_);\n\n  std::map<std::string, monolith::parameter_sync::ClientConfig_TargetExtraInfo>\n      caddr_to_extra_info_map_ ABSL_GUARDED_BY(mu_);\n\n  std::function<std::unique_ptr<SyncClientInterface>(const std::string&)>\n      client_factory_;\n\n  mutable absl::Mutex mu_;\n};\n\n}  // namespace parameter_sync\n}  // namespace monolith\n\n#endif  // MONOLITH_MONOLITH_NATIVE_TRAINING_RUNTIME_PARAMETER_SYNC_SYNC_CLIENT_MANAGER_H_\n"
  },
  {
    "path": "monolith/native_training/save_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\nfrom typing import Set\nimport collections\nimport threading\nimport time\nimport traceback\nimport os, sys\n\nfrom datetime import datetime\nfrom absl import logging\nfrom google.protobuf import text_format\nimport tensorflow as tf\nfrom tensorflow.core.protobuf import saver_pb2\nfrom tensorflow.python.client import session\nfrom tensorflow.python.framework import ops, errors\nfrom tensorflow.python.lib.io import file_io\nfrom tensorflow.python.ops import variables\nfrom tensorflow.python.training import checkpoint_management\nfrom tensorflow.python.training import saver as tf_saver\nfrom tensorflow.python.training.py_checkpoint_reader import NewCheckpointReader, CheckpointReader\n\nfrom monolith.native_training.monolith_checkpoint_state_pb2 import MonolithCheckpointState\nfrom monolith.native_training import utils\nfrom monolith.native_training.session_run_hooks import tide_available_now\nfrom monolith.native_training.model_export.export_context import is_exporting\nfrom monolith.native_training.dense_reload_utils import CUSTOM_RESTORE_OP, calc_feed_dict\nfrom monolith.native_training.metric import cli\nfrom monolith.native_training import native_task_context\n\n_CkptStateCache = collections.namedtuple(\"_CkptStateCache\",\n                                         [\"global_step_value\", \"ckpt_state\"])\n\n_ckpt_state_cache_map = {}\n\nMONOLITH_CKPT_STATE_FILE_NAME = \"monolith_checkpoint\"\n\n\ndef get_latest_checkpoint_state(checkpoint_dir: str, global_step_value: int):\n  \"\"\"A function that helps to get ckpt state with cache.\n  Args:\n    global_step_value - used to decide if our cache is stale or not.\n  \"\"\"\n  cache = _ckpt_state_cache_map.get(checkpoint_dir, None)\n  if cache is None or cache.global_step_value < global_step_value or cache.ckpt_state is None:\n    cache = _CkptStateCache(\n        global_step_value=global_step_value,\n        ckpt_state=tf.train.get_checkpoint_state(checkpoint_dir))\n    _ckpt_state_cache_map[checkpoint_dir] = cache\n\n  return _ckpt_state_cache_map.get(checkpoint_dir).ckpt_state\n\n\ndef get_monolith_checkpoint_state(checkpoint_dir,\n                                  filename=None,\n                                  remove_invalid_path=False):\n  \"\"\"Returns MonolithCheckpointState proto from the \"monolith_checkpoint\" file.\n\n  If the \"monolith_checkpoint\" file contains a valid MonolithCheckpointState\n  proto, returns it.\n\n  Args:\n    checkpoint_dir: The directory of checkpoints.\n    filename: Optional name of the monolith checkpoint file.  Default to\n      'monolith_checkpoint'.\n\n  Returns:\n    A MonolithCheckpointState if the state was available, None\n    otherwise.\n  \"\"\"\n  ckpt = None\n  coord_checkpoint_filename = os.path.join(\n      checkpoint_dir, filename if filename else MONOLITH_CKPT_STATE_FILE_NAME)\n  try:\n    # Check that the file exists before opening it to avoid\n    # many lines of errors from colossus in the logs.\n    if file_io.file_exists(coord_checkpoint_filename):\n      file_content = file_io.read_file_to_string(coord_checkpoint_filename)\n      ckpt = MonolithCheckpointState()\n      text_format.Merge(file_content, ckpt)\n\n      if remove_invalid_path:\n        # For relative exempt_model_checkpoint_paths, prepend checkpoint_dir.\n        for i, p in enumerate(ckpt.exempt_model_checkpoint_paths):\n          if not os.path.isabs(p):\n            ckpt.exempt_model_checkpoint_paths[i] = os.path.join(\n                checkpoint_dir, p)\n        # Remove ckpt paths which do not exist from exempt_model_checkpoint_paths\n        ckpt_paths_not_exist = []\n        for i, p in enumerate(ckpt.exempt_model_checkpoint_paths):\n          if not checkpoint_management.checkpoint_exists(p):\n            ckpt_paths_not_exist.append(p)\n        for p in ckpt_paths_not_exist:\n          logging.warning(\n              \"%s not exists in file system, remove from monolith_checkpoint\",\n              p)\n          ckpt.exempt_model_checkpoint_paths.remove(p)\n  except errors.OpError as e:\n    # It's ok if the file cannot be read\n    logging.warning(\"%s: %s\", type(e).__name__, e)\n    logging.warning(\"%s: Checkpoint ignored\", coord_checkpoint_filename)\n    return None\n  except text_format.ParseError as e:\n    logging.warning(\"%s: %s\", type(e).__name__, e)\n    logging.warning(\"%s: Checkpoint ignored\", coord_checkpoint_filename)\n    return None\n  return ckpt\n\n\n# TODO(leqi.zou): make this class more powerful.\nclass SaveHelper:\n  \"\"\"A helper that provides some utils for saver listeners.\"\"\"\n\n  def __init__(self, basename: str):\n    self._basename = basename\n\n  def get_ckpt_prefix(self, global_step_value: int) -> str:\n    \"\"\"Returns checkpoint prefix for given basename and global_step_value.\"\"\"\n    return self._basename + \"-\" + str(global_step_value)\n\n  @classmethod\n  def get_ckpt_asset_dir(cls, ckpt_prefix: str) -> str:\n    \"\"\"Returns checkpoint asset directory for given basename and global_step_value.\n    This is mainly to reduce the number files in the model_dir.\n    \"\"\"\n    return ckpt_prefix + \".assets/\"\n\n  def get_global_step_value(self, ckpt_prefix: str) -> int:\n    \"\"\"Returns global step value for given checkpoint prefix.\"\"\"\n    if '-' in ckpt_prefix:\n      return int(ckpt_prefix.split('-')[-1])\n    else:\n      return 0\n\n  def get_existing_checkpoint_steps(self) -> Set[int]:\n    ckpt_state = tf.train.get_checkpoint_state(os.path.dirname(self._basename))\n    checkpoint_steps = set()\n    for path in ckpt_state.all_model_checkpoint_paths:\n      checkpoint_steps.add(self.get_global_step_value(path))\n    return checkpoint_steps\n\n\nclass SecondOrStepTimerWithTideSetting(tf.estimator.SecondOrStepTimer):\n  \"\"\"Timer that triggers at most once every N seconds or once every N steps.\n\n  It'll trigger using a different setting when tide resources is not available.\n  \"\"\"\n\n  def __init__(self,\n               every_secs=None,\n               every_steps=None,\n               tide_start_hour=None,\n               tide_start_minute=None,\n               tide_end_hour=None,\n               tide_end_minute=None,\n               tide_every_secs=None,\n               save_helper: SaveHelper = None):\n    super(SecondOrStepTimerWithTideSetting,\n          self).__init__(every_secs=every_secs, every_steps=every_steps)\n\n    self._tide_start_hour = tide_start_hour\n    self._tide_start_minute = tide_start_minute\n    self._tide_end_hour = tide_end_hour\n    self._tide_end_minute = tide_end_minute\n    self._tide_every_secs = tide_every_secs\n    self._save_helper = save_helper\n    self._enabled = True\n\n  def enable(self):\n    self._enabled = True\n\n  def disable(self):\n    self._enabled = False\n\n  def should_trigger_for_step(self, step):\n    \"\"\"Return true if the timer should trigger for the specified step.\n\n    Args:\n      step: Training step to trigger on.\n\n    Returns:\n      True if the difference between the current time and the time of the last\n      trigger exceeds `every_secs`, or if the difference between the current\n      step and the last triggered step exceeds `every_steps`. False otherwise.\n    \"\"\"\n\n    if not self._enabled:\n      return False\n\n    if self._last_triggered_step is None:\n      return True\n\n    if self._last_triggered_step == step:\n      return False\n\n    if (self._tide_start_hour is not None and self._tide_end_hour is not None\n        and self._tide_every_secs is not None) and not tide_available_now(\n            self._tide_start_hour, self._tide_start_minute, self._tide_end_hour,\n            self._tide_end_minute):\n      if time.time() >= self._last_triggered_time + self._tide_every_secs:\n        logging.info(\"Current UTC time: {} : {}\".format(\n            datetime.utcnow().hour,\n            datetime.utcnow().minute))\n        logging.info(\n            \"Tide not available. Using tide checkpoint saving time interval.\")\n        logging.info(\"Now: {} Last: {} Interval: {}\".format(\n            time.time(), self._last_triggered_time, self._tide_every_secs))\n        return True\n    else:\n      if self._every_secs is not None:\n        if time.time() >= self._last_triggered_time + self._every_secs:\n          return True\n\n      if self._every_steps is not None:\n        if step >= self._last_triggered_step + self._every_steps:\n          return True\n\n    return False\n\n\nclass NoFirstSaveCheckpointSaverHook(tf.estimator.CheckpointSaverHook):\n  \"\"\"A saver hook which won't perform the first save (which happened on after_create_session).\"\"\"\n\n  _has_dense_only: bool = False\n  _in_model_dump_mode: bool = False\n  _last_triggered_step: int = 0\n\n  def __init__(self,\n               checkpoint_dir,\n               save_secs=None,\n               save_steps=None,\n               saver=None,\n               checkpoint_basename=\"model.ckpt\",\n               scaffold=None,\n               listeners=None,\n               save_graph_def=True,\n               tide_start_hour=None,\n               tide_start_minute=None,\n               tide_end_hour=None,\n               tide_end_minute=None,\n               tide_save_secs=None,\n               ignore_save_errors=False,\n               is_dense_only: bool = False,\n               use_native_multi_hash_table: bool = False,\n               no_first_save: bool = True,\n               guard_saver_listeners=None):\n    \"\"\"\n    Args:\n      guard_saver_listeners - listeners which are not related to saving,\n      and will always call even there is an error.\n    \"\"\"\n    super().__init__(checkpoint_dir=checkpoint_dir,\n                     save_secs=save_secs,\n                     save_steps=save_steps,\n                     saver=saver,\n                     checkpoint_basename=checkpoint_basename,\n                     scaffold=scaffold,\n                     listeners=listeners,\n                     save_graph_def=save_graph_def)\n    self._helper = SaveHelper(self._save_path)\n    self._timer = SecondOrStepTimerWithTideSetting(\n        every_secs=save_secs,\n        every_steps=save_steps,\n        tide_start_hour=tide_start_hour,\n        tide_start_minute=tide_start_minute,\n        tide_end_hour=tide_end_hour,\n        tide_end_minute=tide_end_minute,\n        tide_every_secs=tide_save_secs,\n        save_helper=self._helper)\n    self._no_first_save = no_first_save\n    self._save_graph_def = save_graph_def\n    self._ignore_save_errors = ignore_save_errors\n    self._is_dense_only = is_dense_only\n    self._use_native_multi_hash_table = use_native_multi_hash_table\n    self._guard_saver_listeners = guard_saver_listeners or ()\n    self._mcli = cli.get_cli(utils.get_metric_prefix())\n    # Used to protect after_run, which may be called concurrently\n    self._l = threading.Lock()\n\n  @property\n  def timer(self):\n    return self._timer\n\n  # Make sure this hook run after restore hook.\n  def after_create_session(self, session, coord):\n    super().after_create_session(session, coord)\n    if self._save_graph_def:\n      self._get_saver().export_meta_graph(\n          utils.get_meta_graph_file_name(self._checkpoint_dir))\n    if isinstance(self._saver, PartialRecoverySaver):\n      self._saver.setup_ps_initialized_state(session)\n    self._create_or_update_monolith_ckpt_state(do_update=False)\n\n  def trigger_save(self, session, ignore_save_errors=False):\n    # There might be some concurency issue but should be fine\n    # Since our goal is only to do the save.\n    with self._l:\n      self._timer.reset()\n    run_context = tf.estimator.SessionRunContext((), session)\n    run_values = tf.estimator.SessionRunValues(0, None, None)\n    # this function may be called in the different thread.\n    with session.graph.as_default():\n      with self._l:\n        old_value = self._ignore_save_errors\n        try:\n          # Always throw error in this case.\n          self._ignore_save_errors = ignore_save_errors\n          super().after_run(run_context, run_values)\n        finally:\n          self._ignore_save_errors = old_value\n\n  def after_run(self, run_context, run_values):\n    with self._l:\n      super().after_run(run_context, run_values)\n\n  def _save(self, session, step: int) -> bool:\n    if self._no_first_save:\n      self._no_first_save = False\n      return False\n\n    # skip if full ckpt has happened in this step.\n    # [todo] maybe bug when there are more saver hooks\n    if self._is_dense_only:\n      mode = 'dense_only'\n      if step == self._last_triggered_step:\n        return False\n    else:\n      mode = 'full'\n\n    tags = {\"mode\": mode}\n\n    try:\n      for l in self._guard_saver_listeners:\n        try:\n          l.before_save(session, step)\n        except:\n          logging.error(traceback.format_exc())\n      for retries in range(2):\n        try:\n          start_time = time.time()\n          should_stop = super()._save(session, step)\n          self._last_triggered_step = step\n          self._create_or_update_monolith_ckpt_state(do_update=True)\n          end_time = time.time()\n          logging.info(\"saving checkpoint took %f seconds\", end_time - start_time)\n          self._mcli.emit_counter(\"save_checkpoint\", 1, tags)\n          self._mcli.emit_timer(\"save_checkpoint_time\", end_time - start_time,\n                                tags)\n          return should_stop\n        except tf.errors.OpError as op_error:\n          self._mcli.emit_counter(\"save_checkpoint_failed\", 1, tags)\n          logging.error(\"Failed to save, retrying ...\\n%s\",\n                        traceback.format_exc())\n          catched_error = op_error\n          continue\n    finally:\n      for l in reversed(self._guard_saver_listeners):\n        try:\n          l.after_save(session, step)\n        except:\n          logging.error(traceback.format_exc())\n\n    if self._ignore_save_errors:\n      return False\n    raise catched_error\n\n  def _create_or_update_monolith_ckpt_state(self, do_update=False):\n    # only save ckpt state if save_graph_def\n    if not self._save_graph_def:\n      return\n\n    ckpt_state = get_monolith_checkpoint_state(self._checkpoint_dir,\n                                               remove_invalid_path=True)\n    if ckpt_state is None:\n      logging.info(\"Create new monolith ckpt state\")\n      ckpt_state = MonolithCheckpointState()\n      if self._use_native_multi_hash_table:\n        ckpt_state.builtin_hash_table_type = MonolithCheckpointState.HashTableType.MULTI_CUCKOO_HASH_MAP\n      else:\n        ckpt_state.builtin_hash_table_type = MonolithCheckpointState.HashTableType.CUCKOO_HASH_MAP\n    elif do_update is False:\n      return\n    else:\n      logging.info(\"Update new monolith ckpt state\")\n    ckpt_state.last_checkpoint_save_timestamp = int(time.time())\n    monolith_checkpoint_filename = os.path.join(self._checkpoint_dir,\n                                                MONOLITH_CKPT_STATE_FILE_NAME)\n    file_io.atomic_write_string_to_file(monolith_checkpoint_filename,\n                                        text_format.MessageToString(ckpt_state))\n    logging.info(\"monolith ckpt state saved\")\n\n  def end(self, session):\n    last_step = session.run(self._global_step_tensor)\n    if self._is_dense_only:\n      pass\n    elif self._has_dense_only or self._in_model_dump_mode:\n      # force save\n      self._timer.update_last_triggered_step(last_step)\n      super()._save(session, last_step)\n      for l in self._listeners:\n        l.end(session, last_step)\n    else:\n      super().end(session)\n\n\nclass PsMonitor():\n  \"\"\"A monitor that use to detect ps status.\"\"\"\n\n  def __init__(self, ps_num):\n    self._queues = {}\n    self._enqueue_ops = {}\n    self._qsize_ops = {}\n    for i in range(ps_num):\n      device = utils.ps_device(i)\n      with tf.device(device):\n        queue = tf.queue.FIFOQueue(1,\n                                   tf.int32,\n                                   shared_name=\"ps_monitor_\" + str(i))\n        self._queues[device] = queue\n        self._enqueue_ops[device] = queue.enqueue(1)\n        self._qsize_ops[device] = queue.size()\n\n  def is_ps_uninitialized(self, sess, device):\n    if device in self._qsize_ops:\n      return sess.run(self._qsize_ops[device]) == 0\n    return True\n\n  def setup_ps_initialized_state(self, sess):\n    for device in self._queues.keys():\n      if sess.run(self._qsize_ops[device]) == 0:\n        sess.run(self._enqueue_ops[device])\n\n\nclass SaverBuilder(tf_saver.BulkSaverBuilder):\n  \"\"\"SaverBuilder with support for partial recovery.\n     Collect restore ops for each device.\n  \"\"\"\n\n  def _AddShardedRestoreOps(self, filename_tensor, per_device,\n                            restore_sequentially, reshape):\n    \"\"\"Add Ops to restore variables from multiple devices.\n\n    Args:\n      filename_tensor: Tensor for the path of the file to load.\n      per_device: A list of (device, SaveableObject) pairs, as returned by\n        _GroupByDevices().\n      restore_sequentially: True if we want to restore variables sequentially\n        within a shard.\n      reshape: True if we want to reshape loaded tensors to the shape of the\n        corresponding variable.\n\n    Returns:\n      An Operation that restores the variables.\n    \"\"\"\n    sharded_restores = []\n    self._restore_ops_per_device = collections.defaultdict(list)\n    for shard, (device, saveables) in enumerate(per_device):\n      with tf.device(device):\n        restore_op = self._AddRestoreOps(filename_tensor,\n                                         saveables,\n                                         restore_sequentially,\n                                         reshape,\n                                         preferred_shard=shard,\n                                         name=\"restore_shard\")\n        sharded_restores.append(restore_op)\n        self._restore_ops_per_device[device].append(restore_op)\n\n    for device, restore_ops in self._restore_ops_per_device.items():\n      self._restore_ops_per_device[device] = tf.group(*restore_ops,\n                                                      name=\"restore_per_device\")\n\n    return tf.group(*sharded_restores, name=\"restore_all\")\n\n  @property\n  def restore_ops_per_device(self):\n    \"\"\"Return restore ops per device.\"\"\"\n    if hasattr(self, '_restore_ops_per_device'):\n      return self._restore_ops_per_device\n    return {}\n\n\n# Copy from tensorflow/python/training/saver.py. The major change is in restore function.\n# Apply partial recovery of dense part and hash table when ps_monitor is enabled.\n# TODO(xujinghao): Implement partial recovery of hash filter if needed.\nclass PartialRecoverySaver():\n  \"\"\"Saves and restores variables.\n\n  See [Variables](https://tensorflow.org/guide/variables)\n  for an overview of variables, saving and restoring.\n\n  The `Saver` class adds ops to save and restore variables to and from\n  *checkpoints*.  It also provides convenience methods to run these ops.\n\n  Checkpoints are binary files in a proprietary format which map variable names\n  to tensor values.  The best way to examine the contents of a checkpoint is to\n  load it using a `Saver`.\n\n  Savers can automatically number checkpoint filenames with a provided counter.\n  This lets you keep multiple checkpoints at different steps while training a\n  model.  For example you can number the checkpoint filenames with the training\n  step number.  To avoid filling up disks, savers manage checkpoint files\n  automatically. For example, they can keep only the N most recent files, or\n  one checkpoint for every N hours of training.\n\n  You number checkpoint filenames by passing a value to the optional\n  `global_step` argument to `save()`:\n\n  ```python\n  saver.save(sess, 'my-model', global_step=0) ==> filename: 'my-model-0'\n  ...\n  saver.save(sess, 'my-model', global_step=1000) ==> filename: 'my-model-1000'\n  ```\n\n  Additionally, optional arguments to the `Saver()` constructor let you control\n  the proliferation of checkpoint files on disk:\n\n  * `max_to_keep` indicates the maximum number of recent checkpoint files to\n    keep.  As new files are created, older files are deleted.   If None or 0,\n    no checkpoints are deleted from the filesystem but only the last one is\n    kept in the `checkpoint` file.  Defaults to 5 (that is, the 5 most recent\n    checkpoint files are kept.)\n\n  * `keep_checkpoint_every_n_hours`: In addition to keeping the most recent\n    `max_to_keep` checkpoint files, you might want to keep one checkpoint file\n    for every N hours of training.  This can be useful if you want to later\n    analyze how a model progressed during a long training session.  For\n    example, passing `keep_checkpoint_every_n_hours=2` ensures that you keep\n    one checkpoint file for every 2 hours of training.  The default value of\n    10,000 hours effectively disables the feature.\n\n  Note that you still have to call the `save()` method to save the model.\n  Passing these arguments to the constructor will not save variables\n  automatically for you.\n\n  A training program that saves regularly looks like:\n\n  ```python\n  ...\n  # Create a saver.\n  saver = tf.compat.v1.train.Saver(...variables...)\n  # Launch the graph and train, saving the model every 1,000 steps.\n  sess = tf.compat.v1.Session()\n  for step in xrange(1000000):\n      sess.run(..training_op..)\n      if step % 1000 == 0:\n          # Append the step number to the checkpoint name:\n          saver.save(sess, 'my-model', global_step=step)\n  ```\n\n  In addition to checkpoint files, savers keep a protocol buffer on disk with\n  the list of recent checkpoints. This is used to manage numbered checkpoint\n  files and by `latest_checkpoint()`, which makes it easy to discover the path\n  to the most recent checkpoint. That protocol buffer is stored in a file named\n  'checkpoint' next to the checkpoint files.\n\n  If you create several savers, you can specify a different filename for the\n  protocol buffer file in the call to `save()`.\n  \"\"\"\n\n  def __init__(self,\n               var_list=None,\n               reshape=False,\n               sharded=False,\n               max_to_keep=5,\n               keep_checkpoint_every_n_hours=10000.0,\n               name=None,\n               restore_sequentially=False,\n               saver_def=None,\n               builder=None,\n               defer_build=False,\n               allow_empty=False,\n               pad_step_number=False,\n               save_relative_paths=False,\n               filename=None,\n               ps_monitor=None,\n               exempt_checkpoint_paths=None,\n               skip_save=False,\n               model_dir=None):\n    \"\"\"Creates a `Saver`.\n\n    The constructor adds ops to save and restore variables.\n\n    `var_list` specifies the variables that will be saved and restored. It can\n    be passed as a `dict` or a list:\n\n    * A `dict` of names to variables: The keys are the names that will be\n      used to save or restore the variables in the checkpoint files.\n    * A list of variables: The variables will be keyed with their op name in\n      the checkpoint files.\n\n    For example:\n\n    ```python\n    v1 = tf.Variable(..., name='v1')\n    v2 = tf.Variable(..., name='v2')\n\n    # Pass the variables as a dict:\n    saver = tf.compat.v1.train.Saver({'v1': v1, 'v2': v2})\n\n    # Or pass them as a list.\n    saver = tf.compat.v1.train.Saver([v1, v2])\n    # Passing a list is equivalent to passing a dict with the variable op names\n    # as keys:\n    saver = tf.compat.v1.train.Saver({v.op.name: v for v in [v1, v2]})\n    ```\n\n    Note: the newer `AutoTrackable` API is not supported by `Saver`. In this\n    case, the `tf.train.Checkpoint` class should be used.\n\n    The optional `reshape` argument, if `True`, allows restoring a variable from\n    a save file where the variable had a different shape, but the same number\n    of elements and type.  This is useful if you have reshaped a variable and\n    want to reload it from an older checkpoint.\n\n    The optional `sharded` argument, if `True`, instructs the saver to shard\n    checkpoints per device.\n\n    Args:\n      var_list: A list of `Variable`/`SaveableObject`, or a dictionary mapping\n        names to `SaveableObject`s. If `None`, defaults to the list of all\n        saveable objects.\n      reshape: If `True`, allows restoring parameters from a checkpoint where\n        the variables have a different shape.\n      sharded: If `True`, shard the checkpoints, one per device.\n      max_to_keep: Maximum number of recent checkpoints to keep. Defaults to 5.\n      keep_checkpoint_every_n_hours: How often to keep checkpoints. Defaults to\n        10,000 hours.\n      name: String.  Optional name to use as a prefix when adding operations.\n      restore_sequentially: A `Bool`, which if true, causes restore of different\n        variables to happen sequentially within each device.  This can lower\n        memory usage when restoring very large models.\n      saver_def: Optional `SaverDef` proto to use instead of running the\n        builder. This is only useful for specialty code that wants to recreate a\n        `Saver` object for a previously built `Graph` that had a `Saver`. The\n        `saver_def` proto should be the one returned by the `as_saver_def()`\n        call of the `Saver` that was created for that `Graph`.\n      builder: Optional `SaverBuilder` to use if a `saver_def` was not provided.\n        Defaults to `BulkSaverBuilder()`.\n      defer_build: If `True`, defer adding the save and restore ops to the\n        `build()` call. In that case `build()` should be called before\n        finalizing the graph or using the saver.\n      allow_empty: If `False` (default) raise an error if there are no variables\n        in the graph. Otherwise, construct the saver anyway and make it a no-op.\n      pad_step_number: if True, pads the global step number in the checkpoint\n        filepaths to some fixed width (8 by default).  This is turned off by\n        default.\n      save_relative_paths: If `True`, will write relative paths to the\n        checkpoint state file. This is needed if the user wants to copy the\n        checkpoint directory and reload from the copied directory.\n      filename: If known at graph construction time, filename used for variable\n        loading/saving.\n\n    Raises:\n      TypeError: If `var_list` is invalid.\n      ValueError: If any of the keys or values in `var_list` are not unique.\n      RuntimeError: If eager execution is enabled and`var_list` does not specify\n        a list of variables to save.\n\n    @compatibility(eager)\n    When eager execution is enabled, `var_list` must specify a `list` or `dict`\n    of variables to save. Otherwise, a `RuntimeError` will be raised.\n\n    Although Saver works in some cases when executing eagerly, it is\n    fragile. Please switch to `tf.train.Checkpoint` or\n    `tf.keras.Model.save_weights`, which perform a more robust object-based\n    saving. These APIs will load checkpoints written by `Saver`.\n    @end_compatibility\n    \"\"\"\n    if defer_build and var_list:\n      raise ValueError(\n          \"If `var_list` is provided then build cannot be deferred. \"\n          \"Either set defer_build=False or var_list=None.\")\n    if tf.executing_eagerly():\n      logging.warning(\n          \"Saver is deprecated, please switch to tf.train.Checkpoint or \"\n          \"tf.keras.Model.save_weights for training checkpoints. When \"\n          \"executing eagerly variables do not necessarily have unique names, \"\n          \"and so the variable.name-based lookups Saver performs are \"\n          \"error-prone.\")\n      if var_list is None:\n        raise RuntimeError(\n            \"When eager execution is enabled, `var_list` must specify a list \"\n            \"or dict of variables to save\")\n    self._var_list = var_list\n    self._reshape = reshape\n    self._sharded = sharded\n    self._max_to_keep = max_to_keep\n    self._keep_checkpoint_every_n_hours = keep_checkpoint_every_n_hours\n    self._name = name\n    self._restore_sequentially = restore_sequentially\n    self.saver_def = saver_def\n    self._builder = builder\n    self._is_built = False\n    self._allow_empty = allow_empty\n    self._is_empty = None\n    self._write_version = saver_pb2.SaverDef.V2\n    self._pad_step_number = pad_step_number\n    self._filename = filename\n    self._last_checkpoints = []\n    self._checkpoints_to_be_deleted = []\n    self._exempt_checkpoint_paths = set(exempt_checkpoint_paths or [])\n    self._model_dir = model_dir\n    self._skip_save = skip_save\n    if tf.executing_eagerly():\n      self._next_checkpoint_time = (time.time() +\n                                    self._keep_checkpoint_every_n_hours * 3600)\n    elif not defer_build:\n      self.build()\n    if self.saver_def:\n      self._check_saver_def()\n      self._write_version = self.saver_def.version\n    self._save_relative_paths = save_relative_paths\n    # For compatibility with object-based checkpoints, we may build a second\n    # Saver to read the renamed keys.\n    self._object_restore_saver = None\n\n    self._ps_monitor = ps_monitor\n\n  def build(self):\n    if tf.executing_eagerly():\n      raise RuntimeError(\"Use save/restore instead of build in eager mode.\")\n    self._build(self._filename, build_save=True, build_restore=True)\n\n  def _build_eager(self, checkpoint_path, build_save, build_restore):\n    self._build(checkpoint_path,\n                build_save=build_save,\n                build_restore=build_restore)\n\n  def _build(self, checkpoint_path, build_save, build_restore):\n    \"\"\"Builds saver_def.\"\"\"\n    if not tf.executing_eagerly():\n      if self._is_built:\n        return\n      self._is_built = True\n\n    if not self.saver_def or tf.executing_eagerly():\n      if self._builder is None:\n        self._builder = SaverBuilder(self._write_version)\n\n      if self._var_list is None:\n        # pylint: disable=protected-access\n        self._var_list = variables._all_saveable_objects()\n      if not self._var_list:\n        if self._allow_empty:\n          self._is_empty = True\n          return\n        else:\n          raise ValueError(\"No variables to save\")\n      self._is_empty = False\n\n      self.saver_def = self._builder._build_internal(  # pylint: disable=protected-access\n          self._var_list,\n          reshape=self._reshape,\n          sharded=self._sharded,\n          max_to_keep=self._max_to_keep,\n          keep_checkpoint_every_n_hours=self._keep_checkpoint_every_n_hours,\n          name=self._name,\n          restore_sequentially=self._restore_sequentially,\n          filename=checkpoint_path,\n          build_save=build_save,\n          build_restore=build_restore)\n    elif self.saver_def and self._name:\n      # Since self._name is used as a name_scope by builder(), we are\n      # overloading the use of this field to represent the \"import_scope\" as\n      # well.\n      self.saver_def.filename_tensor_name = ops.prepend_name_scope(\n          self.saver_def.filename_tensor_name, self._name)\n      self.saver_def.save_tensor_name = ops.prepend_name_scope(\n          self.saver_def.save_tensor_name, self._name)\n      self.saver_def.restore_op_name = ops.prepend_name_scope(\n          self.saver_def.restore_op_name, self._name)\n\n    self._check_saver_def()\n    if not tf.executing_eagerly():\n      # Updates next checkpoint time.\n      # Set in __init__ when executing eagerly.\n      self._next_checkpoint_time = (\n          time.time() + self.saver_def.keep_checkpoint_every_n_hours * 3600)\n\n  def _check_saver_def(self):\n    if not isinstance(self.saver_def, saver_pb2.SaverDef):\n      raise ValueError(\"saver_def must be a saver_pb2.SaverDef: %s\" %\n                       self.saver_def)\n    if not tf.executing_eagerly():\n      if not self.saver_def.save_tensor_name:\n        raise ValueError(\"saver_def must specify the save_tensor_name: %s\" %\n                         str(self.saver_def))\n      if not self.saver_def.restore_op_name:\n        raise ValueError(\"saver_def must specify the restore_op_name: %s\" %\n                         str(self.saver_def))\n\n  def _CheckpointFilename(self, p):\n    \"\"\"Returns the checkpoint filename given a `(filename, time)` pair.\n\n    Args:\n      p: (filename, time) pair.\n\n    Returns:\n      Checkpoint file name.\n    \"\"\"\n    name, _ = p\n    return name\n\n  def _RecordLastCheckpoint(self, latest_save_path):\n    \"\"\"Manages the list of the latest checkpoints.\"\"\"\n    if not self.saver_def.max_to_keep:\n      return\n    # Remove first from list if the same name was used before.\n    for p in self._last_checkpoints:\n      if latest_save_path == self._CheckpointFilename(p):\n        self._last_checkpoints.remove(p)\n    # Append new path to list\n    self._last_checkpoints.append((latest_save_path, time.time()))\n\n    # If more than max_to_keep, remove oldest but exempt checkpoint.\n    last_checkpoint_paths = set([\n        os.path.basename(self._CheckpointFilename(p))\n        for p in self._last_checkpoints\n    ])\n    exempt_checkpoint_paths = last_checkpoint_paths & self.exempt_checkpoint_paths\n    if len(self._last_checkpoints) - len(\n        exempt_checkpoint_paths) > self.saver_def.max_to_keep:\n      for p in self._last_checkpoints:\n        filename = os.path.basename(self._CheckpointFilename(p))\n        if filename not in self.exempt_checkpoint_paths:\n          self._checkpoints_to_be_deleted.append(p)\n          self._last_checkpoints.remove(p)\n          break\n\n  def _MaybeDeleteOldCheckpoints(self, meta_graph_suffix=\"meta\"):\n    \"\"\"Deletes old checkpoints if necessary.\n\n    `self._checkpoints_to_be_deleted` is going to contain checkpoints that are\n    over `max_to_keep`.  They are going to be deleted.  If\n    `keep_checkpoint_every_n_hours` was specified, keep an additional checkpoint\n    every `N` hours. For example, if `N` is 0.5, an additional checkpoint is\n    kept for every 0.5 hours of training; if `N` is 10, an additional\n    checkpoint is kept for every 10 hours of training.\n\n    Args:\n      meta_graph_suffix: Suffix for `MetaGraphDef` file. Defaults to 'meta'.\n    \"\"\"\n    if self._checkpoints_to_be_deleted:\n      p = self._checkpoints_to_be_deleted.pop(0)\n      # Do not delete the file if we keep_checkpoint_every_n_hours is set and we\n      # have reached N hours of training.\n      should_keep = p[1] > self._next_checkpoint_time\n      if should_keep:\n        self._next_checkpoint_time += (\n            self.saver_def.keep_checkpoint_every_n_hours * 3600)\n        return\n\n      # Otherwise delete the files.\n      logging.info(\"Deleted checkpoint: %s.\", self._CheckpointFilename(p))\n      for pathname in tf.io.gfile.glob(self._CheckpointFilename(p) + \".*\"):\n        try:\n          tf.io.gfile.rmtree(pathname)\n        except tf.errors.NotFoundError:\n          logging.warning(\n              \"Hit NotFoundError when deleting '%s', possibly because another \"\n              \"process/thread is also deleting/moving the same file\", pathname)\n\n  def as_saver_def(self):\n    \"\"\"Generates a `SaverDef` representation of this saver.\n\n    Returns:\n      A `SaverDef` proto.\n    \"\"\"\n    return self.saver_def\n\n  @property\n  def exempt_checkpoint_paths(self):\n    if self._model_dir:\n      monolith_ckpt_state = get_monolith_checkpoint_state(\n          self._model_dir, remove_invalid_path=True)\n      if monolith_ckpt_state and monolith_ckpt_state.exempt_model_checkpoint_paths:\n        exempt_checkpoint_paths = [\n            os.path.basename(p)\n            for p in monolith_ckpt_state.exempt_model_checkpoint_paths\n        ]\n        logging.info(\n            'New exempt checkpoint paths: {}'.format(exempt_checkpoint_paths))\n        self._exempt_checkpoint_paths = set(exempt_checkpoint_paths or [])\n      else:\n        logging.info(\"Get exempt checkpoint paths null\")\n    return self._exempt_checkpoint_paths\n\n  @property\n  def last_checkpoints(self):\n    \"\"\"List of not-yet-deleted checkpoint filenames.\n\n    You can pass any of the returned values to `restore()`.\n\n    Returns:\n      A list of checkpoint filenames, sorted from oldest to newest.\n    \"\"\"\n    return list(self._CheckpointFilename(p) for p in self._last_checkpoints)\n\n  def set_last_checkpoints_with_time(self, last_checkpoints_with_time):\n    \"\"\"Sets the list of old checkpoint filenames and timestamps.\n\n    Args:\n      last_checkpoints_with_time: A list of tuples of checkpoint filenames and\n        timestamps.\n\n    Raises:\n      AssertionError: If last_checkpoints_with_time is not a list.\n    \"\"\"\n    assert isinstance(last_checkpoints_with_time, list)\n    self._last_checkpoints = last_checkpoints_with_time\n\n  def recover_last_checkpoints(self, checkpoint_paths):\n    \"\"\"Recovers the internal saver state after a crash.\n\n    This method is useful for recovering the \"self._last_checkpoints\" state.\n\n    Globs for the checkpoints pointed to by `checkpoint_paths`.  If the files\n    exist, use their mtime as the checkpoint timestamp.\n\n    Args:\n      checkpoint_paths: a list of checkpoint paths.\n    \"\"\"\n    checkpoints_with_mtimes = []\n    for checkpoint_path in checkpoint_paths:\n      try:\n        mtime = checkpoint_management.get_checkpoint_mtimes([checkpoint_path])\n      except tf.errors.NotFoundError:\n        # It's fine if some other thread/process is deleting some older\n        # checkpoint concurrently.\n        continue\n      if mtime:\n        checkpoints_with_mtimes.append((checkpoint_path, mtime[0]))\n    self.set_last_checkpoints_with_time(checkpoints_with_mtimes)\n    logging.info(\"Recover last checkpoints result: {}\".format(\n        self.last_checkpoints))\n\n  def save(self,\n           sess,\n           save_path,\n           global_step=None,\n           latest_filename=None,\n           meta_graph_suffix=\"meta\",\n           write_meta_graph=True,\n           write_state=True,\n           strip_default_attrs=False,\n           save_debug_info=False):\n    # pylint: disable=line-too-long\n    \"\"\"Saves variables.\n\n    This method runs the ops added by the constructor for saving variables.\n    It requires a session in which the graph was launched.  The variables to\n    save must also have been initialized.\n\n    The method returns the path prefix of the newly created checkpoint files.\n    This string can be passed directly to a call to `restore()`.\n\n    Args:\n      sess: A Session to use to save the variables.\n      save_path: String.  Prefix of filenames created for the checkpoint.\n      global_step: If provided the global step number is appended to `save_path`\n        to create the checkpoint filenames. The optional argument can be a\n        `Tensor`, a `Tensor` name or an integer.\n      latest_filename: Optional name for the protocol buffer file that will\n        contains the list of most recent checkpoints.  That file, kept in the\n        same directory as the checkpoint files, is automatically managed by the\n        saver to keep track of recent checkpoints.  Defaults to 'checkpoint'.\n      meta_graph_suffix: Suffix for `MetaGraphDef` file. Defaults to 'meta'.\n      write_meta_graph: `Boolean` indicating whether or not to write the meta\n        graph file.\n      write_state: `Boolean` indicating whether or not to write the\n        `CheckpointStateProto`.\n      strip_default_attrs: Boolean. If `True`, default-valued attributes will be\n        removed from the NodeDefs. For a detailed guide, see\n        [Stripping Default-Valued\n          Attributes](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/saved_model/README.md#stripping-default-valued-attributes).\n      save_debug_info: If `True`, save the GraphDebugInfo to a separate file,\n        which in the same directory of save_path and with `_debug` added before\n        the file extension. This is only enabled when `write_meta_graph` is\n        `True`\n\n    Returns:\n      A string: path prefix used for the checkpoint files.  If the saver is\n        sharded, this string ends with: '-?????-of-nnnnn' where 'nnnnn'\n        is the number of shards created.\n      If the saver is empty, returns None.\n\n    Raises:\n      TypeError: If `sess` is not a `Session`.\n      ValueError: If `latest_filename` contains path components, or if it\n        collides with `save_path`.\n      RuntimeError: If save and restore ops weren't built.\n    \"\"\"\n    # pylint: enable=line-too-long\n    if not self._is_built and not tf.executing_eagerly():\n      raise RuntimeError(\n          \"`build()` should be called before save if defer_build==True\")\n    if self._skip_save:\n      return None\n    if latest_filename is None:\n      latest_filename = \"checkpoint\"\n\n    if os.path.split(latest_filename)[0]:\n      raise ValueError(\"'latest_filename' must not contain path components\")\n\n    save_path = tf.compat.as_str(save_path)\n    if global_step is not None:\n      if not isinstance(global_step, tf.compat.integral_types):\n        global_step = tf.compat.v1.train.global_step(sess, global_step)\n      checkpoint_file = \"%s-%d\" % (save_path, global_step)\n      if self._pad_step_number:\n        # Zero-pads the step numbers, so that they are sorted when listed.\n        checkpoint_file = \"%s-%s\" % (save_path, \"{:08d}\".format(global_step))\n    else:\n      checkpoint_file = save_path\n      if os.path.basename(save_path) == latest_filename and not self._sharded:\n        # Guard against collision between data file and checkpoint state file.\n        raise ValueError(\n            \"'latest_filename' collides with 'save_path': '%s' and '%s'\" %\n            (latest_filename, save_path))\n\n    if (not tf.executing_eagerly() and\n        not isinstance(sess, session.SessionInterface)):\n      raise TypeError(\"'sess' must be a Session; %s\" % sess)\n\n    save_path_parent = os.path.dirname(save_path)\n    if not self._is_empty:\n      try:\n        if tf.executing_eagerly():\n          self._build_eager(checkpoint_file,\n                            build_save=True,\n                            build_restore=False)\n          model_checkpoint_path = self.saver_def.save_tensor_name\n        else:\n          model_checkpoint_path = sess.run(\n              self.saver_def.save_tensor_name,\n              {self.saver_def.filename_tensor_name: checkpoint_file})\n\n        model_checkpoint_path = tf.compat.as_str(model_checkpoint_path)\n        if write_state:\n          self._RecordLastCheckpoint(model_checkpoint_path)\n          checkpoint_management.update_checkpoint_state_internal(\n              save_dir=save_path_parent,\n              model_checkpoint_path=model_checkpoint_path,\n              all_model_checkpoint_paths=self.last_checkpoints,\n              latest_filename=latest_filename,\n              save_relative_paths=self._save_relative_paths)\n          self._MaybeDeleteOldCheckpoints(meta_graph_suffix=meta_graph_suffix)\n      except (tf.errors.FailedPreconditionError,\n              tf.errors.NotFoundError) as exc:\n        if not tf.io.gfile.isdir(save_path_parent):\n          exc = ValueError(\n              \"Parent directory of {} doesn't exist, can't save.\".format(\n                  save_path))\n        raise exc\n\n    if write_meta_graph:\n      meta_graph_filename = checkpoint_management.meta_graph_filename(\n          checkpoint_file, meta_graph_suffix=meta_graph_suffix)\n      if not tf.executing_eagerly():\n        with sess.graph.as_default():\n          self.export_meta_graph(meta_graph_filename,\n                                 strip_default_attrs=strip_default_attrs,\n                                 save_debug_info=save_debug_info)\n\n    if self._is_empty:\n      return None\n    else:\n      return model_checkpoint_path\n\n  def export_meta_graph(self,\n                        filename=None,\n                        collection_list=None,\n                        as_text=False,\n                        export_scope=None,\n                        clear_devices=False,\n                        clear_extraneous_savers=False,\n                        strip_default_attrs=False,\n                        save_debug_info=False):\n    # pylint: disable=line-too-long\n    \"\"\"Writes `MetaGraphDef` to save_path/filename.\n\n    Args:\n      filename: Optional meta_graph filename including the path.\n      collection_list: List of string keys to collect.\n      as_text: If `True`, writes the meta_graph as an ASCII proto.\n      export_scope: Optional `string`. Name scope to remove.\n      clear_devices: Whether or not to clear the device field for an `Operation`\n        or `Tensor` during export.\n      clear_extraneous_savers: Remove any Saver-related information from the\n        graph (both Save/Restore ops and SaverDefs) that are not associated with\n        this Saver.\n      strip_default_attrs: Boolean. If `True`, default-valued attributes will be\n        removed from the NodeDefs. For a detailed guide, see\n        [Stripping Default-Valued\n          Attributes](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/saved_model/README.md#stripping-default-valued-attributes).\n      save_debug_info: If `True`, save the GraphDebugInfo to a separate file,\n        which in the same directory of filename and with `_debug` added before\n        the file extension.\n\n    Returns:\n      A `MetaGraphDef` proto.\n    \"\"\"\n    # pylint: enable=line-too-long\n    return tf_saver.export_meta_graph(\n        filename=filename,\n        graph_def=tf.compat.v1.get_default_graph().as_graph_def(\n            add_shapes=True),\n        saver_def=self.saver_def,\n        collection_list=collection_list,\n        as_text=as_text,\n        export_scope=export_scope,\n        clear_devices=clear_devices,\n        clear_extraneous_savers=clear_extraneous_savers,\n        strip_default_attrs=strip_default_attrs,\n        save_debug_info=save_debug_info)\n\n  def _origin_restore(self, sess, save_path):\n    \"\"\"Restores previously saved variables.\n\n    This method runs the ops added by the constructor for restoring variables.\n    It requires a session in which the graph was launched.  The variables to\n    restore do not have to have been initialized, as restoring is itself a way\n    to initialize variables.\n\n    The `save_path` argument is typically a value previously returned from a\n    `save()` call, or a call to `latest_checkpoint()`.\n\n    Args:\n      sess: A `Session` to use to restore the parameters. None in eager mode.\n      save_path: Path where parameters were previously saved.\n\n    Raises:\n      ValueError: If save_path is None or not a valid checkpoint.\n    \"\"\"\n    if self._is_empty:\n      return\n    if save_path is None:\n      raise ValueError(\"Can't load save_path when it is None.\")\n\n    checkpoint_prefix = tf.compat.as_text(save_path)\n    if not checkpoint_management.checkpoint_exists_internal(checkpoint_prefix):\n      raise ValueError(\"The passed save_path is not a valid checkpoint: \" +\n                       checkpoint_prefix)\n\n    logging.info(\"Restoring parameters from %s\", checkpoint_prefix)\n    try:\n      if tf.executing_eagerly():\n        self._build_eager(save_path, build_save=False, build_restore=True)\n      else:\n        # At some local case, restore_ops_per_device is empty.\n        if not self._builder.restore_ops_per_device:\n          sess.run(self.saver_def.restore_op_name,\n                   {self.saver_def.filename_tensor_name: save_path})\n        else:\n          restore_ops = []\n          for device, restore_op in self._builder.restore_ops_per_device.items(\n          ):\n            if not self._ps_monitor or self._ps_monitor.is_ps_uninitialized(\n                sess, device):\n              restore_ops.append(restore_op)\n          sess.run(restore_ops,\n                   {self.saver_def.filename_tensor_name: save_path})\n\n    except tf.errors.NotFoundError as err:\n      # There are three common conditions that might cause this error:\n      # 0. The file is missing. We ignore here, as this is checked above.\n      # 1. This is an object-based checkpoint trying name-based loading.\n      # 2. The graph has been altered and a variable or other name is missing.\n\n      # 1. The checkpoint would not be loaded successfully as is. Try to parse\n      # it as an object-based checkpoint.\n      try:\n        names_to_keys = tf_saver.object_graph_key_mapping(save_path)\n      except tf.errors.NotFoundError:\n        # 2. This is not an object-based checkpoint, which likely means there\n        # is a graph mismatch. Re-raise the original error with\n        # a helpful message (b/110263146)\n        raise tf_saver._wrap_restore_error_with_msg(\n            err, \"a Variable name or other graph key that is missing\")\n\n      # This is an object-based checkpoint. We'll print a warning and then do\n      # the restore.\n      logging.warning(\n          \"Restoring an object-based checkpoint using a name-based saver. This \"\n          \"may be somewhat fragile, and will re-build the Saver. Instead, \"\n          \"consider loading object-based checkpoints using \"\n          \"tf.train.Checkpoint().\")\n      self._object_restore_saver = tf_saver.saver_from_object_based_checkpoint(\n          checkpoint_path=save_path,\n          var_list=self._var_list,\n          builder=self._builder,\n          names_to_keys=names_to_keys,\n          cached_saver=self._object_restore_saver)\n      self._object_restore_saver.restore(sess=sess, save_path=save_path)\n    except tf.errors.InvalidArgumentError as err:\n      # There is a mismatch between the graph and the checkpoint being loaded.\n      # We add a more reasonable error message here to help users (b/110263146)\n      raise tf_saver._wrap_restore_error_with_msg(\n          err, \"a mismatch between the current graph and the graph\")\n\n  def restore(self, sess, save_path):\n    if is_exporting() or sess is None:\n      logging.info('is_exporting or sess is None, fall back to origin_restore')\n      return self._origin_restore(sess, save_path)\n\n    checkpoint_state = None\n    logging.info(f\"save_path is {save_path}\")\n    model_dir = os.path.dirname(save_path)\n    try:\n      checkpoint_state = tf.train.get_checkpoint_state(checkpoint_dir=model_dir)\n    except Exception as e:\n      logging.info(\n          f'no checkpoint file in {model_dir}, fall back to origin_restore')\n      return self._origin_restore(sess, save_path)\n    if checkpoint_state is None:\n      logging.info(f'checkpoint_state is None, fall back to origin_restore')\n      return self._origin_restore(sess, save_path)\n\n    graph: tf.Graph = None\n    try:\n      graph: tf.Graph = sess.graph\n    except Exception as e:\n      logging.info(\"the eager mode has no attribute graph\")\n      return self._origin_restore(sess, save_path)\n\n    if not graph:\n      logging.info(\"graph is None, pls. check! fall back to origin_restore\")\n      return self._origin_restore(sess, save_path)\n    init_objs = graph.get_collection(CUSTOM_RESTORE_OP)\n\n    if init_objs:\n      init_ops, placeholders, alias_map = init_objs[0]\n      if alias_map:  # alias_map: new -> old\n        ckpt: CheckpointReader = NewCheckpointReader(save_path)\n        feed_dict = calc_feed_dict(ckpt, alias_map, placeholders)\n        if feed_dict:\n          sess.run(init_ops, feed_dict=feed_dict)\n        else:\n          self._origin_restore(sess, save_path)\n      elif init_ops:\n        assert alias_map is None or len(alias_map) == 0\n        restore_dir = os.path.dirname(save_path)\n        model_dir = restore_dir\n        if hasattr(init_ops[0], 'model_dir'):\n          model_dir_tmp = getattr(init_ops[0], 'model_dir')\n          if model_dir_tmp:\n            model_dir = model_dir_tmp\n        flag_file = os.path.join(model_dir, 'clear_nn')\n        logging.info(f'the clear nn flag_file is {flag_file}')\n        if tf.io.gfile.exists(flag_file):\n          logging.info(\n              'clear nn flag_file exists, restore from ckpt, do not clear nn')\n          self._origin_restore(sess, save_path)\n        else:\n          if len(init_ops) == 1:\n            sess.run(init_ops)\n          else:\n            init_op, update_gs_op = init_ops\n            sess.run(init_op)\n            # update global_step to continue training\n            ckpt: CheckpointReader = NewCheckpointReader(save_path)\n            gs_tensor = ckpt.get_tensor('global_step')\n            sess.run(update_gs_op, feed_dict={placeholders[0]: gs_tensor})\n            logging.info(\n                f'update global_step to continue training, {gs_tensor}')\n\n          logging.info('clear nn by calling init_op other than restore ...')\n\n          with tf.io.gfile.GFile(flag_file, 'w') as ostream:\n            ostream.write(file_content='clean nn done!')\n      else:\n        self._origin_restore(sess, save_path)\n\n      with graph._lock:\n        if CUSTOM_RESTORE_OP in graph._collections:\n          del graph._collections[CUSTOM_RESTORE_OP]\n    else:\n      self._origin_restore(sess, save_path)\n\n  def setup_ps_initialized_state(self, sess):\n    if self._ps_monitor:\n      self._ps_monitor.setup_ps_initialized_state(sess)\n"
  },
  {
    "path": "monolith/native_training/save_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\nfrom freezegun import freeze_time\nfrom unittest import mock\nimport numpy as np\nimport os\nimport six\nimport time\n\nimport tensorflow as tf\nfrom tensorflow.core.protobuf import config_pb2\nfrom tensorflow.python.data.ops import iterator_ops\nfrom tensorflow.python.eager import context\nfrom tensorflow.python.framework import test_util\nfrom tensorflow.python.lib.io import file_io\nfrom tensorflow.python.ops import resource_variable_ops\nfrom tensorflow.python.training import saver_test_utils\nfrom tensorflow.python.training import checkpoint_management\n\nfrom monolith.native_training import save_utils\n\n\nclass SaveUtilsTest(tf.test.TestCase):\n\n  def setUp(self):\n    super().setUp()\n    self._global_step = tf.compat.v1.train.get_or_create_global_step()\n    self._saver = save_utils.PartialRecoverySaver(\n        exempt_checkpoint_paths=['ckpt-10', 'ckpt-20'], max_to_keep=3)\n    self._savepath = os.path.join(\n        os.environ[\"TEST_TMPDIR\"],\n        type(self).__name__ + \"_\" + self._testMethodName, \"ckpt\")\n\n  def create_test_ckpt(self, global_step_value: int):\n    with self.session() as sess:\n      self._global_step = tf.compat.v1.assign(self._global_step,\n                                              global_step_value)\n      sess.run(self._global_step)\n      self._saver.save(sess, self._savepath, self._global_step)\n\n  def test_get_ckpt_steps(self):\n    helper = save_utils.SaveHelper(self._savepath)\n    self.create_test_ckpt(10)\n    self.create_test_ckpt(20)\n    self.create_test_ckpt(300)\n    ckpt_steps = helper.get_existing_checkpoint_steps()\n    self.assertSetEqual(ckpt_steps, {10, 20, 300})\n\n  def test_exempt_checkpoints(self):\n    helper = save_utils.SaveHelper(self._savepath)\n    self.create_test_ckpt(10)\n    self.create_test_ckpt(20)\n    self.create_test_ckpt(30)\n    self.create_test_ckpt(40)\n    self.create_test_ckpt(50)\n    ckpt_steps = helper.get_existing_checkpoint_steps()\n    self.assertSetEqual(ckpt_steps, {10, 20, 30, 40, 50})\n\n    self.create_test_ckpt(60)\n    ckpt_steps = helper.get_existing_checkpoint_steps()\n    self.assertSetEqual(ckpt_steps, {10, 20, 40, 50, 60})\n\n\nclass SaverHookTest(tf.test.TestCase):\n\n  def get_ckpt_dir(self):\n    return os.path.join(os.environ[\"TEST_TMPDIR\"],\n                        type(self).__name__ + \"_\" + self._testMethodName)\n\n  def test_basic(self):\n    ckpt_dir = self.get_ckpt_dir()\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    global_step = tf.compat.v1.assign_add(global_step, 1)\n    with tf.compat.v1.train.SingularMonitoredSession([\n        save_utils.NoFirstSaveCheckpointSaverHook(checkpoint_dir=ckpt_dir,\n                                                  save_steps=100)\n    ]) as sess:\n      sess.run(global_step)\n    print(tf.io.gfile.glob(os.path.join(ckpt_dir, \"*\")))\n    # Will not save at beginning.\n    self.assertAllEqual(\n        tf.io.gfile.glob(os.path.join(ckpt_dir, \"model.ckpt-0\\\\.*\")), [])\n    # Will save after when session is closed.\n    self.assertGreater(\n        len(tf.io.gfile.glob(os.path.join(ckpt_dir, \"model.ckpt-1\\\\.*\"))), 0)\n\n  def test_op_error(self):\n\n    class AssertFailListener(tf.estimator.CheckpointSaverListener):\n\n      def __init__(self):\n        self._assert_op = tf.debugging.Assert(False, [False])\n\n      def before_save(self, session, global_step_value):\n        session.run(self._assert_op)\n\n    class FinallyAfterSaveListener(tf.estimator.CheckpointSaverListener):\n\n      def __init__(self):\n        self._called = False\n\n      @property\n      def called(self):\n        return self._called\n\n      def after_save(self, session, global_step_value):\n        self._called = True\n\n    ckpt_dir = self.get_ckpt_dir()\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    l = FinallyAfterSaveListener()\n    with tf.compat.v1.train.SingularMonitoredSession([\n        save_utils.NoFirstSaveCheckpointSaverHook(\n            checkpoint_dir=ckpt_dir,\n            save_steps=100,\n            listeners=[AssertFailListener()],\n            guard_saver_listeners=[l],\n            ignore_save_errors=True,\n            no_first_save=False)\n    ]) as sess:\n      pass\n    self.assertTrue(l.called)\n\n  def test_trigger_save(self):\n    ckpt_dir = self.get_ckpt_dir()\n    global_step = tf.compat.v1.train.get_or_create_global_step()\n    global_step = tf.compat.v1.assign_add(global_step, 1)\n    h = save_utils.NoFirstSaveCheckpointSaverHook(checkpoint_dir=ckpt_dir,\n                                                  save_steps=100)\n    with tf.compat.v1.train.SingularMonitoredSession([h]) as sess:\n      sess.run(global_step)\n      h.trigger_save(sess.raw_session())\n      self.assertGreater(\n          len(tf.io.gfile.glob(os.path.join(ckpt_dir, \"model.ckpt-1\\\\.*\"))), 0)\n\n\nclass SaverTest(tf.test.TestCase):\n\n  def basicSaveRestore(self, variable_op):\n    save_path = os.path.join(self.get_temp_dir(), \"basic_save_restore\")\n\n    with self.session(graph=tf.Graph()) as sess:\n      # Build a graph with 2 parameter nodes, and Save and\n      # Restore nodes for them.\n      v0 = variable_op(10.0, name=\"v0\")\n      v1 = variable_op(20.0, name=\"v1\")\n      v2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n      v2_init = v2.insert(\"k1\", 30.0)\n\n      # Initialize all variables\n      if not tf.executing_eagerly():\n        self.evaluate([tf.compat.v1.global_variables_initializer(), v2_init])\n\n        # Check that the parameter nodes have been initialized.\n      self.assertEqual(10.0, self.evaluate(v0))\n      self.assertEqual(20.0, self.evaluate(v1))\n      self.assertEqual(b\"k1\", self.evaluate(v2.keys()))\n      self.assertEqual(30.0, self.evaluate(v2.values()))\n\n      # Save the initialized values in the file at \"save_path\"\n      save = save_utils.PartialRecoverySaver(\n          {\n              \"v0\": v0,\n              \"v1\": v1,\n              \"v2\": v2.saveable\n          }, restore_sequentially=True)\n      val = save.save(sess, save_path)\n      self.assertTrue(isinstance(val, six.string_types))\n      self.assertEqual(save_path, val)\n\n    # Start a second session.  In that session the parameter nodes\n    # have not been initialized either.\n    with self.session(graph=tf.Graph()) as sess:\n      v0 = variable_op(-1.0, name=\"v0\")\n      v1 = variable_op(-1.0, name=\"v1\")\n      v2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n\n      # Assert that the variables are not initialized.\n      if not tf.executing_eagerly():\n        self.assertEqual(\n            len(tf.compat.v1.report_uninitialized_variables().eval()), 2)\n        self.assertEqual(0, len(self.evaluate(v2.keys())))\n        self.assertEqual(0, len(self.evaluate(v2.values())))\n      # Restore the saved values in the parameter nodes.\n      save = save_utils.PartialRecoverySaver({\n          \"v0\": v0,\n          \"v1\": v1,\n          \"v2\": v2.saveable\n      })\n      save.restore(sess, save_path)\n      # Check that the parameter nodes have been restored.\n      self.assertEqual(10.0, self.evaluate(v0))\n      self.assertEqual(20.0, self.evaluate(v1))\n      self.assertEqual(b\"k1\", self.evaluate(v2.keys()))\n      self.assertEqual(30.0, self.evaluate(v2.values()))\n\n    # Build another graph with 2 nodes, initialized\n    # differently, and a Restore node for them.\n    with self.session(graph=tf.Graph()) as sess:\n      v0_2 = variable_op(1000.0, name=\"v0\")\n      v1_2 = variable_op(2000.0, name=\"v1\")\n      v2_2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n      v2_init = v2_2.insert(\"k1000\", 3000.0)\n\n      # Check that the parameter nodes have been initialized.\n      if not tf.executing_eagerly():\n        init_all_op = [tf.compat.v1.global_variables_initializer(), v2_init]\n        self.evaluate(init_all_op)\n        # TODO(xpan): Why _mutable_hash_table_v2 doesn't create empty\n        # table as it claims in eager mode?\n        self.assertEqual(b\"k1000\", self.evaluate(v2_2.keys()))\n        self.assertEqual(3000.0, self.evaluate(v2_2.values()))\n      self.assertEqual(1000.0, self.evaluate(v0_2))\n      self.assertEqual(2000.0, self.evaluate(v1_2))\n\n      # Restore the values saved earlier in the parameter nodes.\n      save2 = save_utils.PartialRecoverySaver({\n          \"v0\": v0_2,\n          \"v1\": v1_2,\n          \"v2\": v2_2.saveable\n      })\n      save2.restore(sess, save_path)\n      # Check that the parameter nodes have been restored.\n      self.assertEqual(10.0, self.evaluate(v0_2))\n      self.assertEqual(20.0, self.evaluate(v1_2))\n      self.assertEqual(b\"k1\", self.evaluate(v2_2.keys()))\n      self.assertEqual(30.0, self.evaluate(v2_2.values()))\n\n  def testBasic(self):\n    self.basicSaveRestore(tf.Variable)\n\n  def testSaveMaxToKeep(self):\n    save_path = os.path.join(self.get_temp_dir(), \"test_save_max_to_keep\",\n                             \"model.ckpt\")\n\n    with self.session(graph=tf.Graph()) as sess:\n      # Build a graph with 2 parameter nodes, and Save and\n      # Restore nodes for them.\n      v0 = tf.Variable(10.0, name=\"v0\")\n      v1 = tf.Variable(20.0, name=\"v1\")\n      v2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n      v2_init = v2.insert(\"k1\", 30.0)\n\n      # Initialize all variables\n      if not tf.executing_eagerly():\n        self.evaluate([tf.compat.v1.global_variables_initializer(), v2_init])\n\n        # Check that the parameter nodes have been initialized.\n      self.assertEqual(10.0, self.evaluate(v0))\n      self.assertEqual(20.0, self.evaluate(v1))\n      self.assertEqual(b\"k1\", self.evaluate(v2.keys()))\n      self.assertEqual(30.0, self.evaluate(v2.values()))\n\n      # Save the initialized values in the file at \"save_path\"\n      save = save_utils.PartialRecoverySaver(\n          {\n              \"v0\": v0,\n              \"v1\": v1,\n              \"v2\": v2.saveable\n          },\n          restore_sequentially=True,\n          max_to_keep=2,\n          exempt_checkpoint_paths=['model.ckpt-2'])\n      val = save.save(sess, save_path, global_step=1)\n      self.assertEqual(save_path + '-1', val)\n      self.assertGreater(len(tf.io.gfile.glob(save_path + \"-1\\\\.*\")), 0)\n\n      val = save.save(sess, save_path, global_step=2)\n      self.assertEqual(save_path + '-2', val)\n      self.assertGreater(len(tf.io.gfile.glob(save_path + \"-1\\\\.*\")), 0)\n      self.assertGreater(len(tf.io.gfile.glob(save_path + \"-2\\\\.*\")), 0)\n\n      val = save.save(sess, save_path, global_step=3)\n      self.assertEqual(save_path + '-3', val)\n      self.assertGreater(len(tf.io.gfile.glob(save_path + \"-1\\\\.*\")), 0)\n      self.assertGreater(len(tf.io.gfile.glob(save_path + \"-2\\\\.*\")), 0)\n      self.assertGreater(len(tf.io.gfile.glob(save_path + \"-3\\\\.*\")), 0)\n\n      val = save.save(sess, save_path, global_step=4)\n      self.assertEqual(save_path + '-4', val)\n      self.assertEqual(len(tf.io.gfile.glob(save_path + \"-1\\\\.*\")), 0)\n      self.assertGreater(len(tf.io.gfile.glob(save_path + \"-2\\\\.*\")), 0)\n      self.assertGreater(len(tf.io.gfile.glob(save_path + \"-3\\\\.*\")), 0)\n      self.assertGreater(len(tf.io.gfile.glob(save_path + \"-4\\\\.*\")), 0)\n\n      save.restore(sess, val)\n      # Check that the parameter nodes have been restored.\n      self.assertEqual(10.0, self.evaluate(v0))\n      self.assertEqual(20.0, self.evaluate(v1))\n      self.assertEqual(b\"k1\", self.evaluate(v2.keys()))\n      self.assertEqual(30.0, self.evaluate(v2.values()))\n\n  @test_util.run_in_graph_and_eager_modes\n  def testResourceBasic(self):\n    self.basicSaveRestore(resource_variable_ops.ResourceVariable)\n\n  def testResourceColocation(self):\n    # train.Saver is V1 only API.\n    with tf.Graph().as_default():\n      partitioner = tf.compat.v1.fixed_size_partitioner(num_shards=2)\n      with tf.device(\"/job:ps/device:GPU:0\"):\n        v = tf.compat.v1.get_variable(\"v0\",\n                                      shape=[10, 2],\n                                      partitioner=partitioner,\n                                      use_resource=True)\n      save_utils.PartialRecoverySaver({\"v0\": v}).build()\n      save_op = None\n      for op in tf.compat.v1.get_default_graph().get_operations():\n        if op.type == \"SaveV2\":\n          save_op = op\n          break\n      assert save_op is not None\n      for save_inp in save_op.inputs[3:]:\n        # Input to SaveV2 op is placed on CPU of the same device as\n        # the Variable.\n        self.assertEqual(\"/job:ps/device:CPU:0\", save_inp.device)\n\n  def testResourceVariableReadOpsAddedDeterministically(self):\n    graph_defs = []\n    num_graphs = 10\n    for _ in range(num_graphs):\n      with tf.Graph().as_default() as g:\n        for i in range(20):\n          resource_variable_ops.ResourceVariable(i, name=\"var%s\" % i)\n        save_utils.PartialRecoverySaver()\n        graph_defs.append(g.as_graph_def())\n    for i in range(num_graphs - 1):\n      self.assertEqual(graph_defs[i], graph_defs[i + 1])\n\n  def testEagerBasic(self):\n    with context.eager_mode():\n      ckpt_prefix = os.path.join(self.get_temp_dir(), \"ckpt\")\n\n      v1 = resource_variable_ops.ResourceVariable(3.14, name=\"v1\")\n      v2 = resource_variable_ops.ResourceVariable([1, 2], name=\"v2\")\n      save = save_utils.PartialRecoverySaver([v1, v2])\n      save.save(None, ckpt_prefix)\n\n      v1.assign(0.0)\n      v2.assign([0, 0])\n      self.assertNear(0.0, self.evaluate(v1), 1e-5)\n      self.assertAllEqual([0, 0], self.evaluate(v2))\n\n      save.restore(None, ckpt_prefix)\n      self.assertNear(3.14, self.evaluate(v1), 1e-5)\n      self.assertAllEqual([1, 2], self.evaluate(v2))\n\n  def testEagerGraphCompatibility(self):\n    # Save from graph mode and restore from eager mode.\n    graph_ckpt_prefix = os.path.join(self.get_temp_dir(), \"graph_ckpt\")\n    with context.graph_mode():\n      with self.session(graph=tf.Graph()) as sess:\n        # Create a graph model and save the checkpoint.\n        w1 = resource_variable_ops.ResourceVariable(1.0, name=\"w1\")\n        w2 = resource_variable_ops.ResourceVariable(2.0, name=\"w2\")\n        graph_saver = save_utils.PartialRecoverySaver([w1, w2])\n        self.evaluate(tf.compat.v1.global_variables_initializer())\n        graph_saver.save(sess, graph_ckpt_prefix)\n\n    with context.eager_mode():\n      tf.compat.v1.reset_default_graph()\n\n      w1 = resource_variable_ops.ResourceVariable(0.0, name=\"w1\")\n      w2 = resource_variable_ops.ResourceVariable(0.0, name=\"w2\")\n\n      graph_saver = save_utils.PartialRecoverySaver([w1, w2])\n      graph_saver.restore(None, graph_ckpt_prefix)\n\n      self.assertAllEqual(self.evaluate(w1), 1.0)\n      self.assertAllEqual(self.evaluate(w2), 2.0)\n\n    # Save from eager mode and restore from graph mode.\n    eager_ckpt_prefix = os.path.join(self.get_temp_dir(), \"eager_ckpt\")\n    with context.eager_mode():\n      tf.compat.v1.reset_default_graph()\n\n      w3 = resource_variable_ops.ResourceVariable(3.0, name=\"w3\")\n      w4 = resource_variable_ops.ResourceVariable(4.0, name=\"w4\")\n\n      graph_saver = save_utils.PartialRecoverySaver([w3, w4])\n      graph_saver.save(None, eager_ckpt_prefix)\n\n    with context.graph_mode():\n      with self.session(graph=tf.Graph()) as sess:\n        w3 = resource_variable_ops.ResourceVariable(0.0, name=\"w3\")\n        w4 = resource_variable_ops.ResourceVariable(0.0, name=\"w4\")\n        graph_saver = save_utils.PartialRecoverySaver([w3, w4])\n        self.evaluate(tf.compat.v1.global_variables_initializer())\n        graph_saver.restore(sess, eager_ckpt_prefix)\n        self.assertAllEqual(w3, 3.0)\n        self.assertAllEqual(w4, 4.0)\n\n  @test_util.run_in_graph_and_eager_modes\n  def testResourceSaveRestoreCachingDevice(self):\n    save_path = os.path.join(self.get_temp_dir(), \"resource_cache\")\n    with self.session(graph=tf.Graph()) as sess:\n      v = resource_variable_ops.ResourceVariable([1],\n                                                 caching_device=\"/cpu:0\",\n                                                 name=\"v\")\n      if tf.executing_eagerly():\n        sess = None\n      else:\n        self.evaluate(tf.compat.v1.global_variables_initializer())\n      save = save_utils.PartialRecoverySaver([v])\n      save.save(sess, save_path)\n\n      save2 = save_utils.PartialRecoverySaver([v])\n      save2.restore(sess, save_path)\n      self.assertEqual(self.evaluate(v), [1])\n\n  def testNoAdditionalOpsAddedBySaverForResourceVariablesOutsideSaveScope(self):\n    with tf.Graph().as_default() as g:\n      v = resource_variable_ops.ResourceVariable(1.0, name=\"v\")\n      with tf.name_scope(\"saver1\"):\n        save_utils.PartialRecoverySaver()\n      with tf.name_scope(\"saver2\"):\n        save_utils.PartialRecoverySaver({\"name\": v})\n    ops_in_saver1_scope_but_not_save_scope = [\n        op for op in g.get_operations()\n        if (op.name.startswith(\"saver1/\") and\n            not op.name.startswith(\"saver1/save/\"))\n    ]\n    self.assertEqual(ops_in_saver1_scope_but_not_save_scope, [])\n    ops_in_saver2_scope_but_not_save_scope = [\n        op for op in g.get_operations()\n        if (op.name.startswith(\"saver2/\") and\n            not op.name.startswith(\"saver2/save/\"))\n    ]\n    self.assertEqual(ops_in_saver2_scope_but_not_save_scope, [])\n\n  def testSaveCopyRestoreWithSaveRelativePaths(self):\n    \"\"\"Save, copy checkpoint dir and restore from copied dir.\n\n    This only works for save_relative_paths=True.\n    \"\"\"\n    save_dir1 = os.path.join(self.get_temp_dir(), \"save_dir1\")\n    os.mkdir(save_dir1)\n    save_path1 = os.path.join(save_dir1, \"save_copy_restore\")\n\n    # train.Saver is V1 only API.\n    with tf.Graph().as_default():\n      # Build a graph with 2 parameter nodes, and Save and\n      # Restore nodes for them.\n      v0 = tf.compat.v1.Variable(10.0, name=\"v0\")\n      v1 = tf.compat.v1.Variable(20.0, name=\"v1\")\n      v2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n      v2_init = v2.insert(\"k1\", 30.0)\n      save = save_utils.PartialRecoverySaver(var_list={\n          \"v0\": v0,\n          \"v1\": v1,\n          \"v2\": v2.saveable\n      },\n                                             restore_sequentially=True,\n                                             save_relative_paths=True)\n      init_all_op = [tf.compat.v1.global_variables_initializer(), v2_init]\n\n      with self.cached_session() as sess:\n        # Initialize all variables\n        self.evaluate(init_all_op)\n\n        # Check that the parameter nodes have been initialized.\n        self.assertEqual(10.0, self.evaluate(v0))\n        self.assertEqual(20.0, self.evaluate(v1))\n        self.assertEqual(b\"k1\", self.evaluate(v2.keys()))\n        self.assertEqual(30.0, self.evaluate(v2.values()))\n\n        # Save the initialized values in the file at \"save_path\"\n        val = save.save(sess, save_path1)\n        self.assertTrue(isinstance(val, six.string_types))\n        self.assertEqual(save_path1, val)\n\n      self.assertEqual(tf.train.latest_checkpoint(save_dir1), save_path1)\n      save_dir2 = os.path.join(self.get_temp_dir(), \"save_dir2\")\n      os.renames(save_dir1, save_dir2)\n      save_path2 = os.path.join(save_dir2, \"save_copy_restore\")\n      self.assertEqual(tf.train.latest_checkpoint(save_dir2), save_path2)\n\n      # Start a second session.  In that session the parameter nodes\n      # have not been initialized either.\n      with self.cached_session() as sess:\n        v0 = tf.compat.v1.Variable(-1.0, name=\"v0\")\n        v1 = tf.compat.v1.Variable(-1.0, name=\"v1\")\n        v2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n        save = save_utils.PartialRecoverySaver({\n            \"v0\": v0,\n            \"v1\": v1,\n            \"v2\": v2.saveable\n        })\n\n        # Assert that the variables are not initialized.\n        self.assertEqual(\n            len(tf.compat.v1.report_uninitialized_variables().eval()), 2)\n        self.assertEqual(0, len(self.evaluate(v2.keys())))\n        self.assertEqual(0, len(self.evaluate(v2.values())))\n\n        # Restore the saved values in the parameter nodes.\n        save.restore(sess, save_path2)\n        # Check that the parameter nodes have been restored.\n        self.assertEqual(10.0, self.evaluate(v0))\n        self.assertEqual(20.0, self.evaluate(v1))\n        self.assertEqual(b\"k1\", self.evaluate(v2.keys()))\n        self.assertEqual(30.0, self.evaluate(v2.values()))\n\n  def testFilenameTensor(self):\n    # train.Saver is V1 only API.\n    with tf.Graph().as_default():\n      v0 = tf.compat.v1.Variable(0, name=\"v0\")\n      filename = b\"somerandomfilename\"\n      save = save_utils.PartialRecoverySaver({\"v0\": v0}, filename=filename)\n      with self.cached_session() as sess:\n        tensor = sess.graph.get_tensor_by_name(\n            save.saver_def.filename_tensor_name)\n        self.assertEqual(self.evaluate(tensor), filename)\n\n  def testInvalidPath(self):\n    v0 = tf.compat.v1.Variable(0, name=\"v0\")\n    with self.cached_session() as sess:\n      save = save_utils.PartialRecoverySaver({\"v0\": v0})\n      with self.assertRaisesRegex(\n          ValueError, \"The passed save_path is not a valid checkpoint:\"):\n        save.restore(sess, \"invalid path\")\n\n  @test_util.run_v1_only(\"train.Saver is V1 only API.\")\n  def testInt64(self):\n    save_path = os.path.join(self.get_temp_dir(), \"int64\")\n\n    with self.cached_session() as sess:\n      # Build a graph with 1 node, and save and restore for them.\n      v = tf.compat.v1.Variable(np.int64(15), name=\"v\")\n      save = save_utils.PartialRecoverySaver({\"v\": v},\n                                             restore_sequentially=True)\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n\n      # Save the initialized values in the file at \"save_path\"\n      val = save.save(sess, save_path)\n      self.assertTrue(isinstance(val, six.string_types))\n      self.assertEqual(save_path, val)\n\n      with self.cached_session() as sess:\n        v = tf.compat.v1.Variable(np.int64(-1), name=\"v\")\n        save = save_utils.PartialRecoverySaver({\"v\": v})\n\n      with self.assertRaisesWithPredicateMatch(\n          tf.errors.OpError, lambda e: \"uninitialized value v\" in e.message):\n        self.evaluate(v)\n\n      # Restore the saved values in the parameter nodes.\n      save.restore(sess, save_path)\n      # Check that the parameter nodes have been restored.\n      self.assertEqual(np.int64(15), self.evaluate(v))\n\n  def testSomeErrors(self):\n    with tf.Graph().as_default():\n      v0 = tf.compat.v1.Variable([10.0], name=\"v0\")\n      v1 = tf.compat.v1.Variable([20.0], name=\"v1\")\n      v2 = tf.compat.v1.Variable([20.0], name=\"v2\")\n      v2._set_save_slice_info(tf.Variable.SaveSliceInfo(\"v1\", [1], [0], [1]))\n\n      # By default the name used for \"v2\" will be \"v1\" and raise an error.\n      with self.assertRaisesRegex(ValueError, \"same name: v1\"):\n        save_utils.PartialRecoverySaver([v0, v1, v2])\n\n      # The names are different and will work.\n      save_utils.PartialRecoverySaver({\"vee1\": v1, \"other\": [v2]})\n\n      # Partitioned variables also cause name conflicts.\n      p_v1 = tf.compat.v1.get_variable(\n          \"p_v1\",\n          shape=[4, 5],\n          partitioner=tf.compat.v1.fixed_size_partitioner(num_shards=2))\n      p_v2 = tf.compat.v1.get_variable(\n          \"p_v2\",\n          shape=[4, 5],\n          partitioner=tf.compat.v1.fixed_size_partitioner(num_shards=2))\n      p_v2._name = \"p_v1\"\n      with self.assertRaisesRegex(ValueError, \"same name: p_v1\"):\n        save_utils.PartialRecoverySaver([p_v1, p_v2])\n\n  def testSameName(self):\n    with tf.Graph().as_default():\n      v0 = tf.compat.v1.Variable([10.0], name=\"v0\")\n      v2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n\n      # Saving one variable under two names raises an error.\n      with self.assertRaisesRegex(\n          ValueError, \"The same saveable will be restored with two names: v0\"):\n        save_utils.PartialRecoverySaver({\"v0\": v0, \"v0too\": v0})\n\n      # Ditto for custom saveables.\n      with self.assertRaisesRegex(\n          ValueError, \"The same saveable will be restored with two names: v2\"):\n        save_utils.PartialRecoverySaver({\n            \"v2\": v2.saveable,\n            \"v2too\": v2.saveable\n        })\n\n      # Verify non-duplicate names work.\n      save_utils.PartialRecoverySaver({\"v0\": v0, \"v2\": v2.saveable})\n\n  @test_util.run_v1_only(\"train.Saver and VariableV1 are V1 only APIs.\")\n  def testBasicsWithListOfVariables(self):\n    save_path = os.path.join(self.get_temp_dir(), \"basics_with_list\")\n\n    with self.session(graph=tf.Graph()) as sess:\n      # Build a graph with 2 parameter nodes, and Save and\n      # Restore nodes for them.\n      v0 = tf.compat.v1.Variable(10.0, name=\"v0\")\n      v1 = tf.compat.v1.Variable(20.0, name=\"v1\")\n      v2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n      v2_init = v2.insert(\"k1\", 30.0)\n      save = save_utils.PartialRecoverySaver([v0, v1, v2.saveable])\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      v2_init.run()\n\n      # Check that the parameter nodes have been initialized.\n      self.assertEqual(10.0, self.evaluate(v0))\n      self.assertEqual(20.0, self.evaluate(v1))\n      self.assertEqual(b\"k1\", self.evaluate(v2.keys()))\n      self.assertEqual(30.0, self.evaluate(v2.values()))\n\n      # Save the initialized values in the file at \"save_path\"\n      val = save.save(sess, save_path)\n      self.assertTrue(isinstance(val, six.string_types))\n      self.assertEqual(save_path, val)\n\n    # Start a second session.  In that session the variables\n    # have not been initialized either.\n    with self.session(graph=tf.Graph()) as sess:\n      v0 = tf.compat.v1.Variable(-1.0, name=\"v0\")\n      v1 = tf.compat.v1.Variable(-1.0, name=\"v1\")\n      v2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n      save = save_utils.PartialRecoverySaver([v0, v1, v2.saveable])\n\n      with self.assertRaisesWithPredicateMatch(\n          tf.errors.OpError, lambda e: \"uninitialized value v0\" in e.message):\n        self.evaluate(v0)\n      with self.assertRaisesWithPredicateMatch(\n          tf.errors.OpError, lambda e: \"uninitialized value v1\" in e.message):\n        self.evaluate(v1)\n      self.assertEqual(0, len(self.evaluate(v2.keys())))\n      self.assertEqual(0, len(self.evaluate(v2.values())))\n\n      # Restore the saved values in the parameter nodes.\n      save.restore(sess, save_path)\n      # Check that the parameter nodes have been restored.\n      self.assertEqual(10.0, self.evaluate(v0))\n      self.assertEqual(20.0, self.evaluate(v1))\n      self.assertEqual(b\"k1\", self.evaluate(v2.keys()))\n      self.assertEqual(30.0, self.evaluate(v2.values()))\n\n    # Build another graph with 2 nodes, initialized\n    # differently, and a Restore node for them.\n    with self.session(graph=tf.Graph()) as sess:\n      v0_2 = tf.compat.v1.Variable(1000.0, name=\"v0\")\n      v1_2 = tf.compat.v1.Variable(2000.0, name=\"v1\")\n      v2_2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n      save2 = save_utils.PartialRecoverySaver([v0_2, v1_2, v2_2.saveable])\n      v2_2.insert(\"k1000\", 3000.0).run()\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n\n      # Check that the parameter nodes have been initialized.\n      self.assertEqual(1000.0, self.evaluate(v0_2))\n      self.assertEqual(2000.0, self.evaluate(v1_2))\n      self.assertEqual(b\"k1000\", self.evaluate(v2_2.keys()))\n      self.assertEqual(3000.0, self.evaluate(v2_2.values()))\n      # Restore the values saved earlier in the parameter nodes.\n      save2.restore(sess, save_path)\n      # Check that the parameter nodes have been restored.\n      self.assertEqual(10.0, self.evaluate(v0_2))\n      self.assertEqual(20.0, self.evaluate(v1_2))\n      self.assertEqual(b\"k1\", self.evaluate(v2_2.keys()))\n      self.assertEqual(30.0, self.evaluate(v2_2.values()))\n\n  def _SaveAndLoad(self, var_name, var_value, other_value, save_path):\n    with self.session(graph=tf.Graph()) as sess:\n      var = resource_variable_ops.ResourceVariable(var_value, name=var_name)\n      save = save_utils.PartialRecoverySaver({var_name: var})\n      if not tf.executing_eagerly():\n        self.evaluate(var.initializer)\n      val = save.save(sess, save_path)\n      self.assertEqual(save_path, val)\n    with self.session(graph=tf.Graph()) as sess:\n      var = resource_variable_ops.ResourceVariable(other_value, name=var_name)\n      save = save_utils.PartialRecoverySaver({var_name: var})\n      save.restore(sess, save_path)\n      self.assertAllClose(var_value, self.evaluate(var))\n\n  def testCacheRereadsFile(self):\n    save_path = os.path.join(self.get_temp_dir(), \"cache_rereads\")\n    # Save and reload one Variable named \"var0\".\n    self._SaveAndLoad(\"var0\", 0.0, 1.0, save_path)\n    # Save and reload one Variable named \"var1\" in the same file.\n    # The cached readers should know to re-read the file.\n    self._SaveAndLoad(\"var1\", 1.1, 2.2, save_path)\n\n  def testAllowEmpty(self):\n    save_path = os.path.join(self.get_temp_dir(), \"allow_empty\")\n    # train.Saver is V1 only API.\n    with tf.Graph().as_default(), self.cached_session() as sess:\n      _ = tf.constant(1)\n      save = save_utils.PartialRecoverySaver(allow_empty=True)\n      val = save.save(sess, save_path)\n      self.assertIsNone(val)\n    with tf.Graph().as_default(), self.cached_session() as sess:\n      save = save_utils.PartialRecoverySaver(allow_empty=True)\n      save.restore(sess, save_path)\n\n  def testGPU(self):\n    if not tf.test.is_gpu_available():\n      return\n    save_path = os.path.join(self.get_temp_dir(), \"gpu\")\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      with sess.graph.device(tf.test.gpu_device_name()):\n        v0_1 = tf.compat.v1.Variable(123.45)\n      save = save_utils.PartialRecoverySaver({\"v0\": v0_1})\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      save.save(sess, save_path)\n\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      with sess.graph.device(tf.test.gpu_device_name()):\n        v0_2 = tf.compat.v1.Variable(543.21)\n      save = save_utils.PartialRecoverySaver({\"v0\": v0_2})\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n\n  def testSharedServerOnGPU(self):\n    if not tf.test.is_gpu_available():\n      return\n    save_path = os.path.join(self.get_temp_dir(), \"gpu\")\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      with sess.graph.device(tf.test.gpu_device_name()):\n        v0_1 = tf.compat.v1.Variable(123.45)\n      save = save_utils.PartialRecoverySaver({\"v0\": v0_1},\n                                             sharded=True,\n                                             allow_empty=True)\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      save.save(sess, save_path)\n\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      with sess.graph.device(tf.test.gpu_device_name()):\n        v0_2 = tf.compat.v1.Variable(543.21)\n      save = save_utils.PartialRecoverySaver({\"v0\": v0_2},\n                                             sharded=True,\n                                             allow_empty=True)\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n\n  def testVariables(self):\n    save_path = os.path.join(self.get_temp_dir(), \"variables\")\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      one = tf.compat.v1.Variable(1.0)\n      twos = tf.compat.v1.Variable([2.0, 2.0, 2.0])\n      v2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n      init = tf.compat.v1.global_variables_initializer()\n      save = save_utils.PartialRecoverySaver()\n      init.run()\n      v2.insert(\"k1\", 3.0).run()\n      save.save(sess, save_path)\n\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      one = tf.compat.v1.Variable(0.0)\n      twos = tf.compat.v1.Variable([0.0, 0.0, 0.0])\n      v2 = saver_test_utils.CheckpointedOp(name=\"v2\")\n      # Saver with no arg, defaults to 'all variables'.\n      save = save_utils.PartialRecoverySaver()\n      save.restore(sess, save_path)\n      self.assertAllClose(1.0, self.evaluate(one))\n      self.assertAllClose([2.0, 2.0, 2.0], self.evaluate(twos))\n      self.assertEqual(b\"k1\", self.evaluate(v2.keys()))\n      self.assertEqual(3.0, self.evaluate(v2.values()))\n\n  def testVarListShouldBeEmptyInDeferredBuild(self):\n    with tf.Graph().as_default():\n      v = tf.compat.v1.Variable(1.0)\n      with self.assertRaisesRegex(ValueError, \"defer_build\"):\n        save_utils.PartialRecoverySaver([v], defer_build=True)\n\n  def testBuildShouldBeCalledBeforeSaveInCaseOfDeferBuild(self):\n    save_path = os.path.join(self.get_temp_dir(), \"error_deferred_build\")\n    with tf.Graph().as_default(), tf.compat.v1.Session() as sess:\n      tf.compat.v1.Variable(1.0)\n      saver = save_utils.PartialRecoverySaver(defer_build=True)\n      with self.assertRaisesRegex(RuntimeError, \"build\"):\n        saver.save(sess, save_path)\n\n  def testDeferredBuild(self):\n    save_path = os.path.join(self.get_temp_dir(), \"deferred_build\")\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      one = tf.compat.v1.Variable(1.0)\n      save = save_utils.PartialRecoverySaver(defer_build=True)\n      # if build is not deferred, saver cannot save the `twos`.\n      twos = tf.compat.v1.Variable([2.0, 2.0, 2.0])\n      init = tf.compat.v1.global_variables_initializer()\n      save.build()\n      init.run()\n      save.save(sess, save_path)\n\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      one = tf.compat.v1.Variable(0.0)\n      twos = tf.compat.v1.Variable([0.0, 0.0, 0.0])\n      # Saver with no arg, defaults to 'all variables'.\n      save = save_utils.PartialRecoverySaver()\n      save.restore(sess, save_path)\n      self.assertAllClose(1.0, self.evaluate(one))\n      self.assertAllClose([2.0, 2.0, 2.0], self.evaluate(twos))\n\n  @test_util.run_v1_only(\"train.Saver is V1 only API.\")\n  def testReshape(self):\n    save_path = os.path.join(self.get_temp_dir(), \"variables_reshape\")\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      var = tf.compat.v1.Variable([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])\n      init = tf.compat.v1.global_variables_initializer()\n      save = save_utils.PartialRecoverySaver()\n      init.run()\n      save.save(sess, save_path)\n\n    # Error when restoring with default reshape=False\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      var = tf.compat.v1.Variable([[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]])\n      save = save_utils.PartialRecoverySaver()\n      with self.assertRaisesRegex(\n          tf.errors.InvalidArgumentError,\n          \"Assign requires shapes of both tensors to match.\"):\n        save.restore(sess, save_path)\n\n    # Restored to new shape with reshape=True\n    with tf.compat.v1.Session(\"\", graph=tf.Graph()) as sess:\n      var = tf.compat.v1.Variable([[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]])\n      save = save_utils.PartialRecoverySaver(reshape=True)\n      save.restore(sess, save_path)\n      self.assertAllClose([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]],\n                          self.evaluate(var))\n\n  @test_util.run_in_graph_and_eager_modes\n  def testSaveWithGlobalStep(self, pad_step_number=False):\n    save_path = os.path.join(self.get_temp_dir(), \"ckpt_with_global_step\")\n    global_step_int = 5\n    # Save and reload one Variable named \"var0\".\n    self._SaveAndLoad(\"var0\", 0.0, 1.0, save_path)\n    for use_tensor in [True, False]:\n      with self.session(graph=tf.Graph()):\n        var = resource_variable_ops.ResourceVariable(1.0, name=\"var0\")\n        save = save_utils.PartialRecoverySaver({var._shared_name: var},\n                                               pad_step_number=pad_step_number)\n        if tf.executing_eagerly():\n          sess = None\n        else:\n          self.evaluate(var.initializer)\n          sess = tf.compat.v1.get_default_session()\n        if use_tensor:\n          global_step = tf.constant(global_step_int)\n          val = save.save(sess, save_path, global_step=global_step)\n        else:\n          val = save.save(sess, save_path, global_step=global_step_int)\n        if pad_step_number:\n          expected_save_path = \"%s-%s\" % (save_path,\n                                          \"{:08d}\".format(global_step_int))\n        else:\n          expected_save_path = \"%s-%d\" % (save_path, global_step_int)\n        self.assertEqual(expected_save_path, val)\n\n  def testSaveWithGlobalStepWithPadding(self):\n    self.testSaveWithGlobalStep(pad_step_number=True)\n\n  def testSaveToNonexistingPath(self):\n    file_io.write_string_to_file(\n        os.path.join(self.get_temp_dir(), \"actually_a_file\"), \"\")\n    paths = [\n        os.path.join(self.get_temp_dir(), \"nonexisting_dir/path\"),\n        os.path.join(self.get_temp_dir(), \"other_nonexisting_dir/path1/path2\"),\n        os.path.join(self.get_temp_dir(), \"actually_a_file/path\"),\n    ]\n\n    for save_path in paths:\n      # Build a graph with 2 parameter nodes, and Save and\n      # Restore nodes for them.\n      v0 = tf.compat.v1.Variable(10.0, name=\"v0\")\n      v1 = tf.compat.v1.Variable(20.0, name=\"v1\")\n      save = save_utils.PartialRecoverySaver({\n          \"v0\": v0,\n          \"v1\": v1\n      },\n                                             restore_sequentially=True)\n      init_all_op = tf.compat.v1.global_variables_initializer()\n\n      # In the case where the parent directory doesn't exist, whether or not the\n      # save succeeds or fails is implementation dependent.  Therefore we allow\n      # both cases.\n      try:\n        with self.cached_session() as sess:\n          # Initialize all variables\n          self.evaluate(init_all_op)\n\n          # Check that the parameter nodes have been initialized.\n          self.assertEqual(10.0, self.evaluate(v0))\n          self.assertEqual(20.0, self.evaluate(v1))\n\n          # Save the graph.\n          save.save(sess, save_path)\n\n        with self.cached_session() as sess:\n          # Restore the saved values in the parameter nodes.\n          save.restore(sess, save_path)\n          # Check that the parameter nodes have been restored.\n          self.assertEqual(10.0, self.evaluate(v0))\n          self.assertEqual(20.0, self.evaluate(v1))\n      except ValueError as exc:\n        error_msg_template = \"Parent directory of {} doesn't exist, can't save.\"\n        self.assertEqual(error_msg_template.format(save_path), str(exc))\n\n  def testSaveToURI(self):\n    # ParseURI functions don't work on Windows yet.\n    # TODO(jhseu): Remove this check when it works.\n    if os.name == \"nt\":\n      self.skipTest(\"Local URI support doesn't work on Windows\")\n    save_path = \"file://\" + os.path.join(self.get_temp_dir(), \"uri\")\n\n    # Build a graph with 2 parameter nodes, and Save and\n    # Restore nodes for them.\n    v0 = tf.compat.v1.Variable(10.0, name=\"v0\")\n    v1 = tf.compat.v1.Variable(20.0, name=\"v1\")\n    save = save_utils.PartialRecoverySaver({\n        \"v0\": v0,\n        \"v1\": v1\n    },\n                                           restore_sequentially=True)\n    init_all_op = tf.compat.v1.global_variables_initializer()\n\n    with self.cached_session() as sess:\n      # Initialize all variables\n      self.evaluate(init_all_op)\n\n      # Check that the parameter nodes have been initialized.\n      self.assertEqual(10.0, self.evaluate(v0))\n      self.assertEqual(20.0, self.evaluate(v1))\n      save.save(sess, save_path)\n\n  def testSaveRestoreAndValidateVariableDtype(self):\n    for variable_op in [tf.Variable, resource_variable_ops.ResourceVariable]:\n      save_path = os.path.join(self.get_temp_dir(), \"basic_save_restore\")\n\n      # Build the first session.\n      with self.session(graph=tf.Graph()) as sess:\n        v0 = variable_op(10.0, name=\"v0\", dtype=tf.dtypes.float32)\n\n        if not tf.executing_eagerly():\n          self.evaluate([tf.compat.v1.global_variables_initializer()])\n\n        save = save_utils.PartialRecoverySaver({\"v0\": v0})\n        save.save(sess, save_path)\n\n      # Start a second session.\n      with self.session(graph=tf.Graph()) as sess:\n        v0_wrong_dtype = variable_op(1, name=\"v0\", dtype=tf.dtypes.int32)\n        # Restore the saved value with different dtype\n        # in the parameter nodes.\n        save = save_utils.PartialRecoverySaver({\"v0\": v0_wrong_dtype})\n        with self.assertRaisesRegex(tf.errors.InvalidArgumentError,\n                                    \"original dtype\"):\n          save.restore(sess, save_path)\n\n  # Test restoring large tensors (triggers a thread pool)\n  def testRestoreLargeTensors(self):\n    save_dir = self.get_temp_dir()\n\n    def _model():\n      small_v = [\n          tf.compat.v1.get_variable(\"small%d\" % i,\n                                    shape=[10, 2],\n                                    use_resource=True) for i in range(5)\n      ]\n      large_v = [\n          tf.compat.v1.get_variable(\"large%d\" % i,\n                                    shape=[32000, 1000],\n                                    use_resource=True) for i in range(3)\n      ]\n      return small_v + large_v\n\n    save_graph = tf.Graph()\n    with save_graph.as_default(), self.session(graph=save_graph) as sess:\n      orig_vars = _model()\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      save = save_utils.PartialRecoverySaver(max_to_keep=1)\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      save.save(sess, save_dir)\n      orig_vals = self.evaluate(orig_vars)\n\n    restore_graph = tf.Graph()\n    with restore_graph.as_default(), self.session(graph=restore_graph) as sess:\n      restored_vars = _model()\n      save = save_utils.PartialRecoverySaver(max_to_keep=1)\n      save.restore(sess, save_dir)\n      restored_vals = self.evaluate(restored_vars)\n\n    for orig, restored in zip(orig_vals, restored_vals):\n      self.assertAllEqual(orig, restored)\n\n\nclass SaveRestoreShardedTest(tf.test.TestCase):\n\n  def testIterators(self):\n    save_path = os.path.join(self.get_temp_dir(), \"sharded_iterators\")\n\n    # Build a graph with 2 parameter nodes on different devices and save.\n    with tf.compat.v1.Session(\n        target=\"\",\n        config=config_pb2.ConfigProto(device_count={\"CPU\": 2})) as sess:\n      with sess.graph.device(\"/cpu:0\"):\n        ds0 = tf.data.Dataset.range(10)\n        it0 = tf.compat.v1.data.make_initializable_iterator(ds0)\n        get_next0 = it0.get_next()\n      saveable0 = iterator_ops._IteratorSaveable(it0._iterator_resource,\n                                                 name=\"saveable_it0\")\n\n      with sess.graph.device(\"/cpu:1\"):\n        ds1 = tf.data.Dataset.range(20)\n        it1 = tf.compat.v1.data.make_initializable_iterator(ds1)\n        get_next1 = it1.get_next()\n      saveable1 = iterator_ops._IteratorSaveable(it1._iterator_resource,\n                                                 name=\"saveable_it1\")\n      saver = save_utils.PartialRecoverySaver(\n          {\n              \"it0\": saveable0,\n              \"it1\": saveable1\n          }, sharded=True)\n      self.evaluate(it0.initializer)\n      self.evaluate(it1.initializer)\n      self.assertEqual(0, self.evaluate(get_next0))\n      self.assertEqual(1, self.evaluate(get_next0))\n      self.assertEqual(0, self.evaluate(get_next1))\n      val = saver.save(sess, save_path)\n      self.assertEqual(save_path, val)\n      data_files = tf.io.gfile.glob(save_path + \".data*\")\n      self.assertEqual(2, len(data_files))\n\n    # Restore\n    with tf.compat.v1.Session(\n        target=\"\",\n        config=config_pb2.ConfigProto(device_count={\"CPU\": 2})) as sess:\n      with sess.graph.device(\"/cpu:0\"):\n        ds0 = tf.data.Dataset.range(10)\n        it0 = tf.compat.v1.data.make_initializable_iterator(ds0)\n        get_next0 = it0.get_next()\n      saveable0 = iterator_ops._IteratorSaveable(it0._iterator_resource,\n                                                 name=\"saveable_it0\")\n\n      with sess.graph.device(\"/cpu:1\"):\n        ds1 = tf.data.Dataset.range(20)\n        it1 = tf.compat.v1.data.make_initializable_iterator(ds1)\n        get_next1 = it1.get_next()\n      saveable1 = iterator_ops._IteratorSaveable(it1._iterator_resource,\n                                                 name=\"saveable_it1\")\n      saver = save_utils.PartialRecoverySaver(\n          {\n              \"it0\": saveable0,\n              \"it1\": saveable1\n          }, sharded=True)\n      self.evaluate(it0.initializer)\n      self.evaluate(it1.initializer)\n      saver.restore(sess, save_path)\n      self.assertEqual(2, self.evaluate(get_next0))\n      self.assertEqual(1, self.evaluate(get_next1))\n\n  def testIteratorsUnshardedRestore(self):\n    save_path = os.path.join(self.get_temp_dir(), \"restore_unsharded_iterators\")\n\n    # Build a graph with 2 parameter nodes on different devices and save.\n    with tf.compat.v1.Session(\n        target=\"\",\n        config=config_pb2.ConfigProto(device_count={\"CPU\": 2})) as sess:\n      with sess.graph.device(\"/cpu:0\"):\n        ds0 = tf.data.Dataset.range(10)\n        it0 = tf.compat.v1.data.make_initializable_iterator(ds0)\n        get_next0 = it0.get_next()\n      saveable0 = iterator_ops._IteratorSaveable(it0._iterator_resource,\n                                                 name=\"saveable_it0\")\n\n      with sess.graph.device(\"/cpu:1\"):\n        ds1 = tf.data.Dataset.range(20)\n        it1 = tf.compat.v1.data.make_initializable_iterator(ds1)\n        get_next1 = it1.get_next()\n      saveable1 = iterator_ops._IteratorSaveable(it1._iterator_resource,\n                                                 name=\"saveable_it1\")\n      saver = save_utils.PartialRecoverySaver(\n          {\n              \"it0\": saveable0,\n              \"it1\": saveable1\n          }, sharded=True)\n      self.evaluate(it0.initializer)\n      self.evaluate(it1.initializer)\n      self.assertEqual(0, self.evaluate(get_next0))\n      self.assertEqual(1, self.evaluate(get_next0))\n      self.assertEqual(0, self.evaluate(get_next1))\n      val = saver.save(sess, save_path)\n      self.assertEqual(save_path, val)\n      data_files = tf.io.gfile.glob(save_path + \".data*\")\n      self.assertEqual(2, len(data_files))\n\n    # Restore\n    with tf.compat.v1.Session(\n        target=\"\",\n        config=config_pb2.ConfigProto(device_count={\"CPU\": 2})) as sess:\n      with sess.graph.device(\"/cpu:0\"):\n        ds0 = tf.data.Dataset.range(10)\n        it0 = tf.compat.v1.data.make_initializable_iterator(ds0)\n        get_next0 = it0.get_next()\n      saveable0 = iterator_ops._IteratorSaveable(it0._iterator_resource,\n                                                 name=\"saveable_it0\")\n\n      with sess.graph.device(\"/cpu:1\"):\n        ds1 = tf.data.Dataset.range(20)\n        it1 = tf.compat.v1.data.make_initializable_iterator(ds1)\n        get_next1 = it1.get_next()\n      saveable1 = iterator_ops._IteratorSaveable(it1._iterator_resource,\n                                                 name=\"saveable_it1\")\n      saver = save_utils.PartialRecoverySaver(\n          {\n              \"it0\": saveable0,\n              \"it1\": saveable1\n          }, sharded=False)\n      self.evaluate(it0.initializer)\n      self.evaluate(it1.initializer)\n      saver.restore(sess, save_path)\n      self.assertEqual(2, self.evaluate(get_next0))\n      self.assertEqual(1, self.evaluate(get_next1))\n\n\nclass MaxToKeepTest(tf.test.TestCase):\n\n  def _get_test_dir(self, dirname):\n    test_dir = os.path.join(self.get_temp_dir(), dirname)\n    tf.io.gfile.makedirs(test_dir)\n    return test_dir\n\n  def assertCheckpointState(self, model_checkpoint_path,\n                            all_model_checkpoint_paths, save_dir):\n    checkpoint_state = tf.train.get_checkpoint_state(save_dir)\n    self.assertEqual(checkpoint_state.model_checkpoint_path,\n                     model_checkpoint_path)\n    self.assertEqual(checkpoint_state.all_model_checkpoint_paths,\n                     all_model_checkpoint_paths)\n\n  def testMaxToKeepEager(self):\n    with context.eager_mode():\n      save_dir = self._get_test_dir(\"max_to_keep_eager\")\n\n      v = tf.compat.v1.Variable(10.0, name=\"v\")\n      save = save_utils.PartialRecoverySaver({\"v\": v}, max_to_keep=2)\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      if not tf.executing_eagerly():\n        self.assertEqual([], save.last_checkpoints)\n\n      s1 = save.save(None, os.path.join(save_dir, \"s1\"))\n      self.assertEqual([s1], save.last_checkpoints)\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertCheckpointState(model_checkpoint_path=s1,\n                                 all_model_checkpoint_paths=[s1],\n                                 save_dir=save_dir)\n\n      s2 = save.save(None, os.path.join(save_dir, \"s2\"))\n      self.assertEqual([s1, s2], save.last_checkpoints)\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertCheckpointState(model_checkpoint_path=s2,\n                                 all_model_checkpoint_paths=[s1, s2],\n                                 save_dir=save_dir)\n\n      s3 = save.save(None, os.path.join(save_dir, \"s3\"))\n      self.assertEqual([s2, s3], save.last_checkpoints)\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertCheckpointState(model_checkpoint_path=s3,\n                                 all_model_checkpoint_paths=[s2, s3],\n                                 save_dir=save_dir)\n\n      # Create a second helper, identical to the first.\n      save2 = save_utils.PartialRecoverySaver({\"v\": v}, max_to_keep=2)\n      save2.set_last_checkpoints_with_time([\n          (s, np.inf) for s in save.last_checkpoints\n      ])\n\n      # Exercise the first helper.\n\n      # Adding s2 again (old s2 is removed first, then new s2 appended)\n      s2 = save.save(None, os.path.join(save_dir, \"s2\"))\n      self.assertEqual([s3, s2], save.last_checkpoints)\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertCheckpointState(model_checkpoint_path=s2,\n                                 all_model_checkpoint_paths=[s3, s2],\n                                 save_dir=save_dir)\n\n      # Adding s1 (s3 should now be deleted as oldest in list)\n      s1 = save.save(None, os.path.join(save_dir, \"s1\"))\n      self.assertEqual([s2, s1], save.last_checkpoints)\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertCheckpointState(model_checkpoint_path=s1,\n                                 all_model_checkpoint_paths=[s2, s1],\n                                 save_dir=save_dir)\n\n      s2 = save2.save(None, os.path.join(save_dir, \"s2\"))\n      self.assertEqual([s3, s2], save2.last_checkpoints)\n      # Created by the first helper.\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      # Deleted by the first helper.\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s3))\n\n  def testNonSharded(self):\n    save_dir = self._get_test_dir(\"max_to_keep_non_sharded\")\n\n    # train.Saver is V1 only API.\n    with tf.Graph().as_default(), self.cached_session() as sess:\n      v = tf.compat.v1.Variable(10.0, name=\"v\")\n      save = save_utils.PartialRecoverySaver({\"v\": v}, max_to_keep=2)\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      self.assertEqual([], save.last_checkpoints)\n\n      s1 = save.save(sess, os.path.join(save_dir, \"s1\"))\n      self.assertEqual([s1], save.last_checkpoints)\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertCheckpointState(model_checkpoint_path=s1,\n                                 all_model_checkpoint_paths=[s1],\n                                 save_dir=save_dir)\n\n      s2 = save.save(sess, os.path.join(save_dir, \"s2\"))\n      self.assertEqual([s1, s2], save.last_checkpoints)\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertCheckpointState(model_checkpoint_path=s2,\n                                 all_model_checkpoint_paths=[s1, s2],\n                                 save_dir=save_dir)\n\n      s3 = save.save(sess, os.path.join(save_dir, \"s3\"))\n      self.assertEqual([s2, s3], save.last_checkpoints)\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertCheckpointState(model_checkpoint_path=s3,\n                                 all_model_checkpoint_paths=[s2, s3],\n                                 save_dir=save_dir)\n\n      # Create a second helper, identical to the first.\n      save2 = save_utils.PartialRecoverySaver(saver_def=save.as_saver_def())\n      save2.set_last_checkpoints_with_time([\n          (s, np.inf) for s in save.last_checkpoints\n      ])\n\n      # Create a third helper, with the same configuration but no knowledge of\n      # previous checkpoints.\n      save3 = save_utils.PartialRecoverySaver(saver_def=save.as_saver_def())\n\n      # Exercise the first helper.\n\n      # Adding s2 again (old s2 is removed first, then new s2 appended)\n      s2 = save.save(sess, os.path.join(save_dir, \"s2\"))\n      self.assertEqual([s3, s2], save.last_checkpoints)\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertFalse(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s1)))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s3)))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s2)))\n      self.assertCheckpointState(model_checkpoint_path=s2,\n                                 all_model_checkpoint_paths=[s3, s2],\n                                 save_dir=save_dir)\n\n      # Adding s1 (s3 should now be deleted as oldest in list)\n      s1 = save.save(sess, os.path.join(save_dir, \"s1\"))\n      self.assertEqual([s2, s1], save.last_checkpoints)\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertFalse(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s3)))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s2)))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s1)))\n      self.assertCheckpointState(model_checkpoint_path=s1,\n                                 all_model_checkpoint_paths=[s2, s1],\n                                 save_dir=save_dir)\n\n      # Exercise the second helper.\n\n      # Adding s2 again (old s2 is removed first, then new s2 appended)\n      s2 = save2.save(sess, os.path.join(save_dir, \"s2\"))\n      self.assertEqual([s3, s2], save2.last_checkpoints)\n      # Created by the first helper.\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s1)))\n      # Deleted by the first helper.\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertFalse(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s3)))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s2)))\n      self.assertCheckpointState(model_checkpoint_path=s2,\n                                 all_model_checkpoint_paths=[s3, s2],\n                                 save_dir=save_dir)\n\n      # Adding s1 (s3 should now be deleted as oldest in list)\n      s1 = save2.save(sess, os.path.join(save_dir, \"s1\"))\n      self.assertEqual([s2, s1], save2.last_checkpoints)\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertFalse(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s3)))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s2)))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s1)))\n      self.assertCheckpointState(model_checkpoint_path=s1,\n                                 all_model_checkpoint_paths=[s2, s1],\n                                 save_dir=save_dir)\n\n      # Exercise the third helper.\n\n      # Adding s2 again (but helper is unaware of previous s2)\n      s2 = save3.save(sess, os.path.join(save_dir, \"s2\"))\n      self.assertEqual([s2], save3.last_checkpoints)\n      # Created by the first helper.\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s1)))\n      # Deleted by the first helper.\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertFalse(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s3)))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s2)))\n      # Even though the file for s1 exists, this saver isn't aware of it, which\n      # is why it doesn't end up in the checkpoint state.\n      self.assertCheckpointState(model_checkpoint_path=s2,\n                                 all_model_checkpoint_paths=[s2],\n                                 save_dir=save_dir)\n\n      # Adding s1 (s3 should not be deleted because helper is unaware of it)\n      s1 = save3.save(sess, os.path.join(save_dir, \"s1\"))\n      self.assertEqual([s2, s1], save3.last_checkpoints)\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertFalse(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s3)))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s2)))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(\n          tf.compat.v1.train.checkpoint_exists(\n              checkpoint_management.meta_graph_filename(s1)))\n      self.assertCheckpointState(model_checkpoint_path=s1,\n                                 all_model_checkpoint_paths=[s2, s1],\n                                 save_dir=save_dir)\n\n  def testSharded(self):\n    save_dir = self._get_test_dir(\"max_to_keep_sharded\")\n\n    with tf.compat.v1.Session(\n        target=\"\",\n        config=config_pb2.ConfigProto(device_count={\"CPU\": 2})) as sess:\n      with sess.graph.device(\"/cpu:0\"):\n        v0 = tf.compat.v1.Variable(111, name=\"v0\")\n      with sess.graph.device(\"/cpu:1\"):\n        v1 = tf.compat.v1.Variable(222, name=\"v1\")\n      save = save_utils.PartialRecoverySaver({\n          \"v0\": v0,\n          \"v1\": v1\n      },\n                                             sharded=True,\n                                             max_to_keep=2)\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      self.assertEqual([], save.last_checkpoints)\n\n      s1 = save.save(sess, os.path.join(save_dir, \"s1\"))\n      self.assertEqual([s1], save.last_checkpoints)\n      self.assertEqual(4, len(tf.io.gfile.glob(s1 + \"*\")))\n\n      self.assertTrue(\n          tf.io.gfile.exists(checkpoint_management.meta_graph_filename(s1)))\n\n      s2 = save.save(sess, os.path.join(save_dir, \"s2\"))\n      self.assertEqual([s1, s2], save.last_checkpoints)\n      self.assertEqual(4, len(tf.io.gfile.glob(s1 + \"*\")))\n      self.assertTrue(\n          tf.io.gfile.exists(checkpoint_management.meta_graph_filename(s1)))\n      self.assertEqual(4, len(tf.io.gfile.glob(s2 + \"*\")))\n      self.assertTrue(\n          tf.io.gfile.exists(checkpoint_management.meta_graph_filename(s2)))\n\n      s3 = save.save(sess, os.path.join(save_dir, \"s3\"))\n      self.assertEqual([s2, s3], save.last_checkpoints)\n      self.assertEqual(0, len(tf.io.gfile.glob(s1 + \"*\")))\n      self.assertFalse(\n          tf.io.gfile.exists(checkpoint_management.meta_graph_filename(s1)))\n      self.assertEqual(4, len(tf.io.gfile.glob(s2 + \"*\")))\n      self.assertTrue(\n          tf.io.gfile.exists(checkpoint_management.meta_graph_filename(s2)))\n      self.assertEqual(4, len(tf.io.gfile.glob(s3 + \"*\")))\n      self.assertTrue(\n          tf.io.gfile.exists(checkpoint_management.meta_graph_filename(s3)))\n\n  def testNoMaxToKeep(self):\n    save_dir = self._get_test_dir(\"no_max_to_keep\")\n    save_dir2 = self._get_test_dir(\"max_to_keep_0\")\n\n    with self.cached_session() as sess:\n      v = tf.compat.v1.Variable(10.0, name=\"v\")\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n\n      # Test max_to_keep being None.\n      save = save_utils.PartialRecoverySaver({\"v\": v}, max_to_keep=None)\n      self.assertEqual([], save.last_checkpoints)\n      s1 = save.save(sess, os.path.join(save_dir, \"s1\"))\n      self.assertEqual([], save.last_checkpoints)\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      s2 = save.save(sess, os.path.join(save_dir, \"s2\"))\n      self.assertEqual([], save.last_checkpoints)\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n\n      # Test max_to_keep being 0.\n      save2 = save_utils.PartialRecoverySaver({\"v\": v}, max_to_keep=0)\n      self.assertEqual([], save2.last_checkpoints)\n      s1 = save2.save(sess, os.path.join(save_dir2, \"s1\"))\n      self.assertEqual([], save2.last_checkpoints)\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      s2 = save2.save(sess, os.path.join(save_dir2, \"s2\"))\n      self.assertEqual([], save2.last_checkpoints)\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n\n  def testNoMetaGraph(self):\n    save_dir = self._get_test_dir(\"no_meta_graph\")\n\n    with self.cached_session() as sess:\n      v = tf.compat.v1.Variable(10.0, name=\"v\")\n      save = save_utils.PartialRecoverySaver({\"v\": v})\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n\n      s1 = save.save(sess, os.path.join(save_dir, \"s1\"), write_meta_graph=False)\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertFalse(\n          tf.io.gfile.exists(checkpoint_management.meta_graph_filename(s1)))\n\n\nclass RecoverLastCheckpointsTest(tf.test.TestCase):\n\n  def _get_test_dir(self, dirname):\n    test_dir = os.path.join(self.get_temp_dir(), dirname)\n    tf.io.gfile.makedirs(test_dir)\n    return test_dir\n\n  def assertCheckpointState(self, model_checkpoint_path,\n                            all_model_checkpoint_paths, save_dir):\n    checkpoint_state = tf.train.get_checkpoint_state(save_dir)\n    self.assertEqual(checkpoint_state.model_checkpoint_path,\n                     model_checkpoint_path)\n    self.assertEqual(checkpoint_state.all_model_checkpoint_paths,\n                     all_model_checkpoint_paths)\n\n  def test_recover_last_checkpoints(self):\n    with context.eager_mode():\n      save_dir = self._get_test_dir(\"recover_last_checkpoints\")\n\n      v = tf.compat.v1.Variable(10.0, name=\"v\")\n      save = save_utils.PartialRecoverySaver({\"v\": v}, max_to_keep=10)\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      self.assertEqual([], save.last_checkpoints)\n\n      s1 = save.save(None, os.path.join(save_dir, \"ckpt-1\"))\n      s2 = save.save(None, os.path.join(save_dir, \"ckpt-2\"))\n      s3 = save.save(None, os.path.join(save_dir, \"ckpt-3\"))\n      self.assertEqual([s1, s2, s3], save.last_checkpoints)\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertCheckpointState(model_checkpoint_path=s3,\n                                 all_model_checkpoint_paths=[s1, s2, s3],\n                                 save_dir=save_dir)\n\n      # Create another saver and recover last checkpoints.\n      save2 = save_utils.PartialRecoverySaver({\"v\": v}, max_to_keep=10)\n      self.assertEqual([], save2.last_checkpoints)\n      save2.recover_last_checkpoints([s1, s2, s3])\n      self.assertEqual([s1, s2, s3], save2.last_checkpoints)\n\n      # Remove a checkpoint and check that last checkpoints are\n      # restored correctly.\n      for fname in tf.io.gfile.glob(\"{}*\".format(s1)):\n        tf.io.gfile.remove(fname)\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s1))\n\n      # Create another saver and recover last checkpoints. The removed\n      # checkpoint would be correctly omitted.\n      save3 = save_utils.PartialRecoverySaver({\"v\": v}, max_to_keep=10)\n      self.assertEqual([], save3.last_checkpoints)\n      save3.recover_last_checkpoints([s1, s2, s3])\n      self.assertEqual([s2, s3], save3.last_checkpoints)\n      s4 = save3.save(None, os.path.join(save_dir, \"ckpt-4\"))\n      self.assertCheckpointState(model_checkpoint_path=s4,\n                                 all_model_checkpoint_paths=[s2, s3, s4],\n                                 save_dir=save_dir)\n\n\nclass KeepCheckpointEveryNHoursTest(tf.test.TestCase):\n\n  def _get_test_dir(self, dirname):\n    test_dir = os.path.join(self.get_temp_dir(), dirname)\n    tf.io.gfile.makedirs(test_dir)\n    return test_dir\n\n  @test_util.run_in_graph_and_eager_modes\n  @mock.patch.object(save_utils, \"time\")\n  def testNonSharded(self, mock_time):\n    save_dir = self._get_test_dir(\"keep_checkpoint_every_n_hours\")\n\n    with self.cached_session() as sess:\n      v = tf.compat.v1.Variable([10.0], name=\"v\")\n      # Run the initializer NOW to avoid the 0.5s overhead of the first Run()\n      # call, which throws the test timing off in fastbuild mode.\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n      # Create a saver that will keep the last 2 checkpoints plus one every 0.7\n      # seconds.\n      start_time = time.time()\n      mock_time.time.return_value = start_time\n      save = save_utils.PartialRecoverySaver({\"v\": v},\n                                             max_to_keep=2,\n                                             keep_checkpoint_every_n_hours=0.7 /\n                                             3600)\n      self.assertEqual([], save.last_checkpoints)\n\n      # Wait till 1 seconds have elapsed so s1 will be old enough to keep.\n      # sleep may return early, don't trust it.\n      mock_time.time.return_value = start_time + 1.0\n      s1 = save.save(sess, os.path.join(save_dir, \"s1\"))\n      self.assertEqual([s1], save.last_checkpoints)\n\n      s2 = save.save(sess, os.path.join(save_dir, \"s2\"))\n      self.assertEqual([s1, s2], save.last_checkpoints)\n\n      # We now have 2 'last_checkpoints': [s1, s2].  The next call to Save(),\n      # would normally delete s1, because max_to_keep is 2.  However, s1 is\n      # older than 0.7s so we must keep it.\n      s3 = save.save(sess, os.path.join(save_dir, \"s3\"))\n      self.assertEqual([s2, s3], save.last_checkpoints)\n\n      # s1 should still be here, we are Not checking now to reduce time\n      # variance in the test.\n\n      # We now have 2 'last_checkpoints': [s2, s3], and s1 on disk.  The next\n      # call to Save(), will delete s2, because max_to_keep is 2, and because\n      # we already kept the old s1. s2 is very close in time to s1 so it gets\n      # deleted.\n      s4 = save.save(sess, os.path.join(save_dir, \"s4\"))\n      self.assertEqual([s3, s4], save.last_checkpoints)\n\n      # Check that s1 is still here, but s2 is gone.\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s1))\n      self.assertFalse(tf.compat.v1.train.checkpoint_exists(s2))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s3))\n      self.assertTrue(tf.compat.v1.train.checkpoint_exists(s4))\n\n\nclass SaveRestoreWithVariableNameMap(tf.test.TestCase):\n\n  def _testNonReshape(self, variable_op):\n    save_path = os.path.join(self.get_temp_dir(), \"non_reshape\")\n\n    with self.session(graph=tf.Graph()) as sess:\n      # Build a graph with 2 parameter nodes, and Save and\n      # Restore nodes for them.\n      v0 = variable_op(10.0, name=\"v0\")\n      v1 = variable_op(20.0, name=\"v1\")\n      save = save_utils.PartialRecoverySaver({\n          \"save_prefix/v0\": v0,\n          \"save_prefix/v1\": v1\n      })\n      self.evaluate(tf.compat.v1.global_variables_initializer())\n\n      # Check that the parameter nodes have been initialized.\n      self.assertEqual(10.0, self.evaluate(v0))\n      self.assertEqual(20.0, self.evaluate(v1))\n\n      # Save the initialized values in the file at \"save_path\"\n      # Use a variable name map to set the saved tensor names\n      val = save.save(sess, save_path)\n      self.assertTrue(isinstance(val, six.string_types))\n      self.assertEqual(save_path, val)\n\n      # Verify that the original names are not in the Saved file\n      save = save_utils.PartialRecoverySaver({\"v0\": v0, \"v1\": v1})\n      with self.assertRaisesOpError(\"not found in checkpoint\"):\n        save.restore(sess, save_path)\n\n    # Verify that the mapped names are present in the Saved file and can be\n    # Restored using remapped names.\n    with self.session(graph=tf.Graph()) as sess:\n      v0 = variable_op(-1.0, name=\"v0\")\n      v1 = variable_op(-1.0, name=\"v1\")\n\n      if not tf.executing_eagerly():\n        with self.assertRaisesOpError(\"uninitialized\"):\n          self.evaluate(v0)\n        with self.assertRaisesOpError(\"uninitialized\"):\n          self.evaluate(v1)\n\n      save = save_utils.PartialRecoverySaver({\n          \"save_prefix/v0\": v0,\n          \"save_prefix/v1\": v1\n      })\n      save.restore(sess, save_path)\n\n      # Check that the parameter nodes have been restored.\n      if not tf.executing_eagerly():\n        self.assertEqual(10.0, self.evaluate(v0))\n        self.assertEqual(20.0, self.evaluate(v1))\n\n    # Add a prefix to the node names in the current graph and Restore using\n    # remapped names.\n    with self.session(graph=tf.Graph()) as sess:\n      v0 = variable_op(-1.0, name=\"restore_prefix/v0\")\n      v1 = variable_op(-1.0, name=\"restore_prefix/v1\")\n\n      if not tf.executing_eagerly():\n        with self.assertRaisesOpError(\"uninitialized\"):\n          self.evaluate(v0)\n        with self.assertRaisesOpError(\"uninitialized\"):\n          self.evaluate(v1)\n\n      # Restore the saved values in the parameter nodes.\n      save = save_utils.PartialRecoverySaver({\n          \"save_prefix/v0\": v0,\n          \"save_prefix/v1\": v1\n      })\n      save.restore(sess, save_path)\n\n      # Check that the parameter nodes have been restored.\n      self.assertEqual(10.0, self.evaluate(v0))\n      self.assertEqual(20.0, self.evaluate(v1))\n\n  @test_util.run_in_graph_and_eager_modes\n  def testNonReshapeResourceVariable(self):\n    self._testNonReshape(resource_variable_ops.ResourceVariable)\n\n  def testNonReshapeVariable(self):\n    self._testNonReshape(tf.Variable)\n\n\nclass SecondOrStepTimerWithTideSettingTest(tf.test.TestCase):\n\n  def testNoTideSetting(self):\n    timer = save_utils.SecondOrStepTimerWithTideSetting(every_secs=10)\n    with freeze_time(\"2012-01-14 02:00:00\") as freezer:\n      timer.update_last_triggered_step(5)\n      freezer.tick(5.0)\n      self.assertEqual(False, timer.should_trigger_for_step(10))\n      freezer.tick(10.0)\n      self.assertEqual(True, timer.should_trigger_for_step(10))\n\n  def testTideAvailable(self):\n    timer = save_utils.SecondOrStepTimerWithTideSetting(every_secs=10,\n                                                        tide_start_hour=1,\n                                                        tide_start_minute=0,\n                                                        tide_end_hour=3,\n                                                        tide_end_minute=0,\n                                                        tide_every_secs=5)\n    with freeze_time(\"2012-01-14 02:00:00\") as freezer:\n      timer.update_last_triggered_step(5)\n      freezer.tick(5.0)\n      self.assertEqual(False, timer.should_trigger_for_step(10))\n      freezer.tick(10.0)\n      self.assertEqual(True, timer.should_trigger_for_step(10))\n\n  def testTideNotAvailable(self):\n    timer = save_utils.SecondOrStepTimerWithTideSetting(every_secs=10,\n                                                        tide_start_hour=1,\n                                                        tide_start_minute=0,\n                                                        tide_end_hour=3,\n                                                        tide_end_minute=0,\n                                                        tide_every_secs=5)\n    with freeze_time(\"2012-01-14 04:00:00\") as freezer:\n      timer.update_last_triggered_step(5)\n      freezer.tick(2.0)\n      self.assertEqual(False, timer.should_trigger_for_step(10))\n      freezer.tick(7.0)\n      self.assertEqual(True, timer.should_trigger_for_step(10))\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/service_discovery.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nimport abc\nfrom collections import namedtuple, defaultdict\nfrom enum import Enum\nimport socket\nimport os\nimport threading\nimport time\nfrom typing import Dict, NamedTuple\n\nimport numpy as np\nfrom kazoo.retry import KazooRetry\nfrom monolith.native_training.zk_utils import MonolithKazooClient\nfrom kazoo.client import KazooState\nfrom kazoo.exceptions import NoNodeError, NodeExistsError\n\nfrom monolith.native_training import consul\nfrom monolith.native_training.zk_utils import default_zk_servers\nfrom monolith.native_training.mlp_utils import MLPEnv, check_port\n\n\nclass ServiceDiscoveryType(Enum):\n  PRIMUS = 1\n  CONSUL = 2\n  ZK = 3\n  MLP = 4\n\n\nclass ServiceDiscovery(abc.ABC):\n\n  @abc.abstractmethod\n  def register(self, name: str, index: int, addr: str):\n    \"\"\"Register the port to the index\"\"\"\n\n  @abc.abstractmethod\n  def deregister(self, name: str, index: int, addr: str):\n    \"\"\"Deregister the port from index.\"\"\"\n\n  def query(self, name) -> Dict[int, str]:\n    \"\"\"Returns a dict that maps index to str\"\"\"\n\n  def close(self):\n    pass\n\n\n_HostAndPort = namedtuple(\"_HostAndPort\", [\"host\", \"port\"])\n\n_RETRY_MAX_BACKOFF_SECS = 5\n\n\ndef retry_with_socket_error(http_call):\n  tries = 5\n  for i in range(tries):\n    try:\n      return http_call()\n    except socket.error:\n      if i < tries - 1:\n        time.sleep(np.random.rand() * _RETRY_MAX_BACKOFF_SECS)\n        continue\n      raise\n\n\nclass ConsulServiceDiscovery(ServiceDiscovery):\n\n  def __init__(self, consul_id: str, retry_time_secs: float = 3.0):\n    self._consul_id = consul_id\n    self._client = consul.Client()\n    self._retry_time_secs = retry_time_secs\n\n  def register(self, name: str, index: int, addr: str):\n    # This is best effort, deregister the address with the same name & index.\n    while True:\n      index_to_addr = self.query(name)\n      if index in index_to_addr:\n        self.deregister(name, index, index_to_addr[index])\n      else:\n        break\n      time.sleep(self._retry_time_secs)\n\n    host, port = self._get_host_and_port(addr)\n    retry_with_socket_error(lambda: self._client.register(\n        self._consul_id, port, tags={\n            \"index\": index,\n            \"name\": name,\n            \"ip\": host,\n        }))\n\n    # We need to make sure we can be registered\n\n    # We wait upto 180 secs before we think the machine is blacklisted.\n    max_retries = max(2, 180 / max(1, _RETRY_MAX_BACKOFF_SECS))\n    retries = 0\n    while True:\n      index_to_addr = self.query(name)\n      if index in index_to_addr:\n        break\n      retries += 1\n      if retries > max_retries:\n        raise OSError(\"This machine is blacklisted by consul.\")\n      time.sleep(_RETRY_MAX_BACKOFF_SECS)\n\n  def deregister(self, name: str, index: int, addr: str):\n    del name\n    del index\n    host, port = self._get_host_and_port(addr)\n    retry_with_socket_error(\n        lambda: self._client.deregister(self._consul_id, port))\n\n  def query_all(self) -> Dict[str, Dict[int, str]]:\n    elements = retry_with_socket_error(\n        lambda: self._client.lookup(self._consul_id, timeout=15))\n    addrs = defaultdict(dict)\n\n    for element in elements:\n      name = element[\"Tags\"][\"name\"]\n      addr = \"{}:{}\".format(element[\"Tags\"][\"ip\"], element[\"Port\"])\n      index = int(element[\"Tags\"][\"index\"])\n      addrs[name][index] = addr\n\n    return addrs\n\n  def query(self, name: str):\n    named_addrs = self.query_all()\n    return named_addrs[name]\n\n  def _get_host_and_port(self, addr: str) -> _HostAndPort:\n    components = addr.split(\":\")\n    if len(components) != 2:\n      raise ValueError(\"Invalid addr: {}\".format(addr))\n    return _HostAndPort(host=components[0], port=int(components[1]))\n\n\nclass TfConfigServiceDiscovery(ServiceDiscovery):\n\n  def __init__(self, tf_config):\n    self._tf_config = tf_config\n\n  def register(self, name: str, index: int, addr: str):\n    pass\n\n  def deregister(self, name: str, index: int, addr: str):\n    pass\n\n  def query(self, name: str):\n    if name == 'ps':\n      addr_list = self._tf_config['cluster'][name]\n    elif name == 'worker':\n      if 'chief' in self._tf_config['cluster']:\n        addr_list = self._tf_config['cluster']['chief'] + \\\n                    self._tf_config['cluster'][name]\n      else:\n        addr_list = self._tf_config['cluster'][name]\n    else:\n      raise ValueError('name must be ps/worker')\n\n    return {i: addr for i, addr in enumerate(addr_list)}\n\n  @property\n  def server_type(self):\n    task = self._tf_config['task']\n    return 'worker' if task['type'] == 'chief' else task['type']\n\n  @property\n  def addr(self):\n    task = self._tf_config['task']\n    return self._tf_config['cluster'][task['type']][task['index']]\n\n  @property\n  def index(self):\n    task = self._tf_config['task']\n    if 'chief' in self._tf_config['cluster']:\n      return task['index'] + 1 if task['type'] == 'worker' else task['index']\n    else:\n      return task['index']\n\n\nclass ZKListener(object):\n\n  def __init__(self, zkds: 'ZKServiceDiscovery'):\n    self._zksd = zkds\n    self._has_lost = False\n\n  def __call__(self, state: KazooState) -> None:\n    if state == KazooState.LOST:\n      # The connection has been confirmed dead\n      logging.warning(\n          \"Any ephemeral nodes will need to be recreated upon re-establishing a connection.\"\n      )\n      self._has_lost = True\n    elif state == KazooState.SUSPENDED:\n      # Handle being disconnected from Zookeeper\n      return\n    else:\n      # Handle being connected/reconnected to Zookeeper\n      if self._has_lost:\n        logging.info(\n            \"connected/reconnected after lost, restart updater and watcher\")\n        self._zksd.do_all_registrations()\n        self._has_lost = False\n\n\n_ZK_REGISTRATION_PERIOD = 30 * 60\n\n\nclass ZKServiceDiscovery(ServiceDiscovery):\n\n  class ThreadSet(NamedTuple):\n    stop: threading.Event\n    wakeup: threading.Event\n    thread: threading.Thread\n\n    def stop_and_join(self):\n      self.stop.set()\n      self.wakeup.set()\n      self.thread.join()\n\n  def __init__(self,\n               job_name: str,\n               zk_server: str = None,\n               max_tries: int = 3,\n               delay: int = 5):\n    self._max_tries = max_tries\n    self._delay = delay\n    # /monolith/{job_name}/server_type.index -> host:port\n    self._path_prefix = '/monolith/{}'.format(job_name)\n    self._lock = threading.Lock()\n    self._cluster = {}\n    # Maps (name, index) to thread set.\n    self._threads: Dict[Tuple[str, int], ZKServiceDiscovery.ThreadSet] = {}\n\n    try:\n      zk_server = zk_server or default_zk_servers()\n      self._client = MonolithKazooClient(zk_server)\n      self._client.add_listener(ZKListener(self))\n      self._client.start()\n      self._client.ensure_path(self._path_prefix)\n    except Exception as e:\n      logging.error(\"cannot create zk client, {}\".format(e))\n      raise e\n\n    self._watch_data()\n\n  def do_all_registrations(self):\n    for ts in self._threads.values():\n      ts.wakeup.set()\n\n  def _get_node_name(self, server_type: str, index: int):\n    return '{}.{}'.format(server_type, index)\n\n  def _get_path(self, server_type: str, index: int):\n    return \"{}/{}\".format(self._path_prefix,\n                          self._get_node_name(server_type, index))\n\n  def _try_create(self, path: str, value: str):\n    value_bytes = bytes(value, 'utf-8')\n    try:\n      self._client.create(path,\n                          value=value_bytes,\n                          makepath=True,\n                          ephemeral=True)\n    except NodeExistsError:\n      self._client.set(path, value_bytes)\n\n  def _try_delete(self, path):\n    try:\n      self._client.delete(path=path, recursive=True)\n    except NoNodeError:\n      logging.info(\"{path} is not exist, no need to delete\".format(path=path))\n\n  def _children_watch(self, children):\n    with self._lock:\n      old_children = set(\n          self._get_node_name(serve_type, index)\n          for serve_type in self._cluster\n          for index in self._cluster[serve_type])\n      new_children = set(child for child in children if child)\n      added = new_children - old_children\n\n    for node in added:\n      path = '{}/{}'.format(self._path_prefix, node)\n      self._client.DataWatch(path, func=self._get_data_watch(path))\n\n  def _get_data_watch(self, path):\n\n    def data_watch(data, stat):\n      server_type, index = os.path.basename(path).split('.')\n      index = int(index)\n\n      with self._lock:\n        if data is not None and len(data) > 0:\n          addr = data.decode(\"utf-8\")\n          if server_type in self._cluster:\n            self._cluster[server_type][index] = addr\n          else:\n            self._cluster[server_type] = {index: addr}\n        else:\n          if server_type in self._cluster:\n            if index in self._cluster[server_type]:\n              del self._cluster[server_type][index]\n\n    return data_watch\n\n  def _watch_data(self):\n    self._client.ChildrenWatch(self._path_prefix, self._children_watch)\n\n  def register(self, name: str, index: int, addr: str):\n    self._internal_register(name, index, addr, register_periodically=True)\n\n  def _periodically_register(self, name: str, index: int, addr: str,\n                             stop: threading.Event, wakeup: threading.Event):\n    while True:\n      if wakeup.wait(_ZK_REGISTRATION_PERIOD):\n        wakeup.clear()\n      if stop.is_set():\n        break\n      try:\n        self._internal_register(name, index, addr, register_periodically=False)\n      except Exception:\n        # This is the best effort.\n        pass\n\n  def _internal_register(self, name: str, index: int, addr: str,\n                         register_periodically: bool):\n    path = self._get_path(name, index)\n    retry = KazooRetry(max_tries=self._max_tries, delay=self._delay)\n    try:\n      retry(self._try_create, path, addr)\n    except Exception as e:\n      logging.error(\"server_type: {} , index:{} register fail\".format(\n          name, index))\n      raise e\n\n    if register_periodically:\n      stop = threading.Event()\n      wakeup = threading.Event()\n      thread = threading.Thread(target=self._periodically_register,\n                                args=(name, index, addr, stop, wakeup),\n                                daemon=True)\n      thread.start()\n      self._threads[(name, index)] = ZKServiceDiscovery.ThreadSet(stop=stop,\n                                                                  wakeup=wakeup,\n                                                                  thread=thread)\n\n  def deregister(self, name: str, index: int, addr: str):\n    path = self._get_path(name, index)\n    retry = KazooRetry(max_tries=self._max_tries, delay=self._delay)\n    try:\n      retry(self._try_delete, path)\n    except Exception as e:\n      logging.error(\"server_type: {} , index:{} deregister fail\".format(\n          name, index))\n      raise e\n    key = (name, index)\n    ts = self._threads[key]\n    ts.stop_and_join()\n    del self._threads[key]\n\n  def query(self, name) -> Dict[int, str]:\n    with self._lock:\n      if name in self._cluster:\n        return self._cluster[name]\n      else:\n        return {}\n\n  def close(self):\n    if self._client is not None:\n      self._client.stop()\n      self._client.close()\n      self._client = None\n\n    for ts in self._threads.values():\n      ts.stop_and_join()\n\n  def __del__(self):\n    self.close()\n\n\nclass MLPServiceDiscovery(ServiceDiscovery):\n  def __init__(self):\n    self._mlp_env = MLPEnv()\n    self._filters = set()\n    self.addr = f\"{self._mlp_env.host}:{self._mlp_env.port}\"\n\n  def _check(self, name: str, index: int, addr: str):\n    if self._mlp_env is None:\n      return\n    assert name.upper() in self._mlp_env.all_roles\n    assert index < self._mlp_env.num_replicas(name)\n    exp_addr = self._mlp_env.get_addr(name, index=index)\n    exp_host, exp_port = exp_addr.split(':')\n    real_host, real_port = addr.split(':')\n    assert real_host in {'local', 'localhost', '127.0.0.1', '0.0.0.0',\n                         exp_host, self._mlp_env.host,\n                         self._mlp_env.get_host(is_primary=False)}\n    assert exp_port == real_port\n\n  def register(self, name: str, index: int, addr: str):\n    self._check(name, index, addr)\n    key = f'{name.lower()}:{index}'\n    if key in self._filters:\n      self._filters.remove(key)\n\n  def deregister(self, name: str, index: int, addr: str):\n    self._check(name, index, addr)\n    self._filters.add(f'{name.lower()}:{index}')\n\n  def query(self, name, skip_port_check: bool = False) -> Dict[int, str]:\n    assert name is not None and len(name) > 0\n    if self._mlp_env is None:\n      return {}\n    result = {}\n    for idx in range(self._mlp_env.num_replicas(name)):\n      addr = self._mlp_env.get_addr(name, index=idx)\n      assert addr is not None\n      key = f'{name.lower()}:{idx}'\n      if key not in self._filters:\n        result[idx] = addr\n        if name.lower() == 'ps' and not skip_port_check:\n          host, port = addr.split(':')\n          assert check_port(host, int(port), timeout=3600), f'{addr} connect error!'\n    return result\n\n  def deregister_all(self) -> Dict[str, Dict[int, str]]:\n    if self._mlp_env is None:\n      return {}\n    for name, num in self._mlp_env.all_roles.items():\n      for idx in range(num):\n        key = f'{name.lower()}:{idx}'\n        self._filters.add(key)\n\n  def query_all(self):\n    if self._mlp_env is None:\n      return {}\n    result = {}\n    for name, num in self._mlp_env.all_roles.items():\n      if name.lower() in {'ps', 'worker', 'chief'}:\n        name = name.lower()\n        result[name] = self.query(name, True)\n    return result\n\n  @property\n  def server_type(self):\n    if self._mlp_env is None:\n      return None\n    return self._mlp_env.role.lower()\n\n  @property\n  def index(self):\n    if self._mlp_env is None:\n      return None\n    return self._mlp_env.index\n\n  def close(self):\n    self._mlp_env = None\n    self._filters.clear()\n\n  def __del__(self):\n    self.close()\n\n\ndef deregister_all(consul_id: str):\n  \"\"\"Deregisters all records in the given consul_id.\"\"\"\n  discovery = ConsulServiceDiscovery(consul_id)\n  named_addrs = discovery.query_all()\n  for name, addrs in named_addrs.items():\n    for idx, addr in addrs.items():\n      discovery.deregister(name, idx, addr)\n"
  },
  {
    "path": "monolith/native_training/service_discovery_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nfrom collections import defaultdict\nfrom functools import partial\nimport os\nimport socket\nimport threading\nimport time\nimport unittest\nfrom unittest import mock\nfrom kazoo.client import KazooState\nfrom kazoo.exceptions import NoNodeError, NodeExistsError, NotEmptyError\nfrom typing import List, Callable\n\nfrom monolith.native_training import service_discovery\n\n\nclass FakeConsul:\n\n  def __init__(self, blacklist=[]):\n    \"\"\"The host in blacklist will not be registered to consul\"\"\"\n    self._name_to_dict = defaultdict(dict)\n    self._blacklist = blacklist\n\n  def lookup(self, name: str, **kwargs):\n    return list(self._name_to_dict[name].values())\n\n  def register(self,\n               name: str,\n               port: int,\n               tags={},\n               host: str = None,\n               check_script: str = None):\n    del check_script\n    if tags[\"ip\"] in self._blacklist:\n      return\n    key = str(host) + \":\" + str(port)\n    d = self._name_to_dict[name]\n    d[key] = {\"Host\": host, \"Port\": port, \"Tags\": tags}\n\n  def deregister(self, name: str, port: int, host: str = None):\n    key = str(host) + \":\" + str(port)\n    d = self._name_to_dict[name]\n    del d[key]\n\n\nclass FakeKazooClient:\n\n  def __init__(self, zk_server: str):\n    self._lock = threading.RLock()\n    self._zk_server = zk_server\n    self._data = None\n\n    self._children_watches = []\n    self._data_watches = []\n    self.DataWatch = partial(DataWatch, self)\n    self.ChildrenWatch = partial(ChildrenWatch, self)\n    self._listeners = []\n\n  def ensure_path(self, path: str):\n    with self._lock:\n      if path not in self._data:\n        self._data[path] = None\n\n  def start(self):\n    with self._lock:\n      self._data = {}\n\n  def create(self,\n             path: str,\n             value: bytes = b'',\n             makepath: bool = False,\n             ephemeral: bool = False):\n    with self._lock:\n      if path in self._data:\n        raise NodeExistsError('node {} exists'.format(path))\n      else:\n        prefix = os.path.dirname(path)\n        if prefix in self._data or makepath:\n          self._data[path] = value\n\n          for dw in self._data_watches:\n            if dw.path == path:\n              dw(value, None)\n\n          for cw in self._children_watches:\n            dirname = os.path.dirname(path)\n            if dirname == cw.path:\n              children = [\n                  os.path.basename(key)\n                  for key in self._data\n                  if os.path.dirname(key) == cw.path\n              ]\n              cw(children)\n        else:\n          raise NoNodeError('No Node {}'.format(prefix))\n\n  def delete(self, path: str, recursive: bool = True):\n    with self._lock:\n      if path in self._data:\n        del self._data[path]\n        for dw in self._data_watches:\n          if dw.path == path:\n            dw(None, None)\n\n        for cw in self._children_watches:\n          dirname = os.path.dirname(path)\n          if dirname == cw.path:\n            children = [\n                os.path.basename(key)\n                for key in self._data\n                if os.path.dirname(key) == cw.path\n            ]\n            cw(children)\n      else:\n        collected = []\n        for key in self._data:\n          if key.startswith(path):\n            collected.append(key)\n        if collected:\n          if recursive:\n            for key in collected:\n              del self._data[key]\n              for dw in self._data_watches:\n                if dw.path == key:\n                  dw(None, None)\n\n              for cw in self._children_watches:\n                dirname = os.path.dirname(key)\n                if dirname == cw.path:\n                  children = [\n                      os.path.basename(key)\n                      for key in self._data\n                      if os.path.dirname(key) == cw.path\n                  ]\n                  cw(children)\n          else:\n            raise NotEmptyError('node {} has children'.format(path))\n        else:\n          raise NoNodeError('node {} not found'.format(path))\n\n  def set(self, path: str, value: bytes):\n    with self._lock:\n      if path in self._data:\n        self._data[path] = value\n        for dw in self._data_watches:\n          if dw.path == path:\n            dw(value, None)\n      else:\n        raise NoNodeError('node {} is not exist'.format(path))\n\n  def get(self, path: str):\n    with self._lock:\n      if path in self._data:\n        return self._data[path], None\n      else:\n        raise NoNodeError('node {} is not exist'.format(path))\n\n  def get_children(self, path: str):\n    with self._lock:\n      if path in self._data:\n        return []\n      else:\n        collected = []\n        for key in self._data:\n          if key.startswith(path):\n            child = key[len(path) + 1:].split('/')[0]\n            collected.append(child)\n\n        if collected:\n          return collected\n        else:\n          raise NoNodeError('node {} is not exist'.format(path))\n\n  def stop(self):\n    with self._lock:\n      self._data = None\n\n  def close(self):\n    with self._lock:\n      if self._data is not None:\n        self.stop()\n\n  def add_listener(self, listener):\n    self._listeners.append(listener)\n\n  @property\n  def listeners(self):\n    return self._listeners\n\n\nclass ChildrenWatch:\n\n  def __init__(self, client: FakeKazooClient, path: str,\n               func: Callable[[List[str]], None]):\n    self._client = client\n    client._children_watches.append(self)\n    self.path = path\n    self._func = func\n\n    children = []\n    for key in self._client._data:\n      dirname = os.path.dirname(key)\n      if dirname == path:\n        children.append(os.path.basename(key))\n    self._func(children)\n\n  def __call__(self, children: List[str]):\n    self._func(children)\n\n\nclass DataWatch:\n\n  def __init__(self, client: FakeKazooClient, path: str, func: str):\n    self._client = client\n    client._data_watches.append(self)\n    self.path = path\n    self._func = func\n\n    for key, value in self._client._data.items():\n      if key == path:\n        self._func(value, None)\n\n  def __call__(self, data: str, stat=None):\n    self._func(data, stat)\n\n\n_CONSUL_CLIENT = \"monolith.native_training.service_discovery.consul.Client\"\n_ZK_CLIENT = \"monolith.native_training.service_discovery.MonolithKazooClient\"\n\n\nclass ConsultServiceDiscovery(unittest.TestCase):\n\n  def test_basic(self):\n    with mock.patch(_CONSUL_CLIENT) as MockClient:\n      MockClient.return_value = FakeConsul()\n\n      discovery = service_discovery.ConsulServiceDiscovery(\"unique_id\")\n      discovery.register(\"server\", 0, \"192.168.0.1:1001\")\n      discovery.register(\"server\", 1, \"192.168.0.2:1002\")\n      self.assertDictEqual(discovery.query(\"server\"), {\n          0: \"192.168.0.1:1001\",\n          1: \"192.168.0.2:1002\"\n      })\n      discovery.deregister(\"server\", 0, \"192.168.0.1:1001\")\n      discovery.deregister(\"server\", 1, \"192.168.0.2:1002\")\n      self.assertDictEqual(discovery.query(\"server\"), {})\n\n  def test_duplicate_registration(self):\n    with mock.patch(_CONSUL_CLIENT) as MockClient:\n      MockClient.return_value = FakeConsul()\n      discovery = service_discovery.ConsulServiceDiscovery(\"unique_id\",\n                                                           retry_time_secs=0.0)\n      discovery.register(\"server\", 0, \"192.168.0.1:1001\")\n      discovery.register(\"server\", 0, \"192.168.0.1:1002\")\n      self.assertDictEqual(discovery.query(\"server\"), {0: \"192.168.0.1:1002\"})\n\n  def test_multi_names(self):\n    with mock.patch(_CONSUL_CLIENT) as MockClient:\n      MockClient.return_value = FakeConsul()\n      discovery = service_discovery.ConsulServiceDiscovery(\"unique_id\")\n      discovery.register(\"ps\", 0, \"192.168.0.1:1001\")\n      discovery.register(\"worker\", 0, \"192.168.0.1:1002\")\n      self.assertDictEqual(discovery.query(\"worker\"), {0: \"192.168.0.1:1002\"})\n\n  def test_retry(self):\n    with mock.patch(\n        \"monolith.native_training.service_discovery._RETRY_MAX_BACKOFF_SECS\",\n        0):\n      with mock.patch(_CONSUL_CLIENT) as MockClient:\n        mock_client = mock.MagicMock()\n\n        def raise_timeout(*args, **kwargs):\n          raise socket.timeout()\n\n        mock_client.register.side_effect = raise_timeout\n        MockClient.return_value = mock_client\n        discovery = service_discovery.ConsulServiceDiscovery(\"unique_id\")\n\n        self.assertRaises(socket.timeout, discovery.register, \"ps\", 0,\n                          \"192.168.0.1:1001\")\n\n  def test_registeration_failed(self):\n    with mock.patch(\n        \"monolith.native_training.service_discovery._RETRY_MAX_BACKOFF_SECS\",\n        0), mock.patch(_CONSUL_CLIENT) as MockClient:\n      MockClient.return_value = FakeConsul(blacklist=[\"192.168.0.1\"])\n      discovery = service_discovery.ConsulServiceDiscovery(\"unique_id\")\n      with self.assertRaises(OSError):\n        discovery.register(\"ps\", 0, \"192.168.0.1:1001\")\n\n\nclass TfConfigServiceDiscoveryTest(unittest.TestCase):\n\n  def test_tf_conf_sd(self):\n    cluster = {\n        'chief': ['host0:2222'],\n        'ps': ['host1:2222', 'host2:2222'],\n        'worker': ['host3:2222', 'host4:2222', 'host5:2222']\n    }\n    task = {'type': 'worker', 'index': 1}\n    tf_conf = {'cluster': cluster, 'task': task}\n\n    ps_list = cluster['ps']\n    discovery = service_discovery.TfConfigServiceDiscovery(tf_conf)\n    self.assertEqual(discovery.query('ps'),\n                     {i: addr for i, addr in enumerate(ps_list)},\n                     \"['host1:2222', 'host2:2222']\")\n\n    worker_list = cluster['chief'] + cluster['worker']\n    self.assertEqual(discovery.query('worker'),\n                     {i: addr for i, addr in enumerate(worker_list)},\n                     \"[host0:2222, 'host1:2222', 'host2:2222']\")\n\n    self.assertEqual(discovery.addr, 'host4:2222', 'host4:2222')\n    self.assertEqual(discovery.server_type, 'worker', 'worker')\n    self.assertEqual(discovery.index, 2, 2)\n\n\nclass ZKServiceDiscoveryTest(unittest.TestCase):\n\n  def test_basic(self):\n    with mock.patch(_ZK_CLIENT) as MockClient:\n      MockClient.return_value = FakeKazooClient(\"test_model\")\n\n      discovery = service_discovery.ZKServiceDiscovery(\"test_model\", \"fask\")\n      discovery.register(\"server\", 0, \"192.168.0.1:1001\")\n      discovery.register(\"server\", 1, \"192.168.0.2:1002\")\n      self.assertDictEqual(discovery.query(\"server\"), {\n          0: \"192.168.0.1:1001\",\n          1: \"192.168.0.2:1002\"\n      })\n      discovery.deregister(\"server\", 0, \"192.168.0.1:1001\")\n      discovery.deregister(\"server\", 1, \"192.168.0.2:1002\")\n      self.assertDictEqual(discovery.query(\"server\"), {})\n      discovery.close()\n\n  def test_duplicate_registration(self):\n    with mock.patch(_ZK_CLIENT) as MockClient:\n      MockClient.return_value = FakeKazooClient(\"test_model\")\n      discovery = service_discovery.ZKServiceDiscovery(\"test_model\", \"fask\")\n      discovery.register(\"server\", 0, \"192.168.0.1:1001\")\n      discovery.register(\"server\", 0, \"192.168.0.1:1002\")\n      self.assertDictEqual(discovery.query(\"server\"), {0: \"192.168.0.1:1002\"})\n      discovery.close()\n\n  def test_multi_names(self):\n    with mock.patch(_ZK_CLIENT) as MockClient:\n      MockClient.return_value = FakeKazooClient(\"test_model\")\n      discovery = service_discovery.ZKServiceDiscovery(\"test_model\", \"fask\")\n      discovery.register(\"ps\", 0, \"192.168.0.1:1001\")\n      discovery.register(\"worker\", 0, \"192.168.0.1:1002\")\n      self.assertDictEqual(discovery.query(\"worker\"), {0: \"192.168.0.1:1002\"})\n      del discovery\n\n  @mock.patch(\n      \"monolith.native_training.service_discovery._ZK_REGISTRATION_PERIOD\",\n      0.01)\n  def test_periodic_registration(self):\n    with mock.patch(_ZK_CLIENT) as MockClient:\n      c = FakeKazooClient(\"test_model\")\n      MockClient.return_value = c\n      discovery = service_discovery.ZKServiceDiscovery(\"test_model\")\n      discovery.register(\"ps\", 0, \"192.168.0.1:1001\")\n      c.set(\"/monolith/test_model/ps.0\", \"wrongdata\".encode())\n      time.sleep(1)\n      # Periodic registration should register it again\n      self.assertDictEqual(discovery.query(\"ps\"), {0: \"192.168.0.1:1001\"})\n\n  def test_listener(self):\n    with mock.patch(_ZK_CLIENT) as MockClient:\n      c = FakeKazooClient(\"test_model\")\n      MockClient.return_value = c\n      discovery = service_discovery.ZKServiceDiscovery(\"test_model\")\n      discovery.register(\"ps\", 0, \"192.168.0.1:1001\")\n      listener = c.listeners[0]\n      listener(KazooState.LOST)\n      listener(KazooState.CONNECTED)\n      self.assertDictEqual(discovery.query(\"ps\"), {0: \"192.168.0.1:1001\"})\n\n\nclass UtilsTest(unittest.TestCase):\n\n  def test_deregister_all(self):\n    with mock.patch(_CONSUL_CLIENT) as MockClient:\n      MockClient.return_value = FakeConsul()\n      discovery = service_discovery.ConsulServiceDiscovery(\"unique_id\")\n      discovery.register(\"server\", 0, \"192.168.0.1:1001\")\n      service_discovery.deregister_all(\"unique_id\")\n      self.assertDictEqual(discovery.query(\"server\"), {})\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/serving_ps_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom dataclasses import dataclass\nfrom typing import List\nfrom random import randint\nimport tensorflow as tf\n\nfrom monolith.native_training.distribution_ops import *\nfrom idl.matrix.proto.example_pb2 import ExampleBatch, FeatureListType, Feature, \\\n  FeatureConfigs, FeatureConfig, OutConfig, SliceConfig, PoolingType, OutType\n\nbatch_size = 10\n\n\n@dataclass\nclass FeatMeta:\n  name: str = None\n  slot: int = None\n  max_sequence_length: int = 1\n  fid_version: int = 0\n  slice_dims: List[int] = None\n  table: str = None\n  pool_type: int = 1  # 1 sum, 2 mean, 3 fristn\n  fl_type: int = 0  # 0 INDIVIDUAL, 1 SHARED\n\n\n@dataclass\nclass TableMeta:\n  name: str = None\n  slice_dims: List[int] = None\n\n\ntable1 = TableMeta(name='table1', slice_dims=[1, 4, 4, 8])\ntable2 = TableMeta(name='table2', slice_dims=[1, 4, 4])\ntable3 = TableMeta(name='table3', slice_dims=[8])\n\nfeatures = {\n    'f_user_id':\n        FeatMeta(name='f_user_id',\n                 slot=1,\n                 max_sequence_length=1,\n                 slice_dims=table1.slice_dims,\n                 table=table1.name,\n                 pool_type=1,\n                 fl_type=1),\n    'f_user_ctx_network':\n        FeatMeta(name='f_user_ctx_network',\n                 slot=61,\n                 max_sequence_length=1,\n                 slice_dims=table1.slice_dims,\n                 table=table1.name,\n                 pool_type=1,\n                 fl_type=1),\n    'f_user_test10_array':\n        FeatMeta(name='f_user_test10_array',\n                 slot=549,\n                 max_sequence_length=10,\n                 slice_dims=table1.slice_dims,\n                 table=table1.name,\n                 pool_type=2,\n                 fl_type=1),\n    'f_user_id-f_page':\n        FeatMeta(name='f_user_id-f_page',\n                 slot=504,\n                 max_sequence_length=10,\n                 fid_version=1,\n                 slice_dims=table3.slice_dims,\n                 table=table3.name,\n                 pool_type=3,\n                 fl_type=1),\n    'f_goods_id':\n        FeatMeta(name='f_user_id',\n                 slot=200,\n                 max_sequence_length=1,\n                 slice_dims=table2.slice_dims,\n                 table=table2.name,\n                 pool_type=1,\n                 fl_type=0),\n    'f_page':\n        FeatMeta(name='f_page',\n                 slot=305,\n                 max_sequence_length=1,\n                 slice_dims=table2.slice_dims,\n                 table=table2.name,\n                 pool_type=1,\n                 fl_type=0),\n}\n\n\nclass ServingPSTest(tf.test.TestCase):\n\n  def test_example_gen(self):\n    example_batch = ExampleBatch(batch_size=batch_size)\n    for name, meta in features.items():\n      named_feature_list = example_batch.named_feature_list.add()\n      named_feature_list.id = meta.slot\n      named_feature_list.name = name\n      if meta.fl_type == 0:\n        named_feature_list.type = FeatureListType.INDIVIDUAL\n      else:\n        named_feature_list.type = FeatureListType.SHARED\n      for i in range(batch_size):\n        feature = named_feature_list.feature.add()\n        if meta.fid_version == 0:\n          mask = (1 << 54) - 1\n          feature.fid_v1_list.value.extend([\n              (meta.slot << 54) | (randint(1, mask) & mask)\n              for _ in range(meta.max_sequence_length)\n          ])\n        else:\n          mask = (1 << 48) - 1\n          feature.fid_v2_list.value.extend([\n              (meta.slot << 48) | (randint(1, mask) & mask)\n              for _ in range(meta.max_sequence_length)\n          ])\n\n        if named_feature_list.type == FeatureListType.SHARED:\n          break\n\n    print(example_batch, flush=True)\n\n  def test_conf_gen(self):\n    feature_configs = FeatureConfigs()\n    for name, meta in features.items():\n      feat_conf = FeatureConfig(table=meta.table)\n      if meta.max_sequence_length > 1 and meta.pool_type == 3:\n        max_sequence_length = meta.max_sequence_length\n      feat_conf.slice_dims.extend(meta.slice_dims)\n      if meta.pool_type == 1:\n        feat_conf.pooling_type = PoolingType.SUM\n      elif meta.pool_type == 2:\n        feat_conf.pooling_type = PoolingType.MEAN\n      else:\n        feat_conf.pooling_type = PoolingType.FIRSTN\n      feature_configs.feature_configs[name].CopyFrom(feat_conf)\n\n    bias = OutConfig()\n    bias.out_type = OutType.CONCAT\n    bias_shape = (batch_size, len(features) - 1)\n    sub_shape = bias.shape.add()\n    sub_shape.dims.extend(bias_shape)\n    for name, meta in features.items():\n      if meta.pool_type != 3:\n        slice_config = bias.slice_configs.add()\n        slice_config.feature_name = name\n        slice_config.start = 0\n        slice_config.end = 1\n    feature_configs.out_configs['bias'].CopyFrom(bias)\n\n    vec = OutConfig()\n    vec.out_type = OutType.CONCAT\n    vec_shape = (batch_size, (len(features) - 1) * 4)\n    sub_shape = vec.shape.add()\n    sub_shape.dims.extend(vec_shape)\n    for name, meta in features.items():\n      if meta.pool_type != 3:\n        slice_config = vec.slice_configs.add()\n        slice_config.feature_name = name\n        slice_config.start = 1\n        slice_config.end = 5\n    feature_configs.out_configs['vec'].CopyFrom(vec)\n\n    uffm = OutConfig()\n    uffm.out_type = OutType.NONE\n    uffm_shape = (batch_size, 4)\n    for name, meta in features.items():\n      if meta.pool_type != 3 and 'user' in name:\n        sub_shape = uffm.shape.add()\n        sub_shape.dims.extend(uffm_shape)\n        slice_config = uffm.slice_configs.add()\n        slice_config.feature_name = name\n        slice_config.start = 5\n        slice_config.end = 8\n    feature_configs.out_configs['uffm'].CopyFrom(uffm)\n\n    iffm = OutConfig()\n    iffm.out_type = OutType.NONE\n    iffm_shape = (batch_size, 4)\n    for name, meta in features.items():\n      if meta.pool_type != 3 and 'user' not in name:\n        sub_shape = iffm.shape.add()\n        sub_shape.dims.extend(iffm_shape)\n        slice_config = iffm.slice_configs.add()\n        slice_config.feature_name = name\n        slice_config.start = 5\n        slice_config.end = 8\n    feature_configs.out_configs['iffm'].CopyFrom(iffm)\n\n    seq = OutConfig()\n    seq.out_type = OutType.NONE\n    meta = features['f_user_id-f_page']\n    seq_shape = (batch_size, meta.slice_dims[0], meta.max_sequence_length)\n    sub_shape = seq.shape.add()\n    sub_shape.dims.extend(seq_shape)\n    slice_config = seq.slice_configs.add()\n    slice_config.feature_name = meta.name\n    slice_config.start = 0\n    slice_config.end = 8\n    feature_configs.out_configs['seq'].CopyFrom(seq)\n\n    user_only = OutConfig()\n    user_only.out_type = OutType.STACK\n    sub_shape = user_only.shape.add()\n    user_only_shape = (batch_size, 8, 3)\n    sub_shape.dims.extend(user_only_shape)\n    for name, meta in features.items():\n      if meta.pool_type != 3 and 'user' in name and '-' not in name:\n        slice_config = user_only.slice_configs.add()\n        slice_config.feature_name = name\n        slice_config.start = 8\n        slice_config.end = 16\n    feature_configs.out_configs['user_only'].CopyFrom(user_only)\n\n    print(feature_configs, flush=True)\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/session_run_hooks.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nimport random\nimport time\n\nfrom absl import logging\nfrom datetime import datetime\nfrom tensorflow.python.training import training_util\n\n\ndef before(hour1, minute1, hour2, minute2):\n  if hour1 < hour2 or (hour1 == hour2 and minute1 < minute2):\n    return True\n  else:\n    return False\n\n\ndef tide_available_now(tide_start_hour, tide_start_minute, tide_end_hour,\n                       tide_end_minute):\n  if before(tide_start_hour, tide_start_minute, tide_end_hour, tide_end_minute):\n    if not before(datetime.utcnow().hour,\n                  datetime.utcnow().minute,\n                  tide_start_hour, tide_start_minute) and before(\n                      datetime.utcnow().hour,\n                      datetime.utcnow().minute, tide_end_hour, tide_end_minute):\n      return True\n    else:\n      return False\n  else:\n    if before(datetime.utcnow().hour,\n              datetime.utcnow().minute,\n              tide_start_hour, tide_start_minute) or not before(\n                  datetime.utcnow().hour,\n                  datetime.utcnow().minute, tide_end_hour, tide_end_minute):\n      return True\n    else:\n      return False\n\n\nclass CustomGlobalStepWaiterHook(tf.estimator.SessionRunHook):\n  \"\"\"Delays execution until global step reaches `wait_until_step`.\n\n  This hook delays execution until global step reaches to `wait_until_step`. It\n  is used to gradually start workers in distributed settings. One example usage\n  would be setting `wait_until_step=int(K*log(task_id+1))` assuming that\n  task_id=0 is the chief.\n  \"\"\"\n\n  def __init__(self,\n               wait_until_step,\n               tide_start_hour=None,\n               tide_start_minute=None,\n               tide_end_hour=None,\n               tide_end_minute=None,\n               max_non_tide_wait_minute=10):\n    \"\"\"Initializes a `GlobalStepWaiterHook`.\n\n    Args:\n      wait_until_step: an `int` shows until which global step should we wait.\n      tide_start_hour: the first hour in utc timezone when tide resources are available.\n      tide_end_hour: the last hour in utc timezone when tide resources are available.\n    \"\"\"\n    self._wait_until_step = wait_until_step\n    self._tide_start_hour = tide_start_hour\n    self._tide_start_minute = tide_start_minute\n    self._tide_end_hour = tide_end_hour\n    self._tide_end_minute = tide_end_minute\n    self._hook_start_time = None\n    random_extra_seconds = random.randint(0, 600)\n    self._non_tide_wait_second = max_non_tide_wait_minute * 60 + random_extra_seconds\n\n  def begin(self):\n    self._worker_is_started = False\n    self._global_step_tensor = training_util._get_or_create_global_step_read()  # pylint: disable=protected-access\n    if self._global_step_tensor is None:\n      raise RuntimeError(\n          \"Global step should be created to use _GlobalStepWaiterHook.\")\n\n  def before_run(self, run_context):\n    if self._worker_is_started:\n      return None\n\n    if self._wait_until_step <= 0:\n      self._worker_is_started = True\n      return None\n\n    while True:\n      if self._tide_start_hour is not None and self._tide_end_hour is not None:\n        if not tide_available_now(self._tide_start_hour,\n                                  self._tide_start_minute, self._tide_end_hour,\n                                  self._tide_end_minute):\n          logging.info(\"Current UTC time: {} : {}\".format(\n              datetime.utcnow().hour,\n              datetime.utcnow().minute))\n          logging.info(\"Last hour in tide queue. Saving ckpt...\")\n          run_context.request_stop()\n          return\n\n      current_step = run_context.session.run(self._global_step_tensor)\n      if self._hook_start_time is None and current_step > 1:\n        # Wait for the chief node to start training for at least one step\n        # before starting the timer.\n        self._hook_start_time = time.time()\n\n      if current_step >= self._wait_until_step:\n        self._worker_is_started = True\n        logging.info(\n            \"Start training after waiting for {} global steps. Current step is {}.\"\n            .format(self._wait_until_step, current_step))\n        return None\n\n      has_been_waiting_seconds = None\n      if self._hook_start_time is not None:\n        has_been_waiting_seconds = time.time() - self._hook_start_time\n        if has_been_waiting_seconds > self._non_tide_wait_second:\n          self._worker_is_started = True\n          logging.info(\n              \"Start training after waiting for {} seconds. Current step is {}.\"\n              .format(self._non_tide_wait_second, current_step))\n          return None\n\n      logging.log_every_n_seconds(\n          logging.INFO,\n          \"Waiting for global_step >= {} or waiting time > {} seconds before starting training. \"\n          \"Current step is {}, has been waiting for {} seconds already.\".format(\n              self._wait_until_step, self._non_tide_wait_second, current_step,\n              has_been_waiting_seconds), 60)\n      time.sleep(0.5)\n\n\nclass TideStoppingHook(tf.estimator.SessionRunHook):\n\n  def __init__(self,\n               tide_start_hour=None,\n               tide_start_minute=None,\n               tide_end_hour=None,\n               tide_end_minute=None):\n    \"\"\"Initializes a `GlobalStepWaiterHook`.\n\n    Args:\n      wait_until_step: an `int` shows until which global step should we wait.\n      tide_start_hour: the first hour in utc timezone when tide resources are available.\n      tide_end_hour: the last hour in utc timezone when tide resources are available.\n    \"\"\"\n    self._tide_start_hour = tide_start_hour\n    self._tide_start_minute = tide_start_minute\n    self._tide_end_hour = tide_end_hour\n    self._tide_end_minute = tide_end_minute\n\n  def before_run(self, run_context):\n    if self._tide_start_hour is not None and self._tide_end_hour is not None:\n      if not tide_available_now(self._tide_start_hour, self._tide_start_minute,\n                                self._tide_end_hour, self._tide_end_minute):\n        logging.info(\"Current UTC time: {} : {}\".format(\n            datetime.utcnow().hour,\n            datetime.utcnow().minute))\n        logging.info(\"Last hour in tide queue. Saving ckpt...\")\n        run_context.request_stop()\n"
  },
  {
    "path": "monolith/native_training/session_run_hooks_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport threading\nimport time\n\nimport tensorflow as tf\n\nfrom absl import logging\nfrom freezegun import freeze_time\nfrom tensorflow.python.platform import test\n\nfrom monolith.native_training import session_run_hooks\n\n\nclass MockDateTime:\n\n  def __init__(self, hour, minute):\n    self.hour = hour\n    self.minute = minute\n\n\nclass GlobalStepWaiterHookTest(tf.test.TestCase):\n\n  def test_not_wait_for_step_zero(self):\n    with tf.compat.v1.Graph().as_default():\n      tf.compat.v1.train.get_or_create_global_step()\n      hook = session_run_hooks.CustomGlobalStepWaiterHook(wait_until_step=0)\n      hook.begin()\n      with tf.compat.v1.Session() as sess:\n        # Before run should return without waiting gstep increment.\n        hook.before_run(\n            tf.estimator.SessionRunContext(original_args=None, session=sess))\n\n  @freeze_time(\"2012-01-14 10:00:00\")\n  def test_not_wait_if_tide_not_available(self):\n    with tf.compat.v1.Graph().as_default():\n      tf.compat.v1.train.get_or_create_global_step()\n      hook = session_run_hooks.CustomGlobalStepWaiterHook(wait_until_step=0,\n                                                          tide_start_hour=1,\n                                                          tide_start_minute=0,\n                                                          tide_end_hour=5,\n                                                          tide_end_minute=0)\n      hook.begin()\n      with tf.compat.v1.Session() as sess:\n        # Before run should return without waiting gstep increment.\n        hook.before_run(\n            tf.estimator.SessionRunContext(original_args=None, session=sess))\n\n  @test.mock.patch.object(time, 'sleep')\n  def test_wait_for_step(self, mock_sleep):\n    with tf.compat.v1.Graph().as_default():\n      gstep = tf.compat.v1.train.get_or_create_global_step()\n      hook = session_run_hooks.CustomGlobalStepWaiterHook(wait_until_step=1000)\n      hook.begin()\n\n      with tf.compat.v1.Session() as sess:\n        # Mock out calls to time.sleep() to update the global step.\n\n        class Context(object):\n          counter = 0\n\n        def mock_sleep_side_effect(seconds):\n          del seconds  # argument is ignored\n          Context.counter += 1\n          if Context.counter == 1:\n            # The first time sleep() is called, we update the global_step from\n            # 0 to 500.\n            sess.run(tf.compat.v1.assign(gstep, 500))\n          elif Context.counter == 2:\n            # The second time sleep() is called, we update the global_step from\n            # 500 to 1100.\n            sess.run(tf.compat.v1.assign(gstep, 1100))\n          else:\n            raise AssertionError(\n                'Expected before_run() to terminate after the second call to '\n                'time.sleep()')\n\n        mock_sleep.side_effect = mock_sleep_side_effect\n\n        # Run the mocked-out interaction with the hook.\n        self.evaluate(tf.compat.v1.global_variables_initializer())\n        run_context = tf.estimator.SessionRunContext(original_args=None,\n                                                     session=sess)\n        hook.before_run(run_context)\n        self.assertEqual(Context.counter, 2)\n\n\nclass MockSessionRunContext:\n\n  def __init__(self):\n    self.requested_stop = False\n\n  def request_stop(self):\n    logging.info(\"stop requested\")\n    self.requested_stop = True\n    logging.info(self.requested_stop)\n\n\nclass TideStoppingHookTest(tf.test.TestCase):\n\n  @freeze_time(\"2012-01-14 10:00:00\")\n  def test_stop_if_tide_not_available(self):\n    with tf.compat.v1.Graph().as_default():\n      hook = session_run_hooks.TideStoppingHook(tide_start_hour=1,\n                                                tide_start_minute=0,\n                                                tide_end_hour=5,\n                                                tide_end_minute=0)\n      hook.begin()\n      with tf.compat.v1.Session() as _:\n        # Before run should return without waiting gstep increment.\n        context = MockSessionRunContext()\n        hook.before_run(context)\n        self.assertEqual(context.requested_stop, True)\n\n  @freeze_time(\"2012-01-14 10:00:00\")\n  def test_do_not_stop_if_tide_available(self):\n    with tf.compat.v1.Graph().as_default():\n      hook = session_run_hooks.TideStoppingHook(tide_start_hour=1,\n                                                tide_start_minute=0,\n                                                tide_end_hour=12,\n                                                tide_end_minute=0)\n      hook.begin()\n      with tf.compat.v1.Session() as _:\n        # Before run should return without waiting gstep increment.\n        context = MockSessionRunContext()\n        hook.before_run(context)\n        self.assertEqual(context.requested_stop, False)\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/signal_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport traceback\nimport signal\nimport time\n\n\ndef print_stack_trace(sig, frame):\n  for line in traceback.format_stack(frame):\n    print(line.strip())\n\n\ndef add_siguser1_handler():\n  ret = signal.getsignal(signal.SIGUSR1)\n\n  def handler(sig, frame):\n    if callable(ret):\n      ret(sig, frame)\n    print_stack_trace(sig, frame)\n\n  signal.signal(signal.SIGUSR1, handler)\n\n\n# Adds default handler\nadd_siguser1_handler()\n"
  },
  {
    "path": "monolith/native_training/signal_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport signal\nimport unittest\n\nfrom monolith.native_training import signal_utils\n\n\nclass SignalUtilsTest(unittest.TestCase):\n\n  def testBasic(self):\n    # Add twice to test two handlers case.\n    signal_utils.add_siguser1_handler()\n    signal.raise_signal(signal.SIGUSR1)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/static_reshape_op.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom absl import logging\nfrom typing import List, Tuple\n\nimport tensorflow as tf\n\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\nreshape_ops = gen_monolith_ops\n\n\ndef static_reshape(\n    inputs: List[tf.Tensor],\n    shapes: List[Tuple[int]],\n    enable_parallelism: bool = True) -> Tuple[List[tf.Tensor], tf.Tensor]:\n  \"\"\"\n  Arguments:\n    inputs: List of input tensors.\n    shapes: List of target shapes for input tensors.\n  \n  Returns:\n    outputs: List of reshaped tensors.\n    sizes: A Tensor containing the size of output tensors.\n  \"\"\"\n  return reshape_ops.monolith_static_reshape_n(\n      inputs=inputs, shapes=shapes, enable_parallelism=enable_parallelism)\n\n\nclass StaticReshapeNBuilder:\n\n  def __init__(self, enable_parallelism: bool = True):\n    self.inputs = []\n    self.shapes = []\n    self.enable_parallelism = enable_parallelism\n\n  def add(self, input: tf.Tensor, shape: Tuple[int]) -> int:\n    \"\"\"Returns index of input added.\"\"\"\n    self.inputs.append(input)\n    self.shapes.append(shape)\n    return len(self.inputs) - 1\n\n  def build(self):\n    return static_reshape(inputs=self.inputs,\n                          shapes=self.shapes,\n                          enable_parallelism=self.enable_parallelism)\n"
  },
  {
    "path": "monolith/native_training/static_reshape_op_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\n\nfrom monolith.native_training import static_reshape_op\n\n\nclass StaticReshapeOpTest(tf.test.TestCase):\n\n  def test_static_reshape_n(self):\n    inputs = [\n        tf.ones(shape=(5,), dtype=tf.int32),\n        tf.ones(shape=(4, 10), dtype=tf.float32),\n        tf.ones(shape=(2, 2, 3), dtype=tf.int64),\n    ]\n    shapes = [\n        (1, 5),\n        (5, 8),\n        (None, 2),\n    ]\n    with tf.compat.v1.Session() as sess:\n      res = static_reshape_op.static_reshape(inputs, shapes)\n      outputs, sizes = sess.run(res)\n\n    self.assertAllEqual(sizes, [5, 40, 12])\n\n    for out, shape in zip(outputs, shapes):\n      self.assertEqual(len(out.shape), len(shape))\n      for d in range(len(shape)):\n        if shape[d] is not None:\n          self.assertEqual(out.shape[d], shape[d])\n\n  def test_nested_reshape_n(self):\n    builder = static_reshape_op.StaticReshapeNBuilder()\n\n    structure = [{\n        \"key_0\": tf.constant([[1, 2, 3], [4, 5, 6]], dtype=tf.float32),\n        \"key_1\": tf.constant([[3, 3], [5, 5]], dtype=tf.float32)\n    }, {\n        \"key_1\": tf.constant([[0], [1], [2]], dtype=tf.float32)\n    }]\n    target = [{\n        \"key_0\": np.array([1, 2, 3, 4, 5, 6], dtype=np.float32),\n        \"key_1\": np.array([3, 3, 5, 5], dtype=np.float32)\n    }, {\n        \"key_1\": np.array([0, 1, 2], dtype=np.float32)\n    }]\n\n    def flatten(tensor):\n      return builder.add(tensor, (None,))\n\n    list_keyed_id = tf.nest.map_structure(flatten, structure)\n    res = builder.build()\n\n    with tf.compat.v1.Session() as sess:\n      outputs, _ = sess.run(res)\n\n    for di, dt in zip(list_keyed_id, target):\n      for k in sorted(dt.keys()):\n        self.assertAllEqual(outputs[di[k]], dt[k])\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/summary/BUILD",
    "content": "load(\"@rules_python//python:defs.bzl\", \"py_binary\", \"py_library\", \"py_test\")\nload(\"@org_tensorflow//tensorflow:tensorflow.bzl\", \"tf_cc_binary\", \"tf_cc_test\", \"tf_kernel_library\")\nload(\"@pip_deps//:requirements.bzl\", \"requirement\")\n\npackage(\n    default_visibility = [\"//visibility:public\"],\n)\n\npy_library(\n    name = \"utils\",\n    srcs = [\"utils.py\"],\n    srcs_version = \"PY3\",\n    deps = [requirement('tensorboard')],\n)\n\n\npy_test(\n    name = \"utils_test\",\n    srcs = [\"utils_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n    ],\n)\n\n\npy_library(\n    name = \"summary_ops\",\n    srcs = [\"summary_ops.py\"],\n    srcs_version = \"PY3\",\n    deps = [\n        \":utils\",\n        \"//monolith/native_training/layers:layer_ops\",\n    ]\n)\n\n\npy_test(\n    name = \"summary_ops_test\",\n    srcs = [\"summary_ops_test.py\"],\n    python_version = \"PY3\",\n    srcs_version = \"PY3\",\n    deps = [\n        \":summary_ops\",\n    ],\n)\n"
  },
  {
    "path": "monolith/native_training/summary/summary_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nfrom typing import List, Dict, Union, Optional\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.util.tf_export import tf_export\nfrom monolith.native_training.summary import utils\nfrom monolith.native_training.summary.utils import SummaryType\nfrom monolith.native_training.layers.layer_ops import feature_insight\n\n\n@tf_export(v1=[\"summary.nas_data\"])\ndef nas_data(weight, segment_names=None, segment_sizes=None, group_info=None, raw_tag=None,\n             collections=None, description=None, name=None):\n  meta_content, summaty_type = utils.prepare_head(segment_names, segment_sizes, group_info,\n                                                  raw_tag, out_type='bytes')\n  name = f'{name}_{summaty_type}' if name else summaty_type\n  description = description or summaty_type\n  with tf.name_scope(name):\n    return tf.compat.v1.summary.tensor_summary(\n      name=utils.MONOLITH_NAS_DATA,\n      tensor=weight,\n      collections=collections or [ops.GraphKeys.SUMMARIES],\n      summary_metadata=utils.create_summary_metadata(description, meta_content),\n    )\n\n\n@tf_export(v1=[\"summary.feature_insight_data\"])\ndef feature_insight_data(input_tensor: tf.Tensor, segment_names: List[str], segment_sizes: List[int],\n                         weight: tf.Tensor = None, group_info: Union[List[int], List[List[int]]] = None,\n                         label: tf.Tensor = None, collections: List[str] = None,\n                         description: str = None, name: str = None):\n  assert segment_sizes is not None and len(segment_names) == len(segment_sizes)\n  aggregate = True if label is None else False\n  raw_tag = SummaryType.FEATURE_INSIGHT_DIRECT if aggregate else SummaryType.FEATURE_INSIGHT_TRAIN\n  if weight is None:\n    summary_data = input_tensor\n  else:\n    summary_data = feature_insight(\n      input_embedding=input_tensor, weight=weight, segment_sizes=segment_sizes, aggregate=aggregate)\n    segment_sizes = [1 if aggregate else weight.shape.as_list()[-1]] * len(segment_sizes)\n  meta_content, summaty_type = utils.prepare_head(segment_names, segment_sizes, group_info,\n                                                  raw_tag=raw_tag, out_type='json')\n  name = f'{name}_{summaty_type}' if name else summaty_type\n  description = description or summaty_type\n  if label is not None:\n    if label.dtype != tf.float32:\n      label = tf.cast(label, dtype=tf.float32)\n    if label.shape.rank == 1:\n      label = tf.reshape(label, shape=(-1, 1))\n      meta_content['label_size'] = 1\n    else:\n      meta_content['label_size'] = label.shape.as_list()[-1]\n    summary_data = tf.concat(values=[summary_data, label], axis=1)\n  else:\n    meta_content['label_size'] = 0\n\n  with tf.name_scope(name):\n    return tf.compat.v1.summary.tensor_summary(\n      name=utils.MONOLITH_FI_DATA,\n      tensor=summary_data,\n      collections=collections or [ops.GraphKeys.SUMMARIES],\n      summary_metadata=utils.create_summary_metadata(description, json.dumps(meta_content)),\n    )\n"
  },
  {
    "path": "monolith/native_training/summary/summary_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nimport unittest\nimport tensorflow as tf\nfrom tensorboard import plugin_util\nfrom tensorboard.backend.event_processing.plugin_event_multiplexer import EventMultiplexer\nfrom tensorboard.backend.event_processing.data_provider import MultiplexerDataProvider\nfrom tensorflow.python.training.checkpoint_state_pb2 import CheckpointState\nfrom tensorboard.data.provider import DataProvider, RunTagFilter\n\nfrom monolith.native_training.summary.utils import PLUGIN_NAME, prepare_head\nfrom monolith.native_training.summary import summary_ops\n\n\ntf.compat.v1.disable_eager_execution()\n\n\nclass SummaryTest(unittest.TestCase):\n  @classmethod\n  def setUpClass(cls) -> None:\n    cls.log_dir = \"demo_logs_v1\"\n    cls.sess = tf.compat.v1.Session()\n\n    segment_names = ['f1', 'f2', 'f3']\n    segment_sizes = [3, 5, 9]\n    group_info = [['f1', 'f2'], ['f3', 'f4'], ['f5', 'f6']]\n    cls.weight = [0.5, 0.8]\n    weight=tf.constant(value=cls.weight, dtype=tf.float32, name='weight')\n    summary_ops.nas_data(weight, segment_names, segment_sizes, group_info)\n    \n    input_tensor = tf.random.uniform(shape=(3, 17), dtype=tf.float32)\n    label = tf.constant(value=[1, 0, 1], shape=(3,), dtype=tf.float32)\n    weight_tensor = tf.random.uniform(shape=(17, 2), dtype=tf.float32)\n    summary_ops.feature_insight_data(input_tensor, segment_names, segment_sizes,\n                                     label=label, weight=weight_tensor)\n    with cls.sess.as_default():\n      with tf.compat.v1.summary.FileWriter(cls.log_dir) as writer:\n        summaries = tf.compat.v1.summary.merge_all()\n        for global_step in range(10):\n          summaries_out = cls.sess.run(summaries)\n          writer.add_summary(summaries_out, global_step)\n\n    multiplexer = EventMultiplexer()\n    multiplexer.AddRunsFromDirectory(path=cls.log_dir)\n    multiplexer.Reload()\n    cls.data_provider: DataProvider = MultiplexerDataProvider(\n      multiplexer, logdir=cls.log_dir)\n\n  @classmethod\n  def tearDownClass(cls) -> None:\n    if tf.io.gfile.exists(cls.log_dir):\n      tf.io.gfile.rmtree(cls.log_dir)\n\n  def test_nas_data(self):\n    ctx = plugin_util.context({})\n    run = '.'\n    tag = 'gating/monolith_nas_weight'\n    tag_info = self.data_provider.list_tensors(ctx,\n                                               experiment_id='0',\n                                               plugin_name=PLUGIN_NAME,\n                                               run_tag_filter=RunTagFilter(runs=[run], tags=[tag]))\n    tensors = self.data_provider.read_tensors(ctx,\n                                              experiment_id='0',\n                                              plugin_name=PLUGIN_NAME,\n                                              downsample=100,\n                                              run_tag_filter=RunTagFilter(runs=[run], tags=[tag]))\n    tensor_tts = tag_info.get(run, {}).get(tag, None)\n    tensor_datum = tensors.get(run, {}).get(tag, None)\n    self.assertTrue(tensor_datum is not None)\n    if isinstance(tensor_datum, (list, tuple)):\n      tensor_datum = tensor_datum[-1]\n    \n    plugin_content = str(tensor_tts.plugin_content, encoding='utf-8')\n    plugin_content_exp = '{\"tag_type\": \"gating\", \"segment_names\": [\"f1\", \"f2\", \"f3\"], \"segment_sizes\": [3, 5, 9], \"group_index\": [0, 0, 1]}'\n    self.assertEqual(plugin_content, plugin_content_exp)\n    for x, y in zip(tensor_datum.numpy, self.weight):\n      self.assertAlmostEqual(x, y)\n\n  def test_feature_insight_data(self):\n    ctx = plugin_util.context({})\n    run = '.'\n    tag = 'fi_train/monolith_feature_insight'\n    tag_info = self.data_provider.list_tensors(ctx,\n                                               experiment_id='0',\n                                               plugin_name=PLUGIN_NAME,\n                                               run_tag_filter=RunTagFilter(runs=[run], tags=[tag]))\n    tensors = self.data_provider.read_tensors(ctx,\n                                              experiment_id='0',\n                                              plugin_name=PLUGIN_NAME,\n                                              downsample=100,\n                                              run_tag_filter=RunTagFilter(runs=[run], tags=[tag]))\n    tensor_tts = tag_info.get(run, {}).get(tag, None)\n    tensor_datum = tensors.get(run, {}).get(tag, None)\n    self.assertTrue(tensor_datum is not None)\n    if isinstance(tensor_datum, (list, tuple)):\n      tensor_datum = tensor_datum[-1]\n\n    plugin_content = str(tensor_tts.plugin_content, encoding='utf-8')\n    label_size = json.loads(plugin_content)['label_size']\n    dim = 2 if label_size > 0 else 1\n    plugin_content_exp = '{\"tag_type\": \"fi_train\", \"segment_names\": [\"f1\", \"f2\", \"f3\"], \"segment_sizes\": [2, 2, 2], \"group_index\": [0, 1, 2], \"label_size\": 1}'\n    self.assertEqual(plugin_content, plugin_content_exp)\n\n    shape_exp = (3, 7 if label_size > 0 else 3)\n    self.assertTupleEqual(tensor_datum.numpy.shape, shape_exp)\n    \n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/summary/utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport json\nfrom functools import lru_cache\nfrom typing import Any, Dict, List, Tuple, Union\n\nimport tensorflow as tf\nfrom tensorboard.compat.proto import summary_pb2\n\n\nPLUGIN_NAME = 'monolith'\nMONOLITH_NAS_DATA = f'{PLUGIN_NAME}_nas_weight'\nMONOLITH_FI_DATA = f'{PLUGIN_NAME}_feature_insight'\nKTYPE, KMETA, KDATA = 'tag_type', 'meta', 'data'\n\n\nclass SummaryType(object):\n  GATING = 'gating'\n  SELECTING = 'selecting'\n  MIXED = 'mixed'\n  SIMPLE = 'simple'\n  FEATURE_INSIGHT_DIRECT = 'fi_direct'\n  FEATURE_INSIGHT_TRAIN = 'fi_train'\n\n\n# https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/framework/summary.proto\ndef create_summary_metadata(description: str = None, meta_content=b''):\n  return summary_pb2.SummaryMetadata(\n    summary_description=description,\n    plugin_data=summary_pb2.SummaryMetadata.PluginData(\n      plugin_name=PLUGIN_NAME,\n      content=meta_content.encode('utf-8') if isinstance(meta_content, str) else meta_content,\n    ),\n    data_class=summary_pb2.DATA_CLASS_TENSOR,\n  )\n\n\ndef _name_to_group_id(segment_names: List[str], group_info: List[List[str]]):\n  if group_info:\n    name_to_group: Dict[str, int] = {}\n    for i, group in enumerate(group_info):\n      for name in group:\n        name_to_group[name] = i\n\n    group_id_to_names: Dict[int, List[str]] = {}\n    for name in segment_names:\n      assert name in name_to_group\n      group_id = name_to_group[name]\n      if group_id in group_id_to_names:\n        group_id_to_names[group_id].append(name)\n      else:\n        group_id_to_names[group_id] = [name]\n\n    name_to_reorder_id = {}\n    for idx, group_id in enumerate(sorted(group_id_to_names)):\n      for name in group_id_to_names[group_id]:\n        name_to_reorder_id[name] = idx\n    name_to_reorder_id = name_to_reorder_id\n  else:\n    name_to_reorder_id = {name: idx for idx, name in enumerate(segment_names)}\n\n  return name_to_reorder_id\n\n\ndef prepare_head(segment_names: List[str], segment_sizes: Union[List[int], List[List[int]]],\n                 group_info: List[List[str]] = None, raw_tag: str = None, out_type: str = 'tensor'\n                 ) -> Tuple[Any, str]:\n  assert out_type in {'bytes', 'tensor', 'json'}\n  if not (segment_names or segment_sizes):\n    if out_type == 'tensor':\n      return tf.constant(value=[b''], dtype=tf.string, shape=tuple()), raw_tag\n    else:\n      return b'', raw_tag\n\n  raw_tag = raw_tag or (\n    SummaryType.GATING if all(isinstance(s, int) for s in segment_sizes) else SummaryType.SELECTING)\n  data = {\n    KTYPE: raw_tag,\n    'segment_names': segment_names,\n    'segment_sizes': segment_sizes,\n  }\n  if raw_tag in {SummaryType.GATING, SummaryType.FEATURE_INSIGHT_TRAIN}:\n    name_to_reorder_id = _name_to_group_id(segment_names, group_info)\n    data['group_index'] = [name_to_reorder_id[name] for name in segment_names]\n\n  if out_type == 'tensor':\n    return tf.constant(value=[json.dumps(data)], dtype=tf.string, shape=tuple()), raw_tag\n  elif out_type == 'json':\n    return data, raw_tag\n  else:\n    return json.dumps(data), raw_tag\n\n\n@lru_cache\ndef get_nas_weight_json(ckpt_dir_or_file, prefix=None) -> List[str]:\n  prefix = prefix or ARCH_TENSOR_PREFIX\n  ckpt = tf.train.load_checkpoint(ckpt_dir_or_file=ckpt_dir_or_file)\n  if ckpt:\n    for name in ckpt.get_variable_to_dtype_map():\n      if prefix in name:\n        return [str(v) for v in ckpt.get_tensor(name).flat]\n  raise Exception('not arch_weights in ckpt')\n"
  },
  {
    "path": "monolith/native_training/summary/utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport unittest\nfrom monolith.native_training.summary.utils import \\\n  prepare_head, SummaryType, get_nas_weight_json\n\n\nclass UtilsTest(unittest.TestCase):\n\n  def test_read_head_gating(self):\n    segment_names = ['f1', 'f2', 'f3']\n    segment_sizes = [3, 5, 9]\n    group_info = [['f1', 'f2'], ['f3', 'f4'], ['f5', 'f6']]\n    data, nas_type = prepare_head(segment_names, segment_sizes, group_info)\n    data_exp = b'{\"tag_type\": \"gating\", \"segment_names\": [\"f1\", \"f2\", \"f3\"], \"segment_sizes\": [3, 5, 9], \"group_index\": [0, 0, 1]}'\n\n    self.assertEqual(nas_type, SummaryType.GATING)\n    self.assertEqual(data.numpy(), data_exp)\n\n  def test_read_head_selecting(self):\n    segment_names = ['f1', 'f2', 'f3']\n    segment_sizes = [[3, 6], [5, 10], [4, 8, 16]]\n    data, nas_type = prepare_head(segment_names, segment_sizes)\n    data_exp = b'{\"tag_type\": \"selecting\", \"segment_names\": [\"f1\", \"f2\", \"f3\"], \"segment_sizes\": [[3, 6], [5, 10], [4, 8, 16]]}'\n    \n    self.assertEqual(nas_type, SummaryType.SELECTING)\n    self.assertEqual(data.numpy(), data_exp)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/sync_hooks.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport time\n\nfrom absl import logging\n\nimport tensorflow as tf\n\n\nclass SyncHelper:\n\n  #TODO(leqi.zou): maybe in the future, we want to support the dynamic number of workers.\n  def __init__(self,\n               num_workers: int,\n               is_chief,\n               var_device=\"/job:chief/task:0\"):\n    self._num_workers = num_workers\n    with tf.name_scope(\"monolith_sync_helper\"):\n      # In distributed training, the var is local variable for chief, but global variable for worker.\n      collections = [tf.compat.v1.GraphKeys.LOCAL_VARIABLES\n                    ] if is_chief else [tf.compat.v1.GraphKeys.VARIABLES]\n      with tf.device(var_device):\n        # For idx 0, represents the current restore status\n        # For idx >0, represents if the current worker is alive\n        self._var = tf.compat.v1.get_variable(\n            \"monolith_sync_helper/control_var\",\n            initializer=[False] * num_workers,\n            dtype=tf.bool,\n            trainable=False,\n            collections=collections)\n\n      self._idx_ph = tf.compat.v1.placeholder(tf.int32, shape=[], name=\"idx_ph\")\n      self._val_ph = tf.compat.v1.placeholder(tf.bool,\n                                              shape=[],\n                                              name=\"value_ph\")\n      self._read_value = self._var[self._idx_ph]\n      self._assign_value = self._var[self._idx_ph].assign(self._val_ph)\n      self._workers_status = self._var[1:]\n      self._alive_workers = tf.where(self._workers_status) + 1\n      self._num_alive_workers = tf.math.reduce_sum(\n          tf.cast(self._workers_status, tf.int32))\n\n  @property\n  def num_workers(self):\n    return self._num_workers\n\n  def mark_restore_done(self, sess):\n    sess.run(self._assign_value,\n             feed_dict={\n                 self._idx_ph: 0,\n                 self._val_ph: True\n             })\n\n  def get_restore_status(self, sess):\n    return sess.run(self._read_value, feed_dict={self._idx_ph: 0})\n\n  def start_worker(self, sess, idx: int):\n    assert idx > 0 and idx < self._num_workers, f\"Index {idx} is out range \"\n    sess.run(self._assign_value,\n             feed_dict={\n                 self._idx_ph: idx,\n                 self._val_ph: True\n             })\n\n  def finish_worker(self, sess, idx: int):\n    assert idx > 0 and idx < self._num_workers, f\"Index {idx} is out range \"\n    sess.run(self._assign_value,\n             feed_dict={\n                 self._idx_ph: idx,\n                 self._val_ph: False\n             })\n\n  def get_alive_workers(self, sess):\n    return sess.run(self._alive_workers).flatten()\n\n  def get_num_alive_workers(self, sess):\n    return sess.run(self._num_alive_workers)\n\n\n_CHIEF_TIMEOUT_SECONDS = 1800\n\n\nclass ChiefSyncHook(tf.estimator.SessionRunHook):\n  \"\"\"\n  A hook that used for chief and worker sync at the beginning and at the end.\n  \"\"\"\n\n  def __init__(self,\n               sync_helper: SyncHelper,\n               timeout_seconds=_CHIEF_TIMEOUT_SECONDS):\n    self._timeout_seconds = timeout_seconds\n    self._helper = sync_helper\n\n  def after_create_session(self, session, coord):\n    self._helper.mark_restore_done(session)\n\n  def end(self, session):\n    start_time = time.time()\n    while True:\n      num_alive_workers = self._helper.get_num_alive_workers(session)\n      if time.time(\n      ) - start_time > self._timeout_seconds or num_alive_workers == 0:\n        break\n      logging.log_every_n_seconds(\n          logging.INFO,\n          \"Total worker count: {}, remaining count: {}.\\nRemaining workers: %s\".\n          format(self._helper.num_workers, num_alive_workers), 60,\n          self._helper.get_alive_workers(session))\n      time.sleep(1)\n\n    if num_alive_workers > 0:\n      logging.info(\"Reach timeout seconds! Remaining worker count: {}.\".format(\n          num_alive_workers))\n    else:\n      logging.info(\"All other workers had been finished.\")\n\n\nclass WorkerSyncHook(tf.estimator.SessionRunHook):\n  \"\"\"\n  A hook that used for chief and worker sync at the beginning and at the end.\n  \"\"\"\n\n  def __init__(self, worker_index, sync_helper: SyncHelper):\n    self._worker_index = worker_index\n    self._helper = sync_helper\n\n  def after_create_session(self, session, coord):\n    if self._worker_index > 0:\n      self._helper.start_worker(session, self._worker_index)\n      while not self._helper.get_restore_status(session):\n        logging.log_every_n_seconds(\n            logging.INFO,\n            \"The worker {} waits for start signal of chief.\".format(\n                self._worker_index), 60)\n        time.sleep(1)\n\n  def end(self, session):\n    if self._worker_index > 0:\n      self._helper.finish_worker(session, self._worker_index)\n\n\nclass TrainingHooksHelper:\n\n  def __init__(self,\n               enable_sync: bool,\n               num_workers: int,\n               worker_idx: int,\n               chief_timeout_seconds: int = _CHIEF_TIMEOUT_SECONDS):\n    self._enable_sync = enable_sync\n    self._training_chief_hooks = []\n    self._training_hooks = []\n    if self._enable_sync:\n      sync_helper = SyncHelper(num_workers, worker_idx == 0)\n      self._training_chief_hooks.append(\n          ChiefSyncHook(sync_helper, timeout_seconds=chief_timeout_seconds))\n      self._training_hooks.append(WorkerSyncHook(worker_idx, sync_helper))\n\n  @property\n  def training_chief_hooks(self):\n    return tuple(self._training_chief_hooks)\n\n  @property\n  def training_hooks(self):\n    return tuple(self._training_hooks)\n"
  },
  {
    "path": "monolith/native_training/sync_hooks_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport threading\nimport time\n\nimport tensorflow as tf\n\nfrom monolith.native_training import sync_hooks\n\n\nclass CountHook(tf.estimator.SessionRunHook):\n\n  def __init__(self):\n    self.after_create_session_count = 0\n    self.end_count = 0\n\n  def after_create_session(self, session, coord):\n    self.after_create_session_count += 1\n\n  def end(self, session):\n    self.end_count += 1\n\n  def get_counts(self):\n    return {\n        'after_create_session': self.after_create_session_count,\n        'end': self.end_count\n    }\n\n\ndef get_local_helper(num_workers):\n  return sync_hooks.SyncHelper(num_workers, is_chief=True, var_device=None)\n\n\nclass SyncHooksTest(tf.test.TestCase):\n\n  def _after_create_session(self, sess, hooks):\n    for hook in hooks:\n      hook.after_create_session(sess, None)\n\n  def _end(self, sess, hooks):\n    for hook in hooks:\n      hook.end(sess)\n\n  def test_sync_process(self):\n    with tf.compat.v1.Graph().as_default():\n      helper = get_local_helper(2)\n      chief_hook = sync_hooks.ChiefSyncHook(helper)\n      worker_hook = sync_hooks.WorkerSyncHook(1, helper)\n      worker_count_hook = CountHook()\n      chief_count_hook = CountHook()\n\n      with tf.compat.v1.Session() as sess:\n        sess.run(tf.compat.v1.local_variables_initializer())\n        worker = threading.Thread(target=self._after_create_session,\n                                  args=(sess, [worker_hook, worker_count_hook]))\n        worker.daemon = True\n        worker.start()\n\n        time.sleep(1)\n        # Worker hook is pending at 'after_create_session'.\n        self.assertEqual({\n            'after_create_session': 0,\n            'end': 0,\n        }, worker_count_hook.get_counts())\n\n        chief_hook.after_create_session(sess, None)\n        worker.join()\n        self.assertEqual({\n            'after_create_session': 1,\n            'end': 0,\n        }, worker_count_hook.get_counts())\n\n        worker_hook.after_create_session(sess, None)\n        chief = threading.Thread(target=self._end,\n                                 args=(sess, [chief_hook, chief_count_hook]))\n        chief.daemon = True\n        chief.start()\n\n        # Chief hook is pending at 'end'.\n        self.assertEqual({\n            'after_create_session': 0,\n            'end': 0,\n        }, chief_count_hook.get_counts())\n\n        # Make sure logging is covered\n        time.sleep(1)\n        worker_hook.end(sess)\n        chief.join()\n        self.assertEqual({\n            'after_create_session': 0,\n            'end': 1,\n        }, chief_count_hook.get_counts())\n\n  def test_hook_helper(self):\n    h = sync_hooks.TrainingHooksHelper(False, 0, 0)\n    self.assertEqual(h.training_hooks, ())\n    self.assertEqual(h.training_chief_hooks, ())\n\n    h = sync_hooks.TrainingHooksHelper(True, 1, 0)\n    # This only for grammar check\n    h.training_hooks\n    h.training_chief_hooks\n\n\nif __name__ == '__main__':\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/sync_training_hooks.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n#coding:utf-8\nfrom cProfile import run\nimport os\nimport uuid\nimport tempfile\nimport time\nfrom datetime import datetime\n\nfrom absl import logging\nimport grpc\nimport numpy as np\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.training import training_util\nfrom tensorflow.python.training import session_run_hook\n\nfrom tensorflow_serving.apis import predict_pb2, get_model_metadata_pb2\nfrom tensorflow_serving.apis import prediction_service_pb2_grpc\n\nfrom monolith.agent_service import backends\ntry:\n  from monolith.coordinator.utils import token_utils\nexcept ImportError:\n  pass\n\nfrom monolith.native_training.data import datasets\nfrom monolith.native_training import distributed_serving_ops\nfrom monolith.native_training import hvd_lib\nfrom monolith.native_training import native_task\nfrom monolith.native_training import hash_table_ops\nfrom monolith.native_training.distributed_serving_ops import ParameterSyncClient, refresh_sync_config\nfrom monolith.utils import find_main\nfrom monolith.native_training.model_export import export_context\n\n\nclass SyncTrainingBarrierSaverListener(tf.estimator.CheckpointSaverListener):\n\n  def begin(self):\n    self._barrier_op = None\n    self._barrier_var = tf.compat.v1.placeholder(dtype=tf.int64,\n                                                 shape=[],\n                                                 name=\"hvd_export_barrier_ph\")\n    self._barrier_op = hvd_lib.broadcast(tf.identity(self._barrier_var), 0)\n\n  def after_save(self, session, global_step_value):\n    logging.info(f\"exporter barrier begin {hvd_lib.rank()}\")\n    try:\n      barrier_val = session.run(\n          self._barrier_op, feed_dict={self._barrier_var: global_step_value})\n      logging.info(\n          f\"exporter barrier end {hvd_lib.rank()} value: {barrier_val}\")\n    except Exception as ex:\n      logging.error(f\"barrier error: {ex}\")\n\n\nclass ParameterSyncHook(session_run_hook.SessionRunHook):\n  \"\"\"\n  sync parameter sync to online ps\n  \"\"\"\n\n  def __init__(self, sync_backend, ps_index, refresh_interval=100):\n    self._sync_backend = sync_backend\n    self._ps_index = ps_index\n    self._refresh_interval = refresh_interval\n    self._last_sync_time = 0\n    self._last_refresh_time = 0\n    self._sync_config = None\n    logging.info(\n        f\"sync hook for ps_{self._ps_index} with refresh_interval={self._refresh_interval}\"\n    )\n\n  def begin(self):\n    self._config_ph = tf.compat.v1.placeholder(tf.string,\n                                               shape=(),\n                                               name=\"sync_config_str\")\n    sync_client = ParameterSyncClient(\n        distributed_serving_ops.parameter_sync_client_from_config(\n            name_suffix=str(self._ps_index)))\n    self._sync_run_step = sync_client.create_sync_op(self._config_ph)\n\n  def before_run(self, run_context):\n    cur_time = time.time()\n    if cur_time - self._last_refresh_time >= self._refresh_interval:\n      self._sync_config = refresh_sync_config(self._sync_backend,\n                                              self._ps_index)\n      self._last_refresh_time = cur_time\n\n    return session_run_hook.SessionRunArgs(\n        fetches=self._sync_run_step,\n        feed_dict={self._config_ph: self._sync_config})\n\n\nclass SyncTrainingForceDumpHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, model_dir, target_timer, step_interval=100):\n    self._model_dir = model_dir\n    self._target_timer = target_timer\n    self._step_interval = step_interval\n\n  def begin(self):\n    self._global_step_tensor = training_util._get_or_create_global_step_read()  # pylint: disable=protected-access\n    self._ctrl_ph = tf.compat.v1.placeholder(tf.int16,\n                                             shape=(3,),\n                                             name='hvd_dump_ctrl')\n    self._broadcast_op = hvd_lib.broadcast(self._ctrl_ph, 0)\n\n  def after_run(self, run_context, run_values):\n    global_step = run_context.session.run(self._global_step_tensor)\n    if global_step % self._step_interval == 0:\n      utc_hour = datetime.utcnow().hour\n      should_dump, should_stop, timer_enabled = 0, 0, 0\n      if hvd_lib.rank() == 0:\n        timer_enabled = int(utc_hour >= 18 and utc_hour <= 20)\n        logging.info(f\"utc_hour: {utc_hour} time_enabled: {timer_enabled}\")\n        dump_path = os.path.join(self._model_dir, f\"dump_{global_step}\")\n        stop_path = os.path.join(self._model_dir, f\"stop_{global_step}\")\n        should_stop = int(tf.io.gfile.exists(stop_path))\n        logging.info(f\"checked stop {stop_path} {should_stop}\")\n        should_dump = int(tf.io.gfile.exists(dump_path))\n        logging.info(f\"checked dump {dump_path} {should_dump}\")\n\n      try:\n        should_stop, should_dump, timer_enabled = run_context.session.run(\n            self._broadcast_op,\n            feed_dict={\n                self._ctrl_ph: [should_stop, should_dump, timer_enabled]\n            },\n            options=tf.compat.v1.RunOptions(timeout_in_ms=1000 * 10))\n      except (RuntimeError, TypeError, ValueError, tf.errors.OpError) as ex:\n        logging.error('Error occurred in syncing control flags: %s', str(ex))\n\n      if timer_enabled:\n        logging.info(f\"enable timer with utc_hour: {utc_hour}\")\n        self._target_timer.enable()\n      else:\n        logging.info(f\"disable timer with utc_hour: {utc_hour}\")\n        self._target_timer.disable()\n\n      if should_dump or should_stop:\n        logging.info(f\"reset and enable timer for dump at step {global_step}\")\n        self._target_timer.enable()\n        self._target_timer.reset()\n\n      if should_stop:\n        logging.info(f\"request stop at step {global_step}\")\n        run_context.request_stop()\n\n\nclass SyncTrainingSaverControlHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, model_dir, target_timer, step_interval=100):\n    self._model_dir = model_dir\n    self._target_timer = target_timer\n    self._step_interval = step_interval\n\n  def begin(self):\n    self._global_step_tensor = training_util._get_or_create_global_step_read()  # pylint: disable=protected-access\n\n  def after_run(self, run_context, run_values):\n    global_step = run_context.session.run(self._global_step_tensor)\n    if global_step % self._step_interval == 0:\n      check_path = os.path.join(self._model_dir, \"ONLINE\")\n      if tf.io.gfile.exists(check_path):\n        logging.info(f\"{check_path} exists, enable timer\")\n        self._target_timer.enable()\n      else:\n        logging.info(f\"{check_path} not exists, disable timer\")\n        self._target_timer.disable()\n\n\nclass SyncTrainingInfoHook(tf.estimator.SessionRunHook):\n\n  def begin(self):\n    self._last_timestamp = 0\n    self._fetches = {}\n    for table in ops.get_collection(hash_table_ops._HASH_TABLE_GRAPH_KEY):\n      tensor_prefix = hash_table_ops._table_tensor_prefix(table)\n      self._fetches[tensor_prefix] = table.size()\n\n  def before_run(self, run_context):\n    cur_time = int(time.time())\n    if cur_time > self._last_timestamp + 600:\n      self._last_timestamp = cur_time\n      return tf.estimator.SessionRunArgs(self._fetches)\n    else:\n      return None\n\n  def after_run(self, run_context, run_values):\n    if run_values.results:\n      logging.info(\"*** info: {}\".format(run_values.results))\n\n\nclass ReqTimeControlDumpHook(tf.estimator.SessionRunHook):\n\n  def __init__(self, model_dir, target_timer, step_interval=1000):\n    self._model_dir = model_dir\n    self._target_timer = target_timer\n    self._step_interval = step_interval\n\n  def begin(self):\n    if hvd_lib.rank() == 0:\n      req_time_col = tf.compat.v1.get_collection(\"req_time\")\n      assert len(req_time_col) == 1\n      self._req_time = tf.math.reduce_max(req_time_col[0])\n    else:\n      self._req_time = None\n\n    self._global_step_tensor = training_util._get_or_create_global_step_read()  # pylint: disable=protected-access\n\n    self._req_time_ph = tf.compat.v1.placeholder(tf.int64,\n                                                 shape=[2],\n                                                 name=\"hvd_req_time\")\n    self._req_time_bcast_op = hvd_lib.broadcast(self._req_time_ph, 0)\n\n  def before_run(self, run_context):\n    if hvd_lib.rank() == 0:\n      return session_run_hook.SessionRunArgs(\n          fetches={'req_time': self._req_time})\n    else:\n      return None\n\n  def after_run(self, run_context, run_values):\n    global_step = run_context.session.run(self._global_step_tensor)\n    if global_step % self._step_interval == 0:\n      if hvd_lib.rank() == 0:\n        req_time = run_values.results['req_time']\n        file_name = os.path.join(self._model_dir, \"limit_req_time\")\n        if tf.io.gfile.exists(file_name):\n          with tf.io.gfile.GFile(file_name) as f:\n            limit_req_time = int(f.read())\n        else:\n          limit_req_time = -1\n      else:\n        req_time = 0\n        limit_req_time = -1\n      req_time0, limit_req_time0 = run_context.session.run(\n          self._req_time_bcast_op,\n          feed_dict={self._req_time_ph: [req_time, limit_req_time]})\n\n      if req_time0 >= limit_req_time0 and limit_req_time0 > 0:\n        self._target_timer.enable()\n        self._target_timer.reset()\n        run_context.request_stop()\n\n    return super().after_run(run_context, run_values)\n\n\nINPUT_FN_WRAPPER_KEY = \"wrapped\"\n\n\nclass EofAwareTask:\n  \"\"\"A NativeTask like object that helps stop training before the eof was raised.\"\"\"\n  EOF_KEY = \"__EofAwareTask_eof\"\n\n  def __init__(self, task: native_task.NativeTask, use_dataservice: bool = False):\n    self._ori_task = task\n    self.use_dataservice = use_dataservice\n    logging.info(f'init EofAwareTask')\n\n  def create_input_fn(self, mode):\n\n    input_fn = self._ori_task.create_input_fn(mode)\n\n    def new_input_fn_factory(input_fn):\n\n      def new_input_fn():\n        ds = input_fn()\n        if export_context.is_dry_run_or_exporting():\n          return ds\n\n        ds = datasets.CacheOneDataset(ds)\n\n        # There are 2 reasons why we need a map here:\n        # 1. tuple will be treated as features, label in the estimator which are wrong\n        # 2. In sync training, reorder_fids_in_data_pipeline should be able to get\n        # the original data after we wrap the input_fn output.\n        def map_fn(features, eof):\n          if isinstance(features, dict):\n            logging.info(f\"in map_fn: {EofAwareTask.EOF_KEY}\")\n            return {**features, EofAwareTask.EOF_KEY: eof}\n          logging.info('map_fn keys: 1, 2')\n          return {\"1\": features, \"2\": eof}\n\n        return ds.map(map_fn)\n\n      return new_input_fn\n\n    if self.use_dataservice:\n      return new_input_fn_factory(input_fn)\n    else:\n      return input_fn\n\n  def create_model_fn(self):\n\n    model_fn = self._ori_task.create_model_fn()\n\n    def new_model_fn_factory(model_fn):\n      if export_context.is_dry_run_or_exporting():\n        return model_fn\n\n      def new_model_fn(features, mode, config):\n        spec: tf.estimator.EstimatorSpec = model_fn(features, mode, config)\n        dequeued_eof = tf.compat.v1.get_collection(EofAwareTask.EOF_KEY)\n        if dequeued_eof and not export_context.is_dry_run_or_exporting():\n          if isinstance(dequeued_eof, (list, tuple)):\n            dequeued_eof = dequeued_eof[0]\n          assert(isinstance(dequeued_eof, tf.Tensor))\n          training_hooks = spec.training_hooks or ()\n          training_hooks = [self.EofHook(dequeued_eof)] + list(training_hooks)\n          spec = spec._replace(training_hooks=training_hooks)\n          return spec\n        else:\n          return spec\n\n      return new_model_fn\n\n    if self.use_dataservice:\n      return new_model_fn_factory(model_fn)\n    else:\n      return model_fn\n\n  def __getattr__(self, name):\n    return getattr(self._ori_task, name)\n\n  class EofHook(tf.estimator.SessionRunHook):\n\n    def __init__(self, eof_tensor):\n      eof_tensor_for_gather = tf.reshape(tf.cast(eof_tensor, dtype=tf.int32),\n                                         [1],\n                                         name=\"eof_tensor_for_all_gather\")\n      eof_tensors = hvd_lib.allgather(eof_tensor_for_gather)\n      self._agg_eof = tf.math.reduce_sum(eof_tensors)\n\n    def before_run(self, run_context):\n      return tf.estimator.SessionRunArgs(fetches=self._agg_eof)\n\n    def after_run(self, run_context, run_values):\n      if run_values.results:\n        logging.info(f'rank {hvd_lib.rank()} request_stop, results is {run_values.results}, before')\n        run_context.request_stop()\n        logging.info(f'rank {hvd_lib.rank()} request_stop, results is {run_values.results}, after')\n"
  },
  {
    "path": "monolith/native_training/sync_training_hooks_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training import native_task\nfrom monolith.native_training import hvd_lib\nfrom monolith.native_training import sync_training_hooks\n\n\nclass EofAwareTaskTest(tf.test.TestCase):\n\n  def test_basic(self):\n\n    class TestTask(native_task.NativeTask):\n\n      def create_input_fn(self, mode):\n\n        def input_fn():\n          return tf.data.Dataset.from_tensor_slices(\n              tf.constant([1, 2, 3], dtype=tf.int64))\n\n        return input_fn\n\n      def create_model_fn(self):\n\n        def model_fn(features, mode, config):\n          gs = tf.compat.v1.train.get_or_create_global_step()\n          train_op = tf.compat.v1.assign_add(gs, features)\n          return tf.estimator.EstimatorSpec(mode,\n                                            train_op=train_op,\n                                            loss=tf.constant(0.0))\n\n        return model_fn\n\n    hvd_lib.init()\n    p = TestTask.params()\n    p.name = \"test\"\n    t = TestTask(p)\n    t = sync_training_hooks.EofAwareTask(t)\n    est = tf.estimator.Estimator(t.create_model_fn())\n    est.train(t.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n    self.assertEqual(est.get_variable_value(\"global_step\"), 6)\n\n  def test_dict(self):\n\n    class TestTask(native_task.NativeTask):\n\n      def create_input_fn(self, mode):\n\n        def input_fn():\n          ds = tf.data.Dataset.from_tensor_slices(\n              tf.constant([1, 2, 3], dtype=tf.int64))\n          ds = ds.map(lambda x: {\"1\": x})\n          return ds\n\n        return input_fn\n\n      def create_model_fn(self):\n\n        def model_fn(features, mode, config):\n          gs = tf.compat.v1.train.get_or_create_global_step()\n          train_op = tf.compat.v1.assign_add(gs, features[\"1\"])\n          return tf.estimator.EstimatorSpec(mode,\n                                            train_op=train_op,\n                                            loss=tf.constant(0.0))\n\n        return model_fn\n\n    hvd_lib.init()\n    p = TestTask.params()\n    p.name = \"test\"\n    t = TestTask(p)\n    t = sync_training_hooks.EofAwareTask(t)\n    est = tf.estimator.Estimator(t.create_model_fn())\n    est.train(t.create_input_fn(tf.estimator.ModeKeys.TRAIN))\n    self.assertEqual(est.get_variable_value(\"global_step\"), 6)\n\n\nif __name__ == \"__main__\":\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/tensor_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom typing import Callable, DefaultDict, Dict, Iterable, List, Tuple, Optional\nfrom monolith.native_training.static_reshape_op import static_reshape, StaticReshapeNBuilder\n\n\ndef maybe_squeeze_3d_tensor(x: tf.RaggedTensor):\n  \"\"\"Expected to return a raggedtensor which shape is [None/batch_size, None (RaggedRank)]\n  Supports tensor type:\n  [None/batch_size, None],\n  [None/batch_size, 1, None]\n  \"\"\"\n  if not isinstance(x, tf.RaggedTensor):\n    raise ValueError(\"input must be RaggedTensor\")\n  if len(x.shape) == 2:\n    return x\n  elif len(x.shape) == 3:\n    return tf.squeeze(x, axis=1)\n  else:\n    raise ValueError(\"Unknown shape of RaggedTensor. \", x)\n\n\ndef pack_tensors(\n    keyed_tensors: Dict[str, tf.Tensor]) -> Tuple[tf.Tensor, tf.Tensor]:\n  \"\"\"Compact multiple tensors into 1 tensor.\"\"\"\n  builder = StaticReshapeNBuilder()\n  for key in sorted(keyed_tensors):\n    builder.add(keyed_tensors[key], (None,))\n  outputs, sizes = builder.build()\n  return tf.concat(outputs, 0), sizes\n\n\ndef get_keyed_shape(\n    keyed_tensors: Dict[str, tf.Tensor]) -> Dict[str, List[int]]:\n  return {key: val.shape.as_list() for key, val in keyed_tensors.items()}\n\n\ndef unpack_tensors(keyed_shape: Dict[str, List[int]],\n                   packed: Tuple[tf.Tensor, tf.Tensor]) -> Dict[str, tf.Tensor]:\n  \"\"\"The reverse method of _pack_tensors.\"\"\"\n  m = {}\n  tensor, length = packed[0], packed[1]\n  flat_tensors = tf.split(tensor, length, num=len(keyed_shape))\n  builder = StaticReshapeNBuilder()\n  for i, key in enumerate(sorted(keyed_shape)):\n    builder.add(flat_tensors[i], keyed_shape[key])\n  outputs, _ = builder.build()\n  for i, key in enumerate(sorted(keyed_shape)):\n    m[key] = outputs[i]\n  return m\n\n\ndef _get_flat_tensor_and_size(input_tensor):\n  reshaped = tf.reshape(input_tensor, [-1])\n  return reshaped, tf.size(reshaped)\n\n\ndef split_tensors_with_type(\n    keyed_tensors: Dict[str, tf.Tensor]) -> List[Dict[str, tf.Tensor]]:\n  type_dict_dict = {}\n  type_set = set()\n  for key in sorted(keyed_tensors):\n    tensor = keyed_tensors[key]\n    if (str(tensor.dtype) not in type_set):\n      type_set.add(str(tensor.dtype))\n      type_dict_dict[str(tensor.dtype)] = {}\n    type_dict_dict[str(tensor.dtype)][key] = tensor\n\n  convert_list = []\n  for key in sorted(type_dict_dict):\n    convert_list.append(type_dict_dict[key])\n\n  return convert_list\n\n\ndef merge_dicts(\n    tensor_dict_list: List[Dict[str, tf.Tensor]]) -> Dict[str, tf.Tensor]:\n  res_d = {}\n  for d in tensor_dict_list:\n    for key in d.keys():\n      res_d[key] = d[key]\n\n  return res_d\n\n\ndef pack_typed_keyed_tensors(\n    list_keyed_tensors: List[Dict[str, tf.Tensor]]) -> List[tf.Tensor]:\n  builder = StaticReshapeNBuilder()\n\n  def flatten(tensor):\n    return builder.add(tensor, (None,))\n\n  list_keyed_id = tf.nest.map_structure(flatten, list_keyed_tensors)\n  outputs, sizes = builder.build()\n\n  packed_tensors = []\n  packed_size_size_list = []\n\n  for d in list_keyed_id:\n    tensors = [outputs[d[key]] for key in sorted(d.keys())]\n    packed_tensors.append(tf.concat(tensors, 0))\n    packed_size_size_list.append(len(d.keys()))\n\n  packed_size_size = tf.constant(packed_size_size_list, dtype=tf.int64)\n  concat_offset_size = tf.concat([packed_size_size, sizes], 0)\n  packed_tensors.append(concat_offset_size)\n  return packed_tensors\n\n\ndef get_typed_keyed_shape(\n    list_keyed_tensors: List[Dict[str,\n                                  tf.Tensor]]) -> List[Dict[str, List[int]]]:\n  list_keyed_shape = []\n  for d in list_keyed_tensors:\n    list_keyed_shape.append(get_keyed_shape(d))\n  return list_keyed_shape\n\n\ndef unpack_packed_tensors(\n    list_keyed_shape: List[Dict[str, List[int]]],\n    packed_list: List[tf.Tensor]) -> List[Dict[str, tf.Tensor]]:\n  length = len(packed_list)\n  if (length < 2):\n    raise ValueError(\"Wrong packed_list length\")\n  concat_offset_size = packed_list[-1]\n  packed_size_size = tf.slice(concat_offset_size, [0], [length - 1])\n  packed_size = tf.slice(concat_offset_size, [length - 1], [-1])\n  packed_size = tf.split(packed_size,\n                         packed_size_size,\n                         num=len(list_keyed_shape))\n\n  builder = StaticReshapeNBuilder()\n  unpack_list = []\n  for i, d in enumerate(list_keyed_shape):\n    d_size = packed_size[i]\n    d_tensor = tf.split(packed_list[i], d_size, num=len(list_keyed_shape[i]))\n    for j, key in enumerate(sorted(d.keys())):\n      builder.add(d_tensor[j], list_keyed_shape[i][key])\n\n  outputs, _ = builder.build()\n  idx = 0\n  for d in list_keyed_shape:\n    unpack_d = {}\n    for key in sorted(d.keys()):\n      unpack_d[key] = outputs[idx]\n      idx += 1\n    unpack_list.append(unpack_d)\n\n  return unpack_list\n"
  },
  {
    "path": "monolith/native_training/tensor_utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training import tensor_utils\n\n\nclass TensorUtilsTest(tf.test.TestCase):\n\n  def test_maybe_squeeze_3d_tensor(self):\n    x = tf.ragged.constant([[0, 1], [2]])\n    y = tf.RaggedTensor.from_uniform_row_length(x, 1)\n    sx = tensor_utils.maybe_squeeze_3d_tensor(x)\n    sy = tensor_utils.maybe_squeeze_3d_tensor(y)\n    with self.session() as sess:\n      sx_value, sy_value = sess.run([sx, sy])\n      for squeezed in (sx_value, sy_value):\n        self.assertAllEqual(squeezed, [[0, 1], [2]])\n\n  def test_pack_tensors(self):\n    x = tf.constant([1, 2], dtype=tf.int64)\n    y = tf.constant([[4, 5], [6, 7]], dtype=tf.int64)\n    d = {\"x\": x, \"y\": y}\n    packed_d = tensor_utils.pack_tensors(d)\n    unpacked_d = tensor_utils.unpack_tensors(tensor_utils.get_keyed_shape(d),\n                                             packed_d)\n    with self.session() as sess:\n      packed_d_value = sess.run(packed_d)\n      self.assertAllEqual(packed_d_value[0], [1, 2, 4, 5, 6, 7])\n      self.assertAllEqual(packed_d_value[1], [2, 4])\n      unpacked_d_value = sess.run(unpacked_d)\n      original_d = sess.run(d)\n      for key in sorted(unpacked_d_value):\n        self.assertAllEqual(unpacked_d_value[key], original_d[key])\n\n  def test_pack_typed_keyed_tensors(self):\n    t1 = tf.constant([[0, 0, 1, 0], [0, 2, 0, 3]], dtype=tf.int64)\n    t2 = tf.constant([0, 3, 1, 2], dtype=tf.int64)\n    t3 = tf.constant([9.1, 2.2], dtype=tf.float32)\n    t4 = tf.constant([[1.1, 2.2], [3.3, 4.4]], dtype=tf.float32)\n    t5 = tf.constant([3, 4, 5, 6, 7, 8, 9], dtype=tf.float64)\n    d1 = {\"t1\": t1, \"t2\": t2}\n    d2 = {\"t4\": t4, \"t3\": t3}\n    d3 = {\"t5\": t5}\n    l = [d1, d2, d3]\n    packed_l = tensor_utils.pack_typed_keyed_tensors(l)\n    unpacked_l = tensor_utils.unpack_packed_tensors(\n        tensor_utils.get_typed_keyed_shape(l), packed_l)\n\n    packed_d1 = tensor_utils.pack_tensors(d1)\n    packed_d2 = tensor_utils.pack_tensors(d2)\n    packed_d3 = tensor_utils.pack_tensors(d3)\n    with self.session() as sess:\n      packed_l_value = sess.run(packed_l)\n      packed_d1_value = sess.run(packed_d1)\n      packed_d2_value = sess.run(packed_d2)\n      packed_d3_value = sess.run(packed_d3)\n      self.assertAllEqual(packed_l_value[0], packed_d1_value[0])\n      self.assertAllEqual(packed_l_value[1], packed_d2_value[0])\n      self.assertAllEqual(packed_l_value[2], packed_d3_value[0])\n      self.assertAllEqual(packed_l_value[3], [2, 2, 1, 8, 4, 2, 4, 7])\n\n      unpacked_l_value = sess.run(unpacked_l)\n      l_value = sess.run(l)\n      for i, d in enumerate(unpacked_l_value):\n        for key in sorted(d):\n          self.assertAllEqual(d[key], l_value[i][key])\n\n  def test_pack_typed_keyed_tensors_with_placeholder(self):\n    t1 = tf.compat.v1.placeholder(tf.int32,\n                                  shape=(\n                                      None,\n                                      3,\n                                      4,\n                                  ),\n                                  name=\"t1_placeholder\")\n    t2 = tf.compat.v1.placeholder(tf.int32,\n                                  shape=(None, 2),\n                                  name=\"t2_place_holder\")\n    t3 = tf.compat.v1.placeholder(tf.float32,\n                                  shape=(None,),\n                                  name=\"t3_placeholder\")\n    t4 = tf.compat.v1.placeholder(tf.float32,\n                                  shape=(None, 2, 2),\n                                  name=\"t4_placeholder\")\n    d1 = {\"t1\": t1, \"t2\": t2}\n    d2 = {\"t3\": t3, \"t4\": t4}\n    l = [d1, d2]\n\n    t5 = tf.constant([\n        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,\n        21, 22, 23, 24\n    ],\n                     dtype=tf.int32)\n    t5 = tf.reshape(t5, [-1, 3, 4])\n    t6 = tf.constant([9, 8, 7, 6])\n    t6 = tf.reshape(t6, [-1, 2])\n\n    t7 = tf.constant([9, 12, 15], dtype=tf.float32)\n    t8 = tf.constant([1, 2, 3, 4, 5, 6, 7, 8], dtype=tf.float32)\n    t8 = tf.reshape(t8, [-1, 2, 2])\n\n    packed_l = tensor_utils.pack_typed_keyed_tensors(l)\n    unpacked_l = tensor_utils.unpack_packed_tensors(\n        tensor_utils.get_typed_keyed_shape(l), packed_l)\n\n    with self.session() as sess:\n      t5_value = sess.run(t5)\n      t6_value = sess.run(t6)\n      t7_value = sess.run(t7)\n      t8_value = sess.run(t8)\n\n      unpacked_l_value = sess.run(unpacked_l,\n                                  feed_dict={\n                                      t1: t5_value,\n                                      t2: t6_value,\n                                      t3: t7_value,\n                                      t4: t8_value\n                                  })\n\n      original_l_value = sess.run(l,\n                                  feed_dict={\n                                      t1: t5_value,\n                                      t2: t6_value,\n                                      t3: t7_value,\n                                      t4: t8_value\n                                  })\n\n      for d1, d2 in zip(unpacked_l_value, original_l_value):\n        for key in sorted(d1):\n          self.assertAllEqual(d1[key], d2[key])\n\n  def test_split_tensors_with_type_and_merge_dicts(self):\n    t1 = tf.constant([[0, 0, 1, 0], [0, 2, 0, 3]], dtype=tf.int64)\n    t2 = tf.constant([0, 3, 1, 2], dtype=tf.int64)\n    t3 = tf.constant([9.1, 2.2], dtype=tf.float32)\n    t4 = tf.constant([[1.1, 2.2], [3.3, 4.4]], dtype=tf.float32)\n    t5 = tf.constant([3, 4, 5, 6, 7, 8, 9], dtype=tf.float64)\n    d1 = {\"t1\": t1, \"t2\": t2}\n    d2 = {\"t4\": t4, \"t3\": t3}\n    d3 = {\"t5\": t5}\n    l = [d2, d3, d1]\n    total_d = {\"t1\": t1, \"t2\": t2, \"t4\": t4, \"t3\": t3, \"t5\": t5}\n\n    split_total_d_l = tensor_utils.split_tensors_with_type(total_d)\n    total_d_assemble = tensor_utils.merge_dicts(split_total_d_l)\n\n    with self.session() as sess:\n      split_total_d_l_value = sess.run(split_total_d_l)\n      l_value = sess.run(l)\n      for i, d in enumerate(split_total_d_l_value):\n        for key in sorted(d):\n          self.assertAllEqual(d[key], l_value[i][key])\n\n      total_d_assemble_value = sess.run(total_d_assemble)\n      total_d_value = sess.run(total_d)\n      for key in sorted(total_d_assemble_value):\n        self.assertAllEqual(total_d_value[key], total_d_assemble_value[key])\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/test_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training import entry\nfrom monolith.native_training import utils\nfrom monolith.native_training.runtime.hash_table import \\\n    embedding_hash_table_pb2\n\n\ndef generate_test_hash_table_config(dim: int = 2,\n                                    use_float16: float = False,\n                                    learning_rate: float = 1.0):\n  \"\"\"Creates a valid hash table config.\"\"\"\n  table_config = embedding_hash_table_pb2.EmbeddingHashTableConfig()\n  table_config.cuckoo.SetInParent()\n  segment = table_config.entry_config.segments.add()\n  segment.dim_size = dim\n  segment.opt_config.sgd.SetInParent()\n  segment.opt_config.stochastic_rounding_float16 = use_float16\n  segment.init_config.zeros.SetInParent()\n  segment.comp_config.fp32.SetInParent()\n  return entry.HashTableConfigInstance(table_config, [learning_rate])\n\n\ndef create_test_ps_cluster(num_ps: int):\n  \"\"\"Generates a config based on servers\"\"\"\n  servers = []\n  for i in range(num_ps):\n    servers.append(tf.distribute.Server.create_local_server())\n  cluster_def = tf.train.ClusterDef()\n  job = cluster_def.job.add()\n  job.name = utils.PS_JOB_NAME\n  for i, server in enumerate(servers):\n    job.tasks[i] = server.target[len('grpc://'):]\n  return servers, tf.compat.v1.ConfigProto(cluster_def=cluster_def)\n\n\ndef profile_it(fn):\n  \"\"\"Decorator for testcase to profile locally.\"\"\"\n\n  def wrapped_fn(*args, **kwargs):\n    options = tf.profiler.experimental.ProfilerOptions(host_tracer_level=2,\n                                                       python_tracer_level=1,\n                                                       device_tracer_level=1)\n    tf.profiler.experimental.start(\"/tmp/tests_profile\", options)\n    res = fn(*args, **kwargs)\n    tf.profiler.experimental.stop()\n    time.sleep(\n        1)  # ensure distinct profile dir names defined by timestamp on sec\n    return res\n\n  return wrapped_fn\n"
  },
  {
    "path": "monolith/native_training/touched_key_set_ops.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom monolith.native_training.runtime.ops import gen_monolith_ops\n\n# 64 MB\nTOUCHED_KEY_SET_CAPACITY = 64 * 1024 * 1024 // (8 * 4)\nTOUCHED_KEY_SET_CONCURRENCY_LEVEL = 1024\n\ntouched_key_set_ops = gen_monolith_ops\n\n\ndef create_touched_key_set(capacity: int,\n                           concurrency_level: int,\n                           name_suffix: str = \"\") -> tf.Tensor:\n  \"\"\"Creates a touched key set\"\"\"\n  return touched_key_set_ops.MonolithTouchedKeySet(\n      capacity=capacity,\n      concurrency_level=concurrency_level,\n      shared_name=\"MonolithTouchedKeySet\" + name_suffix)\n\n\nclass TouchedKeySet(object):\n\n  def __init__(self,\n               capacity: int = TOUCHED_KEY_SET_CAPACITY,\n               concurrency_level: int = TOUCHED_KEY_SET_CONCURRENCY_LEVEL,\n               name_suffix: str = \"\"):\n    self._set = create_touched_key_set(capacity, concurrency_level)\n    self._capacity = capacity\n    self._concurrency_level = concurrency_level\n\n  def insert(self, ids: tf.Tensor) -> int:\n    return touched_key_set_ops.monolith_touched_key_set_insert(self._set, ids)\n\n  def steal(self) -> int:\n    return touched_key_set_ops.monolith_touched_key_set_steal(self._set)\n\n  @property\n  def capacity(self) -> int:\n    return self._capacity\n\n  @property\n  def concurrency_level(self) -> int:\n    return self._concurrency_level\n\n  @property\n  def handle(self) -> tf.Tensor:\n    return self._set\n"
  },
  {
    "path": "monolith/native_training/touched_key_set_ops_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\nfrom monolith.native_training.touched_key_set_ops import TouchedKeySet\n\n\nclass TouchedKeySetOpsTest(tf.test.TestCase):\n\n  def test_touched_key_set_basic(self):\n    touched_key_set = TouchedKeySet(1000, 1)\n    ids = tf.constant([x for x in range(1000)], dtype=tf.int64)\n    total_dropped_num = touched_key_set.insert(ids)\n    with tf.control_dependencies([total_dropped_num]):\n      output_ids = touched_key_set.steal()\n\n    with self.session() as sess:\n      ids, total_dropped_num, output_ids = sess.run(\n          [ids, total_dropped_num, output_ids])\n      self.assertEqual(0, total_dropped_num)\n      self.assertAllEqual(ids, sorted(output_ids))\n\n  def test_touched_key_set_overflow(self):\n    touched_key_set = TouchedKeySet(1000, 1)\n    ids = tf.constant([x for x in range(1005)], dtype=tf.int64)\n    total_dropped_num = touched_key_set.insert(ids)\n\n    with tf.control_dependencies([total_dropped_num]):\n      output_ids = touched_key_set.steal()\n\n    with self.session() as sess:\n      ids, total_dropped_num, output_ids = sess.run(\n          [ids, total_dropped_num, output_ids])\n      self.assertEqual(1001, total_dropped_num)\n      self.assertAllEqual([1001, 1002, 1003, 1004], sorted(output_ids))\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom collections import defaultdict\nfrom copy import deepcopy\nfrom typing import Any, Dict, Iterable, List, Set, Tuple\nimport os\nimport platform\nimport re\nimport socket\nimport types\nimport threading\nimport six\nfrom inspect import signature, Parameter\nfrom numpy.lib.arraysetops import isin\n\nfrom absl import logging\n\nimport tensorflow as tf\nfrom tensorflow.python.framework import ops\nfrom tensorflow.python.ops import variables\n\nfrom monolith.core.base_layer import get_uname\nfrom monolith.core.hyperparams import allowed_kwargs, InstantiableParams, Params\n\nPS_JOB_NAME = \"ps\"\n\n\ndef ps_device(index: int) -> str:\n  return \"/job:{}/task:{}/device:CPU:0\".format(PS_JOB_NAME, index)\n\n\ndef propagate_back_gradients(\n    grads_and_vars: Iterable[Tuple[tf.Tensor, tf.Tensor]],\n    xs: Iterable[tf.Tensor],\n    valid_var_set: Set[tf.Tensor] = None) -> List[tf.Tensor]:\n  \"\"\"\n  Propagate the gradients from vars back to the xs and return a list of gradients (dxs).\n  Args:\n    xs: tensors we want to get the gradient for. \n    valid_var_set: if non empty, we will verify if var in grad_and_vars is in this set.\n  \"\"\"\n  combined_vars = []\n  combined_grads = []\n  for grad, var in grads_and_vars:\n    if valid_var_set and (not var in valid_var_set):\n      raise RuntimeError(\"Invalid variables in the input\", var, valid_var_set)\n    combined_vars.append(var)\n    combined_grads.append(grad)\n  return tf.gradients(combined_vars, list(xs), combined_grads)\n\n\ndef propagate_back_dict_gradients(\n    grads_and_vars: Iterable[Tuple[tf.Tensor, tf.Tensor]],\n    x_to_key: Dict[tf.Tensor, Any],\n    valid_var_set: Set[tf.Tensor] = None\n) -> Dict[Any, List[Tuple[tf.Tensor, tf.Tensor]]]:\n  \"\"\"\n  Similar to above. But xs is replaced by x_to_key, and the returned gradients will \n  be grouped by key.\n  \"\"\"\n  dxs = propagate_back_gradients(grads_and_vars, x_to_key.keys(), valid_var_set)\n  grouped = defaultdict(list)\n  for dx, (x, key) in zip(dxs, x_to_key.items()):\n    grouped[key].append((dx, x))\n  return grouped\n\n\ndef get_ndim(x: tf.Tensor):\n  dims = x.get_shape()._dims\n  if dims is not None:\n    return len(dims)\n  return None\n\n\ndef int_shape(x):\n  try:\n    shapes = []\n    for dim in x.get_shape().as_list():\n      if dim is None:\n        shapes.append(-1)\n      elif isinstance(dim, int):\n        shapes.append(dim)\n      elif isinstance(dim, tf.compat.v1.Dimension):\n        shapes.append(dim.value)\n      else:\n        raise ValueError(f'dim {dim} is error')\n    return tuple(shapes)\n  except ValueError:\n    return None\n\n\ndef extend_as_list(x, n):\n  \"\"\"This is a helper function to extend x as list, it will do:\n    1. If x is a list, padding it to specified length n with None, if the length\n    is less than n;\n    2. If x is not a list, create a list with n elements x, please note that,\n    these n elements are the same object, not a copy of x.\n    \"\"\"\n  if isinstance(x, (list, tuple)):\n    if len(x) < n:\n      return x + [None] * (n - len(x))\n    else:\n      return x\n  else:\n    try:\n      return [x if i == 0 else deepcopy(x) for i in range(n)]\n    except:\n      return [x] * n\n\n\ndef check_list(candidate, length_checker, could_be_none=False):\n  \"\"\"Checks whether a list has valid length\n    Args:\n        length_checker: a callable object takes a single integer\n            return T/F on whether the candidate in the range or not\n        could_be_none: None type is acceptable\n\n    Returns: candidate\n\n    Raises:\n        TypeError\n        ValueError\n    \"\"\"\n  if not could_be_none and candidate is None:\n    raise TypeError('ListChecker cannot accept None candidate')\n  if type(candidate) not in [type(None), list]:\n    raise TypeError('ListChecker got candidate '\n                    'in the wrong type[{}]'.format(type(candidate)))\n  if candidate is not None and not length_checker(len(candidate)):\n    raise ValueError('ListChecker got candidate beyonds the range')\n  return candidate\n\n\ndef to_snake_case(name):\n  intermediate = re.sub('(.)([A-Z][a-z0-9]+)', r'\\1_\\2', name)\n  insecure = re.sub('([a-z])([A-Z])', r'\\1_\\2', intermediate).lower()\n  # If the class is private the name starts with \"_\" which is not secure\n  # for creating scopes. We prefix the name with \"private\" in this case.\n  if insecure[0] != '_':\n    return insecure\n  return 'private' + insecure\n\n\ndef to_list(x):\n  \"\"\"Normalizes a list/tensor into a list.\n\n    If a tensor is passed, we return\n    a list of size 1 containing the tensor.\n\n    # Arguments\n        x: target object to be normalized.\n\n    # Returns\n        A list.\n    \"\"\"\n  if isinstance(x, list):\n    return x\n  return [x]\n\n\ndef _get_parameters(cls, parameters):\n  for p in signature(cls.__init__).parameters.values():\n    if p.name in {'self', 'cls'} or \\\n        p.kind in {Parameter.VAR_KEYWORD, Parameter.VAR_POSITIONAL}:\n      continue\n    else:\n      parameters[p.name] = p\n\n\ndef _get_all_parameters(cls, parameters):\n  if cls is not object:\n    for base in cls.__bases__:\n      _get_all_parameters(base, parameters)\n  _get_parameters(cls, parameters)\n\n\ndef _inverted_index(ips: InstantiableParams, idx_dict):\n  for name, item in ips.iter_params():\n    if isinstance(item, (InstantiableParams, Params)):\n      _inverted_index(item, idx_dict)\n    else:\n      idx_dict[name] = ips\n\n\ndef params(cls):\n  \"\"\"Returns the layer params.\"\"\"\n\n  ips = None\n  for base in cls.__mro__:\n    if base is cls:\n      continue\n\n    if hasattr(base, 'params'):\n      ips = base.params()\n      ips.cls = cls\n      break\n  ips = ips or InstantiableParams(cls)\n\n  parameters = {}\n  _get_all_parameters(cls, parameters)\n\n  reversed_dict = {}\n  _inverted_index(ips, reversed_dict)\n\n  try:\n    ips.define('name', get_uname(cls.__name__), \"name\")\n  except:\n    pass\n\n  for p in parameters.values():\n    if p.name in {'cls', 'self'}:\n      continue\n\n    if p.name in reversed_dict:\n      _ips = reversed_dict[p.name]\n      if p.default != Parameter.empty:\n        _ips[p.name] = p.default\n    else:\n      try:\n        ips.define(p.name, None if p.default == Parameter.empty else p.default,\n                   p.name)\n      except:\n        if p.default != Parameter.empty and p.default != None:\n          ips[p.name] = p.default\n\n  for kw in allowed_kwargs:\n    try:\n      ips.define(kw, None, kw)\n    except:\n      pass\n\n  return ips\n\n\ndef check_ops_dependence(op_names_1, op_names_2):\n  \"\"\"Check whether op_names_1 depend on op_names_2.\n\n  Raises:\n    Exception: If op_names_1 depend on op_names_2.\n\n  \"\"\"\n  op_names_1 = to_list(op_names_1)\n  graph_def = tf.compat.v1.get_default_graph().as_graph_def()\n  sub_graph_1 = tf.compat.v1.graph_util.extract_sub_graph(graph_def, op_names_1)\n\n  op_names_2 = set(to_list(op_names_2))\n\n  depended_op_names = [\n      node.name for node in sub_graph_1.node if node.name in op_names_2\n  ]\n  if depended_op_names:\n    raise Exception(\n        \"Checking ops dependence, the ops [%s] depend on ops [%s], which may cause ops [%s] to be run twice.\"\n        % (\",\".join(op_names_1), \",\".join(depended_op_names),\n           \",\".join(depended_op_names)))\n\n\ndef with_params(cls):\n  cls.params = types.MethodType(params, cls)\n  return cls\n\n\ndef get_local_host():\n  if platform.system() in (\"Windows\", \"Linux\"):\n    local_host = socket.gethostbyname(socket.gethostname())\n  else:\n    local_host = socket.gethostbyname(socket.gethostname() + \".local\")\n\n  return local_host\n\n\ndef get_test_tmp_dir():\n  return os.environ.get(\"TEST_TMPDIR\", \"/tmp\")\n\n\ndef get_debugging_info_file_name(model_dir: str):\n  return os.path.join(model_dir, \"debugging_info.pb\")\n\n\ndef get_meta_graph_file_name(model_dir: str):\n  return os.path.join(model_dir, \"meta_graph_for_debugging.pb\")\n\n\ndef add_to_collections(names, value):\n  if isinstance(value, (bool, int, float, str)):\n    tf.compat.v1.add_to_collections(names, value)\n  elif value:\n    tf.compat.v1.add_to_collections(names, value)\n  else:\n    logging.info(f'value is {value}, skip')\n\n\ndef get_collection(name):\n  collection = tf.compat.v1.get_collection(name)\n  if isinstance(collection, (bool, int, float, str)):\n    return collection\n  elif collection:\n    return collection\n  else:\n    return None\n\n\ndef set_metric_prefix(prefix: str):\n  os.environ[\"MONOLITH_METRIC_PREFIX\"] = prefix\n\n\ndef get_metric_prefix():\n  return os.environ.get(\"MONOLITH_METRIC_PREFIX\", \"monolith.training\")\n"
  },
  {
    "path": "monolith/native_training/utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training import utils\n\n\nclass UtilsTest(tf.test.TestCase):\n\n  def test_propagate_back_dict_gradients(self):\n    x = tf.Variable(8.0)\n    y = 2 * x\n    # Use a grad related to x\n    grad_y = 3 * y\n    valid_vars = set([y])\n    grouped = utils.propagate_back_dict_gradients(zip([grad_y], [y]),\n                                                  {x: \"group1\"}, valid_vars)\n    with self.session() as sess:\n      sess.run(tf.compat.v1.global_variables_initializer())\n      dx_and_x = sess.run(grouped[\"group1\"])\n      self.assertAllEqual(dx_and_x, [(96, 8)])\n\n  def test_check_ops_dependence(self):\n    v = tf.Variable(0)\n    add = v.assign_add(1)\n    with tf.control_dependencies([add]):\n      t1 = tf.constant(0)\n      t2 = tf.constant(0)\n    with self.assertRaises(Exception):\n      utils.check_ops_dependence(t1.op.name, add.name)\n    # OK to check\n    utils.check_ops_dependence(t1.op.name, t2.op.name)\n\n  def test_collections(self):\n    utils.add_to_collections('int', 1)\n    utils.add_to_collections('int', 2)\n    utils.add_to_collections('str', 'str')\n    utils.add_to_collections('str', None)\n    utils.add_to_collections('bool', True)\n    utils.add_to_collections('int_list', [1, 2, 3])\n    utils.add_to_collections('str_list', None)\n    utils.add_to_collections('bool_list', [])\n    utils.add_to_collections('int_list', [4, 5, 6])\n    utils.add_to_collections('str_list', ['hello', 'world'])\n    utils.add_to_collections('bool_list', [False])\n\n    self.assertTrue(utils.get_collection('int')[-1] == 2)\n    self.assertTrue(utils.get_collection('str')[-1] == 'str')\n    self.assertTrue(utils.get_collection('bool')[-1])\n    self.assertListEqual(utils.get_collection('int_list')[-1], [4, 5, 6])\n    self.assertListEqual(\n        utils.get_collection('str_list')[-1], ['hello', 'world'])\n    self.assertListEqual(utils.get_collection('bool_list')[-1], [False])\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/variables.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport collections\nimport dataclasses\nfrom typing import Dict, List\n\nimport tensorflow as tf\n\nfrom tensorflow.python.types import core\nfrom tensorflow.python.ops import variables as variables_lib\nfrom tensorflow.python.ops import resource_variable_ops\n\nfrom monolith.native_training import graph_meta\n\n_CACHED_VARIABLES = \"monolith_cached_variables\"\n\n\n@dataclasses.dataclass\nclass CachedVariableAssociates:\n  async_fetched_var: tf.Variable\n  async_cached_var: tf.Variable\n\n\n@dataclasses.dataclass\nclass CachedVariableMeta:\n\n  var_id_to_assoc: Dict[int, CachedVariableAssociates] = dataclasses.field(\n      default_factory=dict)\n\n\ndef _get_meta() -> CachedVariableMeta:\n  return graph_meta.get_meta(\"cached_variables_meta\", CachedVariableMeta)\n\n\n@tf.custom_gradient\ndef cached_value(var, async_cached_var):\n\n  def grad(dy):\n    return dy, None\n\n  return async_cached_var, grad\n\n\ndef _get_valid_op_name(name: str):\n  return name.replace(\":\", \"_\").replace(\"/\", \"_\")\n\n\ndef cached_variable_creator(next_creator, **kwargs):\n  var = next_creator(**kwargs)\n  if not isinstance(var, resource_variable_ops.ResourceVariable):\n    raise ValueError(\"Only ResourceVariable is supported. \"\n                     \"Do you disable V2 behavior or use strategy?\")\n\n  if not var._cached_value is None:\n    raise ValueError(\"The variable has already been cached. \"\n                     \"Consider about removing cache_device.\")\n\n  with tf.device(None):\n    async_cached_var = resource_variable_ops.ResourceVariable(\n        initial_value=var.initial_value,\n        trainable=False,\n        collections=[tf.compat.v1.GraphKeys.LOCAL_VARIABLES],\n        shape=var.shape,\n        dtype=var.dtype)\n    async_fetched_var = resource_variable_ops.ResourceVariable(\n        initial_value=var.initial_value,\n        trainable=False,\n        collections=[tf.compat.v1.GraphKeys.LOCAL_VARIABLES],\n        shape=var.shape,\n        dtype=var.dtype)\n\n  if async_cached_var.device == var.device:\n    # In this case, we shouldn't do the cache since we try assign vars\n    # on the remote machines.\n    #\n    # This is common when cached_var is forced to colocate with var.\n    # For example, var is optimizer's slot variables.\n    return var\n\n  tf.compat.v1.add_to_collection(_CACHED_VARIABLES, var)\n  var._cached_value = cached_value(var, async_cached_var)\n\n  meta = _get_meta()\n  meta.var_id_to_assoc[id(var)] = CachedVariableAssociates(\n      async_fetched_var=async_fetched_var, async_cached_var=async_cached_var)\n  return var\n\n\ndef fetch_all_cached_variables():\n  meta = _get_meta()\n  ops = []\n  for var in tf.compat.v1.get_collection(_CACHED_VARIABLES):\n    fetched_var = meta.var_id_to_assoc[id(var)].async_fetched_var\n    ops.append(\n        fetched_var.assign(var._read_variable_op(),\n                           name=\"fetch_from_{}\".format(\n                               _get_valid_op_name(str(var.device))),\n                           read_value=False))\n  return tf.group(ops)\n\n\ndef assign_all_cached_variables():\n  meta = _get_meta()\n  ops = []\n  for var in tf.compat.v1.get_collection(_CACHED_VARIABLES):\n    associates = meta.var_id_to_assoc[id(var)]\n    ops.append(\n        associates.async_cached_var.assign(associates.async_fetched_var,\n                                           name=\"assign_cached_var\",\n                                           read_value=False))\n  return tf.group(ops, name=\"assign_all_cached_variables\")\n\n\nclass FetchAllCachedVariablesHook(tf.estimator.SessionRunHook):\n  \"\"\"Fetch variables.\"\"\"\n\n  def __init__(self):\n    self._fetch_op = fetch_all_cached_variables()\n    self._assign_op = assign_all_cached_variables()\n    self._first_run = True\n\n  def after_create_session(self, session, coord):\n    self._first_run = True\n\n  def before_run(self, run_context: tf.estimator.SessionRunContext):\n    if self._first_run:\n      # For the first run, we do a sync fetch since the local values might be\n      # super stale.\n      run_context.session.run(self._fetch_op)\n      run_context.session.run(self._assign_op)\n      self._first_run = False\n    return tf.estimator.SessionRunArgs(self._fetch_op)\n\n  def after_run(self, run_context, run_values):\n    run_context.session.run(self._assign_op)\n"
  },
  {
    "path": "monolith/native_training/variables_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport tensorflow as tf\n\nfrom monolith.native_training import variables\nfrom monolith.native_training import test_utils\n\n\nclass CachedVariableTest(tf.test.TestCase):\n\n  def testBasic(self):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.compat.v1.Session(target=servers[0].target, config=config) as sess:\n      with tf.variable_creator_scope(variables.cached_variable_creator):\n        with tf.device(\"/job:ps/task:1\"):\n          var = tf.Variable(5.0)\n      sess.run([\n          tf.compat.v1.global_variables_initializer(),\n          tf.compat.v1.local_variables_initializer()\n      ])\n      # We use var * 1.0 since direct run var will use var.ref()\n      # which is original value of var.\n      self.assertAllEqual(5.0, self.evaluate(var * 1.0))\n\n      update_op = var.assign_add(2.0)\n      sess.run(update_op)\n      # update op won't take effect until fetch happened.\n      self.assertAllEqual(5.0, self.evaluate(var * 1.0))\n      # But the original value should be updated.\n      self.assertAllEqual(7.0, self.evaluate(var))\n\n      sess.run(variables.fetch_all_cached_variables())\n      sess.run(variables.assign_all_cached_variables())\n      # update takes effect.\n      self.assertAllEqual(7.0, self.evaluate(var * 1.0))\n\n  def testHook(self):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.variable_creator_scope(variables.cached_variable_creator):\n      with tf.device(\"/job:ps/task:1\"):\n        var = tf.Variable(5.0)\n    var_cached = var * 1.0\n    sub_op = tf.compat.v1.assign_sub(var, 1.0)\n    with tf.compat.v1.train.SingularMonitoredSession(\n        master=servers[0].target,\n        config=config,\n        hooks=[variables.FetchAllCachedVariablesHook()]) as sess:\n      var_cached_value = sess.run(var_cached)\n      self.assertAllEqual(5.0, var_cached_value)\n      sess.run(sub_op)\n      # At most twice, local var will be finally updated.\n      var_cached_value = sess.run(var_cached)\n      var_cached_value = sess.run(var_cached)\n      self.assertAllEqual(4.0, var_cached_value)\n\n  def testGradient(self):\n    servers, config = test_utils.create_test_ps_cluster(2)\n    with tf.variable_creator_scope(variables.cached_variable_creator):\n      with tf.device(\"/job:ps/task:1\"):\n        var = tf.Variable(5.0)\n    loss = var\n    opt = tf.compat.v1.train.GradientDescentOptimizer(1.0)\n    op = opt.minimize(loss)\n    with tf.compat.v1.Session(target=servers[0].target, config=config) as sess:\n      sess.run([\n          tf.compat.v1.global_variables_initializer(),\n          tf.compat.v1.local_variables_initializer()\n      ])\n      sess.run(op)\n      self.assertAllEqual(4.0, sess.run(var))\n      # The result should not be fetched yet.\n      self.assertAllEqual(5.0, sess.run(var * 1.0))\n\n\nif __name__ == \"__main__\":\n  tf.compat.v1.disable_eager_execution()\n  tf.test.main()\n"
  },
  {
    "path": "monolith/native_training/yarn_runtime.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Functionalities will help to get/set some information from the yarn runtime.\"\"\"\n\nimport collections\nimport os\nimport socket\nimport time\n\nfrom absl import logging\nimport grpc\n\nfrom monolith.native_training import net_utils\nfrom monolith.native_training.proto import primus_am_service_pb2\nfrom monolith.native_training.proto import primus_am_service_pb2_grpc\n\n\ndef get_local_host():\n  if \"CLOUDNATIVE_INET_ADDR\" in os.environ:\n    ips = os.environ[\"CLOUDNATIVE_INET_ADDR\"]\n    local_host = ips.split(\",\")[0]\n  elif \"YARN_INET_ADDR\" in os.environ:\n    local_host = os.environ[\"YARN_INET_ADDR\"]\n  else:\n    local_host = net_utils.get_local_ip()\n  assert local_host\n  return local_host\n\n\ndef _get_primus_am_host():\n  if \"PRIMUS_AM_RPC_HOST\" in os.environ and \"PRIMUS_AM_RPC_PORT\" in os.environ:\n    host = os.environ[\"PRIMUS_AM_RPC_HOST\"]\n    port = os.environ[\"PRIMUS_AM_RPC_PORT\"]\n    return host + \":\" + port\n  return \"\"\n\n\n_CHANNEL_MAP = {}\n\n\ndef _get_channel(addr: str) -> grpc.Channel:\n  if not addr in _CHANNEL_MAP:\n    _CHANNEL_MAP[addr] = grpc.insecure_channel(addr)\n  return _CHANNEL_MAP[addr]\n\n\ndef maybe_kill_application(reason: str) -> bool:\n  \"\"\"Send a request to AM to kill application.\"\"\"\n  if _get_primus_am_host():\n    stub = primus_am_service_pb2_grpc.AppMasterServiceStub(\n        _get_channel(_get_primus_am_host()))\n    req = primus_am_service_pb2.KillRequest()\n    req.exit_code = 1\n    req.diagnose = reason\n    req.graceful_shutdown_timeout_ms.value = 20000\n    try:\n      resp = stub.kill(req, timeout=10)\n      logging.info(\"Successfully killed application.\")\n      return True\n    except grpc.RpcError as e:\n      logging.info(\"Failed to kill application: %s\", e)\n      return False\n  logging.info(\"Current framework doesn't support kill. Ignore killing...\")\n  return False\n\n\ndef maybe_finish_application():\n  if _get_primus_am_host():\n    stub = primus_am_service_pb2_grpc.AppMasterServiceStub(\n        _get_channel(_get_primus_am_host()))\n    req = primus_am_service_pb2.SucceedRequest()\n    req.graceful_shutdown_timeout_ms.value = 20000\n    try:\n      resp = stub.succeed(req, timeout=10)\n      logging.info(\"Successfully mark the application success.\")\n      return True\n    except grpc.RpcError as e:\n      logging.info(\"Failed to finish application: %s\", e)\n\n\ndef create_primus_save_point(dst):\n  if _get_primus_am_host():\n    stub = primus_am_service_pb2_grpc.AppMasterServiceStub(\n        _get_channel(_get_primus_am_host()))\n    create_req = primus_am_service_pb2.CreateSavepointRequest()\n    create_req.savepoint_dir = dst\n    try:\n      create_resp = stub.createSavepoint(create_req, timeout=10)\n      if create_resp.code != 0:\n        logging.error(\"Failed to create primus save point: %s\",\n                      create_resp.message)\n        return False\n      savepoint_id = create_resp.savepoint_id\n\n      status_req = primus_am_service_pb2.CreateSavepointStatusRequest()\n      status_req.savepoint_restore_id = savepoint_id\n      while True:\n        statue_resp = stub.createSavepointStatus(status_req, timeout=10)\n        if statue_resp.create_savepoint_state in [\n            primus_am_service_pb2.CreateSavepointStatusResponse.\n            CreateSavepointState.PENDING, primus_am_service_pb2.\n            CreateSavepointStatusResponse.CreateSavepointState.RUNNING\n        ]:\n          time.sleep(5)\n          continue\n        elif statue_resp.create_savepoint_state == primus_am_service_pb2.CreateSavepointStatusResponse.CreateSavepointState.SUCCEEDED:\n          logging.info(\"Create primus save point succeeded.\")\n          return True\n        else:\n          logging.error(\"Failed to create primus save point: %s\",\n                        statue_resp.message)\n          return False\n    except grpc.RpcError as e:\n      logging.info(\"Failed to create primus save point: %s\", e)\n      return False\n"
  },
  {
    "path": "monolith/native_training/yarn_runtime_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom concurrent import futures\nimport os\nimport types\nimport unittest\nfrom unittest import mock\n\nimport grpc\n\nfrom monolith.native_training import yarn_runtime\nfrom monolith.native_training.proto import primus_am_service_pb2\nfrom monolith.native_training.proto import primus_am_service_pb2_grpc\n\n\nclass YarnRuntimeTest(unittest.TestCase):\n\n  @mock.patch.dict(os.environ, {\"YARN_INET_ADDR\": \"1.2.3.4\"})\n  def test_get_local_host_overwrite(self):\n    self.assertEqual(yarn_runtime.get_local_host(), \"1.2.3.4\")\n\n  @mock.patch.dict(os.environ, {\"CLOUDNATIVE_INET_ADDR\": \"1.2.3.4,5.6.7.8\"})\n  def test_get_local_host_overwrite_by_cloudnative(self):\n    self.assertEqual(yarn_runtime.get_local_host(), \"1.2.3.4\")\n\n  def test_get_local_host_basic(self):\n    yarn_runtime.get_local_host()\n\n  @mock.patch.dict(os.environ, {\n      \"PRIMUS_AM_RPC_HOST\": \"unix\",\n      \"PRIMUS_AM_RPC_PORT\": \"test_kill\"\n  })\n  def test_kill(self):\n    servicer = primus_am_service_pb2_grpc.AppMasterServiceServicer()\n\n    called = False\n    reason = \"TestKill\"\n\n    def kill(servicer_self, request, context):\n      nonlocal called\n      called = True\n      self.assertEqual(request.diagnose, reason)\n      return primus_am_service_pb2.KillResponse()\n\n    servicer.kill = types.MethodType(kill, servicer)\n    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))\n    primus_am_service_pb2_grpc.add_AppMasterServiceServicer_to_server(\n        servicer, server)\n    addr = \"unix:test_kill\"\n    server.add_insecure_port(addr)\n    server.start()\n    yarn_runtime.maybe_kill_application(reason)\n    self.assertTrue(called)\n    server.stop(True)\n\n  @mock.patch.dict(os.environ, {\n      \"PRIMUS_AM_RPC_HOST\": \"unix\",\n      \"PRIMUS_AM_RPC_PORT\": \"test_succeed\"\n  })\n  def test_finish(self):\n    servicer = primus_am_service_pb2_grpc.AppMasterServiceServicer()\n    called = False\n\n    def succeed(self, request, context):\n      nonlocal called\n      called = True\n      return primus_am_service_pb2.SucceedResponse()\n\n    servicer.succeed = types.MethodType(succeed, servicer)\n    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))\n    primus_am_service_pb2_grpc.add_AppMasterServiceServicer_to_server(\n        servicer, server)\n    addr = \"unix:test_succeed\"\n    server.add_insecure_port(addr)\n    server.start()\n    yarn_runtime.maybe_finish_application()\n    self.assertTrue(called)\n    server.stop(True)\n\n  @mock.patch.dict(os.environ, {\n      \"PRIMUS_AM_RPC_HOST\": \"unix\",\n      \"PRIMUS_AM_RPC_PORT\": \"test_save_primus\"\n  })\n  def test_save_primus(self):\n    servicer = primus_am_service_pb2_grpc.AppMasterServiceServicer()\n    create_called = False\n    status_called = False\n    dst = \"test\"\n\n    def createSavepoint(self, request, context):\n      nonlocal create_called\n      create_called = True\n      resp = primus_am_service_pb2.CreateSavepointResponse()\n      resp.savepoint_id = \"123\"\n      return resp\n\n    def createSavepointStatus(self, request, context):\n      nonlocal status_called\n      status_called = True\n      resp = primus_am_service_pb2.CreateSavepointStatusResponse()\n      resp.create_savepoint_state = primus_am_service_pb2.CreateSavepointStatusResponse.CreateSavepointState.SUCCEEDED\n      return resp\n\n    servicer.createSavepoint = types.MethodType(createSavepoint, servicer)\n    servicer.createSavepointStatus = types.MethodType(createSavepointStatus,\n                                                      servicer)\n    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))\n    primus_am_service_pb2_grpc.add_AppMasterServiceServicer_to_server(\n        servicer, server)\n    addr = \"unix:test_save_primus\"\n    server.add_insecure_port(addr)\n    server.start()\n    resp = yarn_runtime.create_primus_save_point(dst)\n    self.assertTrue(resp)\n    self.assertTrue(create_called)\n    self.assertTrue(status_called)\n    server.stop(True)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "monolith/native_training/zk_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nfrom absl import logging\nfrom datetime import datetime, timedelta\nfrom kazoo.client import KazooClient\nfrom monolith.native_training.env_utils import get_zk_auth_data\nimport socket\n\n_PORT = 2181\n\n\n_HOSTS = ['10.226.91.73', '10.226.86.70', '10.224.126.131', '10.224.109.135']\n_HOSTS_IPV6 = [\n    'fdbd:dc02:ff:2:1:226:91:73', 'fdbd:dc02:ff:2:1:226:86:70',\n    'fdbd:dc01:ff:1:1:224:126:131', 'fdbd:dc01:ff:1:1:224:109:135'\n]\n\n\ndef is_ipv6_only():\n  if \"MY_HOST_IP\" in os.environ or \"MY_POD_IP\" in os.environ or \"MY_HOST_IPV6\" in os.environ:\n    # in tce/byterec environment\n    ipv4_addr = os.environ.get(\"MY_HOST_IP\", os.environ.get(\"MY_POD_IP\", None))\n    logging.info(f\"in tce env, ipv4 address is {ipv4_addr}\")\n  else:\n    try:\n      ipv4_addr = socket.gethostbyname(socket.gethostname())\n    except:\n      ipv4_addr = None\n    logging.info(f\"not in tce env, ipv4 address is {ipv4_addr}\")\n  ipv6_only = not ipv4_addr\n  logging.info(f\"is_ipv6_only is {ipv6_only}\")\n  return ipv6_only\n_HOSTS = []\n_HOSTS_IPV6 = []\ndef default_zk_servers(use_ipv6: bool = False):\n  if use_ipv6 or is_ipv6_only():\n    return ','.join(\n        ['[{ip}]:{port}'.format(ip=ip, port=_PORT) for ip in _HOSTS_IPV6])\n  return ','.join(['{ip}:{port}'.format(ip=ip, port=_PORT) for ip in _HOSTS])\n\n\nclass MonolithKazooClient(KazooClient):\n\n  def __init__(self, *args, **kwargs):\n    if \"auth_data\" not in kwargs:\n      kwargs[\"auth_data\"] = get_zk_auth_data()\n    super().__init__(*args, **kwargs)\n\n\ndef clear_zk_path(zk_server: str, job_name: str, force_clear_zk_path: bool):\n  \"\"\"Try to clear old path (no modification since 9 weeks ago), Clear ZK Path of current job.\"\"\"\n\n  zk_client = MonolithKazooClient(zk_server or default_zk_servers())\n  base_path = '/monolith'\n  delta = timedelta(weeks=9)  # two months\n\n  try:\n    zk_client.start()\n    # 1) try to delete very old nodes, just like TTL\n    zk_client.ensure_path(base_path)\n    children = zk_client.get_children(base_path)\n    for child in children:\n      path = '{}/{}'.format(base_path, child)\n      _, stat = zk_client.get_children(path, include_data=True)\n      if datetime.fromtimestamp(stat.mtime // 1000) + delta < datetime.now():\n        try:\n          zk_client.delete(path, recursive=True)\n        except:\n          # in case error in parallel condition\n          pass\n\n    # 2) try to delete job_name\n    job_path = '{}/{}'.format(base_path, job_name)\n    state = zk_client.exists(job_path)\n    if state is not None:\n      if force_clear_zk_path:\n        zk_client.delete(job_path, recursive=True)\n      else:\n        children = zk_client.get_children('/monolith')\n        raise ValueError('there are [{}] in monolith zk path'.format(\n            ','.join(children)))\n  finally:\n    zk_client.stop()\n"
  },
  {
    "path": "monolith/path_utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n\"\"\"Make sure do not include any other third-party library in this file. e.g., tensorflow, absl ...\"\"\"\n\nimport os\n\n\ndef find_main():\n  \"\"\"Find base directory of our codebase, which should be current dir\n  for all binaries in monolith codebase.\"\"\"\n  path = os.path.abspath(__file__)\n\n  splits = ['/__main__/', '/site-packages/', '/monolith/']\n  main_dir = None\n  for split in splits:\n    if split in path:\n      end = path.rfind(split)\n      if split == '/monolith/':\n        main_dir = path[0:end]\n      else:\n        main_dir = os.path.join(path[0:end], split.strip('/'))\n      break\n\n  if main_dir is not None and os.path.exists(os.path.join(main_dir,\n                                                          'monolith')):\n    return main_dir\n  else:\n    raise ValueError(\n        \"Unable to find the monolith base directory. This file directory is {}. Are you running under bazel structure?\"\n        .format(path))\n\n\ndef get_libops_path(lib_name):\n  base = find_main()  # monolith base\n  return os.path.join(base, lib_name)\n"
  },
  {
    "path": "monolith/tf_serving_workspace.bzl",
    "content": "# The file is copied from https://github.com/tensorflow/serving/blob/master/tensorflow_serving/workspace.bzl\n# The modification is that we modify //third_part to @org_tensorflow_serving//third_part\n\n# TensorFlow Serving external dependencies that can be loaded in WORKSPACE\n# files.\n\nload(\"@org_tensorflow//third_party:repo.bzl\", \"tf_http_archive\")\nload(\"@org_tensorflow//tensorflow:workspace.bzl\", \"tf_workspace\")\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\ndef tf_serving_workspace():\n    \"\"\"All TensorFlow Serving external dependencies.\"\"\"\n\n    tf_workspace(path_prefix = \"\", tf_repo_name = \"org_tensorflow\")\n\n    # ===== gRPC dependencies =====\n    native.bind(\n        name = \"libssl\",\n        actual = \"@boringssl//:ssl\",\n    )\n\n    # gRPC wants the existence of a cares dependence but its contents are not\n    # actually important since we have set GRPC_ARES=0 in tools/bazel.rc\n    native.bind(\n        name = \"cares\",\n        actual = \"@grpc//third_party/nanopb:nanopb\",\n    )\n\n    # ===== RapidJSON (rapidjson.org) dependencies =====\n    http_archive(\n        name = \"com_github_tencent_rapidjson\",\n        urls = [\n            \"https://github.com/Tencent/rapidjson/archive/v1.1.0.zip\",\n        ],\n        sha256 = \"8e00c38829d6785a2dfb951bb87c6974fa07dfe488aa5b25deec4b8bc0f6a3ab\",\n        strip_prefix = \"rapidjson-1.1.0\",\n        build_file = \"@org_tensorflow_serving//third_party/rapidjson:BUILD\",\n    )\n\n    # ===== libevent (libevent.org) dependencies =====\n    http_archive(\n        name = \"com_github_libevent_libevent\",\n        urls = [\n            \"https://github.com/libevent/libevent/archive/release-2.1.8-stable.zip\",\n        ],\n        sha256 = \"70158101eab7ed44fd9cc34e7f247b3cae91a8e4490745d9d6eb7edc184e4d96\",\n        strip_prefix = \"libevent-release-2.1.8-stable\",\n        build_file = \"@org_tensorflow_serving//third_party/libevent:BUILD\",\n    )\n\n    # ===== Override TF & TF Text defined 'ICU'. (we need a version that contains all data).\n    http_archive(\n        name = \"icu\",\n        strip_prefix = \"icu-release-64-2\",\n        sha256 = \"dfc62618aa4bd3ca14a3df548cd65fe393155edd213e49c39f3a30ccd618fc27\",\n        urls = [\n            \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/unicode-org/icu/archive/release-64-2.zip\",\n            \"https://github.com/unicode-org/icu/archive/release-64-2.zip\",\n        ],\n        build_file = \"@org_tensorflow_serving//third_party/icu:BUILD\",\n        patches = [\"@org_tensorflow_serving//third_party/icu:data.patch\"],\n        patch_args = [\"-p1\", \"-s\"],\n    )\n\n    # ===== Pin `com_google_absl` with the same version(and patch) with Tensorflow.\n    tf_http_archive(\n        name = \"com_google_absl\",\n        build_file = str(Label(\"@org_tensorflow//third_party:com_google_absl.BUILD\")),\n        # TODO: Remove the patch when https://github.com/abseil/abseil-cpp/issues/326 is resolved\n        # and when TensorFlow is build against CUDA 10.2\n        patch_file = str(Label(\"@org_tensorflow//third_party:com_google_absl_fix_mac_and_nvcc_build.patch\")),\n        sha256 = \"f368a8476f4e2e0eccf8a7318b98dafbe30b2600f4e3cf52636e5eb145aba06a\",  # SHARED_ABSL_SHA\n        strip_prefix = \"abseil-cpp-df3ea785d8c30a9503321a3d35ee7d35808f190d\",\n        urls = [\n            \"https://storage.googleapis.com/mirror.tensorflow.org/github.com/abseil/abseil-cpp/archive/df3ea785d8c30a9503321a3d35ee7d35808f190d.tar.gz\",\n            \"https://github.com/abseil/abseil-cpp/archive/df3ea785d8c30a9503321a3d35ee7d35808f190d.tar.gz\",\n        ],\n    )\n\n    # ===== TF.Text dependencies\n    # NOTE: Before updating this version, you must update the test model\n    # and double check all custom ops have a test:\n    # https://github.com/tensorflow/text/blob/master/oss_scripts/model_server/save_models.py\n    http_archive(\n        name = \"org_tensorflow_text\",\n        sha256 = \"05cc1b0eda8f4f734cb81d4389a637d26372b8621cb4c4a7e30ee5bc1e8c63da\",\n        strip_prefix = \"text-2.3.0\",\n        urls = [\n            \"https://github.com/tensorflow/text/archive/v2.3.0.zip\",\n        ],\n        patches = [\"@org_tensorflow_serving//third_party/tf_text:tftext.patch\"],\n        patch_args = [\"-p1\"],\n        repo_mapping = {\"@com_google_re2\": \"@com_googlesource_code_re2\"},\n    )\n\n    http_archive(\n        name = \"com_google_sentencepiece\",\n        strip_prefix = \"sentencepiece-1.0.0\",\n        sha256 = \"c05901f30a1d0ed64cbcf40eba08e48894e1b0e985777217b7c9036cac631346\",\n        urls = [\n            \"https://github.com/google/sentencepiece/archive/1.0.0.zip\",\n        ],\n    )\n\n    http_archive(\n        name = \"com_google_glog\",\n        sha256 = \"1ee310e5d0a19b9d584a855000434bb724aa744745d5b8ab1855c85bff8a8e21\",\n        strip_prefix = \"glog-028d37889a1e80e8a07da1b8945ac706259e5fd8\",\n        urls = [\n            \"https://mirror.bazel.build/github.com/google/glog/archive/028d37889a1e80e8a07da1b8945ac706259e5fd8.tar.gz\",\n            \"https://github.com/google/glog/archive/028d37889a1e80e8a07da1b8945ac706259e5fd8.tar.gz\",\n        ],\n    )\n\n    # ==== we need to modify eigen_archive to make it compilable in gcc6\n    tf_http_archive(\n        name = \"eigen_archive\",\n        build_file = str(Label(\"@org_tensorflow//third_party:eigen.BUILD\")),\n        patch_file = \"//third_party:eigen3/eigen_gcc6.patch\",\n        sha256 = \"e807a6a6f3a0e8ab10adeb59bb5a9bbb113e8e1684f9b4b32f73f58fd758b4cf\",  # SHARED_EIGEN_SHA\n        strip_prefix = \"eigen-011e0db31d1bed8b7f73662be6d57d9f30fa457a\",\n        urls = [\n            \"https://storage.googleapis.com/mirror.tensorflow.org/gitlab.com/libeigen/eigen/-/archive/011e0db31d1bed8b7f73662be6d57d9f30fa457a/eigen-011e0db31d1bed8b7f73662be6d57d9f30fa457a.tar.gz\",\n            \"https://gitlab.com/libeigen/eigen/-/archive/011e0db31d1bed8b7f73662be6d57d9f30fa457a/eigen-011e0db31d1bed8b7f73662be6d57d9f30fa457a.tar.gz\",\n        ],\n    )\n"
  },
  {
    "path": "monolith/tpu_runner.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\n# Copyright 2018 The TensorFlow Authors. All Rights Reserved.\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# ==============================================================================\n\"\"\"Base task.\"\"\"\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nfrom absl import app\nfrom absl import flags\nfrom absl import logging\nimport os\nimport sys\nimport time\n\nimport tensorflow.compat.v1 as tf\nfrom cloud_tpu_client import client\n\nfrom monolith.base_runner import BaseRunner\nfrom monolith.core import model_registry\nfrom monolith.core.auto_checkpoint_feed_hook import TPUInfeedOutfeedSessionWithEndOfStreamHandlingHook\nfrom monolith.core.base_embedding_task import BaseEmbeddingTask\n\nFLAGS = flags.FLAGS\n\nflags.DEFINE_string(\"tf_version\", default=\"nightly\", help=\"TensorFlow version\")\n\nflags.DEFINE_string(\n    \"tpu\",\n    default=None,\n    help=\"The Cloud TPU to use for training. This should be either the name \"\n    \"used when creating the Cloud TPU, or a grpc://ip.address.of.tpu:8470 \"\n    \"url.\")\n\nflags.DEFINE_string(\n    \"gcp_project\",\n    default=None,\n    help=\"Project name for the Cloud TPU-enabled project. If not specified, \"\n    \"we will attempt to automatically detect the GCE project from metadata.\")\n\nflags.DEFINE_string(\n    \"tpu_zone\",\n    default=None,\n    help=\"GCE zone where the Cloud TPU is located in. If not specified, we \"\n    \"will attempt to automatically detect the zone from metadata.\")\n\nflags.DEFINE_string(\"task\", default=None, help=\"Name of the task class to run.\")\n\nflags.DEFINE_string(\n    \"model_dir\",\n    default=None,\n    help=(\"The directory where the model and summaries are stored.\"))\n\nflags.DEFINE_enum(\"mode\", \"train\", [\"train_and_eval\", \"train\", \"eval\"],\n                  \"Job mode.\")\n\nflags.DEFINE_integer(\n    \"save_checkpoints_steps\",\n    default=None,\n    help=\n    (\"Save checkpoint every save_checkpoints_steps. If None, no checkpoint saved.\"\n    ))\n\nflags.DEFINE_integer(\"iterations_per_loop\",\n                     default=10000,\n                     help=(\"This is the number of train steps running \"\n                           \"in TPU system before returning to CPU host .\"))\n\n# TPU Embedding flags.\nflags.DEFINE_bool(\n    \"pipeline_execution\",\n    default=False,\n    help=(\"If True, speed up training by overlaping embedding lookups with \"\n          \"dense layer computations. Embedding lookups will be one step old.\"))\n\nflags.DEFINE_bool(\"enable_tpu_version_config\",\n                  default=True,\n                  help=(\"Whether enable tpu configuration or not.\"))\n\nflags.DEFINE_integer(\"host_call_every_n_steps\",\n                     default=500,\n                     help=(\"Host call every n steps.\"))\n\n# Whether enable handling end of stream and auto checkpointing. If this is False, then\n# not handle end of stream. If this is True, enable end of stream handling\n# and save a checkpoint before training job end.\nflags.DEFINE_bool(\n    \"enable_stopping_signals\",\n    default=False,\n    help=(\"Whether enable stopping signals and auto checkpointing.\"))\n\n# This is only set to True when use CPU to do some simple test. Note that the internal\n# embedding update logic is not implemented yet. So do not use this mode to do actually\n# training.\nflags.DEFINE_bool(\"cpu_test\",\n                  default=False,\n                  help=(\"Wheter use CPU in TPU estimator.\"))\n\n# Allowed value are \"div\" and \"mod\". \"div\" is the default partition_strategy.\n# Use 'mod' which runs faster than 'div' given our id distribution especially\n# with the incremental generated data. Incremental generated data are more likely\n# to have processing ids distributed in some small ranges of vocab table rather\n# than randomly distributed across vocab whole table. So 'div' will make\n# those some cores more busy with processing those id ranges. 'mod' here will\n# help distribute ids more evenly across more cores.\nflags.DEFINE_string(\"partition_strategy\",\n                    default=\"mod\",\n                    help=(\"Partition strategy of embedding table.\"))\n\n# This will override end_date if provided not empty value.\nflags.DEFINE_string(\"overwrite_end_date\",\n                    default=\"\",\n                    help=(\"End date of input data.\"))\n\n\nclass TPURunner(BaseRunner):\n\n  def __init__(self, task_param, *args, **kwargs):\n    super(TPURunner, self).__init__(*args, **kwargs)\n    # TODO(youlong.cheng): all the parse logic should genearte a hyperparam class.\n    self._tpu = FLAGS.tpu\n    self._tpu_zone = FLAGS.tpu_zone\n    self._gcp_project = FLAGS.gcp_project\n    self._num_replicas_per_host = 8\n    self._model_dir = FLAGS.model_dir\n    self._pipeline_execution = FLAGS.pipeline_execution\n    self.iterations_per_loop = FLAGS.iterations_per_loop\n    self._enable_tpu_version_config = FLAGS.enable_tpu_version_config\n    self._host_call_every_n_steps = FLAGS.host_call_every_n_steps\n    self._enable_stopping_signals = FLAGS.enable_stopping_signals\n    self._cpu_test = FLAGS.cpu_test\n    self._partition_strategy = FLAGS.partition_strategy\n\n    if task_param.train.save_checkpoints_steps is not None:\n      self._save_checkpoints_steps = task_param.train.save_checkpoints_steps\n      logging.info(\n          \"Overwrite save_checkpoints_steps by task_param.train: {}\".format(\n              self._save_checkpoints_steps))\n    else:\n      self._save_checkpoints_steps = FLAGS.save_checkpoints_steps\n      logging.info(\"Use save_checkpoints_steps by FLAGS: {}\".format(\n          self._save_checkpoints_steps))\n    #TODO(hemang.jangle) Allow subclass task_params to override tpu_runner params\n    self._task_param = task_param\n    self._task_param.accelerator = \"tpu\"\n    if FLAGS.overwrite_end_date is not None and FLAGS.overwrite_end_date != \"\" and self._task_param.train.contain(\n        \"end_date\"):\n      self._task_param.train.end_date = FLAGS.overwrite_end_date\n      logging.info(\n          \"Use flag end_date {} to replace parameter train.end_date.\".format(\n              self._task_param.train.end_date))\n    self._mode = FLAGS.mode\n    self._task = None\n\n  def _experimental_gradient_multiplier_fn(self, global_step):\n    return self._task_param.gradient_multiplier\n\n  def _create_params(self, total_replicas):\n    # TODO(youlong.cheng): this is a little bit Adhoc solution, consider\n    # abstract HostCall class with hyper_parameter.\n\n    params = {\n        \"model_dir\": self._model_dir,\n        \"enable_host_call\": self._host_call_every_n_steps > 0,\n        \"num_replicas\": total_replicas,\n        \"accelerator\": self._task_param.accelerator,\n        \"host_call_every_n_steps\": self._host_call_every_n_steps,\n        \"enable_stopping_signals\": self._enable_stopping_signals,\n        \"cpu_test\": self._cpu_test,\n    }\n    logging.info(\"params: {}\".format(params))\n    return params\n\n  def create_tpu_estimator(self, model_fn, feature_config, table_config):\n    \"\"\"Creates the TPU Estimator, with accelerated lookups for embedding tables.\"\"\"\n    if self._enable_tpu_version_config == True:\n      logging.info(\n          \"Enable tpu version config, reset remote tpu version with {}\".format(\n              tf.__version__))\n      # This is to let the cloud TPU always restart in case last round operation is still not finished.\n      tpu_client = client.Client(tpu=self._tpu,\n                                 zone=self._tpu_zone,\n                                 project=self._gcp_project)\n      tpu_client.configure_tpu_version(version=tf.__version__)\n      tpu_client.wait_for_healthy()\n\n    tpu_cluster_resolver = tf.distribute.cluster_resolver.TPUClusterResolver(\n        self._tpu, zone=self._tpu_zone, project=self._gcp_project)\n    num_hosts = tpu_cluster_resolver.cluster_spec().num_tasks(\"worker\")\n    total_replicas = self._num_replicas_per_host * num_hosts\n    train_global_batch_size = total_replicas * self._task_param.train.per_replica_batch_size\n    logging.info(\n        \"num_hosts: {} total_replicas: {} train_global_batch_size: {}\".format(\n            num_hosts, total_replicas, train_global_batch_size))\n\n    # experimental_host_call_every_n_steps can't be 0. If _host_call_every_n_steps is not specified,\n    # then experimental_host_call_every_n_steps will use 100.\n    if self._host_call_every_n_steps == 0:\n      _experimental_host_call_every_n_steps = 100\n    else:\n      _experimental_host_call_every_n_steps = self._host_call_every_n_steps\n\n    if self._enable_stopping_signals is True:\n      experimental_feed_hook = TPUInfeedOutfeedSessionWithEndOfStreamHandlingHook\n    else:\n      experimental_feed_hook = None\n\n    config = tf.compat.v1.estimator.tpu.RunConfig(\n        cluster=tpu_cluster_resolver,\n        model_dir=self._model_dir,\n        save_checkpoints_steps=self._save_checkpoints_steps,\n        tpu_config=tf.compat.v1.estimator.tpu.TPUConfig(\n            iterations_per_loop=self.iterations_per_loop,\n            experimental_host_call_every_n_steps=\n            _experimental_host_call_every_n_steps,\n            per_host_input_for_training=tf.compat.v1.estimator.tpu.\n            InputPipelineConfig.PER_HOST_V2,\n            experimental_allow_per_host_v2_parallel_get_next=True,\n            experimental_feed_hook=experimental_feed_hook,\n        ))\n\n    # Disable meta_optimizer which is not needed and takes long time to run.\n    config.session_config.graph_options.rewrite_options.disable_meta_optimizer = True\n\n    if feature_config and table_config:\n      embedding_config_spec = tf.compat.v1.estimator.tpu.experimental.EmbeddingConfigSpec(\n          feature_to_config_dict=feature_config,\n          table_to_config_dict=table_config,\n          partition_strategy=self._partition_strategy,\n          pipeline_execution_with_tensor_core=self._pipeline_execution,\n          experimental_gradient_multiplier_fn=self.\n          _experimental_gradient_multiplier_fn,\n          optimization_parameters=tf.compat.v1.tpu.experimental.\n          AdagradParameters(learning_rate=1.0))\n    else:\n      embedding_config_spec = None\n\n    params = self._create_params(total_replicas)\n\n    if self._task_param.eval.per_replica_batch_size is not None:\n      eval_batch_size = self._task_param.eval.per_replica_batch_size * total_replicas\n    else:\n      eval_batch_size = train_global_batch_size\n\n    return tf.compat.v1.estimator.tpu.TPUEstimator(\n        use_tpu=True,\n        model_fn=model_fn,\n        config=config,\n        train_batch_size=train_global_batch_size,\n        eval_batch_size=eval_batch_size,\n        params=params,\n        embedding_config_spec=embedding_config_spec), total_replicas\n\n  def create_tpu_estimator_on_cpu(self, model_fn, feature_config, table_config):\n    if self._host_call_every_n_steps == 0:\n      _experimental_host_call_every_n_steps = 100\n    else:\n      _experimental_host_call_every_n_steps = self._host_call_every_n_steps\n\n    config = tf.compat.v1.estimator.tpu.RunConfig(\n        cluster=None,\n        model_dir=None,\n        save_checkpoints_steps=self._save_checkpoints_steps,\n        tpu_config=tf.compat.v1.estimator.tpu.TPUConfig(\n            iterations_per_loop=self.iterations_per_loop,\n            experimental_host_call_every_n_steps=\n            _experimental_host_call_every_n_steps,\n            per_host_input_for_training=tf.compat.v1.estimator.tpu.\n            InputPipelineConfig.PER_HOST_V2,\n            experimental_allow_per_host_v2_parallel_get_next=True))\n\n    if feature_config and table_config:\n      embedding_config_spec = tf.compat.v1.estimator.tpu.experimental.EmbeddingConfigSpec(\n          feature_to_config_dict=feature_config,\n          table_to_config_dict=table_config,\n          pipeline_execution_with_tensor_core=self._pipeline_execution,\n          experimental_gradient_multiplier_fn=self.\n          _experimental_gradient_multiplier_fn,\n          optimization_parameters=tf.compat.v1.tpu.experimental.\n          AdagradParameters(learning_rate=1.0))\n    else:\n      embedding_config_spec = None\n\n    total_replicas = 1\n    params = self._create_params(total_replicas)\n\n    return tf.compat.v1.estimator.tpu.TPUEstimator(\n        use_tpu=False,\n        model_fn=model_fn,\n        config=config,\n        train_batch_size=128,\n        params=params,\n        embedding_config_spec=embedding_config_spec), total_replicas\n\n  def run(self):\n    try:\n      current_step = tf.train.load_variable(self._model_dir,\n                                            tf.compat.v1.GraphKeys.GLOBAL_STEP)\n    except (TypeError, ValueError, tf.errors.NotFoundError):\n      current_step = 0\n    logging.info(\"Current step :{}\".format(current_step))\n\n    task = self._task_param.instantiate()\n    self._task = task\n    feature_config, table_config = None, None\n    if isinstance(task, BaseEmbeddingTask):\n      task.init_slot_to_env()\n      feature_config, table_config = task.create_feature_and_table_config_dict()\n    input_fn_train = task.create_input_fn(tf.estimator.ModeKeys.TRAIN)\n\n    model_fn = task.create_model_fn()\n\n    assert self._cpu_test is False or self._mode == 'train', \\\n      \"Cpu test can only work with train mode.\"\n\n    if self._cpu_test:\n      # If running CPU test, wrap model a little bit to pre-process features.\n      def model_fn_test_wrapper(features, mode, params):\n        features = task.process_features_for_cpu_test(features)\n        return model_fn(features, mode, params)\n\n      est, total_replicas = self.create_tpu_estimator_on_cpu(\n          model_fn_test_wrapper, feature_config, table_config)\n    else:\n      est, total_replicas = self.create_tpu_estimator(model_fn, feature_config,\n                                                      table_config)\n\n    start_timestamp = time.time()  # This time will include compilation time\n\n    if self._mode == 'train':\n      est.train(input_fn=input_fn_train,\n                max_steps=self._task_param.train.max_steps)\n    elif self._mode == 'eval':\n      input_fn_eval = task.create_input_fn(tf.estimator.ModeKeys.EVAL)\n      total_examples = self._task_param.input.eval_examples\n      eval_batch_size = self._task_param.eval.per_replica_batch_size * total_replicas\n\n      eval_steps = total_examples // eval_batch_size\n      logging.info(\n          \"Evaluation: total_examples:{} eval_batch_size:{} num_eval_steps: {}\".\n          format(total_examples, eval_batch_size, eval_steps))\n      output_dir = os.path.join(self._model_dir, 'eval')\n      tf.io.gfile.makedirs(output_dir)\n\n      # Run evaluation when there's a new checkpoint\n      for ckpt in tf.train.checkpoints_iterator(self._model_dir,\n                                                timeout=60 * 60 * 5):\n        # Terminate eval job when final checkpoint is reached\n        current_step = int(os.path.basename(ckpt).split('-')[1])\n        try:\n          current_step = int(os.path.basename(ckpt).split('-')[1])\n          logging.info(\"Starting to evaluate step: {}\".format(current_step))\n        except:\n          logging.warning(\"Could not find current step value\")\n\n        try:\n          start_timestamp = time.time(\n          )  # This time will include compilation time\n          eval_results = est.evaluate(input_fn=input_fn_eval,\n                                      steps=eval_steps,\n                                      checkpoint_path=ckpt)\n          elapsed_time = int(time.time() - start_timestamp)\n          logging.info(\"Eval results: {}. Elapsed seconds: {}\".format(\n              eval_results, elapsed_time))\n\n          # Summary writer writes out eval metrics.\n          summary_writer = tf.compat.v1.summary.FileWriter(output_dir)\n          self.write_summary(eval_results, summary_writer, current_step)\n          summary_writer.close()\n\n          if current_step >= self._task_param.train.max_steps:\n            logging.info(\"Evaluation finished after training step {}\".format(\n                current_step))\n            break\n\n        except tf.errors.NotFoundError:\n          # Since the coordinator is on a different job than the TPU worker,\n          # sometimes the TPU worker does not finish initializing until long after\n          # the CPU job tells it to start evaluating. In this case, the checkpoint\n          # file could have been deleted already.\n          logging.info(\n              \"Checkpoint {} no longer exists, skipping checkpoint\".format(\n                  ckpt))\n    else:  # train_and_eval\n      raise TypeError(\"{} has not been supported.\".format(self._mode))\n\n\ndef main(unused_argv):\n  task_name = FLAGS.task\n  task_param = model_registry.GetParams(task_name)\n\n  logging.info(\"FLAGS:\")\n  for key, value in FLAGS.__flags.items():\n    logging.info(\"{}: {}\".format(key, value.value))\n  logging.info(\"task_param: {}\".format(str(task_param)))\n  runner = TPURunner(task_param)\n  runner.run()\n\n\nif __name__ == '__main__':\n  logging.set_verbosity(logging.INFO)\n  tf.disable_v2_behavior()\n  app.run(main)\n"
  },
  {
    "path": "monolith/utils.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nimport os\nimport sys\nfrom concurrent.futures import ThreadPoolExecutor\n\nimport tensorflow as tf\n\nfrom monolith import path_utils\n\nfind_main = path_utils.find_main\nget_libops_path = path_utils.get_libops_path\n\n\ndef enable_monkey_patch():\n  name = \"tensorflow.python.training.monitored_session\"\n  orig_mod = sys.modules.get(name)\n  if orig_mod is None:\n    orig_mod = __import__(name)\n  setattr(orig_mod, \"_PREEMPTION_ERRORS\", (tf.errors.AbortedError,))\n\n\ndef CopyFile(src, dst, overwrite=True, skip_nonexist=True, max_retries=5):\n  for _ in range(max_retries):\n    try:\n      tf.io.gfile.copy(src, dst, overwrite=overwrite)\n    except tf.errors.NotFoundError as e:\n      if skip_nonexist:\n        continue\n      else:\n        raise e\n    break\n\n\ndef CopyRecursively(src: str,\n                    dst: str,\n                    max_workers: int = 1,\n                    skip_nonexist: bool = True,\n                    max_retries: int = 5):\n  src_dst = []\n\n  def _CopyRecursivelyImpl(src, dst):\n    if not tf.io.gfile.exists(src):\n      if skip_nonexist:\n        return\n      raise ValueError(\"{} doesn't exist!\".format(src))\n    if not tf.io.gfile.isdir(src):\n      if max_workers > 1:\n        src_dst.append((src, dst))\n      else:\n        CopyFile(src, dst, overwrite=True, skip_nonexist=skip_nonexist)\n      return\n    if tf.io.gfile.exists(dst):\n      tf.io.gfile.rmtree(dst)\n    tf.io.gfile.makedirs(dst)\n    for relpath in tf.io.gfile.listdir(src):\n      src_path = os.path.join(src, relpath)\n      dst_path = os.path.join(dst, relpath)\n      _CopyRecursivelyImpl(src_path, dst_path)\n\n  _CopyRecursivelyImpl(src, dst)\n  if max_workers > 1:\n    with ThreadPoolExecutor(max_workers=max_workers) as executor:\n      executor.map(\n          lambda args: CopyFile(args[0],\n                                args[1],\n                                overwrite=True,\n                                skip_nonexist=skip_nonexist,\n                                max_retries=max_retries), src_dst)\n"
  },
  {
    "path": "monolith/utils_test.py",
    "content": "# Copyright 2022 ByteDance and/or its affiliates.\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\nfrom monolith import utils\nfrom typing import Union\nimport os\nimport unittest\nimport uuid\n\nfrom tensorflow.python.framework import errors\nimport tensorflow.python.training.monitored_session as monitored_session\nimport tensorflow as tf\n\nutils.enable_monkey_patch()\n\n\nclass UtilsTest(unittest.TestCase):\n\n  def testFindMain(self):\n    basedir = utils.find_main()\n    self.assertEqual(basedir.split(\"/\")[-1], \"__main__\")\n\n  def testGetLibopsPath(self):\n    self.assertTrue(\n        os.path.exists(utils.get_libops_path(\"monolith/utils_test.py\")))\n\n  def testLoadMonitoredSession(self):\n    self.assertEqual(monitored_session._PREEMPTION_ERRORS,\n                     (errors.AbortedError,))\n\n  def testMultiThreadedCopy(self):\n    test_id = uuid.uuid4().hex\n\n    def _gen_dir():\n      root = os.path.join('/tmp', test_id, 'src')\n      tf.io.gfile.makedirs(root)\n      subdir = os.path.join(root, 'subdir')\n      tf.io.gfile.mkdir(subdir)\n      with tf.io.gfile.GFile(os.path.join(root, 'file.txt'), 'w+') as f:\n        f.write('root')\n      with tf.io.gfile.GFile(os.path.join(subdir, 'innerfile.txt'), 'w+') as f:\n        f.write('inner')\n      return root\n\n    src = _gen_dir()\n    dst = os.path.join('/tmp', test_id, 'dst')\n    utils.CopyRecursively(src, dst, max_workers=2)\n    with tf.io.gfile.GFile(os.path.join(dst, 'subdir', 'innerfile.txt'),\n                           'r') as f:\n      self.assertEqual(f.read(), 'inner')\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "third_party/BUILD",
    "content": "exports_files(glob([\"**\"]))\n"
  },
  {
    "path": "third_party/arrow.BUILD",
    "content": "# Description:\n#   Apache Arrow library\n\npackage(default_visibility = [\"//visibility:public\"])\n\nlicenses([\"notice\"])  # Apache 2.0\n\nexports_files([\"LICENSE.txt\"])\n\ngenrule(\n    name = \"arrow_util_config\",\n    srcs = [\"cpp/src/arrow/util/config.h.cmake\"],\n    outs = [\"cpp/src/arrow/util/config.h\"],\n    cmd = (\"sed \" +\n           \"-e 's/@ARROW_VERSION_MAJOR@/3/g' \" +\n           \"-e 's/@ARROW_VERSION_MINOR@/0/g' \" +\n           \"-e 's/@ARROW_VERSION_PATCH@/0/g' \" +\n           \"-e 's/cmakedefine ARROW_USE_NATIVE_INT128/undef ARROW_USE_NATIVE_INT128/g' \" +\n           \"-e 's/cmakedefine/define/g' \" +\n           \"$< >$@\"),\n)\n\ngenrule(\n    name = \"parquet_version_h\",\n    srcs = [\"cpp/src/parquet/parquet_version.h.in\"],\n    outs = [\"cpp/src/parquet/parquet_version.h\"],\n    cmd = (\"sed \" +\n           \"-e 's/@PARQUET_VERSION_MAJOR@/1/g' \" +\n           \"-e 's/@PARQUET_VERSION_MINOR@/5/g' \" +\n           \"-e 's/@PARQUET_VERSION_PATCH@/1/g' \" +\n           \"$< >$@\"),\n)\n\ncc_library(\n    name = \"arrow\",\n    srcs = glob(\n        [\n            \"cpp/src/arrow/*.cc\",\n            \"cpp/src/arrow/array/*.cc\",\n            \"cpp/src/arrow/csv/*.cc\",\n            \"cpp/src/arrow/io/*.cc\",\n            \"cpp/src/arrow/ipc/*.cc\",\n            \"cpp/src/arrow/json/*.cc\",\n            \"cpp/src/arrow/tensor/*.cc\",\n            \"cpp/src/arrow/util/*.cc\",\n            \"cpp/src/arrow/vendored/optional.hpp\",\n            \"cpp/src/arrow/vendored/string_view.hpp\",\n            \"cpp/src/arrow/vendored/variant.hpp\",\n            \"cpp/src/arrow/**/*.h\",\n            \"cpp/src/parquet/**/*.h\",\n            \"cpp/src/parquet/**/*.cc\",\n            \"cpp/src/generated/*.h\",\n            \"cpp/src/generated/*.cpp\",\n            \"cpp/thirdparty/flatbuffers/include/flatbuffers/*.h\",\n            \"cpp/src/arrow/vendored/uriparser/*.c\",\n            \"cpp/src/arrow/vendored/base64.cpp\",\n            \"cpp/src/arrow/compute/**/*.cc\",\n            \"cpp/src/arrow/vendored/datetime/*.h\",\n            \"cpp/src/arrow/vendored/datetime/*.cpp\",\n            \"cpp/src/arrow/vendored/pcg/*.hpp\",\n        ],\n        exclude = [\n            \"cpp/src/**/*_benchmark.cc\",\n            \"cpp/src/**/*_main.cc\",\n            \"cpp/src/**/*_nossl.cc\",\n            \"cpp/src/**/*_test.cc\",\n            \"cpp/src/**/test_*.cc\",\n            \"cpp/src/**/*hdfs*.cc\",\n            \"cpp/src/**/*fuzz*.cc\",\n            \"cpp/src/**/file_to_stream.cc\",\n            \"cpp/src/**/stream_to_file.cc\",\n            \"cpp/src/arrow/util/bpacking_avx2.cc\",\n            \"cpp/src/arrow/util/bpacking_avx512.cc\",\n            \"cpp/src/arrow/util/bpacking_neon.cc\",\n            \"cpp/src/arrow/util/tracing_internal.cc\",\n            \"cpp/src/arrow/compute/exec/*_avx*.cc\",\n        ],\n    ) + select({\n        \"@bazel_tools//src/conditions:windows\": [\n            \"cpp/src/arrow/vendored/musl/strptime.c\",\n        ],\n        \"//conditions:default\": [],\n    }),\n    hdrs = [\n        # declare header from above genrule\n        \"cpp/src/arrow/util/config.h\",\n        \"cpp/src/parquet/parquet_version.h\",\n    ],\n    copts = select({\n        \"@bazel_tools//src/conditions:windows\": [\n            \"/std:c++14\",\n        ],\n        \"//conditions:default\": [\n            \"-std=c++14\",\n        ],\n    }),\n    defines = [\n        \"ARROW_WITH_BROTLI\",\n        \"ARROW_WITH_SNAPPY\",\n        \"ARROW_WITH_LZ4\",\n        \"ARROW_WITH_ZLIB\",\n        \"ARROW_WITH_ZSTD\",\n        \"ARROW_WITH_BZ2\",\n        \"ARROW_STATIC\",\n        \"ARROW_EXPORT=\",\n        \"PARQUET_STATIC\",\n        \"PARQUET_EXPORT=\",\n        \"WIN32_LEAN_AND_MEAN\",\n    ],\n    includes = [\n        \"cpp/src\",\n        \"cpp/src/arrow/vendored/xxhash\",\n        \"cpp/thirdparty/flatbuffers/include\",\n    ],\n    textual_hdrs = [\n        \"cpp/src/arrow/vendored/xxhash/xxhash.c\",\n    ],\n    deps = [\n        \"@boringssl//:crypto\",\n        \"@brotli\",\n        \"@bzip2\",\n        \"@double_conversion//:double-conversion\",\n        \"@lz4\",\n        \"@rapidjson\",\n        \"@snappy//:snappy\",\n        \"@thrift\",\n        \"@xsimd\",\n        \"@zlib\",\n        \"@zstd\",\n    ],\n)"
  },
  {
    "path": "third_party/brotli.BUILD",
    "content": "# Description:\n#   Brotli library\n\nlicenses([\"notice\"])  # MIT license\n\nexports_files([\"LICENSE\"])\n\ncc_library(\n    name = \"brotli\",\n    srcs = glob([\n        \"c/common/*.c\",\n        \"c/common/*.h\",\n        \"c/dec/*.c\",\n        \"c/dec/*.h\",\n        \"c/enc/*.c\",\n        \"c/enc/*.h\",\n        \"c/include/brotli/*.h\",\n    ]),\n    hdrs = [],\n    defines = [],\n    includes = [\n        \"c/dec\",\n        \"c/include\",\n    ],\n    linkopts = [],\n    visibility = [\"//visibility:public\"],\n)"
  },
  {
    "path": "third_party/bzip2.BUILD",
    "content": "package(default_visibility = [\"//visibility:public\"])\n\nlicenses([\"notice\"])  # BSD-like license\n\ncc_library(\n    name = \"bzip2\",\n    srcs = [\n        \"blocksort.c\",\n        \"bzlib.c\",\n        \"bzlib_private.h\",\n        \"compress.c\",\n        \"crctable.c\",\n        \"decompress.c\",\n        \"huffman.c\",\n        \"randtable.c\",\n    ],\n    hdrs = [\n        \"bzlib.h\",\n    ],\n    copts = [\n    ],\n    includes = [\".\"],\n)"
  },
  {
    "path": "third_party/cli11/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_library(\n    name = \"cli11\",\n    hdrs = [\"CLI11.hpp\"],\n)\n\n"
  },
  {
    "path": "third_party/cli11/CLI11.hpp",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n// CLI11: Version 2.3.2\n// Originally designed by Henry Schreiner\n// https://github.com/CLIUtils/CLI11\n//\n// This is a standalone header file generated by MakeSingleHeader.py in\n// CLI11/scripts from: v2.3.2\n//\n// CLI11 2.3.2 Copyright (c) 2017-2022 University of Cincinnati, developed by\n// Henry Schreiner under NSF AWARD 1414736. All rights reserved.\n//\n// Redistribution and use in source and binary forms of CLI11, with or without\n// modification, are permitted provided that the following conditions are met:\n//\n// 1. Redistributions of source code must retain the above copyright notice,\n// this\n//    list of conditions and the following disclaimer.\n// 2. Redistributions in binary form must reproduce the above copyright notice,\n//    this list of conditions and the following disclaimer in the documentation\n//    and/or other materials provided with the distribution.\n// 3. Neither the name of the copyright holder nor the names of its contributors\n//    may be used to endorse or promote products derived from this software\n//    without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n// POSSIBILITY OF SUCH DAMAGE.\n\n#pragma once\n\n// Standard combined includes:\n#include <algorithm>\n#include <cmath>\n#include <cstdint>\n#include <exception>\n#include <fstream>\n#include <functional>\n#include <iomanip>\n#include <iostream>\n#include <iterator>\n#include <limits>\n#include <locale>\n#include <map>\n#include <memory>\n#include <numeric>\n#include <set>\n#include <sstream>\n#include <stdexcept>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#define CLI11_VERSION_MAJOR 2\n#define CLI11_VERSION_MINOR 3\n#define CLI11_VERSION_PATCH 2\n#define CLI11_VERSION \"2.3.2\"\n\n// The following version macro is very similar to the one in pybind11\n#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)\n#if __cplusplus >= 201402L\n#define CLI11_CPP14\n#if __cplusplus >= 201703L\n#define CLI11_CPP17\n#if __cplusplus > 201703L\n#define CLI11_CPP20\n#endif\n#endif\n#endif\n#elif defined(_MSC_VER) && __cplusplus == 199711L\n// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard\n// is fully implemented) Unless you use the /Zc:__cplusplus flag on Visual\n// Studio 2017 15.7 Preview 3 or newer\n#if _MSVC_LANG >= 201402L\n#define CLI11_CPP14\n#if _MSVC_LANG > 201402L && _MSC_VER >= 1910\n#define CLI11_CPP17\n#if _MSVC_LANG > 201703L && _MSC_VER >= 1910\n#define CLI11_CPP20\n#endif\n#endif\n#endif\n#endif\n\n#if defined(CLI11_CPP14)\n#define CLI11_DEPRECATED(reason) [[deprecated(reason)]]\n#elif defined(_MSC_VER)\n#define CLI11_DEPRECATED(reason) __declspec(deprecated(reason))\n#else\n#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason)))\n#endif\n\n// GCC < 10 doesn't ignore this in unevaluated contexts\n#if !defined(CLI11_CPP17) ||                                                  \\\n    (defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && \\\n     __GNUC__ < 10 && __GNUC__ > 4)\n#define CLI11_NODISCARD\n#else\n#define CLI11_NODISCARD [[nodiscard]]\n#endif\n\n/** detection of rtti */\n#ifndef CLI11_USE_STATIC_RTTI\n#if (defined(_HAS_STATIC_RTTI) && _HAS_STATIC_RTTI)\n#define CLI11_USE_STATIC_RTTI 1\n#elif defined(__cpp_rtti)\n#if (defined(_CPPRTTI) && _CPPRTTI == 0)\n#define CLI11_USE_STATIC_RTTI 1\n#else\n#define CLI11_USE_STATIC_RTTI 0\n#endif\n#elif (defined(__GCC_RTTI) && __GXX_RTTI)\n#define CLI11_USE_STATIC_RTTI 0\n#else\n#define CLI11_USE_STATIC_RTTI 1\n#endif\n#endif\n\n/** Inline macro **/\n#ifdef CLI11_COMPILE\n#define CLI11_INLINE\n#else\n#define CLI11_INLINE inline\n#endif\n\n// C standard library\n// Only needed for existence checking\n#if defined CLI11_CPP17 && defined __has_include && \\\n    !defined CLI11_HAS_FILESYSTEM\n#if __has_include(<filesystem>)\n// Filesystem cannot be used if targeting macOS < 10.15\n#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && \\\n    __MAC_OS_X_VERSION_MIN_REQUIRED < 101500\n#define CLI11_HAS_FILESYSTEM 0\n#elif defined(__wasi__)\n// As of wasi-sdk-14, filesystem is not implemented\n#define CLI11_HAS_FILESYSTEM 0\n#else\n#include <filesystem>\n#if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703\n#if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9\n#define CLI11_HAS_FILESYSTEM 1\n#elif defined(__GLIBCXX__)\n// if we are using gcc and Version <9 default to no filesystem\n#define CLI11_HAS_FILESYSTEM 0\n#else\n#define CLI11_HAS_FILESYSTEM 1\n#endif\n#else\n#define CLI11_HAS_FILESYSTEM 0\n#endif\n#endif\n#endif\n#endif\n\n#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0\n#include <filesystem>  // NOLINT(build/include)\n#else\n#include <sys/stat.h>\n#include <sys/types.h>\n#endif\n\nnamespace CLI {\n\n/// Include the items in this namespace to get free conversion of enums to/from\n/// streams. (This is available inside CLI as well, so CLI11 will use this\n/// without a using statement).\nnamespace enums {\n\n/// output streaming for enumerations\ntemplate <typename T,\n          typename = typename std::enable_if<std::is_enum<T>::value>::type>\nstd::ostream &operator<<(std::ostream &in, const T &item) {\n  // make sure this is out of the detail namespace otherwise it won't be found\n  // when needed\n  return in << static_cast<typename std::underlying_type<T>::type>(item);\n}\n\n}  // namespace enums\n\n/// Export to CLI namespace\nusing enums::operator<<;\n\nnamespace detail {\n/// a constant defining an expected max vector size defined to be a big number\n/// that could be multiplied by 4 and not produce overflow for some expected\n/// uses\nconstexpr int expected_max_vector_size{1 << 29};\n// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c\n/// Split a string by a delim\nCLI11_INLINE std::vector<std::string> split(const std::string &s, char delim);\n\n/// Simple function to join a string\ntemplate <typename T>\nstd::string join(const T &v, std::string delim = \",\") {\n  std::ostringstream s;\n  auto beg = std::begin(v);\n  auto end = std::end(v);\n  if (beg != end) s << *beg++;\n  while (beg != end) {\n    s << delim << *beg++;\n  }\n  return s.str();\n}\n\n/// Simple function to join a string from processed elements\ntemplate <typename T, typename Callable,\n          typename = typename std::enable_if<\n              !std::is_constructible<std::string, Callable>::value>::type>\nstd::string join(const T &v, Callable func, std::string delim = \",\") {\n  std::ostringstream s;\n  auto beg = std::begin(v);\n  auto end = std::end(v);\n  auto loc = s.tellp();\n  while (beg != end) {\n    auto nloc = s.tellp();\n    if (nloc > loc) {\n      s << delim;\n      loc = nloc;\n    }\n    s << func(*beg++);\n  }\n  return s.str();\n}\n\n/// Join a string in reverse order\ntemplate <typename T>\nstd::string rjoin(const T &v, std::string delim = \",\") {\n  std::ostringstream s;\n  for (std::size_t start = 0; start < v.size(); start++) {\n    if (start > 0) s << delim;\n    s << v[v.size() - start - 1];\n  }\n  return s.str();\n}\n\n// Based roughly on\n// http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string\n\n/// Trim whitespace from left of string\nCLI11_INLINE std::string &ltrim(std::string &str);\n\n/// Trim anything from left of string\nCLI11_INLINE std::string &ltrim(std::string &str, const std::string &filter);\n\n/// Trim whitespace from right of string\nCLI11_INLINE std::string &rtrim(std::string &str);\n\n/// Trim anything from right of string\nCLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter);\n\n/// Trim whitespace from string\ninline std::string &trim(std::string &str) { return ltrim(rtrim(str)); }\n\n/// Trim anything from string\ninline std::string &trim(std::string &str, const std::string filter) {\n  return ltrim(rtrim(str, filter), filter);\n}\n\n/// Make a copy of the string and then trim it\ninline std::string trim_copy(const std::string &str) {\n  std::string s = str;\n  return trim(s);\n}\n\n/// remove quotes at the front and back of a string either '\"' or '\\''\nCLI11_INLINE std::string &remove_quotes(std::string &str);\n\n/// Add a leader to the beginning of all new lines (nothing is added\n/// at the start of the first line). `\"; \"` would be for ini files\n///\n/// Can't use Regex, or this would be a subs.\nCLI11_INLINE std::string fix_newlines(const std::string &leader,\n                                      std::string input);\n\n/// Make a copy of the string and then trim it, any filter string can be used\n/// (any char in string is filtered)\ninline std::string trim_copy(const std::string &str,\n                             const std::string &filter) {\n  std::string s = str;\n  return trim(s, filter);\n}\n/// Print a two part \"help\" string\nCLI11_INLINE std::ostream &format_help(std::ostream &out, std::string name,\n                                       const std::string &description,\n                                       std::size_t wid);\n\n/// Print subcommand aliases\nCLI11_INLINE std::ostream &format_aliases(\n    std::ostream &out, const std::vector<std::string> &aliases,\n    std::size_t wid);\n\n/// Verify the first character of an option\n/// - is a trigger character, ! has special meaning and new lines would just be\n/// annoying to deal with\ntemplate <typename T>\nbool valid_first_char(T c) {\n  return ((c != '-') && (c != '!') && (c != ' ') && c != '\\n');\n}\n\n/// Verify following characters of an option\ntemplate <typename T>\nbool valid_later_char(T c) {\n  // = and : are value separators, { has special meaning for option defaults,\n  // and \\n would just be annoying to deal with in many places allowing space\n  // here has too much potential for inadvertent entry errors and bugs\n  return ((c != '=') && (c != ':') && (c != '{') && (c != ' ') && c != '\\n');\n}\n\n/// Verify an option/subcommand name\nCLI11_INLINE bool valid_name_string(const std::string &str);\n\n/// Verify an app name\ninline bool valid_alias_name_string(const std::string &str) {\n  static const std::string badChars(std::string(\"\\n\") + '\\0');\n  return (str.find_first_of(badChars) == std::string::npos);\n}\n\n/// check if a string is a container segment separator (empty or \"%%\")\ninline bool is_separator(const std::string &str) {\n  static const std::string sep(\"%%\");\n  return (str.empty() || str == sep);\n}\n\n/// Verify that str consists of letters only\ninline bool isalpha(const std::string &str) {\n  return std::all_of(str.begin(), str.end(),\n                     [](char c) { return std::isalpha(c, std::locale()); });\n}\n\n/// Return a lower case version of a string\ninline std::string to_lower(std::string str) {\n  std::transform(std::begin(str), std::end(str), std::begin(str),\n                 [](const std::string::value_type &x) {\n                   return std::tolower(x, std::locale());\n                 });\n  return str;\n}\n\n/// remove underscores from a string\ninline std::string remove_underscore(std::string str) {\n  str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str));\n  return str;\n}\n\n/// Find and replace a substring with another substring\nCLI11_INLINE std::string find_and_replace(std::string str, std::string from,\n                                          std::string to);\n\n/// check if the flag definitions has possible false flags\ninline bool has_default_flag_values(const std::string &flags) {\n  return (flags.find_first_of(\"{!\") != std::string::npos);\n}\n\nCLI11_INLINE void remove_default_flag_values(std::string &flags);\n\n/// Check if a string is a member of a list of strings and optionally ignore\n/// case or ignore underscores\nCLI11_INLINE std::ptrdiff_t find_member(std::string name,\n                                        const std::vector<std::string> names,\n                                        bool ignore_case = false,\n                                        bool ignore_underscore = false);\n\n/// Find a trigger string and call a modify callable function that takes the\n/// current string and starting position of the trigger and returns the position\n/// in the string to search for the next trigger string\ntemplate <typename Callable>\ninline std::string find_and_modify(std::string str, std::string trigger,\n                                   Callable modify) {\n  std::size_t start_pos = 0;\n  while ((start_pos = str.find(trigger, start_pos)) != std::string::npos) {\n    start_pos = modify(str, start_pos);\n  }\n  return str;\n}\n\n/// Split a string '\"one two\" \"three\"' into 'one two', 'three'\n/// Quote characters can be ` ' or \"\nCLI11_INLINE std::vector<std::string> split_up(std::string str,\n                                               char delimiter = '\\0');\n\n/// This function detects an equal or colon followed by an escaped quote after\n/// an argument then modifies the string to replace the equality with a space.\n/// This is needed to allow the split up function to work properly and is\n/// intended to be used with the find_and_modify function the return value is\n/// the offset+1 which is required by the find_and_modify function.\nCLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset);\n\n/// Add quotes if the string contains spaces\nCLI11_INLINE std::string &add_quotes_if_needed(std::string &str);\n\n}  // namespace detail\n\nnamespace detail {\nCLI11_INLINE std::vector<std::string> split(const std::string &s, char delim) {\n  std::vector<std::string> elems;\n  // Check to see if empty string, give consistent result\n  if (s.empty()) {\n    elems.emplace_back();\n  } else {\n    std::stringstream ss;\n    ss.str(s);\n    std::string item;\n    while (std::getline(ss, item, delim)) {\n      elems.push_back(item);\n    }\n  }\n  return elems;\n}\n\nCLI11_INLINE std::string &ltrim(std::string &str) {\n  auto it = std::find_if(str.begin(), str.end(), [](char ch) {\n    return !std::isspace<char>(ch, std::locale());\n  });\n  str.erase(str.begin(), it);\n  return str;\n}\n\nCLI11_INLINE std::string &ltrim(std::string &str, const std::string &filter) {\n  auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) {\n    return filter.find(ch) == std::string::npos;\n  });\n  str.erase(str.begin(), it);\n  return str;\n}\n\nCLI11_INLINE std::string &rtrim(std::string &str) {\n  auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) {\n    return !std::isspace<char>(ch, std::locale());\n  });\n  str.erase(it.base(), str.end());\n  return str;\n}\n\nCLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter) {\n  auto it = std::find_if(str.rbegin(), str.rend(), [&filter](char ch) {\n    return filter.find(ch) == std::string::npos;\n  });\n  str.erase(it.base(), str.end());\n  return str;\n}\n\nCLI11_INLINE std::string &remove_quotes(std::string &str) {\n  if (str.length() > 1 && (str.front() == '\"' || str.front() == '\\'')) {\n    if (str.front() == str.back()) {\n      str.pop_back();\n      str.erase(str.begin(), str.begin() + 1);\n    }\n  }\n  return str;\n}\n\nCLI11_INLINE std::string fix_newlines(const std::string &leader,\n                                      std::string input) {\n  std::string::size_type n = 0;\n  while (n != std::string::npos && n < input.size()) {\n    n = input.find('\\n', n);\n    if (n != std::string::npos) {\n      input = input.substr(0, n + 1) + leader + input.substr(n + 1);\n      n += leader.size();\n    }\n  }\n  return input;\n}\n\nCLI11_INLINE std::ostream &format_help(std::ostream &out, std::string name,\n                                       const std::string &description,\n                                       std::size_t wid) {\n  name = \"  \" + name;\n  out << std::setw(static_cast<int>(wid)) << std::left << name;\n  if (!description.empty()) {\n    if (name.length() >= wid)\n      out << \"\\n\" << std::setw(static_cast<int>(wid)) << \"\";\n    for (const char c : description) {\n      out.put(c);\n      if (c == '\\n') {\n        out << std::setw(static_cast<int>(wid)) << \"\";\n      }\n    }\n  }\n  out << \"\\n\";\n  return out;\n}\n\nCLI11_INLINE std::ostream &format_aliases(\n    std::ostream &out, const std::vector<std::string> &aliases,\n    std::size_t wid) {\n  if (!aliases.empty()) {\n    out << std::setw(static_cast<int>(wid)) << \"     aliases: \";\n    bool front = true;\n    for (const auto &alias : aliases) {\n      if (!front) {\n        out << \", \";\n      } else {\n        front = false;\n      }\n      out << detail::fix_newlines(\"              \", alias);\n    }\n    out << \"\\n\";\n  }\n  return out;\n}\n\nCLI11_INLINE bool valid_name_string(const std::string &str) {\n  if (str.empty() || !valid_first_char(str[0])) {\n    return false;\n  }\n  auto e = str.end();\n  for (auto c = str.begin() + 1; c != e; ++c)\n    if (!valid_later_char(*c)) return false;\n  return true;\n}\n\nCLI11_INLINE std::string find_and_replace(std::string str, std::string from,\n                                          std::string to) {\n  std::size_t start_pos = 0;\n\n  while ((start_pos = str.find(from, start_pos)) != std::string::npos) {\n    str.replace(start_pos, from.length(), to);\n    start_pos += to.length();\n  }\n\n  return str;\n}\n\nCLI11_INLINE void remove_default_flag_values(std::string &flags) {\n  auto loc = flags.find_first_of('{', 2);\n  while (loc != std::string::npos) {\n    auto finish = flags.find_first_of(\"},\", loc + 1);\n    if ((finish != std::string::npos) && (flags[finish] == '}')) {\n      flags.erase(flags.begin() + static_cast<std::ptrdiff_t>(loc),\n                  flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);\n    }\n    loc = flags.find_first_of('{', loc + 1);\n  }\n  flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end());\n}\n\nCLI11_INLINE std::ptrdiff_t find_member(std::string name,\n                                        const std::vector<std::string> names,\n                                        bool ignore_case,\n                                        bool ignore_underscore) {\n  auto it = std::end(names);\n  if (ignore_case) {\n    if (ignore_underscore) {\n      name = detail::to_lower(detail::remove_underscore(name));\n      it = std::find_if(\n          std::begin(names), std::end(names), [&name](std::string local_name) {\n            return detail::to_lower(detail::remove_underscore(local_name)) ==\n                   name;\n          });\n    } else {\n      name = detail::to_lower(name);\n      it = std::find_if(std::begin(names), std::end(names),\n                        [&name](std::string local_name) {\n                          return detail::to_lower(local_name) == name;\n                        });\n    }\n\n  } else if (ignore_underscore) {\n    name = detail::remove_underscore(name);\n    it = std::find_if(std::begin(names), std::end(names),\n                      [&name](std::string local_name) {\n                        return detail::remove_underscore(local_name) == name;\n                      });\n  } else {\n    it = std::find(std::begin(names), std::end(names), name);\n  }\n\n  return (it != std::end(names)) ? (it - std::begin(names)) : (-1);\n}\n\nCLI11_INLINE std::vector<std::string> split_up(std::string str,\n                                               char delimiter) {\n  const std::string delims(\"\\'\\\"`\");\n  auto find_ws = [delimiter](char ch) {\n    return (delimiter == '\\0') ? std::isspace<char>(ch, std::locale())\n                               : (ch == delimiter);\n  };\n  trim(str);\n\n  std::vector<std::string> output;\n  bool embeddedQuote = false;\n  char keyChar = ' ';\n  while (!str.empty()) {\n    if (delims.find_first_of(str[0]) != std::string::npos) {\n      keyChar = str[0];\n      auto end = str.find_first_of(keyChar, 1);\n      while ((end != std::string::npos) &&\n             (str[end - 1] == '\\\\')) {  // deal with escaped quotes\n        end = str.find_first_of(keyChar, end + 1);\n        embeddedQuote = true;\n      }\n      if (end != std::string::npos) {\n        output.push_back(str.substr(1, end - 1));\n        if (end + 2 < str.size()) {\n          str = str.substr(end + 2);\n        } else {\n          str.clear();\n        }\n\n      } else {\n        output.push_back(str.substr(1));\n        str = \"\";\n      }\n    } else {\n      auto it = std::find_if(std::begin(str), std::end(str), find_ws);\n      if (it != std::end(str)) {\n        std::string value = std::string(str.begin(), it);\n        output.push_back(value);\n        str = std::string(it + 1, str.end());\n      } else {\n        output.push_back(str);\n        str = \"\";\n      }\n    }\n    // transform any embedded quotes into the regular character\n    if (embeddedQuote) {\n      output.back() = find_and_replace(\n          output.back(), std::string(\"\\\\\") + keyChar, std::string(1, keyChar));\n      embeddedQuote = false;\n    }\n    trim(str);\n  }\n  return output;\n}\n\nCLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset) {\n  auto next = str[offset + 1];\n  if ((next == '\\\"') || (next == '\\'') || (next == '`')) {\n    auto astart = str.find_last_of(\"-/ \\\"\\'`\", offset - 1);\n    if (astart != std::string::npos) {\n      if (str[astart] == ((str[offset] == '=') ? '-' : '/'))\n        str[offset] =\n            ' ';  // interpret this as a space so the split_up works properly\n    }\n  }\n  return offset + 1;\n}\n\nCLI11_INLINE std::string &add_quotes_if_needed(std::string &str) {\n  if ((str.front() != '\"' && str.front() != '\\'') ||\n      str.front() != str.back()) {\n    char quote = str.find('\"') < str.find('\\'') ? '\\'' : '\"';\n    if (str.find(' ') != std::string::npos) {\n      str.insert(0, 1, quote);\n      str.append(1, quote);\n    }\n  }\n  return str;\n}\n\n}  // namespace detail\n\n// Use one of these on all error classes.\n// These are temporary and are undef'd at the end of this file.\n#define CLI11_ERROR_DEF(parent, name)                           \\\n protected:                                                     \\\n  name(std::string ename, std::string msg, int exit_code)       \\\n      : parent(std::move(ename), std::move(msg), exit_code) {}  \\\n  name(std::string ename, std::string msg, ExitCodes exit_code) \\\n      : parent(std::move(ename), std::move(msg), exit_code) {}  \\\n                                                                \\\n public:                                                        \\\n  name(std::string msg, ExitCodes exit_code)                    \\\n      : parent(#name, std::move(msg), exit_code) {}             \\\n  name(std::string msg, int exit_code)                          \\\n      : parent(#name, std::move(msg), exit_code) {}\n\n// This is added after the one above if a class is used directly and builds its\n// own message\n#define CLI11_ERROR_SIMPLE(name) \\\n  explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {}\n\n/// These codes are part of every error in CLI. They can be obtained from e\n/// using e.exit_code or as a quick shortcut, int values from\n/// e.get_error_code().\nenum class ExitCodes {\n  Success = 0,\n  IncorrectConstruction = 100,\n  BadNameString,\n  OptionAlreadyAdded,\n  FileError,\n  ConversionError,\n  ValidationError,\n  RequiredError,\n  RequiresError,\n  ExcludesError,\n  ExtrasError,\n  ConfigError,\n  InvalidError,\n  HorribleError,\n  OptionNotFound,\n  ArgumentMismatch,\n  BaseClass = 127\n};\n\n// Error definitions\n\n/// @defgroup error_group Errors\n/// @brief Errors thrown by CLI11\n///\n/// These are the errors that can be thrown. Some of them, like CLI::Success,\n/// are not really errors.\n/// @{\n\n/// All errors derive from this one\nclass Error : public std::runtime_error {\n  int actual_exit_code;\n  std::string error_name{\"Error\"};\n\n public:\n  CLI11_NODISCARD int get_exit_code() const { return actual_exit_code; }\n\n  CLI11_NODISCARD std::string get_name() const { return error_name; }\n\n  Error(std::string name, std::string msg,\n        int exit_code = static_cast<int>(ExitCodes::BaseClass))\n      : runtime_error(msg),\n        actual_exit_code(exit_code),\n        error_name(std::move(name)) {}\n\n  Error(std::string name, std::string msg, ExitCodes exit_code)\n      : Error(name, msg, static_cast<int>(exit_code)) {}\n};\n\n// Note: Using Error::Error constructors does not work on GCC 4.7\n\n/// Construction errors (not in parsing)\nclass ConstructionError : public Error {\n  CLI11_ERROR_DEF(Error, ConstructionError)\n};\n\n/// Thrown when an option is set to conflicting values (non-vector and multi\n/// args, for example)\nclass IncorrectConstruction : public ConstructionError {\n  CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction)\n  CLI11_ERROR_SIMPLE(IncorrectConstruction)\n  static IncorrectConstruction PositionalFlag(std::string name) {\n    return IncorrectConstruction(name + \": Flags cannot be positional\");\n  }\n  static IncorrectConstruction Set0Opt(std::string name) {\n    return IncorrectConstruction(name +\n                                 \": Cannot set 0 expected, use a flag instead\");\n  }\n  static IncorrectConstruction SetFlag(std::string name) {\n    return IncorrectConstruction(name +\n                                 \": Cannot set an expected number for flags\");\n  }\n  static IncorrectConstruction ChangeNotVector(std::string name) {\n    return IncorrectConstruction(\n        name + \": You can only change the expected arguments for vectors\");\n  }\n  static IncorrectConstruction AfterMultiOpt(std::string name) {\n    return IncorrectConstruction(name +\n                                 \": You can't change expected arguments after \"\n                                 \"you've changed the multi option policy!\");\n  }\n  static IncorrectConstruction MissingOption(std::string name) {\n    return IncorrectConstruction(\"Option \" + name + \" is not defined\");\n  }\n  static IncorrectConstruction MultiOptionPolicy(std::string name) {\n    return IncorrectConstruction(\n        name +\n        \": multi_option_policy only works for flags and exact value options\");\n  }\n};\n\n/// Thrown on construction of a bad name\nclass BadNameString : public ConstructionError {\n  CLI11_ERROR_DEF(ConstructionError, BadNameString)\n  CLI11_ERROR_SIMPLE(BadNameString)\n  static BadNameString OneCharName(std::string name) {\n    return BadNameString(\"Invalid one char name: \" + name);\n  }\n  static BadNameString BadLongName(std::string name) {\n    return BadNameString(\"Bad long name: \" + name);\n  }\n  static BadNameString DashesOnly(std::string name) {\n    return BadNameString(\"Must have a name, not just dashes: \" + name);\n  }\n  static BadNameString MultiPositionalNames(std::string name) {\n    return BadNameString(\"Only one positional name allowed, remove: \" + name);\n  }\n};\n\n/// Thrown when an option already exists\nclass OptionAlreadyAdded : public ConstructionError {\n  CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded)\n  explicit OptionAlreadyAdded(std::string name)\n      : OptionAlreadyAdded(name + \" is already added\",\n                           ExitCodes::OptionAlreadyAdded) {}\n  static OptionAlreadyAdded Requires(std::string name, std::string other) {\n    return {name + \" requires \" + other, ExitCodes::OptionAlreadyAdded};\n  }\n  static OptionAlreadyAdded Excludes(std::string name, std::string other) {\n    return {name + \" excludes \" + other, ExitCodes::OptionAlreadyAdded};\n  }\n};\n\n// Parsing errors\n\n/// Anything that can error in Parse\nclass ParseError : public Error {\n  CLI11_ERROR_DEF(Error, ParseError)\n};\n\n// Not really \"errors\"\n\n/// This is a successful completion on parsing, supposed to exit\nclass Success : public ParseError {\n  CLI11_ERROR_DEF(ParseError, Success)\n  Success()\n      : Success(\"Successfully completed, should be caught and quit\",\n                ExitCodes::Success) {}\n};\n\n/// -h or --help on command line\nclass CallForHelp : public Success {\n  CLI11_ERROR_DEF(Success, CallForHelp)\n  CallForHelp()\n      : CallForHelp(\"This should be caught in your main function, see examples\",\n                    ExitCodes::Success) {}\n};\n\n/// Usually something like --help-all on command line\nclass CallForAllHelp : public Success {\n  CLI11_ERROR_DEF(Success, CallForAllHelp)\n  CallForAllHelp()\n      : CallForAllHelp(\n            \"This should be caught in your main function, see examples\",\n            ExitCodes::Success) {}\n};\n\n/// -v or --version on command line\nclass CallForVersion : public Success {\n  CLI11_ERROR_DEF(Success, CallForVersion)\n  CallForVersion()\n      : CallForVersion(\n            \"This should be caught in your main function, see examples\",\n            ExitCodes::Success) {}\n};\n\n/// Does not output a diagnostic in CLI11_PARSE, but allows main() to return\n/// with a specific error code.\nclass RuntimeError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, RuntimeError)\n  explicit RuntimeError(int exit_code = 1)\n      : RuntimeError(\"Runtime error\", exit_code) {}\n};\n\n/// Thrown when parsing an INI file and it is missing\nclass FileError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, FileError)\n  CLI11_ERROR_SIMPLE(FileError)\n  static FileError Missing(std::string name) {\n    return FileError(name + \" was not readable (missing?)\");\n  }\n};\n\n/// Thrown when conversion call back fails, such as when an int fails to coerce\n/// to a string\nclass ConversionError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, ConversionError)\n  CLI11_ERROR_SIMPLE(ConversionError)\n  ConversionError(std::string member, std::string name)\n      : ConversionError(\"The value \" + member +\n                        \" is not an allowed value for \" + name) {}\n  ConversionError(std::string name, std::vector<std::string> results)\n      : ConversionError(\"Could not convert: \" + name + \" = \" +\n                        detail::join(results)) {}\n  static ConversionError TooManyInputsFlag(std::string name) {\n    return ConversionError(name + \": too many inputs for a flag\");\n  }\n  static ConversionError TrueFalse(std::string name) {\n    return ConversionError(name + \": Should be true/false or a number\");\n  }\n};\n\n/// Thrown when validation of results fails\nclass ValidationError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, ValidationError)\n  CLI11_ERROR_SIMPLE(ValidationError)\n  explicit ValidationError(std::string name, std::string msg)\n      : ValidationError(name + \": \" + msg) {}\n};\n\n/// Thrown when a required option is missing\nclass RequiredError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, RequiredError)\n  explicit RequiredError(std::string name)\n      : RequiredError(name + \" is required\", ExitCodes::RequiredError) {}\n  static RequiredError Subcommand(std::size_t min_subcom) {\n    if (min_subcom == 1) {\n      return RequiredError(\"A subcommand\");\n    }\n    return {\"Requires at least \" + std::to_string(min_subcom) + \" subcommands\",\n            ExitCodes::RequiredError};\n  }\n  static RequiredError Option(std::size_t min_option, std::size_t max_option,\n                              std::size_t used,\n                              const std::string &option_list) {\n    if ((min_option == 1) && (max_option == 1) && (used == 0))\n      return RequiredError(\"Exactly 1 option from [\" + option_list + \"]\");\n    if ((min_option == 1) && (max_option == 1) && (used > 1)) {\n      return {\"Exactly 1 option from [\" + option_list + \"] is required and \" +\n                  std::to_string(used) + \" were given\",\n              ExitCodes::RequiredError};\n    }\n    if ((min_option == 1) && (used == 0))\n      return RequiredError(\"At least 1 option from [\" + option_list + \"]\");\n    if (used < min_option) {\n      return {\"Requires at least \" + std::to_string(min_option) +\n                  \" options used and only \" + std::to_string(used) +\n                  \"were given from [\" + option_list + \"]\",\n              ExitCodes::RequiredError};\n    }\n    if (max_option == 1)\n      return {\"Requires at most 1 options be given from [\" + option_list + \"]\",\n              ExitCodes::RequiredError};\n\n    return {\"Requires at most \" + std::to_string(max_option) +\n                \" options be used and \" + std::to_string(used) +\n                \"were given from [\" + option_list + \"]\",\n            ExitCodes::RequiredError};\n  }\n};\n\n/// Thrown when the wrong number of arguments has been received\nclass ArgumentMismatch : public ParseError {\n  CLI11_ERROR_DEF(ParseError, ArgumentMismatch)\n  CLI11_ERROR_SIMPLE(ArgumentMismatch)\n  ArgumentMismatch(std::string name, int expected, std::size_t received)\n      : ArgumentMismatch(expected > 0\n                             ? (\"Expected exactly \" + std::to_string(expected) +\n                                \" arguments to \" + name + \", got \" +\n                                std::to_string(received))\n                             : (\"Expected at least \" +\n                                std::to_string(-expected) + \" arguments to \" +\n                                name + \", got \" + std::to_string(received)),\n                         ExitCodes::ArgumentMismatch) {}\n\n  static ArgumentMismatch AtLeast(std::string name, int num,\n                                  std::size_t received) {\n    return ArgumentMismatch(name + \": At least \" + std::to_string(num) +\n                            \" required but received \" +\n                            std::to_string(received));\n  }\n  static ArgumentMismatch AtMost(std::string name, int num,\n                                 std::size_t received) {\n    return ArgumentMismatch(name + \": At Most \" + std::to_string(num) +\n                            \" required but received \" +\n                            std::to_string(received));\n  }\n  static ArgumentMismatch TypedAtLeast(std::string name, int num,\n                                       std::string type) {\n    return ArgumentMismatch(name + \": \" + std::to_string(num) + \" required \" +\n                            type + \" missing\");\n  }\n  static ArgumentMismatch FlagOverride(std::string name) {\n    return ArgumentMismatch(name + \" was given a disallowed flag override\");\n  }\n  static ArgumentMismatch PartialType(std::string name, int num,\n                                      std::string type) {\n    return ArgumentMismatch(name + \": \" + type + \" only partially specified: \" +\n                            std::to_string(num) + \" required for each element\");\n  }\n};\n\n/// Thrown when a requires option is missing\nclass RequiresError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, RequiresError)\n  RequiresError(std::string curname, std::string subname)\n      : RequiresError(curname + \" requires \" + subname,\n                      ExitCodes::RequiresError) {}\n};\n\n/// Thrown when an excludes option is present\nclass ExcludesError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, ExcludesError)\n  ExcludesError(std::string curname, std::string subname)\n      : ExcludesError(curname + \" excludes \" + subname,\n                      ExitCodes::ExcludesError) {}\n};\n\n/// Thrown when too many positionals or options are found\nclass ExtrasError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, ExtrasError)\n  explicit ExtrasError(std::vector<std::string> args)\n      : ExtrasError(\n            (args.size() > 1 ? \"The following arguments were not expected: \"\n                             : \"The following argument was not expected: \") +\n                detail::rjoin(args, \" \"),\n            ExitCodes::ExtrasError) {}\n  ExtrasError(const std::string &name, std::vector<std::string> args)\n      : ExtrasError(\n            name,\n            (args.size() > 1 ? \"The following arguments were not expected: \"\n                             : \"The following argument was not expected: \") +\n                detail::rjoin(args, \" \"),\n            ExitCodes::ExtrasError) {}\n};\n\n/// Thrown when extra values are found in an INI file\nclass ConfigError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, ConfigError)\n  CLI11_ERROR_SIMPLE(ConfigError)\n  static ConfigError Extras(std::string item) {\n    return ConfigError(\"INI was not able to parse \" + item);\n  }\n  static ConfigError NotConfigurable(std::string item) {\n    return ConfigError(item +\n                       \": This option is not allowed in a configuration file\");\n  }\n};\n\n/// Thrown when validation fails before parsing\nclass InvalidError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, InvalidError)\n  explicit InvalidError(std::string name)\n      : InvalidError(\n            name +\n                \": Too many positional arguments with unlimited expected args\",\n            ExitCodes::InvalidError) {}\n};\n\n/// This is just a safety check to verify selection and parsing match - you\n/// should not ever see it Strings are directly added to this error, but again,\n/// it should never be seen.\nclass HorribleError : public ParseError {\n  CLI11_ERROR_DEF(ParseError, HorribleError)\n  CLI11_ERROR_SIMPLE(HorribleError)\n};\n\n// After parsing\n\n/// Thrown when counting a non-existent option\nclass OptionNotFound : public Error {\n  CLI11_ERROR_DEF(Error, OptionNotFound)\n  explicit OptionNotFound(std::string name)\n      : OptionNotFound(name + \" not found\", ExitCodes::OptionNotFound) {}\n};\n\n#undef CLI11_ERROR_DEF\n#undef CLI11_ERROR_SIMPLE\n\n/// @}\n\n// Type tools\n\n// Utilities for type enabling\nnamespace detail {\n// Based generally on https://rmf.io/cxx11/almost-static-if\n/// Simple empty scoped class\nenum class enabler {};\n\n/// An instance to use in EnableIf\nconstexpr enabler dummy = {};\n}  // namespace detail\n\n/// A copy of enable_if_t from C++14, compatible with C++11.\n///\n/// We could check to see if C++14 is being used, but it does not hurt to\n/// redefine this (even Google does this:\n/// https://github.com/google/skia/blob/main/include/private/SkTLogic.h) It is\n/// not in the std namespace anyway, so no harm done.\ntemplate <bool B, class T = void>\nusing enable_if_t = typename std::enable_if<B, T>::type;\n\n/// A copy of std::void_t from C++17 (helper for C++11 and C++14)\ntemplate <typename... Ts>\nstruct make_void {\n  using type = void;\n};\n\n/// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does\n/// not hurt to redefine\ntemplate <typename... Ts>\nusing void_t = typename make_void<Ts...>::type;\n\n/// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it\n/// does not hurt to redefine\ntemplate <bool B, class T, class F>\nusing conditional_t = typename std::conditional<B, T, F>::type;\n\n/// Check to see if something is bool (fail check by default)\ntemplate <typename T>\nstruct is_bool : std::false_type {};\n\n/// Check to see if something is bool (true if actually a bool)\ntemplate <>\nstruct is_bool<bool> : std::true_type {};\n\n/// Check to see if something is a shared pointer\ntemplate <typename T>\nstruct is_shared_ptr : std::false_type {};\n\n/// Check to see if something is a shared pointer (True if really a shared\n/// pointer)\ntemplate <typename T>\nstruct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};\n\n/// Check to see if something is a shared pointer (True if really a shared\n/// pointer)\ntemplate <typename T>\nstruct is_shared_ptr<const std::shared_ptr<T>> : std::true_type {};\n\n/// Check to see if something is copyable pointer\ntemplate <typename T>\nstruct is_copyable_ptr {\n  static bool const value =\n      is_shared_ptr<T>::value || std::is_pointer<T>::value;\n};\n\n/// This can be specialized to override the type deduction for IsMember.\ntemplate <typename T>\nstruct IsMemberType {\n  using type = T;\n};\n\n/// The main custom type needed here is const char * should be a string.\ntemplate <>\nstruct IsMemberType<const char *> {\n  using type = std::string;\n};\n\nnamespace detail {\n\n// These are utilities for IsMember and other transforming objects\n\n/// Handy helper to access the element_type generically. This is not part of\n/// is_copyable_ptr because it requires that pointer_traits<T> be valid.\n\n/// not a pointer\ntemplate <typename T, typename Enable = void>\nstruct element_type {\n  using type = T;\n};\n\ntemplate <typename T>\nstruct element_type<T,\n                    typename std::enable_if<is_copyable_ptr<T>::value>::type> {\n  using type = typename std::pointer_traits<T>::element_type;\n};\n\n/// Combination of the element type and value type - remove pointer (including\n/// smart pointers) and get the value_type of the container\ntemplate <typename T>\nstruct element_value_type {\n  using type = typename element_type<T>::type::value_type;\n};\n\n/// Adaptor for set-like structure: This just wraps a normal container in a few\n/// utilities that do almost nothing.\ntemplate <typename T, typename _ = void>\nstruct pair_adaptor : std::false_type {\n  using value_type = typename T::value_type;\n  using first_type = typename std::remove_const<value_type>::type;\n  using second_type = typename std::remove_const<value_type>::type;\n\n  /// Get the first value (really just the underlying value)\n  template <typename Q>\n  static auto first(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {\n    return std::forward<Q>(pair_value);\n  }\n  /// Get the second value (really just the underlying value)\n  template <typename Q>\n  static auto second(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {\n    return std::forward<Q>(pair_value);\n  }\n};\n\n/// Adaptor for map-like structure (true version, must have key_type and\n/// mapped_type). This wraps a mapped container in a few utilities access it in\n/// a general way.\ntemplate <typename T>\nstruct pair_adaptor<T,\n                    conditional_t<false,\n                                  void_t<typename T::value_type::first_type,\n                                         typename T::value_type::second_type>,\n                                  void>> : std::true_type {\n  using value_type = typename T::value_type;\n  using first_type =\n      typename std::remove_const<typename value_type::first_type>::type;\n  using second_type =\n      typename std::remove_const<typename value_type::second_type>::type;\n\n  /// Get the first value (really just the underlying value)\n  template <typename Q>\n  static auto first(Q &&pair_value)\n      -> decltype(std::get<0>(std::forward<Q>(pair_value))) {\n    return std::get<0>(std::forward<Q>(pair_value));\n  }\n  /// Get the second value (really just the underlying value)\n  template <typename Q>\n  static auto second(Q &&pair_value)\n      -> decltype(std::get<1>(std::forward<Q>(pair_value))) {\n    return std::get<1>(std::forward<Q>(pair_value));\n  }\n};\n\n// Warning is suppressed due to \"bug\" in gcc<5.0 and gcc 7.0 with c++17 enabled\n// that generates a Wnarrowing warning in the unevaluated context even if the\n// function that was using this wasn't used.  The standard says narrowing in\n// brace initialization shouldn't be allowed but for backwards compatibility gcc\n// allows it in some contexts.  It is a little fuzzy what happens in template\n// constructs and I think that was something GCC took a little while to work\n// out. But regardless some versions of gcc generate a warning when they\n// shouldn't from the following code so that should be suppressed\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wnarrowing\"\n#endif\n// check for constructibility from a specific type and copy assignable used in\n// the parse detection\ntemplate <typename T, typename C>\nclass is_direct_constructible {\n  template <typename TT, typename CC>\n  static auto test(int, std::true_type) -> decltype(\n// NVCC warns about narrowing conversions here\n#ifdef __CUDACC__\n#pragma diag_suppress 2361\n#endif\n      TT{std::declval<CC>()}\n#ifdef __CUDACC__\n#pragma diag_default 2361\n#endif\n      ,\n      std::is_move_assignable<TT>());\n\n  template <typename TT, typename CC>\n  static auto test(int, std::false_type) -> std::false_type;\n\n  template <typename, typename>\n  static auto test(...) -> std::false_type;\n\n public:\n  static constexpr bool value = decltype(test<T, C>(\n      0, typename std::is_constructible<T, C>::type()))::value;\n};\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n\n// Check for output streamability\n// Based on\n// https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream\n\ntemplate <typename T, typename S = std::ostringstream>\nclass is_ostreamable {\n  template <typename TT, typename SS>\n  static auto test(int)\n      -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());\n\n  template <typename, typename>\n  static auto test(...) -> std::false_type;\n\n public:\n  static constexpr bool value = decltype(test<T, S>(0))::value;\n};\n\n/// Check for input streamability\ntemplate <typename T, typename S = std::istringstream>\nclass is_istreamable {\n  template <typename TT, typename SS>\n  static auto test(int)\n      -> decltype(std::declval<SS &>() >> std::declval<TT &>(),\n                  std::true_type());\n\n  template <typename, typename>\n  static auto test(...) -> std::false_type;\n\n public:\n  static constexpr bool value = decltype(test<T, S>(0))::value;\n};\n\n/// Check for complex\ntemplate <typename T>\nclass is_complex {\n  template <typename TT>\n  static auto test(int)\n      -> decltype(std::declval<TT>().real(), std::declval<TT>().imag(),\n                  std::true_type());\n\n  template <typename>\n  static auto test(...) -> std::false_type;\n\n public:\n  static constexpr bool value = decltype(test<T>(0))::value;\n};\n\n/// Templated operation to get a value from a stream\ntemplate <typename T, enable_if_t<is_istreamable<T>::value, detail::enabler> =\n                          detail::dummy>\nbool from_stream(const std::string &istring, T &obj) {\n  std::istringstream is;\n  is.str(istring);\n  is >> obj;\n  return !is.fail() && !is.rdbuf()->in_avail();\n}\n\ntemplate <typename T, enable_if_t<!is_istreamable<T>::value, detail::enabler> =\n                          detail::dummy>\nbool from_stream(const std::string & /*istring*/, T & /*obj*/) {\n  return false;\n}\n\n// check to see if an object is a mutable container (fail by default)\ntemplate <typename T, typename _ = void>\nstruct is_mutable_container : std::false_type {};\n\n/// type trait to test if a type is a mutable container meaning it has a\n/// value_type, it has an iterator, a clear, and end methods and an insert\n/// function.  And for our purposes we exclude std::string and types that can be\n/// constructed from a std::string\ntemplate <typename T>\nstruct is_mutable_container<\n    T, conditional_t<\n           false,\n           void_t<typename T::value_type, decltype(std::declval<T>().end()),\n                  decltype(std::declval<T>().clear()),\n                  decltype(std::declval<T>().insert(\n                      std::declval<decltype(std::declval<T>().end())>(),\n                      std::declval<const typename T::value_type &>()))>,\n           void>>\n    : public conditional_t<std::is_constructible<T, std::string>::value,\n                           std::false_type, std::true_type> {};\n\n// check to see if an object is a mutable container (fail by default)\ntemplate <typename T, typename _ = void>\nstruct is_readable_container : std::false_type {};\n\n/// type trait to test if a type is a container meaning it has a value_type, it\n/// has an iterator, a clear, and an end methods and an insert function.  And\n/// for our purposes we exclude std::string and types that can be constructed\n/// from a std::string\ntemplate <typename T>\nstruct is_readable_container<\n    T, conditional_t<false,\n                     void_t<decltype(std::declval<T>().end()),\n                            decltype(std::declval<T>().begin())>,\n                     void>> : public std::true_type {};\n\n// check to see if an object is a wrapper (fail by default)\ntemplate <typename T, typename _ = void>\nstruct is_wrapper : std::false_type {};\n\n// check if an object is a wrapper (it has a value_type defined)\ntemplate <typename T>\nstruct is_wrapper<T, conditional_t<false, void_t<typename T::value_type>, void>>\n    : public std::true_type {};\n\n// Check for tuple like types, as in classes with a tuple_size type trait\ntemplate <typename S>\nclass is_tuple_like {\n  template <typename SS>\n  // static auto test(int)\n  //     -> decltype(std::conditional<(std::tuple_size<SS>::value > 0),\n  //     std::true_type, std::false_type>::type());\n  static auto test(int)\n      -> decltype(std::tuple_size<typename std::decay<SS>::type>::value,\n                  std::true_type{});\n  template <typename>\n  static auto test(...) -> std::false_type;\n\n public:\n  static constexpr bool value = decltype(test<S>(0))::value;\n};\n\n/// Convert an object to a string (directly forward if this can become a string)\ntemplate <typename T, enable_if_t<std::is_convertible<T, std::string>::value,\n                                  detail::enabler> = detail::dummy>\nauto to_string(T &&value) -> decltype(std::forward<T>(value)) {\n  return std::forward<T>(value);\n}\n\n/// Construct a string from the object\ntemplate <typename T,\n          enable_if_t<std::is_constructible<std::string, T>::value &&\n                          !std::is_convertible<T, std::string>::value,\n                      detail::enabler> = detail::dummy>\nstd::string to_string(const T &value) {\n  return std::string(value);  // NOLINT(google-readability-casting)\n}\n\n/// Convert an object to a string (streaming must be supported for that type)\ntemplate <typename T,\n          enable_if_t<!std::is_convertible<std::string, T>::value &&\n                          !std::is_constructible<std::string, T>::value &&\n                          is_ostreamable<T>::value,\n                      detail::enabler> = detail::dummy>\nstd::string to_string(T &&value) {\n  std::stringstream stream;\n  stream << value;\n  return stream.str();\n}\n\n/// If conversion is not supported, return an empty string (streaming is not\n/// supported for that type)\ntemplate <typename T,\n          enable_if_t<!std::is_constructible<std::string, T>::value &&\n                          !is_ostreamable<T>::value &&\n                          !is_readable_container<\n                              typename std::remove_const<T>::type>::value,\n                      detail::enabler> = detail::dummy>\nstd::string to_string(T &&) {\n  return {};\n}\n\n/// convert a readable container to a string\ntemplate <typename T,\n          enable_if_t<!std::is_constructible<std::string, T>::value &&\n                          !is_ostreamable<T>::value &&\n                          is_readable_container<T>::value,\n                      detail::enabler> = detail::dummy>\nstd::string to_string(T &&variable) {\n  auto cval = variable.begin();\n  auto end = variable.end();\n  if (cval == end) {\n    return {\"{}\"};\n  }\n  std::vector<std::string> defaults;\n  while (cval != end) {\n    defaults.emplace_back(CLI::detail::to_string(*cval));\n    ++cval;\n  }\n  return {\"[\" + detail::join(defaults) + \"]\"};\n}\n\n/// special template overload\ntemplate <\n    typename T1, typename T2, typename T,\n    enable_if_t<std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>\nauto checked_to_string(T &&value)\n    -> decltype(to_string(std::forward<T>(value))) {\n  return to_string(std::forward<T>(value));\n}\n\n/// special template overload\ntemplate <\n    typename T1, typename T2, typename T,\n    enable_if_t<!std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>\nstd::string checked_to_string(T &&) {\n  return std::string{};\n}\n/// get a string as a convertible value for arithmetic types\ntemplate <typename T, enable_if_t<std::is_arithmetic<T>::value,\n                                  detail::enabler> = detail::dummy>\nstd::string value_string(const T &value) {\n  return std::to_string(value);\n}\n/// get a string as a convertible value for enumerations\ntemplate <typename T,\n          enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>\nstd::string value_string(const T &value) {\n  return std::to_string(\n      static_cast<typename std::underlying_type<T>::type>(value));\n}\n/// for other types just use the regular to_string function\ntemplate <typename T,\n          enable_if_t<!std::is_enum<T>::value && !std::is_arithmetic<T>::value,\n                      detail::enabler> = detail::dummy>\nauto value_string(const T &value) -> decltype(to_string(value)) {\n  return to_string(value);\n}\n\n/// template to get the underlying value type if it exists or use a default\ntemplate <typename T, typename def, typename Enable = void>\nstruct wrapped_type {\n  using type = def;\n};\n\n/// Type size for regular object types that do not look like a tuple\ntemplate <typename T, typename def>\nstruct wrapped_type<T, def,\n                    typename std::enable_if<is_wrapper<T>::value>::type> {\n  using type = typename T::value_type;\n};\n\n/// This will only trigger for actual void type\ntemplate <typename T, typename Enable = void>\nstruct type_count_base {\n  static const int value{0};\n};\n\n/// Type size for regular object types that do not look like a tuple\ntemplate <typename T>\nstruct type_count_base<\n    T, typename std::enable_if<!is_tuple_like<T>::value &&\n                               !is_mutable_container<T>::value &&\n                               !std::is_void<T>::value>::type> {\n  static constexpr int value{1};\n};\n\n/// the base tuple size\ntemplate <typename T>\nstruct type_count_base<\n    T, typename std::enable_if<is_tuple_like<T>::value &&\n                               !is_mutable_container<T>::value>::type> {\n  static constexpr int value{std::tuple_size<T>::value};\n};\n\n/// Type count base for containers is the type_count_base of the individual\n/// element\ntemplate <typename T>\nstruct type_count_base<\n    T, typename std::enable_if<is_mutable_container<T>::value>::type> {\n  static constexpr int value{type_count_base<typename T::value_type>::value};\n};\n\n/// Set of overloads to get the type size of an object\n\n/// forward declare the subtype_count structure\ntemplate <typename T>\nstruct subtype_count;\n\n/// forward declare the subtype_count_min structure\ntemplate <typename T>\nstruct subtype_count_min;\n\n/// This will only trigger for actual void type\ntemplate <typename T, typename Enable = void>\nstruct type_count {\n  static const int value{0};\n};\n\n/// Type size for regular object types that do not look like a tuple\ntemplate <typename T>\nstruct type_count<T,\n                  typename std::enable_if<\n                      !is_wrapper<T>::value && !is_tuple_like<T>::value &&\n                      !is_complex<T>::value && !std::is_void<T>::value>::type> {\n  static constexpr int value{1};\n};\n\n/// Type size for complex since it sometimes looks like a wrapper\ntemplate <typename T>\nstruct type_count<T, typename std::enable_if<is_complex<T>::value>::type> {\n  static constexpr int value{2};\n};\n\n/// Type size of types that are wrappers,except complex and tuples(which can\n/// also be wrappers sometimes)\ntemplate <typename T>\nstruct type_count<\n    T, typename std::enable_if<is_mutable_container<T>::value>::type> {\n  static constexpr int value{subtype_count<typename T::value_type>::value};\n};\n\n/// Type size of types that are wrappers,except containers complex and\n/// tuples(which can also be wrappers sometimes)\ntemplate <typename T>\nstruct type_count<\n    T, typename std::enable_if<is_wrapper<T>::value && !is_complex<T>::value &&\n                               !is_tuple_like<T>::value &&\n                               !is_mutable_container<T>::value>::type> {\n  static constexpr int value{type_count<typename T::value_type>::value};\n};\n\n/// 0 if the index > tuple size\ntemplate <typename T, std::size_t I>\nconstexpr typename std::enable_if<I == type_count_base<T>::value, int>::type\ntuple_type_size() {\n  return 0;\n}\n\n/// Recursively generate the tuple type name\ntemplate <typename T, std::size_t I>\n    constexpr typename std::enable_if <\n    I<type_count_base<T>::value, int>::type tuple_type_size() {\n  return subtype_count<typename std::tuple_element<I, T>::type>::value +\n         tuple_type_size<T, I + 1>();\n}\n\n/// Get the type size of the sum of type sizes for all the individual tuple\n/// types\ntemplate <typename T>\nstruct type_count<T, typename std::enable_if<is_tuple_like<T>::value>::type> {\n  static constexpr int value{tuple_type_size<T, 0>()};\n};\n\n/// definition of subtype count\ntemplate <typename T>\nstruct subtype_count {\n  static constexpr int value{is_mutable_container<T>::value\n                                 ? expected_max_vector_size\n                                 : type_count<T>::value};\n};\n\n/// This will only trigger for actual void type\ntemplate <typename T, typename Enable = void>\nstruct type_count_min {\n  static const int value{0};\n};\n\n/// Type size for regular object types that do not look like a tuple\ntemplate <typename T>\nstruct type_count_min<\n    T, typename std::enable_if<!is_mutable_container<T>::value &&\n                               !is_tuple_like<T>::value &&\n                               !is_wrapper<T>::value && !is_complex<T>::value &&\n                               !std::is_void<T>::value>::type> {\n  static constexpr int value{type_count<T>::value};\n};\n\n/// Type size for complex since it sometimes looks like a wrapper\ntemplate <typename T>\nstruct type_count_min<T, typename std::enable_if<is_complex<T>::value>::type> {\n  static constexpr int value{1};\n};\n\n/// Type size min of types that are wrappers,except complex and tuples(which can\n/// also be wrappers sometimes)\ntemplate <typename T>\nstruct type_count_min<\n    T, typename std::enable_if<is_wrapper<T>::value && !is_complex<T>::value &&\n                               !is_tuple_like<T>::value>::type> {\n  static constexpr int value{subtype_count_min<typename T::value_type>::value};\n};\n\n/// 0 if the index > tuple size\ntemplate <typename T, std::size_t I>\nconstexpr typename std::enable_if<I == type_count_base<T>::value, int>::type\ntuple_type_size_min() {\n  return 0;\n}\n\n/// Recursively generate the tuple type name\ntemplate <typename T, std::size_t I>\n    constexpr typename std::enable_if <\n    I<type_count_base<T>::value, int>::type tuple_type_size_min() {\n  return subtype_count_min<typename std::tuple_element<I, T>::type>::value +\n         tuple_type_size_min<T, I + 1>();\n}\n\n/// Get the type size of the sum of type sizes for all the individual tuple\n/// types\ntemplate <typename T>\nstruct type_count_min<T,\n                      typename std::enable_if<is_tuple_like<T>::value>::type> {\n  static constexpr int value{tuple_type_size_min<T, 0>()};\n};\n\n/// definition of subtype count\ntemplate <typename T>\nstruct subtype_count_min {\n  static constexpr int value{\n      is_mutable_container<T>::value\n          ? ((type_count<T>::value < expected_max_vector_size)\n                 ? type_count<T>::value\n                 : 0)\n          : type_count_min<T>::value};\n};\n\n/// This will only trigger for actual void type\ntemplate <typename T, typename Enable = void>\nstruct expected_count {\n  static const int value{0};\n};\n\n/// For most types the number of expected items is 1\ntemplate <typename T>\nstruct expected_count<T,\n                      typename std::enable_if<!is_mutable_container<T>::value &&\n                                              !is_wrapper<T>::value &&\n                                              !std::is_void<T>::value>::type> {\n  static constexpr int value{1};\n};\n/// number of expected items in a vector\ntemplate <typename T>\nstruct expected_count<\n    T, typename std::enable_if<is_mutable_container<T>::value>::type> {\n  static constexpr int value{expected_max_vector_size};\n};\n\n/// number of expected items in a vector\ntemplate <typename T>\nstruct expected_count<T,\n                      typename std::enable_if<!is_mutable_container<T>::value &&\n                                              is_wrapper<T>::value>::type> {\n  static constexpr int value{expected_count<typename T::value_type>::value};\n};\n\n// Enumeration of the different supported categorizations of objects\nenum class object_category : int {\n  char_value = 1,\n  integral_value = 2,\n  unsigned_integral = 4,\n  enumeration = 6,\n  boolean_value = 8,\n  floating_point = 10,\n  number_constructible = 12,\n  double_constructible = 14,\n  integer_constructible = 16,\n  // string like types\n  string_assignable = 23,\n  string_constructible = 24,\n  other = 45,\n  // special wrapper or container types\n  wrapper_value = 50,\n  complex_number = 60,\n  tuple_value = 70,\n  container_value = 80,\n\n};\n\n/// Set of overloads to classify an object according to type\n\n/// some type that is not otherwise recognized\ntemplate <typename T, typename Enable = void>\nstruct classify_object {\n  static constexpr object_category value{object_category::other};\n};\n\n/// Signed integers\ntemplate <typename T>\nstruct classify_object<\n    T, typename std::enable_if<std::is_integral<T>::value &&\n                               !std::is_same<T, char>::value &&\n                               std::is_signed<T>::value && !is_bool<T>::value &&\n                               !std::is_enum<T>::value>::type> {\n  static constexpr object_category value{object_category::integral_value};\n};\n\n/// Unsigned integers\ntemplate <typename T>\nstruct classify_object<\n    T, typename std::enable_if<\n           std::is_integral<T>::value && std::is_unsigned<T>::value &&\n           !std::is_same<T, char>::value && !is_bool<T>::value>::type> {\n  static constexpr object_category value{object_category::unsigned_integral};\n};\n\n/// single character values\ntemplate <typename T>\nstruct classify_object<T,\n                       typename std::enable_if<std::is_same<T, char>::value &&\n                                               !std::is_enum<T>::value>::type> {\n  static constexpr object_category value{object_category::char_value};\n};\n\n/// Boolean values\ntemplate <typename T>\nstruct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {\n  static constexpr object_category value{object_category::boolean_value};\n};\n\n/// Floats\ntemplate <typename T>\nstruct classify_object<\n    T, typename std::enable_if<std::is_floating_point<T>::value>::type> {\n  static constexpr object_category value{object_category::floating_point};\n};\n\n/// String and similar direct assignment\ntemplate <typename T>\nstruct classify_object<\n    T, typename std::enable_if<\n           !std::is_floating_point<T>::value && !std::is_integral<T>::value &&\n           std::is_assignable<T &, std::string>::value>::type> {\n  static constexpr object_category value{object_category::string_assignable};\n};\n\n/// String and similar constructible and copy assignment\ntemplate <typename T>\nstruct classify_object<\n    T, typename std::enable_if<\n           !std::is_floating_point<T>::value && !std::is_integral<T>::value &&\n           !std::is_assignable<T &, std::string>::value &&\n           (type_count<T>::value == 1) &&\n           std::is_constructible<T, std::string>::value>::type> {\n  static constexpr object_category value{object_category::string_constructible};\n};\n\n/// Enumerations\ntemplate <typename T>\nstruct classify_object<T,\n                       typename std::enable_if<std::is_enum<T>::value>::type> {\n  static constexpr object_category value{object_category::enumeration};\n};\n\ntemplate <typename T>\nstruct classify_object<T, typename std::enable_if<is_complex<T>::value>::type> {\n  static constexpr object_category value{object_category::complex_number};\n};\n\n/// Handy helper to contain a bunch of checks that rule out many common types\n/// (integers, string like, floating point, vectors, and enumerations\ntemplate <typename T>\nstruct uncommon_type {\n  using type = typename std::conditional<\n      !std::is_floating_point<T>::value && !std::is_integral<T>::value &&\n          !std::is_assignable<T &, std::string>::value &&\n          !std::is_constructible<T, std::string>::value &&\n          !is_complex<T>::value && !is_mutable_container<T>::value &&\n          !std::is_enum<T>::value,\n      std::true_type, std::false_type>::type;\n  static constexpr bool value = type::value;\n};\n\n/// wrapper type\ntemplate <typename T>\nstruct classify_object<\n    T, typename std::enable_if<(\n           !is_mutable_container<T>::value && is_wrapper<T>::value &&\n           !is_tuple_like<T>::value && uncommon_type<T>::value)>::type> {\n  static constexpr object_category value{object_category::wrapper_value};\n};\n\n/// Assignable from double or int\ntemplate <typename T>\nstruct classify_object<\n    T, typename std::enable_if<\n           uncommon_type<T>::value && type_count<T>::value == 1 &&\n           !is_wrapper<T>::value && is_direct_constructible<T, double>::value &&\n           is_direct_constructible<T, int>::value>::type> {\n  static constexpr object_category value{object_category::number_constructible};\n};\n\n/// Assignable from int\ntemplate <typename T>\nstruct classify_object<\n    T,\n    typename std::enable_if<\n        uncommon_type<T>::value && type_count<T>::value == 1 &&\n        !is_wrapper<T>::value && !is_direct_constructible<T, double>::value &&\n        is_direct_constructible<T, int>::value>::type> {\n  static constexpr object_category value{\n      object_category::integer_constructible};\n};\n\n/// Assignable from double\ntemplate <typename T>\nstruct classify_object<\n    T, typename std::enable_if<\n           uncommon_type<T>::value && type_count<T>::value == 1 &&\n           !is_wrapper<T>::value && is_direct_constructible<T, double>::value &&\n           !is_direct_constructible<T, int>::value>::type> {\n  static constexpr object_category value{object_category::double_constructible};\n};\n\n/// Tuple type\ntemplate <typename T>\nstruct classify_object<\n    T, typename std::enable_if<\n           is_tuple_like<T>::value &&\n           ((type_count<T>::value >= 2 && !is_wrapper<T>::value) ||\n            (uncommon_type<T>::value &&\n             !is_direct_constructible<T, double>::value &&\n             !is_direct_constructible<T, int>::value) ||\n            (uncommon_type<T>::value && type_count<T>::value >= 2))>::type> {\n  static constexpr object_category value{object_category::tuple_value};\n  // the condition on this class requires it be like a tuple, but on some\n  // compilers (like Xcode) tuples can be constructed from just the first\n  // element so tuples of <string, int,int> can be constructed from a string,\n  // which could lead to issues so there are two variants of the condition, the\n  // first isolates things with a type size >=2 mainly to get tuples on Xcode\n  // with the exception of wrappers, the second is the main one and just\n  // separating out those cases that are caught by other object classifications\n};\n\n/// container type\ntemplate <typename T>\nstruct classify_object<\n    T, typename std::enable_if<is_mutable_container<T>::value>::type> {\n  static constexpr object_category value{object_category::container_value};\n};\n\n// Type name print\n\n/// Was going to be based on\n///  http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template\n/// But this is cleaner and works better in this case\n\ntemplate <typename T,\n          enable_if_t<classify_object<T>::value == object_category::char_value,\n                      detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n  return \"CHAR\";\n}\n\ntemplate <\n    typename T,\n    enable_if_t<classify_object<T>::value == object_category::integral_value ||\n                    classify_object<T>::value ==\n                        object_category::integer_constructible,\n                detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n  return \"INT\";\n}\n\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::unsigned_integral,\n                                  detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n  return \"UINT\";\n}\n\ntemplate <\n    typename T,\n    enable_if_t<classify_object<T>::value == object_category::floating_point ||\n                    classify_object<T>::value ==\n                        object_category::number_constructible ||\n                    classify_object<T>::value ==\n                        object_category::double_constructible,\n                detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n  return \"FLOAT\";\n}\n\n/// Print name for enumeration types\ntemplate <typename T,\n          enable_if_t<classify_object<T>::value == object_category::enumeration,\n                      detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n  return \"ENUM\";\n}\n\n/// Print name for enumeration types\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::boolean_value,\n                                  detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n  return \"BOOLEAN\";\n}\n\n/// Print name for enumeration types\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::complex_number,\n                                  detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n  return \"COMPLEX\";\n}\n\n/// Print for all other types\ntemplate <typename T,\n          enable_if_t<classify_object<T>::value >=\n                              object_category::string_assignable &&\n                          classify_object<T>::value <= object_category::other,\n                      detail::enabler> = detail::dummy>\nconstexpr const char *type_name() {\n  return \"TEXT\";\n}\n/// typename for tuple value\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                          object_category::tuple_value &&\n                                      type_count_base<T>::value >= 2,\n                                  detail::enabler> = detail::dummy>\nstd::string type_name();  // forward declaration\n\n/// Generate type name for a wrapper or container value\ntemplate <\n    typename T,\n    enable_if_t<classify_object<T>::value == object_category::container_value ||\n                    classify_object<T>::value == object_category::wrapper_value,\n                detail::enabler> = detail::dummy>\nstd::string type_name();  // forward declaration\n\n/// Print name for single element tuple types\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                          object_category::tuple_value &&\n                                      type_count_base<T>::value == 1,\n                                  detail::enabler> = detail::dummy>\ninline std::string type_name() {\n  return type_name<\n      typename std::decay<typename std::tuple_element<0, T>::type>::type>();\n}\n\n/// Empty string if the index > tuple size\ntemplate <typename T, std::size_t I>\ninline\n    typename std::enable_if<I == type_count_base<T>::value, std::string>::type\n    tuple_name() {\n  return std::string{};\n}\n\n/// Recursively generate the tuple type name\ntemplate <typename T, std::size_t I>\ninline\n    typename std::enable_if<(I < type_count_base<T>::value), std::string>::type\n    tuple_name() {\n  auto str = std::string{type_name<typename std::decay<\n                 typename std::tuple_element<I, T>::type>::type>()} +\n             ',' + tuple_name<T, I + 1>();\n  if (str.back() == ',') str.pop_back();\n  return str;\n}\n\n/// Print type name for tuples with 2 or more elements\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                          object_category::tuple_value &&\n                                      type_count_base<T>::value >= 2,\n                                  detail::enabler>>\ninline std::string type_name() {\n  auto tname = std::string(1, '[') + tuple_name<T, 0>();\n  tname.push_back(']');\n  return tname;\n}\n\n/// get the type name for a type that has a value_type member\ntemplate <\n    typename T,\n    enable_if_t<classify_object<T>::value == object_category::container_value ||\n                    classify_object<T>::value == object_category::wrapper_value,\n                detail::enabler>>\ninline std::string type_name() {\n  return type_name<typename T::value_type>();\n}\n\n// Lexical cast\n\n/// Convert to an unsigned integral\ntemplate <typename T, enable_if_t<std::is_unsigned<T>::value, detail::enabler> =\n                          detail::dummy>\nbool integral_conversion(const std::string &input, T &output) noexcept {\n  if (input.empty() || input.front() == '-') {\n    return false;\n  }\n  char *val = nullptr;\n  errno = 0;\n  std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0);\n  if (errno == ERANGE) {\n    return false;\n  }\n  output = static_cast<T>(output_ll);\n  if (val == (input.c_str() + input.size()) &&\n      static_cast<std::uint64_t>(output) == output_ll) {\n    return true;\n  }\n  val = nullptr;\n  std::int64_t output_sll = std::strtoll(input.c_str(), &val, 0);\n  if (val == (input.c_str() + input.size())) {\n    output = (output_sll < 0) ? static_cast<T>(0) : static_cast<T>(output_sll);\n    return (static_cast<std::int64_t>(output) == output_sll);\n  }\n  return false;\n}\n\n/// Convert to a signed integral\ntemplate <typename T, enable_if_t<std::is_signed<T>::value, detail::enabler> =\n                          detail::dummy>\nbool integral_conversion(const std::string &input, T &output) noexcept {\n  if (input.empty()) {\n    return false;\n  }\n  char *val = nullptr;\n  errno = 0;\n  std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0);\n  if (errno == ERANGE) {\n    return false;\n  }\n  output = static_cast<T>(output_ll);\n  if (val == (input.c_str() + input.size()) &&\n      static_cast<std::int64_t>(output) == output_ll) {\n    return true;\n  }\n  if (input == \"true\") {\n    // this is to deal with a few oddities with flags and wrapper int types\n    output = static_cast<T>(1);\n    return true;\n  }\n  return false;\n}\n\n/// Convert a flag into an integer value  typically binary flags\ninline std::int64_t to_flag_value(std::string val) {\n  static const std::string trueString(\"true\");\n  static const std::string falseString(\"false\");\n  if (val == trueString) {\n    return 1;\n  }\n  if (val == falseString) {\n    return -1;\n  }\n  val = detail::to_lower(val);\n  std::int64_t ret = 0;\n  if (val.size() == 1) {\n    if (val[0] >= '1' && val[0] <= '9') {\n      return (static_cast<std::int64_t>(val[0]) - '0');\n    }\n    switch (val[0]) {\n      case '0':\n      case 'f':\n      case 'n':\n      case '-':\n        ret = -1;\n        break;\n      case 't':\n      case 'y':\n      case '+':\n        ret = 1;\n        break;\n      default:\n        throw std::invalid_argument(\"unrecognized character\");\n    }\n    return ret;\n  }\n  if (val == trueString || val == \"on\" || val == \"yes\" || val == \"enable\") {\n    ret = 1;\n  } else if (val == falseString || val == \"off\" || val == \"no\" ||\n             val == \"disable\") {\n    ret = -1;\n  } else {\n    ret = std::stoll(val);\n  }\n  return ret;\n}\n\n/// Integer conversion\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                          object_category::integral_value ||\n                                      classify_object<T>::value ==\n                                          object_category::unsigned_integral,\n                                  detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  return integral_conversion(input, output);\n}\n\n/// char values\ntemplate <typename T,\n          enable_if_t<classify_object<T>::value == object_category::char_value,\n                      detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  if (input.size() == 1) {\n    output = static_cast<T>(input[0]);\n    return true;\n  }\n  return integral_conversion(input, output);\n}\n\n/// Boolean values\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::boolean_value,\n                                  detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  try {\n    auto out = to_flag_value(input);\n    output = (out > 0);\n    return true;\n  } catch (const std::invalid_argument &) {\n    return false;\n  } catch (const std::out_of_range &) {\n    // if the number is out of the range of a 64 bit value then it is still a\n    // number and for this purpose is still valid all we care about the sign\n    output = (input[0] != '-');\n    return true;\n  }\n}\n\n/// Floats\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::floating_point,\n                                  detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  if (input.empty()) {\n    return false;\n  }\n  char *val = nullptr;\n  auto output_ld = std::strtold(input.c_str(), &val);\n  output = static_cast<T>(output_ld);\n  return val == (input.c_str() + input.size());\n}\n\n/// complex\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::complex_number,\n                                  detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  using XC = typename wrapped_type<T, double>::type;\n  XC x{0.0}, y{0.0};\n  auto str1 = input;\n  bool worked = false;\n  auto nloc = str1.find_last_of(\"+-\");\n  if (nloc != std::string::npos && nloc > 0) {\n    worked = lexical_cast(str1.substr(0, nloc), x);\n    str1 = str1.substr(nloc);\n    if (str1.back() == 'i' || str1.back() == 'j') str1.pop_back();\n    worked = worked && lexical_cast(str1, y);\n  } else {\n    if (str1.back() == 'i' || str1.back() == 'j') {\n      str1.pop_back();\n      worked = lexical_cast(str1, y);\n      x = XC{0};\n    } else {\n      worked = lexical_cast(str1, x);\n      y = XC{0};\n    }\n  }\n  if (worked) {\n    output = T{x, y};\n    return worked;\n  }\n  return from_stream(input, output);\n}\n\n/// String and similar direct assignment\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::string_assignable,\n                                  detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  output = input;\n  return true;\n}\n\n/// String and similar constructible and copy assignment\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::string_constructible,\n                                  detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  output = T(input);\n  return true;\n}\n\n/// Enumerations\ntemplate <typename T,\n          enable_if_t<classify_object<T>::value == object_category::enumeration,\n                      detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  typename std::underlying_type<T>::type val;\n  if (!integral_conversion(input, val)) {\n    return false;\n  }\n  output = static_cast<T>(val);\n  return true;\n}\n\n/// wrapper types\ntemplate <\n    typename T,\n    enable_if_t<classify_object<T>::value == object_category::wrapper_value &&\n                    std::is_assignable<T &, typename T::value_type>::value,\n                detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  typename T::value_type val;\n  if (lexical_cast(input, val)) {\n    output = val;\n    return true;\n  }\n  return from_stream(input, output);\n}\n\ntemplate <\n    typename T,\n    enable_if_t<classify_object<T>::value == object_category::wrapper_value &&\n                    !std::is_assignable<T &, typename T::value_type>::value &&\n                    std::is_assignable<T &, T>::value,\n                detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  typename T::value_type val;\n  if (lexical_cast(input, val)) {\n    output = T{val};\n    return true;\n  }\n  return from_stream(input, output);\n}\n\n/// Assignable from double or int\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::number_constructible,\n                                  detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  int val = 0;\n  if (integral_conversion(input, val)) {\n    output = T(val);\n    return true;\n  }\n\n  double dval = 0.0;\n  if (lexical_cast(input, dval)) {\n    output = T{dval};\n    return true;\n  }\n\n  return from_stream(input, output);\n}\n\n/// Assignable from int\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::integer_constructible,\n                                  detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  int val = 0;\n  if (integral_conversion(input, val)) {\n    output = T(val);\n    return true;\n  }\n  return from_stream(input, output);\n}\n\n/// Assignable from double\ntemplate <typename T, enable_if_t<classify_object<T>::value ==\n                                      object_category::double_constructible,\n                                  detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  double val = 0.0;\n  if (lexical_cast(input, val)) {\n    output = T{val};\n    return true;\n  }\n  return from_stream(input, output);\n}\n\n/// Non-string convertible from an int\ntemplate <typename T,\n          enable_if_t<classify_object<T>::value == object_category::other &&\n                          std::is_assignable<T &, int>::value,\n                      detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  int val = 0;\n  if (integral_conversion(input, val)) {\n#ifdef _MSC_VER\n#pragma warning(push)\n#pragma warning(disable : 4800)\n#endif\n    // with Atomic<XX> this could produce a warning due to the conversion but if\n    // atomic gets here it is an old style so will most likely still work\n    output = val;\n#ifdef _MSC_VER\n#pragma warning(pop)\n#endif\n    return true;\n  }\n  // LCOV_EXCL_START\n  // This version of cast is only used for odd cases in an older compilers the\n  // fail over from_stream is tested elsewhere an not relevant for coverage here\n  return from_stream(input, output);\n  // LCOV_EXCL_STOP\n}\n\n/// Non-string parsable by a stream\ntemplate <typename T,\n          enable_if_t<classify_object<T>::value == object_category::other &&\n                          !std::is_assignable<T &, int>::value,\n                      detail::enabler> = detail::dummy>\nbool lexical_cast(const std::string &input, T &output) {\n  static_assert(is_istreamable<T>::value,\n                \"option object type must have a lexical cast overload or \"\n                \"streaming input operator(>>) defined, if it \"\n                \"is convertible from another type use the add_option<T, \"\n                \"XC>(...) with XC being the known type\");\n  return from_stream(input, output);\n}\n\n/// Assign a value through lexical cast operations\n/// Strings can be empty so we need to do a little different\ntemplate <typename AssignTo, typename ConvertTo,\n          enable_if_t<std::is_same<AssignTo, ConvertTo>::value &&\n                          (classify_object<AssignTo>::value ==\n                               object_category::string_assignable ||\n                           classify_object<AssignTo>::value ==\n                               object_category::string_constructible),\n                      detail::enabler> = detail::dummy>\nbool lexical_assign(const std::string &input, AssignTo &output) {\n  return lexical_cast(input, output);\n}\n\n/// Assign a value through lexical cast operations\ntemplate <typename AssignTo, typename ConvertTo,\n          enable_if_t<std::is_same<AssignTo, ConvertTo>::value &&\n                          std::is_assignable<AssignTo &, AssignTo>::value &&\n                          classify_object<AssignTo>::value !=\n                              object_category::string_assignable &&\n                          classify_object<AssignTo>::value !=\n                              object_category::string_constructible,\n                      detail::enabler> = detail::dummy>\nbool lexical_assign(const std::string &input, AssignTo &output) {\n  if (input.empty()) {\n    output = AssignTo{};\n    return true;\n  }\n\n  return lexical_cast(input, output);\n}\n\n/// Assign a value through lexical cast operations\ntemplate <typename AssignTo, typename ConvertTo,\n          enable_if_t<std::is_same<AssignTo, ConvertTo>::value &&\n                          !std::is_assignable<AssignTo &, AssignTo>::value &&\n                          classify_object<AssignTo>::value ==\n                              object_category::wrapper_value,\n                      detail::enabler> = detail::dummy>\nbool lexical_assign(const std::string &input, AssignTo &output) {\n  if (input.empty()) {\n    typename AssignTo::value_type emptyVal{};\n    output = emptyVal;\n    return true;\n  }\n  return lexical_cast(input, output);\n}\n\n/// Assign a value through lexical cast operations for int compatible values\n/// mainly for atomic operations on some compilers\ntemplate <typename AssignTo, typename ConvertTo,\n          enable_if_t<std::is_same<AssignTo, ConvertTo>::value &&\n                          !std::is_assignable<AssignTo &, AssignTo>::value &&\n                          classify_object<AssignTo>::value !=\n                              object_category::wrapper_value &&\n                          std::is_assignable<AssignTo &, int>::value,\n                      detail::enabler> = detail::dummy>\nbool lexical_assign(const std::string &input, AssignTo &output) {\n  if (input.empty()) {\n    output = 0;\n    return true;\n  }\n  int val = 0;\n  if (lexical_cast(input, val)) {\n    output = val;\n    return true;\n  }\n  return false;\n}\n\n/// Assign a value converted from a string in lexical cast to the output value\n/// directly\ntemplate <typename AssignTo, typename ConvertTo,\n          enable_if_t<!std::is_same<AssignTo, ConvertTo>::value &&\n                          std::is_assignable<AssignTo &, ConvertTo &>::value,\n                      detail::enabler> = detail::dummy>\nbool lexical_assign(const std::string &input, AssignTo &output) {\n  ConvertTo val{};\n  bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true;\n  if (parse_result) {\n    output = val;\n  }\n  return parse_result;\n}\n\n/// Assign a value from a lexical cast through constructing a value and move\n/// assigning it\ntemplate <typename AssignTo, typename ConvertTo,\n          enable_if_t<!std::is_same<AssignTo, ConvertTo>::value &&\n                          !std::is_assignable<AssignTo &, ConvertTo &>::value &&\n                          std::is_move_assignable<AssignTo>::value,\n                      detail::enabler> = detail::dummy>\nbool lexical_assign(const std::string &input, AssignTo &output) {\n  ConvertTo val{};\n  bool parse_result = input.empty() ? true : lexical_cast(input, val);\n  if (parse_result) {\n    output = AssignTo(\n        val);  // use () form of constructor to allow some implicit conversions\n  }\n  return parse_result;\n}\n\n/// primary lexical conversion operation, 1 string to 1 type of some kind\ntemplate <\n    typename AssignTo, typename ConvertTo,\n    enable_if_t<classify_object<ConvertTo>::value <= object_category::other &&\n                    classify_object<AssignTo>::value <=\n                        object_category::wrapper_value,\n                detail::enabler> = detail::dummy>\nbool lexical_conversion(const std::vector<std ::string> &strings,\n                        AssignTo &output) {\n  return lexical_assign<AssignTo, ConvertTo>(strings[0], output);\n}\n\n/// Lexical conversion if there is only one element but the conversion type is\n/// for two, then call a two element constructor\ntemplate <typename AssignTo, typename ConvertTo,\n          enable_if_t<(type_count<AssignTo>::value <= 2) &&\n                          expected_count<AssignTo>::value == 1 &&\n                          is_tuple_like<ConvertTo>::value &&\n                          type_count_base<ConvertTo>::value == 2,\n                      detail::enabler> = detail::dummy>\nbool lexical_conversion(const std::vector<std ::string> &strings,\n                        AssignTo &output) {\n  // the remove const is to handle pair types coming from a container\n  typename std::remove_const<\n      typename std::tuple_element<0, ConvertTo>::type>::type v1;\n  typename std::tuple_element<1, ConvertTo>::type v2;\n  bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[0], v1);\n  if (strings.size() > 1) {\n    retval =\n        retval && lexical_assign<decltype(v2), decltype(v2)>(strings[1], v2);\n  }\n  if (retval) {\n    output = AssignTo{v1, v2};\n  }\n  return retval;\n}\n\n/// Lexical conversion of a container types of single elements\ntemplate <class AssignTo, class ConvertTo,\n          enable_if_t<is_mutable_container<AssignTo>::value &&\n                          is_mutable_container<ConvertTo>::value &&\n                          type_count<ConvertTo>::value == 1,\n                      detail::enabler> = detail::dummy>\nbool lexical_conversion(const std::vector<std ::string> &strings,\n                        AssignTo &output) {\n  output.erase(output.begin(), output.end());\n  if (strings.size() == 1 && strings[0] == \"{}\") {\n    return true;\n  }\n  bool skip_remaining = false;\n  if (strings.size() == 2 && strings[0] == \"{}\" && is_separator(strings[1])) {\n    skip_remaining = true;\n  }\n  for (const auto &elem : strings) {\n    typename AssignTo::value_type out;\n    bool retval = lexical_assign<typename AssignTo::value_type,\n                                 typename ConvertTo::value_type>(elem, out);\n    if (!retval) {\n      return false;\n    }\n    output.insert(output.end(), std::move(out));\n    if (skip_remaining) {\n      break;\n    }\n  }\n  return (!output.empty());\n}\n\n/// Lexical conversion for complex types\ntemplate <\n    class AssignTo, class ConvertTo,\n    enable_if_t<is_complex<ConvertTo>::value, detail::enabler> = detail::dummy>\nbool lexical_conversion(const std::vector<std::string> &strings,\n                        AssignTo &output) {\n  if (strings.size() >= 2 && !strings[1].empty()) {\n    using XC2 = typename wrapped_type<ConvertTo, double>::type;\n    XC2 x{0.0}, y{0.0};\n    auto str1 = strings[1];\n    if (str1.back() == 'i' || str1.back() == 'j') {\n      str1.pop_back();\n    }\n    auto worked = lexical_cast(strings[0], x) && lexical_cast(str1, y);\n    if (worked) {\n      output = ConvertTo{x, y};\n    }\n    return worked;\n  }\n  return lexical_assign<AssignTo, ConvertTo>(strings[0], output);\n}\n\n/// Conversion to a vector type using a particular single type as the conversion\n/// type\ntemplate <class AssignTo, class ConvertTo,\n          enable_if_t<is_mutable_container<AssignTo>::value &&\n                          (expected_count<ConvertTo>::value == 1) &&\n                          (type_count<ConvertTo>::value == 1),\n                      detail::enabler> = detail::dummy>\nbool lexical_conversion(const std::vector<std ::string> &strings,\n                        AssignTo &output) {\n  bool retval = true;\n  output.clear();\n  output.reserve(strings.size());\n  for (const auto &elem : strings) {\n    output.emplace_back();\n    retval = retval && lexical_assign<typename AssignTo::value_type, ConvertTo>(\n                           elem, output.back());\n  }\n  return (!output.empty()) && retval;\n}\n\n// forward declaration\n\n/// Lexical conversion of a container types with conversion type of two elements\ntemplate <class AssignTo, class ConvertTo,\n          enable_if_t<is_mutable_container<AssignTo>::value &&\n                          is_mutable_container<ConvertTo>::value &&\n                          type_count_base<ConvertTo>::value == 2,\n                      detail::enabler> = detail::dummy>\nbool lexical_conversion(std::vector<std::string> strings, AssignTo &output);\n\n/// Lexical conversion of a vector types with type_size >2 forward declaration\ntemplate <class AssignTo, class ConvertTo,\n          enable_if_t<is_mutable_container<AssignTo>::value &&\n                          is_mutable_container<ConvertTo>::value &&\n                          type_count_base<ConvertTo>::value != 2 &&\n                          ((type_count<ConvertTo>::value > 2) ||\n                           (type_count<ConvertTo>::value >\n                            type_count_base<ConvertTo>::value)),\n                      detail::enabler> = detail::dummy>\nbool lexical_conversion(const std::vector<std::string> &strings,\n                        AssignTo &output);\n\n/// Conversion for tuples\ntemplate <class AssignTo, class ConvertTo,\n          enable_if_t<is_tuple_like<AssignTo>::value &&\n                          is_tuple_like<ConvertTo>::value &&\n                          (type_count_base<ConvertTo>::value !=\n                               type_count<ConvertTo>::value ||\n                           type_count<ConvertTo>::value > 2),\n                      detail::enabler> = detail::dummy>\nbool lexical_conversion(const std::vector<std::string> &strings,\n                        AssignTo &output);  // forward declaration\n\n/// Conversion for operations where the assigned type is some class but the\n/// conversion is a mutable container or large tuple\ntemplate <typename AssignTo, typename ConvertTo,\n          enable_if_t<!is_tuple_like<AssignTo>::value &&\n                          !is_mutable_container<AssignTo>::value &&\n                          classify_object<ConvertTo>::value !=\n                              object_category::wrapper_value &&\n                          (is_mutable_container<ConvertTo>::value ||\n                           type_count<ConvertTo>::value > 2),\n                      detail::enabler> = detail::dummy>\nbool lexical_conversion(const std::vector<std ::string> &strings,\n                        AssignTo &output) {\n  if (strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) {\n    ConvertTo val;\n    auto retval = lexical_conversion<ConvertTo, ConvertTo>(strings, val);\n    output = AssignTo{val};\n    return retval;\n  }\n  output = AssignTo{};\n  return true;\n}\n\n/// function template for converting tuples if the static Index is greater than\n/// the tuple size\ntemplate <class AssignTo, class ConvertTo, std::size_t I>\ninline\n    typename std::enable_if<(I >= type_count_base<AssignTo>::value), bool>::type\n    tuple_conversion(const std::vector<std::string> &, AssignTo &) {\n  return true;\n}\n\n/// Conversion of a tuple element where the type size ==1 and not a mutable\n/// container\ntemplate <class AssignTo, class ConvertTo>\ninline typename std::enable_if<!is_mutable_container<ConvertTo>::value &&\n                                   type_count<ConvertTo>::value == 1,\n                               bool>::type\ntuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {\n  auto retval = lexical_assign<AssignTo, ConvertTo>(strings[0], output);\n  strings.erase(strings.begin());\n  return retval;\n}\n\n/// Conversion of a tuple element where the type size !=1 but the size is fixed\n/// and not a mutable container\ntemplate <class AssignTo, class ConvertTo>\ninline typename std::enable_if<!is_mutable_container<ConvertTo>::value &&\n                                   (type_count<ConvertTo>::value > 1) &&\n                                   type_count<ConvertTo>::value ==\n                                       type_count_min<ConvertTo>::value,\n                               bool>::type\ntuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {\n  auto retval = lexical_conversion<AssignTo, ConvertTo>(strings, output);\n  strings.erase(strings.begin(),\n                strings.begin() + type_count<ConvertTo>::value);\n  return retval;\n}\n\n/// Conversion of a tuple element where the type is a mutable container or a\n/// type with different min and max type sizes\ntemplate <class AssignTo, class ConvertTo>\ninline typename std::enable_if<is_mutable_container<ConvertTo>::value ||\n                                   type_count<ConvertTo>::value !=\n                                       type_count_min<ConvertTo>::value,\n                               bool>::type\ntuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {\n  std::size_t index{subtype_count_min<ConvertTo>::value};\n  const std::size_t mx_count{subtype_count<ConvertTo>::value};\n  const std::size_t mx{(std::max)(mx_count, strings.size())};\n\n  while (index < mx) {\n    if (is_separator(strings[index])) {\n      break;\n    }\n    ++index;\n  }\n  bool retval = lexical_conversion<AssignTo, ConvertTo>(\n      std::vector<std::string>(\n          strings.begin(),\n          strings.begin() + static_cast<std::ptrdiff_t>(index)),\n      output);\n  strings.erase(strings.begin(),\n                strings.begin() + static_cast<std::ptrdiff_t>(index) + 1);\n  return retval;\n}\n\n/// Tuple conversion operation\ntemplate <class AssignTo, class ConvertTo, std::size_t I>\ninline\n    typename std::enable_if<(I < type_count_base<AssignTo>::value), bool>::type\n    tuple_conversion(std::vector<std::string> strings, AssignTo &output) {\n  bool retval = true;\n  using ConvertToElement =\n      typename std::conditional<is_tuple_like<ConvertTo>::value,\n                                typename std::tuple_element<I, ConvertTo>::type,\n                                ConvertTo>::type;\n  if (!strings.empty()) {\n    retval =\n        retval &&\n        tuple_type_conversion<typename std::tuple_element<I, AssignTo>::type,\n                              ConvertToElement>(strings, std::get<I>(output));\n  }\n  retval = retval && tuple_conversion<AssignTo, ConvertTo, I + 1>(\n                         std::move(strings), output);\n  return retval;\n}\n\n/// Lexical conversion of a container types with tuple elements of size 2\ntemplate <class AssignTo, class ConvertTo,\n          enable_if_t<is_mutable_container<AssignTo>::value &&\n                          is_mutable_container<ConvertTo>::value &&\n                          type_count_base<ConvertTo>::value == 2,\n                      detail::enabler>>\nbool lexical_conversion(std::vector<std::string> strings, AssignTo &output) {\n  output.clear();\n  while (!strings.empty()) {\n    typename std::remove_const<typename std::tuple_element<\n        0, typename ConvertTo::value_type>::type>::type v1;\n    typename std::tuple_element<1, typename ConvertTo::value_type>::type v2;\n    bool retval =\n        tuple_type_conversion<decltype(v1), decltype(v1)>(strings, v1);\n    if (!strings.empty()) {\n      retval = retval &&\n               tuple_type_conversion<decltype(v2), decltype(v2)>(strings, v2);\n    }\n    if (retval) {\n      output.insert(output.end(), typename AssignTo::value_type{v1, v2});\n    } else {\n      return false;\n    }\n  }\n  return (!output.empty());\n}\n\n/// lexical conversion of tuples with type count>2 or tuples of types of some\n/// element with a type size>=2\ntemplate <class AssignTo, class ConvertTo,\n          enable_if_t<is_tuple_like<AssignTo>::value &&\n                          is_tuple_like<ConvertTo>::value &&\n                          (type_count_base<ConvertTo>::value !=\n                               type_count<ConvertTo>::value ||\n                           type_count<ConvertTo>::value > 2),\n                      detail::enabler>>\nbool lexical_conversion(const std::vector<std ::string> &strings,\n                        AssignTo &output) {\n  static_assert(\n      !is_tuple_like<ConvertTo>::value ||\n          type_count_base<AssignTo>::value == type_count_base<ConvertTo>::value,\n      \"if the conversion type is defined as a tuple it must be the same size \"\n      \"as the type you are converting to\");\n  return tuple_conversion<AssignTo, ConvertTo, 0>(strings, output);\n}\n\n/// Lexical conversion of a vector types for everything but tuples of two\n/// elements and types of size 1\ntemplate <class AssignTo, class ConvertTo,\n          enable_if_t<is_mutable_container<AssignTo>::value &&\n                          is_mutable_container<ConvertTo>::value &&\n                          type_count_base<ConvertTo>::value != 2 &&\n                          ((type_count<ConvertTo>::value > 2) ||\n                           (type_count<ConvertTo>::value >\n                            type_count_base<ConvertTo>::value)),\n                      detail::enabler>>\nbool lexical_conversion(const std::vector<std ::string> &strings,\n                        AssignTo &output) {\n  bool retval = true;\n  output.clear();\n  std::vector<std::string> temp;\n  std::size_t ii{0};\n  std::size_t icount{0};\n  std::size_t xcm{type_count<ConvertTo>::value};\n  auto ii_max = strings.size();\n  while (ii < ii_max) {\n    temp.push_back(strings[ii]);\n    ++ii;\n    ++icount;\n    if (icount == xcm || is_separator(temp.back()) || ii == ii_max) {\n      if (static_cast<int>(xcm) > type_count_min<ConvertTo>::value &&\n          is_separator(temp.back())) {\n        temp.pop_back();\n      }\n      typename AssignTo::value_type temp_out;\n      retval = retval && lexical_conversion<typename AssignTo::value_type,\n                                            typename ConvertTo::value_type>(\n                             temp, temp_out);\n      temp.clear();\n      if (!retval) {\n        return false;\n      }\n      output.insert(output.end(), std::move(temp_out));\n      icount = 0;\n    }\n  }\n  return retval;\n}\n\n/// conversion for wrapper types\ntemplate <typename AssignTo, class ConvertTo,\n          enable_if_t<classify_object<ConvertTo>::value ==\n                              object_category::wrapper_value &&\n                          std::is_assignable<ConvertTo &, ConvertTo>::value,\n                      detail::enabler> = detail::dummy>\nbool lexical_conversion(const std::vector<std::string> &strings,\n                        AssignTo &output) {\n  if (strings.empty() || strings.front().empty()) {\n    output = ConvertTo{};\n    return true;\n  }\n  typename ConvertTo::value_type val;\n  if (lexical_conversion<typename ConvertTo::value_type,\n                         typename ConvertTo::value_type>(strings, val)) {\n    output = ConvertTo{val};\n    return true;\n  }\n  return false;\n}\n\n/// conversion for wrapper types\ntemplate <typename AssignTo, class ConvertTo,\n          enable_if_t<classify_object<ConvertTo>::value ==\n                              object_category::wrapper_value &&\n                          !std::is_assignable<AssignTo &, ConvertTo>::value,\n                      detail::enabler> = detail::dummy>\nbool lexical_conversion(const std::vector<std::string> &strings,\n                        AssignTo &output) {\n  using ConvertType = typename ConvertTo::value_type;\n  if (strings.empty() || strings.front().empty()) {\n    output = ConvertType{};\n    return true;\n  }\n  ConvertType val;\n  if (lexical_conversion<typename ConvertTo::value_type,\n                         typename ConvertTo::value_type>(strings, val)) {\n    output = val;\n    return true;\n  }\n  return false;\n}\n\n/// Sum a vector of strings\ninline std::string sum_string_vector(const std::vector<std::string> &values) {\n  double val{0.0};\n  bool fail{false};\n  std::string output;\n  for (const auto &arg : values) {\n    double tv{0.0};\n    auto comp = lexical_cast(arg, tv);\n    if (!comp) {\n      try {\n        tv = static_cast<double>(detail::to_flag_value(arg));\n      } catch (const std::exception &) {\n        fail = true;\n        break;\n      }\n    }\n    val += tv;\n  }\n  if (fail) {\n    for (const auto &arg : values) {\n      output.append(arg);\n    }\n  } else {\n    if (val <=\n            static_cast<double>((std::numeric_limits<std::int64_t>::min)()) ||\n        val >=\n            static_cast<double>((std::numeric_limits<std::int64_t>::max)()) ||\n        std::ceil(val) == std::floor(val)) {\n      output = detail::value_string(static_cast<int64_t>(val));\n    } else {\n      output = detail::value_string(val);\n    }\n  }\n  return output;\n}\n\n}  // namespace detail\n\nnamespace detail {\n\n// Returns false if not a short option. Otherwise, sets opt name and rest and\n// returns true\nCLI11_INLINE bool split_short(const std::string &current, std::string &name,\n                              std::string &rest);\n\n// Returns false if not a long option. Otherwise, sets opt name and other side\n// of = and returns true\nCLI11_INLINE bool split_long(const std::string &current, std::string &name,\n                             std::string &value);\n\n// Returns false if not a windows style option. Otherwise, sets opt name and\n// value and returns true\nCLI11_INLINE bool split_windows_style(const std::string &current,\n                                      std::string &name, std::string &value);\n\n// Splits a string into multiple long and short names\nCLI11_INLINE std::vector<std::string> split_names(std::string current);\n\n/// extract default flag values either {def} or starting with a !\nCLI11_INLINE std::vector<std::pair<std::string, std::string>>\nget_default_flag_values(const std::string &str);\n\n/// Get a vector of short names, one of long names, and a single name\nCLI11_INLINE\n    std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>\n    get_names(const std::vector<std::string> &input);\n\n}  // namespace detail\n\nnamespace detail {\n\nCLI11_INLINE bool split_short(const std::string &current, std::string &name,\n                              std::string &rest) {\n  if (current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) {\n    name = current.substr(1, 1);\n    rest = current.substr(2);\n    return true;\n  }\n  return false;\n}\n\nCLI11_INLINE bool split_long(const std::string &current, std::string &name,\n                             std::string &value) {\n  if (current.size() > 2 && current.substr(0, 2) == \"--\" &&\n      valid_first_char(current[2])) {\n    auto loc = current.find_first_of('=');\n    if (loc != std::string::npos) {\n      name = current.substr(2, loc - 2);\n      value = current.substr(loc + 1);\n    } else {\n      name = current.substr(2);\n      value = \"\";\n    }\n    return true;\n  }\n  return false;\n}\n\nCLI11_INLINE bool split_windows_style(const std::string &current,\n                                      std::string &name, std::string &value) {\n  if (current.size() > 1 && current[0] == '/' && valid_first_char(current[1])) {\n    auto loc = current.find_first_of(':');\n    if (loc != std::string::npos) {\n      name = current.substr(1, loc - 1);\n      value = current.substr(loc + 1);\n    } else {\n      name = current.substr(1);\n      value = \"\";\n    }\n    return true;\n  }\n  return false;\n}\n\nCLI11_INLINE std::vector<std::string> split_names(std::string current) {\n  std::vector<std::string> output;\n  std::size_t val = 0;\n  while ((val = current.find(',')) != std::string::npos) {\n    output.push_back(trim_copy(current.substr(0, val)));\n    current = current.substr(val + 1);\n  }\n  output.push_back(trim_copy(current));\n  return output;\n}\n\nCLI11_INLINE std::vector<std::pair<std::string, std::string>>\nget_default_flag_values(const std::string &str) {\n  std::vector<std::string> flags = split_names(str);\n  flags.erase(\n      std::remove_if(flags.begin(), flags.end(),\n                     [](const std::string &name) {\n                       return (\n                           (name.empty()) ||\n                           (!(((name.find_first_of('{') != std::string::npos) &&\n                               (name.back() == '}')) ||\n                              (name[0] == '!'))));\n                     }),\n      flags.end());\n  std::vector<std::pair<std::string, std::string>> output;\n  output.reserve(flags.size());\n  for (auto &flag : flags) {\n    auto def_start = flag.find_first_of('{');\n    std::string defval = \"false\";\n    if ((def_start != std::string::npos) && (flag.back() == '}')) {\n      defval = flag.substr(def_start + 1);\n      defval.pop_back();\n      flag.erase(\n          def_start,\n          std::string::npos);  // NOLINT(readability-suspicious-call-argument)\n    }\n    flag.erase(0, flag.find_first_not_of(\"-!\"));\n    output.emplace_back(flag, defval);\n  }\n  return output;\n}\n\nCLI11_INLINE\n    std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>\n    get_names(const std::vector<std::string> &input) {\n  std::vector<std::string> short_names;\n  std::vector<std::string> long_names;\n  std::string pos_name;\n\n  for (std::string name : input) {\n    if (name.length() == 0) {\n      continue;\n    }\n    if (name.length() > 1 && name[0] == '-' && name[1] != '-') {\n      if (name.length() == 2 && valid_first_char(name[1]))\n        short_names.emplace_back(1, name[1]);\n      else\n        throw BadNameString::OneCharName(name);\n    } else if (name.length() > 2 && name.substr(0, 2) == \"--\") {\n      name = name.substr(2);\n      if (valid_name_string(name))\n        long_names.push_back(name);\n      else\n        throw BadNameString::BadLongName(name);\n    } else if (name == \"-\" || name == \"--\") {\n      throw BadNameString::DashesOnly(name);\n    } else {\n      if (pos_name.length() > 0)\n        throw BadNameString::MultiPositionalNames(name);\n      pos_name = name;\n    }\n  }\n\n  return std::make_tuple(short_names, long_names, pos_name);\n}\n\n}  // namespace detail\n\nclass App;\n\n/// Holds values to load into Options\nstruct ConfigItem {\n  /// This is the list of parents\n  std::vector<std::string> parents{};\n\n  /// This is the name\n  std::string name{};\n\n  /// Listing of inputs\n  std::vector<std::string> inputs{};\n\n  /// The list of parents and name joined by \".\"\n  CLI11_NODISCARD std::string fullname() const {\n    std::vector<std::string> tmp = parents;\n    tmp.emplace_back(name);\n    return detail::join(tmp, \".\");\n  }\n};\n\n/// This class provides a converter for configuration files.\nclass Config {\n protected:\n  std::vector<ConfigItem> items{};\n\n public:\n  /// Convert an app into a configuration\n  virtual std::string to_config(const App *, bool, bool, std::string) const = 0;\n\n  /// Convert a configuration into an app\n  virtual std::vector<ConfigItem> from_config(std::istream &) const = 0;\n\n  /// Get a flag value\n  CLI11_NODISCARD virtual std::string to_flag(const ConfigItem &item) const {\n    if (item.inputs.size() == 1) {\n      return item.inputs.at(0);\n    }\n    if (item.inputs.empty()) {\n      return \"{}\";\n    }\n    throw ConversionError::TooManyInputsFlag(\n        item.fullname());  // LCOV_EXCL_LINE\n  }\n\n  /// Parse a config file, throw an error (ParseError:ConfigParseError or\n  /// FileError) on failure\n  CLI11_NODISCARD std::vector<ConfigItem> from_file(\n      const std::string &name) const {\n    std::ifstream input{name};\n    if (!input.good()) throw FileError::Missing(name);\n\n    return from_config(input);\n  }\n\n  /// Virtual destructor\n  virtual ~Config() = default;\n};\n\n/// This converter works with INI/TOML files; to write INI files use ConfigINI\nclass ConfigBase : public Config {\n protected:\n  /// the character used for comments\n  char commentChar = '#';\n  /// the character used to start an array '\\0' is a default to not use\n  char arrayStart = '[';\n  /// the character used to end an array '\\0' is a default to not use\n  char arrayEnd = ']';\n  /// the character used to separate elements in an array\n  char arraySeparator = ',';\n  /// the character used separate the name from the value\n  char valueDelimiter = '=';\n  /// the character to use around strings\n  char stringQuote = '\"';\n  /// the character to use around single characters\n  char characterQuote = '\\'';\n  /// the maximum number of layers to allow\n  uint8_t maximumLayers{255};\n  /// the separator used to separator parent layers\n  char parentSeparatorChar{'.'};\n  /// Specify the configuration index to use for arrayed sections\n  int16_t configIndex{-1};\n  /// Specify the configuration section that should be used\n  std::string configSection{};\n\n public:\n  std::string to_config(const App * /*app*/, bool default_also,\n                        bool write_description,\n                        std::string prefix) const override;\n\n  std::vector<ConfigItem> from_config(std::istream &input) const override;\n  /// Specify the configuration for comment characters\n  ConfigBase *comment(char cchar) {\n    commentChar = cchar;\n    return this;\n  }\n  /// Specify the start and end characters for an array\n  ConfigBase *arrayBounds(char aStart, char aEnd) {\n    arrayStart = aStart;\n    arrayEnd = aEnd;\n    return this;\n  }\n  /// Specify the delimiter character for an array\n  ConfigBase *arrayDelimiter(char aSep) {\n    arraySeparator = aSep;\n    return this;\n  }\n  /// Specify the delimiter between a name and value\n  ConfigBase *valueSeparator(char vSep) {\n    valueDelimiter = vSep;\n    return this;\n  }\n  /// Specify the quote characters used around strings and characters\n  ConfigBase *quoteCharacter(char qString, char qChar) {\n    stringQuote = qString;\n    characterQuote = qChar;\n    return this;\n  }\n  /// Specify the maximum number of parents\n  ConfigBase *maxLayers(uint8_t layers) {\n    maximumLayers = layers;\n    return this;\n  }\n  /// Specify the separator to use for parent layers\n  ConfigBase *parentSeparator(char sep) {\n    parentSeparatorChar = sep;\n    return this;\n  }\n  /// get a reference to the configuration section\n  std::string &sectionRef() { return configSection; }\n  /// get the section\n  CLI11_NODISCARD const std::string &section() const { return configSection; }\n  /// specify a particular section of the configuration file to use\n  ConfigBase *section(const std::string &sectionName) {\n    configSection = sectionName;\n    return this;\n  }\n\n  /// get a reference to the configuration index\n  int16_t &indexRef() { return configIndex; }\n  /// get the section index\n  CLI11_NODISCARD int16_t index() const { return configIndex; }\n  /// specify a particular index in the section to use (-1) for all sections to\n  /// use\n  ConfigBase *index(int16_t sectionIndex) {\n    configIndex = sectionIndex;\n    return this;\n  }\n};\n\n/// the default Config is the TOML file format\nusing ConfigTOML = ConfigBase;\n\n/// ConfigINI generates a \"standard\" INI compliant output\nclass ConfigINI : public ConfigTOML {\n public:\n  ConfigINI() {\n    commentChar = ';';\n    arrayStart = '\\0';\n    arrayEnd = '\\0';\n    arraySeparator = ' ';\n    valueDelimiter = '=';\n  }\n};\n\nclass Option;\n\n/// @defgroup validator_group Validators\n\n/// @brief Some validators that are provided\n///\n/// These are simple `std::string(const std::string&)` validators that are\n/// useful. They return a string if the validation fails. A custom struct is\n/// provided, as well, with the same user semantics, but with the ability to\n/// provide a new type name.\n/// @{\n\n///\nclass Validator {\n protected:\n  /// This is the description function, if empty the description_ will be used\n  std::function<std::string()> desc_function_{[]() { return std::string{}; }};\n\n  /// This is the base function that is to be called.\n  /// Returns a string error message if validation fails.\n  std::function<std::string(std::string &)> func_{\n      [](std::string &) { return std::string{}; }};\n  /// The name for search purposes of the Validator\n  std::string name_{};\n  /// A Validator will only apply to an indexed value (-1 is all elements)\n  int application_index_ = -1;\n  /// Enable for Validator to allow it to be disabled if need be\n  bool active_{true};\n  /// specify that a validator should not modify the input\n  bool non_modifying_{false};\n\n  Validator(std::string validator_desc,\n            std::function<std::string(std::string &)> func)\n      : desc_function_([validator_desc]() { return validator_desc; }),\n        func_(std::move(func)) {}\n\n public:\n  Validator() = default;\n  /// Construct a Validator with just the description string\n  explicit Validator(std::string validator_desc)\n      : desc_function_([validator_desc]() { return validator_desc; }) {}\n  /// Construct Validator from basic information\n  Validator(std::function<std::string(std::string &)> op,\n            std::string validator_desc, std::string validator_name = \"\")\n      : desc_function_([validator_desc]() { return validator_desc; }),\n        func_(std::move(op)),\n        name_(std::move(validator_name)) {}\n  /// Set the Validator operation function\n  Validator &operation(std::function<std::string(std::string &)> op) {\n    func_ = std::move(op);\n    return *this;\n  }\n  /// This is the required operator for a Validator - provided to help\n  /// users (CLI11 uses the member `func` directly)\n  std::string operator()(std::string &str) const;\n\n  /// This is the required operator for a Validator - provided to help\n  /// users (CLI11 uses the member `func` directly)\n  std::string operator()(const std::string &str) const {\n    std::string value = str;\n    return (active_) ? func_(value) : std::string{};\n  }\n\n  /// Specify the type string\n  Validator &description(std::string validator_desc) {\n    desc_function_ = [validator_desc]() { return validator_desc; };\n    return *this;\n  }\n  /// Specify the type string\n  CLI11_NODISCARD Validator description(std::string validator_desc) const;\n\n  /// Generate type description information for the Validator\n  CLI11_NODISCARD std::string get_description() const {\n    if (active_) {\n      return desc_function_();\n    }\n    return std::string{};\n  }\n  /// Specify the type string\n  Validator &name(std::string validator_name) {\n    name_ = std::move(validator_name);\n    return *this;\n  }\n  /// Specify the type string\n  CLI11_NODISCARD Validator name(std::string validator_name) const {\n    Validator newval(*this);\n    newval.name_ = std::move(validator_name);\n    return newval;\n  }\n  /// Get the name of the Validator\n  CLI11_NODISCARD const std::string &get_name() const { return name_; }\n  /// Specify whether the Validator is active or not\n  Validator &active(bool active_val = true) {\n    active_ = active_val;\n    return *this;\n  }\n  /// Specify whether the Validator is active or not\n  CLI11_NODISCARD Validator active(bool active_val = true) const {\n    Validator newval(*this);\n    newval.active_ = active_val;\n    return newval;\n  }\n\n  /// Specify whether the Validator can be modifying or not\n  Validator &non_modifying(bool no_modify = true) {\n    non_modifying_ = no_modify;\n    return *this;\n  }\n  /// Specify the application index of a validator\n  Validator &application_index(int app_index) {\n    application_index_ = app_index;\n    return *this;\n  }\n  /// Specify the application index of a validator\n  CLI11_NODISCARD Validator application_index(int app_index) const {\n    Validator newval(*this);\n    newval.application_index_ = app_index;\n    return newval;\n  }\n  /// Get the current value of the application index\n  CLI11_NODISCARD int get_application_index() const {\n    return application_index_;\n  }\n  /// Get a boolean if the validator is active\n  CLI11_NODISCARD bool get_active() const { return active_; }\n\n  /// Get a boolean if the validator is allowed to modify the input returns true\n  /// if it can modify the input\n  CLI11_NODISCARD bool get_modifying() const { return !non_modifying_; }\n\n  /// Combining validators is a new validator. Type comes from left validator if\n  /// function, otherwise only set if the same.\n  Validator operator&(const Validator &other) const;\n\n  /// Combining validators is a new validator. Type comes from left validator if\n  /// function, otherwise only set if the same.\n  Validator operator|(const Validator &other) const;\n\n  /// Create a validator that fails when a given validator succeeds\n  Validator operator!() const;\n\n private:\n  void _merge_description(const Validator &val1, const Validator &val2,\n                          const std::string &merger);\n};\n\n/// Class wrapping some of the accessors of Validator\nclass CustomValidator : public Validator {\n public:\n};\n// The implementation of the built in validators is using the Validator class;\n// the user is only expected to use the const (static) versions (since there's\n// no setup). Therefore, this is in detail.\nnamespace detail {\n\n/// CLI enumeration of different file types\nenum class path_type { nonexistent, file, directory };\n\n/// get the type of the path from a file name\nCLI11_INLINE path_type check_path(const char *file) noexcept;\n\n/// Check for an existing file (returns error message if check fails)\nclass ExistingFileValidator : public Validator {\n public:\n  ExistingFileValidator();\n};\n\n/// Check for an existing directory (returns error message if check fails)\nclass ExistingDirectoryValidator : public Validator {\n public:\n  ExistingDirectoryValidator();\n};\n\n/// Check for an existing path\nclass ExistingPathValidator : public Validator {\n public:\n  ExistingPathValidator();\n};\n\n/// Check for an non-existing path\nclass NonexistentPathValidator : public Validator {\n public:\n  NonexistentPathValidator();\n};\n\n/// Validate the given string is a legal ipv4 address\nclass IPV4Validator : public Validator {\n public:\n  IPV4Validator();\n};\n\n}  // namespace detail\n\n// Static is not needed here, because global const implies static.\n\n/// Check for existing file (returns error message if check fails)\nconst detail::ExistingFileValidator ExistingFile;\n\n/// Check for an existing directory (returns error message if check fails)\nconst detail::ExistingDirectoryValidator ExistingDirectory;\n\n/// Check for an existing path\nconst detail::ExistingPathValidator ExistingPath;\n\n/// Check for an non-existing path\nconst detail::NonexistentPathValidator NonexistentPath;\n\n/// Check for an IP4 address\nconst detail::IPV4Validator ValidIPV4;\n\n/// Validate the input as a particular type\ntemplate <typename DesiredType>\nclass TypeValidator : public Validator {\n public:\n  explicit TypeValidator(const std::string &validator_name)\n      : Validator(validator_name, [](std::string &input_string) {\n          using CLI::detail::lexical_cast;\n          auto val = DesiredType();\n          if (!lexical_cast(input_string, val)) {\n            return std::string(\"Failed parsing \") + input_string + \" as a \" +\n                   detail::type_name<DesiredType>();\n          }\n          return std::string();\n        }) {}\n  TypeValidator() : TypeValidator(detail::type_name<DesiredType>()) {}\n};\n\n/// Check for a number\nconst TypeValidator<double> Number(\"NUMBER\");\n\n/// Modify a path if the file is a particular default location, can be used as\n/// Check or transform with the error return optionally disabled\nclass FileOnDefaultPath : public Validator {\n public:\n  explicit FileOnDefaultPath(std::string default_path,\n                             bool enableErrorReturn = true);\n};\n\n/// Produce a range (factory). Min and max are inclusive.\nclass Range : public Validator {\n public:\n  /// This produces a range with min and max inclusive.\n  ///\n  /// Note that the constructor is templated, but the struct is not, so C++17 is\n  /// not needed to provide nice syntax for Range(a,b).\n  template <typename T>\n  Range(T min_val, T max_val, const std::string &validator_name = std::string{})\n      : Validator(validator_name) {\n    if (validator_name.empty()) {\n      std::stringstream out;\n      out << detail::type_name<T>() << \" in [\" << min_val << \" - \" << max_val\n          << \"]\";\n      description(out.str());\n    }\n\n    func_ = [min_val, max_val](std::string &input) {\n      using CLI::detail::lexical_cast;\n      T val;\n      bool converted = lexical_cast(input, val);\n      if ((!converted) || (val < min_val || val > max_val)) {\n        std::stringstream out;\n        out << \"Value \" << input << \" not in range [\";\n        out << min_val << \" - \" << max_val << \"]\";\n        return out.str();\n      }\n      return std::string{};\n    };\n  }\n\n  /// Range of one value is 0 to value\n  template <typename T>\n  explicit Range(T max_val, const std::string &validator_name = std::string{})\n      : Range(static_cast<T>(0), max_val, validator_name) {}\n};\n\n/// Check for a non negative number\nconst Range NonNegativeNumber((std::numeric_limits<double>::max)(),\n                              \"NONNEGATIVE\");\n\n/// Check for a positive valued number (val>0.0), <double>::min  here is the\n/// smallest positive number\nconst Range PositiveNumber((std::numeric_limits<double>::min)(),\n                           (std::numeric_limits<double>::max)(), \"POSITIVE\");\n\n/// Produce a bounded range (factory). Min and max are inclusive.\nclass Bound : public Validator {\n public:\n  /// This bounds a value with min and max inclusive.\n  ///\n  /// Note that the constructor is templated, but the struct is not, so C++17 is\n  /// not needed to provide nice syntax for Range(a,b).\n  template <typename T>\n  Bound(T min_val, T max_val) {\n    std::stringstream out;\n    out << detail::type_name<T>() << \" bounded to [\" << min_val << \" - \"\n        << max_val << \"]\";\n    description(out.str());\n\n    func_ = [min_val, max_val](std::string &input) {\n      using CLI::detail::lexical_cast;\n      T val;\n      bool converted = lexical_cast(input, val);\n      if (!converted) {\n        return std::string(\"Value \") + input + \" could not be converted\";\n      }\n      if (val < min_val)\n        input = detail::to_string(min_val);\n      else if (val > max_val)\n        input = detail::to_string(max_val);\n\n      return std::string{};\n    };\n  }\n\n  /// Range of one value is 0 to value\n  template <typename T>\n  explicit Bound(T max_val) : Bound(static_cast<T>(0), max_val) {}\n};\n\nnamespace detail {\ntemplate <\n    typename T,\n    enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value,\n                detail::enabler> = detail::dummy>\nauto smart_deref(T value) -> decltype(*value) {\n  return *value;\n}\n\ntemplate <typename T,\n          enable_if_t<\n              !is_copyable_ptr<typename std::remove_reference<T>::type>::value,\n              detail::enabler> = detail::dummy>\ntypename std::remove_reference<T>::type &smart_deref(T &value) {\n  return value;\n}\n/// Generate a string representation of a set\ntemplate <typename T>\nstd::string generate_set(const T &set) {\n  using element_t = typename detail::element_type<T>::type;\n  using iteration_type_t =\n      typename detail::pair_adaptor<element_t>::value_type;  // the type of the\n                                                             // object pair\n  std::string out(1, '{');\n  out.append(detail::join(\n      detail::smart_deref(set),\n      [](const iteration_type_t &v) {\n        return detail::pair_adaptor<element_t>::first(v);\n      },\n      \",\"));\n  out.push_back('}');\n  return out;\n}\n\n/// Generate a string representation of a map\ntemplate <typename T>\nstd::string generate_map(const T &map, bool key_only = false) {\n  using element_t = typename detail::element_type<T>::type;\n  using iteration_type_t =\n      typename detail::pair_adaptor<element_t>::value_type;  // the type of the\n                                                             // object pair\n  std::string out(1, '{');\n  out.append(detail::join(\n      detail::smart_deref(map),\n      [key_only](const iteration_type_t &v) {\n        std::string res{\n            detail::to_string(detail::pair_adaptor<element_t>::first(v))};\n\n        if (!key_only) {\n          res.append(\"->\");\n          res += detail::to_string(detail::pair_adaptor<element_t>::second(v));\n        }\n        return res;\n      },\n      \",\"));\n  out.push_back('}');\n  return out;\n}\n\ntemplate <typename C, typename V>\nstruct has_find {\n  template <typename CC, typename VV>\n  static auto test(int) -> decltype(std::declval<CC>().find(std::declval<VV>()),\n                                    std::true_type());\n  template <typename, typename>\n  static auto test(...) -> decltype(std::false_type());\n\n  static const auto value = decltype(test<C, V>(0))::value;\n  using type = std::integral_constant<bool, value>;\n};\n\n/// A search function\ntemplate <typename T, typename V,\n          enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>\nauto search(const T &set, const V &val)\n    -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {\n  using element_t = typename detail::element_type<T>::type;\n  auto &setref = detail::smart_deref(set);\n  auto it =\n      std::find_if(std::begin(setref), std::end(setref),\n                   [&val](decltype(*std::begin(setref)) v) {\n                     return (detail::pair_adaptor<element_t>::first(v) == val);\n                   });\n  return {(it != std::end(setref)), it};\n}\n\n/// A search function that uses the built in find function\ntemplate <typename T, typename V,\n          enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>\nauto search(const T &set, const V &val)\n    -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {\n  auto &setref = detail::smart_deref(set);\n  auto it = setref.find(val);\n  return {(it != std::end(setref)), it};\n}\n\n/// A search function with a filter function\ntemplate <typename T, typename V>\nauto search(const T &set, const V &val,\n            const std::function<V(V)> &filter_function)\n    -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {\n  using element_t = typename detail::element_type<T>::type;\n  // do the potentially faster first search\n  auto res = search(set, val);\n  if ((res.first) || (!(filter_function))) {\n    return res;\n  }\n  // if we haven't found it do the longer linear search with all the element\n  // translations\n  auto &setref = detail::smart_deref(set);\n  auto it = std::find_if(std::begin(setref), std::end(setref),\n                         [&](decltype(*std::begin(setref)) v) {\n                           V a{detail::pair_adaptor<element_t>::first(v)};\n                           a = filter_function(a);\n                           return (a == val);\n                         });\n  return {(it != std::end(setref)), it};\n}\n\n// the following suggestion was made by Nikita Ofitserov(@himikof)\n// done in templates to prevent compiler warnings on negation of unsigned\n// numbers\n\n/// Do a check for overflow on signed numbers\ntemplate <typename T>\ninline typename std::enable_if<std::is_signed<T>::value, T>::type overflowCheck(\n    const T &a, const T &b) {\n  if ((a > 0) == (b > 0)) {\n    return ((std::numeric_limits<T>::max)() / (std::abs)(a) < (std::abs)(b));\n  }\n  return ((std::numeric_limits<T>::min)() / (std::abs)(a) > -(std::abs)(b));\n}\n/// Do a check for overflow on unsigned numbers\ntemplate <typename T>\ninline typename std::enable_if<!std::is_signed<T>::value, T>::type\noverflowCheck(const T &a, const T &b) {\n  return ((std::numeric_limits<T>::max)() / a < b);\n}\n\n/// Performs a *= b; if it doesn't cause integer overflow. Returns false\n/// otherwise.\ntemplate <typename T>\ntypename std::enable_if<std::is_integral<T>::value, bool>::type\nchecked_multiply(T &a, T b) {\n  if (a == 0 || b == 0 || a == 1 || b == 1) {\n    a *= b;\n    return true;\n  }\n  if (a == (std::numeric_limits<T>::min)() ||\n      b == (std::numeric_limits<T>::min)()) {\n    return false;\n  }\n  if (overflowCheck(a, b)) {\n    return false;\n  }\n  a *= b;\n  return true;\n}\n\n/// Performs a *= b; if it doesn't equal infinity. Returns false otherwise.\ntemplate <typename T>\ntypename std::enable_if<std::is_floating_point<T>::value, bool>::type\nchecked_multiply(T &a, T b) {\n  T c = a * b;\n  if (std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {\n    return false;\n  }\n  a = c;\n  return true;\n}\n\n}  // namespace detail\n/// Verify items are in a set\nclass IsMember : public Validator {\n public:\n  using filter_fn_t = std::function<std::string(std::string)>;\n\n  /// This allows in-place construction using an initializer list\n  template <typename T, typename... Args>\n  IsMember(std::initializer_list<T> values, Args &&...args)\n      : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}\n\n  /// This checks to see if an item is in a set (empty function)\n  template <typename T>\n  explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}\n\n  /// This checks to see if an item is in a set: pointer or copy version. You\n  /// can pass in a function that will filter both sides of the comparison\n  /// before computing the comparison.\n  template <typename T, typename F>\n  explicit IsMember(T set, F filter_function) {\n    // Get the type of the contained item - requires a container have\n    // ::value_type if the type does not have first_type and second_type, these\n    // are both value_type\n    using element_t =\n        typename detail::element_type<T>::type;  // Removes (smart) pointers if\n                                                 // needed\n    using item_t =\n        typename detail::pair_adaptor<element_t>::first_type;  // Is value_type\n                                                               // if not a map\n\n    using local_item_t =\n        typename IsMemberType<item_t>::type;  // This will convert bad types to\n                                              // good ones (const char * to\n                                              // std::string)\n\n    // Make a local copy of the filter function, using a std::function if not\n    // one already\n    std::function<local_item_t(local_item_t)> filter_fn = filter_function;\n\n    // This is the type name for help, it will take the current version of the\n    // set contents\n    desc_function_ = [set]() {\n      return detail::generate_set(detail::smart_deref(set));\n    };\n\n    // This is the function that validates\n    // It stores a copy of the set pointer-like, so shared_ptr will stay alive\n    func_ = [set, filter_fn](std::string &input) {\n      using CLI::detail::lexical_cast;\n      local_item_t b;\n      if (!lexical_cast(input, b)) {\n        throw ValidationError(input);  // name is added later\n      }\n      if (filter_fn) {\n        b = filter_fn(b);\n      }\n      auto res = detail::search(set, b, filter_fn);\n      if (res.first) {\n        // Make sure the version in the input string is identical to the one in\n        // the set\n        if (filter_fn) {\n          input = detail::value_string(\n              detail::pair_adaptor<element_t>::first(*(res.second)));\n        }\n\n        // Return empty error string (success)\n        return std::string{};\n      }\n\n      // If you reach this point, the result was not found\n      return input + \" not in \" +\n             detail::generate_set(detail::smart_deref(set));\n    };\n  }\n\n  /// You can pass in as many filter functions as you like, they nest (string\n  /// only currently)\n  template <typename T, typename... Args>\n  IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2,\n           Args &&...other)\n      : IsMember(\n            std::forward<T>(set),\n            [filter_fn_1, filter_fn_2](std::string a) {\n              return filter_fn_2(filter_fn_1(a));\n            },\n            other...) {}\n};\n\n/// definition of the default transformation object\ntemplate <typename T>\nusing TransformPairs = std::vector<std::pair<std::string, T>>;\n\n/// Translate named items to other or a value set\nclass Transformer : public Validator {\n public:\n  using filter_fn_t = std::function<std::string(std::string)>;\n\n  /// This allows in-place construction\n  template <typename... Args>\n  Transformer(std::initializer_list<std::pair<std::string, std::string>> values,\n              Args &&...args)\n      : Transformer(TransformPairs<std::string>(values),\n                    std::forward<Args>(args)...) {}\n\n  /// direct map of std::string to std::string\n  template <typename T>\n  explicit Transformer(T &&mapping)\n      : Transformer(std::forward<T>(mapping), nullptr) {}\n\n  /// This checks to see if an item is in a set: pointer or copy version. You\n  /// can pass in a function that will filter both sides of the comparison\n  /// before computing the comparison.\n  template <typename T, typename F>\n  explicit Transformer(T mapping, F filter_function) {\n    static_assert(\n        detail::pair_adaptor<typename detail::element_type<T>::type>::value,\n        \"mapping must produce value pairs\");\n    // Get the type of the contained item - requires a container have\n    // ::value_type if the type does not have first_type and second_type, these\n    // are both value_type\n    using element_t =\n        typename detail::element_type<T>::type;  // Removes (smart) pointers if\n                                                 // needed\n    using item_t =\n        typename detail::pair_adaptor<element_t>::first_type;  // Is value_type\n                                                               // if not a map\n    using local_item_t =\n        typename IsMemberType<item_t>::type;  // Will convert bad types to good\n                                              // ones (const char * to\n                                              // std::string)\n\n    // Make a local copy of the filter function, using a std::function if not\n    // one already\n    std::function<local_item_t(local_item_t)> filter_fn = filter_function;\n\n    // This is the type name for help, it will take the current version of the\n    // set contents\n    desc_function_ = [mapping]() {\n      return detail::generate_map(detail::smart_deref(mapping));\n    };\n\n    func_ = [mapping, filter_fn](std::string &input) {\n      using CLI::detail::lexical_cast;\n      local_item_t b;\n      if (!lexical_cast(input, b)) {\n        return std::string();\n        // there is no possible way we can match anything in the mapping if we\n        // can't convert so just return\n      }\n      if (filter_fn) {\n        b = filter_fn(b);\n      }\n      auto res = detail::search(mapping, b, filter_fn);\n      if (res.first) {\n        input = detail::value_string(\n            detail::pair_adaptor<element_t>::second(*res.second));\n      }\n      return std::string{};\n    };\n  }\n\n  /// You can pass in as many filter functions as you like, they nest\n  template <typename T, typename... Args>\n  Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2,\n              Args &&...other)\n      : Transformer(\n            std::forward<T>(mapping),\n            [filter_fn_1, filter_fn_2](std::string a) {\n              return filter_fn_2(filter_fn_1(a));\n            },\n            other...) {}\n};\n\n/// translate named items to other or a value set\nclass CheckedTransformer : public Validator {\n public:\n  using filter_fn_t = std::function<std::string(std::string)>;\n\n  /// This allows in-place construction\n  template <typename... Args>\n  CheckedTransformer(\n      std::initializer_list<std::pair<std::string, std::string>> values,\n      Args &&...args)\n      : CheckedTransformer(TransformPairs<std::string>(values),\n                           std::forward<Args>(args)...) {}\n\n  /// direct map of std::string to std::string\n  template <typename T>\n  explicit CheckedTransformer(T mapping)\n      : CheckedTransformer(std::move(mapping), nullptr) {}\n\n  /// This checks to see if an item is in a set: pointer or copy version. You\n  /// can pass in a function that will filter both sides of the comparison\n  /// before computing the comparison.\n  template <typename T, typename F>\n  explicit CheckedTransformer(T mapping, F filter_function) {\n    static_assert(\n        detail::pair_adaptor<typename detail::element_type<T>::type>::value,\n        \"mapping must produce value pairs\");\n    // Get the type of the contained item - requires a container have\n    // ::value_type if the type does not have first_type and second_type, these\n    // are both value_type\n    using element_t =\n        typename detail::element_type<T>::type;  // Removes (smart) pointers if\n                                                 // needed\n    using item_t =\n        typename detail::pair_adaptor<element_t>::first_type;  // Is value_type\n                                                               // if not a map\n    using local_item_t =\n        typename IsMemberType<item_t>::type;  // Will convert bad types to good\n                                              // ones (const char * to\n                                              // std::string)\n    using iteration_type_t = typename detail::pair_adaptor<\n        element_t>::value_type;  // the type of the object pair\n\n    // Make a local copy of the filter function, using a std::function if not\n    // one already\n    std::function<local_item_t(local_item_t)> filter_fn = filter_function;\n\n    auto tfunc = [mapping]() {\n      std::string out(\"value in \");\n      out += detail::generate_map(detail::smart_deref(mapping)) + \" OR {\";\n      out += detail::join(\n          detail::smart_deref(mapping),\n          [](const iteration_type_t &v) {\n            return detail::to_string(\n                detail::pair_adaptor<element_t>::second(v));\n          },\n          \",\");\n      out.push_back('}');\n      return out;\n    };\n\n    desc_function_ = tfunc;\n\n    func_ = [mapping, tfunc, filter_fn](std::string &input) {\n      using CLI::detail::lexical_cast;\n      local_item_t b;\n      bool converted = lexical_cast(input, b);\n      if (converted) {\n        if (filter_fn) {\n          b = filter_fn(b);\n        }\n        auto res = detail::search(mapping, b, filter_fn);\n        if (res.first) {\n          input = detail::value_string(\n              detail::pair_adaptor<element_t>::second(*res.second));\n          return std::string{};\n        }\n      }\n      for (const auto &v : detail::smart_deref(mapping)) {\n        auto output_string =\n            detail::value_string(detail::pair_adaptor<element_t>::second(v));\n        if (output_string == input) {\n          return std::string();\n        }\n      }\n\n      return \"Check \" + input + \" \" + tfunc() + \" FAILED\";\n    };\n  }\n\n  /// You can pass in as many filter functions as you like, they nest\n  template <typename T, typename... Args>\n  CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1,\n                     filter_fn_t filter_fn_2, Args &&...other)\n      : CheckedTransformer(\n            std::forward<T>(mapping),\n            [filter_fn_1, filter_fn_2](std::string a) {\n              return filter_fn_2(filter_fn_1(a));\n            },\n            other...) {}\n};\n\n/// Helper function to allow ignore_case to be passed to IsMember or Transform\ninline std::string ignore_case(std::string item) {\n  return detail::to_lower(item);\n}\n\n/// Helper function to allow ignore_underscore to be passed to IsMember or\n/// Transform\ninline std::string ignore_underscore(std::string item) {\n  return detail::remove_underscore(item);\n}\n\n/// Helper function to allow checks to ignore spaces to be passed to IsMember or\n/// Transform\ninline std::string ignore_space(std::string item) {\n  item.erase(std::remove(std::begin(item), std::end(item), ' '),\n             std::end(item));\n  item.erase(std::remove(std::begin(item), std::end(item), '\\t'),\n             std::end(item));\n  return item;\n}\n\n/// Multiply a number by a factor using given mapping.\n/// Can be used to write transforms for SIZE or DURATION inputs.\n///\n/// Example:\n///   With mapping = `{\"b\"->1, \"kb\"->1024, \"mb\"->1024*1024}`\n///   one can recognize inputs like \"100\", \"12kb\", \"100 MB\",\n///   that will be automatically transformed to 100, 14448, 104857600.\n///\n/// Output number type matches the type in the provided mapping.\n/// Therefore, if it is required to interpret real inputs like \"0.42 s\",\n/// the mapping should be of a type <string, float> or <string, double>.\nclass AsNumberWithUnit : public Validator {\n public:\n  /// Adjust AsNumberWithUnit behavior.\n  /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched.\n  /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError\n  ///   if UNIT_REQUIRED is set and unit literal is not found.\n  enum Options {\n    CASE_SENSITIVE = 0,\n    CASE_INSENSITIVE = 1,\n    UNIT_OPTIONAL = 0,\n    UNIT_REQUIRED = 2,\n    DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL\n  };\n\n  template <typename Number>\n  explicit AsNumberWithUnit(std::map<std::string, Number> mapping,\n                            Options opts = DEFAULT,\n                            const std::string &unit_name = \"UNIT\") {\n    description(generate_description<Number>(unit_name, opts));\n    validate_mapping(mapping, opts);\n\n    // transform function\n    func_ = [mapping, opts](std::string &input) -> std::string {\n      Number num{};\n\n      detail::rtrim(input);\n      if (input.empty()) {\n        throw ValidationError(\"Input is empty\");\n      }\n\n      // Find split position between number and prefix\n      auto unit_begin = input.end();\n      while (unit_begin > input.begin() &&\n             std::isalpha(*(unit_begin - 1), std::locale())) {\n        --unit_begin;\n      }\n\n      std::string unit{unit_begin, input.end()};\n      input.resize(\n          static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));\n      detail::trim(input);\n\n      if (opts & UNIT_REQUIRED && unit.empty()) {\n        throw ValidationError(\"Missing mandatory unit\");\n      }\n      if (opts & CASE_INSENSITIVE) {\n        unit = detail::to_lower(unit);\n      }\n      if (unit.empty()) {\n        using CLI::detail::lexical_cast;\n        if (!lexical_cast(input, num)) {\n          throw ValidationError(std::string(\"Value \") + input +\n                                \" could not be converted to \" +\n                                detail::type_name<Number>());\n        }\n        // No need to modify input if no unit passed\n        return {};\n      }\n\n      // find corresponding factor\n      auto it = mapping.find(unit);\n      if (it == mapping.end()) {\n        throw ValidationError(unit +\n                              \" unit not recognized. \"\n                              \"Allowed values: \" +\n                              detail::generate_map(mapping, true));\n      }\n\n      if (!input.empty()) {\n        using CLI::detail::lexical_cast;\n        bool converted = lexical_cast(input, num);\n        if (!converted) {\n          throw ValidationError(std::string(\"Value \") + input +\n                                \" could not be converted to \" +\n                                detail::type_name<Number>());\n        }\n        // perform safe multiplication\n        bool ok = detail::checked_multiply(num, it->second);\n        if (!ok) {\n          throw ValidationError(\n              detail::to_string(num) + \" multiplied by \" + unit +\n              \" factor would cause number overflow. Use smaller value.\");\n        }\n      } else {\n        num = static_cast<Number>(it->second);\n      }\n\n      input = detail::to_string(num);\n\n      return {};\n    };\n  }\n\n private:\n  /// Check that mapping contains valid units.\n  /// Update mapping for CASE_INSENSITIVE mode.\n  template <typename Number>\n  static void validate_mapping(std::map<std::string, Number> &mapping,\n                               Options opts) {\n    for (auto &kv : mapping) {\n      if (kv.first.empty()) {\n        throw ValidationError(\"Unit must not be empty.\");\n      }\n      if (!detail::isalpha(kv.first)) {\n        throw ValidationError(\"Unit must contain only letters.\");\n      }\n    }\n\n    // make all units lowercase if CASE_INSENSITIVE\n    if (opts & CASE_INSENSITIVE) {\n      std::map<std::string, Number> lower_mapping;\n      for (auto &kv : mapping) {\n        auto s = detail::to_lower(kv.first);\n        if (lower_mapping.count(s)) {\n          throw ValidationError(std::string(\"Several matching lowercase unit \"\n                                            \"representations are found: \") +\n                                s);\n        }\n        lower_mapping[detail::to_lower(kv.first)] = kv.second;\n      }\n      mapping = std::move(lower_mapping);\n    }\n  }\n\n  /// Generate description like this: NUMBER [UNIT]\n  template <typename Number>\n  static std::string generate_description(const std::string &name,\n                                          Options opts) {\n    std::stringstream out;\n    out << detail::type_name<Number>() << ' ';\n    if (opts & UNIT_REQUIRED) {\n      out << name;\n    } else {\n      out << '[' << name << ']';\n    }\n    return out.str();\n  }\n};\n\ninline AsNumberWithUnit::Options operator|(const AsNumberWithUnit::Options &a,\n                                           const AsNumberWithUnit::Options &b) {\n  return static_cast<AsNumberWithUnit::Options>(static_cast<int>(a) |\n                                                static_cast<int>(b));\n}\n\n/// Converts a human-readable size string (with unit literal) to uin64_t size.\n/// Example:\n///   \"100\" => 100\n///   \"1 b\" => 100\n///   \"10Kb\" => 10240 // you can configure this to be interpreted as kilobyte\n///   (*1000) or kibibyte (*1024) \"10 KB\" => 10240 \"10 kb\" => 10240 \"10 kib\" =>\n///   10240 // *i, *ib are always interpreted as *bibyte (*1024) \"10kb\" => 10240\n///   \"2 MB\" => 2097152\n///   \"2 EiB\" => 2^61 // Units up to exibyte are supported\nclass AsSizeValue : public AsNumberWithUnit {\n public:\n  using result_t = std::uint64_t;\n\n  /// If kb_is_1000 is true,\n  /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024\n  /// (same applies to higher order units as well).\n  /// Otherwise, interpret all literals as factors of 1024.\n  /// The first option is formally correct, but\n  /// the second interpretation is more wide-spread\n  /// (see https://en.wikipedia.org/wiki/Binary_prefix).\n  explicit AsSizeValue(bool kb_is_1000);\n\n private:\n  /// Get <size unit, factor> mapping\n  static std::map<std::string, result_t> init_mapping(bool kb_is_1000);\n\n  /// Cache calculated mapping\n  static std::map<std::string, result_t> get_mapping(bool kb_is_1000);\n};\n\nnamespace detail {\n/// Split a string into a program name and command line arguments\n/// the string is assumed to contain a file name followed by other arguments\n/// the return value contains is a pair with the first argument containing the\n/// program name and the second everything else.\nCLI11_INLINE std::pair<std::string, std::string> split_program_name(\n    std::string commandline);\n\n}  // namespace detail\n/// @}\n\nCLI11_INLINE std::string Validator::operator()(std::string &str) const {\n  std::string retstring;\n  if (active_) {\n    if (non_modifying_) {\n      std::string value = str;\n      retstring = func_(value);\n    } else {\n      retstring = func_(str);\n    }\n  }\n  return retstring;\n}\n\nCLI11_NODISCARD CLI11_INLINE Validator\nValidator::description(std::string validator_desc) const {\n  Validator newval(*this);\n  newval.desc_function_ = [validator_desc]() { return validator_desc; };\n  return newval;\n}\n\nCLI11_INLINE Validator Validator::operator&(const Validator &other) const {\n  Validator newval;\n\n  newval._merge_description(*this, other, \" AND \");\n\n  // Give references (will make a copy in lambda function)\n  const std::function<std::string(std::string & filename)> &f1 = func_;\n  const std::function<std::string(std::string & filename)> &f2 = other.func_;\n\n  newval.func_ = [f1, f2](std::string &input) {\n    std::string s1 = f1(input);\n    std::string s2 = f2(input);\n    if (!s1.empty() && !s2.empty())\n      return std::string(\"(\") + s1 + \") AND (\" + s2 + \")\";\n    return s1 + s2;\n  };\n\n  newval.active_ = active_ && other.active_;\n  newval.application_index_ = application_index_;\n  return newval;\n}\n\nCLI11_INLINE Validator Validator::operator|(const Validator &other) const {\n  Validator newval;\n\n  newval._merge_description(*this, other, \" OR \");\n\n  // Give references (will make a copy in lambda function)\n  const std::function<std::string(std::string &)> &f1 = func_;\n  const std::function<std::string(std::string &)> &f2 = other.func_;\n\n  newval.func_ = [f1, f2](std::string &input) {\n    std::string s1 = f1(input);\n    std::string s2 = f2(input);\n    if (s1.empty() || s2.empty()) return std::string();\n\n    return std::string(\"(\") + s1 + \") OR (\" + s2 + \")\";\n  };\n  newval.active_ = active_ && other.active_;\n  newval.application_index_ = application_index_;\n  return newval;\n}\n\nCLI11_INLINE Validator Validator::operator!() const {\n  Validator newval;\n  const std::function<std::string()> &dfunc1 = desc_function_;\n  newval.desc_function_ = [dfunc1]() {\n    auto str = dfunc1();\n    return (!str.empty()) ? std::string(\"NOT \") + str : std::string{};\n  };\n  // Give references (will make a copy in lambda function)\n  const std::function<std::string(std::string & res)> &f1 = func_;\n\n  newval.func_ = [f1, dfunc1](std::string &test) -> std::string {\n    std::string s1 = f1(test);\n    if (s1.empty()) {\n      return std::string(\"check \") + dfunc1() + \" succeeded improperly\";\n    }\n    return std::string{};\n  };\n  newval.active_ = active_;\n  newval.application_index_ = application_index_;\n  return newval;\n}\n\nCLI11_INLINE void Validator::_merge_description(const Validator &val1,\n                                                const Validator &val2,\n                                                const std::string &merger) {\n  const std::function<std::string()> &dfunc1 = val1.desc_function_;\n  const std::function<std::string()> &dfunc2 = val2.desc_function_;\n\n  desc_function_ = [=]() {\n    std::string f1 = dfunc1();\n    std::string f2 = dfunc2();\n    if ((f1.empty()) || (f2.empty())) {\n      return f1 + f2;\n    }\n    return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';\n  };\n}\n\nnamespace detail {\n\n#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0\nCLI11_INLINE path_type check_path(const char *file) noexcept {\n  std::error_code ec;\n  auto stat = std::filesystem::status(file, ec);\n  if (ec) {\n    return path_type::nonexistent;\n  }\n  switch (stat.type()) {\n    case std::filesystem::file_type::none:  // LCOV_EXCL_LINE\n    case std::filesystem::file_type::not_found:\n      return path_type::nonexistent;\n    case std::filesystem::file_type::directory:\n      return path_type::directory;\n    case std::filesystem::file_type::symlink:\n    case std::filesystem::file_type::block:\n    case std::filesystem::file_type::character:\n    case std::filesystem::file_type::fifo:\n    case std::filesystem::file_type::socket:\n    case std::filesystem::file_type::regular:\n    case std::filesystem::file_type::unknown:\n    default:\n      return path_type::file;\n  }\n}\n#else\nCLI11_INLINE path_type check_path(const char *file) noexcept {\n#if defined(_MSC_VER)\n  struct __stat64 buffer;\n  if (_stat64(file, &buffer) == 0) {\n    return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory\n                                             : path_type::file;\n  }\n#else\n  struct stat buffer;\n  if (stat(file, &buffer) == 0) {\n    return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory\n                                             : path_type::file;\n  }\n#endif\n  return path_type::nonexistent;\n}\n#endif\n\nCLI11_INLINE ExistingFileValidator::ExistingFileValidator()\n    : Validator(\"FILE\") {\n  func_ = [](std::string &filename) {\n    auto path_result = check_path(filename.c_str());\n    if (path_result == path_type::nonexistent) {\n      return \"File does not exist: \" + filename;\n    }\n    if (path_result == path_type::directory) {\n      return \"File is actually a directory: \" + filename;\n    }\n    return std::string();\n  };\n}\n\nCLI11_INLINE ExistingDirectoryValidator::ExistingDirectoryValidator()\n    : Validator(\"DIR\") {\n  func_ = [](std::string &filename) {\n    auto path_result = check_path(filename.c_str());\n    if (path_result == path_type::nonexistent) {\n      return \"Directory does not exist: \" + filename;\n    }\n    if (path_result == path_type::file) {\n      return \"Directory is actually a file: \" + filename;\n    }\n    return std::string();\n  };\n}\n\nCLI11_INLINE ExistingPathValidator::ExistingPathValidator()\n    : Validator(\"PATH(existing)\") {\n  func_ = [](std::string &filename) {\n    auto path_result = check_path(filename.c_str());\n    if (path_result == path_type::nonexistent) {\n      return \"Path does not exist: \" + filename;\n    }\n    return std::string();\n  };\n}\n\nCLI11_INLINE NonexistentPathValidator::NonexistentPathValidator()\n    : Validator(\"PATH(non-existing)\") {\n  func_ = [](std::string &filename) {\n    auto path_result = check_path(filename.c_str());\n    if (path_result != path_type::nonexistent) {\n      return \"Path already exists: \" + filename;\n    }\n    return std::string();\n  };\n}\n\nCLI11_INLINE IPV4Validator::IPV4Validator() : Validator(\"IPV4\") {\n  func_ = [](std::string &ip_addr) {\n    auto result = CLI::detail::split(ip_addr, '.');\n    if (result.size() != 4) {\n      return std::string(\"Invalid IPV4 address must have four parts (\") +\n             ip_addr + ')';\n    }\n    int num = 0;\n    for (const auto &var : result) {\n      using CLI::detail::lexical_cast;\n      bool retval = lexical_cast(var, num);\n      if (!retval) {\n        return std::string(\"Failed parsing number (\") + var + ')';\n      }\n      if (num < 0 || num > 255) {\n        return std::string(\"Each IP number must be between 0 and 255 \") + var;\n      }\n    }\n    return std::string();\n  };\n}\n\n}  // namespace detail\n\nCLI11_INLINE FileOnDefaultPath::FileOnDefaultPath(std::string default_path,\n                                                  bool enableErrorReturn)\n    : Validator(\"FILE\") {\n  func_ = [default_path, enableErrorReturn](std::string &filename) {\n    auto path_result = detail::check_path(filename.c_str());\n    if (path_result == detail::path_type::nonexistent) {\n      std::string test_file_path = default_path;\n      if (default_path.back() != '/' && default_path.back() != '\\\\') {\n        // Add folder separator\n        test_file_path += '/';\n      }\n      test_file_path.append(filename);\n      path_result = detail::check_path(test_file_path.c_str());\n      if (path_result == detail::path_type::file) {\n        filename = test_file_path;\n      } else {\n        if (enableErrorReturn) {\n          return \"File does not exist: \" + filename;\n        }\n      }\n    }\n    return std::string{};\n  };\n}\n\nCLI11_INLINE AsSizeValue::AsSizeValue(bool kb_is_1000)\n    : AsNumberWithUnit(get_mapping(kb_is_1000)) {\n  if (kb_is_1000) {\n    description(\"SIZE [b, kb(=1000b), kib(=1024b), ...]\");\n  } else {\n    description(\"SIZE [b, kb(=1024b), ...]\");\n  }\n}\n\nCLI11_INLINE std::map<std::string, AsSizeValue::result_t>\nAsSizeValue::init_mapping(bool kb_is_1000) {\n  std::map<std::string, result_t> m;\n  result_t k_factor = kb_is_1000 ? 1000 : 1024;\n  result_t ki_factor = 1024;\n  result_t k = 1;\n  result_t ki = 1;\n  m[\"b\"] = 1;\n  for (std::string p : {\"k\", \"m\", \"g\", \"t\", \"p\", \"e\"}) {\n    k *= k_factor;\n    ki *= ki_factor;\n    m[p] = k;\n    m[p + \"b\"] = k;\n    m[p + \"i\"] = ki;\n    m[p + \"ib\"] = ki;\n  }\n  return m;\n}\n\nCLI11_INLINE std::map<std::string, AsSizeValue::result_t>\nAsSizeValue::get_mapping(bool kb_is_1000) {\n  if (kb_is_1000) {\n    static auto m = init_mapping(true);\n    return m;\n  }\n  static auto m = init_mapping(false);\n  return m;\n}\n\nnamespace detail {\n\nCLI11_INLINE std::pair<std::string, std::string> split_program_name(\n    std::string commandline) {\n  // try to determine the programName\n  std::pair<std::string, std::string> vals;\n  trim(commandline);\n  auto esp = commandline.find_first_of(' ', 1);\n  while (detail::check_path(commandline.substr(0, esp).c_str()) !=\n         path_type::file) {\n    esp = commandline.find_first_of(' ', esp + 1);\n    if (esp == std::string::npos) {\n      // if we have reached the end and haven't found a valid file just assume\n      // the first argument is the program name\n      if (commandline[0] == '\"' || commandline[0] == '\\'' ||\n          commandline[0] == '`') {\n        bool embeddedQuote = false;\n        auto keyChar = commandline[0];\n        auto end = commandline.find_first_of(keyChar, 1);\n        while ((end != std::string::npos) &&\n               (commandline[end - 1] == '\\\\')) {  // deal with escaped quotes\n          end = commandline.find_first_of(keyChar, end + 1);\n          embeddedQuote = true;\n        }\n        if (end != std::string::npos) {\n          vals.first = commandline.substr(1, end - 1);\n          esp = end + 1;\n          if (embeddedQuote) {\n            vals.first =\n                find_and_replace(vals.first, std::string(\"\\\\\") + keyChar,\n                                 std::string(1, keyChar));\n          }\n        } else {\n          esp = commandline.find_first_of(' ', 1);\n        }\n      } else {\n        esp = commandline.find_first_of(' ', 1);\n      }\n\n      break;\n    }\n  }\n  if (vals.first.empty()) {\n    vals.first = commandline.substr(0, esp);\n    rtrim(vals.first);\n  }\n\n  // strip the program name\n  vals.second = (esp < commandline.length() - 1) ? commandline.substr(esp + 1)\n                                                 : std::string{};\n  ltrim(vals.second);\n  return vals;\n}\n\n}  // namespace detail\n/// @}\n\nclass Option;\nclass App;\n\n/// This enum signifies the type of help requested\n///\n/// This is passed in by App; all user classes must accept this as\n/// the second argument.\n\nenum class AppFormatMode {\n  Normal,  ///< The normal, detailed help\n  All,     ///< A fully expanded help\n  Sub,     ///< Used when printed as part of expanded subcommand\n};\n\n/// This is the minimum requirements to run a formatter.\n///\n/// A user can subclass this is if they do not care at all\n/// about the structure in CLI::Formatter.\nclass FormatterBase {\n protected:\n  /// @name Options\n  ///@{\n\n  /// The width of the first column\n  std::size_t column_width_{30};\n\n  /// @brief The required help printout labels (user changeable)\n  /// Values are Needs, Excludes, etc.\n  std::map<std::string, std::string> labels_{};\n\n  ///@}\n  /// @name Basic\n  ///@{\n\n public:\n  FormatterBase() = default;\n  FormatterBase(const FormatterBase &) = default;\n  FormatterBase(FormatterBase &&) = default;\n  FormatterBase &operator=(const FormatterBase &) = default;\n  FormatterBase &operator=(FormatterBase &&) = default;\n\n  /// Adding a destructor in this form to work around bug in GCC 4.7\n  virtual ~FormatterBase() noexcept {}  // NOLINT(modernize-use-equals-default)\n\n  /// This is the key method that puts together help\n  virtual std::string make_help(const App *, std::string,\n                                AppFormatMode) const = 0;\n\n  ///@}\n  /// @name Setters\n  ///@{\n\n  /// Set the \"REQUIRED\" label\n  void label(std::string key, std::string val) { labels_[key] = val; }\n\n  /// Set the column width\n  void column_width(std::size_t val) { column_width_ = val; }\n\n  ///@}\n  /// @name Getters\n  ///@{\n\n  /// Get the current value of a name (REQUIRED, etc.)\n  CLI11_NODISCARD std::string get_label(std::string key) const {\n    if (labels_.find(key) == labels_.end()) return key;\n    return labels_.at(key);\n  }\n\n  /// Get the current column width\n  CLI11_NODISCARD std::size_t get_column_width() const { return column_width_; }\n\n  ///@}\n};\n\n/// This is a specialty override for lambda functions\nclass FormatterLambda final : public FormatterBase {\n  using funct_t =\n      std::function<std::string(const App *, std::string, AppFormatMode)>;\n\n  /// The lambda to hold and run\n  funct_t lambda_;\n\n public:\n  /// Create a FormatterLambda with a lambda function\n  explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {}\n\n  /// Adding a destructor (mostly to make GCC 4.7 happy)\n  ~FormatterLambda() noexcept override {\n  }  // NOLINT(modernize-use-equals-default)\n\n  /// This will simply call the lambda function\n  std::string make_help(const App *app, std::string name,\n                        AppFormatMode mode) const override {\n    return lambda_(app, name, mode);\n  }\n};\n\n/// This is the default Formatter for CLI11. It pretty prints help output, and\n/// is broken into quite a few overridable methods, to be highly customizable\n/// with minimal effort.\nclass Formatter : public FormatterBase {\n public:\n  Formatter() = default;\n  Formatter(const Formatter &) = default;\n  Formatter(Formatter &&) = default;\n  Formatter &operator=(const Formatter &) = default;\n  Formatter &operator=(Formatter &&) = default;\n\n  /// @name Overridables\n  ///@{\n\n  /// This prints out a group of options with title\n  ///\n  CLI11_NODISCARD virtual std::string make_group(\n      std::string group, bool is_positional,\n      std::vector<const Option *> opts) const;\n\n  /// This prints out just the positionals \"group\"\n  virtual std::string make_positionals(const App *app) const;\n\n  /// This prints out all the groups of options\n  std::string make_groups(const App *app, AppFormatMode mode) const;\n\n  /// This prints out all the subcommands\n  virtual std::string make_subcommands(const App *app,\n                                       AppFormatMode mode) const;\n\n  /// This prints out a subcommand\n  virtual std::string make_subcommand(const App *sub) const;\n\n  /// This prints out a subcommand in help-all\n  virtual std::string make_expanded(const App *sub) const;\n\n  /// This prints out all the groups of options\n  virtual std::string make_footer(const App *app) const;\n\n  /// This displays the description line\n  virtual std::string make_description(const App *app) const;\n\n  /// This displays the usage line\n  virtual std::string make_usage(const App *app, std::string name) const;\n\n  /// This puts everything together\n  std::string make_help(const App * /*app*/, std::string,\n                        AppFormatMode) const override;\n\n  ///@}\n  /// @name Options\n  ///@{\n\n  /// This prints out an option help line, either positional or optional form\n  virtual std::string make_option(const Option *opt, bool is_positional) const {\n    std::stringstream out;\n    detail::format_help(\n        out, make_option_name(opt, is_positional) + make_option_opts(opt),\n        make_option_desc(opt), column_width_);\n    return out.str();\n  }\n\n  /// @brief This is the name part of an option, Default: left column\n  virtual std::string make_option_name(const Option *, bool) const;\n\n  /// @brief This is the options part of the name, Default: combined into left\n  /// column\n  virtual std::string make_option_opts(const Option *) const;\n\n  /// @brief This is the description. Default: Right column, on new line if left\n  /// column too large\n  virtual std::string make_option_desc(const Option *) const;\n\n  /// @brief This is used to print the name on the USAGE line\n  virtual std::string make_option_usage(const Option *opt) const;\n\n  ///@}\n};\n\nusing results_t = std::vector<std::string>;\n/// callback function definition\nusing callback_t = std::function<bool(const results_t &)>;\n\nclass Option;\nclass App;\n\nusing Option_p = std::unique_ptr<Option>;\n/// Enumeration of the multiOption Policy selection\nenum class MultiOptionPolicy : char {\n  Throw,      //!< Throw an error if any extra arguments were given\n  TakeLast,   //!< take only the last Expected number of arguments\n  TakeFirst,  //!< take only the first Expected number of arguments\n  Join,       //!< merge all the arguments together into a single string via the\n              //!< delimiter character default('\\n')\n  TakeAll,    //!< just get all the passed argument regardless\n  Sum  //!< sum all the arguments together if numerical or concatenate directly\n       //!< without delimiter\n};\n\n/// This is the CRTP base class for Option and OptionDefaults. It was designed\n/// this way to share parts of the class; an OptionDefaults can copy to an\n/// Option.\ntemplate <typename CRTP>\nclass OptionBase {\n  friend App;\n\n protected:\n  /// The group membership\n  std::string group_ = std::string(\"Options\");\n\n  /// True if this is a required option\n  bool required_{false};\n\n  /// Ignore the case when matching (option, not value)\n  bool ignore_case_{false};\n\n  /// Ignore underscores when matching (option, not value)\n  bool ignore_underscore_{false};\n\n  /// Allow this option to be given in a configuration file\n  bool configurable_{true};\n\n  /// Disable overriding flag values with '=value'\n  bool disable_flag_override_{false};\n\n  /// Specify a delimiter character for vector arguments\n  char delimiter_{'\\0'};\n\n  /// Automatically capture default value\n  bool always_capture_default_{false};\n\n  /// Policy for handling multiple arguments beyond the expected Max\n  MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};\n\n  /// Copy the contents to another similar class (one based on OptionBase)\n  template <typename T>\n  void copy_to(T *other) const;\n\n public:\n  // setters\n\n  /// Changes the group membership\n  CRTP *group(const std::string &name) {\n    if (!detail::valid_alias_name_string(name)) {\n      throw IncorrectConstruction(\n          \"Group names may not contain newlines or null characters\");\n    }\n    group_ = name;\n    return static_cast<CRTP *>(this);\n  }\n\n  /// Set the option as required\n  CRTP *required(bool value = true) {\n    required_ = value;\n    return static_cast<CRTP *>(this);\n  }\n\n  /// Support Plumbum term\n  CRTP *mandatory(bool value = true) { return required(value); }\n\n  CRTP *always_capture_default(bool value = true) {\n    always_capture_default_ = value;\n    return static_cast<CRTP *>(this);\n  }\n\n  // Getters\n\n  /// Get the group of this option\n  CLI11_NODISCARD const std::string &get_group() const { return group_; }\n\n  /// True if this is a required option\n  CLI11_NODISCARD bool get_required() const { return required_; }\n\n  /// The status of ignore case\n  CLI11_NODISCARD bool get_ignore_case() const { return ignore_case_; }\n\n  /// The status of ignore_underscore\n  CLI11_NODISCARD bool get_ignore_underscore() const {\n    return ignore_underscore_;\n  }\n\n  /// The status of configurable\n  CLI11_NODISCARD bool get_configurable() const { return configurable_; }\n\n  /// The status of configurable\n  CLI11_NODISCARD bool get_disable_flag_override() const {\n    return disable_flag_override_;\n  }\n\n  /// Get the current delimiter char\n  CLI11_NODISCARD char get_delimiter() const { return delimiter_; }\n\n  /// Return true if this will automatically capture the default value for help\n  /// printing\n  CLI11_NODISCARD bool get_always_capture_default() const {\n    return always_capture_default_;\n  }\n\n  /// The status of the multi option policy\n  CLI11_NODISCARD MultiOptionPolicy get_multi_option_policy() const {\n    return multi_option_policy_;\n  }\n\n  // Shortcuts for multi option policy\n\n  /// Set the multi option policy to take last\n  CRTP *take_last() {\n    auto *self = static_cast<CRTP *>(this);\n    self->multi_option_policy(MultiOptionPolicy::TakeLast);\n    return self;\n  }\n\n  /// Set the multi option policy to take last\n  CRTP *take_first() {\n    auto *self = static_cast<CRTP *>(this);\n    self->multi_option_policy(MultiOptionPolicy::TakeFirst);\n    return self;\n  }\n\n  /// Set the multi option policy to take all arguments\n  CRTP *take_all() {\n    auto self = static_cast<CRTP *>(this);\n    self->multi_option_policy(MultiOptionPolicy::TakeAll);\n    return self;\n  }\n\n  /// Set the multi option policy to join\n  CRTP *join() {\n    auto *self = static_cast<CRTP *>(this);\n    self->multi_option_policy(MultiOptionPolicy::Join);\n    return self;\n  }\n\n  /// Set the multi option policy to join with a specific delimiter\n  CRTP *join(char delim) {\n    auto self = static_cast<CRTP *>(this);\n    self->delimiter_ = delim;\n    self->multi_option_policy(MultiOptionPolicy::Join);\n    return self;\n  }\n\n  /// Allow in a configuration file\n  CRTP *configurable(bool value = true) {\n    configurable_ = value;\n    return static_cast<CRTP *>(this);\n  }\n\n  /// Allow in a configuration file\n  CRTP *delimiter(char value = '\\0') {\n    delimiter_ = value;\n    return static_cast<CRTP *>(this);\n  }\n};\n\n/// This is a version of OptionBase that only supports setting values,\n/// for defaults. It is stored as the default option in an App.\nclass OptionDefaults : public OptionBase<OptionDefaults> {\n public:\n  OptionDefaults() = default;\n\n  // Methods here need a different implementation if they are Option vs.\n  // OptionDefault\n\n  /// Take the last argument if given multiple times\n  OptionDefaults *multi_option_policy(\n      MultiOptionPolicy value = MultiOptionPolicy::Throw) {\n    multi_option_policy_ = value;\n    return this;\n  }\n\n  /// Ignore the case of the option name\n  OptionDefaults *ignore_case(bool value = true) {\n    ignore_case_ = value;\n    return this;\n  }\n\n  /// Ignore underscores in the option name\n  OptionDefaults *ignore_underscore(bool value = true) {\n    ignore_underscore_ = value;\n    return this;\n  }\n\n  /// Disable overriding flag values with an '=<value>' segment\n  OptionDefaults *disable_flag_override(bool value = true) {\n    disable_flag_override_ = value;\n    return this;\n  }\n\n  /// set a delimiter character to split up single arguments to treat as\n  /// multiple inputs\n  OptionDefaults *delimiter(char value = '\\0') {\n    delimiter_ = value;\n    return this;\n  }\n};\n\nclass Option : public OptionBase<Option> {\n  friend App;\n\n protected:\n  /// @name Names\n  ///@{\n\n  /// A list of the short names (`-a`) without the leading dashes\n  std::vector<std::string> snames_{};\n\n  /// A list of the long names (`--long`) without the leading dashes\n  std::vector<std::string> lnames_{};\n\n  /// A list of the flag names with the appropriate default value, the first\n  /// part of the pair should be duplicates of what is in snames or lnames but\n  /// will trigger a particular response on a flag\n  std::vector<std::pair<std::string, std::string>> default_flag_values_{};\n\n  /// a list of flag names with specified default values;\n  std::vector<std::string> fnames_{};\n\n  /// A positional name\n  std::string pname_{};\n\n  /// If given, check the environment for this option\n  std::string envname_{};\n\n  ///@}\n  /// @name Help\n  ///@{\n\n  /// The description for help strings\n  std::string description_{};\n\n  /// A human readable default value, either manually set, captured, or captured\n  /// by default\n  std::string default_str_{};\n\n  /// If given, replace the text that describes the option type and usage in the\n  /// help text\n  std::string option_text_{};\n\n  /// A human readable type value, set when App creates this\n  ///\n  /// This is a lambda function so \"types\" can be dynamic, such as when a set\n  /// prints its contents.\n  std::function<std::string()> type_name_{[]() { return std::string(); }};\n\n  /// Run this function to capture a default (ignore if empty)\n  std::function<std::string()> default_function_{};\n\n  ///@}\n  /// @name Configuration\n  ///@{\n\n  /// The number of arguments that make up one option. max is the nominal type\n  /// size, min is the minimum number of strings\n  int type_size_max_{1};\n  /// The minimum number of arguments an option should be expecting\n  int type_size_min_{1};\n\n  /// The minimum number of expected values\n  int expected_min_{1};\n  /// The maximum number of expected values\n  int expected_max_{1};\n\n  /// A list of Validators to run on each value parsed\n  std::vector<Validator> validators_{};\n\n  /// A list of options that are required with this option\n  std::set<Option *> needs_{};\n\n  /// A list of options that are excluded with this option\n  std::set<Option *> excludes_{};\n\n  ///@}\n  /// @name Other\n  ///@{\n\n  /// link back up to the parent App for fallthrough\n  App *parent_{nullptr};\n\n  /// Options store a callback to do all the work\n  callback_t callback_{};\n\n  ///@}\n  /// @name Parsing results\n  ///@{\n\n  /// complete Results of parsing\n  results_t results_{};\n  /// results after reduction\n  results_t proc_results_{};\n  /// enumeration for the option state machine\n  enum class option_state : char {\n    parsing = 0,       //!< The option is currently collecting parsed results\n    validated = 2,     //!< the results have been validated\n    reduced = 4,       //!< a subset of results has been generated\n    callback_run = 6,  //!< the callback has been executed\n  };\n  /// Whether the callback has run (needed for INI parsing)\n  option_state current_option_state_{option_state::parsing};\n  /// Specify that extra args beyond type_size_max should be allowed\n  bool allow_extra_args_{false};\n  /// Specify that the option should act like a flag vs regular option\n  bool flag_like_{false};\n  /// Control option to run the callback to set the default\n  bool run_callback_for_default_{false};\n  /// flag indicating a separator needs to be injected after each argument call\n  bool inject_separator_{false};\n  /// flag indicating that the option should trigger the validation and callback\n  /// chain on each result when loaded\n  bool trigger_on_result_{false};\n  /// flag indicating that the option should force the callback regardless if\n  /// any results present\n  bool force_callback_{false};\n  ///@}\n\n  /// Making an option by hand is not defined, it must be made by the App class\n  Option(std::string option_name, std::string option_description,\n         callback_t callback, App *parent)\n      : description_(std::move(option_description)),\n        parent_(parent),\n        callback_(std::move(callback)) {\n    std::tie(snames_, lnames_, pname_) =\n        detail::get_names(detail::split_names(option_name));\n  }\n\n public:\n  /// @name Basic\n  ///@{\n\n  Option(const Option &) = delete;\n  Option &operator=(const Option &) = delete;\n\n  /// Count the total number of times an option was passed\n  CLI11_NODISCARD std::size_t count() const { return results_.size(); }\n\n  /// True if the option was not passed\n  CLI11_NODISCARD bool empty() const { return results_.empty(); }\n\n  /// This bool operator returns true if any arguments were passed or the option\n  /// callback is forced\n  explicit operator bool() const { return !empty() || force_callback_; }\n\n  /// Clear the parsed results (mostly for testing)\n  void clear() {\n    results_.clear();\n    current_option_state_ = option_state::parsing;\n  }\n\n  ///@}\n  /// @name Setting options\n  ///@{\n\n  /// Set the number of expected arguments\n  Option *expected(int value);\n\n  /// Set the range of expected arguments\n  Option *expected(int value_min, int value_max);\n\n  /// Set the value of allow_extra_args which allows extra value arguments on\n  /// the flag or option to be included with each instance\n  Option *allow_extra_args(bool value = true) {\n    allow_extra_args_ = value;\n    return this;\n  }\n  /// Get the current value of allow extra args\n  CLI11_NODISCARD bool get_allow_extra_args() const {\n    return allow_extra_args_;\n  }\n  /// Set the value of trigger_on_parse which specifies that the option callback\n  /// should be triggered on every parse\n  Option *trigger_on_parse(bool value = true) {\n    trigger_on_result_ = value;\n    return this;\n  }\n  /// The status of trigger on parse\n  CLI11_NODISCARD bool get_trigger_on_parse() const {\n    return trigger_on_result_;\n  }\n\n  /// Set the value of force_callback\n  Option *force_callback(bool value = true) {\n    force_callback_ = value;\n    return this;\n  }\n  /// The status of force_callback\n  CLI11_NODISCARD bool get_force_callback() const { return force_callback_; }\n\n  /// Set the value of run_callback_for_default which controls whether the\n  /// callback function should be called to set the default This is controlled\n  /// automatically but could be manipulated by the user.\n  Option *run_callback_for_default(bool value = true) {\n    run_callback_for_default_ = value;\n    return this;\n  }\n  /// Get the current value of run_callback_for_default\n  CLI11_NODISCARD bool get_run_callback_for_default() const {\n    return run_callback_for_default_;\n  }\n\n  /// Adds a Validator with a built in type name\n  Option *check(Validator validator, const std::string &validator_name = \"\");\n\n  /// Adds a Validator. Takes a const string& and returns an error message\n  /// (empty if conversion/check is okay).\n  Option *check(std::function<std::string(const std::string &)> Validator,\n                std::string Validator_description = \"\",\n                std::string Validator_name = \"\");\n\n  /// Adds a transforming Validator with a built in type name\n  Option *transform(Validator Validator,\n                    const std::string &Validator_name = \"\");\n\n  /// Adds a Validator-like function that can change result\n  Option *transform(const std::function<std::string(std::string)> &func,\n                    std::string transform_description = \"\",\n                    std::string transform_name = \"\");\n\n  /// Adds a user supplied function to run on each item passed in (communicate\n  /// though lambda capture)\n  Option *each(const std::function<void(std::string)> &func);\n\n  /// Get a named Validator\n  Validator *get_validator(const std::string &Validator_name = \"\");\n\n  /// Get a Validator by index NOTE: this may not be the order of definition\n  Validator *get_validator(int index);\n\n  /// Sets required options\n  Option *needs(Option *opt) {\n    if (opt != this) {\n      needs_.insert(opt);\n    }\n    return this;\n  }\n\n  /// Can find a string if needed\n  template <typename T = App>\n  Option *needs(std::string opt_name) {\n    auto opt = static_cast<T *>(parent_)->get_option_no_throw(opt_name);\n    if (opt == nullptr) {\n      throw IncorrectConstruction::MissingOption(opt_name);\n    }\n    return needs(opt);\n  }\n\n  /// Any number supported, any mix of string and Opt\n  template <typename A, typename B, typename... ARG>\n  Option *needs(A opt, B opt1, ARG... args) {\n    needs(opt);\n    return needs(opt1,\n                 args...);  // NOLINT(readability-suspicious-call-argument)\n  }\n\n  /// Remove needs link from an option. Returns true if the option really was in\n  /// the needs list.\n  bool remove_needs(Option *opt);\n\n  /// Sets excluded options\n  Option *excludes(Option *opt);\n\n  /// Can find a string if needed\n  template <typename T = App>\n  Option *excludes(std::string opt_name) {\n    auto opt = static_cast<T *>(parent_)->get_option_no_throw(opt_name);\n    if (opt == nullptr) {\n      throw IncorrectConstruction::MissingOption(opt_name);\n    }\n    return excludes(opt);\n  }\n\n  /// Any number supported, any mix of string and Opt\n  template <typename A, typename B, typename... ARG>\n  Option *excludes(A opt, B opt1, ARG... args) {\n    excludes(opt);\n    return excludes(opt1, args...);\n  }\n\n  /// Remove needs link from an option. Returns true if the option really was in\n  /// the needs list.\n  bool remove_excludes(Option *opt);\n\n  /// Sets environment variable to read if no option given\n  Option *envname(std::string name) {\n    envname_ = std::move(name);\n    return this;\n  }\n\n  /// Ignore case\n  ///\n  /// The template hides the fact that we don't have the definition of App yet.\n  /// You are never expected to add an argument to the template here.\n  template <typename T = App>\n  Option *ignore_case(bool value = true);\n\n  /// Ignore underscores in the option names\n  ///\n  /// The template hides the fact that we don't have the definition of App yet.\n  /// You are never expected to add an argument to the template here.\n  template <typename T = App>\n  Option *ignore_underscore(bool value = true);\n\n  /// Take the last argument if given multiple times (or another policy)\n  Option *multi_option_policy(\n      MultiOptionPolicy value = MultiOptionPolicy::Throw);\n\n  /// Disable flag overrides values, e.g. --flag=<value> is not allowed\n  Option *disable_flag_override(bool value = true) {\n    disable_flag_override_ = value;\n    return this;\n  }\n  ///@}\n  /// @name Accessors\n  ///@{\n\n  /// The number of arguments the option expects\n  CLI11_NODISCARD int get_type_size() const { return type_size_min_; }\n\n  /// The minimum number of arguments the option expects\n  CLI11_NODISCARD int get_type_size_min() const { return type_size_min_; }\n  /// The maximum number of arguments the option expects\n  CLI11_NODISCARD int get_type_size_max() const { return type_size_max_; }\n\n  /// Return the inject_separator flag\n  CLI11_NODISCARD bool get_inject_separator() const {\n    return inject_separator_;\n  }\n\n  /// The environment variable associated to this value\n  CLI11_NODISCARD std::string get_envname() const { return envname_; }\n\n  /// The set of options needed\n  CLI11_NODISCARD std::set<Option *> get_needs() const { return needs_; }\n\n  /// The set of options excluded\n  CLI11_NODISCARD std::set<Option *> get_excludes() const { return excludes_; }\n\n  /// The default value (for help printing)\n  CLI11_NODISCARD std::string get_default_str() const { return default_str_; }\n\n  /// Get the callback function\n  CLI11_NODISCARD callback_t get_callback() const { return callback_; }\n\n  /// Get the long names\n  CLI11_NODISCARD const std::vector<std::string> &get_lnames() const {\n    return lnames_;\n  }\n\n  /// Get the short names\n  CLI11_NODISCARD const std::vector<std::string> &get_snames() const {\n    return snames_;\n  }\n\n  /// Get the flag names with specified default values\n  CLI11_NODISCARD const std::vector<std::string> &get_fnames() const {\n    return fnames_;\n  }\n  /// Get a single name for the option, first of lname, pname, sname, envname\n  CLI11_NODISCARD const std::string &get_single_name() const {\n    if (!lnames_.empty()) {\n      return lnames_[0];\n    }\n    if (!pname_.empty()) {\n      return pname_;\n    }\n    if (!snames_.empty()) {\n      return snames_[0];\n    }\n    return envname_;\n  }\n  /// The number of times the option expects to be included\n  CLI11_NODISCARD int get_expected() const { return expected_min_; }\n\n  /// The number of times the option expects to be included\n  CLI11_NODISCARD int get_expected_min() const { return expected_min_; }\n  /// The max number of times the option expects to be included\n  CLI11_NODISCARD int get_expected_max() const { return expected_max_; }\n\n  /// The total min number of expected  string values to be used\n  CLI11_NODISCARD int get_items_expected_min() const {\n    return type_size_min_ * expected_min_;\n  }\n\n  /// Get the maximum number of items expected to be returned and used for the\n  /// callback\n  CLI11_NODISCARD int get_items_expected_max() const {\n    int t = type_size_max_;\n    return detail::checked_multiply(t, expected_max_)\n               ? t\n               : detail::expected_max_vector_size;\n  }\n  /// The total min number of expected  string values to be used\n  CLI11_NODISCARD int get_items_expected() const {\n    return get_items_expected_min();\n  }\n\n  /// True if the argument can be given directly\n  CLI11_NODISCARD bool get_positional() const { return pname_.length() > 0; }\n\n  /// True if option has at least one non-positional name\n  CLI11_NODISCARD bool nonpositional() const {\n    return (snames_.size() + lnames_.size()) > 0;\n  }\n\n  /// True if option has description\n  CLI11_NODISCARD bool has_description() const {\n    return description_.length() > 0;\n  }\n\n  /// Get the description\n  CLI11_NODISCARD const std::string &get_description() const {\n    return description_;\n  }\n\n  /// Set the description\n  Option *description(std::string option_description) {\n    description_ = std::move(option_description);\n    return this;\n  }\n\n  Option *option_text(std::string text) {\n    option_text_ = std::move(text);\n    return this;\n  }\n\n  CLI11_NODISCARD const std::string &get_option_text() const {\n    return option_text_;\n  }\n\n  ///@}\n  /// @name Help tools\n  ///@{\n\n  /// \\brief Gets a comma separated list of names.\n  /// Will include / prefer the positional name if positional is true.\n  /// If all_options is false, pick just the most descriptive name to show.\n  /// Use `get_name(true)` to get the positional name (replaces `get_pname`)\n  CLI11_NODISCARD std::string get_name(\n      bool positional = false,  ///< Show the positional name\n      bool all_options = false  ///< Show every option\n  ) const;\n\n  ///@}\n  /// @name Parser tools\n  ///@{\n\n  /// Process the callback\n  void run_callback();\n\n  /// If options share any of the same names, find it\n  CLI11_NODISCARD const std::string &matching_name(const Option &other) const;\n\n  /// If options share any of the same names, they are equal (not counting\n  /// positional)\n  bool operator==(const Option &other) const {\n    return !matching_name(other).empty();\n  }\n\n  /// Check a name. Requires \"-\" or \"--\" for short / long, supports positional\n  /// name\n  CLI11_NODISCARD bool check_name(const std::string &name) const;\n\n  /// Requires \"-\" to be removed from string\n  CLI11_NODISCARD bool check_sname(std::string name) const {\n    return (detail::find_member(std::move(name), snames_, ignore_case_) >= 0);\n  }\n\n  /// Requires \"--\" to be removed from string\n  CLI11_NODISCARD bool check_lname(std::string name) const {\n    return (detail::find_member(std::move(name), lnames_, ignore_case_,\n                                ignore_underscore_) >= 0);\n  }\n\n  /// Requires \"--\" to be removed from string\n  CLI11_NODISCARD bool check_fname(std::string name) const {\n    if (fnames_.empty()) {\n      return false;\n    }\n    return (detail::find_member(std::move(name), fnames_, ignore_case_,\n                                ignore_underscore_) >= 0);\n  }\n\n  /// Get the value that goes for a flag, nominally gets the default value but\n  /// allows for overrides if not disabled\n  CLI11_NODISCARD std::string get_flag_value(const std::string &name,\n                                             std::string input_value) const;\n\n  /// Puts a result at the end\n  Option *add_result(std::string s);\n\n  /// Puts a result at the end and get a count of the number of arguments\n  /// actually added\n  Option *add_result(std::string s, int &results_added);\n\n  /// Puts a result at the end\n  Option *add_result(std::vector<std::string> s);\n\n  /// Get the current complete results set\n  CLI11_NODISCARD const results_t &results() const { return results_; }\n\n  /// Get a copy of the results\n  CLI11_NODISCARD results_t reduced_results() const;\n\n  /// Get the results as a specified type\n  template <typename T>\n  void results(T &output) const {\n    bool retval = false;\n    if (current_option_state_ >= option_state::reduced ||\n        (results_.size() == 1 && validators_.empty())) {\n      const results_t &res = (proc_results_.empty()) ? results_ : proc_results_;\n      retval = detail::lexical_conversion<T, T>(res, output);\n    } else {\n      results_t res;\n      if (results_.empty()) {\n        if (!default_str_.empty()) {\n          // _add_results takes an rvalue only\n          _add_result(std::string(default_str_), res);\n          _validate_results(res);\n          results_t extra;\n          _reduce_results(extra, res);\n          if (!extra.empty()) {\n            res = std::move(extra);\n          }\n        } else {\n          res.emplace_back();\n        }\n      } else {\n        res = reduced_results();\n      }\n      retval = detail::lexical_conversion<T, T>(res, output);\n    }\n    if (!retval) {\n      throw ConversionError(get_name(), results_);\n    }\n  }\n\n  /// Return the results as the specified type\n  template <typename T>\n  CLI11_NODISCARD T as() const {\n    T output;\n    results(output);\n    return output;\n  }\n\n  /// See if the callback has been run already\n  CLI11_NODISCARD bool get_callback_run() const {\n    return (current_option_state_ == option_state::callback_run);\n  }\n\n  ///@}\n  /// @name Custom options\n  ///@{\n\n  /// Set the type function to run when displayed on this option\n  Option *type_name_fn(std::function<std::string()> typefun) {\n    type_name_ = std::move(typefun);\n    return this;\n  }\n\n  /// Set a custom option typestring\n  Option *type_name(std::string typeval) {\n    type_name_fn([typeval]() { return typeval; });\n    return this;\n  }\n\n  /// Set a custom option size\n  Option *type_size(int option_type_size);\n\n  /// Set a custom option type size range\n  Option *type_size(int option_type_size_min, int option_type_size_max);\n\n  /// Set the value of the separator injection flag\n  void inject_separator(bool value = true) { inject_separator_ = value; }\n\n  /// Set a capture function for the default. Mostly used by App.\n  Option *default_function(const std::function<std::string()> &func) {\n    default_function_ = func;\n    return this;\n  }\n\n  /// Capture the default value from the original value (if it can be captured)\n  Option *capture_default_str() {\n    if (default_function_) {\n      default_str_ = default_function_();\n    }\n    return this;\n  }\n\n  /// Set the default value string representation (does not change the contained\n  /// value)\n  Option *default_str(std::string val) {\n    default_str_ = std::move(val);\n    return this;\n  }\n\n  /// Set the default value and validate the results and run the callback if\n  /// appropriate to set the value into the bound value only available for types\n  /// that can be converted to a string\n  template <typename X>\n  Option *default_val(const X &val) {\n    std::string val_str = detail::to_string(val);\n    auto old_option_state = current_option_state_;\n    results_t old_results{std::move(results_)};\n    results_.clear();\n    try {\n      add_result(val_str);\n      // if trigger_on_result_ is set the callback already ran\n      if (run_callback_for_default_ && !trigger_on_result_) {\n        run_callback();  // run callback sets the state, we need to reset it\n                         // again\n        current_option_state_ = option_state::parsing;\n      } else {\n        _validate_results(results_);\n        current_option_state_ = old_option_state;\n      }\n    } catch (const CLI::Error &) {\n      // this should be done\n      results_ = std::move(old_results);\n      current_option_state_ = old_option_state;\n      throw;\n    }\n    results_ = std::move(old_results);\n    default_str_ = std::move(val_str);\n    return this;\n  }\n\n  /// Get the full typename for this option\n  CLI11_NODISCARD std::string get_type_name() const;\n\n private:\n  /// Run the results through the Validators\n  void _validate_results(results_t &res) const;\n\n  /** reduce the results in accordance with the MultiOptionPolicy\n  @param[out] out results are assigned to res if there if they are different\n  */\n  void _reduce_results(results_t &out, const results_t &original) const;\n\n  // Run a result through the Validators\n  std::string _validate(std::string &result, int index) const;\n\n  /// Add a single result to the result set, taking into account delimiters\n  int _add_result(std::string &&result, std::vector<std::string> &res) const;\n};\n\ntemplate <typename CRTP>\ntemplate <typename T>\nvoid OptionBase<CRTP>::copy_to(T *other) const {\n  other->group(group_);\n  other->required(required_);\n  other->ignore_case(ignore_case_);\n  other->ignore_underscore(ignore_underscore_);\n  other->configurable(configurable_);\n  other->disable_flag_override(disable_flag_override_);\n  other->delimiter(delimiter_);\n  other->always_capture_default(always_capture_default_);\n  other->multi_option_policy(multi_option_policy_);\n}\n\nCLI11_INLINE Option *Option::expected(int value) {\n  if (value < 0) {\n    expected_min_ = -value;\n    if (expected_max_ < expected_min_) {\n      expected_max_ = expected_min_;\n    }\n    allow_extra_args_ = true;\n    flag_like_ = false;\n  } else if (value == detail::expected_max_vector_size) {\n    expected_min_ = 1;\n    expected_max_ = detail::expected_max_vector_size;\n    allow_extra_args_ = true;\n    flag_like_ = false;\n  } else {\n    expected_min_ = value;\n    expected_max_ = value;\n    flag_like_ = (expected_min_ == 0);\n  }\n  return this;\n}\n\nCLI11_INLINE Option *Option::expected(int value_min, int value_max) {\n  if (value_min < 0) {\n    value_min = -value_min;\n  }\n\n  if (value_max < 0) {\n    value_max = detail::expected_max_vector_size;\n  }\n  if (value_max < value_min) {\n    expected_min_ = value_max;\n    expected_max_ = value_min;\n  } else {\n    expected_max_ = value_max;\n    expected_min_ = value_min;\n  }\n\n  return this;\n}\n\nCLI11_INLINE Option *Option::check(Validator validator,\n                                   const std::string &validator_name) {\n  validator.non_modifying();\n  validators_.push_back(std::move(validator));\n  if (!validator_name.empty()) validators_.back().name(validator_name);\n  return this;\n}\n\nCLI11_INLINE Option *Option::check(\n    std::function<std::string(const std::string &)> Validator,\n    std::string Validator_description, std::string Validator_name) {\n  validators_.emplace_back(Validator, std::move(Validator_description),\n                           std::move(Validator_name));\n  validators_.back().non_modifying();\n  return this;\n}\n\nCLI11_INLINE Option *Option::transform(Validator Validator,\n                                       const std::string &Validator_name) {\n  validators_.insert(validators_.begin(), std::move(Validator));\n  if (!Validator_name.empty()) validators_.front().name(Validator_name);\n  return this;\n}\n\nCLI11_INLINE Option *Option::transform(\n    const std::function<std::string(std::string)> &func,\n    std::string transform_description, std::string transform_name) {\n  validators_.insert(validators_.begin(), Validator(\n                                              [func](std::string &val) {\n                                                val = func(val);\n                                                return std::string{};\n                                              },\n                                              std::move(transform_description),\n                                              std::move(transform_name)));\n\n  return this;\n}\n\nCLI11_INLINE Option *Option::each(\n    const std::function<void(std::string)> &func) {\n  validators_.emplace_back(\n      [func](std::string &inout) {\n        func(inout);\n        return std::string{};\n      },\n      std::string{});\n  return this;\n}\n\nCLI11_INLINE Validator *Option::get_validator(\n    const std::string &Validator_name) {\n  for (auto &Validator : validators_) {\n    if (Validator_name == Validator.get_name()) {\n      return &Validator;\n    }\n  }\n  if ((Validator_name.empty()) && (!validators_.empty())) {\n    return &(validators_.front());\n  }\n  throw OptionNotFound(std::string{\"Validator \"} + Validator_name +\n                       \" Not Found\");\n}\n\nCLI11_INLINE Validator *Option::get_validator(int index) {\n  // This is an signed int so that it is not equivalent to a pointer.\n  if (index >= 0 && index < static_cast<int>(validators_.size())) {\n    return &(validators_[static_cast<decltype(validators_)::size_type>(index)]);\n  }\n  throw OptionNotFound(\"Validator index is not valid\");\n}\n\nCLI11_INLINE bool Option::remove_needs(Option *opt) {\n  auto iterator = std::find(std::begin(needs_), std::end(needs_), opt);\n\n  if (iterator == std::end(needs_)) {\n    return false;\n  }\n  needs_.erase(iterator);\n  return true;\n}\n\nCLI11_INLINE Option *Option::excludes(Option *opt) {\n  if (opt == this) {\n    throw(IncorrectConstruction(\"and option cannot exclude itself\"));\n  }\n  excludes_.insert(opt);\n\n  // Help text should be symmetric - excluding a should exclude b\n  opt->excludes_.insert(this);\n\n  // Ignoring the insert return value, excluding twice is now allowed.\n  // (Mostly to allow both directions to be excluded by user, even though the\n  // library does it for you.)\n\n  return this;\n}\n\nCLI11_INLINE bool Option::remove_excludes(Option *opt) {\n  auto iterator = std::find(std::begin(excludes_), std::end(excludes_), opt);\n\n  if (iterator == std::end(excludes_)) {\n    return false;\n  }\n  excludes_.erase(iterator);\n  return true;\n}\n\ntemplate <typename T>\nOption *Option::ignore_case(bool value) {\n  if (!ignore_case_ && value) {\n    ignore_case_ = value;\n    auto *parent = static_cast<T *>(parent_);\n    for (const Option_p &opt : parent->options_) {\n      if (opt.get() == this) {\n        continue;\n      }\n      const auto &omatch = opt->matching_name(*this);\n      if (!omatch.empty()) {\n        ignore_case_ = false;\n        throw OptionAlreadyAdded(\n            \"adding ignore case caused a name conflict with \" + omatch);\n      }\n    }\n  } else {\n    ignore_case_ = value;\n  }\n  return this;\n}\n\ntemplate <typename T>\nOption *Option::ignore_underscore(bool value) {\n  if (!ignore_underscore_ && value) {\n    ignore_underscore_ = value;\n    auto *parent = static_cast<T *>(parent_);\n    for (const Option_p &opt : parent->options_) {\n      if (opt.get() == this) {\n        continue;\n      }\n      const auto &omatch = opt->matching_name(*this);\n      if (!omatch.empty()) {\n        ignore_underscore_ = false;\n        throw OptionAlreadyAdded(\n            \"adding ignore underscore caused a name conflict with \" + omatch);\n      }\n    }\n  } else {\n    ignore_underscore_ = value;\n  }\n  return this;\n}\n\nCLI11_INLINE Option *Option::multi_option_policy(MultiOptionPolicy value) {\n  if (value != multi_option_policy_) {\n    if (multi_option_policy_ == MultiOptionPolicy::Throw &&\n        expected_max_ == detail::expected_max_vector_size &&\n        expected_min_ > 1) {  // this bizarre condition is to maintain backwards\n                              // compatibility with the previous behavior of\n                              // expected_ with vectors\n      expected_max_ = expected_min_;\n    }\n    multi_option_policy_ = value;\n    current_option_state_ = option_state::parsing;\n  }\n  return this;\n}\n\nCLI11_NODISCARD CLI11_INLINE std::string Option::get_name(\n    bool positional, bool all_options) const {\n  if (get_group().empty()) return {};  // Hidden\n\n  if (all_options) {\n    std::vector<std::string> name_list;\n\n    /// The all list will never include a positional unless asked or that's the\n    /// only name.\n    if ((positional && (!pname_.empty())) ||\n        (snames_.empty() && lnames_.empty())) {\n      name_list.push_back(pname_);\n    }\n    if ((get_items_expected() == 0) && (!fnames_.empty())) {\n      for (const std::string &sname : snames_) {\n        name_list.push_back(\"-\" + sname);\n        if (check_fname(sname)) {\n          name_list.back() += \"{\" + get_flag_value(sname, \"\") + \"}\";\n        }\n      }\n\n      for (const std::string &lname : lnames_) {\n        name_list.push_back(\"--\" + lname);\n        if (check_fname(lname)) {\n          name_list.back() += \"{\" + get_flag_value(lname, \"\") + \"}\";\n        }\n      }\n    } else {\n      for (const std::string &sname : snames_) name_list.push_back(\"-\" + sname);\n\n      for (const std::string &lname : lnames_)\n        name_list.push_back(\"--\" + lname);\n    }\n\n    return detail::join(name_list);\n  }\n\n  // This returns the positional name no matter what\n  if (positional) return pname_;\n\n  // Prefer long name\n  if (!lnames_.empty()) return std::string(2, '-') + lnames_[0];\n\n  // Or short name if no long name\n  if (!snames_.empty()) return std::string(1, '-') + snames_[0];\n\n  // If positional is the only name, it's okay to use that\n  return pname_;\n}\n\nCLI11_INLINE void Option::run_callback() {\n  if (force_callback_ && results_.empty()) {\n    add_result(default_str_);\n  }\n  if (current_option_state_ == option_state::parsing) {\n    _validate_results(results_);\n    current_option_state_ = option_state::validated;\n  }\n\n  if (current_option_state_ < option_state::reduced) {\n    _reduce_results(proc_results_, results_);\n    current_option_state_ = option_state::reduced;\n  }\n  if (current_option_state_ >= option_state::reduced) {\n    current_option_state_ = option_state::callback_run;\n    if (!(callback_)) {\n      return;\n    }\n    const results_t &send_results =\n        proc_results_.empty() ? results_ : proc_results_;\n    bool local_result = callback_(send_results);\n\n    if (!local_result) throw ConversionError(get_name(), results_);\n  }\n}\n\nCLI11_NODISCARD CLI11_INLINE const std::string &Option::matching_name(\n    const Option &other) const {\n  static const std::string estring;\n  for (const std::string &sname : snames_)\n    if (other.check_sname(sname)) return sname;\n  for (const std::string &lname : lnames_)\n    if (other.check_lname(lname)) return lname;\n\n  if (ignore_case_ ||\n      ignore_underscore_) {  // We need to do the inverse, in case we are\n                             // ignore_case or ignore underscore\n    for (const std::string &sname : other.snames_)\n      if (check_sname(sname)) return sname;\n    for (const std::string &lname : other.lnames_)\n      if (check_lname(lname)) return lname;\n  }\n  return estring;\n}\n\nCLI11_NODISCARD CLI11_INLINE bool Option::check_name(\n    const std::string &name) const {\n  if (name.length() > 2 && name[0] == '-' && name[1] == '-')\n    return check_lname(name.substr(2));\n  if (name.length() > 1 && name.front() == '-')\n    return check_sname(name.substr(1));\n  if (!pname_.empty()) {\n    std::string local_pname = pname_;\n    std::string local_name = name;\n    if (ignore_underscore_) {\n      local_pname = detail::remove_underscore(local_pname);\n      local_name = detail::remove_underscore(local_name);\n    }\n    if (ignore_case_) {\n      local_pname = detail::to_lower(local_pname);\n      local_name = detail::to_lower(local_name);\n    }\n    if (local_name == local_pname) {\n      return true;\n    }\n  }\n\n  if (!envname_.empty()) {\n    // this needs to be the original since envname_ shouldn't match on case\n    // insensitivity\n    return (name == envname_);\n  }\n  return false;\n}\n\nCLI11_NODISCARD CLI11_INLINE std::string Option::get_flag_value(\n    const std::string &name, std::string input_value) const {\n  static const std::string trueString{\"true\"};\n  static const std::string falseString{\"false\"};\n  static const std::string emptyString{\"{}\"};\n  // check for disable flag override_\n  if (disable_flag_override_) {\n    if (!((input_value.empty()) || (input_value == emptyString))) {\n      auto default_ind =\n          detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);\n      if (default_ind >= 0) {\n        // We can static cast this to std::size_t because it is more than 0 in\n        // this block\n        if (default_flag_values_[static_cast<std::size_t>(default_ind)]\n                .second != input_value) {\n          throw(ArgumentMismatch::FlagOverride(name));\n        }\n      } else {\n        if (input_value != trueString) {\n          throw(ArgumentMismatch::FlagOverride(name));\n        }\n      }\n    }\n  }\n  auto ind =\n      detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);\n  if ((input_value.empty()) || (input_value == emptyString)) {\n    if (flag_like_) {\n      return (ind < 0)\n                 ? trueString\n                 : default_flag_values_[static_cast<std::size_t>(ind)].second;\n    }\n    return (ind < 0)\n               ? default_str_\n               : default_flag_values_[static_cast<std::size_t>(ind)].second;\n  }\n  if (ind < 0) {\n    return input_value;\n  }\n  if (default_flag_values_[static_cast<std::size_t>(ind)].second ==\n      falseString) {\n    try {\n      auto val = detail::to_flag_value(input_value);\n      return (val == 1) ? falseString\n                        : (val == (-1) ? trueString : std::to_string(-val));\n    } catch (const std::invalid_argument &) {\n      return input_value;\n    }\n  } else {\n    return input_value;\n  }\n}\n\nCLI11_INLINE Option *Option::add_result(std::string s) {\n  _add_result(std::move(s), results_);\n  current_option_state_ = option_state::parsing;\n  return this;\n}\n\nCLI11_INLINE Option *Option::add_result(std::string s, int &results_added) {\n  results_added = _add_result(std::move(s), results_);\n  current_option_state_ = option_state::parsing;\n  return this;\n}\n\nCLI11_INLINE Option *Option::add_result(std::vector<std::string> s) {\n  current_option_state_ = option_state::parsing;\n  for (auto &str : s) {\n    _add_result(std::move(str), results_);\n  }\n  return this;\n}\n\nCLI11_NODISCARD CLI11_INLINE results_t Option::reduced_results() const {\n  results_t res = proc_results_.empty() ? results_ : proc_results_;\n  if (current_option_state_ < option_state::reduced) {\n    if (current_option_state_ == option_state::parsing) {\n      res = results_;\n      _validate_results(res);\n    }\n    if (!res.empty()) {\n      results_t extra;\n      _reduce_results(extra, res);\n      if (!extra.empty()) {\n        res = std::move(extra);\n      }\n    }\n  }\n  return res;\n}\n\nCLI11_INLINE Option *Option::type_size(int option_type_size) {\n  if (option_type_size < 0) {\n    // this section is included for backwards compatibility\n    type_size_max_ = -option_type_size;\n    type_size_min_ = -option_type_size;\n    expected_max_ = detail::expected_max_vector_size;\n  } else {\n    type_size_max_ = option_type_size;\n    if (type_size_max_ < detail::expected_max_vector_size) {\n      type_size_min_ = option_type_size;\n    } else {\n      inject_separator_ = true;\n    }\n    if (type_size_max_ == 0) required_ = false;\n  }\n  return this;\n}\n\nCLI11_INLINE Option *Option::type_size(int option_type_size_min,\n                                       int option_type_size_max) {\n  if (option_type_size_min < 0 || option_type_size_max < 0) {\n    // this section is included for backwards compatibility\n    expected_max_ = detail::expected_max_vector_size;\n    option_type_size_min = (std::abs)(option_type_size_min);\n    option_type_size_max = (std::abs)(option_type_size_max);\n  }\n\n  if (option_type_size_min > option_type_size_max) {\n    type_size_max_ = option_type_size_min;\n    type_size_min_ = option_type_size_max;\n  } else {\n    type_size_min_ = option_type_size_min;\n    type_size_max_ = option_type_size_max;\n  }\n  if (type_size_max_ == 0) {\n    required_ = false;\n  }\n  if (type_size_max_ >= detail::expected_max_vector_size) {\n    inject_separator_ = true;\n  }\n  return this;\n}\n\nCLI11_NODISCARD CLI11_INLINE std::string Option::get_type_name() const {\n  std::string full_type_name = type_name_();\n  if (!validators_.empty()) {\n    for (const auto &Validator : validators_) {\n      std::string vtype = Validator.get_description();\n      if (!vtype.empty()) {\n        full_type_name += \":\" + vtype;\n      }\n    }\n  }\n  return full_type_name;\n}\n\nCLI11_INLINE void Option::_validate_results(results_t &res) const {\n  // Run the Validators (can change the string)\n  if (!validators_.empty()) {\n    if (type_size_max_ >\n        1) {  // in this context index refers to the index in the type\n      int index = 0;\n      if (get_items_expected_max() < static_cast<int>(res.size()) &&\n          multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {\n        // create a negative index for the earliest ones\n        index = get_items_expected_max() - static_cast<int>(res.size());\n      }\n\n      for (std::string &result : res) {\n        if (detail::is_separator(result) && type_size_max_ != type_size_min_ &&\n            index >= 0) {\n          index = 0;  // reset index for variable size chunks\n          continue;\n        }\n        auto err_msg =\n            _validate(result, (index >= 0) ? (index % type_size_max_) : index);\n        if (!err_msg.empty()) throw ValidationError(get_name(), err_msg);\n        ++index;\n      }\n    } else {\n      int index = 0;\n      if (expected_max_ < static_cast<int>(res.size()) &&\n          multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {\n        // create a negative index for the earliest ones\n        index = expected_max_ - static_cast<int>(res.size());\n      }\n      for (std::string &result : res) {\n        auto err_msg = _validate(result, index);\n        ++index;\n        if (!err_msg.empty()) throw ValidationError(get_name(), err_msg);\n      }\n    }\n  }\n}\n\nCLI11_INLINE void Option::_reduce_results(results_t &out,\n                                          const results_t &original) const {\n  // max num items expected or length of vector, always at least 1\n  // Only valid for a trimming policy\n\n  out.clear();\n  // Operation depends on the policy setting\n  switch (multi_option_policy_) {\n    case MultiOptionPolicy::TakeAll:\n      break;\n    case MultiOptionPolicy::TakeLast: {\n      // Allow multi-option sizes (including 0)\n      std::size_t trim_size = std::min<std::size_t>(\n          static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)),\n          original.size());\n      if (original.size() != trim_size) {\n        out.assign(\n            original.end() - static_cast<results_t::difference_type>(trim_size),\n            original.end());\n      }\n    } break;\n    case MultiOptionPolicy::TakeFirst: {\n      std::size_t trim_size = std::min<std::size_t>(\n          static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)),\n          original.size());\n      if (original.size() != trim_size) {\n        out.assign(original.begin(),\n                   original.begin() +\n                       static_cast<results_t::difference_type>(trim_size));\n      }\n    } break;\n    case MultiOptionPolicy::Join:\n      if (results_.size() > 1) {\n        out.push_back(detail::join(\n            original,\n            std::string(1, (delimiter_ == '\\0') ? '\\n' : delimiter_)));\n      }\n      break;\n    case MultiOptionPolicy::Sum:\n      out.push_back(detail::sum_string_vector(original));\n      break;\n    case MultiOptionPolicy::Throw:\n    default: {\n      auto num_min = static_cast<std::size_t>(get_items_expected_min());\n      auto num_max = static_cast<std::size_t>(get_items_expected_max());\n      if (num_min == 0) {\n        num_min = 1;\n      }\n      if (num_max == 0) {\n        num_max = 1;\n      }\n      if (original.size() < num_min) {\n        throw ArgumentMismatch::AtLeast(get_name(), static_cast<int>(num_min),\n                                        original.size());\n      }\n      if (original.size() > num_max) {\n        throw ArgumentMismatch::AtMost(get_name(), static_cast<int>(num_max),\n                                       original.size());\n      }\n      break;\n    }\n  }\n  // this check is to allow an empty vector in certain circumstances but not if\n  // expected is not zero.\n  // {} is the indicator for an empty container\n  if (out.empty()) {\n    if (original.size() == 1 && original[0] == \"{}\" &&\n        get_items_expected_min() > 0) {\n      out.push_back(\"{}\");\n      out.push_back(\"%%\");\n    }\n  } else if (out.size() == 1 && out[0] == \"{}\" &&\n             get_items_expected_min() > 0) {\n    out.push_back(\"%%\");\n  }\n}\n\nCLI11_INLINE std::string Option::_validate(std::string &result,\n                                           int index) const {\n  std::string err_msg;\n  if (result.empty() && expected_min_ == 0) {\n    // an empty with nothing expected is allowed\n    return err_msg;\n  }\n  for (const auto &vali : validators_) {\n    auto v = vali.get_application_index();\n    if (v == -1 || v == index) {\n      try {\n        err_msg = vali(result);\n      } catch (const ValidationError &err) {\n        err_msg = err.what();\n      }\n      if (!err_msg.empty()) break;\n    }\n  }\n\n  return err_msg;\n}\n\nCLI11_INLINE int Option::_add_result(std::string &&result,\n                                     std::vector<std::string> &res) const {\n  int result_count = 0;\n  if (allow_extra_args_ && !result.empty() && result.front() == '[' &&\n      result.back() == ']') {  // this is now a vector string likely from the\n                               // default or user entry\n    result.pop_back();\n\n    for (auto &var : CLI::detail::split(result.substr(1), ',')) {\n      if (!var.empty()) {\n        result_count += _add_result(std::move(var), res);\n      }\n    }\n    return result_count;\n  }\n  if (delimiter_ == '\\0') {\n    res.push_back(std::move(result));\n    ++result_count;\n  } else {\n    if ((result.find_first_of(delimiter_) != std::string::npos)) {\n      for (const auto &var : CLI::detail::split(result, delimiter_)) {\n        if (!var.empty()) {\n          res.push_back(var);\n          ++result_count;\n        }\n      }\n    } else {\n      res.push_back(std::move(result));\n      ++result_count;\n    }\n  }\n  return result_count;\n}\n\n#ifndef CLI11_PARSE\n#define CLI11_PARSE(app, argc, argv)   \\\n  try {                                \\\n    (app).parse((argc), (argv));       \\\n  } catch (const CLI::ParseError &e) { \\\n    return (app).exit(e);              \\\n  }\n#endif\n\nnamespace detail {\nenum class Classifier {\n  NONE,\n  POSITIONAL_MARK,\n  SHORT,\n  LONG,\n  WINDOWS_STYLE,\n  SUBCOMMAND,\n  SUBCOMMAND_TERMINATOR\n};\nstruct AppFriend;\n}  // namespace detail\n\nnamespace FailureMessage {\n/// Printout a clean, simple message on error (the default in CLI11 1.5+)\nCLI11_INLINE std::string simple(const App *app, const Error &e);\n\n/// Printout the full help string on error (if this fn is set, the old default\n/// for CLI11)\nCLI11_INLINE std::string help(const App *app, const Error &e);\n}  // namespace FailureMessage\n\n/// enumeration of modes of how to deal with extras in config files\n\nenum class config_extras_mode : char { error = 0, ignore, ignore_all, capture };\n\nclass App;\n\nusing App_p = std::shared_ptr<App>;\n\nnamespace detail {\n/// helper functions for adding in appropriate flag modifiers for add_flag\n\ntemplate <typename T,\n          enable_if_t<!std::is_integral<T>::value || (sizeof(T) <= 1U),\n                      detail::enabler> = detail::dummy>\nOption *default_flag_modifiers(Option *opt) {\n  return opt->always_capture_default();\n}\n\n/// summing modifiers\ntemplate <typename T,\n          enable_if_t<std::is_integral<T>::value && (sizeof(T) > 1U),\n                      detail::enabler> = detail::dummy>\nOption *default_flag_modifiers(Option *opt) {\n  return opt->multi_option_policy(MultiOptionPolicy::Sum)\n      ->default_str(\"0\")\n      ->force_callback();\n}\n\n}  // namespace detail\n\nclass Option_group;\n/// Creates a command line program, with very few defaults.\n/** To use, create a new `Program()` instance with `argc`, `argv`, and a help\n * description. The templated add_option methods make it easy to prepare\n * options. Remember to call `.start` before starting your program, so that the\n * options can be evaluated and the help option doesn't accidentally run your\n * program. */\nclass App {\n  friend Option;\n  friend detail::AppFriend;\n\n protected:\n  // This library follows the Google style guide for member names ending in\n  // underscores\n\n  /// @name Basics\n  ///@{\n\n  /// Subcommand name or program name (from parser if name is empty)\n  std::string name_{};\n\n  /// Description of the current program/subcommand\n  std::string description_{};\n\n  /// If true, allow extra arguments (ie, don't throw an error). INHERITABLE\n  bool allow_extras_{false};\n\n  /// If ignore, allow extra arguments in the ini file (ie, don't throw an\n  /// error). INHERITABLE if error error on an extra argument, and if capture\n  /// feed it to the app\n  config_extras_mode allow_config_extras_{config_extras_mode::ignore};\n\n  ///  If true, return immediately on an unrecognized option (implies\n  ///  allow_extras) INHERITABLE\n  bool prefix_command_{false};\n\n  /// If set to true the name was automatically generated from the command line\n  /// vs a user set name\n  bool has_automatic_name_{false};\n\n  /// If set to true the subcommand is required to be processed and used,\n  /// ignored for main app\n  bool required_{false};\n\n  /// If set to true the subcommand is disabled and cannot be used, ignored for\n  /// main app\n  bool disabled_{false};\n\n  /// Flag indicating that the pre_parse_callback has been triggered\n  bool pre_parse_called_{false};\n\n  /// Flag indicating that the callback for the subcommand should be executed\n  /// immediately on parse completion which is before help or ini files are\n  /// processed. INHERITABLE\n  bool immediate_callback_{false};\n\n  /// This is a function that runs prior to the start of parsing\n  std::function<void(std::size_t)> pre_parse_callback_{};\n\n  /// This is a function that runs when parsing has finished.\n  std::function<void()> parse_complete_callback_{};\n\n  /// This is a function that runs when all processing has completed\n  std::function<void()> final_callback_{};\n\n  ///@}\n  /// @name Options\n  ///@{\n\n  /// The default values for options, customizable and changeable INHERITABLE\n  OptionDefaults option_defaults_{};\n\n  /// The list of options, stored locally\n  std::vector<Option_p> options_{};\n\n  ///@}\n  /// @name Help\n  ///@{\n\n  /// Footer to put after all options in the help output INHERITABLE\n  std::string footer_{};\n\n  /// This is a function that generates a footer to put after all other options\n  /// in help output\n  std::function<std::string()> footer_callback_{};\n\n  /// A pointer to the help flag if there is one INHERITABLE\n  Option *help_ptr_{nullptr};\n\n  /// A pointer to the help all flag if there is one INHERITABLE\n  Option *help_all_ptr_{nullptr};\n\n  /// A pointer to a version flag if there is one\n  Option *version_ptr_{nullptr};\n\n  /// This is the formatter for help printing. Default provided. INHERITABLE\n  /// (same pointer)\n  std::shared_ptr<FormatterBase> formatter_{new Formatter()};\n\n  /// The error message printing function INHERITABLE\n  std::function<std::string(const App *, const Error &e)> failure_message_{\n      FailureMessage::simple};\n\n  ///@}\n  /// @name Parsing\n  ///@{\n\n  using missing_t = std::vector<std::pair<detail::Classifier, std::string>>;\n\n  /// Pair of classifier, string for missing options. (extra detail is removed\n  /// on returning from parse)\n  ///\n  /// This is faster and cleaner than storing just a list of strings and\n  /// reparsing. This may contain the -- separator.\n  missing_t missing_{};\n\n  /// This is a list of pointers to options with the original parse order\n  std::vector<Option *> parse_order_{};\n\n  /// This is a list of the subcommands collected, in order\n  std::vector<App *> parsed_subcommands_{};\n\n  /// this is a list of subcommands that are exclusionary to this one\n  std::set<App *> exclude_subcommands_{};\n\n  /// This is a list of options which are exclusionary to this App, if the\n  /// options were used this subcommand should not be\n  std::set<Option *> exclude_options_{};\n\n  /// this is a list of subcommands or option groups that are required by this\n  /// one, the list is not mutual,  the listed subcommands do not require this\n  /// one\n  std::set<App *> need_subcommands_{};\n\n  /// This is a list of options which are required by this app, the list is not\n  /// mutual, listed options do not need the subcommand not be\n  std::set<Option *> need_options_{};\n\n  ///@}\n  /// @name Subcommands\n  ///@{\n\n  /// Storage for subcommand list\n  std::vector<App_p> subcommands_{};\n\n  /// If true, the program name is not case sensitive INHERITABLE\n  bool ignore_case_{false};\n\n  /// If true, the program should ignore underscores INHERITABLE\n  bool ignore_underscore_{false};\n\n  /// Allow subcommand fallthrough, so that parent commands can collect commands\n  /// after subcommand.  INHERITABLE\n  bool fallthrough_{false};\n\n  /// Allow '/' for options for Windows like options. Defaults to true on\n  /// Windows, false otherwise. INHERITABLE\n  bool allow_windows_style_options_{\n#ifdef _WIN32\n      true\n#else\n      false\n#endif\n  };\n  /// specify that positional arguments come at the end of the argument sequence\n  /// not inheritable\n  bool positionals_at_end_{false};\n\n  enum class startup_mode : char { stable, enabled, disabled };\n  /// specify the startup mode for the app\n  /// stable=no change, enabled= startup enabled, disabled=startup disabled\n  startup_mode default_startup{startup_mode::stable};\n\n  /// if set to true the subcommand can be triggered via configuration files\n  /// INHERITABLE\n  bool configurable_{false};\n\n  /// If set to true positional options are validated before assigning\n  /// INHERITABLE\n  bool validate_positionals_{false};\n\n  /// If set to true optional vector arguments are validated before assigning\n  /// INHERITABLE\n  bool validate_optional_arguments_{false};\n\n  /// indicator that the subcommand is silent and won't show up in subcommands\n  /// list This is potentially useful as a modifier subcommand\n  bool silent_{false};\n\n  /// Counts the number of times this command/subcommand was parsed\n  std::uint32_t parsed_{0U};\n\n  /// Minimum required subcommands (not inheritable!)\n  std::size_t require_subcommand_min_{0};\n\n  /// Max number of subcommands allowed (parsing stops after this number). 0 is\n  /// unlimited INHERITABLE\n  std::size_t require_subcommand_max_{0};\n\n  /// Minimum required options (not inheritable!)\n  std::size_t require_option_min_{0};\n\n  /// Max number of options allowed. 0 is unlimited (not inheritable)\n  std::size_t require_option_max_{0};\n\n  /// A pointer to the parent if this is a subcommand\n  App *parent_{nullptr};\n\n  /// The group membership INHERITABLE\n  std::string group_{\"Subcommands\"};\n\n  /// Alias names for the subcommand\n  std::vector<std::string> aliases_{};\n\n  ///@}\n  /// @name Config\n  ///@{\n\n  /// Pointer to the config option\n  Option *config_ptr_{nullptr};\n\n  /// This is the formatter for help printing. Default provided. INHERITABLE\n  /// (same pointer)\n  std::shared_ptr<Config> config_formatter_{new ConfigTOML()};\n\n  ///@}\n\n  /// Special private constructor for subcommand\n  App(std::string app_description, std::string app_name, App *parent);\n\n public:\n  /// @name Basic\n  ///@{\n\n  /// Create a new program. Pass in the same arguments as main(), along with a\n  /// help string.\n  explicit App(std::string app_description = \"\", std::string app_name = \"\")\n      : App(app_description, app_name, nullptr) {\n    set_help_flag(\"-h,--help\", \"Print this help message and exit\");\n  }\n\n  App(const App &) = delete;\n  App &operator=(const App &) = delete;\n\n  /// virtual destructor\n  virtual ~App() = default;\n\n  /// Set a callback for execution when all parsing and processing has completed\n  ///\n  /// Due to a bug in c++11,\n  /// it is not possible to overload on std::function (fixed in c++14\n  /// and backported to c++11 on newer compilers). Use capture by reference\n  /// to get a pointer to App if needed.\n  App *callback(std::function<void()> app_callback) {\n    if (immediate_callback_) {\n      parse_complete_callback_ = std::move(app_callback);\n    } else {\n      final_callback_ = std::move(app_callback);\n    }\n    return this;\n  }\n\n  /// Set a callback for execution when all parsing and processing has completed\n  /// aliased as callback\n  App *final_callback(std::function<void()> app_callback) {\n    final_callback_ = std::move(app_callback);\n    return this;\n  }\n\n  /// Set a callback to execute when parsing has completed for the app\n  ///\n  App *parse_complete_callback(std::function<void()> pc_callback) {\n    parse_complete_callback_ = std::move(pc_callback);\n    return this;\n  }\n\n  /// Set a callback to execute prior to parsing.\n  ///\n  App *preparse_callback(std::function<void(std::size_t)> pp_callback) {\n    pre_parse_callback_ = std::move(pp_callback);\n    return this;\n  }\n\n  /// Set a name for the app (empty will use parser to set the name)\n  App *name(std::string app_name = \"\");\n\n  /// Set an alias for the app\n  App *alias(std::string app_name);\n\n  /// Remove the error when extras are left over on the command line.\n  App *allow_extras(bool allow = true) {\n    allow_extras_ = allow;\n    return this;\n  }\n\n  /// Remove the error when extras are left over on the command line.\n  App *required(bool require = true) {\n    required_ = require;\n    return this;\n  }\n\n  /// Disable the subcommand or option group\n  App *disabled(bool disable = true) {\n    disabled_ = disable;\n    return this;\n  }\n\n  /// silence the subcommand from showing up in the processed list\n  App *silent(bool silence = true) {\n    silent_ = silence;\n    return this;\n  }\n\n  /// Set the subcommand to be disabled by default, so on clear(), at the start\n  /// of each parse it is disabled\n  App *disabled_by_default(bool disable = true) {\n    if (disable) {\n      default_startup = startup_mode::disabled;\n    } else {\n      default_startup = (default_startup == startup_mode::enabled)\n                            ? startup_mode::enabled\n                            : startup_mode::stable;\n    }\n    return this;\n  }\n\n  /// Set the subcommand to be enabled by default, so on clear(), at the start\n  /// of each parse it is enabled (not disabled)\n  App *enabled_by_default(bool enable = true) {\n    if (enable) {\n      default_startup = startup_mode::enabled;\n    } else {\n      default_startup = (default_startup == startup_mode::disabled)\n                            ? startup_mode::disabled\n                            : startup_mode::stable;\n    }\n    return this;\n  }\n\n  /// Set the subcommand callback to be executed immediately on subcommand\n  /// completion\n  App *immediate_callback(bool immediate = true);\n\n  /// Set the subcommand to validate positional arguments before assigning\n  App *validate_positionals(bool validate = true) {\n    validate_positionals_ = validate;\n    return this;\n  }\n\n  /// Set the subcommand to validate optional vector arguments before assigning\n  App *validate_optional_arguments(bool validate = true) {\n    validate_optional_arguments_ = validate;\n    return this;\n  }\n\n  /// ignore extras in config files\n  App *allow_config_extras(bool allow = true) {\n    if (allow) {\n      allow_config_extras_ = config_extras_mode::capture;\n      allow_extras_ = true;\n    } else {\n      allow_config_extras_ = config_extras_mode::error;\n    }\n    return this;\n  }\n\n  /// ignore extras in config files\n  App *allow_config_extras(config_extras_mode mode) {\n    allow_config_extras_ = mode;\n    return this;\n  }\n\n  /// Do not parse anything after the first unrecognized option and return\n  App *prefix_command(bool allow = true) {\n    prefix_command_ = allow;\n    return this;\n  }\n\n  /// Ignore case. Subcommands inherit value.\n  App *ignore_case(bool value = true);\n\n  /// Allow windows style options, such as `/opt`. First matching short or long\n  /// name used. Subcommands inherit value.\n  App *allow_windows_style_options(bool value = true) {\n    allow_windows_style_options_ = value;\n    return this;\n  }\n\n  /// Specify that the positional arguments are only at the end of the sequence\n  App *positionals_at_end(bool value = true) {\n    positionals_at_end_ = value;\n    return this;\n  }\n\n  /// Specify that the subcommand can be triggered by a config file\n  App *configurable(bool value = true) {\n    configurable_ = value;\n    return this;\n  }\n\n  /// Ignore underscore. Subcommands inherit value.\n  App *ignore_underscore(bool value = true);\n\n  /// Set the help formatter\n  App *formatter(std::shared_ptr<FormatterBase> fmt) {\n    formatter_ = fmt;\n    return this;\n  }\n\n  /// Set the help formatter\n  App *formatter_fn(\n      std::function<std::string(const App *, std::string, AppFormatMode)> fmt) {\n    formatter_ = std::make_shared<FormatterLambda>(fmt);\n    return this;\n  }\n\n  /// Set the config formatter\n  App *config_formatter(std::shared_ptr<Config> fmt) {\n    config_formatter_ = fmt;\n    return this;\n  }\n\n  /// Check to see if this subcommand was parsed, true only if received on\n  /// command line.\n  CLI11_NODISCARD bool parsed() const { return parsed_ > 0; }\n\n  /// Get the OptionDefault object, to set option defaults\n  OptionDefaults *option_defaults() { return &option_defaults_; }\n\n  ///@}\n  /// @name Adding options\n  ///@{\n\n  /// Add an option, will automatically understand the type for common types.\n  ///\n  /// To use, create a variable with the expected type, and pass it in after the\n  /// name. After start is called, you can use count to see if the value was\n  /// passed, and the value will be initialized properly. Numbers, vectors, and\n  /// strings are supported.\n  ///\n  /// ->required(), ->default, and the validators are options,\n  /// The positional options take an optional number of arguments.\n  ///\n  /// For example,\n  ///\n  ///     std::string filename;\n  ///     program.add_option(\"filename\", filename, \"description of filename\");\n  ///\n  Option *add_option(std::string option_name, callback_t option_callback,\n                     std::string option_description = \"\",\n                     bool defaulted = false,\n                     std::function<std::string()> func = {});\n\n  /// Add option for assigning to a variable\n  template <typename AssignTo, typename ConvertTo = AssignTo,\n            enable_if_t<!std::is_const<ConvertTo>::value, detail::enabler> =\n                detail::dummy>\n  Option *add_option(std::string option_name,\n                     AssignTo &variable,  ///< The variable to set\n                     std::string option_description = \"\") {\n    auto fun = [&variable](const CLI::results_t &res) {  // comment for spacing\n      return detail::lexical_conversion<AssignTo, ConvertTo>(res, variable);\n    };\n\n    Option *opt =\n        add_option(option_name, fun, option_description, false, [&variable]() {\n          return CLI::detail::checked_to_string<AssignTo, ConvertTo>(variable);\n        });\n    opt->type_name(detail::type_name<ConvertTo>());\n    // these must be actual lvalues since (std::max) sometimes is defined in\n    // terms of references and references to structs used in the evaluation can\n    // be temporary so that would cause issues.\n    auto Tcount = detail::type_count<AssignTo>::value;\n    auto XCcount = detail::type_count<ConvertTo>::value;\n    opt->type_size(detail::type_count_min<ConvertTo>::value,\n                   (std::max)(Tcount, XCcount));\n    opt->expected(detail::expected_count<ConvertTo>::value);\n    opt->run_callback_for_default();\n    return opt;\n  }\n\n  /// Add option for assigning to a variable\n  template <typename AssignTo, enable_if_t<!std::is_const<AssignTo>::value,\n                                           detail::enabler> = detail::dummy>\n  Option *add_option_no_stream(std::string option_name,\n                               AssignTo &variable,  ///< The variable to set\n                               std::string option_description = \"\") {\n    auto fun = [&variable](const CLI::results_t &res) {  // comment for spacing\n      return detail::lexical_conversion<AssignTo, AssignTo>(res, variable);\n    };\n\n    Option *opt = add_option(option_name, fun, option_description, false,\n                             []() { return std::string{}; });\n    opt->type_name(detail::type_name<AssignTo>());\n    opt->type_size(detail::type_count_min<AssignTo>::value,\n                   detail::type_count<AssignTo>::value);\n    opt->expected(detail::expected_count<AssignTo>::value);\n    opt->run_callback_for_default();\n    return opt;\n  }\n\n  /// Add option for a callback of a specific type\n  template <typename ArgType>\n  Option *add_option_function(std::string option_name,\n                              const std::function<void(const ArgType &)>\n                                  &func,  ///< the callback to execute\n                              std::string option_description = \"\") {\n    auto fun = [func](const CLI::results_t &res) {\n      ArgType variable;\n      bool result = detail::lexical_conversion<ArgType, ArgType>(res, variable);\n      if (result) {\n        func(variable);\n      }\n      return result;\n    };\n\n    Option *opt =\n        add_option(option_name, std::move(fun), option_description, false);\n    opt->type_name(detail::type_name<ArgType>());\n    opt->type_size(detail::type_count_min<ArgType>::value,\n                   detail::type_count<ArgType>::value);\n    opt->expected(detail::expected_count<ArgType>::value);\n    return opt;\n  }\n\n  /// Add option with no description or variable assignment\n  Option *add_option(std::string option_name) {\n    return add_option(option_name, CLI::callback_t{}, std::string{}, false);\n  }\n\n  /// Add option with description but with no variable assignment or callback\n  template <typename T,\n            enable_if_t<std::is_const<T>::value &&\n                            std::is_constructible<std::string, T>::value,\n                        detail::enabler> = detail::dummy>\n  Option *add_option(std::string option_name, T &option_description) {\n    return add_option(option_name, CLI::callback_t(), option_description,\n                      false);\n  }\n\n  /// Set a help flag, replace the existing one if present\n  Option *set_help_flag(std::string flag_name = \"\",\n                        const std::string &help_description = \"\");\n\n  /// Set a help all flag, replaced the existing one if present\n  Option *set_help_all_flag(std::string help_name = \"\",\n                            const std::string &help_description = \"\");\n\n  /// Set a version flag and version display string, replace the existing one if\n  /// present\n  Option *set_version_flag(std::string flag_name = \"\",\n                           const std::string &versionString = \"\",\n                           const std::string &version_help =\n                               \"Display program version information and exit\");\n\n  /// Generate the version string through a callback function\n  Option *set_version_flag(std::string flag_name,\n                           std::function<std::string()> vfunc,\n                           const std::string &version_help =\n                               \"Display program version information and exit\");\n\n private:\n  /// Internal function for adding a flag\n  Option *_add_flag_internal(std::string flag_name, CLI::callback_t fun,\n                             std::string flag_description);\n\n public:\n  /// Add a flag with no description or variable assignment\n  Option *add_flag(std::string flag_name) {\n    return _add_flag_internal(flag_name, CLI::callback_t(), std::string{});\n  }\n\n  /// Add flag with description but with no variable assignment or callback\n  /// takes a constant string,  if a variable string is passed that variable\n  /// will be assigned the results from the flag\n  template <typename T,\n            enable_if_t<std::is_const<T>::value &&\n                            std::is_constructible<std::string, T>::value,\n                        detail::enabler> = detail::dummy>\n  Option *add_flag(std::string flag_name, T &flag_description) {\n    return _add_flag_internal(flag_name, CLI::callback_t(), flag_description);\n  }\n\n  /// Other type version accepts all other types that are not vectors such as\n  /// bool, enum, string or other classes that can be converted from a string\n  template <\n      typename T,\n      enable_if_t<\n          !detail::is_mutable_container<T>::value && !std::is_const<T>::value &&\n              !std::is_constructible<std::function<void(int)>, T>::value,\n          detail::enabler> = detail::dummy>\n  Option *add_flag(std::string flag_name,\n                   T &flag_result,  ///< A variable holding the flag result\n                   std::string flag_description = \"\") {\n    CLI::callback_t fun = [&flag_result](const CLI::results_t &res) {\n      using CLI::detail::lexical_cast;\n      return lexical_cast(res[0], flag_result);\n    };\n    auto *opt = _add_flag_internal(flag_name, std::move(fun),\n                                   std::move(flag_description));\n    return detail::default_flag_modifiers<T>(opt);\n  }\n\n  /// Vector version to capture multiple flags.\n  template <typename T,\n            enable_if_t<!std::is_assignable<std::function<void(std::int64_t)> &,\n                                            T>::value,\n                        detail::enabler> = detail::dummy>\n  Option *add_flag(std::string flag_name,\n                   std::vector<T> &flag_results,  ///< A vector of values with\n                                                  ///< the flag results\n                   std::string flag_description = \"\") {\n    CLI::callback_t fun = [&flag_results](const CLI::results_t &res) {\n      bool retval = true;\n      for (const auto &elem : res) {\n        using CLI::detail::lexical_cast;\n        flag_results.emplace_back();\n        retval &= lexical_cast(elem, flag_results.back());\n      }\n      return retval;\n    };\n    return _add_flag_internal(flag_name, std::move(fun),\n                              std::move(flag_description))\n        ->multi_option_policy(MultiOptionPolicy::TakeAll)\n        ->run_callback_for_default();\n  }\n\n  /// Add option for callback that is triggered with a true flag and takes no\n  /// arguments\n  Option *add_flag_callback(\n      std::string flag_name,\n      std::function<void(void)> function,  ///< A function to call, void(void)\n      std::string flag_description = \"\");\n\n  /// Add option for callback with an integer value\n  Option *add_flag_function(std::string flag_name,\n                            std::function<void(std::int64_t)>\n                                function,  ///< A function to call, void(int)\n                            std::string flag_description = \"\");\n\n#ifdef CLI11_CPP14\n  /// Add option for callback (C++14 or better only)\n  Option *add_flag(std::string flag_name,\n                   std::function<void(std::int64_t)>\n                       function,  ///< A function to call, void(std::int64_t)\n                   std::string flag_description = \"\") {\n    return add_flag_function(std::move(flag_name), std::move(function),\n                             std::move(flag_description));\n  }\n#endif\n\n  /// Set a configuration ini file option, or clear it if no name passed\n  Option *set_config(std::string option_name = \"\",\n                     std::string default_filename = \"\",\n                     const std::string &help_message = \"Read an ini file\",\n                     bool config_required = false);\n\n  /// Removes an option from the App. Takes an option pointer. Returns true if\n  /// found and removed.\n  bool remove_option(Option *opt);\n\n  /// creates an option group as part of the given app\n  template <typename T = Option_group>\n  T *add_option_group(std::string group_name,\n                      std::string group_description = \"\") {\n    if (!detail::valid_alias_name_string(group_name)) {\n      throw IncorrectConstruction(\n          \"option group names may not contain newlines or null characters\");\n    }\n    auto option_group =\n        std::make_shared<T>(std::move(group_description), group_name, this);\n    auto *ptr = option_group.get();\n    // move to App_p for overload resolution on older gcc versions\n    App_p app_ptr = std::dynamic_pointer_cast<App>(option_group);\n    add_subcommand(std::move(app_ptr));\n    return ptr;\n  }\n\n  ///@}\n  /// @name Subcommands\n  ///@{\n\n  /// Add a subcommand. Inherits INHERITABLE and OptionDefaults, and help flag\n  App *add_subcommand(std::string subcommand_name = \"\",\n                      std::string subcommand_description = \"\");\n\n  /// Add a previously created app as a subcommand\n  App *add_subcommand(CLI::App_p subcom);\n\n  /// Removes a subcommand from the App. Takes a subcommand pointer. Returns\n  /// true if found and removed.\n  bool remove_subcommand(App *subcom);\n\n  /// Check to see if a subcommand is part of this command (doesn't have to be\n  /// in command line) returns the first subcommand if passed a nullptr\n  App *get_subcommand(const App *subcom) const;\n\n  /// Check to see if a subcommand is part of this command (text version)\n  CLI11_NODISCARD App *get_subcommand(std::string subcom) const;\n\n  /// Get a pointer to subcommand by index\n  CLI11_NODISCARD App *get_subcommand(int index = 0) const;\n\n  /// Check to see if a subcommand is part of this command and get a shared_ptr\n  /// to it\n  CLI::App_p get_subcommand_ptr(App *subcom) const;\n\n  /// Check to see if a subcommand is part of this command (text version)\n  CLI11_NODISCARD CLI::App_p get_subcommand_ptr(std::string subcom) const;\n\n  /// Get an owning pointer to subcommand by index\n  CLI11_NODISCARD CLI::App_p get_subcommand_ptr(int index = 0) const;\n\n  /// Check to see if an option group is part of this App\n  CLI11_NODISCARD App *get_option_group(std::string group_name) const;\n\n  /// No argument version of count counts the number of times this subcommand\n  /// was passed in. The main app will return 1. Unnamed subcommands will also\n  /// return 1 unless otherwise modified in a callback\n  CLI11_NODISCARD std::size_t count() const { return parsed_; }\n\n  /// Get a count of all the arguments processed in options and subcommands,\n  /// this excludes arguments which were treated as extras.\n  CLI11_NODISCARD std::size_t count_all() const;\n\n  /// Changes the group membership\n  App *group(std::string group_name) {\n    group_ = group_name;\n    return this;\n  }\n\n  /// The argumentless form of require subcommand requires 1 or more subcommands\n  App *require_subcommand() {\n    require_subcommand_min_ = 1;\n    require_subcommand_max_ = 0;\n    return this;\n  }\n\n  /// Require a subcommand to be given (does not affect help call)\n  /// The number required can be given. Negative values indicate maximum\n  /// number allowed (0 for any number). Max number inheritable.\n  App *require_subcommand(int value) {\n    if (value < 0) {\n      require_subcommand_min_ = 0;\n      require_subcommand_max_ = static_cast<std::size_t>(-value);\n    } else {\n      require_subcommand_min_ = static_cast<std::size_t>(value);\n      require_subcommand_max_ = static_cast<std::size_t>(value);\n    }\n    return this;\n  }\n\n  /// Explicitly control the number of subcommands required. Setting 0\n  /// for the max means unlimited number allowed. Max number inheritable.\n  App *require_subcommand(std::size_t min, std::size_t max) {\n    require_subcommand_min_ = min;\n    require_subcommand_max_ = max;\n    return this;\n  }\n\n  /// The argumentless form of require option requires 1 or more options be used\n  App *require_option() {\n    require_option_min_ = 1;\n    require_option_max_ = 0;\n    return this;\n  }\n\n  /// Require an option to be given (does not affect help call)\n  /// The number required can be given. Negative values indicate maximum\n  /// number allowed (0 for any number).\n  App *require_option(int value) {\n    if (value < 0) {\n      require_option_min_ = 0;\n      require_option_max_ = static_cast<std::size_t>(-value);\n    } else {\n      require_option_min_ = static_cast<std::size_t>(value);\n      require_option_max_ = static_cast<std::size_t>(value);\n    }\n    return this;\n  }\n\n  /// Explicitly control the number of options required. Setting 0\n  /// for the max means unlimited number allowed. Max number inheritable.\n  App *require_option(std::size_t min, std::size_t max) {\n    require_option_min_ = min;\n    require_option_max_ = max;\n    return this;\n  }\n\n  /// Stop subcommand fallthrough, so that parent commands cannot collect\n  /// commands after subcommand. Default from parent, usually set on parent.\n  App *fallthrough(bool value = true) {\n    fallthrough_ = value;\n    return this;\n  }\n\n  /// Check to see if this subcommand was parsed, true only if received on\n  /// command line. This allows the subcommand to be directly checked.\n  explicit operator bool() const { return parsed_ > 0; }\n\n  ///@}\n  /// @name Extras for subclassing\n  ///@{\n\n  /// This allows subclasses to inject code before callbacks but after parse.\n  ///\n  /// This does not run if any errors or help is thrown.\n  virtual void pre_callback() {}\n\n  ///@}\n  /// @name Parsing\n  ///@{\n  //\n  /// Reset the parsed data\n  void clear();\n\n  /// Parses the command line - throws errors.\n  /// This must be called after the options are in but before the rest of the\n  /// program.\n  void parse(int argc, const char *const *argv);\n\n  /// Parse a single string as if it contained command line arguments.\n  /// This function splits the string into arguments then calls\n  /// parse(std::vector<std::string> &) the function takes an optional boolean\n  /// argument specifying if the programName is included in the string to\n  /// process\n  void parse(std::string commandline, bool program_name_included = false);\n\n  /// The real work is done here. Expects a reversed vector.\n  /// Changes the vector to the remaining options.\n  void parse(std::vector<std::string> &args);\n\n  /// The real work is done here. Expects a reversed vector.\n  void parse(std::vector<std::string> &&args);\n\n  void parse_from_stream(std::istream &input);\n\n  /// Provide a function to print a help message. The function gets access to\n  /// the App pointer and error.\n  void failure_message(\n      std::function<std::string(const App *, const Error &e)> function) {\n    failure_message_ = function;\n  }\n\n  /// Print a nice error message and return the exit code\n  int exit(const Error &e, std::ostream &out = std::cout,\n           std::ostream &err = std::cerr) const;\n\n  ///@}\n  /// @name Post parsing\n  ///@{\n\n  /// Counts the number of times the given option was passed.\n  CLI11_NODISCARD std::size_t count(std::string option_name) const {\n    return get_option(option_name)->count();\n  }\n\n  /// Get a subcommand pointer list to the currently selected subcommands (after\n  /// parsing by default, in command line order; use parsed = false to get the\n  /// original definition list.)\n  CLI11_NODISCARD std::vector<App *> get_subcommands() const {\n    return parsed_subcommands_;\n  }\n\n  /// Get a filtered subcommand pointer list from the original definition list.\n  /// An empty function will provide all subcommands (const)\n  std::vector<const App *> get_subcommands(\n      const std::function<bool(const App *)> &filter) const;\n\n  /// Get a filtered subcommand pointer list from the original definition list.\n  /// An empty function will provide all subcommands\n  std::vector<App *> get_subcommands(const std::function<bool(App *)> &filter);\n\n  /// Check to see if given subcommand was selected\n  bool got_subcommand(const App *subcom) const {\n    // get subcom needed to verify that this was a real subcommand\n    return get_subcommand(subcom)->parsed_ > 0;\n  }\n\n  /// Check with name instead of pointer to see if subcommand was selected\n  CLI11_NODISCARD bool got_subcommand(std::string subcommand_name) const {\n    return get_subcommand(subcommand_name)->parsed_ > 0;\n  }\n\n  /// Sets excluded options for the subcommand\n  App *excludes(Option *opt) {\n    if (opt == nullptr) {\n      throw OptionNotFound(\"nullptr passed\");\n    }\n    exclude_options_.insert(opt);\n    return this;\n  }\n\n  /// Sets excluded subcommands for the subcommand\n  App *excludes(App *app) {\n    if (app == nullptr) {\n      throw OptionNotFound(\"nullptr passed\");\n    }\n    if (app == this) {\n      throw OptionNotFound(\"cannot self reference in needs\");\n    }\n    auto res = exclude_subcommands_.insert(app);\n    // subcommand exclusion should be symmetric\n    if (res.second) {\n      app->exclude_subcommands_.insert(this);\n    }\n    return this;\n  }\n\n  App *needs(Option *opt) {\n    if (opt == nullptr) {\n      throw OptionNotFound(\"nullptr passed\");\n    }\n    need_options_.insert(opt);\n    return this;\n  }\n\n  App *needs(App *app) {\n    if (app == nullptr) {\n      throw OptionNotFound(\"nullptr passed\");\n    }\n    if (app == this) {\n      throw OptionNotFound(\"cannot self reference in needs\");\n    }\n    need_subcommands_.insert(app);\n    return this;\n  }\n\n  /// Removes an option from the excludes list of this subcommand\n  bool remove_excludes(Option *opt);\n\n  /// Removes a subcommand from the excludes list of this subcommand\n  bool remove_excludes(App *app);\n\n  /// Removes an option from the needs list of this subcommand\n  bool remove_needs(Option *opt);\n\n  /// Removes a subcommand from the needs list of this subcommand\n  bool remove_needs(App *app);\n  ///@}\n  /// @name Help\n  ///@{\n\n  /// Set footer.\n  App *footer(std::string footer_string) {\n    footer_ = std::move(footer_string);\n    return this;\n  }\n  /// Set footer.\n  App *footer(std::function<std::string()> footer_function) {\n    footer_callback_ = std::move(footer_function);\n    return this;\n  }\n  /// Produce a string that could be read in as a config of the current values\n  /// of the App. Set default_also to include default arguments.\n  /// write_descriptions will print a description for the App and for each\n  /// option.\n  CLI11_NODISCARD std::string config_to_str(\n      bool default_also = false, bool write_description = false) const {\n    return config_formatter_->to_config(this, default_also, write_description,\n                                        \"\");\n  }\n\n  /// Makes a help message, using the currently configured formatter\n  /// Will only do one subcommand at a time\n  CLI11_NODISCARD std::string help(\n      std::string prev = \"\", AppFormatMode mode = AppFormatMode::Normal) const;\n\n  /// Displays a version string\n  CLI11_NODISCARD std::string version() const;\n  ///@}\n  /// @name Getters\n  ///@{\n\n  /// Access the formatter\n  CLI11_NODISCARD std::shared_ptr<FormatterBase> get_formatter() const {\n    return formatter_;\n  }\n\n  /// Access the config formatter\n  CLI11_NODISCARD std::shared_ptr<Config> get_config_formatter() const {\n    return config_formatter_;\n  }\n\n  /// Access the config formatter as a configBase pointer\n  CLI11_NODISCARD std::shared_ptr<ConfigBase> get_config_formatter_base()\n      const {\n    // This is safer as a dynamic_cast if we have RTTI, as Config -> ConfigBase\n#if CLI11_USE_STATIC_RTTI == 0\n    return std::dynamic_pointer_cast<ConfigBase>(config_formatter_);\n#else\n    return std::static_pointer_cast<ConfigBase>(config_formatter_);\n#endif\n  }\n\n  /// Get the app or subcommand description\n  CLI11_NODISCARD std::string get_description() const { return description_; }\n\n  /// Set the description of the app\n  App *description(std::string app_description) {\n    description_ = std::move(app_description);\n    return this;\n  }\n\n  /// Get the list of options (user facing function, so returns raw pointers),\n  /// has optional filter function\n  std::vector<const Option *> get_options(\n      const std::function<bool(const Option *)> filter = {}) const;\n\n  /// Non-const version of the above\n  std::vector<Option *> get_options(\n      const std::function<bool(Option *)> filter = {});\n\n  /// Get an option by name (noexcept non-const version)\n  Option *get_option_no_throw(std::string option_name) noexcept;\n\n  /// Get an option by name (noexcept const version)\n  CLI11_NODISCARD const Option *get_option_no_throw(\n      std::string option_name) const noexcept;\n\n  /// Get an option by name\n  CLI11_NODISCARD const Option *get_option(std::string option_name) const {\n    const auto *opt = get_option_no_throw(option_name);\n    if (opt == nullptr) {\n      throw OptionNotFound(option_name);\n    }\n    return opt;\n  }\n\n  /// Get an option by name (non-const version)\n  Option *get_option(std::string option_name) {\n    auto *opt = get_option_no_throw(option_name);\n    if (opt == nullptr) {\n      throw OptionNotFound(option_name);\n    }\n    return opt;\n  }\n\n  /// Shortcut bracket operator for getting a pointer to an option\n  const Option *operator[](const std::string &option_name) const {\n    return get_option(option_name);\n  }\n\n  /// Shortcut bracket operator for getting a pointer to an option\n  const Option *operator[](const char *option_name) const {\n    return get_option(option_name);\n  }\n\n  /// Check the status of ignore_case\n  CLI11_NODISCARD bool get_ignore_case() const { return ignore_case_; }\n\n  /// Check the status of ignore_underscore\n  CLI11_NODISCARD bool get_ignore_underscore() const {\n    return ignore_underscore_;\n  }\n\n  /// Check the status of fallthrough\n  CLI11_NODISCARD bool get_fallthrough() const { return fallthrough_; }\n\n  /// Check the status of the allow windows style options\n  CLI11_NODISCARD bool get_allow_windows_style_options() const {\n    return allow_windows_style_options_;\n  }\n\n  /// Check the status of the allow windows style options\n  CLI11_NODISCARD bool get_positionals_at_end() const {\n    return positionals_at_end_;\n  }\n\n  /// Check the status of the allow windows style options\n  CLI11_NODISCARD bool get_configurable() const { return configurable_; }\n\n  /// Get the group of this subcommand\n  CLI11_NODISCARD const std::string &get_group() const { return group_; }\n\n  /// Generate and return the footer.\n  CLI11_NODISCARD std::string get_footer() const {\n    return (footer_callback_) ? footer_callback_() + '\\n' + footer_ : footer_;\n  }\n\n  /// Get the required min subcommand value\n  CLI11_NODISCARD std::size_t get_require_subcommand_min() const {\n    return require_subcommand_min_;\n  }\n\n  /// Get the required max subcommand value\n  CLI11_NODISCARD std::size_t get_require_subcommand_max() const {\n    return require_subcommand_max_;\n  }\n\n  /// Get the required min option value\n  CLI11_NODISCARD std::size_t get_require_option_min() const {\n    return require_option_min_;\n  }\n\n  /// Get the required max option value\n  CLI11_NODISCARD std::size_t get_require_option_max() const {\n    return require_option_max_;\n  }\n\n  /// Get the prefix command status\n  CLI11_NODISCARD bool get_prefix_command() const { return prefix_command_; }\n\n  /// Get the status of allow extras\n  CLI11_NODISCARD bool get_allow_extras() const { return allow_extras_; }\n\n  /// Get the status of required\n  CLI11_NODISCARD bool get_required() const { return required_; }\n\n  /// Get the status of disabled\n  CLI11_NODISCARD bool get_disabled() const { return disabled_; }\n\n  /// Get the status of silence\n  CLI11_NODISCARD bool get_silent() const { return silent_; }\n\n  /// Get the status of disabled\n  CLI11_NODISCARD bool get_immediate_callback() const {\n    return immediate_callback_;\n  }\n\n  /// Get the status of disabled by default\n  CLI11_NODISCARD bool get_disabled_by_default() const {\n    return (default_startup == startup_mode::disabled);\n  }\n\n  /// Get the status of disabled by default\n  CLI11_NODISCARD bool get_enabled_by_default() const {\n    return (default_startup == startup_mode::enabled);\n  }\n  /// Get the status of validating positionals\n  CLI11_NODISCARD bool get_validate_positionals() const {\n    return validate_positionals_;\n  }\n  /// Get the status of validating optional vector arguments\n  CLI11_NODISCARD bool get_validate_optional_arguments() const {\n    return validate_optional_arguments_;\n  }\n\n  /// Get the status of allow extras\n  CLI11_NODISCARD config_extras_mode get_allow_config_extras() const {\n    return allow_config_extras_;\n  }\n\n  /// Get a pointer to the help flag.\n  Option *get_help_ptr() { return help_ptr_; }\n\n  /// Get a pointer to the help flag. (const)\n  CLI11_NODISCARD const Option *get_help_ptr() const { return help_ptr_; }\n\n  /// Get a pointer to the help all flag. (const)\n  CLI11_NODISCARD const Option *get_help_all_ptr() const {\n    return help_all_ptr_;\n  }\n\n  /// Get a pointer to the config option.\n  Option *get_config_ptr() { return config_ptr_; }\n\n  /// Get a pointer to the config option. (const)\n  CLI11_NODISCARD const Option *get_config_ptr() const { return config_ptr_; }\n\n  /// Get a pointer to the version option.\n  Option *get_version_ptr() { return version_ptr_; }\n\n  /// Get a pointer to the version option. (const)\n  CLI11_NODISCARD const Option *get_version_ptr() const { return version_ptr_; }\n\n  /// Get the parent of this subcommand (or nullptr if main app)\n  App *get_parent() { return parent_; }\n\n  /// Get the parent of this subcommand (or nullptr if main app) (const version)\n  CLI11_NODISCARD const App *get_parent() const { return parent_; }\n\n  /// Get the name of the current app\n  CLI11_NODISCARD const std::string &get_name() const { return name_; }\n\n  /// Get the aliases of the current app\n  CLI11_NODISCARD const std::vector<std::string> &get_aliases() const {\n    return aliases_;\n  }\n\n  /// clear all the aliases of the current App\n  App *clear_aliases() {\n    aliases_.clear();\n    return this;\n  }\n\n  /// Get a display name for an app\n  CLI11_NODISCARD std::string get_display_name(bool with_aliases = false) const;\n\n  /// Check the name, case insensitive and underscore insensitive if set\n  CLI11_NODISCARD bool check_name(std::string name_to_check) const;\n\n  /// Get the groups available directly from this option (in order)\n  CLI11_NODISCARD std::vector<std::string> get_groups() const;\n\n  /// This gets a vector of pointers with the original parse order\n  CLI11_NODISCARD const std::vector<Option *> &parse_order() const {\n    return parse_order_;\n  }\n\n  /// This returns the missing options from the current subcommand\n  CLI11_NODISCARD std::vector<std::string> remaining(\n      bool recurse = false) const;\n\n  /// This returns the missing options in a form ready for processing by another\n  /// command line program\n  CLI11_NODISCARD std::vector<std::string> remaining_for_passthrough(\n      bool recurse = false) const;\n\n  /// This returns the number of remaining options, minus the -- separator\n  CLI11_NODISCARD std::size_t remaining_size(bool recurse = false) const;\n\n  ///@}\n\n protected:\n  /// Check the options to make sure there are no conflicts.\n  ///\n  /// Currently checks to see if multiple positionals exist with unlimited args\n  /// and checks if the min and max options are feasible\n  void _validate() const;\n\n  /// configure subcommands to enable parsing through the current object\n  /// set the correct fallthrough and prefix for nameless subcommands and manage\n  /// the automatic enable or disable makes sure parent is set correctly\n  void _configure();\n\n  /// Internal function to run (App) callback, bottom up\n  void run_callback(bool final_mode = false,\n                    bool suppress_final_callback = false);\n\n  /// Check to see if a subcommand is valid. Give up immediately if subcommand\n  /// max has been reached.\n  CLI11_NODISCARD bool _valid_subcommand(const std::string &current,\n                                         bool ignore_used = true) const;\n\n  /// Selects a Classifier enum based on the type of the current argument\n  CLI11_NODISCARD detail::Classifier _recognize(\n      const std::string &current, bool ignore_used_subcommands = true) const;\n\n  // The parse function is now broken into several parts, and part of process\n\n  /// Read and process a configuration file (main app only)\n  void _process_config_file();\n\n  /// Get envname options if not yet passed. Runs on *all* subcommands.\n  void _process_env();\n\n  /// Process callbacks. Runs on *all* subcommands.\n  void _process_callbacks();\n\n  /// Run help flag processing if any are found.\n  ///\n  /// The flags allow recursive calls to remember if there was a help flag on a\n  /// parent.\n  void _process_help_flags(bool trigger_help = false,\n                           bool trigger_all_help = false) const;\n\n  /// Verify required options and cross requirements. Subcommands too (only if\n  /// selected).\n  void _process_requirements();\n\n  /// Process callbacks and such.\n  void _process();\n\n  /// Throw an error if anything is left over and should not be.\n  void _process_extras();\n\n  /// Throw an error if anything is left over and should not be.\n  /// Modifies the args to fill in the missing items before throwing.\n  void _process_extras(std::vector<std::string> &args);\n\n  /// Internal function to recursively increment the parsed counter on the\n  /// current app as well unnamed subcommands\n  void increment_parsed();\n\n  /// Internal parse function\n  void _parse(std::vector<std::string> &args);\n\n  /// Internal parse function\n  void _parse(std::vector<std::string> &&args);\n\n  /// Internal function to parse a stream\n  void _parse_stream(std::istream &input);\n\n  /// Parse one config param, return false if not found in any subcommand,\n  /// remove if it is\n  ///\n  /// If this has more than one dot.separated.name, go into the subcommand\n  /// matching it Returns true if it managed to find the option, if false you'll\n  /// need to remove the arg manually.\n  void _parse_config(const std::vector<ConfigItem> &args);\n\n  /// Fill in a single config option\n  bool _parse_single_config(const ConfigItem &item, std::size_t level = 0);\n\n  /// Parse \"one\" argument (some may eat more than one), delegate to parent if\n  /// fails, add to missing if missing from main return false if the parse has\n  /// failed and needs to return to parent\n  bool _parse_single(std::vector<std::string> &args, bool &positional_only);\n\n  /// Count the required remaining positional arguments\n  CLI11_NODISCARD std::size_t _count_remaining_positionals(\n      bool required_only = false) const;\n\n  /// Count the required remaining positional arguments\n  CLI11_NODISCARD bool _has_remaining_positionals() const;\n\n  /// Parse a positional, go up the tree to check\n  /// @param haltOnSubcommand if set to true the operation will not process\n  /// subcommands merely return false Return true if the positional was used\n  /// false otherwise\n  bool _parse_positional(std::vector<std::string> &args, bool haltOnSubcommand);\n\n  /// Locate a subcommand by name with two conditions, should disabled\n  /// subcommands be ignored, and should used subcommands be ignored\n  CLI11_NODISCARD App *_find_subcommand(const std::string &subc_name,\n                                        bool ignore_disabled,\n                                        bool ignore_used) const noexcept;\n\n  /// Parse a subcommand, modify args and continue\n  ///\n  /// Unlike the others, this one will always allow fallthrough\n  /// return true if the subcommand was processed false otherwise\n  bool _parse_subcommand(std::vector<std::string> &args);\n\n  /// Parse a short (false) or long (true) argument, must be at the top of the\n  /// list return true if the argument was processed or false if nothing was\n  /// done\n  bool _parse_arg(std::vector<std::string> &args,\n                  detail::Classifier current_type);\n\n  /// Trigger the pre_parse callback if needed\n  void _trigger_pre_parse(std::size_t remaining_args);\n\n  /// Get the appropriate parent to fallthrough to which is the first one that\n  /// has a name or the main app\n  App *_get_fallthrough_parent();\n\n  /// Helper function to run through all possible comparisons of subcommand\n  /// names to check there is no overlap\n  CLI11_NODISCARD const std::string &_compare_subcommand_names(\n      const App &subcom, const App &base) const;\n\n  /// Helper function to place extra values in the most appropriate position\n  void _move_to_missing(detail::Classifier val_type, const std::string &val);\n\n public:\n  /// function that could be used by subclasses of App to shift options around\n  /// into subcommands\n  void _move_option(Option *opt, App *app);\n};  // namespace CLI\n\n/// Extension of App to better manage groups of options\nclass Option_group : public App {\n public:\n  Option_group(std::string group_description, std::string group_name,\n               App *parent)\n      : App(std::move(group_description), \"\", parent) {\n    group(group_name);\n    // option groups should have automatic fallthrough\n  }\n  using App::add_option;\n  /// Add an existing option to the Option_group\n  Option *add_option(Option *opt) {\n    if (get_parent() == nullptr) {\n      throw OptionNotFound(\"Unable to locate the specified option\");\n    }\n    get_parent()->_move_option(opt, this);\n    return opt;\n  }\n  /// Add an existing option to the Option_group\n  void add_options(Option *opt) { add_option(opt); }\n  /// Add a bunch of options to the group\n  template <typename... Args>\n  void add_options(Option *opt, Args... args) {\n    add_option(opt);\n    add_options(args...);\n  }\n  using App::add_subcommand;\n  /// Add an existing subcommand to be a member of an option_group\n  App *add_subcommand(App *subcom) {\n    App_p subc = subcom->get_parent()->get_subcommand_ptr(subcom);\n    subc->get_parent()->remove_subcommand(subcom);\n    add_subcommand(std::move(subc));\n    return subcom;\n  }\n};\n\n/// Helper function to enable one option group/subcommand when another is used\nCLI11_INLINE void TriggerOn(App *trigger_app, App *app_to_enable);\n\n/// Helper function to enable one option group/subcommand when another is used\nCLI11_INLINE void TriggerOn(App *trigger_app,\n                            std::vector<App *> apps_to_enable);\n\n/// Helper function to disable one option group/subcommand when another is used\nCLI11_INLINE void TriggerOff(App *trigger_app, App *app_to_enable);\n\n/// Helper function to disable one option group/subcommand when another is used\nCLI11_INLINE void TriggerOff(App *trigger_app,\n                             std::vector<App *> apps_to_enable);\n\n/// Helper function to mark an option as deprecated\nCLI11_INLINE void deprecate_option(Option *opt,\n                                   const std::string &replacement = \"\");\n\n/// Helper function to mark an option as deprecated\ninline void deprecate_option(App *app, const std::string &option_name,\n                             const std::string &replacement = \"\") {\n  auto *opt = app->get_option(option_name);\n  deprecate_option(opt, replacement);\n}\n\n/// Helper function to mark an option as deprecated\ninline void deprecate_option(App &app, const std::string &option_name,\n                             const std::string &replacement = \"\") {\n  auto *opt = app.get_option(option_name);\n  deprecate_option(opt, replacement);\n}\n\n/// Helper function to mark an option as retired\nCLI11_INLINE void retire_option(App *app, Option *opt);\n\n/// Helper function to mark an option as retired\nCLI11_INLINE void retire_option(App &app, Option *opt);\n\n/// Helper function to mark an option as retired\nCLI11_INLINE void retire_option(App *app, const std::string &option_name);\n\n/// Helper function to mark an option as retired\nCLI11_INLINE void retire_option(App &app, const std::string &option_name);\n\nnamespace detail {\n/// This class is simply to allow tests access to App's protected functions\nstruct AppFriend {\n#ifdef CLI11_CPP14\n\n  /// Wrap _parse_short, perfectly forward arguments and return\n  template <typename... Args>\n  static decltype(auto) parse_arg(App *app, Args &&...args) {\n    return app->_parse_arg(std::forward<Args>(args)...);\n  }\n\n  /// Wrap _parse_subcommand, perfectly forward arguments and return\n  template <typename... Args>\n  static decltype(auto) parse_subcommand(App *app, Args &&...args) {\n    return app->_parse_subcommand(std::forward<Args>(args)...);\n  }\n#else\n  /// Wrap _parse_short, perfectly forward arguments and return\n  template <typename... Args>\n  static auto parse_arg(App *app, Args &&...args) ->\n      typename std::result_of<decltype (&App::_parse_arg)(App, Args...)>::type {\n    return app->_parse_arg(std::forward<Args>(args)...);\n  }\n\n  /// Wrap _parse_subcommand, perfectly forward arguments and return\n  template <typename... Args>\n  static auto parse_subcommand(App *app, Args &&...args) ->\n      typename std::result_of<\n          decltype (&App::_parse_subcommand)(App, Args...)>::type {\n    return app->_parse_subcommand(std::forward<Args>(args)...);\n  }\n#endif\n  /// Wrap the fallthrough parent function to make sure that is working\n  /// correctly\n  static App *get_fallthrough_parent(App *app) {\n    return app->_get_fallthrough_parent();\n  }\n};\n}  // namespace detail\n\nCLI11_INLINE App::App(std::string app_description, std::string app_name,\n                      App *parent)\n    : name_(std::move(app_name)),\n      description_(std::move(app_description)),\n      parent_(parent) {\n  // Inherit if not from a nullptr\n  if (parent_ != nullptr) {\n    if (parent_->help_ptr_ != nullptr)\n      set_help_flag(parent_->help_ptr_->get_name(false, true),\n                    parent_->help_ptr_->get_description());\n    if (parent_->help_all_ptr_ != nullptr)\n      set_help_all_flag(parent_->help_all_ptr_->get_name(false, true),\n                        parent_->help_all_ptr_->get_description());\n\n    /// OptionDefaults\n    option_defaults_ = parent_->option_defaults_;\n\n    // INHERITABLE\n    failure_message_ = parent_->failure_message_;\n    allow_extras_ = parent_->allow_extras_;\n    allow_config_extras_ = parent_->allow_config_extras_;\n    prefix_command_ = parent_->prefix_command_;\n    immediate_callback_ = parent_->immediate_callback_;\n    ignore_case_ = parent_->ignore_case_;\n    ignore_underscore_ = parent_->ignore_underscore_;\n    fallthrough_ = parent_->fallthrough_;\n    validate_positionals_ = parent_->validate_positionals_;\n    validate_optional_arguments_ = parent_->validate_optional_arguments_;\n    configurable_ = parent_->configurable_;\n    allow_windows_style_options_ = parent_->allow_windows_style_options_;\n    group_ = parent_->group_;\n    footer_ = parent_->footer_;\n    formatter_ = parent_->formatter_;\n    config_formatter_ = parent_->config_formatter_;\n    require_subcommand_max_ = parent_->require_subcommand_max_;\n  }\n}\n\nCLI11_INLINE App *App::name(std::string app_name) {\n  if (parent_ != nullptr) {\n    auto oname = name_;\n    name_ = app_name;\n    const auto &res =\n        _compare_subcommand_names(*this, *_get_fallthrough_parent());\n    if (!res.empty()) {\n      name_ = oname;\n      throw(OptionAlreadyAdded(app_name +\n                               \" conflicts with existing subcommand names\"));\n    }\n  } else {\n    name_ = app_name;\n  }\n  has_automatic_name_ = false;\n  return this;\n}\n\nCLI11_INLINE App *App::alias(std::string app_name) {\n  if (app_name.empty() || !detail::valid_alias_name_string(app_name)) {\n    throw IncorrectConstruction(\n        \"Aliases may not be empty or contain newlines or null characters\");\n  }\n  if (parent_ != nullptr) {\n    aliases_.push_back(app_name);\n    const auto &res =\n        _compare_subcommand_names(*this, *_get_fallthrough_parent());\n    if (!res.empty()) {\n      aliases_.pop_back();\n      throw(OptionAlreadyAdded(\n          \"alias already matches an existing subcommand: \" + app_name));\n    }\n  } else {\n    aliases_.push_back(app_name);\n  }\n\n  return this;\n}\n\nCLI11_INLINE App *App::immediate_callback(bool immediate) {\n  immediate_callback_ = immediate;\n  if (immediate_callback_) {\n    if (final_callback_ && !(parse_complete_callback_)) {\n      std::swap(final_callback_, parse_complete_callback_);\n    }\n  } else if (!(final_callback_) && parse_complete_callback_) {\n    std::swap(final_callback_, parse_complete_callback_);\n  }\n  return this;\n}\n\nCLI11_INLINE App *App::ignore_case(bool value) {\n  if (value && !ignore_case_) {\n    ignore_case_ = true;\n    auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;\n    const auto &match = _compare_subcommand_names(*this, *p);\n    if (!match.empty()) {\n      ignore_case_ =\n          false;  // we are throwing so need to be exception invariant\n      throw OptionAlreadyAdded(\n          \"ignore case would cause subcommand name conflicts: \" + match);\n    }\n  }\n  ignore_case_ = value;\n  return this;\n}\n\nCLI11_INLINE App *App::ignore_underscore(bool value) {\n  if (value && !ignore_underscore_) {\n    ignore_underscore_ = true;\n    auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;\n    const auto &match = _compare_subcommand_names(*this, *p);\n    if (!match.empty()) {\n      ignore_underscore_ = false;\n      throw OptionAlreadyAdded(\n          \"ignore underscore would cause subcommand name conflicts: \" + match);\n    }\n  }\n  ignore_underscore_ = value;\n  return this;\n}\n\nCLI11_INLINE Option *App::add_option(std::string option_name,\n                                     callback_t option_callback,\n                                     std::string option_description,\n                                     bool defaulted,\n                                     std::function<std::string()> func) {\n  Option myopt{option_name, option_description, option_callback, this};\n\n  if (std::find_if(std::begin(options_), std::end(options_),\n                   [&myopt](const Option_p &v) { return *v == myopt; }) ==\n      std::end(options_)) {\n    options_.emplace_back();\n    Option_p &option = options_.back();\n    option.reset(\n        new Option(option_name, option_description, option_callback, this));\n\n    // Set the default string capture function\n    option->default_function(func);\n\n    // For compatibility with CLI11 1.7 and before, capture the default string\n    // here\n    if (defaulted) option->capture_default_str();\n\n    // Transfer defaults to the new option\n    option_defaults_.copy_to(option.get());\n\n    // Don't bother to capture if we already did\n    if (!defaulted && option->get_always_capture_default())\n      option->capture_default_str();\n\n    return option.get();\n  }\n  // we know something matches now find what it is so we can produce more error\n  // information\n  for (auto &opt : options_) {\n    const auto &matchname = opt->matching_name(myopt);\n    if (!matchname.empty()) {\n      throw(OptionAlreadyAdded(\"added option matched existing option name: \" +\n                               matchname));\n    }\n  }\n  // this line should not be reached the above loop should trigger the throw\n  throw(OptionAlreadyAdded(\n      \"added option matched existing option name\"));  // LCOV_EXCL_LINE\n}\n\nCLI11_INLINE Option *App::set_help_flag(std::string flag_name,\n                                        const std::string &help_description) {\n  // take flag_description by const reference otherwise add_flag tries to assign\n  // to help_description\n  if (help_ptr_ != nullptr) {\n    remove_option(help_ptr_);\n    help_ptr_ = nullptr;\n  }\n\n  // Empty name will simply remove the help flag\n  if (!flag_name.empty()) {\n    help_ptr_ = add_flag(flag_name, help_description);\n    help_ptr_->configurable(false);\n  }\n\n  return help_ptr_;\n}\n\nCLI11_INLINE Option *App::set_help_all_flag(\n    std::string help_name, const std::string &help_description) {\n  // take flag_description by const reference otherwise add_flag tries to assign\n  // to flag_description\n  if (help_all_ptr_ != nullptr) {\n    remove_option(help_all_ptr_);\n    help_all_ptr_ = nullptr;\n  }\n\n  // Empty name will simply remove the help all flag\n  if (!help_name.empty()) {\n    help_all_ptr_ = add_flag(help_name, help_description);\n    help_all_ptr_->configurable(false);\n  }\n\n  return help_all_ptr_;\n}\n\nCLI11_INLINE Option *App::set_version_flag(std::string flag_name,\n                                           const std::string &versionString,\n                                           const std::string &version_help) {\n  // take flag_description by const reference otherwise add_flag tries to assign\n  // to version_description\n  if (version_ptr_ != nullptr) {\n    remove_option(version_ptr_);\n    version_ptr_ = nullptr;\n  }\n\n  // Empty name will simply remove the version flag\n  if (!flag_name.empty()) {\n    version_ptr_ = add_flag_callback(\n        flag_name,\n        [versionString]() { throw(CLI::CallForVersion(versionString, 0)); },\n        version_help);\n    version_ptr_->configurable(false);\n  }\n\n  return version_ptr_;\n}\n\nCLI11_INLINE Option *App::set_version_flag(std::string flag_name,\n                                           std::function<std::string()> vfunc,\n                                           const std::string &version_help) {\n  if (version_ptr_ != nullptr) {\n    remove_option(version_ptr_);\n    version_ptr_ = nullptr;\n  }\n\n  // Empty name will simply remove the version flag\n  if (!flag_name.empty()) {\n    version_ptr_ = add_flag_callback(\n        flag_name, [vfunc]() { throw(CLI::CallForVersion(vfunc(), 0)); },\n        version_help);\n    version_ptr_->configurable(false);\n  }\n\n  return version_ptr_;\n}\n\nCLI11_INLINE Option *App::_add_flag_internal(std::string flag_name,\n                                             CLI::callback_t fun,\n                                             std::string flag_description) {\n  Option *opt = nullptr;\n  if (detail::has_default_flag_values(flag_name)) {\n    // check for default values and if it has them\n    auto flag_defaults = detail::get_default_flag_values(flag_name);\n    detail::remove_default_flag_values(flag_name);\n    opt = add_option(std::move(flag_name), std::move(fun),\n                     std::move(flag_description), false);\n    for (const auto &fname : flag_defaults) opt->fnames_.push_back(fname.first);\n    opt->default_flag_values_ = std::move(flag_defaults);\n  } else {\n    opt = add_option(std::move(flag_name), std::move(fun),\n                     std::move(flag_description), false);\n  }\n  // flags cannot have positional values\n  if (opt->get_positional()) {\n    auto pos_name = opt->get_name(true);\n    remove_option(opt);\n    throw IncorrectConstruction::PositionalFlag(pos_name);\n  }\n  opt->multi_option_policy(MultiOptionPolicy::TakeLast);\n  opt->expected(0);\n  opt->required(false);\n  return opt;\n}\n\nCLI11_INLINE Option *App::add_flag_callback(\n    std::string flag_name,\n    std::function<void(void)> function,  ///< A function to call, void(void)\n    std::string flag_description) {\n  CLI::callback_t fun = [function](const CLI::results_t &res) {\n    using CLI::detail::lexical_cast;\n    bool trigger{false};\n    auto result = lexical_cast(res[0], trigger);\n    if (result && trigger) {\n      function();\n    }\n    return result;\n  };\n  return _add_flag_internal(flag_name, std::move(fun),\n                            std::move(flag_description));\n}\n\nCLI11_INLINE Option *App::add_flag_function(\n    std::string flag_name,\n    std::function<void(std::int64_t)>\n        function,  ///< A function to call, void(int)\n    std::string flag_description) {\n  CLI::callback_t fun = [function](const CLI::results_t &res) {\n    using CLI::detail::lexical_cast;\n    std::int64_t flag_count{0};\n    lexical_cast(res[0], flag_count);\n    function(flag_count);\n    return true;\n  };\n  return _add_flag_internal(flag_name, std::move(fun),\n                            std::move(flag_description))\n      ->multi_option_policy(MultiOptionPolicy::Sum);\n}\n\nCLI11_INLINE Option *App::set_config(std::string option_name,\n                                     std::string default_filename,\n                                     const std::string &help_message,\n                                     bool config_required) {\n  // Remove existing config if present\n  if (config_ptr_ != nullptr) {\n    remove_option(config_ptr_);\n    config_ptr_ = nullptr;  // need to remove the config_ptr completely\n  }\n\n  // Only add config if option passed\n  if (!option_name.empty()) {\n    config_ptr_ = add_option(option_name, help_message);\n    if (config_required) {\n      config_ptr_->required();\n    }\n    if (!default_filename.empty()) {\n      config_ptr_->default_str(std::move(default_filename));\n    }\n    config_ptr_->configurable(false);\n  }\n\n  return config_ptr_;\n}\n\nCLI11_INLINE bool App::remove_option(Option *opt) {\n  // Make sure no links exist\n  for (Option_p &op : options_) {\n    op->remove_needs(opt);\n    op->remove_excludes(opt);\n  }\n\n  if (help_ptr_ == opt) help_ptr_ = nullptr;\n  if (help_all_ptr_ == opt) help_all_ptr_ = nullptr;\n\n  auto iterator =\n      std::find_if(std::begin(options_), std::end(options_),\n                   [opt](const Option_p &v) { return v.get() == opt; });\n  if (iterator != std::end(options_)) {\n    options_.erase(iterator);\n    return true;\n  }\n  return false;\n}\n\nCLI11_INLINE App *App::add_subcommand(std::string subcommand_name,\n                                      std::string subcommand_description) {\n  if (!subcommand_name.empty() && !detail::valid_name_string(subcommand_name)) {\n    if (!detail::valid_first_char(subcommand_name[0])) {\n      throw IncorrectConstruction(\n          \"Subcommand name starts with invalid character, '!' and '-' are not \"\n          \"allowed\");\n    }\n    for (auto c : subcommand_name) {\n      if (!detail::valid_later_char(c)) {\n        throw IncorrectConstruction(\n            std::string(\"Subcommand name contains invalid character ('\") + c +\n            \"'), all characters are allowed except\"\n            \"'=',':','{','}', and ' '\");\n      }\n    }\n  }\n  CLI::App_p subcom = std::shared_ptr<App>(\n      new App(std::move(subcommand_description), subcommand_name, this));\n  return add_subcommand(std::move(subcom));\n}\n\nCLI11_INLINE App *App::add_subcommand(CLI::App_p subcom) {\n  if (!subcom) throw IncorrectConstruction(\"passed App is not valid\");\n  auto *ckapp =\n      (name_.empty() && parent_ != nullptr) ? _get_fallthrough_parent() : this;\n  const auto &mstrg = _compare_subcommand_names(*subcom, *ckapp);\n  if (!mstrg.empty()) {\n    throw(OptionAlreadyAdded(\n        \"subcommand name or alias matches existing subcommand: \" + mstrg));\n  }\n  subcom->parent_ = this;\n  subcommands_.push_back(std::move(subcom));\n  return subcommands_.back().get();\n}\n\nCLI11_INLINE bool App::remove_subcommand(App *subcom) {\n  // Make sure no links exist\n  for (App_p &sub : subcommands_) {\n    sub->remove_excludes(subcom);\n    sub->remove_needs(subcom);\n  }\n\n  auto iterator =\n      std::find_if(std::begin(subcommands_), std::end(subcommands_),\n                   [subcom](const App_p &v) { return v.get() == subcom; });\n  if (iterator != std::end(subcommands_)) {\n    subcommands_.erase(iterator);\n    return true;\n  }\n  return false;\n}\n\nCLI11_INLINE App *App::get_subcommand(const App *subcom) const {\n  if (subcom == nullptr) throw OptionNotFound(\"nullptr passed\");\n  for (const App_p &subcomptr : subcommands_)\n    if (subcomptr.get() == subcom) return subcomptr.get();\n  throw OptionNotFound(subcom->get_name());\n}\n\nCLI11_NODISCARD CLI11_INLINE App *App::get_subcommand(\n    std::string subcom) const {\n  auto *subc = _find_subcommand(subcom, false, false);\n  if (subc == nullptr) throw OptionNotFound(subcom);\n  return subc;\n}\n\nCLI11_NODISCARD CLI11_INLINE App *App::get_subcommand(int index) const {\n  if (index >= 0) {\n    auto uindex = static_cast<unsigned>(index);\n    if (uindex < subcommands_.size()) return subcommands_[uindex].get();\n  }\n  throw OptionNotFound(std::to_string(index));\n}\n\nCLI11_INLINE CLI::App_p App::get_subcommand_ptr(App *subcom) const {\n  if (subcom == nullptr) throw OptionNotFound(\"nullptr passed\");\n  for (const App_p &subcomptr : subcommands_)\n    if (subcomptr.get() == subcom) return subcomptr;\n  throw OptionNotFound(subcom->get_name());\n}\n\nCLI11_NODISCARD CLI11_INLINE CLI::App_p App::get_subcommand_ptr(\n    std::string subcom) const {\n  for (const App_p &subcomptr : subcommands_)\n    if (subcomptr->check_name(subcom)) return subcomptr;\n  throw OptionNotFound(subcom);\n}\n\nCLI11_NODISCARD CLI11_INLINE CLI::App_p App::get_subcommand_ptr(\n    int index) const {\n  if (index >= 0) {\n    auto uindex = static_cast<unsigned>(index);\n    if (uindex < subcommands_.size()) return subcommands_[uindex];\n  }\n  throw OptionNotFound(std::to_string(index));\n}\n\nCLI11_NODISCARD CLI11_INLINE CLI::App *App::get_option_group(\n    std::string group_name) const {\n  for (const App_p &app : subcommands_) {\n    if (app->name_.empty() && app->group_ == group_name) {\n      return app.get();\n    }\n  }\n  throw OptionNotFound(group_name);\n}\n\nCLI11_NODISCARD CLI11_INLINE std::size_t App::count_all() const {\n  std::size_t cnt{0};\n  for (const auto &opt : options_) {\n    cnt += opt->count();\n  }\n  for (const auto &sub : subcommands_) {\n    cnt += sub->count_all();\n  }\n  if (!get_name().empty()) {  // for named subcommands add the number of times\n                              // the subcommand was called\n    cnt += parsed_;\n  }\n  return cnt;\n}\n\nCLI11_INLINE void App::clear() {\n  parsed_ = 0;\n  pre_parse_called_ = false;\n\n  missing_.clear();\n  parsed_subcommands_.clear();\n  for (const Option_p &opt : options_) {\n    opt->clear();\n  }\n  for (const App_p &subc : subcommands_) {\n    subc->clear();\n  }\n}\n\nCLI11_INLINE void App::parse(int argc, const char *const *argv) {\n  // If the name is not set, read from command line\n  if (name_.empty() || has_automatic_name_) {\n    has_automatic_name_ = true;\n    name_ = argv[0];\n  }\n\n  std::vector<std::string> args;\n  args.reserve(static_cast<std::size_t>(argc) - 1U);\n  for (auto i = static_cast<std::size_t>(argc) - 1U; i > 0U; --i)\n    args.emplace_back(argv[i]);\n  parse(std::move(args));\n}\n\nCLI11_INLINE void App::parse(std::string commandline,\n                             bool program_name_included) {\n  if (program_name_included) {\n    auto nstr = detail::split_program_name(commandline);\n    if ((name_.empty()) || (has_automatic_name_)) {\n      has_automatic_name_ = true;\n      name_ = nstr.first;\n    }\n    commandline = std::move(nstr.second);\n  } else {\n    detail::trim(commandline);\n  }\n  // the next section of code is to deal with quoted arguments after an '=' or\n  // ':' for windows like operations\n  if (!commandline.empty()) {\n    commandline =\n        detail::find_and_modify(commandline, \"=\", detail::escape_detect);\n    if (allow_windows_style_options_)\n      commandline =\n          detail::find_and_modify(commandline, \":\", detail::escape_detect);\n  }\n\n  auto args = detail::split_up(std::move(commandline));\n  // remove all empty strings\n  args.erase(std::remove(args.begin(), args.end(), std::string{}), args.end());\n  std::reverse(args.begin(), args.end());\n\n  parse(std::move(args));\n}\n\nCLI11_INLINE void App::parse(std::vector<std::string> &args) {\n  // Clear if parsed\n  if (parsed_ > 0) clear();\n\n  // parsed_ is incremented in commands/subcommands,\n  // but placed here to make sure this is cleared when\n  // running parse after an error is thrown, even by _validate or _configure.\n  parsed_ = 1;\n  _validate();\n  _configure();\n  // set the parent as nullptr as this object should be the top now\n  parent_ = nullptr;\n  parsed_ = 0;\n\n  _parse(args);\n  run_callback();\n}\n\nCLI11_INLINE void App::parse(std::vector<std::string> &&args) {\n  // Clear if parsed\n  if (parsed_ > 0) clear();\n\n  // parsed_ is incremented in commands/subcommands,\n  // but placed here to make sure this is cleared when\n  // running parse after an error is thrown, even by _validate or _configure.\n  parsed_ = 1;\n  _validate();\n  _configure();\n  // set the parent as nullptr as this object should be the top now\n  parent_ = nullptr;\n  parsed_ = 0;\n\n  _parse(std::move(args));\n  run_callback();\n}\n\nCLI11_INLINE void App::parse_from_stream(std::istream &input) {\n  if (parsed_ == 0) {\n    _validate();\n    _configure();\n    // set the parent as nullptr as this object should be the top now\n  }\n\n  _parse_stream(input);\n  run_callback();\n}\n\nCLI11_INLINE int App::exit(const Error &e, std::ostream &out,\n                           std::ostream &err) const {\n  /// Avoid printing anything if this is a CLI::RuntimeError\n  if (e.get_name() == \"RuntimeError\") return e.get_exit_code();\n\n  if (e.get_name() == \"CallForHelp\") {\n    out << help();\n    return e.get_exit_code();\n  }\n\n  if (e.get_name() == \"CallForAllHelp\") {\n    out << help(\"\", AppFormatMode::All);\n    return e.get_exit_code();\n  }\n\n  if (e.get_name() == \"CallForVersion\") {\n    out << e.what() << std::endl;\n    return e.get_exit_code();\n  }\n\n  if (e.get_exit_code() != static_cast<int>(ExitCodes::Success)) {\n    if (failure_message_) err << failure_message_(this, e) << std::flush;\n  }\n\n  return e.get_exit_code();\n}\n\nCLI11_INLINE std::vector<const App *> App::get_subcommands(\n    const std::function<bool(const App *)> &filter) const {\n  std::vector<const App *> subcomms(subcommands_.size());\n  std::transform(std::begin(subcommands_), std::end(subcommands_),\n                 std::begin(subcomms), [](const App_p &v) { return v.get(); });\n\n  if (filter) {\n    subcomms.erase(\n        std::remove_if(std::begin(subcomms), std::end(subcomms),\n                       [&filter](const App *app) { return !filter(app); }),\n        std::end(subcomms));\n  }\n\n  return subcomms;\n}\n\nCLI11_INLINE std::vector<App *> App::get_subcommands(\n    const std::function<bool(App *)> &filter) {\n  std::vector<App *> subcomms(subcommands_.size());\n  std::transform(std::begin(subcommands_), std::end(subcommands_),\n                 std::begin(subcomms), [](const App_p &v) { return v.get(); });\n\n  if (filter) {\n    subcomms.erase(std::remove_if(std::begin(subcomms), std::end(subcomms),\n                                  [&filter](App *app) { return !filter(app); }),\n                   std::end(subcomms));\n  }\n\n  return subcomms;\n}\n\nCLI11_INLINE bool App::remove_excludes(Option *opt) {\n  auto iterator =\n      std::find(std::begin(exclude_options_), std::end(exclude_options_), opt);\n  if (iterator == std::end(exclude_options_)) {\n    return false;\n  }\n  exclude_options_.erase(iterator);\n  return true;\n}\n\nCLI11_INLINE bool App::remove_excludes(App *app) {\n  auto iterator = std::find(std::begin(exclude_subcommands_),\n                            std::end(exclude_subcommands_), app);\n  if (iterator == std::end(exclude_subcommands_)) {\n    return false;\n  }\n  auto *other_app = *iterator;\n  exclude_subcommands_.erase(iterator);\n  other_app->remove_excludes(this);\n  return true;\n}\n\nCLI11_INLINE bool App::remove_needs(Option *opt) {\n  auto iterator =\n      std::find(std::begin(need_options_), std::end(need_options_), opt);\n  if (iterator == std::end(need_options_)) {\n    return false;\n  }\n  need_options_.erase(iterator);\n  return true;\n}\n\nCLI11_INLINE bool App::remove_needs(App *app) {\n  auto iterator = std::find(std::begin(need_subcommands_),\n                            std::end(need_subcommands_), app);\n  if (iterator == std::end(need_subcommands_)) {\n    return false;\n  }\n  need_subcommands_.erase(iterator);\n  return true;\n}\n\nCLI11_NODISCARD CLI11_INLINE std::string App::help(std::string prev,\n                                                   AppFormatMode mode) const {\n  if (prev.empty())\n    prev = get_name();\n  else\n    prev += \" \" + get_name();\n\n  // Delegate to subcommand if needed\n  auto selected_subcommands = get_subcommands();\n  if (!selected_subcommands.empty()) {\n    return selected_subcommands.back()->help(prev, mode);\n  }\n  return formatter_->make_help(this, prev, mode);\n}\n\nCLI11_NODISCARD CLI11_INLINE std::string App::version() const {\n  std::string val;\n  if (version_ptr_ != nullptr) {\n    auto rv = version_ptr_->results();\n    version_ptr_->clear();\n    version_ptr_->add_result(\"true\");\n    try {\n      version_ptr_->run_callback();\n    } catch (const CLI::CallForVersion &cfv) {\n      val = cfv.what();\n    }\n    version_ptr_->clear();\n    version_ptr_->add_result(rv);\n  }\n  return val;\n}\n\nCLI11_INLINE std::vector<const Option *> App::get_options(\n    const std::function<bool(const Option *)> filter) const {\n  std::vector<const Option *> options(options_.size());\n  std::transform(std::begin(options_), std::end(options_), std::begin(options),\n                 [](const Option_p &val) { return val.get(); });\n\n  if (filter) {\n    options.erase(\n        std::remove_if(std::begin(options), std::end(options),\n                       [&filter](const Option *opt) { return !filter(opt); }),\n        std::end(options));\n  }\n\n  return options;\n}\n\nCLI11_INLINE std::vector<Option *> App::get_options(\n    const std::function<bool(Option *)> filter) {\n  std::vector<Option *> options(options_.size());\n  std::transform(std::begin(options_), std::end(options_), std::begin(options),\n                 [](const Option_p &val) { return val.get(); });\n\n  if (filter) {\n    options.erase(\n        std::remove_if(std::begin(options), std::end(options),\n                       [&filter](Option *opt) { return !filter(opt); }),\n        std::end(options));\n  }\n\n  return options;\n}\n\nCLI11_INLINE Option *App::get_option_no_throw(\n    std::string option_name) noexcept {\n  for (Option_p &opt : options_) {\n    if (opt->check_name(option_name)) {\n      return opt.get();\n    }\n  }\n  for (auto &subc : subcommands_) {\n    // also check down into nameless subcommands\n    if (subc->get_name().empty()) {\n      auto *opt = subc->get_option_no_throw(option_name);\n      if (opt != nullptr) {\n        return opt;\n      }\n    }\n  }\n  return nullptr;\n}\n\nCLI11_NODISCARD CLI11_INLINE const Option *App::get_option_no_throw(\n    std::string option_name) const noexcept {\n  for (const Option_p &opt : options_) {\n    if (opt->check_name(option_name)) {\n      return opt.get();\n    }\n  }\n  for (const auto &subc : subcommands_) {\n    // also check down into nameless subcommands\n    if (subc->get_name().empty()) {\n      auto *opt = subc->get_option_no_throw(option_name);\n      if (opt != nullptr) {\n        return opt;\n      }\n    }\n  }\n  return nullptr;\n}\n\nCLI11_NODISCARD CLI11_INLINE std::string App::get_display_name(\n    bool with_aliases) const {\n  if (name_.empty()) {\n    return std::string(\"[Option Group: \") + get_group() + \"]\";\n  }\n  if (aliases_.empty() || !with_aliases) {\n    return name_;\n  }\n  std::string dispname = name_;\n  for (const auto &lalias : aliases_) {\n    dispname.push_back(',');\n    dispname.push_back(' ');\n    dispname.append(lalias);\n  }\n  return dispname;\n}\n\nCLI11_NODISCARD CLI11_INLINE bool App::check_name(\n    std::string name_to_check) const {\n  std::string local_name = name_;\n  if (ignore_underscore_) {\n    local_name = detail::remove_underscore(name_);\n    name_to_check = detail::remove_underscore(name_to_check);\n  }\n  if (ignore_case_) {\n    local_name = detail::to_lower(name_);\n    name_to_check = detail::to_lower(name_to_check);\n  }\n\n  if (local_name == name_to_check) {\n    return true;\n  }\n  for (auto les : aliases_) {  // NOLINT(performance-for-range-copy)\n    if (ignore_underscore_) {\n      les = detail::remove_underscore(les);\n    }\n    if (ignore_case_) {\n      les = detail::to_lower(les);\n    }\n    if (les == name_to_check) {\n      return true;\n    }\n  }\n  return false;\n}\n\nCLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::get_groups() const {\n  std::vector<std::string> groups;\n\n  for (const Option_p &opt : options_) {\n    // Add group if it is not already in there\n    if (std::find(groups.begin(), groups.end(), opt->get_group()) ==\n        groups.end()) {\n      groups.push_back(opt->get_group());\n    }\n  }\n\n  return groups;\n}\n\nCLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::remaining(\n    bool recurse) const {\n  std::vector<std::string> miss_list;\n  for (const std::pair<detail::Classifier, std::string> &miss : missing_) {\n    miss_list.push_back(std::get<1>(miss));\n  }\n  // Get from a subcommand that may allow extras\n  if (recurse) {\n    if (!allow_extras_) {\n      for (const auto &sub : subcommands_) {\n        if (sub->name_.empty() && !sub->missing_.empty()) {\n          for (const std::pair<detail::Classifier, std::string> &miss :\n               sub->missing_) {\n            miss_list.push_back(std::get<1>(miss));\n          }\n        }\n      }\n    }\n    // Recurse into subcommands\n\n    for (const App *sub : parsed_subcommands_) {\n      std::vector<std::string> output = sub->remaining(recurse);\n      std::copy(std::begin(output), std::end(output),\n                std::back_inserter(miss_list));\n    }\n  }\n  return miss_list;\n}\n\nCLI11_NODISCARD CLI11_INLINE std::vector<std::string>\nApp::remaining_for_passthrough(bool recurse) const {\n  std::vector<std::string> miss_list = remaining(recurse);\n  std::reverse(std::begin(miss_list), std::end(miss_list));\n  return miss_list;\n}\n\nCLI11_NODISCARD CLI11_INLINE std::size_t App::remaining_size(\n    bool recurse) const {\n  auto remaining_options = static_cast<std::size_t>(\n      std::count_if(std::begin(missing_), std::end(missing_),\n                    [](const std::pair<detail::Classifier, std::string> &val) {\n                      return val.first != detail::Classifier::POSITIONAL_MARK;\n                    }));\n\n  if (recurse) {\n    for (const App_p &sub : subcommands_) {\n      remaining_options += sub->remaining_size(recurse);\n    }\n  }\n  return remaining_options;\n}\n\nCLI11_INLINE void App::_validate() const {\n  // count the number of positional only args\n  auto pcount = std::count_if(std::begin(options_), std::end(options_),\n                              [](const Option_p &opt) {\n                                return opt->get_items_expected_max() >=\n                                           detail::expected_max_vector_size &&\n                                       !opt->nonpositional();\n                              });\n  if (pcount > 1) {\n    auto pcount_req = std::count_if(\n        std::begin(options_), std::end(options_), [](const Option_p &opt) {\n          return opt->get_items_expected_max() >=\n                     detail::expected_max_vector_size &&\n                 !opt->nonpositional() && opt->get_required();\n        });\n    if (pcount - pcount_req > 1) {\n      throw InvalidError(name_);\n    }\n  }\n\n  std::size_t nameless_subs{0};\n  for (const App_p &app : subcommands_) {\n    app->_validate();\n    if (app->get_name().empty()) ++nameless_subs;\n  }\n\n  if (require_option_min_ > 0) {\n    if (require_option_max_ > 0) {\n      if (require_option_max_ < require_option_min_) {\n        throw(InvalidError(\n            \"Required min options greater than required max options\",\n            ExitCodes::InvalidError));\n      }\n    }\n    if (require_option_min_ > (options_.size() + nameless_subs)) {\n      throw(InvalidError(\n          \"Required min options greater than number of available options\",\n          ExitCodes::InvalidError));\n    }\n  }\n}\n\nCLI11_INLINE void App::_configure() {\n  if (default_startup == startup_mode::enabled) {\n    disabled_ = false;\n  } else if (default_startup == startup_mode::disabled) {\n    disabled_ = true;\n  }\n  for (const App_p &app : subcommands_) {\n    if (app->has_automatic_name_) {\n      app->name_.clear();\n    }\n    if (app->name_.empty()) {\n      app->fallthrough_ =\n          false;  // make sure fallthrough_ is false to prevent infinite loop\n      app->prefix_command_ = false;\n    }\n    // make sure the parent is set to be this object in preparation for parse\n    app->parent_ = this;\n    app->_configure();\n  }\n}\n\nCLI11_INLINE void App::run_callback(bool final_mode,\n                                    bool suppress_final_callback) {\n  pre_callback();\n  // in the main app if immediate_callback_ is set it runs the main callback\n  // before the used subcommands\n  if (!final_mode && parse_complete_callback_) {\n    parse_complete_callback_();\n  }\n  // run the callbacks for the received subcommands\n  for (App *subc : get_subcommands()) {\n    if (subc->parent_ == this) {\n      subc->run_callback(true, suppress_final_callback);\n    }\n  }\n  // now run callbacks for option_groups\n  for (auto &subc : subcommands_) {\n    if (subc->name_.empty() && subc->count_all() > 0) {\n      subc->run_callback(true, suppress_final_callback);\n    }\n  }\n\n  // finally run the main callback\n  if (final_callback_ && (parsed_ > 0) && (!suppress_final_callback)) {\n    if (!name_.empty() || count_all() > 0 || parent_ == nullptr) {\n      final_callback_();\n    }\n  }\n}\n\nCLI11_NODISCARD CLI11_INLINE bool App::_valid_subcommand(\n    const std::string &current, bool ignore_used) const {\n  // Don't match if max has been reached - but still check parents\n  if (require_subcommand_max_ != 0 &&\n      parsed_subcommands_.size() >= require_subcommand_max_) {\n    return parent_ != nullptr &&\n           parent_->_valid_subcommand(current, ignore_used);\n  }\n  auto *com = _find_subcommand(current, true, ignore_used);\n  if (com != nullptr) {\n    return true;\n  }\n  // Check parent if exists, else return false\n  return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);\n}\n\nCLI11_NODISCARD CLI11_INLINE detail::Classifier App::_recognize(\n    const std::string &current, bool ignore_used_subcommands) const {\n  std::string dummy1, dummy2;\n\n  if (current == \"--\") return detail::Classifier::POSITIONAL_MARK;\n  if (_valid_subcommand(current, ignore_used_subcommands))\n    return detail::Classifier::SUBCOMMAND;\n  if (detail::split_long(current, dummy1, dummy2))\n    return detail::Classifier::LONG;\n  if (detail::split_short(current, dummy1, dummy2)) {\n    if (dummy1[0] >= '0' && dummy1[0] <= '9') {\n      if (get_option_no_throw(std::string{'-', dummy1[0]}) == nullptr) {\n        return detail::Classifier::NONE;\n      }\n    }\n    return detail::Classifier::SHORT;\n  }\n  if ((allow_windows_style_options_) &&\n      (detail::split_windows_style(current, dummy1, dummy2)))\n    return detail::Classifier::WINDOWS_STYLE;\n  if ((current == \"++\") && !name_.empty() && parent_ != nullptr)\n    return detail::Classifier::SUBCOMMAND_TERMINATOR;\n  return detail::Classifier::NONE;\n}\n\nCLI11_INLINE void App::_process_config_file() {\n  if (config_ptr_ != nullptr) {\n    bool config_required = config_ptr_->get_required();\n    auto file_given = config_ptr_->count() > 0;\n    auto config_files = config_ptr_->as<std::vector<std::string>>();\n    if (config_files.empty() || config_files.front().empty()) {\n      if (config_required) {\n        throw FileError::Missing(\"no specified config file\");\n      }\n      return;\n    }\n    for (auto rit = config_files.rbegin(); rit != config_files.rend(); ++rit) {\n      const auto &config_file = *rit;\n      auto path_result = detail::check_path(config_file.c_str());\n      if (path_result == detail::path_type::file) {\n        try {\n          std::vector<ConfigItem> values =\n              config_formatter_->from_file(config_file);\n          _parse_config(values);\n          if (!file_given) {\n            config_ptr_->add_result(config_file);\n          }\n        } catch (const FileError &) {\n          if (config_required || file_given) throw;\n        }\n      } else if (config_required || file_given) {\n        throw FileError::Missing(config_file);\n      }\n    }\n  }\n}\n\nCLI11_INLINE void App::_process_env() {\n  for (const Option_p &opt : options_) {\n    if (opt->count() == 0 && !opt->envname_.empty()) {\n      char *buffer = nullptr;\n      std::string ename_string;\n\n#ifdef _MSC_VER\n      // Windows version\n      std::size_t sz = 0;\n      if (_dupenv_s(&buffer, &sz, opt->envname_.c_str()) == 0 &&\n          buffer != nullptr) {\n        ename_string = std::string(buffer);\n        free(buffer);\n      }\n#else\n      // This also works on Windows, but gives a warning\n      buffer = std::getenv(opt->envname_.c_str());\n      if (buffer != nullptr) ename_string = std::string(buffer);\n#endif\n\n      if (!ename_string.empty()) {\n        opt->add_result(ename_string);\n      }\n    }\n  }\n\n  for (App_p &sub : subcommands_) {\n    if (sub->get_name().empty() || !sub->parse_complete_callback_)\n      sub->_process_env();\n  }\n}\n\nCLI11_INLINE void App::_process_callbacks() {\n  for (App_p &sub : subcommands_) {\n    // process the priority option_groups first\n    if (sub->get_name().empty() && sub->parse_complete_callback_) {\n      if (sub->count_all() > 0) {\n        sub->_process_callbacks();\n        sub->run_callback();\n      }\n    }\n  }\n\n  for (const Option_p &opt : options_) {\n    if ((*opt) && !opt->get_callback_run()) {\n      opt->run_callback();\n    }\n  }\n  for (App_p &sub : subcommands_) {\n    if (!sub->parse_complete_callback_) {\n      sub->_process_callbacks();\n    }\n  }\n}\n\nCLI11_INLINE void App::_process_help_flags(bool trigger_help,\n                                           bool trigger_all_help) const {\n  const Option *help_ptr = get_help_ptr();\n  const Option *help_all_ptr = get_help_all_ptr();\n\n  if (help_ptr != nullptr && help_ptr->count() > 0) trigger_help = true;\n  if (help_all_ptr != nullptr && help_all_ptr->count() > 0)\n    trigger_all_help = true;\n\n  // If there were parsed subcommands, call those. First subcommand wins if\n  // there are multiple ones.\n  if (!parsed_subcommands_.empty()) {\n    for (const App *sub : parsed_subcommands_)\n      sub->_process_help_flags(trigger_help, trigger_all_help);\n\n    // Only the final subcommand should call for help. All help wins over help.\n  } else if (trigger_all_help) {\n    throw CallForAllHelp();\n  } else if (trigger_help) {\n    throw CallForHelp();\n  }\n}\n\nCLI11_INLINE void App::_process_requirements() {\n  // check excludes\n  bool excluded{false};\n  std::string excluder;\n  for (const auto &opt : exclude_options_) {\n    if (opt->count() > 0) {\n      excluded = true;\n      excluder = opt->get_name();\n    }\n  }\n  for (const auto &subc : exclude_subcommands_) {\n    if (subc->count_all() > 0) {\n      excluded = true;\n      excluder = subc->get_display_name();\n    }\n  }\n  if (excluded) {\n    if (count_all() > 0) {\n      throw ExcludesError(get_display_name(), excluder);\n    }\n    // if we are excluded but didn't receive anything, just return\n    return;\n  }\n\n  // check excludes\n  bool missing_needed{false};\n  std::string missing_need;\n  for (const auto &opt : need_options_) {\n    if (opt->count() == 0) {\n      missing_needed = true;\n      missing_need = opt->get_name();\n    }\n  }\n  for (const auto &subc : need_subcommands_) {\n    if (subc->count_all() == 0) {\n      missing_needed = true;\n      missing_need = subc->get_display_name();\n    }\n  }\n  if (missing_needed) {\n    if (count_all() > 0) {\n      throw RequiresError(get_display_name(), missing_need);\n    }\n    // if we missing something but didn't have any options, just return\n    return;\n  }\n\n  std::size_t used_options = 0;\n  for (const Option_p &opt : options_) {\n    if (opt->count() != 0) {\n      ++used_options;\n    }\n    // Required but empty\n    if (opt->get_required() && opt->count() == 0) {\n      throw RequiredError(opt->get_name());\n    }\n    // Requires\n    for (const Option *opt_req : opt->needs_)\n      if (opt->count() > 0 && opt_req->count() == 0)\n        throw RequiresError(opt->get_name(), opt_req->get_name());\n    // Excludes\n    for (const Option *opt_ex : opt->excludes_)\n      if (opt->count() > 0 && opt_ex->count() != 0)\n        throw ExcludesError(opt->get_name(), opt_ex->get_name());\n  }\n  // check for the required number of subcommands\n  if (require_subcommand_min_ > 0) {\n    auto selected_subcommands = get_subcommands();\n    if (require_subcommand_min_ > selected_subcommands.size())\n      throw RequiredError::Subcommand(require_subcommand_min_);\n  }\n\n  // Max error cannot occur, the extra subcommand will parse as an ExtrasError\n  // or a remaining item.\n\n  // run this loop to check how many unnamed subcommands were actually used\n  // since they are considered options from the perspective of an App\n  for (App_p &sub : subcommands_) {\n    if (sub->disabled_) continue;\n    if (sub->name_.empty() && sub->count_all() > 0) {\n      ++used_options;\n    }\n  }\n\n  if (require_option_min_ > used_options ||\n      (require_option_max_ > 0 && require_option_max_ < used_options)) {\n    auto option_list = detail::join(options_, [this](const Option_p &ptr) {\n      if (ptr.get() == help_ptr_ || ptr.get() == help_all_ptr_) {\n        return std::string{};\n      }\n      return ptr->get_name(false, true);\n    });\n\n    auto subc_list = get_subcommands([](App *app) {\n      return ((app->get_name().empty()) && (!app->disabled_));\n    });\n    if (!subc_list.empty()) {\n      option_list += \",\" + detail::join(subc_list, [](const App *app) {\n                       return app->get_display_name();\n                     });\n    }\n    throw RequiredError::Option(require_option_min_, require_option_max_,\n                                used_options, option_list);\n  }\n\n  // now process the requirements for subcommands if needed\n  for (App_p &sub : subcommands_) {\n    if (sub->disabled_) continue;\n    if (sub->name_.empty() && sub->required_ == false) {\n      if (sub->count_all() == 0) {\n        if (require_option_min_ > 0 && require_option_min_ <= used_options) {\n          continue;\n          // if we have met the requirement and there is nothing in this option\n          // group skip checking requirements\n        }\n        if (require_option_max_ > 0 && used_options >= require_option_min_) {\n          continue;\n          // if we have met the requirement and there is nothing in this option\n          // group skip checking requirements\n        }\n      }\n    }\n    if (sub->count() > 0 || sub->name_.empty()) {\n      sub->_process_requirements();\n    }\n\n    if (sub->required_ && sub->count_all() == 0) {\n      throw(CLI::RequiredError(sub->get_display_name()));\n    }\n  }\n}\n\nCLI11_INLINE void App::_process() {\n  try {\n    // the config file might generate a FileError but that should not be\n    // processed until later in the process to allow for help, version and other\n    // errors to generate first.\n    _process_config_file();\n\n    // process env shouldn't throw but no reason to process it if config\n    // generated an error\n    _process_env();\n  } catch (const CLI::FileError &) {\n    // callbacks and help_flags can generate exceptions which should take\n    // priority over the config file error if one exists.\n    _process_callbacks();\n    _process_help_flags();\n    throw;\n  }\n\n  _process_callbacks();\n  _process_help_flags();\n\n  _process_requirements();\n}\n\nCLI11_INLINE void App::_process_extras() {\n  if (!(allow_extras_ || prefix_command_)) {\n    std::size_t num_left_over = remaining_size();\n    if (num_left_over > 0) {\n      throw ExtrasError(name_, remaining(false));\n    }\n  }\n\n  for (App_p &sub : subcommands_) {\n    if (sub->count() > 0) sub->_process_extras();\n  }\n}\n\nCLI11_INLINE void App::_process_extras(std::vector<std::string> &args) {\n  if (!(allow_extras_ || prefix_command_)) {\n    std::size_t num_left_over = remaining_size();\n    if (num_left_over > 0) {\n      args = remaining(false);\n      throw ExtrasError(name_, args);\n    }\n  }\n\n  for (App_p &sub : subcommands_) {\n    if (sub->count() > 0) sub->_process_extras(args);\n  }\n}\n\nCLI11_INLINE void App::increment_parsed() {\n  ++parsed_;\n  for (App_p &sub : subcommands_) {\n    if (sub->get_name().empty()) sub->increment_parsed();\n  }\n}\n\nCLI11_INLINE void App::_parse(std::vector<std::string> &args) {\n  increment_parsed();\n  _trigger_pre_parse(args.size());\n  bool positional_only = false;\n\n  while (!args.empty()) {\n    if (!_parse_single(args, positional_only)) {\n      break;\n    }\n  }\n\n  if (parent_ == nullptr) {\n    _process();\n\n    // Throw error if any items are left over (depending on settings)\n    _process_extras(args);\n\n    // Convert missing (pairs) to extras (string only) ready for processing in\n    // another app\n    args = remaining_for_passthrough(false);\n  } else if (parse_complete_callback_) {\n    _process_env();\n    _process_callbacks();\n    _process_help_flags();\n    _process_requirements();\n    run_callback(false, true);\n  }\n}\n\nCLI11_INLINE void App::_parse(std::vector<std::string> &&args) {\n  // this can only be called by the top level in which case parent == nullptr by\n  // definition operation is simplified\n  increment_parsed();\n  _trigger_pre_parse(args.size());\n  bool positional_only = false;\n\n  while (!args.empty()) {\n    _parse_single(args, positional_only);\n  }\n  _process();\n\n  // Throw error if any items are left over (depending on settings)\n  _process_extras();\n}\n\nCLI11_INLINE void App::_parse_stream(std::istream &input) {\n  auto values = config_formatter_->from_config(input);\n  _parse_config(values);\n  increment_parsed();\n  _trigger_pre_parse(values.size());\n  _process();\n\n  // Throw error if any items are left over (depending on settings)\n  _process_extras();\n}\n\nCLI11_INLINE void App::_parse_config(const std::vector<ConfigItem> &args) {\n  for (const ConfigItem &item : args) {\n    if (!_parse_single_config(item) &&\n        allow_config_extras_ == config_extras_mode::error)\n      throw ConfigError::Extras(item.fullname());\n  }\n}\n\nCLI11_INLINE bool App::_parse_single_config(const ConfigItem &item,\n                                            std::size_t level) {\n  if (level < item.parents.size()) {\n    try {\n      auto *subcom = get_subcommand(item.parents.at(level));\n      auto result = subcom->_parse_single_config(item, level + 1);\n\n      return result;\n    } catch (const OptionNotFound &) {\n      return false;\n    }\n  }\n  // check for section open\n  if (item.name == \"++\") {\n    if (configurable_) {\n      increment_parsed();\n      _trigger_pre_parse(2);\n      if (parent_ != nullptr) {\n        parent_->parsed_subcommands_.push_back(this);\n      }\n    }\n    return true;\n  }\n  // check for section close\n  if (item.name == \"--\") {\n    if (configurable_ && parse_complete_callback_) {\n      _process_callbacks();\n      _process_requirements();\n      run_callback();\n    }\n    return true;\n  }\n  Option *op = get_option_no_throw(\"--\" + item.name);\n  if (op == nullptr) {\n    if (item.name.size() == 1) {\n      op = get_option_no_throw(\"-\" + item.name);\n    }\n  }\n  if (op == nullptr) {\n    op = get_option_no_throw(item.name);\n  }\n  if (op == nullptr) {\n    // If the option was not present\n    if (get_allow_config_extras() == config_extras_mode::capture)\n      // Should we worry about classifying the extras properly?\n      missing_.emplace_back(detail::Classifier::NONE, item.fullname());\n    return false;\n  }\n\n  if (!op->get_configurable()) {\n    if (get_allow_config_extras() == config_extras_mode::ignore_all) {\n      return false;\n    }\n    throw ConfigError::NotConfigurable(item.fullname());\n  }\n\n  if (op->empty()) {\n    if (op->get_expected_min() == 0) {\n      if (item.inputs.size() <= 1) {\n        // Flag parsing\n        auto res = config_formatter_->to_flag(item);\n        bool converted{false};\n        if (op->get_disable_flag_override()) {\n          try {\n            auto val = detail::to_flag_value(res);\n            if (val == 1) {\n              res = op->get_flag_value(item.name, \"{}\");\n              converted = true;\n            }\n          } catch (...) {\n          }\n        }\n\n        if (!converted) {\n          res = op->get_flag_value(item.name, res);\n        }\n\n        op->add_result(res);\n        return true;\n      }\n      if (static_cast<int>(item.inputs.size()) > op->get_items_expected_max()) {\n        if (op->get_items_expected_max() > 1) {\n          throw ArgumentMismatch::AtMost(item.fullname(),\n                                         op->get_items_expected_max(),\n                                         item.inputs.size());\n        }\n        throw ConversionError::TooManyInputsFlag(item.fullname());\n      }\n    }\n    op->add_result(item.inputs);\n    op->run_callback();\n  }\n\n  return true;\n}\n\nCLI11_INLINE bool App::_parse_single(std::vector<std::string> &args,\n                                     bool &positional_only) {\n  bool retval = true;\n  detail::Classifier classifier =\n      positional_only ? detail::Classifier::NONE : _recognize(args.back());\n  switch (classifier) {\n    case detail::Classifier::POSITIONAL_MARK:\n      args.pop_back();\n      positional_only = true;\n      if ((!_has_remaining_positionals()) && (parent_ != nullptr)) {\n        retval = false;\n      } else {\n        _move_to_missing(classifier, \"--\");\n      }\n      break;\n    case detail::Classifier::SUBCOMMAND_TERMINATOR:\n      // treat this like a positional mark if in the parent app\n      args.pop_back();\n      retval = false;\n      break;\n    case detail::Classifier::SUBCOMMAND:\n      retval = _parse_subcommand(args);\n      break;\n    case detail::Classifier::LONG:\n    case detail::Classifier::SHORT:\n    case detail::Classifier::WINDOWS_STYLE:\n      // If already parsed a subcommand, don't accept options_\n      _parse_arg(args, classifier);\n      break;\n    case detail::Classifier::NONE:\n      // Probably a positional or something for a parent (sub)command\n      retval = _parse_positional(args, false);\n      if (retval && positionals_at_end_) {\n        positional_only = true;\n      }\n      break;\n      // LCOV_EXCL_START\n    default:\n      throw HorribleError(\"unrecognized classifier (you should not see this!)\");\n      // LCOV_EXCL_STOP\n  }\n  return retval;\n}\n\nCLI11_NODISCARD CLI11_INLINE std::size_t App::_count_remaining_positionals(\n    bool required_only) const {\n  std::size_t retval = 0;\n  for (const Option_p &opt : options_) {\n    if (opt->get_positional() && (!required_only || opt->get_required())) {\n      if (opt->get_items_expected_min() > 0 &&\n          static_cast<int>(opt->count()) < opt->get_items_expected_min()) {\n        retval += static_cast<std::size_t>(opt->get_items_expected_min()) -\n                  opt->count();\n      }\n    }\n  }\n  return retval;\n}\n\nCLI11_NODISCARD CLI11_INLINE bool App::_has_remaining_positionals() const {\n  for (const Option_p &opt : options_) {\n    if (opt->get_positional() &&\n        ((static_cast<int>(opt->count()) < opt->get_items_expected_min()))) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nCLI11_INLINE bool App::_parse_positional(std::vector<std::string> &args,\n                                         bool haltOnSubcommand) {\n  const std::string &positional = args.back();\n\n  if (positionals_at_end_) {\n    // deal with the case of required arguments at the end which should take\n    // precedence over other arguments\n    auto arg_rem = args.size();\n    auto remreq = _count_remaining_positionals(true);\n    if (arg_rem <= remreq) {\n      for (const Option_p &opt : options_) {\n        if (opt->get_positional() && opt->required_) {\n          if (static_cast<int>(opt->count()) < opt->get_items_expected_min()) {\n            if (validate_positionals_) {\n              std::string pos = positional;\n              pos = opt->_validate(pos, 0);\n              if (!pos.empty()) {\n                continue;\n              }\n            }\n\n            parse_order_.push_back(opt.get());\n            /// if we require a separator add it here\n            if (opt->get_inject_separator()) {\n              if (!opt->results().empty() && !opt->results().back().empty()) {\n                opt->add_result(std::string{});\n              }\n            }\n            if (opt->get_trigger_on_parse() &&\n                opt->current_option_state_ ==\n                    Option::option_state::callback_run) {\n              opt->clear();\n            }\n            opt->add_result(positional);\n            if (opt->get_trigger_on_parse()) {\n              opt->run_callback();\n            }\n            args.pop_back();\n            return true;\n          }\n        }\n      }\n    }\n  }\n  for (const Option_p &opt : options_) {\n    // Eat options, one by one, until done\n    if (opt->get_positional() &&\n        (static_cast<int>(opt->count()) < opt->get_items_expected_min() ||\n         opt->get_allow_extra_args())) {\n      if (validate_positionals_) {\n        std::string pos = positional;\n        pos = opt->_validate(pos, 0);\n        if (!pos.empty()) {\n          continue;\n        }\n      }\n      if (opt->get_inject_separator()) {\n        if (!opt->results().empty() && !opt->results().back().empty()) {\n          opt->add_result(std::string{});\n        }\n      }\n      if (opt->get_trigger_on_parse() &&\n          opt->current_option_state_ == Option::option_state::callback_run) {\n        opt->clear();\n      }\n      opt->add_result(positional);\n      if (opt->get_trigger_on_parse()) {\n        opt->run_callback();\n      }\n      parse_order_.push_back(opt.get());\n      args.pop_back();\n      return true;\n    }\n  }\n\n  for (auto &subc : subcommands_) {\n    if ((subc->name_.empty()) && (!subc->disabled_)) {\n      if (subc->_parse_positional(args, false)) {\n        if (!subc->pre_parse_called_) {\n          subc->_trigger_pre_parse(args.size());\n        }\n        return true;\n      }\n    }\n  }\n  // let the parent deal with it if possible\n  if (parent_ != nullptr && fallthrough_)\n    return _get_fallthrough_parent()->_parse_positional(\n        args, static_cast<bool>(parse_complete_callback_));\n\n  /// Try to find a local subcommand that is repeated\n  auto *com = _find_subcommand(args.back(), true, false);\n  if (com != nullptr &&\n      (require_subcommand_max_ == 0 ||\n       require_subcommand_max_ > parsed_subcommands_.size())) {\n    if (haltOnSubcommand) {\n      return false;\n    }\n    args.pop_back();\n    com->_parse(args);\n    return true;\n  }\n  /// now try one last gasp at subcommands that have been executed before, go to\n  /// root app and try to find a subcommand in a broader way, if one exists let\n  /// the parent deal with it\n  auto *parent_app = (parent_ != nullptr) ? _get_fallthrough_parent() : this;\n  com = parent_app->_find_subcommand(args.back(), true, false);\n  if (com != nullptr && (com->parent_->require_subcommand_max_ == 0 ||\n                         com->parent_->require_subcommand_max_ >\n                             com->parent_->parsed_subcommands_.size())) {\n    return false;\n  }\n\n  if (positionals_at_end_) {\n    throw CLI::ExtrasError(name_, args);\n  }\n  /// If this is an option group don't deal with it\n  if (parent_ != nullptr && name_.empty()) {\n    return false;\n  }\n  /// We are out of other options this goes to missing\n  _move_to_missing(detail::Classifier::NONE, positional);\n  args.pop_back();\n  if (prefix_command_) {\n    while (!args.empty()) {\n      _move_to_missing(detail::Classifier::NONE, args.back());\n      args.pop_back();\n    }\n  }\n\n  return true;\n}\n\nCLI11_NODISCARD CLI11_INLINE App *App::_find_subcommand(\n    const std::string &subc_name, bool ignore_disabled,\n    bool ignore_used) const noexcept {\n  for (const App_p &com : subcommands_) {\n    if (com->disabled_ && ignore_disabled) continue;\n    if (com->get_name().empty()) {\n      auto *subc =\n          com->_find_subcommand(subc_name, ignore_disabled, ignore_used);\n      if (subc != nullptr) {\n        return subc;\n      }\n    }\n    if (com->check_name(subc_name)) {\n      if ((!*com) || !ignore_used) return com.get();\n    }\n  }\n  return nullptr;\n}\n\nCLI11_INLINE bool App::_parse_subcommand(std::vector<std::string> &args) {\n  if (_count_remaining_positionals(/* required */ true) > 0) {\n    _parse_positional(args, false);\n    return true;\n  }\n  auto *com = _find_subcommand(args.back(), true, true);\n  if (com != nullptr) {\n    args.pop_back();\n    if (!com->silent_) {\n      parsed_subcommands_.push_back(com);\n    }\n    com->_parse(args);\n    auto *parent_app = com->parent_;\n    while (parent_app != this) {\n      parent_app->_trigger_pre_parse(args.size());\n      if (!com->silent_) {\n        parent_app->parsed_subcommands_.push_back(com);\n      }\n      parent_app = parent_app->parent_;\n    }\n    return true;\n  }\n\n  if (parent_ == nullptr)\n    throw HorribleError(\"Subcommand \" + args.back() + \" missing\");\n  return false;\n}\n\nCLI11_INLINE bool App::_parse_arg(std::vector<std::string> &args,\n                                  detail::Classifier current_type) {\n  std::string current = args.back();\n\n  std::string arg_name;\n  std::string value;\n  std::string rest;\n\n  switch (current_type) {\n    case detail::Classifier::LONG:\n      if (!detail::split_long(current, arg_name, value))\n        throw HorribleError(\n            \"Long parsed but missing (you should not see this):\" + args.back());\n      break;\n    case detail::Classifier::SHORT:\n      if (!detail::split_short(current, arg_name, rest))\n        throw HorribleError(\n            \"Short parsed but missing! You should not see this\");\n      break;\n    case detail::Classifier::WINDOWS_STYLE:\n      if (!detail::split_windows_style(current, arg_name, value))\n        throw HorribleError(\n            \"windows option parsed but missing! You should not see this\");\n      break;\n    case detail::Classifier::SUBCOMMAND:\n    case detail::Classifier::SUBCOMMAND_TERMINATOR:\n    case detail::Classifier::POSITIONAL_MARK:\n    case detail::Classifier::NONE:\n    default:\n      throw HorribleError(\n          \"parsing got called with invalid option! You should not see this\");\n  }\n\n  auto op_ptr = std::find_if(std::begin(options_), std::end(options_),\n                             [arg_name, current_type](const Option_p &opt) {\n                               if (current_type == detail::Classifier::LONG)\n                                 return opt->check_lname(arg_name);\n                               if (current_type == detail::Classifier::SHORT)\n                                 return opt->check_sname(arg_name);\n                               // this will only get called for\n                               // detail::Classifier::WINDOWS_STYLE\n                               return opt->check_lname(arg_name) ||\n                                      opt->check_sname(arg_name);\n                             });\n\n  // Option not found\n  if (op_ptr == std::end(options_)) {\n    for (auto &subc : subcommands_) {\n      if (subc->name_.empty() && !subc->disabled_) {\n        if (subc->_parse_arg(args, current_type)) {\n          if (!subc->pre_parse_called_) {\n            subc->_trigger_pre_parse(args.size());\n          }\n          return true;\n        }\n      }\n    }\n\n    // don't capture missing if this is a nameless subcommand and nameless\n    // subcommands can't fallthrough\n    if (parent_ != nullptr && name_.empty()) {\n      return false;\n    }\n\n    // If a subcommand, try the main command\n    if (parent_ != nullptr && fallthrough_)\n      return _get_fallthrough_parent()->_parse_arg(args, current_type);\n\n    // Otherwise, add to missing\n    args.pop_back();\n    _move_to_missing(current_type, current);\n    return true;\n  }\n\n  args.pop_back();\n\n  // Get a reference to the pointer to make syntax bearable\n  Option_p &op = *op_ptr;\n  /// if we require a separator add it here\n  if (op->get_inject_separator()) {\n    if (!op->results().empty() && !op->results().back().empty()) {\n      op->add_result(std::string{});\n    }\n  }\n  if (op->get_trigger_on_parse() &&\n      op->current_option_state_ == Option::option_state::callback_run) {\n    op->clear();\n  }\n  int min_num =\n      (std::min)(op->get_type_size_min(), op->get_items_expected_min());\n  int max_num = op->get_items_expected_max();\n  // check container like options to limit the argument size to a single type if\n  // the allow_extra_flags argument is set. 16 is somewhat arbitrary (needs to\n  // be at least 4)\n  if (max_num >= detail::expected_max_vector_size / 16 &&\n      !op->get_allow_extra_args()) {\n    auto tmax = op->get_type_size_max();\n    max_num = detail::checked_multiply(tmax, op->get_expected_min())\n                  ? tmax\n                  : detail::expected_max_vector_size;\n  }\n  // Make sure we always eat the minimum for unlimited vectors\n  int collected = 0;  // total number of arguments collected\n  int result_count =\n      0;  // local variable for number of results in a single arg string\n  // deal with purely flag like things\n  if (max_num == 0) {\n    auto res = op->get_flag_value(arg_name, value);\n    op->add_result(res);\n    parse_order_.push_back(op.get());\n  } else if (!value.empty()) {  // --this=value\n    op->add_result(value, result_count);\n    parse_order_.push_back(op.get());\n    collected += result_count;\n    // -Trest\n  } else if (!rest.empty()) {\n    op->add_result(rest, result_count);\n    parse_order_.push_back(op.get());\n    rest = \"\";\n    collected += result_count;\n  }\n\n  // gather the minimum number of arguments\n  while (min_num > collected && !args.empty()) {\n    std::string current_ = args.back();\n    args.pop_back();\n    op->add_result(current_, result_count);\n    parse_order_.push_back(op.get());\n    collected += result_count;\n  }\n\n  if (min_num > collected) {  // if we have run out of arguments and the minimum\n                              // was not met\n    throw ArgumentMismatch::TypedAtLeast(op->get_name(), min_num,\n                                         op->get_type_name());\n  }\n\n  // now check for optional arguments\n  if (max_num > collected ||\n      op->get_allow_extra_args()) {  // we allow optional arguments\n    auto remreqpos = _count_remaining_positionals(true);\n    // we have met the minimum now optionally check up to the maximum\n    while ((collected < max_num || op->get_allow_extra_args()) &&\n           !args.empty() &&\n           _recognize(args.back(), false) == detail::Classifier::NONE) {\n      // If any required positionals remain, don't keep eating\n      if (remreqpos >= args.size()) {\n        break;\n      }\n      if (validate_optional_arguments_) {\n        std::string arg = args.back();\n        arg = op->_validate(arg, 0);\n        if (!arg.empty()) {\n          break;\n        }\n      }\n      op->add_result(args.back(), result_count);\n      parse_order_.push_back(op.get());\n      args.pop_back();\n      collected += result_count;\n    }\n\n    // Allow -- to end an unlimited list and \"eat\" it\n    if (!args.empty() &&\n        _recognize(args.back()) == detail::Classifier::POSITIONAL_MARK)\n      args.pop_back();\n    // optional flag that didn't receive anything now get the default value\n    if (min_num == 0 && max_num > 0 && collected == 0) {\n      auto res = op->get_flag_value(arg_name, std::string{});\n      op->add_result(res);\n      parse_order_.push_back(op.get());\n    }\n  }\n  // if we only partially completed a type then add an empty string if allowed\n  // for later processing\n  if (min_num > 0 && (collected % op->get_type_size_max()) != 0) {\n    if (op->get_type_size_max() != op->get_type_size_min()) {\n      op->add_result(std::string{});\n    } else {\n      throw ArgumentMismatch::PartialType(\n          op->get_name(), op->get_type_size_min(), op->get_type_name());\n    }\n  }\n  if (op->get_trigger_on_parse()) {\n    op->run_callback();\n  }\n  if (!rest.empty()) {\n    rest = \"-\" + rest;\n    args.push_back(rest);\n  }\n  return true;\n}\n\nCLI11_INLINE void App::_trigger_pre_parse(std::size_t remaining_args) {\n  if (!pre_parse_called_) {\n    pre_parse_called_ = true;\n    if (pre_parse_callback_) {\n      pre_parse_callback_(remaining_args);\n    }\n  } else if (immediate_callback_) {\n    if (!name_.empty()) {\n      auto pcnt = parsed_;\n      auto extras = std::move(missing_);\n      clear();\n      parsed_ = pcnt;\n      pre_parse_called_ = true;\n      missing_ = std::move(extras);\n    }\n  }\n}\n\nCLI11_INLINE App *App::_get_fallthrough_parent() {\n  if (parent_ == nullptr) {\n    throw(HorribleError(\"No Valid parent\"));\n  }\n  auto *fallthrough_parent = parent_;\n  while ((fallthrough_parent->parent_ != nullptr) &&\n         (fallthrough_parent->get_name().empty())) {\n    fallthrough_parent = fallthrough_parent->parent_;\n  }\n  return fallthrough_parent;\n}\n\nCLI11_NODISCARD CLI11_INLINE const std::string &App::_compare_subcommand_names(\n    const App &subcom, const App &base) const {\n  static const std::string estring;\n  if (subcom.disabled_) {\n    return estring;\n  }\n  for (const auto &subc : base.subcommands_) {\n    if (subc.get() != &subcom) {\n      if (subc->disabled_) {\n        continue;\n      }\n      if (!subcom.get_name().empty()) {\n        if (subc->check_name(subcom.get_name())) {\n          return subcom.get_name();\n        }\n      }\n      if (!subc->get_name().empty()) {\n        if (subcom.check_name(subc->get_name())) {\n          return subc->get_name();\n        }\n      }\n      for (const auto &les : subcom.aliases_) {\n        if (subc->check_name(les)) {\n          return les;\n        }\n      }\n      // this loop is needed in case of ignore_underscore or ignore_case on one\n      // but not the other\n      for (const auto &les : subc->aliases_) {\n        if (subcom.check_name(les)) {\n          return les;\n        }\n      }\n      // if the subcommand is an option group we need to check deeper\n      if (subc->get_name().empty()) {\n        const auto &cmpres = _compare_subcommand_names(subcom, *subc);\n        if (!cmpres.empty()) {\n          return cmpres;\n        }\n      }\n      // if the test subcommand is an option group we need to check deeper\n      if (subcom.get_name().empty()) {\n        const auto &cmpres = _compare_subcommand_names(*subc, subcom);\n        if (!cmpres.empty()) {\n          return cmpres;\n        }\n      }\n    }\n  }\n  return estring;\n}\n\nCLI11_INLINE void App::_move_to_missing(detail::Classifier val_type,\n                                        const std::string &val) {\n  if (allow_extras_ || subcommands_.empty()) {\n    missing_.emplace_back(val_type, val);\n    return;\n  }\n  // allow extra arguments to be places in an option group if it is allowed\n  // there\n  for (auto &subc : subcommands_) {\n    if (subc->name_.empty() && subc->allow_extras_) {\n      subc->missing_.emplace_back(val_type, val);\n      return;\n    }\n  }\n  // if we haven't found any place to put them yet put them in missing\n  missing_.emplace_back(val_type, val);\n}\n\nCLI11_INLINE void App::_move_option(Option *opt, App *app) {\n  if (opt == nullptr) {\n    throw OptionNotFound(\"the option is NULL\");\n  }\n  // verify that the give app is actually a subcommand\n  bool found = false;\n  for (auto &subc : subcommands_) {\n    if (app == subc.get()) {\n      found = true;\n    }\n  }\n  if (!found) {\n    throw OptionNotFound(\"The Given app is not a subcommand\");\n  }\n\n  if ((help_ptr_ == opt) || (help_all_ptr_ == opt))\n    throw OptionAlreadyAdded(\"cannot move help options\");\n\n  if (config_ptr_ == opt)\n    throw OptionAlreadyAdded(\"cannot move config file options\");\n\n  auto iterator =\n      std::find_if(std::begin(options_), std::end(options_),\n                   [opt](const Option_p &v) { return v.get() == opt; });\n  if (iterator != std::end(options_)) {\n    const auto &opt_p = *iterator;\n    if (std::find_if(std::begin(app->options_), std::end(app->options_),\n                     [&opt_p](const Option_p &v) { return (*v == *opt_p); }) ==\n        std::end(app->options_)) {\n      // only erase after the insertion was successful\n      app->options_.push_back(std::move(*iterator));\n      options_.erase(iterator);\n    } else {\n      throw OptionAlreadyAdded(\"option was not located: \" + opt->get_name());\n    }\n  } else {\n    throw OptionNotFound(\"could not locate the given Option\");\n  }\n}\n\nCLI11_INLINE void TriggerOn(App *trigger_app, App *app_to_enable) {\n  app_to_enable->enabled_by_default(false);\n  app_to_enable->disabled_by_default();\n  trigger_app->preparse_callback(\n      [app_to_enable](std::size_t) { app_to_enable->disabled(false); });\n}\n\nCLI11_INLINE void TriggerOn(App *trigger_app,\n                            std::vector<App *> apps_to_enable) {\n  for (auto &app : apps_to_enable) {\n    app->enabled_by_default(false);\n    app->disabled_by_default();\n  }\n\n  trigger_app->preparse_callback([apps_to_enable](std::size_t) {\n    for (const auto &app : apps_to_enable) {\n      app->disabled(false);\n    }\n  });\n}\n\nCLI11_INLINE void TriggerOff(App *trigger_app, App *app_to_enable) {\n  app_to_enable->disabled_by_default(false);\n  app_to_enable->enabled_by_default();\n  trigger_app->preparse_callback(\n      [app_to_enable](std::size_t) { app_to_enable->disabled(); });\n}\n\nCLI11_INLINE void TriggerOff(App *trigger_app,\n                             std::vector<App *> apps_to_enable) {\n  for (auto &app : apps_to_enable) {\n    app->disabled_by_default(false);\n    app->enabled_by_default();\n  }\n\n  trigger_app->preparse_callback([apps_to_enable](std::size_t) {\n    for (const auto &app : apps_to_enable) {\n      app->disabled();\n    }\n  });\n}\n\nCLI11_INLINE void deprecate_option(Option *opt,\n                                   const std::string &replacement) {\n  Validator deprecate_warning{[opt, replacement](std::string &) {\n                                std::cout << opt->get_name()\n                                          << \" is deprecated please use '\"\n                                          << replacement << \"' instead\\n\";\n                                return std::string();\n                              },\n                              \"DEPRECATED\"};\n  deprecate_warning.application_index(0);\n  opt->check(deprecate_warning);\n  if (!replacement.empty()) {\n    opt->description(opt->get_description() + \" DEPRECATED: please use '\" +\n                     replacement + \"' instead\");\n  }\n}\n\nCLI11_INLINE void retire_option(App *app, Option *opt) {\n  App temp;\n  auto *option_copy =\n      temp.add_option(opt->get_name(false, true))\n          ->type_size(opt->get_type_size_min(), opt->get_type_size_max())\n          ->expected(opt->get_expected_min(), opt->get_expected_max())\n          ->allow_extra_args(opt->get_allow_extra_args());\n\n  app->remove_option(opt);\n  auto *opt2 = app->add_option(option_copy->get_name(false, true),\n                               \"option has been retired and has no effect\")\n                   ->type_name(\"RETIRED\")\n                   ->default_str(\"RETIRED\")\n                   ->type_size(option_copy->get_type_size_min(),\n                               option_copy->get_type_size_max())\n                   ->expected(option_copy->get_expected_min(),\n                              option_copy->get_expected_max())\n                   ->allow_extra_args(option_copy->get_allow_extra_args());\n\n  Validator retired_warning{[opt2](std::string &) {\n                              std::cout << \"WARNING \" << opt2->get_name()\n                                        << \" is retired and has no effect\\n\";\n                              return std::string();\n                            },\n                            \"\"};\n  retired_warning.application_index(0);\n  opt2->check(retired_warning);\n}\n\nCLI11_INLINE void retire_option(App &app, Option *opt) {\n  retire_option(&app, opt);\n}\n\nCLI11_INLINE void retire_option(App *app, const std::string &option_name) {\n  auto *opt = app->get_option_no_throw(option_name);\n  if (opt != nullptr) {\n    retire_option(app, opt);\n    return;\n  }\n  auto *opt2 =\n      app->add_option(option_name, \"option has been retired and has no effect\")\n          ->type_name(\"RETIRED\")\n          ->expected(0, 1)\n          ->default_str(\"RETIRED\");\n  Validator retired_warning{[opt2](std::string &) {\n                              std::cout << \"WARNING \" << opt2->get_name()\n                                        << \" is retired and has no effect\\n\";\n                              return std::string();\n                            },\n                            \"\"};\n  retired_warning.application_index(0);\n  opt2->check(retired_warning);\n}\n\nCLI11_INLINE void retire_option(App &app, const std::string &option_name) {\n  retire_option(&app, option_name);\n}\n\nnamespace FailureMessage {\n\nCLI11_INLINE std::string simple(const App *app, const Error &e) {\n  std::string header = std::string(e.what()) + \"\\n\";\n  std::vector<std::string> names;\n\n  // Collect names\n  if (app->get_help_ptr() != nullptr)\n    names.push_back(app->get_help_ptr()->get_name());\n\n  if (app->get_help_all_ptr() != nullptr)\n    names.push_back(app->get_help_all_ptr()->get_name());\n\n  // If any names found, suggest those\n  if (!names.empty())\n    header +=\n        \"Run with \" + detail::join(names, \" or \") + \" for more information.\\n\";\n\n  return header;\n}\n\nCLI11_INLINE std::string help(const App *app, const Error &e) {\n  std::string header =\n      std::string(\"ERROR: \") + e.get_name() + \": \" + e.what() + \"\\n\";\n  header += app->help();\n  return header;\n}\n\n}  // namespace FailureMessage\n\nnamespace detail {\n\nstd::string convert_arg_for_ini(const std::string &arg, char stringQuote = '\"',\n                                char characterQuote = '\\'');\n\n/// Comma separated join, adds quotes if needed\nstd::string ini_join(const std::vector<std::string> &args, char sepChar = ',',\n                     char arrayStart = '[', char arrayEnd = ']',\n                     char stringQuote = '\"', char characterQuote = '\\'');\n\nstd::vector<std::string> generate_parents(const std::string &section,\n                                          std::string &name,\n                                          char parentSeparator);\n\n/// assuming non default segments do a check on the close and open of the\n/// segments in a configItem structure\nvoid checkParentSegments(std::vector<ConfigItem> &output,\n                         const std::string &currentSection,\n                         char parentSeparator);\n}  // namespace detail\n\nnamespace detail {\n\nCLI11_INLINE std::string convert_arg_for_ini(const std::string &arg,\n                                             char stringQuote,\n                                             char characterQuote) {\n  if (arg.empty()) {\n    return std::string(2, stringQuote);\n  }\n  // some specifically supported strings\n  if (arg == \"true\" || arg == \"false\" || arg == \"nan\" || arg == \"inf\") {\n    return arg;\n  }\n  // floating point conversion can convert some hex codes, but don't try that\n  // here\n  if (arg.compare(0, 2, \"0x\") != 0 && arg.compare(0, 2, \"0X\") != 0) {\n    using CLI::detail::lexical_cast;\n    double val = 0.0;\n    if (lexical_cast(arg, val)) {\n      return arg;\n    }\n  }\n  // just quote a single non numeric character\n  if (arg.size() == 1) {\n    return std::string(1, characterQuote) + arg + characterQuote;\n  }\n  // handle hex, binary or octal arguments\n  if (arg.front() == '0') {\n    if (arg[1] == 'x') {\n      if (std::all_of(arg.begin() + 2, arg.end(), [](char x) {\n            return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') ||\n                   (x >= 'a' && x <= 'f');\n          })) {\n        return arg;\n      }\n    } else if (arg[1] == 'o') {\n      if (std::all_of(arg.begin() + 2, arg.end(),\n                      [](char x) { return (x >= '0' && x <= '7'); })) {\n        return arg;\n      }\n    } else if (arg[1] == 'b') {\n      if (std::all_of(arg.begin() + 2, arg.end(),\n                      [](char x) { return (x == '0' || x == '1'); })) {\n        return arg;\n      }\n    }\n  }\n  if (arg.find_first_of(stringQuote) == std::string::npos) {\n    return std::string(1, stringQuote) + arg + stringQuote;\n  }\n  return characterQuote + arg + characterQuote;\n}\n\nCLI11_INLINE std::string ini_join(const std::vector<std::string> &args,\n                                  char sepChar, char arrayStart, char arrayEnd,\n                                  char stringQuote, char characterQuote) {\n  std::string joined;\n  if (args.size() > 1 && arrayStart != '\\0') {\n    joined.push_back(arrayStart);\n  }\n  std::size_t start = 0;\n  for (const auto &arg : args) {\n    if (start++ > 0) {\n      joined.push_back(sepChar);\n      if (!std::isspace<char>(sepChar, std::locale())) {\n        joined.push_back(' ');\n      }\n    }\n    joined.append(convert_arg_for_ini(arg, stringQuote, characterQuote));\n  }\n  if (args.size() > 1 && arrayEnd != '\\0') {\n    joined.push_back(arrayEnd);\n  }\n  return joined;\n}\n\nCLI11_INLINE std::vector<std::string> generate_parents(\n    const std::string &section, std::string &name, char parentSeparator) {\n  std::vector<std::string> parents;\n  if (detail::to_lower(section) != \"default\") {\n    if (section.find(parentSeparator) != std::string::npos) {\n      parents = detail::split(section, parentSeparator);\n    } else {\n      parents = {section};\n    }\n  }\n  if (name.find(parentSeparator) != std::string::npos) {\n    std::vector<std::string> plist = detail::split(name, parentSeparator);\n    name = plist.back();\n    detail::remove_quotes(name);\n    plist.pop_back();\n    parents.insert(parents.end(), plist.begin(), plist.end());\n  }\n\n  // clean up quotes on the parents\n  for (auto &parent : parents) {\n    detail::remove_quotes(parent);\n  }\n  return parents;\n}\n\nCLI11_INLINE void checkParentSegments(std::vector<ConfigItem> &output,\n                                      const std::string &currentSection,\n                                      char parentSeparator) {\n  std::string estring;\n  auto parents =\n      detail::generate_parents(currentSection, estring, parentSeparator);\n  if (!output.empty() && output.back().name == \"--\") {\n    std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;\n    while (output.back().parents.size() >= msize) {\n      output.push_back(output.back());\n      output.back().parents.pop_back();\n    }\n\n    if (parents.size() > 1) {\n      std::size_t common = 0;\n      std::size_t mpair =\n          (std::min)(output.back().parents.size(), parents.size() - 1);\n      for (std::size_t ii = 0; ii < mpair; ++ii) {\n        if (output.back().parents[ii] != parents[ii]) {\n          break;\n        }\n        ++common;\n      }\n      if (common == mpair) {\n        output.pop_back();\n      } else {\n        while (output.back().parents.size() > common + 1) {\n          output.push_back(output.back());\n          output.back().parents.pop_back();\n        }\n      }\n      for (std::size_t ii = common; ii < parents.size() - 1; ++ii) {\n        output.emplace_back();\n        output.back().parents.assign(\n            parents.begin(),\n            parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);\n        output.back().name = \"++\";\n      }\n    }\n  } else if (parents.size() > 1) {\n    for (std::size_t ii = 0; ii < parents.size() - 1; ++ii) {\n      output.emplace_back();\n      output.back().parents.assign(\n          parents.begin(),\n          parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);\n      output.back().name = \"++\";\n    }\n  }\n\n  // insert a section end which is just an empty items_buffer\n  output.emplace_back();\n  output.back().parents = std::move(parents);\n  output.back().name = \"++\";\n}\n}  // namespace detail\n\ninline std::vector<ConfigItem> ConfigBase::from_config(\n    std::istream &input) const {\n  std::string line;\n  std::string currentSection = \"default\";\n  std::string previousSection = \"default\";\n  std::vector<ConfigItem> output;\n  bool isDefaultArray =\n      (arrayStart == '[' && arrayEnd == ']' && arraySeparator == ',');\n  bool isINIArray =\n      (arrayStart == '\\0' || arrayStart == ' ') && arrayStart == arrayEnd;\n  bool inSection{false};\n  char aStart = (isINIArray) ? '[' : arrayStart;\n  char aEnd = (isINIArray) ? ']' : arrayEnd;\n  char aSep = (isINIArray && arraySeparator == ' ') ? ',' : arraySeparator;\n  int currentSectionIndex{0};\n  while (getline(input, line)) {\n    std::vector<std::string> items_buffer;\n    std::string name;\n\n    detail::trim(line);\n    std::size_t len = line.length();\n    // lines have to be at least 3 characters to have any meaning to CLI just\n    // skip the rest\n    if (len < 3) {\n      continue;\n    }\n    if (line.front() == '[' && line.back() == ']') {\n      if (currentSection != \"default\") {\n        // insert a section end which is just an empty items_buffer\n        output.emplace_back();\n        output.back().parents =\n            detail::generate_parents(currentSection, name, parentSeparatorChar);\n        output.back().name = \"--\";\n      }\n      currentSection = line.substr(1, len - 2);\n      // deal with double brackets for TOML\n      if (currentSection.size() > 1 && currentSection.front() == '[' &&\n          currentSection.back() == ']') {\n        currentSection = currentSection.substr(1, currentSection.size() - 2);\n      }\n      if (detail::to_lower(currentSection) == \"default\") {\n        currentSection = \"default\";\n      } else {\n        detail::checkParentSegments(output, currentSection,\n                                    parentSeparatorChar);\n      }\n      inSection = false;\n      if (currentSection == previousSection) {\n        ++currentSectionIndex;\n      } else {\n        currentSectionIndex = 0;\n        previousSection = currentSection;\n      }\n      continue;\n    }\n\n    // comment lines\n    if (line.front() == ';' || line.front() == '#' ||\n        line.front() == commentChar) {\n      continue;\n    }\n\n    // Find = in string, split and recombine\n    auto pos = line.find(valueDelimiter);\n    if (pos != std::string::npos) {\n      name = detail::trim_copy(line.substr(0, pos));\n      std::string item = detail::trim_copy(line.substr(pos + 1));\n      auto cloc = item.find(commentChar);\n      if (cloc != std::string::npos) {\n        item.erase(\n            cloc,\n            std::string::npos);  // NOLINT(readability-suspicious-call-argument)\n        detail::trim(item);\n      }\n      if (item.size() > 1 && item.front() == aStart) {\n        for (std::string multiline;\n             item.back() != aEnd && std::getline(input, multiline);) {\n          detail::trim(multiline);\n          item += multiline;\n        }\n        items_buffer =\n            detail::split_up(item.substr(1, item.length() - 2), aSep);\n      } else if ((isDefaultArray || isINIArray) &&\n                 item.find_first_of(aSep) != std::string::npos) {\n        items_buffer = detail::split_up(item, aSep);\n      } else if ((isDefaultArray || isINIArray) &&\n                 item.find_first_of(' ') != std::string::npos) {\n        items_buffer = detail::split_up(item);\n      } else {\n        items_buffer = {item};\n      }\n    } else {\n      name = detail::trim_copy(line);\n      auto cloc = name.find(commentChar);\n      if (cloc != std::string::npos) {\n        name.erase(\n            cloc,\n            std::string::npos);  // NOLINT(readability-suspicious-call-argument)\n        detail::trim(name);\n      }\n\n      items_buffer = {\"true\"};\n    }\n    if (name.find(parentSeparatorChar) == std::string::npos) {\n      detail::remove_quotes(name);\n    }\n    // clean up quotes on the items\n    for (auto &it : items_buffer) {\n      detail::remove_quotes(it);\n    }\n\n    std::vector<std::string> parents =\n        detail::generate_parents(currentSection, name, parentSeparatorChar);\n    if (parents.size() > maximumLayers) {\n      continue;\n    }\n    if (!configSection.empty() && !inSection) {\n      if (parents.empty() || parents.front() != configSection) {\n        continue;\n      }\n      if (configIndex >= 0 && currentSectionIndex != configIndex) {\n        continue;\n      }\n      parents.erase(parents.begin());\n      inSection = true;\n    }\n    if (!output.empty() && name == output.back().name &&\n        parents == output.back().parents) {\n      output.back().inputs.insert(output.back().inputs.end(),\n                                  items_buffer.begin(), items_buffer.end());\n    } else {\n      output.emplace_back();\n      output.back().parents = std::move(parents);\n      output.back().name = std::move(name);\n      output.back().inputs = std::move(items_buffer);\n    }\n  }\n  if (currentSection != \"default\") {\n    // insert a section end which is just an empty items_buffer\n    std::string ename;\n    output.emplace_back();\n    output.back().parents =\n        detail::generate_parents(currentSection, ename, parentSeparatorChar);\n    output.back().name = \"--\";\n    while (output.back().parents.size() > 1) {\n      output.push_back(output.back());\n      output.back().parents.pop_back();\n    }\n  }\n  return output;\n}\n\nCLI11_INLINE std::string ConfigBase::to_config(const App *app,\n                                               bool default_also,\n                                               bool write_description,\n                                               std::string prefix) const {\n  std::stringstream out;\n  std::string commentLead;\n  commentLead.push_back(commentChar);\n  commentLead.push_back(' ');\n\n  std::vector<std::string> groups = app->get_groups();\n  bool defaultUsed = false;\n  groups.insert(groups.begin(), std::string(\"Options\"));\n  if (write_description &&\n      (app->get_configurable() || app->get_parent() == nullptr ||\n       app->get_name().empty())) {\n    out << commentLead\n        << detail::fix_newlines(commentLead, app->get_description()) << '\\n';\n  }\n  for (auto &group : groups) {\n    if (group == \"Options\" || group.empty()) {\n      if (defaultUsed) {\n        continue;\n      }\n      defaultUsed = true;\n    }\n    if (write_description && group != \"Options\" && !group.empty()) {\n      out << '\\n' << commentLead << group << \" Options\\n\";\n    }\n    for (const Option *opt : app->get_options({})) {\n      // Only process options that are configurable\n      if (opt->get_configurable()) {\n        if (opt->get_group() != group) {\n          if (!(group == \"Options\" && opt->get_group().empty())) {\n            continue;\n          }\n        }\n        std::string name = prefix + opt->get_single_name();\n        std::string value =\n            detail::ini_join(opt->reduced_results(), arraySeparator, arrayStart,\n                             arrayEnd, stringQuote, characterQuote);\n\n        if (value.empty() && default_also) {\n          if (!opt->get_default_str().empty()) {\n            value = detail::convert_arg_for_ini(opt->get_default_str(),\n                                                stringQuote, characterQuote);\n          } else if (opt->get_expected_min() == 0) {\n            value = \"false\";\n          } else if (opt->get_run_callback_for_default()) {\n            value = \"\\\"\\\"\";  // empty string default value\n          }\n        }\n\n        if (!value.empty()) {\n          if (!opt->get_fnames().empty()) {\n            value = opt->get_flag_value(name, value);\n          }\n          if (write_description && opt->has_description()) {\n            out << '\\n';\n            out << commentLead\n                << detail::fix_newlines(commentLead, opt->get_description())\n                << '\\n';\n          }\n          out << name << valueDelimiter << value << '\\n';\n        }\n      }\n    }\n  }\n  auto subcommands = app->get_subcommands({});\n  for (const App *subcom : subcommands) {\n    if (subcom->get_name().empty()) {\n      if (write_description && !subcom->get_group().empty()) {\n        out << '\\n' << commentLead << subcom->get_group() << \" Options\\n\";\n      }\n      out << to_config(subcom, default_also, write_description, prefix);\n    }\n  }\n\n  for (const App *subcom : subcommands) {\n    if (!subcom->get_name().empty()) {\n      if (subcom->get_configurable() && app->got_subcommand(subcom)) {\n        if (!prefix.empty() || app->get_parent() == nullptr) {\n          out << '[' << prefix << subcom->get_name() << \"]\\n\";\n        } else {\n          std::string subname =\n              app->get_name() + parentSeparatorChar + subcom->get_name();\n          const auto *p = app->get_parent();\n          while (p->get_parent() != nullptr) {\n            subname = p->get_name() + parentSeparatorChar + subname;\n            p = p->get_parent();\n          }\n          out << '[' << subname << \"]\\n\";\n        }\n        out << to_config(subcom, default_also, write_description, \"\");\n      } else {\n        out << to_config(subcom, default_also, write_description,\n                         prefix + subcom->get_name() + parentSeparatorChar);\n      }\n    }\n  }\n\n  return out.str();\n}\n\nCLI11_INLINE std::string Formatter::make_group(\n    std::string group, bool is_positional,\n    std::vector<const Option *> opts) const {\n  std::stringstream out;\n\n  out << \"\\n\" << group << \":\\n\";\n  for (const Option *opt : opts) {\n    out << make_option(opt, is_positional);\n  }\n\n  return out.str();\n}\n\nCLI11_INLINE std::string Formatter::make_positionals(const App *app) const {\n  std::vector<const Option *> opts = app->get_options([](const Option *opt) {\n    return !opt->get_group().empty() && opt->get_positional();\n  });\n\n  if (opts.empty()) return {};\n\n  return make_group(get_label(\"Positionals\"), true, opts);\n}\n\nCLI11_INLINE std::string Formatter::make_groups(const App *app,\n                                                AppFormatMode mode) const {\n  std::stringstream out;\n  std::vector<std::string> groups = app->get_groups();\n\n  // Options\n  for (const std::string &group : groups) {\n    std::vector<const Option *> opts =\n        app->get_options([app, mode, &group](const Option *opt) {\n          return opt->get_group() == group       // Must be in the right group\n                 && opt->nonpositional()         // Must not be a positional\n                 && (mode != AppFormatMode::Sub  // If mode is Sub, then\n                     || (app->get_help_ptr() != opt  // Ignore help pointer\n                         && app->get_help_all_ptr() !=\n                                opt));  // Ignore help all pointer\n        });\n    if (!group.empty() && !opts.empty()) {\n      out << make_group(group, false, opts);\n\n      if (group != groups.back()) out << \"\\n\";\n    }\n  }\n\n  return out.str();\n}\n\nCLI11_INLINE std::string Formatter::make_description(const App *app) const {\n  std::string desc = app->get_description();\n  auto min_options = app->get_require_option_min();\n  auto max_options = app->get_require_option_max();\n  if (app->get_required()) {\n    desc += \" REQUIRED \";\n  }\n  if ((max_options == min_options) && (min_options > 0)) {\n    if (min_options == 1) {\n      desc += \" \\n[Exactly 1 of the following options is required]\";\n    } else {\n      desc += \" \\n[Exactly \" + std::to_string(min_options) +\n              \" options from the following list are required]\";\n    }\n  } else if (max_options > 0) {\n    if (min_options > 0) {\n      desc += \" \\n[Between \" + std::to_string(min_options) + \" and \" +\n              std::to_string(max_options) +\n              \" of the follow options are required]\";\n    } else {\n      desc += \" \\n[At most \" + std::to_string(max_options) +\n              \" of the following options are allowed]\";\n    }\n  } else if (min_options > 0) {\n    desc += \" \\n[At least \" + std::to_string(min_options) +\n            \" of the following options are required]\";\n  }\n  return (!desc.empty()) ? desc + \"\\n\" : std::string{};\n}\n\nCLI11_INLINE std::string Formatter::make_usage(const App *app,\n                                               std::string name) const {\n  std::stringstream out;\n\n  out << get_label(\"Usage\") << \":\" << (name.empty() ? \"\" : \" \") << name;\n\n  std::vector<std::string> groups = app->get_groups();\n\n  // Print an Options badge if any options exist\n  std::vector<const Option *> non_pos_options =\n      app->get_options([](const Option *opt) { return opt->nonpositional(); });\n  if (!non_pos_options.empty()) out << \" [\" << get_label(\"OPTIONS\") << \"]\";\n\n  // Positionals need to be listed here\n  std::vector<const Option *> positionals =\n      app->get_options([](const Option *opt) { return opt->get_positional(); });\n\n  // Print out positionals if any are left\n  if (!positionals.empty()) {\n    // Convert to help names\n    std::vector<std::string> positional_names(positionals.size());\n    std::transform(\n        positionals.begin(), positionals.end(), positional_names.begin(),\n        [this](const Option *opt) { return make_option_usage(opt); });\n\n    out << \" \" << detail::join(positional_names, \" \");\n  }\n\n  // Add a marker if subcommands are expected or optional\n  if (!app->get_subcommands([](const CLI::App *subc) {\n            return ((!subc->get_disabled()) && (!subc->get_name().empty()));\n          })\n           .empty()) {\n    out << \" \" << (app->get_require_subcommand_min() == 0 ? \"[\" : \"\")\n        << get_label(app->get_require_subcommand_max() < 2 ||\n                             app->get_require_subcommand_min() > 1\n                         ? \"SUBCOMMAND\"\n                         : \"SUBCOMMANDS\")\n        << (app->get_require_subcommand_min() == 0 ? \"]\" : \"\");\n  }\n\n  out << std::endl;\n\n  return out.str();\n}\n\nCLI11_INLINE std::string Formatter::make_footer(const App *app) const {\n  std::string footer = app->get_footer();\n  if (footer.empty()) {\n    return std::string{};\n  }\n  return \"\\n\" + footer + \"\\n\";\n}\n\nCLI11_INLINE std::string Formatter::make_help(const App *app, std::string name,\n                                              AppFormatMode mode) const {\n  // This immediately forwards to the make_expanded method. This is done this\n  // way so that subcommands can have overridden formatters\n  if (mode == AppFormatMode::Sub) return make_expanded(app);\n\n  std::stringstream out;\n  if ((app->get_name().empty()) && (app->get_parent() != nullptr)) {\n    if (app->get_group() != \"Subcommands\") {\n      out << app->get_group() << ':';\n    }\n  }\n\n  out << make_description(app);\n  out << make_usage(app, name);\n  out << make_positionals(app);\n  out << make_groups(app, mode);\n  out << make_subcommands(app, mode);\n  out << make_footer(app);\n\n  return out.str();\n}\n\nCLI11_INLINE std::string Formatter::make_subcommands(const App *app,\n                                                     AppFormatMode mode) const {\n  std::stringstream out;\n\n  std::vector<const App *> subcommands = app->get_subcommands({});\n\n  // Make a list in definition order of the groups seen\n  std::vector<std::string> subcmd_groups_seen;\n  for (const App *com : subcommands) {\n    if (com->get_name().empty()) {\n      if (!com->get_group().empty()) {\n        out << make_expanded(com);\n      }\n      continue;\n    }\n    std::string group_key = com->get_group();\n    if (!group_key.empty() &&\n        std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(),\n                     [&group_key](std::string a) {\n                       return detail::to_lower(a) ==\n                              detail::to_lower(group_key);\n                     }) == subcmd_groups_seen.end())\n      subcmd_groups_seen.push_back(group_key);\n  }\n\n  // For each group, filter out and print subcommands\n  for (const std::string &group : subcmd_groups_seen) {\n    out << \"\\n\" << group << \":\\n\";\n    std::vector<const App *> subcommands_group =\n        app->get_subcommands([&group](const App *sub_app) {\n          return detail::to_lower(sub_app->get_group()) ==\n                 detail::to_lower(group);\n        });\n    for (const App *new_com : subcommands_group) {\n      if (new_com->get_name().empty()) continue;\n      if (mode != AppFormatMode::All) {\n        out << make_subcommand(new_com);\n      } else {\n        out << new_com->help(new_com->get_name(), AppFormatMode::Sub);\n        out << \"\\n\";\n      }\n    }\n  }\n\n  return out.str();\n}\n\nCLI11_INLINE std::string Formatter::make_subcommand(const App *sub) const {\n  std::stringstream out;\n  detail::format_help(out, sub->get_display_name(true), sub->get_description(),\n                      column_width_);\n  return out.str();\n}\n\nCLI11_INLINE std::string Formatter::make_expanded(const App *sub) const {\n  std::stringstream out;\n  out << sub->get_display_name(true) << \"\\n\";\n\n  out << make_description(sub);\n  if (sub->get_name().empty() && !sub->get_aliases().empty()) {\n    detail::format_aliases(out, sub->get_aliases(), column_width_ + 2);\n  }\n  out << make_positionals(sub);\n  out << make_groups(sub, AppFormatMode::Sub);\n  out << make_subcommands(sub, AppFormatMode::Sub);\n\n  // Drop blank spaces\n  std::string tmp = detail::find_and_replace(out.str(), \"\\n\\n\", \"\\n\");\n  tmp = tmp.substr(0, tmp.size() - 1);  // Remove the final '\\n'\n\n  // Indent all but the first line (the name)\n  return detail::find_and_replace(tmp, \"\\n\", \"\\n  \") + \"\\n\";\n}\n\nCLI11_INLINE std::string Formatter::make_option_name(const Option *opt,\n                                                     bool is_positional) const {\n  if (is_positional) return opt->get_name(true, false);\n\n  return opt->get_name(false, true);\n}\n\nCLI11_INLINE std::string Formatter::make_option_opts(const Option *opt) const {\n  std::stringstream out;\n\n  if (!opt->get_option_text().empty()) {\n    out << \" \" << opt->get_option_text();\n  } else {\n    if (opt->get_type_size() != 0) {\n      if (!opt->get_type_name().empty())\n        out << \" \" << get_label(opt->get_type_name());\n      if (!opt->get_default_str().empty())\n        out << \" [\" << opt->get_default_str() << \"] \";\n      if (opt->get_expected_max() == detail::expected_max_vector_size)\n        out << \" ...\";\n      else if (opt->get_expected_min() > 1)\n        out << \" x \" << opt->get_expected();\n\n      if (opt->get_required()) out << \" \" << get_label(\"REQUIRED\");\n    }\n    if (!opt->get_envname().empty())\n      out << \" (\" << get_label(\"Env\") << \":\" << opt->get_envname() << \")\";\n    if (!opt->get_needs().empty()) {\n      out << \" \" << get_label(\"Needs\") << \":\";\n      for (const Option *op : opt->get_needs()) out << \" \" << op->get_name();\n    }\n    if (!opt->get_excludes().empty()) {\n      out << \" \" << get_label(\"Excludes\") << \":\";\n      for (const Option *op : opt->get_excludes()) out << \" \" << op->get_name();\n    }\n  }\n  return out.str();\n}\n\nCLI11_INLINE std::string Formatter::make_option_desc(const Option *opt) const {\n  return opt->get_description();\n}\n\nCLI11_INLINE std::string Formatter::make_option_usage(const Option *opt) const {\n  // Note that these are positionals usages\n  std::stringstream out;\n  out << make_option_name(opt, true);\n  if (opt->get_expected_max() >= detail::expected_max_vector_size)\n    out << \"...\";\n  else if (opt->get_expected_max() > 1)\n    out << \"(\" << opt->get_expected() << \"x)\";\n\n  return opt->get_required() ? out.str() : \"[\" + out.str() + \"]\";\n}\n\n}  // namespace CLI\n"
  },
  {
    "path": "third_party/cli11/current_version",
    "content": "https://github.com/CLIUtils/CLI11/releases/tag/v2.3.2\n\n"
  },
  {
    "path": "third_party/cuCollections.patch",
    "content": "diff --git a/.gitignore b/.gitignore\nindex 4146530..8edb720 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -81,9 +81,6 @@ cpp/thirdparty/googletest/\n /html\n /latex\n \n-#Java\n-target\n-\n # Translations\n *.mo\n *.pot\ndiff --git a/include/cuco/detail/__config b/include/cuco/detail/__config\nindex c76a1bb..248dff1 100644\n--- a/include/cuco/detail/__config\n+++ b/include/cuco/detail/__config\n@@ -16,7 +16,7 @@\n \n  #pragma once\n \n- #include <nv/target>\n+ #include <cuco/detail/nv/target>\n \n // WAR for libcudacxx/296\n #define CUCO_CUDA_MINIMUM_ARCH _NV_FIRST_ARG(__CUDA_ARCH_LIST__)\ndiff --git a/include/cuco/detail/nv/detail/__preprocessor b/include/cuco/detail/nv/detail/__preprocessor\nnew file mode 100644\nindex 0000000..3485565\n--- /dev/null\n+++ b/include/cuco/detail/nv/detail/__preprocessor\n@@ -0,0 +1,117 @@\n+//===----------------------------------------------------------------------===//\n+//\n+// Part of libcu++, the C++ Standard Library for your entire system,\n+// under the Apache License v2.0 with LLVM Exceptions.\n+// See https://llvm.org/LICENSE.txt for license information.\n+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n+//\n+//===----------------------------------------------------------------------===//\n+\n+#if defined(__GNUC__)\n+#pragma GCC system_header\n+#endif\n+\n+// For all compilers and dialects this header defines:\n+//  _NV_EVAL\n+//  _NV_IF\n+//  _NV_CONCAT_EVAL\n+// For C++11 and up it defines:\n+//  _NV_STRIP_PAREN\n+//  _NV_DISPATCH_N_ARY\n+//  _NV_FIRST_ARG\n+//  _NV_REMOVE_PAREN\n+\n+#if defined(_NV_TARGET_CPP11)\n+#  define _NV_EVAL1(...) __VA_ARGS__\n+#  define _NV_EVAL(...) _NV_EVAL1(__VA_ARGS__)\n+#else\n+#  define _NV_EVAL1(x) x\n+#  define _NV_EVAL(x) _NV_EVAL1(x)\n+#endif // C++11\n+\n+#define _NV_CONCAT_EVAL1(l, r) _NV_EVAL(l ## r)\n+#define _NV_CONCAT_EVAL(l, r) _NV_CONCAT_EVAL1(l, r)\n+\n+#define _NV_IF_0(t, f) f\n+#define _NV_IF_1(t, f) t\n+\n+#define _NV_IF_BIT(b) _NV_EVAL(_NV_IF_##b)\n+#define _NV_IF__EVAL(fn, t, f) _NV_EVAL(fn(t, f))\n+#define _NV_IF_EVAL(cond, t, f) _NV_IF__EVAL(_NV_IF_BIT(cond), t, f)\n+\n+#define _NV_IF1(cond, t, f) _NV_IF_EVAL(cond, t, f)\n+#define _NV_IF(cond, t, f) _NV_IF1(_NV_EVAL(cond), _NV_EVAL(t), _NV_EVAL(f))\n+\n+#if defined(_NV_TARGET_CPP11)\n+\n+// The below mechanisms were derived from: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/\n+\n+#define _NV_ARG32(...) _NV_EVAL(_NV_ARG32_0(__VA_ARGS__))\n+#define _NV_ARG32_0(                                                                         \\\n+    _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15,                    \\\n+    _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, ...) _31\n+\n+#define _NV_HAS_COMMA(...) _NV_ARG32(__VA_ARGS__,   \\\n+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \\\n+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)\n+\n+#define _NV_TRIGGER_PARENTHESIS_(...) ,\n+\n+#define _NV_ISEMPTY(...)                                                    \\\n+    _NV_ISEMPTY0(                                                           \\\n+          /* test if there is just one argument, eventually an empty        \\\n+             one */                                                         \\\n+          _NV_EVAL(_NV_HAS_COMMA(__VA_ARGS__)),                                       \\\n+          /* test if _TRIGGER_PARENTHESIS_ together with the argument       \\\n+             adds a comma */                                                \\\n+          _NV_EVAL(_NV_HAS_COMMA(_NV_TRIGGER_PARENTHESIS_ __VA_ARGS__)),              \\\n+          /* test if the argument together with a parenthesis               \\\n+             adds a comma */                                                \\\n+          _NV_EVAL(_NV_HAS_COMMA(__VA_ARGS__ (/*empty*/))),                           \\\n+          /* test if placing it between _TRIGGER_PARENTHESIS_ and the       \\\n+             parenthesis adds a comma */                                    \\\n+          _NV_EVAL(_NV_HAS_COMMA(_NV_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)))   \\\n+          )\n+\n+#define _NV_PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4\n+#define _NV_ISEMPTY0(_0, _1, _2, _3) _NV_HAS_COMMA(_NV_PASTE5(_NV_IS_EMPTY_CASE_, _0, _1, _2, _3))\n+#define _NV_IS_EMPTY_CASE_0001 ,\n+\n+\n+#define _NV_REMOVE_PAREN(...) _NV_REMOVE_PAREN1(__VA_ARGS__)\n+#define _NV_REMOVE_PAREN1(...) _NV_STRIP_PAREN(_NV_IF(_NV_TEST_PAREN(__VA_ARGS__), (_NV_STRIP_PAREN(__VA_ARGS__)), (__VA_ARGS__)))\n+\n+#define _NV_STRIP_PAREN2(...) __VA_ARGS__\n+#define _NV_STRIP_PAREN1(...) _NV_STRIP_PAREN2 __VA_ARGS__\n+#define _NV_STRIP_PAREN(...) _NV_STRIP_PAREN1(__VA_ARGS__)\n+\n+#define _NV_TEST_PAREN(...) _NV_TEST_PAREN1(__VA_ARGS__)\n+#define _NV_TEST_PAREN1(...) _NV_TEST_PAREN2(_NV_TEST_PAREN_DUMMY __VA_ARGS__)\n+#define _NV_TEST_PAREN2(...) _NV_TEST_PAREN3(_NV_CONCAT_EVAL(_, __VA_ARGS__))\n+#define _NV_TEST_PAREN3(...) _NV_EVAL(_NV_FIRST_ARG(__VA_ARGS__))\n+\n+#define __NV_PAREN_YES 1\n+#define __NV_PAREN_NO 0\n+\n+#define _NV_TEST_PAREN_DUMMY(...) _NV_PAREN_YES\n+#define __NV_TEST_PAREN_DUMMY     __NV_PAREN_NO,\n+\n+#define _NV_FIRST_ARG1(x, ...) x\n+#define _NV_FIRST_ARG(x, ...) _NV_FIRST_ARG1(x)\n+\n+#define _NV_REMOVE_FIRST_ARGS1(...) __VA_ARGS__\n+#define _NV_REMOVE_FIRST_ARGS(x, ...) _NV_REMOVE_FIRST_ARGS1(__VA_ARGS__)\n+\n+#define _NV_NUM_ARGS(...) _NV_NUM_ARGS0(__VA_ARGS__)\n+#define _NV_NUM_ARGS0(...) _NV_EVAL(_NV_NUM_ARGS1(__VA_ARGS__))\n+#define _NV_NUM_ARGS1(...) _NV_IF(_NV_ISEMPTY(__VA_ARGS__), 0, _NV_NUM_ARGS2(__VA_ARGS__))\n+#define _NV_NUM_ARGS2(...) _NV_ARG32(__VA_ARGS__,   \\\n+    31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16, \\\n+    15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)\n+\n+#define _NV_DISPATCH_N_IMPL1(name, ...) _NV_EVAL(name(__VA_ARGS__))\n+#define _NV_DISPATCH_N_IMPL0(depth, name, ...) _NV_DISPATCH_N_IMPL1(_NV_CONCAT_EVAL(name, depth), __VA_ARGS__)\n+#define _NV_DISPATCH_N_IMPL(name, ...) _NV_DISPATCH_N_IMPL0(_NV_NUM_ARGS(__VA_ARGS__), name, __VA_ARGS__)\n+#define _NV_DISPATCH_N_ARY(name, ...) _NV_DISPATCH_N_IMPL(name, __VA_ARGS__)\n+\n+#endif // C++11\n\\ No newline at end of file\ndiff --git a/include/cuco/detail/nv/detail/__target_macros b/include/cuco/detail/nv/detail/__target_macros\nnew file mode 100644\nindex 0000000..d67d923\n--- /dev/null\n+++ b/include/cuco/detail/nv/detail/__target_macros\n@@ -0,0 +1,472 @@\n+//===----------------------------------------------------------------------===//\n+//\n+// Part of libcu++, the C++ Standard Library for your entire system,\n+// under the Apache License v2.0 with LLVM Exceptions.\n+// See https://llvm.org/LICENSE.txt for license information.\n+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n+//\n+//===----------------------------------------------------------------------===//\n+\n+#ifndef _NV__TARGET_MACROS\n+#define _NV__TARGET_MACROS\n+\n+#include \"__preprocessor\"\n+\n+#if defined(__GNUC__)\n+#pragma GCC system_header\n+#endif\n+\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_350 nv::target::sm_35\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_370 nv::target::sm_37\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_500 nv::target::sm_50\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_520 nv::target::sm_52\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_530 nv::target::sm_53\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_600 nv::target::sm_60\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_610 nv::target::sm_61\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_620 nv::target::sm_62\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_700 nv::target::sm_70\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_720 nv::target::sm_72\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_750 nv::target::sm_75\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_800 nv::target::sm_80\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_860 nv::target::sm_86\n+#  define _NV_TARGET_ARCH_TO_SELECTOR_870 nv::target::sm_87\n+\n+#  define _NV_TARGET_ARCH_TO_SM_350 35\n+#  define _NV_TARGET_ARCH_TO_SM_370 37\n+#  define _NV_TARGET_ARCH_TO_SM_500 50\n+#  define _NV_TARGET_ARCH_TO_SM_520 52\n+#  define _NV_TARGET_ARCH_TO_SM_530 53\n+#  define _NV_TARGET_ARCH_TO_SM_600 60\n+#  define _NV_TARGET_ARCH_TO_SM_610 61\n+#  define _NV_TARGET_ARCH_TO_SM_620 62\n+#  define _NV_TARGET_ARCH_TO_SM_700 70\n+#  define _NV_TARGET_ARCH_TO_SM_720 72\n+#  define _NV_TARGET_ARCH_TO_SM_750 75\n+#  define _NV_TARGET_ARCH_TO_SM_800 80\n+#  define _NV_TARGET_ARCH_TO_SM_860 86\n+#  define _NV_TARGET_ARCH_TO_SM_870 87\n+\n+// Only enable when compiling for CUDA/stdpar\n+#if defined(_NV_COMPILER_NVCXX) && defined(_NVHPC_CUDA)\n+\n+#  define _NV_TARGET_VAL_SM_35 nv::target::sm_35\n+#  define _NV_TARGET_VAL_SM_37 nv::target::sm_37\n+#  define _NV_TARGET_VAL_SM_50 nv::target::sm_50\n+#  define _NV_TARGET_VAL_SM_52 nv::target::sm_52\n+#  define _NV_TARGET_VAL_SM_53 nv::target::sm_53\n+#  define _NV_TARGET_VAL_SM_60 nv::target::sm_60\n+#  define _NV_TARGET_VAL_SM_61 nv::target::sm_61\n+#  define _NV_TARGET_VAL_SM_62 nv::target::sm_62\n+#  define _NV_TARGET_VAL_SM_70 nv::target::sm_70\n+#  define _NV_TARGET_VAL_SM_72 nv::target::sm_72\n+#  define _NV_TARGET_VAL_SM_75 nv::target::sm_75\n+#  define _NV_TARGET_VAL_SM_80 nv::target::sm_80\n+#  define _NV_TARGET_VAL_SM_86 nv::target::sm_86\n+#  define _NV_TARGET_VAL_SM_87 nv::target::sm_87\n+\n+#  define _NV_TARGET___NV_IS_HOST nv::target::is_host\n+#  define _NV_TARGET___NV_IS_DEVICE nv::target::is_device\n+\n+#  define _NV_TARGET___NV_ANY_TARGET (nv::target::any_target)\n+#  define _NV_TARGET___NV_NO_TARGET (nv::target::no_target)\n+\n+#  if defined(NV_TARGET_SM_INTEGER_LIST)\n+#    define NV_TARGET_MINIMUM_SM_SELECTOR _NV_FIRST_ARG(NV_TARGET_SM_SELECTOR_LIST)\n+#    define NV_TARGET_MINIMUM_SM_INTEGER _NV_FIRST_ARG(NV_TARGET_SM_INTEGER_LIST)\n+#    define __CUDA_MINIMUM_ARCH__ _NV_CONCAT_EVAL(_NV_FIRST_ARG(NV_TARGET_SM_INTEGER_LIST), 0)\n+#  endif\n+\n+#  define _NV_TARGET_PROVIDES(q)   nv::target::provides(q)\n+#  define _NV_TARGET_IS_EXACTLY(q) nv::target::is_exactly(q)\n+\n+#elif defined(_NV_COMPILER_NVCC) || defined (_NV_COMPILER_CLANG_CUDA)\n+\n+#  define _NV_TARGET_VAL_SM_35 350\n+#  define _NV_TARGET_VAL_SM_37 370\n+#  define _NV_TARGET_VAL_SM_50 500\n+#  define _NV_TARGET_VAL_SM_52 520\n+#  define _NV_TARGET_VAL_SM_53 530\n+#  define _NV_TARGET_VAL_SM_60 600\n+#  define _NV_TARGET_VAL_SM_61 610\n+#  define _NV_TARGET_VAL_SM_62 620\n+#  define _NV_TARGET_VAL_SM_70 700\n+#  define _NV_TARGET_VAL_SM_72 720\n+#  define _NV_TARGET_VAL_SM_75 750\n+#  define _NV_TARGET_VAL_SM_80 800\n+#  define _NV_TARGET_VAL_SM_86 860\n+#  define _NV_TARGET_VAL_SM_87 870\n+\n+#  if defined(__CUDA_ARCH__)\n+#    define _NV_TARGET_VAL __CUDA_ARCH__\n+#    define NV_TARGET_MINIMUM_SM_SELECTOR _NV_CONCAT_EVAL(_NV_TARGET_ARCH_TO_SELECTOR_, __CUDA_ARCH__)\n+#    define NV_TARGET_MINIMUM_SM_INTEGER _NV_CONCAT_EVAL(_NV_TARGET_ARCH_TO_SM_, __CUDA_ARCH__)\n+#    define __CUDA_MINIMUM_ARCH__ __CUDA_ARCH__\n+#  endif\n+\n+#  if defined(__CUDA_ARCH__)\n+#    define _NV_TARGET_IS_HOST   0\n+#    define _NV_TARGET_IS_DEVICE 1\n+#  else\n+#    define _NV_TARGET_IS_HOST   1\n+#    define _NV_TARGET_IS_DEVICE 0\n+#  endif\n+\n+#  if defined(_NV_TARGET_VAL)\n+#    define _NV_DEVICE_CHECK(q) (q)\n+#  else\n+#    define _NV_DEVICE_CHECK(q) (0)\n+#  endif\n+\n+#  define _NV_TARGET_PROVIDES(q)   _NV_DEVICE_CHECK(_NV_TARGET_VAL >= q)\n+#  define _NV_TARGET_IS_EXACTLY(q) _NV_DEVICE_CHECK(_NV_TARGET_VAL == q)\n+\n+// NVCC/NVCXX not being used, only host dispatches allowed\n+#else\n+\n+#  define _NV_COMPILER_NVCC\n+\n+#  define _NV_TARGET_VAL_SM_35 350\n+#  define _NV_TARGET_VAL_SM_37 370\n+#  define _NV_TARGET_VAL_SM_50 500\n+#  define _NV_TARGET_VAL_SM_52 520\n+#  define _NV_TARGET_VAL_SM_53 530\n+#  define _NV_TARGET_VAL_SM_60 600\n+#  define _NV_TARGET_VAL_SM_61 610\n+#  define _NV_TARGET_VAL_SM_62 620\n+#  define _NV_TARGET_VAL_SM_70 700\n+#  define _NV_TARGET_VAL_SM_72 720\n+#  define _NV_TARGET_VAL_SM_75 750\n+#  define _NV_TARGET_VAL_SM_80 800\n+#  define _NV_TARGET_VAL_SM_86 860\n+#  define _NV_TARGET_VAL_SM_87 870\n+\n+#  define _NV_TARGET_VAL 0\n+\n+#  define _NV_TARGET_IS_HOST   1\n+#  define _NV_TARGET_IS_DEVICE 0\n+\n+#  define _NV_DEVICE_CHECK(q) (false)\n+\n+#  define _NV_TARGET_PROVIDES(q)   _NV_DEVICE_CHECK(_NV_TARGET_VAL >= q)\n+#  define _NV_TARGET_IS_EXACTLY(q) _NV_DEVICE_CHECK(_NV_TARGET_VAL == q)\n+\n+#endif\n+\n+#define _NV_TARGET___NV_PROVIDES_SM_35 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_35))\n+#define _NV_TARGET___NV_PROVIDES_SM_37 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_37))\n+#define _NV_TARGET___NV_PROVIDES_SM_50 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_50))\n+#define _NV_TARGET___NV_PROVIDES_SM_52 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_52))\n+#define _NV_TARGET___NV_PROVIDES_SM_53 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_53))\n+#define _NV_TARGET___NV_PROVIDES_SM_60 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_60))\n+#define _NV_TARGET___NV_PROVIDES_SM_61 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_61))\n+#define _NV_TARGET___NV_PROVIDES_SM_62 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_62))\n+#define _NV_TARGET___NV_PROVIDES_SM_70 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_70))\n+#define _NV_TARGET___NV_PROVIDES_SM_72 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_72))\n+#define _NV_TARGET___NV_PROVIDES_SM_75 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_75))\n+#define _NV_TARGET___NV_PROVIDES_SM_80 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_80))\n+#define _NV_TARGET___NV_PROVIDES_SM_86 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_86))\n+#define _NV_TARGET___NV_PROVIDES_SM_87 (_NV_TARGET_PROVIDES(_NV_TARGET_VAL_SM_87))\n+\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_35 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_35))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_37 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_37))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_50 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_50))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_52 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_52))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_53 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_53))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_60 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_60))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_61 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_61))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_62 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_62))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_70 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_70))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_72 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_72))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_75 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_75))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_80 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_80))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_86 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_86))\n+#define _NV_TARGET___NV_IS_EXACTLY_SM_87 (_NV_TARGET_IS_EXACTLY(_NV_TARGET_VAL_SM_87))\n+\n+#define NV_PROVIDES_SM_35   __NV_PROVIDES_SM_35\n+#define NV_PROVIDES_SM_37   __NV_PROVIDES_SM_37\n+#define NV_PROVIDES_SM_50   __NV_PROVIDES_SM_50\n+#define NV_PROVIDES_SM_52   __NV_PROVIDES_SM_52\n+#define NV_PROVIDES_SM_53   __NV_PROVIDES_SM_53\n+#define NV_PROVIDES_SM_60   __NV_PROVIDES_SM_60\n+#define NV_PROVIDES_SM_61   __NV_PROVIDES_SM_61\n+#define NV_PROVIDES_SM_62   __NV_PROVIDES_SM_62\n+#define NV_PROVIDES_SM_70   __NV_PROVIDES_SM_70\n+#define NV_PROVIDES_SM_72   __NV_PROVIDES_SM_72\n+#define NV_PROVIDES_SM_75   __NV_PROVIDES_SM_75\n+#define NV_PROVIDES_SM_80   __NV_PROVIDES_SM_80\n+#define NV_PROVIDES_SM_86   __NV_PROVIDES_SM_86\n+#define NV_PROVIDES_SM_87   __NV_PROVIDES_SM_87\n+\n+#define NV_IS_EXACTLY_SM_35 __NV_IS_EXACTLY_SM_35\n+#define NV_IS_EXACTLY_SM_37 __NV_IS_EXACTLY_SM_37\n+#define NV_IS_EXACTLY_SM_50 __NV_IS_EXACTLY_SM_50\n+#define NV_IS_EXACTLY_SM_52 __NV_IS_EXACTLY_SM_52\n+#define NV_IS_EXACTLY_SM_53 __NV_IS_EXACTLY_SM_53\n+#define NV_IS_EXACTLY_SM_60 __NV_IS_EXACTLY_SM_60\n+#define NV_IS_EXACTLY_SM_61 __NV_IS_EXACTLY_SM_61\n+#define NV_IS_EXACTLY_SM_62 __NV_IS_EXACTLY_SM_62\n+#define NV_IS_EXACTLY_SM_70 __NV_IS_EXACTLY_SM_70\n+#define NV_IS_EXACTLY_SM_72 __NV_IS_EXACTLY_SM_72\n+#define NV_IS_EXACTLY_SM_75 __NV_IS_EXACTLY_SM_75\n+#define NV_IS_EXACTLY_SM_80 __NV_IS_EXACTLY_SM_80\n+#define NV_IS_EXACTLY_SM_86 __NV_IS_EXACTLY_SM_86\n+#define NV_IS_EXACTLY_SM_87 __NV_IS_EXACTLY_SM_87\n+\n+#define NV_IS_HOST         __NV_IS_HOST\n+#define NV_IS_DEVICE       __NV_IS_DEVICE\n+\n+#define NV_ANY_TARGET      __NV_ANY_TARGET\n+#define NV_NO_TARGET       __NV_NO_TARGET\n+\n+// Platform invoke mechanisms\n+#if defined(_NV_COMPILER_NVCXX) && defined(_NVHPC_CUDA)\n+\n+#  define _NV_ARCH_COND(q) (_NV_TARGET_##q)\n+\n+#  define _NV_BLOCK_EXPAND(...) _NV_REMOVE_PAREN(__VA_ARGS__)\n+\n+#  define _NV_TARGET_IF(cond, t, ...) \\\n+    (if target _NV_ARCH_COND(cond) {    \\\n+      _NV_BLOCK_EXPAND(t)        \\\n+    } else { _NV_BLOCK_EXPAND(__VA_ARGS__) })\n+\n+#elif defined(_NV_COMPILER_NVCC) || defined (_NV_COMPILER_CLANG_CUDA)\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_35)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_35 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_35 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_37)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_37 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_37 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_50)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_50 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_50 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_52)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_52 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_52 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_53)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_53 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_53 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_60)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_60 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_60 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_61)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_61 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_61 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_62)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_62 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_62 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_70)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_70 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_70 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_72)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_72 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_72 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_75)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_75 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_75 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_80)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_80 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_80 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_86)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_86 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_86 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_IS_EXACTLY_SM_87)\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_87 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_EXACTLY_SM_87 0\n+#  endif\n+\n+#  if (_NV_TARGET_IS_HOST)\n+#    define _NV_TARGET_BOOL___NV_IS_HOST   1\n+#    define _NV_TARGET_BOOL___NV_IS_DEVICE 0\n+#  else\n+#    define _NV_TARGET_BOOL___NV_IS_HOST   0\n+#    define _NV_TARGET_BOOL___NV_IS_DEVICE 1\n+#  endif\n+\n+#  define _NV_TARGET_BOOL___NV_ANY_TARGET 1\n+#  define _NV_TARGET_BOOL___NV_NO_TARGET 0\n+\n+// NVCC Greater than stuff\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_35)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_35 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_35 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_37)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_37 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_37 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_50)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_50 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_50 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_52)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_52 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_52 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_53)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_53 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_53 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_60)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_60 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_60 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_61)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_61 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_61 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_62)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_62 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_62 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_70)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_70 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_70 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_72)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_72 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_72 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_75)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_75 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_75 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_80)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_80 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_80 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_86)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_86 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_86 0\n+#  endif\n+\n+#  if (_NV_TARGET___NV_PROVIDES_SM_87)\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_87 1\n+#  else\n+#    define _NV_TARGET_BOOL___NV_PROVIDES_SM_87 0\n+#  endif\n+\n+#  define _NV_ARCH_COND_CAT1(cond) _NV_TARGET_BOOL_##cond\n+#  define _NV_ARCH_COND_CAT(cond) _NV_EVAL(_NV_ARCH_COND_CAT1(cond))\n+\n+#    define _NV_TARGET_EMPTY_PARAM ;\n+\n+#  if defined(_NV_TARGET_CPP11)\n+\n+#    define _NV_BLOCK_EXPAND(...) { _NV_REMOVE_PAREN(__VA_ARGS__) }\n+#    define _NV_TARGET_IF(cond, t, ...) _NV_IF( _NV_ARCH_COND_CAT(cond), t, __VA_ARGS__)\n+\n+#  else // <C++11 fallback\n+\n+#    define _NV_BLOCK_EXPAND(x) { x }\n+\n+#    define _NV_TARGET_IF(cond, t)         _NV_IF(_NV_ARCH_COND_CAT(cond), t, _NV_TARGET_EMPTY_PARAM)\n+#    define _NV_TARGET_IF_ELSE(cond, t, f) _NV_IF(_NV_ARCH_COND_CAT(cond), t, f)\n+\n+#  endif\n+\n+#endif // _NV_COMPILER_NVCC\n+\n+#if defined(_NV_TARGET_CPP11)\n+\n+#  define _NV_TARGET_DISPATCH_HANDLE0()\n+#  define _NV_TARGET_DISPATCH_HANDLE2(q, fn)        _NV_TARGET_IF(q, fn)\n+#  define _NV_TARGET_DISPATCH_HANDLE4(q, fn, ...)   _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE2(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE6(q, fn, ...)   _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE4(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE8(q, fn, ...)   _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE6(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE10(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE8(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE12(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE10(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE14(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE12(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE16(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE14(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE18(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE16(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE20(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE18(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE22(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE20(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE24(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE22(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE26(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE24(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE28(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE26(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE30(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE28(__VA_ARGS__))\n+#  define _NV_TARGET_DISPATCH_HANDLE32(q, fn, ...)  _NV_TARGET_IF(q, fn, _NV_TARGET_DISPATCH_HANDLE30(__VA_ARGS__))\n+\n+#  define _NV_TARGET_DISPATCH(...) _NV_BLOCK_EXPAND(_NV_DISPATCH_N_ARY(_NV_TARGET_DISPATCH_HANDLE, __VA_ARGS__))\n+\n+// NV_IF_TARGET supports a false statement provided as a variadic macro\n+#  define NV_IF_TARGET(cond, ...)    _NV_BLOCK_EXPAND(_NV_TARGET_IF(cond, __VA_ARGS__))\n+#  define NV_IF_ELSE_TARGET(cond, t, f) _NV_BLOCK_EXPAND(_NV_TARGET_IF(cond, t, f))\n+#  define NV_DISPATCH_TARGET(...)       _NV_TARGET_DISPATCH(__VA_ARGS__)\n+\n+#else // <C++11 fallback\n+\n+// NV_IF_TARGET does not support a fallback false statement in C++03 or C dialects\n+#  define NV_IF_TARGET(cond, t)         _NV_BLOCK_EXPAND(_NV_TARGET_IF(cond, t))\n+#  define NV_IF_ELSE_TARGET(cond, t, f) _NV_BLOCK_EXPAND(_NV_TARGET_IF_ELSE(cond, t, f))\n+\n+#endif\n+\n+#endif // _NV__TARGET_MACROS\n\\ No newline at end of file\ndiff --git a/include/cuco/detail/nv/target b/include/cuco/detail/nv/target\nnew file mode 100644\nindex 0000000..f199ee7\n--- /dev/null\n+++ b/include/cuco/detail/nv/target\n@@ -0,0 +1,201 @@\n+//===----------------------------------------------------------------------===//\n+//\n+// Part of libcu++, the C++ Standard Library for your entire system,\n+// under the Apache License v2.0 with LLVM Exceptions.\n+// See https://llvm.org/LICENSE.txt for license information.\n+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n+//\n+//===----------------------------------------------------------------------===//\n+\n+// This header contains a preview of a portability system that enables\n+// CUDA C++ development with NVC++, NVCC, and supported host compilers.\n+// These interfaces are not guaranteed to be stable.\n+\n+#ifndef __NV_TARGET_H\n+#define __NV_TARGET_H\n+\n+#if defined(__NVCC__) || defined(__CUDACC_RTC__)\n+#  define _NV_COMPILER_NVCC\n+#elif defined(__NVCOMPILER) && __cplusplus >= 201103L\n+#  define _NV_COMPILER_NVCXX\n+#elif defined(__clang__) && defined(__CUDA__) && defined(__CUDA_ARCH__)\n+// clang compiling CUDA code, device mode.\n+#  define _NV_COMPILER_CLANG_CUDA\n+#endif\n+\n+#if defined(__CUDACC_RTC__)\n+#  define _NV_FUNCTION_ANNOTATION __device__\n+#else\n+#  define _NV_FUNCTION_ANNOTATION\n+#endif\n+\n+#if defined(_NV_COMPILER_NVCXX)\n+#  define _NV_BITSET_ATTRIBUTE [[nv::__target_bitset]]\n+#else\n+#  define _NV_BITSET_ATTRIBUTE\n+#endif\n+\n+#if (!defined(__ibmxl__)) && \\\n+    ((defined(__cplusplus) && __cplusplus >= 201103L) || \\\n+     (defined(_MSC_VER) && _MSVC_LANG >= 201103L))\n+#  define _NV_TARGET_CPP11\n+#endif\n+\n+#if defined(_NV_TARGET_CPP11)\n+\n+namespace nv {\n+  namespace target {\n+    namespace detail {\n+\n+      typedef unsigned long long base_int_t;\n+\n+      // No host specialization\n+      constexpr base_int_t all_hosts = 1;\n+\n+      // NVIDIA GPUs\n+      constexpr base_int_t sm_35_bit = 1 << 1;\n+      constexpr base_int_t sm_37_bit = 1 << 2;\n+      constexpr base_int_t sm_50_bit = 1 << 3;\n+      constexpr base_int_t sm_52_bit = 1 << 4;\n+      constexpr base_int_t sm_53_bit = 1 << 5;\n+      constexpr base_int_t sm_60_bit = 1 << 6;\n+      constexpr base_int_t sm_61_bit = 1 << 7;\n+      constexpr base_int_t sm_62_bit = 1 << 8;\n+      constexpr base_int_t sm_70_bit = 1 << 9;\n+      constexpr base_int_t sm_72_bit = 1 << 10;\n+      constexpr base_int_t sm_75_bit = 1 << 11;\n+      constexpr base_int_t sm_80_bit = 1 << 12;\n+      constexpr base_int_t sm_86_bit = 1 << 13;\n+      constexpr base_int_t sm_87_bit = 1 << 14;\n+      constexpr base_int_t all_devices =\n+          sm_35_bit | sm_37_bit |\n+          sm_50_bit | sm_52_bit | sm_53_bit |\n+          sm_60_bit | sm_61_bit | sm_62_bit |\n+          sm_70_bit | sm_72_bit | sm_75_bit |\n+          sm_80_bit | sm_86_bit | sm_87_bit;\n+\n+      // Store a set of targets as a set of bits\n+      struct _NV_BITSET_ATTRIBUTE target_description {\n+        base_int_t targets;\n+        _NV_FUNCTION_ANNOTATION\n+        constexpr target_description(base_int_t a) : targets(a) { }\n+      };\n+\n+      // The type of the user-visible names of the NVIDIA GPU targets\n+      enum class sm_selector : base_int_t {\n+        sm_35 = 35, sm_37 = 37,\n+        sm_50 = 50, sm_52 = 52, sm_53 = 53,\n+        sm_60 = 60, sm_61 = 61, sm_62 = 62,\n+        sm_70 = 70, sm_72 = 72, sm_75 = 75,\n+        sm_80 = 80, sm_86 = 86, sm_87 = 87,\n+      };\n+      _NV_FUNCTION_ANNOTATION\n+      constexpr base_int_t toint(sm_selector a) {\n+        return static_cast<base_int_t>(a);\n+      }\n+      _NV_FUNCTION_ANNOTATION\n+      constexpr base_int_t bitexact(sm_selector a) {\n+        return toint(a) == 35 ? sm_35_bit :\n+               toint(a) == 37 ? sm_37_bit :\n+               toint(a) == 50 ? sm_50_bit :\n+               toint(a) == 52 ? sm_52_bit :\n+               toint(a) == 53 ? sm_53_bit :\n+               toint(a) == 60 ? sm_60_bit :\n+               toint(a) == 61 ? sm_61_bit :\n+               toint(a) == 62 ? sm_62_bit :\n+               toint(a) == 70 ? sm_70_bit :\n+               toint(a) == 72 ? sm_72_bit :\n+               toint(a) == 75 ? sm_75_bit :\n+               toint(a) == 80 ? sm_80_bit :\n+               toint(a) == 86 ? sm_86_bit :\n+               toint(a) == 87 ? sm_87_bit : 0;\n+      }\n+      _NV_FUNCTION_ANNOTATION\n+      constexpr base_int_t bitrounddown(sm_selector a) {\n+        return toint(a) >= 87 ? sm_87_bit :\n+               toint(a) >= 86 ? sm_86_bit :\n+               toint(a) >= 80 ? sm_80_bit :\n+               toint(a) >= 75 ? sm_75_bit :\n+               toint(a) >= 72 ? sm_72_bit :\n+               toint(a) >= 70 ? sm_70_bit :\n+               toint(a) >= 62 ? sm_62_bit :\n+               toint(a) >= 61 ? sm_61_bit :\n+               toint(a) >= 60 ? sm_60_bit :\n+               toint(a) >= 53 ? sm_53_bit :\n+               toint(a) >= 52 ? sm_52_bit :\n+               toint(a) >= 50 ? sm_50_bit :\n+               toint(a) >= 37 ? sm_37_bit :\n+               toint(a) >= 35 ? sm_35_bit : 0;\n+      }\n+\n+      // Public API for NVIDIA GPUs\n+\n+      _NV_FUNCTION_ANNOTATION\n+      constexpr target_description is_exactly(sm_selector a) {\n+        return target_description(bitexact(a));\n+      }\n+\n+      _NV_FUNCTION_ANNOTATION\n+      constexpr target_description provides(sm_selector a) {\n+        return target_description(~(bitrounddown(a) - 1) & all_devices);\n+      }\n+\n+      // Boolean operations on target sets\n+\n+      _NV_FUNCTION_ANNOTATION\n+      constexpr target_description operator&&(target_description a,\n+                                              target_description b) {\n+        return target_description(a.targets & b.targets);\n+      }\n+\n+      _NV_FUNCTION_ANNOTATION\n+      constexpr target_description operator||(target_description a,\n+                                              target_description b) {\n+        return target_description(a.targets | b.targets);\n+      }\n+\n+      _NV_FUNCTION_ANNOTATION\n+      constexpr target_description operator!(target_description a) {\n+        return target_description(~a.targets & (all_devices | all_hosts));\n+      }\n+    }\n+\n+    using detail::target_description;\n+    using detail::sm_selector;\n+\n+    // The predicates for basic host/device selection\n+    constexpr target_description is_host =\n+      target_description(detail::all_hosts);\n+    constexpr target_description is_device =\n+      target_description(detail::all_devices);\n+    constexpr target_description any_target =\n+      target_description(detail::all_hosts | detail::all_devices);\n+    constexpr target_description no_target =\n+      target_description(0);\n+\n+    // The public names for NVIDIA GPU architectures\n+    constexpr sm_selector sm_35 = sm_selector::sm_35;\n+    constexpr sm_selector sm_37 = sm_selector::sm_37;\n+    constexpr sm_selector sm_50 = sm_selector::sm_50;\n+    constexpr sm_selector sm_52 = sm_selector::sm_52;\n+    constexpr sm_selector sm_53 = sm_selector::sm_53;\n+    constexpr sm_selector sm_60 = sm_selector::sm_60;\n+    constexpr sm_selector sm_61 = sm_selector::sm_61;\n+    constexpr sm_selector sm_62 = sm_selector::sm_62;\n+    constexpr sm_selector sm_70 = sm_selector::sm_70;\n+    constexpr sm_selector sm_72 = sm_selector::sm_72;\n+    constexpr sm_selector sm_75 = sm_selector::sm_75;\n+    constexpr sm_selector sm_80 = sm_selector::sm_80;\n+    constexpr sm_selector sm_86 = sm_selector::sm_86;\n+    constexpr sm_selector sm_87 = sm_selector::sm_87;\n+\n+    using detail::is_exactly;\n+    using detail::provides;\n+  }\n+}\n+\n+#endif // C++11\n+\n+#include \"detail/__target_macros\"\n+\n+#endif // __NV_TARGET_H\n\\ No newline at end of file\ndiff --git a/include/cuco/detail/pair.cuh b/include/cuco/detail/pair.cuh\nindex 7ea3988..846728b 100644\n--- a/include/cuco/detail/pair.cuh\n+++ b/include/cuco/detail/pair.cuh\n@@ -24,6 +24,39 @@\n #include <algorithm>\n #include <tuple>\n #include <type_traits>\n+namespace std {\n+template <class...>\n+using void_t = void;\n+\n+template <bool B>\n+using bool_constant = std::integral_constant<bool, B>;\n+\n+template <typename _Tp>\n+struct has_unique_object_representations : bool_constant<__has_unique_object_representations(std::remove_cv_t<std::remove_all_extents_t<_Tp>>)> {};\n+\n+template <class T>\n+inline constexpr bool has_unique_object_representations_v = has_unique_object_representations<T>::value;\n+\n+// Fangzhou Ai @ 2022/07/07 3:28 PM PST\n+// This deprecated function cast is for older veriosn of cuCollection\n+// template< class From, class To >\n+// inline constexpr bool is_convertible_v = is_convertible<From, To>::value;\n+\n+template <typename Key, typename ProbeKey, typename KeyEqual>\n+struct is_invocable :\n+    std::is_constructible<\n+        std::function<void(ProbeKey, KeyEqual)>,\n+        std::reference_wrapper<typename std::remove_reference<Key>::type>\n+    >\n+{\n+};\n+\n+template <typename Key, typename ProbeKey, typename KeyEqual>\n+inline constexpr bool is_invocable_v = is_invocable<Key, ProbeKey, KeyEqual>::value;\n+\n+template <typename Base, typename Derived>\n+inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;\n+} // namespace std\n \n namespace cuco {\n namespace detail {\ndiff --git a/include/cuco/detail/static_map.inl b/include/cuco/detail/static_map.inl\nindex 09e9d05..5e16f13 100644\n--- a/include/cuco/detail/static_map.inl\n+++ b/include/cuco/detail/static_map.inl\n@@ -25,50 +25,17 @@\n #include <cub/device/device_select.cuh>\n \n namespace cuco {\n-\n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n-static_map<Key, Value, Scope, Allocator>::static_map(\n-  std::size_t capacity,\n-  sentinel::empty_key<Key> empty_key_sentinel,\n-  sentinel::empty_value<Value> empty_value_sentinel,\n-  Allocator const& alloc,\n-  cudaStream_t stream)\n-  : capacity_{std::max(capacity, std::size_t{1})},  // to avoid dereferencing a nullptr (Issue #72)\n-    empty_key_sentinel_{empty_key_sentinel.value},\n-    empty_value_sentinel_{empty_value_sentinel.value},\n-    erased_key_sentinel_{empty_key_sentinel.value},\n-    slot_allocator_{alloc},\n-    counter_allocator_{alloc}\n-{\n-  slots_         = std::allocator_traits<slot_allocator_type>::allocate(slot_allocator_, capacity_);\n-  num_successes_ = std::allocator_traits<counter_allocator_type>::allocate(counter_allocator_, 1);\n-\n-  auto constexpr block_size = 256;\n-  auto constexpr stride     = 4;\n-  auto const grid_size      = (capacity_ + stride * block_size - 1) / (stride * block_size);\n-  detail::initialize<block_size, atomic_key_type, atomic_mapped_type>\n-    <<<grid_size, block_size, 0, stream>>>(\n-      slots_, empty_key_sentinel_, empty_value_sentinel_, capacity_);\n-}\n-\n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n-static_map<Key, Value, Scope, Allocator>::static_map(\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n+static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::static_map(\n   std::size_t capacity,\n-  sentinel::empty_key<Key> empty_key_sentinel,\n-  sentinel::empty_value<Value> empty_value_sentinel,\n-  sentinel::erased_key<Key> erased_key_sentinel,\n   Allocator const& alloc,\n   cudaStream_t stream)\n   : capacity_{std::max(capacity, std::size_t{1})},  // to avoid dereferencing a nullptr (Issue #72)\n-    empty_key_sentinel_{empty_key_sentinel.value},\n-    empty_value_sentinel_{empty_value_sentinel.value},\n-    erased_key_sentinel_{erased_key_sentinel.value},\n     slot_allocator_{alloc},\n     counter_allocator_{alloc}\n {\n-  CUCO_RUNTIME_EXPECTS(empty_key_sentinel_ != erased_key_sentinel_,\n-                       \"The empty key sentinel and erased key sentinel cannot be the same value.\");\n-\n   slots_         = std::allocator_traits<slot_allocator_type>::allocate(slot_allocator_, capacity_);\n   num_successes_ = std::allocator_traits<counter_allocator_type>::allocate(counter_allocator_, 1);\n \n@@ -80,16 +47,20 @@ static_map<Key, Value, Scope, Allocator>::static_map(\n       slots_, empty_key_sentinel_, empty_value_sentinel_, capacity_);\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n-static_map<Key, Value, Scope, Allocator>::~static_map()\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n+static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::~static_map()\n {\n   std::allocator_traits<slot_allocator_type>::deallocate(slot_allocator_, slots_, capacity_);\n   std::allocator_traits<counter_allocator_type>::deallocate(counter_allocator_, num_successes_, 1);\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename InputIt, typename Hash, typename KeyEqual>\n-void static_map<Key, Value, Scope, Allocator>::insert(\n+void static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::insert(\n   InputIt first, InputIt last, Hash hash, KeyEqual key_equal, cudaStream_t stream)\n {\n   auto num_keys = std::distance(first, last);\n@@ -116,13 +87,15 @@ void static_map<Key, Value, Scope, Allocator>::insert(\n   size_ += h_num_successes;\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename InputIt,\n           typename StencilIt,\n           typename Predicate,\n           typename Hash,\n           typename KeyEqual>\n-void static_map<Key, Value, Scope, Allocator>::insert_if(InputIt first,\n+void static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::insert_if(InputIt first,\n                                                          InputIt last,\n                                                          StencilIt stencil,\n                                                          Predicate pred,\n@@ -153,14 +126,13 @@ void static_map<Key, Value, Scope, Allocator>::insert_if(InputIt first,\n   size_ += h_num_successes;\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename InputIt, typename Hash, typename KeyEqual>\n-void static_map<Key, Value, Scope, Allocator>::erase(\n+void static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::erase(\n   InputIt first, InputIt last, Hash hash, KeyEqual key_equal, cudaStream_t stream)\n {\n-  CUCO_RUNTIME_EXPECTS(get_empty_key_sentinel() != get_erased_key_sentinel(),\n-                       \"You must provide a unique erased key sentinel value at map construction.\");\n-\n   auto num_keys = std::distance(first, last);\n   if (num_keys == 0) { return; }\n \n@@ -185,9 +157,11 @@ void static_map<Key, Value, Scope, Allocator>::erase(\n   size_ -= h_num_successes;\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename InputIt, typename OutputIt, typename Hash, typename KeyEqual>\n-void static_map<Key, Value, Scope, Allocator>::find(InputIt first,\n+void static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::find(InputIt first,\n                                                     InputIt last,\n                                                     OutputIt output_begin,\n                                                     Hash hash,\n@@ -207,9 +181,11 @@ void static_map<Key, Value, Scope, Allocator>::find(InputIt first,\n     <<<grid_size, block_size, 0, stream>>>(first, last, output_begin, view, hash, key_equal);\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename KeyOut, typename ValueOut>\n-std::pair<KeyOut, ValueOut> static_map<Key, Value, Scope, Allocator>::retrieve_all(\n+std::pair<KeyOut, ValueOut> static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::retrieve_all(\n   KeyOut keys_out, ValueOut values_out, cudaStream_t stream)\n {\n   static_assert(sizeof(pair_atomic_type) == sizeof(value_type));\n@@ -251,12 +227,19 @@ std::pair<KeyOut, ValueOut> static_map<Key, Value, Scope, Allocator>::retrieve_a\n     cudaMemcpyAsync(&h_num_out, d_num_out, sizeof(std::size_t), cudaMemcpyDeviceToHost, stream));\n   CUCO_CUDA_TRY(cudaStreamSynchronize(stream));\n \n+  std::allocator_traits<temp_allocator_type>::deallocate(\n+    temp_allocator, reinterpret_cast<char*>(d_num_out), sizeof(std::size_t));\n+  std::allocator_traits<temp_allocator_type>::deallocate(\n+    temp_allocator, d_temp_storage, temp_storage_bytes);\n+\n   return std::make_pair(keys_out + h_num_out, values_out + h_num_out);\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename InputIt, typename OutputIt, typename Hash, typename KeyEqual>\n-void static_map<Key, Value, Scope, Allocator>::contains(InputIt first,\n+void static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::contains(InputIt first,\n                                                         InputIt last,\n                                                         OutputIt output_begin,\n                                                         Hash hash,\n@@ -276,10 +259,12 @@ void static_map<Key, Value, Scope, Allocator>::contains(InputIt first,\n     <<<grid_size, block_size, 0, stream>>>(first, last, output_begin, view, hash, key_equal);\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename KeyEqual>\n-__device__ static_map<Key, Value, Scope, Allocator>::device_mutable_view::insert_result\n-static_map<Key, Value, Scope, Allocator>::device_mutable_view::packed_cas(\n+__device__ static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::insert_result\n+static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::packed_cas(\n   iterator current_slot,\n   value_type const& insert_pair,\n   KeyEqual key_equal,\n@@ -308,10 +293,12 @@ static_map<Key, Value, Scope, Allocator>::device_mutable_view::packed_cas(\n   return insert_result::CONTINUE;\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename KeyEqual>\n-__device__ static_map<Key, Value, Scope, Allocator>::device_mutable_view::insert_result\n-static_map<Key, Value, Scope, Allocator>::device_mutable_view::back_to_back_cas(\n+__device__ static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::insert_result\n+static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::back_to_back_cas(\n   iterator current_slot,\n   value_type const& insert_pair,\n   KeyEqual key_equal,\n@@ -348,10 +335,12 @@ static_map<Key, Value, Scope, Allocator>::device_mutable_view::back_to_back_cas(\n   return insert_result::CONTINUE;\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename KeyEqual>\n-__device__ static_map<Key, Value, Scope, Allocator>::device_mutable_view::insert_result\n-static_map<Key, Value, Scope, Allocator>::device_mutable_view::cas_dependent_write(\n+__device__ static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::insert_result\n+static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::cas_dependent_write(\n   iterator current_slot,\n   value_type const& insert_pair,\n   KeyEqual key_equal,\n@@ -376,9 +365,11 @@ static_map<Key, Value, Scope, Allocator>::device_mutable_view::cas_dependent_wri\n   return insert_result::CONTINUE;\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename Hash, typename KeyEqual>\n-__device__ bool static_map<Key, Value, Scope, Allocator>::device_mutable_view::insert(\n+__device__ bool static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::insert(\n   value_type const& insert_pair, Hash hash, KeyEqual key_equal) noexcept\n {\n   auto current_slot{initial_slot(insert_pair.first, hash)};\n@@ -422,12 +413,14 @@ __device__ bool static_map<Key, Value, Scope, Allocator>::device_mutable_view::i\n   }\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename Hash, typename KeyEqual>\n __device__\n-  thrust::pair<typename static_map<Key, Value, Scope, Allocator>::device_mutable_view::iterator,\n+  thrust::pair<typename static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::iterator,\n                bool>\n-  static_map<Key, Value, Scope, Allocator>::device_mutable_view::insert_and_find(\n+  static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::insert_and_find(\n     value_type const& insert_pair, Hash hash, KeyEqual key_equal) noexcept\n {\n #if __CUDA_ARCH__ < 700\n@@ -512,9 +505,11 @@ __device__\n   }\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename CG, typename Hash, typename KeyEqual>\n-__device__ bool static_map<Key, Value, Scope, Allocator>::device_mutable_view::insert(\n+__device__ bool static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::insert(\n   CG const& g, value_type const& insert_pair, Hash hash, KeyEqual key_equal) noexcept\n {\n   auto current_slot = initial_slot(g, insert_pair.first, hash);\n@@ -576,9 +571,11 @@ __device__ bool static_map<Key, Value, Scope, Allocator>::device_mutable_view::i\n   }\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename Hash, typename KeyEqual>\n-__device__ bool static_map<Key, Value, Scope, Allocator>::device_mutable_view::erase(\n+__device__ bool static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::erase(\n   key_type const& k, Hash hash, KeyEqual key_equal) noexcept\n {\n   auto current_slot{initial_slot(k, hash)};\n@@ -622,9 +619,11 @@ __device__ bool static_map<Key, Value, Scope, Allocator>::device_mutable_view::e\n   }\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename CG, typename Hash, typename KeyEqual>\n-__device__ bool static_map<Key, Value, Scope, Allocator>::device_mutable_view::erase(\n+__device__ bool static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_mutable_view::erase(\n   CG const& g, key_type const& k, Hash hash, KeyEqual key_equal) noexcept\n {\n   auto current_slot = initial_slot(g, k, hash);\n@@ -680,10 +679,12 @@ __device__ bool static_map<Key, Value, Scope, Allocator>::device_mutable_view::e\n   }\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename Hash, typename KeyEqual>\n-__device__ typename static_map<Key, Value, Scope, Allocator>::device_view::iterator\n-static_map<Key, Value, Scope, Allocator>::device_view::find(Key const& k,\n+__device__ typename static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_view::iterator\n+static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_view::find(Key const& k,\n                                                             Hash hash,\n                                                             KeyEqual key_equal) noexcept\n {\n@@ -703,10 +704,12 @@ static_map<Key, Value, Scope, Allocator>::device_view::find(Key const& k,\n   }\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename Hash, typename KeyEqual>\n-__device__ typename static_map<Key, Value, Scope, Allocator>::device_view::const_iterator\n-static_map<Key, Value, Scope, Allocator>::device_view::find(Key const& k,\n+__device__ typename static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_view::const_iterator\n+static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_view::find(Key const& k,\n                                                             Hash hash,\n                                                             KeyEqual key_equal) const noexcept\n {\n@@ -726,13 +729,15 @@ static_map<Key, Value, Scope, Allocator>::device_view::find(Key const& k,\n   }\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename CG, typename Hash, typename KeyEqual>\n-__device__ typename static_map<Key, Value, Scope, Allocator>::device_view::iterator\n-static_map<Key, Value, Scope, Allocator>::device_view::find(CG g,\n+__device__ typename static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_view::iterator\n+static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_view::find(CG g,\n                                                             Key const& k,\n                                                             Hash hash,\n-                                                            KeyEqual key_equal) noexcept\n+                                                            KeyEqual key_equal) const noexcept\n {\n   auto current_slot = initial_slot(g, k, hash);\n \n@@ -764,49 +769,11 @@ static_map<Key, Value, Scope, Allocator>::device_view::find(CG g,\n   }\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n-template <typename CG, typename Hash, typename KeyEqual>\n-__device__ typename static_map<Key, Value, Scope, Allocator>::device_view::const_iterator\n-static_map<Key, Value, Scope, Allocator>::device_view::find(CG g,\n-                                                            Key const& k,\n-                                                            Hash hash,\n-                                                            KeyEqual key_equal) const noexcept\n-{\n-  auto current_slot = initial_slot(g, k, hash);\n-\n-  while (true) {\n-    auto const existing_key = current_slot->first.load(cuda::std::memory_order_relaxed);\n-\n-    // The user provide `key_equal` can never be used to compare against `empty_key_sentinel` as\n-    // the sentinel is not a valid key value. Therefore, first check for the sentinel\n-    auto const slot_is_empty =\n-      detail::bitwise_compare(existing_key, this->get_empty_key_sentinel());\n-\n-    // the key we were searching for was found by one of the threads, so we return an iterator to\n-    // the entry\n-    auto const exists = g.ballot(not slot_is_empty and key_equal(existing_key, k));\n-    if (exists) {\n-      uint32_t src_lane = __ffs(exists) - 1;\n-      // TODO: This shouldn't cast an iterator to an int to shuffle. Instead, get the index of the\n-      // current_slot and shuffle that instead.\n-      intptr_t res_slot = g.shfl(reinterpret_cast<intptr_t>(current_slot), src_lane);\n-      return reinterpret_cast<const_iterator>(res_slot);\n-    }\n-\n-    // we found an empty slot, meaning that the key we're searching\n-    // for isn't in this submap, so we should move onto the next one\n-    if (g.ballot(slot_is_empty)) { return this->end(); }\n-\n-    // otherwise, all slots in the current window are full with other keys,\n-    // so we move onto the next window in the current submap\n-\n-    current_slot = next_slot(g, current_slot);\n-  }\n-}\n-\n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename ProbeKey, typename Hash, typename KeyEqual>\n-__device__ bool static_map<Key, Value, Scope, Allocator>::device_view::contains(\n+__device__ bool static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_view::contains(\n   ProbeKey const& k, Hash hash, KeyEqual key_equal) const noexcept\n {\n   auto current_slot = initial_slot(k, hash);\n@@ -822,10 +789,12 @@ __device__ bool static_map<Key, Value, Scope, Allocator>::device_view::contains(\n   }\n }\n \n-template <typename Key, typename Value, cuda::thread_scope Scope, typename Allocator>\n+template <typename Key, typename Value, \n+          Key empty_key_sentinel, Value empty_value_sentinel, Key erased_key_sentinel,\n+          cuda::thread_scope Scope, typename Allocator>\n template <typename CG, typename ProbeKey, typename Hash, typename KeyEqual>\n __device__ std::enable_if_t<std::is_invocable_v<KeyEqual, ProbeKey, Key>, bool>\n-static_map<Key, Value, Scope, Allocator>::device_view::contains(CG const& g,\n+static_map<Key, Value, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel, Scope, Allocator>::device_view::contains(CG const& g,\n                                                                 ProbeKey const& k,\n                                                                 Hash hash,\n                                                                 KeyEqual key_equal) const noexcept\ndiff --git a/include/cuco/static_map.cuh b/include/cuco/static_map.cuh\nindex 1daad99..25f7532 100644\n--- a/include/cuco/static_map.cuh\n+++ b/include/cuco/static_map.cuh\n@@ -22,7 +22,6 @@\n #include <cuco/detail/hash_functions.cuh>\n #include <cuco/detail/pair.cuh>\n #include <cuco/detail/static_map_kernels.cuh>\n-#include <cuco/sentinel.cuh>\n #include <cuco/traits.hpp>\n \n #include <thrust/functional.h>\n@@ -121,6 +120,9 @@ class dynamic_map;\n  */\n template <typename Key,\n           typename Value,\n+          Key empty_key_sentinel,\n+          Value empty_value_sentinel,\n+          Key erased_key_sentinel,\n           cuda::thread_scope Scope = cuda::thread_scope_device,\n           typename Allocator       = cuco::cuda_allocator<char>>\n class static_map {\n@@ -135,7 +137,6 @@ class static_map {\n                 \"cuco::is_bitwise_comparable_v<Value>.\");\n \n   friend class dynamic_map<Key, Value, Scope, Allocator>;  ///< Dynamic map as friend class\n-\n  public:\n   using value_type         = cuco::pair_type<Key, Value>;       ///< Type of key/value pairs\n   using key_type           = Key;                               ///< Key type\n@@ -194,35 +195,10 @@ class static_map {\n    * that contains either.\n    *\n    * @param capacity The total number of slots in the map\n-   * @param empty_key_sentinel The reserved key value for empty slots\n-   * @param empty_value_sentinel The reserved mapped value for empty slots\n-   * @param alloc Allocator used for allocating device storage\n-   * @param stream Stream used for executing the kernels\n-   */\n-  static_map(std::size_t capacity,\n-             sentinel::empty_key<Key> empty_key_sentinel,\n-             sentinel::empty_value<Value> empty_value_sentinel,\n-             Allocator const& alloc = Allocator{},\n-             cudaStream_t stream    = 0);\n-\n-  /**\n-   * @brief Constructs a fixed-size map with erase capability.\n-   * empty_key_sentinel and erased_key_sentinel must be different values.\n-   *\n-   * @throw std::runtime error if the empty key sentinel and erased key sentinel\n-   * are the same value\n-   *\n-   * @param capacity The total number of slots in the map\n-   * @param empty_key_sentinel The reserved key value for empty slots\n-   * @param empty_value_sentinel The reserved mapped value for empty slots\n-   * @param erased_key_sentinel The reserved value to denote erased slots\n    * @param alloc Allocator used for allocating device storage\n    * @param stream Stream used for executing the kernels\n    */\n   static_map(std::size_t capacity,\n-             sentinel::empty_key<Key> empty_key_sentinel,\n-             sentinel::empty_value<Value> empty_value_sentinel,\n-             sentinel::erased_key<Key> erased_key_sentinel,\n              Allocator const& alloc = Allocator{},\n              cudaStream_t stream    = 0);\n \n@@ -416,9 +392,8 @@ class static_map {\n                 KeyEqual key_equal  = KeyEqual{},\n                 cudaStream_t stream = 0) const;\n \n- private:\n-  class device_view_base {\n-   protected:\n+  class __align__(16) device_view_base {\n+   public:\n     // Import member type definitions from `static_map`\n     using value_type     = value_type;\n     using key_type       = Key;\n@@ -427,34 +402,13 @@ class static_map {\n     using const_iterator = pair_atomic_type const*;\n     using slot_type      = slot_type;\n \n-    Key empty_key_sentinel_{};      ///< Key value that represents an empty slot\n-    Key erased_key_sentinel_{};     ///< Key value that represents an erased slot\n-    Value empty_value_sentinel_{};  ///< Initial Value of empty slot\n     pair_atomic_type* slots_{};     ///< Pointer to flat slots storage\n     std::size_t capacity_{};        ///< Total number of slots\n \n     __host__ __device__ device_view_base(pair_atomic_type* slots,\n-                                         std::size_t capacity,\n-                                         sentinel::empty_key<Key> empty_key_sentinel,\n-                                         sentinel::empty_value<Value> empty_value_sentinel) noexcept\n-      : slots_{slots},\n-        capacity_{capacity},\n-        empty_key_sentinel_{empty_key_sentinel.value},\n-        erased_key_sentinel_{empty_key_sentinel.value},\n-        empty_value_sentinel_{empty_value_sentinel.value}\n-    {\n-    }\n-\n-    __host__ __device__ device_view_base(pair_atomic_type* slots,\n-                                         std::size_t capacity,\n-                                         sentinel::empty_key<Key> empty_key_sentinel,\n-                                         sentinel::empty_value<Value> empty_value_sentinel,\n-                                         sentinel::erased_key<Key> erased_key_sentinel) noexcept\n+                                         std::size_t capacity) noexcept\n       : slots_{slots},\n-        capacity_{capacity},\n-        empty_key_sentinel_{empty_key_sentinel.value},\n-        erased_key_sentinel_{erased_key_sentinel.value},\n-        empty_value_sentinel_{empty_value_sentinel.value}\n+        capacity_{capacity}\n     {\n     }\n \n@@ -505,27 +459,7 @@ class static_map {\n      * @return Pointer to the initial slot for `k`\n      */\n     template <typename CG, typename ProbeKey, typename Hash>\n-    __device__ iterator initial_slot(CG const& g, ProbeKey const& k, Hash hash) noexcept\n-    {\n-      return &slots_[(hash(k) + g.thread_rank()) % capacity_];\n-    }\n-\n-    /**\n-     * @brief Returns the initial slot for a given key `k`\n-     *\n-     * To be used for Cooperative Group based probing.\n-     *\n-     * @tparam CG Cooperative Group type\n-     * @tparam ProbeKey Probe key type\n-     * @tparam Hash Unary callable type\n-     *\n-     * @param g the Cooperative Group for which the initial slot is needed\n-     * @param k The key to get the slot for\n-     * @param hash The unary callable used to hash the key\n-     * @return Pointer to the initial slot for `k`\n-     */\n-    template <typename CG, typename ProbeKey, typename Hash>\n-    __device__ const_iterator initial_slot(CG const& g, ProbeKey const& k, Hash hash) const noexcept\n+    __device__ iterator initial_slot(CG const& g, ProbeKey const& k, Hash hash) const noexcept\n     {\n       return &slots_[(hash(k) + g.thread_rank()) % capacity_];\n     }\n@@ -565,7 +499,7 @@ class static_map {\n      * @return The next slot after `s`\n      */\n     template <typename CG>\n-    __device__ iterator next_slot(CG const& g, iterator s) noexcept\n+    __device__ iterator next_slot(CG const& g, iterator s) const noexcept\n     {\n       uint32_t index = s - slots_;\n       return &slots_[(index + g.size()) % capacity_];\n@@ -643,22 +577,16 @@ class static_map {\n      *\n      * @return The sentinel value used to represent an empty key slot\n      */\n-    __host__ __device__ Key get_empty_key_sentinel() const noexcept { return empty_key_sentinel_; }\n+    static constexpr Key get_empty_key_sentinel() { return empty_key_sentinel; }\n \n     /**\n      * @brief Gets the sentinel value used to represent an empty value slot.\n      *\n      * @return The sentinel value used to represent an empty value slot\n      */\n-    __host__ __device__ Value get_empty_value_sentinel() const noexcept\n-    {\n-      return empty_value_sentinel_;\n-    }\n+    static constexpr Value get_empty_value_sentinel() { return empty_value_sentinel; }\n \n-    __host__ __device__ Key get_erased_key_sentinel() const noexcept\n-    {\n-      return erased_key_sentinel_;\n-    }\n+    static constexpr Key get_erased_key_sentinel() { return erased_key_sentinel; }\n \n     /**\n      * @brief Returns iterator to the first slot.\n@@ -690,19 +618,12 @@ class static_map {\n      */\n     __device__ const_iterator begin_slot() const noexcept { return slots_; }\n \n-    /**\n-     * @brief Returns a const_iterator to one past the last slot.\n-     *\n-     * @return A const_iterator to one past the last slot\n-     */\n-    __host__ __device__ const_iterator end_slot() const noexcept { return slots_ + capacity_; }\n-\n     /**\n      * @brief Returns an iterator to one past the last slot.\n      *\n      * @return An iterator to one past the last slot\n      */\n-    __host__ __device__ iterator end_slot() noexcept { return slots_ + capacity_; }\n+    __host__ __device__ iterator end_slot() const noexcept { return slots_ + capacity_; }\n \n     /**\n      * @brief Returns a const_iterator to one past the last slot.\n@@ -712,17 +633,7 @@ class static_map {\n      *\n      * @return A const_iterator to one past the last slot\n      */\n-    __host__ __device__ const_iterator end() const noexcept { return end_slot(); }\n-\n-    /**\n-     * @brief Returns an iterator to one past the last slot.\n-     *\n-     * `end()` calls `end_slot()` and is provided for convenience for those familiar with checking\n-     * an iterator returned from `find()` against the `end()` iterator.\n-     *\n-     * @return An iterator to one past the last slot\n-     */\n-    __host__ __device__ iterator end() noexcept { return end_slot(); }\n+    __host__ __device__ iterator end() const noexcept { return end_slot(); }\n   };\n \n  public:\n@@ -746,7 +657,7 @@ class static_map {\n    *                  });\n    * \\endcode\n    */\n-  class device_mutable_view : public device_view_base {\n+  class __align__(16) device_mutable_view : public device_view_base {\n    public:\n     using value_type  = typename device_view_base::value_type;   ///< Type of key/value pairs\n     using key_type    = typename device_view_base::key_type;     ///< Key type\n@@ -764,40 +675,14 @@ class static_map {\n      *\n      * @param slots Pointer to beginning of initialized slots array\n      * @param capacity The number of slots viewed by this object\n-     * @param empty_key_sentinel The reserved value for keys to represent empty slots\n-     * @param empty_value_sentinel The reserved value for mapped values to\n-     * represent empty slots\n      */\n     __host__ __device__\n     device_mutable_view(pair_atomic_type* slots,\n-                        std::size_t capacity,\n-                        sentinel::empty_key<Key> empty_key_sentinel,\n-                        sentinel::empty_value<Value> empty_value_sentinel) noexcept\n-      : device_view_base{slots, capacity, empty_key_sentinel, empty_value_sentinel}\n+                        std::size_t capacity) noexcept\n+      : device_view_base{slots, capacity}\n     {\n     }\n \n-    /**\n-     * @brief Constructs a mutable view of the first `capacity` slots of the\n-     * slots array pointed to by `slots`.\n-     *\n-     * @param slots Pointer to beginning of initialized slots array\n-     * @param capacity The number of slots viewed by this object\n-     * @param empty_key_sentinel The reserved value for keys to represent empty slots\n-     * @param empty_value_sentinel The reserved value for mapped values to represent empty slots\n-     * @param erased_key_sentinel The reserved value for keys to represent erased slots\n-     */\n-    __host__ __device__ device_mutable_view(pair_atomic_type* slots,\n-                                            std::size_t capacity,\n-                                            sentinel::empty_key<Key> empty_key_sentinel,\n-                                            sentinel::empty_value<Value> empty_value_sentinel,\n-                                            sentinel::erased_key<Key> erased_key_sentinel) noexcept\n-      : device_view_base{\n-          slots, capacity, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel}\n-    {\n-    }\n-\n-   private:\n     /**\n      * @brief Enumeration of the possible results of attempting to insert into a hash bucket.\n      */\n@@ -860,64 +745,6 @@ class static_map {\n                                                  Key expected_key) noexcept;\n \n    public:\n-    /**\n-     * @brief Given a slot pointer `slots`, initializes the first `capacity` slots with the given\n-     * sentinel values and returns a `device_mutable_view` object of those slots.\n-     *\n-     * @tparam CG The type of the cooperative thread group\n-     *\n-     * @param g The cooperative thread group used to copy the slots\n-     * @param slots Pointer to the hash map slots\n-     * @param capacity The total number of slots in the map\n-     * @param empty_key_sentinel The reserved value for keys to represent empty slots\n-     * @param empty_value_sentinel The reserved value for mapped values to represent empty slots\n-     * @return A device_mutable_view object based on the given parameters\n-     */\n-    template <typename CG>\n-    __device__ static device_mutable_view make_from_uninitialized_slots(\n-      CG const& g,\n-      pair_atomic_type* slots,\n-      std::size_t capacity,\n-      sentinel::empty_key<Key> empty_key_sentinel,\n-      sentinel::empty_value<Value> empty_value_sentinel) noexcept\n-    {\n-      device_view_base::initialize_slots(\n-        g, slots, capacity, empty_key_sentinel.value, empty_value_sentinel.value);\n-      return device_mutable_view{slots,\n-                                 capacity,\n-                                 empty_key_sentinel,\n-                                 empty_value_sentinel,\n-                                 sentinel::erased_key<Key>{empty_key_sentinel.value}};\n-    }\n-\n-    /**\n-     * @brief Given a slot pointer `slots`, initializes the first `capacity` slots with the given\n-     * sentinel values and returns a `device_mutable_view` object of those slots.\n-     *\n-     * @tparam CG The type of the cooperative thread group\n-     *\n-     * @param g The cooperative thread group used to copy the slots\n-     * @param slots Pointer to the hash map slots\n-     * @param capacity The total number of slots in the map\n-     * @param empty_key_sentinel The reserved value for keys to represent empty slots\n-     * @param empty_value_sentinel The reserved value for mapped values to represent empty slots\n-     * @param erased_key_sentinel The reserved value for keys to represent erased slots\n-     * @return A device_mutable_view object based on the given parameters\n-     */\n-    template <typename CG>\n-    __device__ static device_mutable_view make_from_uninitialized_slots(\n-      CG const& g,\n-      pair_atomic_type* slots,\n-      std::size_t capacity,\n-      sentinel::empty_key<Key> empty_key_sentinel,\n-      sentinel::empty_value<Value> empty_value_sentinel,\n-      sentinel::erased_key<Key> erased_key_sentinel) noexcept\n-    {\n-      device_view_base::initialize_slots(\n-        g, slots, capacity, empty_key_sentinel, empty_value_sentinel);\n-      return device_mutable_view{\n-        slots, capacity, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel};\n-    }\n \n     /**\n      * @brief Inserts the specified key/value pair into the map.\n@@ -1047,7 +874,7 @@ class static_map {\n    * value.\n    *\n    */\n-  class device_view : public device_view_base {\n+  class __align__(16) device_view : public device_view_base {\n    public:\n     using value_type  = typename device_view_base::value_type;   ///< Type of key/value pairs\n     using key_type    = typename device_view_base::key_type;     ///< Key type\n@@ -1065,34 +892,10 @@ class static_map {\n      *\n      * @param slots Pointer to beginning of initialized slots array\n      * @param capacity The number of slots viewed by this object\n-     * @param empty_key_sentinel The reserved value for keys to represent empty slots\n-     * @param empty_value_sentinel The reserved value for mapped values to represent empty slots\n      */\n     __host__ __device__ device_view(pair_atomic_type* slots,\n-                                    std::size_t capacity,\n-                                    sentinel::empty_key<Key> empty_key_sentinel,\n-                                    sentinel::empty_value<Value> empty_value_sentinel) noexcept\n-      : device_view_base{slots, capacity, empty_key_sentinel, empty_value_sentinel}\n-    {\n-    }\n-\n-    /**\n-     * @brief Construct a view of the first `capacity` slots of the\n-     * slots array pointed to by `slots`.\n-     *\n-     * @param slots Pointer to beginning of initialized slots array\n-     * @param capacity The number of slots viewed by this object\n-     * @param empty_key_sentinel The reserved value for keys to represent empty slots\n-     * @param empty_value_sentinel The reserved value for mapped values to represent empty slots\n-     * @param erased_key_sentinel The reserved value for keys to represent erased slots\n-     */\n-    __host__ __device__ device_view(pair_atomic_type* slots,\n-                                    std::size_t capacity,\n-                                    sentinel::empty_key<Key> empty_key_sentinel,\n-                                    sentinel::empty_value<Value> empty_value_sentinel,\n-                                    sentinel::erased_key<Key> erased_key_sentinel) noexcept\n-      : device_view_base{\n-          slots, capacity, empty_key_sentinel, empty_value_sentinel, erased_key_sentinel}\n+                                    std::size_t capacity) noexcept\n+      : device_view_base{slots, capacity}\n     {\n     }\n \n@@ -1103,84 +906,8 @@ class static_map {\n      */\n     __host__ __device__ explicit device_view(device_mutable_view mutable_map)\n       : device_view_base{mutable_map.get_slots(),\n-                         mutable_map.get_capacity(),\n-                         sentinel::empty_key<Key>{mutable_map.get_empty_key_sentinel()},\n-                         sentinel::empty_value<Value>{mutable_map.get_empty_value_sentinel()},\n-                         sentinel::erased_key<Key>{mutable_map.get_erased_key_sentinel()}}\n-    {\n-    }\n-\n-    /**\n-     * @brief Makes a copy of given `device_view` using non-owned memory.\n-     *\n-     * This function is intended to be used to create shared memory copies of small static maps,\n-     * although global memory can be used as well.\n-     *\n-     * Example:\n-     * @code{.cpp}\n-     * template <typename MapType, int CAPACITY>\n-     * __global__ void use_device_view(const typename MapType::device_view device_view,\n-     *                                 map_key_t const* const keys_to_search,\n-     *                                 map_value_t* const values_found,\n-     *                                 const size_t number_of_elements)\n-     * {\n-     *     const size_t index = blockIdx.x * blockDim.x + threadIdx.x;\n-     *\n-     *     __shared__ typename MapType::pair_atomic_type sm_buffer[CAPACITY];\n-     *\n-     *     auto g = cg::this_thread_block();\n-     *\n-     *     const map_t::device_view sm_static_map = device_view.make_copy(g,\n-     *                                                                    sm_buffer);\n-     *\n-     *     for (size_t i = g.thread_rank(); i < number_of_elements; i += g.size())\n-     *     {\n-     *         values_found[i] = sm_static_map.find(keys_to_search[i])->second;\n-     *     }\n-     * }\n-     * @endcode\n-     *\n-     * @tparam CG The type of the cooperative thread group\n-     * @param g The ooperative thread group used to copy the slots\n-     * @param source_device_view `device_view` to copy from\n-     * @param memory_to_use Array large enough to support `capacity` elements. Object does not take\n-     * the ownership of the memory\n-     * @return Copy of passed `device_view`\n-     */\n-    template <typename CG>\n-    __device__ static device_view make_copy(CG g,\n-                                            pair_atomic_type* const memory_to_use,\n-                                            device_view source_device_view) noexcept\n+                         mutable_map.get_capacity()}\n     {\n-#if defined(CUDA_HAS_CUDA_BARRIER)\n-      __shared__ cuda::barrier<cuda::thread_scope::thread_scope_block> barrier;\n-      if (g.thread_rank() == 0) { init(&barrier, g.size()); }\n-      g.sync();\n-\n-      cuda::memcpy_async(g,\n-                         memory_to_use,\n-                         source_device_view.get_slots(),\n-                         sizeof(pair_atomic_type) * source_device_view.get_capacity(),\n-                         barrier);\n-\n-      barrier.arrive_and_wait();\n-#else\n-      pair_atomic_type const* const slots_ptr = source_device_view.get_slots();\n-      for (std::size_t i = g.thread_rank(); i < source_device_view.get_capacity(); i += g.size()) {\n-        new (&memory_to_use[i].first)\n-          atomic_key_type{slots_ptr[i].first.load(cuda::memory_order_relaxed)};\n-        new (&memory_to_use[i].second)\n-          atomic_mapped_type{slots_ptr[i].second.load(cuda::memory_order_relaxed)};\n-      }\n-      g.sync();\n-#endif\n-\n-      return device_view(\n-        memory_to_use,\n-        source_device_view.get_capacity(),\n-        sentinel::empty_key<Key>{source_device_view.get_empty_key_sentinel()},\n-        sentinel::empty_value<Value>{source_device_view.get_empty_value_sentinel()},\n-        sentinel::erased_key<Key>{source_device_view.get_erased_key_sentinel()});\n     }\n \n     /**\n@@ -1248,32 +975,6 @@ class static_map {\n               typename Hash     = cuco::detail::MurmurHash3_32<key_type>,\n               typename KeyEqual = thrust::equal_to<key_type>>\n     __device__ iterator\n-    find(CG g, Key const& k, Hash hash = Hash{}, KeyEqual key_equal = KeyEqual{}) noexcept;\n-\n-    /**\n-     * @brief Finds the value corresponding to the key `k`.\n-     *\n-     * Returns a const_iterator to the pair whose key is equivalent to `k`.\n-     * If no such pair exists, returns `end()`. Uses the CUDA Cooperative Groups API to\n-     * to leverage multiple threads to perform a single find. This provides a\n-     * significant boost in throughput compared to the non Cooperative Group\n-     * `find` at moderate to high load factors.\n-     *\n-     * @tparam CG Cooperative Group type\n-     * @tparam Hash Unary callable type\n-     * @tparam KeyEqual Binary callable type\n-     * @param g The Cooperative Group used to perform the find\n-     * @param k The key to search for\n-     * @param hash The unary callable used to hash the key\n-     * @param key_equal The binary callable used to compare two keys\n-     * for equality\n-     * @return An iterator to the position at which the key/value pair\n-     * containing `k` was inserted\n-     */\n-    template <typename CG,\n-              typename Hash     = cuco::detail::MurmurHash3_32<key_type>,\n-              typename KeyEqual = thrust::equal_to<key_type>>\n-    __device__ const_iterator\n     find(CG g, Key const& k, Hash hash = Hash{}, KeyEqual key_equal = KeyEqual{}) const noexcept;\n \n     /**\n@@ -1370,21 +1071,21 @@ class static_map {\n    *\n    * @return The sentinel value used to represent an empty key slot\n    */\n-  Key get_empty_key_sentinel() const noexcept { return empty_key_sentinel_; }\n+  static constexpr Key get_empty_key_sentinel() { return empty_key_sentinel_; }\n \n   /**\n    * @brief Gets the sentinel value used to represent an empty value slot.\n    *\n    * @return The sentinel value used to represent an empty value slot\n    */\n-  Value get_empty_value_sentinel() const noexcept { return empty_value_sentinel_; }\n+  static constexpr Value get_empty_value_sentinel() { return empty_value_sentinel_; }\n \n   /**\n    * @brief Gets the sentinel value used to represent an erased value slot.\n    *\n    * @return The sentinel value used to represent an erased value slot\n    */\n-  Key get_erased_key_sentinel() const noexcept { return erased_key_sentinel_; }\n+  static constexpr Key get_erased_key_sentinel() { return erased_key_sentinel_; }\n \n   /**\n    * @brief Constructs a device_view object based on the members of the `static_map` object.\n@@ -1394,10 +1095,7 @@ class static_map {\n   device_view get_device_view() const noexcept\n   {\n     return device_view(slots_,\n-                       capacity_,\n-                       sentinel::empty_key<Key>{empty_key_sentinel_},\n-                       sentinel::empty_value<Value>{empty_value_sentinel_},\n-                       sentinel::erased_key<Key>{erased_key_sentinel_});\n+                       capacity_);\n   }\n \n   /**\n@@ -1408,19 +1106,15 @@ class static_map {\n   device_mutable_view get_device_mutable_view() const noexcept\n   {\n     return device_mutable_view(slots_,\n-                               capacity_,\n-                               sentinel::empty_key<Key>{empty_key_sentinel_},\n-                               sentinel::empty_value<Value>{empty_value_sentinel_},\n-                               sentinel::erased_key<Key>{erased_key_sentinel_});\n+                               capacity_);\n   }\n \n- private:\n   pair_atomic_type* slots_{nullptr};            ///< Pointer to flat slots storage\n   std::size_t capacity_{};                      ///< Total number of slots\n   std::size_t size_{};                          ///< Number of keys in map\n-  Key empty_key_sentinel_{};                    ///< Key value that represents an empty slot\n-  Value empty_value_sentinel_{};                ///< Initial value of empty slot\n-  Key erased_key_sentinel_{};                   ///< Key value that represents an erased slot\n+  static constexpr Key empty_key_sentinel_ = empty_key_sentinel;                    ///< Key value that represents an empty slot\n+  static constexpr Value empty_value_sentinel_ = empty_value_sentinel;                ///< Initial value of empty slot\n+  static constexpr Key erased_key_sentinel_ = erased_key_sentinel;                   ///< Key value that represents an erased slot\n   atomic_ctr_type* num_successes_{};            ///< Number of successfully inserted keys on insert\n   slot_allocator_type slot_allocator_{};        ///< Allocator used to allocate slots\n   counter_allocator_type counter_allocator_{};  ///< Allocator used to allocate `num_successes_`\ndiff --git a/include/cuco/traits.hpp b/include/cuco/traits.hpp\nindex 445a40d..07fe954 100644\n--- a/include/cuco/traits.hpp\n+++ b/include/cuco/traits.hpp\n@@ -17,6 +17,7 @@\n #pragma once\n \n #include <type_traits>\n+#include <cuco/detail/pair.cuh>\n \n namespace cuco {\n \n"
  },
  {
    "path": "third_party/cuco.BUILD",
    "content": "# May be deprecated later\nlicenses([\"notice\"])\n\nload(\"@local_config_cuda//cuda:build_defs.bzl\", \"cuda_default_copts\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"LICENSE\"])\n\ncc_library(\n    name = \"cuco_hash_table\",\n    hdrs = glob([\"include/**\"]),\n    include_prefix = \"third_party/cuco_hash_table\",\n    strip_include_prefix = \"include\",\n    includes = [\n        \"include\",\n    ],\n    copts = [\n        \"-std=c++17\",\n    ],\n    deps = [\n        \"@local_config_cuda//cuda:cuda_headers\",\n    ],\n)"
  },
  {
    "path": "third_party/dpssdk.BUILD",
    "content": "load(\"@pip_deps//:requirements.bzl\", \"requirement\")\n\npy_library(\n    name = \"dpssdk\",\n    srcs = glob([\"dpstoken/*.py\"]),\n    imports = [\"dpstoken/\"],\n    visibility = [\"//visibility:public\"],\n    deps= [requirement(\"cryptography\")]\n)"
  },
  {
    "path": "third_party/eigen3/README.txt",
    "content": "How to update the patch files?\n\nFirst, copy tensorflow/third_party/eigen3/gpu_packet_math.patch into the update.\nThen, apply changes on files in unsupported/Eigen/CXX11/src/Tensor.\n"
  },
  {
    "path": "third_party/eigen3/eigen_gcc6.patch",
    "content": "diff -ru a/Eigen/src/Geometry/arch/Geometry_SSE.h b/Eigen/src/Geometry/arch/Geometry_SSE.h\n--- a/Eigen/src/Geometry/arch/Geometry_SSE.h\n+++ b/Eigen/src/Geometry/arch/Geometry_SSE.h\n@@ -33,13 +33,14 @@\n     Packet4f b = be.template packet<BAlignment,Packet4f>(0);\n     Packet4f s1 = pmul(vec4f_swizzle1(a,1,2,0,2),vec4f_swizzle1(b,2,0,1,2));\n     Packet4f s2 = pmul(vec4f_swizzle1(a,3,3,3,1),vec4f_swizzle1(b,0,1,2,1));\n-    pstoret<float,Packet4f,ResAlignment>(\n-              &res.x(),\n-              padd(psub(pmul(a,vec4f_swizzle1(b,3,3,3,3)),\n-                                    pmul(vec4f_swizzle1(a,2,0,1,0),\n-                                               vec4f_swizzle1(b,1,2,0,0))),\n-                         pxor(mask,padd(s1,s2))));\n-    \n+    pstoret<float, Packet4f, ResAlignment>(\n+        &res.x(),\n+        padd<Packet4f>(\n+            psub<Packet4f>(pmul<Packet4f>(a, vec4f_swizzle1(b, 3, 3, 3, 3)),\n+                           pmul<Packet4f>(vec4f_swizzle1(a, 2, 0, 1, 0),\n+                                          vec4f_swizzle1(b, 1, 2, 0, 0))),\n+            pxor<Packet4f>(mask, padd(s1, s2))));\n+\n     return res;\n   }\n };\ndiff -ru a/Eigen/src/Core/GenericPacketMath.h b/Eigen/src/Core/GenericPacketMath.h\n--- a/Eigen/src/Core/GenericPacketMath.h\n+++ b/Eigen/src/Core/GenericPacketMath.h\n@@ -255,49 +255,43 @@\n   return std::complex<RealScalar>(b, b);\n }\n \n-template <typename Packet, typename Op>\n-EIGEN_DEVICE_FUNC inline Packet bitwise_helper(const Packet& a, const Packet& b, Op op) {\n+/** \\internal \\returns the bitwise and of \\a a and \\a b */\n+template<typename Packet> EIGEN_DEVICE_FUNC inline Packet\n+pand(const Packet& a, const Packet& b) {\n   const unsigned char* a_ptr = reinterpret_cast<const unsigned char*>(&a);\n   const unsigned char* b_ptr = reinterpret_cast<const unsigned char*>(&b);\n   Packet c;\n   unsigned char* c_ptr = reinterpret_cast<unsigned char*>(&c);\n   for (size_t i = 0; i < sizeof(Packet); ++i) {\n-    *c_ptr++ = op(*a_ptr++, *b_ptr++);\n+    *c_ptr++ = *a_ptr++ & *b_ptr++;\n   }\n   return c;\n }\n \n-/** \\internal \\returns the bitwise and of \\a a and \\a b */\n-template<typename Packet> EIGEN_DEVICE_FUNC inline Packet\n-pand(const Packet& a, const Packet& b) {\n-#if defined(EIGEN_HIP_DEVICE_COMPILE)\n-  return bitwise_helper(a ,b, std::bit_and<unsigned char>());\n-#else\n-  EIGEN_USING_STD(bit_and);\n-  return bitwise_helper(a ,b, bit_and<unsigned char>());\n-#endif\n-}\n-\n /** \\internal \\returns the bitwise or of \\a a and \\a b */\n template<typename Packet> EIGEN_DEVICE_FUNC inline Packet\n por(const Packet& a, const Packet& b) {\n-#if defined(EIGEN_HIP_DEVICE_COMPILE)\n-  return bitwise_helper(a ,b, std::bit_or<unsigned char>());\n-#else\n-  EIGEN_USING_STD(bit_or);\n-  return bitwise_helper(a ,b, bit_or<unsigned char>());\n-#endif\n+  const unsigned char* a_ptr = reinterpret_cast<const unsigned char*>(&a);\n+  const unsigned char* b_ptr = reinterpret_cast<const unsigned char*>(&b);\n+  Packet c;\n+  unsigned char* c_ptr = reinterpret_cast<unsigned char*>(&c);\n+  for (size_t i = 0; i < sizeof(Packet); ++i) {\n+    *c_ptr++ = *a_ptr++ | *b_ptr++;\n+  }\n+  return c;\n }\n \n /** \\internal \\returns the bitwise xor of \\a a and \\a b */\n template<typename Packet> EIGEN_DEVICE_FUNC inline Packet\n pxor(const Packet& a, const Packet& b) {\n-#if defined(EIGEN_HIP_DEVICE_COMPILE)\n-  return bitwise_helper(a ,b, std::bit_xor<unsigned char>());\n-#else\n-  EIGEN_USING_STD(bit_xor);\n-  return bitwise_helper(a ,b, bit_xor<unsigned char>());\n-#endif\n+  const unsigned char* a_ptr = reinterpret_cast<const unsigned char*>(&a);\n+  const unsigned char* b_ptr = reinterpret_cast<const unsigned char*>(&b);\n+  Packet c;\n+  unsigned char* c_ptr = reinterpret_cast<unsigned char*>(&c);\n+  for (size_t i = 0; i < sizeof(Packet); ++i) {\n+    *c_ptr++ = *a_ptr++ ^ *b_ptr++;\n+  }\n+  return c;\n }\n \n /** \\internal \\returns the bitwise and of \\a a and not \\a b */\ndiff --git a/unsupported/Eigen/CXX11/src/Tensor/TensorImagePatch.h b/unsupported/Eigen/CXX11/src/Tensor/TensorImagePatch.h\nindex 49d1004..b160105 100644\n--- a/unsupported/Eigen/CXX11/src/Tensor/TensorImagePatch.h\n+++ b/unsupported/Eigen/CXX11/src/Tensor/TensorImagePatch.h\n@@ -543,7 +543,8 @@ struct TensorEvaluator<const TensorImagePatchOp<Rows, Cols, ArgType>, Device>\n     EIGEN_ALIGN_MAX typename internal::remove_const<CoeffReturnType>::type values[PacketSize];\n     EIGEN_UNROLL_LOOP\n     for (int i = 0; i < PacketSize; ++i) {\n-      values[i] = coeff(index+i);\n+      Self::CoeffReturnType a = coeff(index+i);\n+      values[i] = a;\n     }\n     PacketReturnType rslt = internal::pload<PacketReturnType>(values);\n     return rslt;\ndiff --git a/unsupported/Eigen/CXX11/src/Tensor/TensorPadding.h b/unsupported/Eigen/CXX11/src/Tensor/TensorPadding.h\nindex 561666c..7f94cf1 100644\n--- a/unsupported/Eigen/CXX11/src/Tensor/TensorPadding.h\n+++ b/unsupported/Eigen/CXX11/src/Tensor/TensorPadding.h\n@@ -683,7 +683,8 @@ struct TensorEvaluator<const TensorPaddingOp<PaddingDimensions, ArgType>, Device\n     EIGEN_ALIGN_MAX typename internal::remove_const<CoeffReturnType>::type values[PacketSize];\n     EIGEN_UNROLL_LOOP\n     for (int i = 0; i < PacketSize; ++i) {\n-      values[i] = coeff(index+i);\n+      CoeffReturnType a = coeff(index+i);\n+      values[i] = a;\n     }\n     PacketReturnType rslt = internal::pload<PacketReturnType>(values);\n     return rslt;\ndiff --git a/unsupported/Eigen/CXX11/src/Tensor/TensorReduction.h b/unsupported/Eigen/CXX11/src/Tensor/TensorReduction.h\nindex 9b0eb3e..f1ac6dc 100644\n--- a/unsupported/Eigen/CXX11/src/Tensor/TensorReduction.h\n+++ b/unsupported/Eigen/CXX11/src/Tensor/TensorReduction.h\n@@ -815,8 +815,9 @@ struct TensorReductionEvaluatorBase<const TensorReductionOp<Op, Dims, ArgType, M\n       const Index firstIndex = firstInput(index);\n       for (Index i = 0; i < PacketSize; ++i) {\n         Op reducer(m_reducer);\n-        values[i] = internal::InnerMostDimReducer<Self, Op>::reduce(*this, firstIndex + i * num_values_to_reduce,\n-                                                                    num_values_to_reduce, reducer);\n+        Self::CoeffReturnType a = internal::InnerMostDimReducer<Self, Op>::reduce(*this, firstIndex + i * num_values_to_reduce,\n+                                                                                  num_values_to_reduce, reducer);\n+        values[i] = a;\n       }\n     } else if (PreservingInnerMostDims) {\n       const Index firstIndex = firstInput(index);\n"
  },
  {
    "path": "third_party/gperftools/gperftools.BUILD",
    "content": "load(\"@rules_foreign_cc//tools/build_defs:configure.bzl\", \"configure_make\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n    name = \"src\",\n    srcs = glob([\"**\"]),\n)\n\nconfigure_make(\n    name = \"libtcmalloc\",\n    binaries = [\"pprof\"],\n    # In cuda env, `-lm` is missing so we add here manually.\n    configure_env_vars = {\"LDFLAGS\": \"-lm\"},\n    lib_source = \":src\",\n    shared_libraries = [\"libtcmalloc.so\"],\n    static_libraries = [\"libtcmalloc.a\"],\n)\n"
  },
  {
    "path": "third_party/gperftools/gperftools.patch",
    "content": "diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc\n--- a/src/heap-profiler.cc\n+++ b/src/heap-profiler.cc\n@@ -314,8 +314,94 @@ static void MaybeDumpProfileLocked() {\n   }\n }\n \n+// Sample related functionalities\n+DEFINE_double(\n+    heap_profile_sample_ratio, EnvToDouble(\"HEAP_PROFILE_SAMPLE_RATIO\", 1.0),\n+    \"If non-one, we will sample the pointer during the heap profiling.\");\n+\n+static uint32_t CRCTable[256] = {\n+    0x00000000u, 0x77073096u, 0xee0e612cu, 0x990951bau, 0x076dc419u,\n+    0x706af48fu, 0xe963a535u, 0x9e6495a3u, 0x0edb8832u, 0x79dcb8a4u,\n+    0xe0d5e91eu, 0x97d2d988u, 0x09b64c2bu, 0x7eb17cbdu, 0xe7b82d07u,\n+    0x90bf1d91u, 0x1db71064u, 0x6ab020f2u, 0xf3b97148u, 0x84be41deu,\n+    0x1adad47du, 0x6ddde4ebu, 0xf4d4b551u, 0x83d385c7u, 0x136c9856u,\n+    0x646ba8c0u, 0xfd62f97au, 0x8a65c9ecu, 0x14015c4fu, 0x63066cd9u,\n+    0xfa0f3d63u, 0x8d080df5u, 0x3b6e20c8u, 0x4c69105eu, 0xd56041e4u,\n+    0xa2677172u, 0x3c03e4d1u, 0x4b04d447u, 0xd20d85fdu, 0xa50ab56bu,\n+    0x35b5a8fau, 0x42b2986cu, 0xdbbbc9d6u, 0xacbcf940u, 0x32d86ce3u,\n+    0x45df5c75u, 0xdcd60dcfu, 0xabd13d59u, 0x26d930acu, 0x51de003au,\n+    0xc8d75180u, 0xbfd06116u, 0x21b4f4b5u, 0x56b3c423u, 0xcfba9599u,\n+    0xb8bda50fu, 0x2802b89eu, 0x5f058808u, 0xc60cd9b2u, 0xb10be924u,\n+    0x2f6f7c87u, 0x58684c11u, 0xc1611dabu, 0xb6662d3du, 0x76dc4190u,\n+    0x01db7106u, 0x98d220bcu, 0xefd5102au, 0x71b18589u, 0x06b6b51fu,\n+    0x9fbfe4a5u, 0xe8b8d433u, 0x7807c9a2u, 0x0f00f934u, 0x9609a88eu,\n+    0xe10e9818u, 0x7f6a0dbbu, 0x086d3d2du, 0x91646c97u, 0xe6635c01u,\n+    0x6b6b51f4u, 0x1c6c6162u, 0x856530d8u, 0xf262004eu, 0x6c0695edu,\n+    0x1b01a57bu, 0x8208f4c1u, 0xf50fc457u, 0x65b0d9c6u, 0x12b7e950u,\n+    0x8bbeb8eau, 0xfcb9887cu, 0x62dd1ddfu, 0x15da2d49u, 0x8cd37cf3u,\n+    0xfbd44c65u, 0x4db26158u, 0x3ab551ceu, 0xa3bc0074u, 0xd4bb30e2u,\n+    0x4adfa541u, 0x3dd895d7u, 0xa4d1c46du, 0xd3d6f4fbu, 0x4369e96au,\n+    0x346ed9fcu, 0xad678846u, 0xda60b8d0u, 0x44042d73u, 0x33031de5u,\n+    0xaa0a4c5fu, 0xdd0d7cc9u, 0x5005713cu, 0x270241aau, 0xbe0b1010u,\n+    0xc90c2086u, 0x5768b525u, 0x206f85b3u, 0xb966d409u, 0xce61e49fu,\n+    0x5edef90eu, 0x29d9c998u, 0xb0d09822u, 0xc7d7a8b4u, 0x59b33d17u,\n+    0x2eb40d81u, 0xb7bd5c3bu, 0xc0ba6cadu, 0xedb88320u, 0x9abfb3b6u,\n+    0x03b6e20cu, 0x74b1d29au, 0xead54739u, 0x9dd277afu, 0x04db2615u,\n+    0x73dc1683u, 0xe3630b12u, 0x94643b84u, 0x0d6d6a3eu, 0x7a6a5aa8u,\n+    0xe40ecf0bu, 0x9309ff9du, 0x0a00ae27u, 0x7d079eb1u, 0xf00f9344u,\n+    0x8708a3d2u, 0x1e01f268u, 0x6906c2feu, 0xf762575du, 0x806567cbu,\n+    0x196c3671u, 0x6e6b06e7u, 0xfed41b76u, 0x89d32be0u, 0x10da7a5au,\n+    0x67dd4accu, 0xf9b9df6fu, 0x8ebeeff9u, 0x17b7be43u, 0x60b08ed5u,\n+    0xd6d6a3e8u, 0xa1d1937eu, 0x38d8c2c4u, 0x4fdff252u, 0xd1bb67f1u,\n+    0xa6bc5767u, 0x3fb506ddu, 0x48b2364bu, 0xd80d2bdau, 0xaf0a1b4cu,\n+    0x36034af6u, 0x41047a60u, 0xdf60efc3u, 0xa867df55u, 0x316e8eefu,\n+    0x4669be79u, 0xcb61b38cu, 0xbc66831au, 0x256fd2a0u, 0x5268e236u,\n+    0xcc0c7795u, 0xbb0b4703u, 0x220216b9u, 0x5505262fu, 0xc5ba3bbeu,\n+    0xb2bd0b28u, 0x2bb45a92u, 0x5cb36a04u, 0xc2d7ffa7u, 0xb5d0cf31u,\n+    0x2cd99e8bu, 0x5bdeae1du, 0x9b64c2b0u, 0xec63f226u, 0x756aa39cu,\n+    0x026d930au, 0x9c0906a9u, 0xeb0e363fu, 0x72076785u, 0x05005713u,\n+    0x95bf4a82u, 0xe2b87a14u, 0x7bb12baeu, 0x0cb61b38u, 0x92d28e9bu,\n+    0xe5d5be0du, 0x7cdcefb7u, 0x0bdbdf21u, 0x86d3d2d4u, 0xf1d4e242u,\n+    0x68ddb3f8u, 0x1fda836eu, 0x81be16cdu, 0xf6b9265bu, 0x6fb077e1u,\n+    0x18b74777u, 0x88085ae6u, 0xff0f6a70u, 0x66063bcau, 0x11010b5cu,\n+    0x8f659effu, 0xf862ae69u, 0x616bffd3u, 0x166ccf45u, 0xa00ae278u,\n+    0xd70dd2eeu, 0x4e048354u, 0x3903b3c2u, 0xa7672661u, 0xd06016f7u,\n+    0x4969474du, 0x3e6e77dbu, 0xaed16a4au, 0xd9d65adcu, 0x40df0b66u,\n+    0x37d83bf0u, 0xa9bcae53u, 0xdebb9ec5u, 0x47b2cf7fu, 0x30b5ffe9u,\n+    0xbdbdf21cu, 0xcabac28au, 0x53b39330u, 0x24b4a3a6u, 0xbad03605u,\n+    0xcdd70693u, 0x54de5729u, 0x23d967bfu, 0xb3667a2eu, 0xc4614ab8u,\n+    0x5d681b02u, 0x2a6f2b94u, 0xb40bbe37u, 0xc30c8ea1u, 0x5a05df1bu,\n+    0x2d02ef8du};\n+\n+static uint32_t CRC32(const void* ptr) {\n+  size_t n = (size_t)ptr;\n+  uint32_t crc32 = 0xFFFFFFFFu;\n+\n+  for (size_t i = 0; i < __WORDSIZE / 8; i++) {\n+    const uint32_t data = n & 0xff;\n+    const uint32_t lookupIndex = (crc32 ^ data) & 0xff;\n+    crc32 = (crc32 >> 8) ^ CRCTable[lookupIndex];\n+    n >>= 8;\n+  }\n+\n+  // Finalize the CRC-32 value by inverting all the bits\n+  crc32 ^= 0xFFFFFFFFu;\n+  return crc32;\n+}\n+\n+static int ShouldRecord(const void* ptr) {\n+  static unsigned threshold =\n+      (unsigned)(0xFFFFFFFFu * FLAGS_heap_profile_sample_ratio);\n+  if (CRC32(ptr) <= threshold) {\n+    return 1;\n+  } else {\n+    return 0;\n+  }\n+}\n+\n // Record an allocation in the profile.\n static void RecordAlloc(const void* ptr, size_t bytes, int skip_count) {\n+  if (!ShouldRecord(ptr)) return;\n   // Take the stack trace outside the critical section.\n   void* stack[HeapProfileTable::kMaxStackDepth];\n   int depth = HeapProfileTable::GetCallerStackTrace(skip_count + 1, stack);\n@@ -328,6 +414,7 @@ static void RecordAlloc(const void* ptr, size_t bytes, int skip_count) {\n \n // Record a deallocation in the profile.\n static void RecordFree(const void* ptr) {\n+  if (!ShouldRecord(ptr)) return;\n   SpinLockHolder l(&heap_lock);\n   if (is_on) {\n     heap_profile->RecordFree(ptr);\n"
  },
  {
    "path": "third_party/half_sourceforge_net/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_library(\n    name = \"half\",\n    hdrs = [\"half.hpp\"],\n)\n"
  },
  {
    "path": "third_party/half_sourceforge_net/half.hpp",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n// half - IEEE 754-based half-precision floating-point library.\n//\n// Copyright (c) 2012-2019 Christian Rau <rauy@users.sourceforge.net>\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation\n// files (the \"Software\"), to deal in the Software without restriction,\n// including without limitation the rights to use, copy,\n// modify, merge, publish, distribute, sublicense, and/or sell copies of the\n// Software, and to permit persons to whom the\n// Software is furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n// DEALINGS IN THE SOFTWARE.\n\n// Version 2.1.0\n\n/// \\file\n/// Main header file for half-precision functionality.\n\n#ifndef HALF_HALF_HPP\n#define HALF_HALF_HPP\n\n#define HALF_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)\n\n#if defined(__INTEL_COMPILER)\n#define HALF_ICC_VERSION __INTEL_COMPILER\n#elif defined(__ICC)\n#define HALF_ICC_VERSION __ICC\n#elif defined(__ICL)\n#define HALF_ICC_VERSION __ICL\n#else\n#define HALF_ICC_VERSION 0\n#endif\n\n// check C++11 language features\n#if defined(__clang__)  // clang\n#if __has_feature(cxx_static_assert) && \\\n    !defined(HALF_ENABLE_CPP11_STATIC_ASSERT)\n#define HALF_ENABLE_CPP11_STATIC_ASSERT 1\n#endif\n#if __has_feature(cxx_constexpr) && !defined(HALF_ENABLE_CPP11_CONSTEXPR)\n#define HALF_ENABLE_CPP11_CONSTEXPR 1\n#endif\n#if __has_feature(cxx_noexcept) && !defined(HALF_ENABLE_CPP11_NOEXCEPT)\n#define HALF_ENABLE_CPP11_NOEXCEPT 1\n#endif\n#if __has_feature(cxx_user_literals) && \\\n    !defined(HALF_ENABLE_CPP11_USER_LITERALS)\n#define HALF_ENABLE_CPP11_USER_LITERALS 1\n#endif\n#if __has_feature(cxx_thread_local) && !defined(HALF_ENABLE_CPP11_THREAD_LOCAL)\n#define HALF_ENABLE_CPP11_THREAD_LOCAL 1\n#endif\n#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L) && \\\n    !defined(HALF_ENABLE_CPP11_LONG_LONG)\n#define HALF_ENABLE_CPP11_LONG_LONG 1\n#endif\n#elif HALF_ICC_VERSION && defined(__INTEL_CXX11_MODE__)  // Intel C++\n#if HALF_ICC_VERSION >= 1500 && !defined(HALF_ENABLE_CPP11_THREAD_LOCAL)\n#define HALF_ENABLE_CPP11_THREAD_LOCAL 1\n#endif\n#if HALF_ICC_VERSION >= 1500 && !defined(HALF_ENABLE_CPP11_USER_LITERALS)\n#define HALF_ENABLE_CPP11_USER_LITERALS 1\n#endif\n#if HALF_ICC_VERSION >= 1400 && !defined(HALF_ENABLE_CPP11_CONSTEXPR)\n#define HALF_ENABLE_CPP11_CONSTEXPR 1\n#endif\n#if HALF_ICC_VERSION >= 1400 && !defined(HALF_ENABLE_CPP11_NOEXCEPT)\n#define HALF_ENABLE_CPP11_NOEXCEPT 1\n#endif\n#if HALF_ICC_VERSION >= 1110 && !defined(HALF_ENABLE_CPP11_STATIC_ASSERT)\n#define HALF_ENABLE_CPP11_STATIC_ASSERT 1\n#endif\n#if HALF_ICC_VERSION >= 1110 && !defined(HALF_ENABLE_CPP11_LONG_LONG)\n#define HALF_ENABLE_CPP11_LONG_LONG 1\n#endif\n#elif defined(__GNUC__)  // gcc\n#if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L\n#if HALF_GCC_VERSION >= 408 && !defined(HALF_ENABLE_CPP11_THREAD_LOCAL)\n#define HALF_ENABLE_CPP11_THREAD_LOCAL 1\n#endif\n#if HALF_GCC_VERSION >= 407 && !defined(HALF_ENABLE_CPP11_USER_LITERALS)\n#define HALF_ENABLE_CPP11_USER_LITERALS 1\n#endif\n#if HALF_GCC_VERSION >= 406 && !defined(HALF_ENABLE_CPP11_CONSTEXPR)\n#define HALF_ENABLE_CPP11_CONSTEXPR 1\n#endif\n#if HALF_GCC_VERSION >= 406 && !defined(HALF_ENABLE_CPP11_NOEXCEPT)\n#define HALF_ENABLE_CPP11_NOEXCEPT 1\n#endif\n#if HALF_GCC_VERSION >= 403 && !defined(HALF_ENABLE_CPP11_STATIC_ASSERT)\n#define HALF_ENABLE_CPP11_STATIC_ASSERT 1\n#endif\n#if !defined(HALF_ENABLE_CPP11_LONG_LONG)\n#define HALF_ENABLE_CPP11_LONG_LONG 1\n#endif\n#endif\n#define HALF_TWOS_COMPLEMENT_INT 1\n#elif defined(_MSC_VER)  // Visual C++\n#if _MSC_VER >= 1900 && !defined(HALF_ENABLE_CPP11_THREAD_LOCAL)\n#define HALF_ENABLE_CPP11_THREAD_LOCAL 1\n#endif\n#if _MSC_VER >= 1900 && !defined(HALF_ENABLE_CPP11_USER_LITERALS)\n#define HALF_ENABLE_CPP11_USER_LITERALS 1\n#endif\n#if _MSC_VER >= 1900 && !defined(HALF_ENABLE_CPP11_CONSTEXPR)\n#define HALF_ENABLE_CPP11_CONSTEXPR 1\n#endif\n#if _MSC_VER >= 1900 && !defined(HALF_ENABLE_CPP11_NOEXCEPT)\n#define HALF_ENABLE_CPP11_NOEXCEPT 1\n#endif\n#if _MSC_VER >= 1600 && !defined(HALF_ENABLE_CPP11_STATIC_ASSERT)\n#define HALF_ENABLE_CPP11_STATIC_ASSERT 1\n#endif\n#if _MSC_VER >= 1310 && !defined(HALF_ENABLE_CPP11_LONG_LONG)\n#define HALF_ENABLE_CPP11_LONG_LONG 1\n#endif\n#define HALF_TWOS_COMPLEMENT_INT 1\n#define HALF_POP_WARNINGS 1\n#pragma warning(push)\n#pragma warning(disable : 4099 4127 4146)  // struct vs class, constant in if,\n                                           // negative unsigned\n#endif\n\n// check C++11 library features\n#include <utility>\n#if defined(_LIBCPP_VERSION)  // libc++\n#if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103\n#ifndef HALF_ENABLE_CPP11_TYPE_TRAITS\n#define HALF_ENABLE_CPP11_TYPE_TRAITS 1\n#endif\n#ifndef HALF_ENABLE_CPP11_CSTDINT\n#define HALF_ENABLE_CPP11_CSTDINT 1\n#endif\n#ifndef HALF_ENABLE_CPP11_CMATH\n#define HALF_ENABLE_CPP11_CMATH 1\n#endif\n#ifndef HALF_ENABLE_CPP11_HASH\n#define HALF_ENABLE_CPP11_HASH 1\n#endif\n#ifndef HALF_ENABLE_CPP11_CFENV\n#define HALF_ENABLE_CPP11_CFENV 1\n#endif\n#endif\n#elif defined(__GLIBCXX__)  // libstdc++\n#if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103\n#ifdef __clang__\n#if __GLIBCXX__ >= 20080606 && !defined(HALF_ENABLE_CPP11_TYPE_TRAITS)\n#define HALF_ENABLE_CPP11_TYPE_TRAITS 1\n#endif\n#if __GLIBCXX__ >= 20080606 && !defined(HALF_ENABLE_CPP11_CSTDINT)\n#define HALF_ENABLE_CPP11_CSTDINT 1\n#endif\n#if __GLIBCXX__ >= 20080606 && !defined(HALF_ENABLE_CPP11_CMATH)\n#define HALF_ENABLE_CPP11_CMATH 1\n#endif\n#if __GLIBCXX__ >= 20080606 && !defined(HALF_ENABLE_CPP11_HASH)\n#define HALF_ENABLE_CPP11_HASH 1\n#endif\n#if __GLIBCXX__ >= 20080606 && !defined(HALF_ENABLE_CPP11_CFENV)\n#define HALF_ENABLE_CPP11_CFENV 1\n#endif\n#else\n#if HALF_GCC_VERSION >= 403 && !defined(HALF_ENABLE_CPP11_TYPE_TRAITS)\n#define HALF_ENABLE_CPP11_TYPE_TRAITS 1\n#endif\n#if HALF_GCC_VERSION >= 403 && !defined(HALF_ENABLE_CPP11_CSTDINT)\n#define HALF_ENABLE_CPP11_CSTDINT 1\n#endif\n#if HALF_GCC_VERSION >= 403 && !defined(HALF_ENABLE_CPP11_CMATH)\n#define HALF_ENABLE_CPP11_CMATH 1\n#endif\n#if HALF_GCC_VERSION >= 403 && !defined(HALF_ENABLE_CPP11_HASH)\n#define HALF_ENABLE_CPP11_HASH 1\n#endif\n#if HALF_GCC_VERSION >= 403 && !defined(HALF_ENABLE_CPP11_CFENV)\n#define HALF_ENABLE_CPP11_CFENV 1\n#endif\n#endif\n#endif\n#elif defined(_CPPLIB_VER)  // Dinkumware/Visual C++\n#if _CPPLIB_VER >= 520 && !defined(HALF_ENABLE_CPP11_TYPE_TRAITS)\n#define HALF_ENABLE_CPP11_TYPE_TRAITS 1\n#endif\n#if _CPPLIB_VER >= 520 && !defined(HALF_ENABLE_CPP11_CSTDINT)\n#define HALF_ENABLE_CPP11_CSTDINT 1\n#endif\n#if _CPPLIB_VER >= 520 && !defined(HALF_ENABLE_CPP11_HASH)\n#define HALF_ENABLE_CPP11_HASH 1\n#endif\n#if _CPPLIB_VER >= 610 && !defined(HALF_ENABLE_CPP11_CMATH)\n#define HALF_ENABLE_CPP11_CMATH 1\n#endif\n#if _CPPLIB_VER >= 610 && !defined(HALF_ENABLE_CPP11_CFENV)\n#define HALF_ENABLE_CPP11_CFENV 1\n#endif\n#endif\n#undef HALF_GCC_VERSION\n#undef HALF_ICC_VERSION\n\n// any error throwing C++ exceptions?\n#if defined(HALF_ERRHANDLING_THROW_INVALID) ||   \\\n    defined(HALF_ERRHANDLING_THROW_DIVBYZERO) || \\\n    defined(HALF_ERRHANDLING_THROW_OVERFLOW) ||  \\\n    defined(HALF_ERRHANDLING_THROW_UNDERFLOW) || \\\n    defined(HALF_ERRHANDLING_THROW_INEXACT)\n#define HALF_ERRHANDLING_THROWS 1\n#endif\n\n// any error handling enabled?\n#define HALF_ERRHANDLING                               \\\n  (HALF_ERRHANDLING_FLAGS || HALF_ERRHANDLING_ERRNO || \\\n   HALF_ERRHANDLING_FENV || HALF_ERRHANDLING_THROWS)\n\n#if HALF_ERRHANDLING\n#define HALF_UNUSED_NOERR(name) name\n#else\n#define HALF_UNUSED_NOERR(name)\n#endif\n\n// support constexpr\n#if HALF_ENABLE_CPP11_CONSTEXPR\n#define HALF_CONSTEXPR constexpr\n#define HALF_CONSTEXPR_CONST constexpr\n#if HALF_ERRHANDLING\n#define HALF_CONSTEXPR_NOERR\n#else\n#define HALF_CONSTEXPR_NOERR constexpr\n#endif\n#else\n#define HALF_CONSTEXPR\n#define HALF_CONSTEXPR_CONST const\n#define HALF_CONSTEXPR_NOERR\n#endif\n\n// support noexcept\n#if HALF_ENABLE_CPP11_NOEXCEPT\n#define HALF_NOEXCEPT noexcept\n#define HALF_NOTHROW noexcept\n#else\n#define HALF_NOEXCEPT\n#define HALF_NOTHROW throw()\n#endif\n\n// support thread storage\n#if HALF_ENABLE_CPP11_THREAD_LOCAL\n#define HALF_THREAD_LOCAL thread_local\n#else\n#define HALF_THREAD_LOCAL static\n#endif\n\n#include <algorithm>\n#include <climits>\n#include <cmath>\n#include <cstdlib>\n#include <cstring>\n#include <istream>\n#include <limits>\n#include <ostream>\n#include <stdexcept>\n#include <utility>\n#if HALF_ENABLE_CPP11_TYPE_TRAITS\n#include <type_traits>\n#endif\n#if HALF_ENABLE_CPP11_CSTDINT\n#include <cstdint>\n#endif\n#if HALF_ERRHANDLING_ERRNO\n#include <cerrno>\n#endif\n#if HALF_ENABLE_CPP11_CFENV\n#include <cfenv>\n#endif\n#if HALF_ENABLE_CPP11_HASH\n#include <functional>\n#endif\n#if HALF_ENABLE_F16C_INTRINSICS\n#include <immintrin.h>\n#endif\n\n#ifndef HALF_ENABLE_F16C_INTRINSICS\n/// Enable F16C intruction set intrinsics.\n/// Defining this to 1 enables the use of [F16C compiler\n/// intrinsics](https://en.wikipedia.org/wiki/F16C) for converting between\n/// half-precision and single-precision values which may result in improved\n/// performance. This will not perform additional checks\n/// for support of the F16C instruction set, so an appropriate target platform\n/// is required when enabling this feature.\n///\n/// Unless predefined it will be enabled automatically when the `__F16C__`\n/// symbol is defined, which some compilers do on supporting platforms.\n#define HALF_ENABLE_F16C_INTRINSICS __F16C__\n#endif\n\n#ifdef HALF_DOXYGEN_ONLY\n/// Type for internal floating-point computations.\n/// This can be predefined to a built-in floating-point type (`float`, `double`\n/// or `long double`) to override the internal\n/// half-precision implementation to use this type for computing arithmetic\n/// operations and mathematical function (if available).\n/// This can result in improved performance for arithmetic operators and\n/// mathematical functions but might cause results to\n/// deviate from the specified half-precision rounding mode and inhibits proper\n/// detection of half-precision exceptions.\n#define HALF_ARITHMETIC_TYPE (undefined)\n\n/// Enable internal exception flags.\n/// Defining this to 1 causes operations on half-precision values to raise\n/// internal floating-point exception flags according to\n/// the IEEE 754 standard. These can then be cleared and checked with\n/// clearexcept(), testexcept().\n#define HALF_ERRHANDLING_FLAGS 0\n\n/// Enable exception propagation to `errno`.\n/// Defining this to 1 causes operations on half-precision values to propagate\n/// floating-point exceptions to\n/// [errno](https://en.cppreference.com/w/cpp/error/errno) from `<cerrno>`.\n/// Specifically this will propagate domain errors as\n/// [EDOM](https://en.cppreference.com/w/cpp/error/errno_macros) and pole,\n/// overflow and underflow errors as\n/// [ERANGE](https://en.cppreference.com/w/cpp/error/errno_macros). Inexact\n/// errors won't be propagated.\n#define HALF_ERRHANDLING_ERRNO 0\n\n/// Enable exception propagation to built-in floating-point platform.\n/// Defining this to 1 causes operations on half-precision values to propagate\n/// floating-point exceptions to the built-in\n/// single- and double-precision implementation's exception flags using the\n/// [C++11 floating-point environment\n/// control](https://en.cppreference.com/w/cpp/numeric/fenv) from `<cfenv>`.\n/// However, this\n/// does not work in reverse and single- or double-precision exceptions will not\n/// raise the corresponding half-precision\n/// exception flags, nor will explicitly clearing flags clear the corresponding\n/// built-in flags.\n#define HALF_ERRHANDLING_FENV 0\n\n/// Throw C++ exception on domain errors.\n/// Defining this to a string literal causes operations on half-precision values\n/// to throw a\n/// [std::domain_error](https://en.cppreference.com/w/cpp/error/domain_error)\n/// with the specified message on domain errors.\n#define HALF_ERRHANDLING_THROW_INVALID (undefined)\n\n/// Throw C++ exception on pole errors.\n/// Defining this to a string literal causes operations on half-precision values\n/// to throw a\n/// [std::domain_error](https://en.cppreference.com/w/cpp/error/domain_error)\n/// with the specified message on pole errors.\n#define HALF_ERRHANDLING_THROW_DIVBYZERO (undefined)\n\n/// Throw C++ exception on overflow errors.\n/// Defining this to a string literal causes operations on half-precision values\n/// to throw a\n/// [std::overflow_error](https://en.cppreference.com/w/cpp/error/overflow_error)\n/// with the specified message on overflows.\n#define HALF_ERRHANDLING_THROW_OVERFLOW (undefined)\n\n/// Throw C++ exception on underflow errors.\n/// Defining this to a string literal causes operations on half-precision values\n/// to throw a\n/// [std::underflow_error](https://en.cppreference.com/w/cpp/error/underflow_error)\n/// with the specified message on underflows.\n#define HALF_ERRHANDLING_THROW_UNDERFLOW (undefined)\n\n/// Throw C++ exception on rounding errors.\n/// Defining this to 1 causes operations on half-precision values to throw a\n/// [std::range_error](https://en.cppreference.com/w/cpp/error/range_error) with\n/// the specified message on general rounding errors.\n#define HALF_ERRHANDLING_THROW_INEXACT (undefined)\n#endif\n\n#ifndef HALF_ERRHANDLING_OVERFLOW_TO_INEXACT\n/// Raise INEXACT exception on overflow.\n/// Defining this to 1 (default) causes overflow errors to automatically raise\n/// inexact exceptions in addition.\n/// These will be raised after any possible handling of the underflow exception.\n#define HALF_ERRHANDLING_OVERFLOW_TO_INEXACT 1\n#endif\n\n#ifndef HALF_ERRHANDLING_UNDERFLOW_TO_INEXACT\n/// Raise INEXACT exception on underflow.\n/// Defining this to 1 (default) causes underflow errors to automatically raise\n/// inexact exceptions in addition.\n/// These will be raised after any possible handling of the underflow exception.\n///\n/// **Note:** This will actually cause underflow (and the accompanying inexact)\n/// exceptions to be raised *only* when the result\n/// is inexact, while if disabled bare underflow errors will be raised for *any*\n/// (possibly exact) subnormal result.\n#define HALF_ERRHANDLING_UNDERFLOW_TO_INEXACT 1\n#endif\n\n/// Default rounding mode.\n/// This specifies the rounding mode used for all conversions between\n/// [half](\\ref half_float::half)s and more precise types\n/// (unless using half_cast() and specifying the rounding mode directly) as well\n/// as in arithmetic operations and mathematical\n/// functions. It can be redefined (before including half.hpp) to one of the\n/// standard rounding modes using their respective\n/// constants or the equivalent values of\n/// [std::float_round_style](https://en.cppreference.com/w/cpp/types/numeric_limits/float_round_style):\n///\n/// `std::float_round_style`         | value | rounding\n/// ---------------------------------|-------|-------------------------\n/// `std::round_indeterminate`       | -1    | fastest\n/// `std::round_toward_zero`         | 0     | toward zero\n/// `std::round_to_nearest`          | 1     | to nearest (default)\n/// `std::round_toward_infinity`     | 2     | toward positive infinity\n/// `std::round_toward_neg_infinity` | 3     | toward negative infinity\n///\n/// By default this is set to `1` (`std::round_to_nearest`), which rounds\n/// results to the nearest representable value. It can even\n/// be set to\n/// [std::numeric_limits<float>::round_style](https://en.cppreference.com/w/cpp/types/numeric_limits/round_style)\n/// to synchronize\n/// the rounding mode with that of the built-in single-precision implementation\n/// (which is likely `std::round_to_nearest`, though).\n#ifndef HALF_ROUND_STYLE\n#define HALF_ROUND_STYLE 1  // = std::round_to_nearest\n#endif\n\n/// Value signaling overflow.\n/// In correspondence with `HUGE_VAL[F|L]` from `<cmath>` this symbol expands to\n/// a positive value signaling the overflow of an\n/// operation, in particular it just evaluates to positive infinity.\n///\n/// **See also:** Documentation for\n/// [HUGE_VAL](https://en.cppreference.com/w/cpp/numeric/math/HUGE_VAL)\n#define HUGE_VALH std::numeric_limits<half_float::half>::infinity()\n\n/// Fast half-precision fma function.\n/// This symbol is defined if the fma() function generally executes as fast as,\n/// or faster than, a separate\n/// half-precision multiplication followed by an addition, which is always the\n/// case.\n///\n/// **See also:** Documentation for\n/// [FP_FAST_FMA](https://en.cppreference.com/w/cpp/numeric/math/fma)\n#define FP_FAST_FMAH 1\n\n///\tHalf rounding mode.\n/// In correspondence with `FLT_ROUNDS` from `<cfloat>` this symbol expands to\n/// the rounding mode used for\n/// half-precision operations. It is an alias for [HALF_ROUND_STYLE](\\ref\n/// HALF_ROUND_STYLE).\n///\n/// **See also:** Documentation for\n/// [FLT_ROUNDS](https://en.cppreference.com/w/cpp/types/climits/FLT_ROUNDS)\n#define HLF_ROUNDS HALF_ROUND_STYLE\n\n#ifndef FP_ILOGB0\n#define FP_ILOGB0 INT_MIN\n#endif\n#ifndef FP_ILOGBNAN\n#define FP_ILOGBNAN INT_MAX\n#endif\n#ifndef FP_SUBNORMAL\n#define FP_SUBNORMAL 0\n#endif\n#ifndef FP_ZERO\n#define FP_ZERO 1\n#endif\n#ifndef FP_NAN\n#define FP_NAN 2\n#endif\n#ifndef FP_INFINITE\n#define FP_INFINITE 3\n#endif\n#ifndef FP_NORMAL\n#define FP_NORMAL 4\n#endif\n\n#if !HALF_ENABLE_CPP11_CFENV && !defined(FE_ALL_EXCEPT)\n#define FE_INVALID 0x10\n#define FE_DIVBYZERO 0x08\n#define FE_OVERFLOW 0x04\n#define FE_UNDERFLOW 0x02\n#define FE_INEXACT 0x01\n#define FE_ALL_EXCEPT \\\n  (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT)\n#endif\n\n/// Main namespace for half-precision functionality.\n/// This namespace contains all the functionality provided by the library.\nnamespace half_float {\nclass half;\n\n#if HALF_ENABLE_CPP11_USER_LITERALS\n/// Library-defined half-precision literals.\n/// Import this namespace to enable half-precision floating-point literals:\n/// ~~~~{.cpp}\n/// using namespace half_float::literal;\n/// half_float::half = 4.2_h;\n/// ~~~~\nnamespace literal {\nhalf operator\"\" _h(long double);\n}\n#endif\n\n/// \\internal\n/// \\brief Implementation details.\nnamespace detail {\n#if HALF_ENABLE_CPP11_TYPE_TRAITS\n/// Conditional type.\ntemplate <bool B, typename T, typename F>\nstruct conditional : std::conditional<B, T, F> {};\n\n/// Helper for tag dispatching.\ntemplate <bool B>\nstruct bool_type : std::integral_constant<bool, B> {};\nusing std::true_type;\nusing std::false_type;\n\n/// Type traits for floating-point types.\ntemplate <typename T>\nstruct is_float : std::is_floating_point<T> {};\n#else\n/// Conditional type.\ntemplate <bool, typename T, typename>\nstruct conditional {\n  typedef T type;\n};\ntemplate <typename T, typename F>\nstruct conditional<false, T, F> {\n  typedef F type;\n};\n\n/// Helper for tag dispatching.\ntemplate <bool>\nstruct bool_type {};\ntypedef bool_type<true> true_type;\ntypedef bool_type<false> false_type;\n\n/// Type traits for floating-point types.\ntemplate <typename>\nstruct is_float : false_type {};\ntemplate <typename T>\nstruct is_float<const T> : is_float<T> {};\ntemplate <typename T>\nstruct is_float<volatile T> : is_float<T> {};\ntemplate <typename T>\nstruct is_float<const volatile T> : is_float<T> {};\ntemplate <>\nstruct is_float<float> : true_type {};\ntemplate <>\nstruct is_float<double> : true_type {};\ntemplate <>\nstruct is_float<long double> : true_type {};\n#endif\n\n/// Type traits for floating-point bits.\ntemplate <typename T>\nstruct bits {\n  typedef unsigned char type;\n};\ntemplate <typename T>\nstruct bits<const T> : bits<T> {};\ntemplate <typename T>\nstruct bits<volatile T> : bits<T> {};\ntemplate <typename T>\nstruct bits<const volatile T> : bits<T> {};\n\n#if HALF_ENABLE_CPP11_CSTDINT\n/// Unsigned integer of (at least) 16 bits width.\ntypedef std::uint_least16_t uint16;\n\n/// Fastest unsigned integer of (at least) 32 bits width.\ntypedef std::uint_fast32_t uint32;\n\n/// Fastest signed integer of (at least) 32 bits width.\ntypedef std::int_fast32_t int32;\n\n/// Unsigned integer of (at least) 32 bits width.\ntemplate <>\nstruct bits<float> {\n  typedef std::uint_least32_t type;\n};\n\n/// Unsigned integer of (at least) 64 bits width.\ntemplate <>\nstruct bits<double> {\n  typedef std::uint_least64_t type;\n};\n#else\n/// Unsigned integer of (at least) 16 bits width.\ntypedef unsigned short uint16;\n\n/// Fastest unsigned integer of (at least) 32 bits width.\ntypedef unsigned long uint32;\n\n/// Fastest unsigned integer of (at least) 32 bits width.\ntypedef long int32;\n\n/// Unsigned integer of (at least) 32 bits width.\ntemplate <>\nstruct bits<float>\n    : conditional<std::numeric_limits<unsigned int>::digits >= 32, unsigned int,\n                  unsigned long> {};\n\n#if HALF_ENABLE_CPP11_LONG_LONG\n/// Unsigned integer of (at least) 64 bits width.\ntemplate <>\nstruct bits<double>\n    : conditional<std::numeric_limits<unsigned long>::digits >= 64,\n                  unsigned long, unsigned long long> {};\n#else\n/// Unsigned integer of (at least) 64 bits width.\ntemplate <>\nstruct bits<double> {\n  typedef unsigned long type;\n};\n#endif\n#endif\n\n#ifdef HALF_ARITHMETIC_TYPE\n/// Type to use for arithmetic computations and mathematic functions internally.\ntypedef HALF_ARITHMETIC_TYPE internal_t;\n#endif\n\n/// Tag type for binary construction.\nstruct binary_t {};\n\n/// Tag for binary construction.\nHALF_CONSTEXPR_CONST binary_t binary = binary_t();\n\n/// \\name Implementation defined classification and arithmetic\n/// \\{\n\n/// Check for infinity.\n/// \\tparam T argument type (builtin floating-point type)\n/// \\param arg value to query\n/// \\retval true if infinity\n/// \\retval false else\ntemplate <typename T>\nbool builtin_isinf(T arg) {\n#if HALF_ENABLE_CPP11_CMATH\n  return std::isinf(arg);\n#elif defined(_MSC_VER)\n  return !::_finite(static_cast<double>(arg)) &&\n         !::_isnan(static_cast<double>(arg));\n#else\n  return arg == std::numeric_limits<T>::infinity() ||\n         arg == -std::numeric_limits<T>::infinity();\n#endif\n}\n\n/// Check for NaN.\n/// \\tparam T argument type (builtin floating-point type)\n/// \\param arg value to query\n/// \\retval true if not a number\n/// \\retval false else\ntemplate <typename T>\nbool builtin_isnan(T arg) {\n#if HALF_ENABLE_CPP11_CMATH\n  return std::isnan(arg);\n#elif defined(_MSC_VER)\n  return ::_isnan(static_cast<double>(arg)) != 0;\n#else\n  return arg != arg;\n#endif\n}\n\n/// Check sign.\n/// \\tparam T argument type (builtin floating-point type)\n/// \\param arg value to query\n/// \\retval true if signbit set\n/// \\retval false else\ntemplate <typename T>\nbool builtin_signbit(T arg) {\n#if HALF_ENABLE_CPP11_CMATH\n  return std::signbit(arg);\n#else\n  return arg < T() || (arg == T() && T(1) / arg < T());\n#endif\n}\n\n/// Platform-independent sign mask.\n/// \\param arg integer value in two's complement\n/// \\retval -1 if \\a arg negative\n/// \\retval 0 if \\a arg positive\ninline uint32 sign_mask(uint32 arg) {\n  static const int N = std::numeric_limits<uint32>::digits - 1;\n#if HALF_TWOS_COMPLEMENT_INT\n  return static_cast<int32>(arg) >> N;\n#else\n  return -((arg >> N) & 1);\n#endif\n}\n\n/// Platform-independent arithmetic right shift.\n/// \\param arg integer value in two's complement\n/// \\param i shift amount (at most 31)\n/// \\return \\a arg right shifted for \\a i bits with possible sign extension\ninline uint32 arithmetic_shift(uint32 arg, int i) {\n#if HALF_TWOS_COMPLEMENT_INT\n  return static_cast<int32>(arg) >> i;\n#else\n  return static_cast<int32>(arg) / (static_cast<int32>(1) << i) -\n         ((arg >> (std::numeric_limits<uint32>::digits - 1)) & 1);\n#endif\n}\n\n/// \\}\n/// \\name Error handling\n/// \\{\n\n/// Internal exception flags.\n/// \\return reference to global exception flags\ninline int &errflags() {\n  HALF_THREAD_LOCAL int flags = 0;\n  return flags;\n}\n\n/// Raise floating-point exception.\n/// \\param flags exceptions to raise\n/// \\param cond condition to raise exceptions for\ninline void raise(int HALF_UNUSED_NOERR(flags),\n                  bool HALF_UNUSED_NOERR(cond) = true) {\n#if HALF_ERRHANDLING\n  if (!cond) return;\n#if HALF_ERRHANDLING_FLAGS\n  errflags() |= flags;\n#endif\n#if HALF_ERRHANDLING_ERRNO\n  if (flags & FE_INVALID)\n    errno = EDOM;\n  else if (flags & (FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW))\n    errno = ERANGE;\n#endif\n#if HALF_ERRHANDLING_FENV && HALF_ENABLE_CPP11_CFENV\n  std::feraiseexcept(flags);\n#endif\n#ifdef HALF_ERRHANDLING_THROW_INVALID\n  if (flags & FE_INVALID)\n    throw std::domain_error(HALF_ERRHANDLING_THROW_INVALID);\n#endif\n#ifdef HALF_ERRHANDLING_THROW_DIVBYZERO\n  if (flags & FE_DIVBYZERO)\n    throw std::domain_error(HALF_ERRHANDLING_THROW_DIVBYZERO);\n#endif\n#ifdef HALF_ERRHANDLING_THROW_OVERFLOW\n  if (flags & FE_OVERFLOW)\n    throw std::overflow_error(HALF_ERRHANDLING_THROW_OVERFLOW);\n#endif\n#ifdef HALF_ERRHANDLING_THROW_UNDERFLOW\n  if (flags & FE_UNDERFLOW)\n    throw std::underflow_error(HALF_ERRHANDLING_THROW_UNDERFLOW);\n#endif\n#ifdef HALF_ERRHANDLING_THROW_INEXACT\n  if (flags & FE_INEXACT)\n    throw std::range_error(HALF_ERRHANDLING_THROW_INEXACT);\n#endif\n#if HALF_ERRHANDLING_UNDERFLOW_TO_INEXACT\n  if ((flags & FE_UNDERFLOW) && !(flags & FE_INEXACT)) raise(FE_INEXACT);\n#endif\n#if HALF_ERRHANDLING_OVERFLOW_TO_INEXACT\n  if ((flags & FE_OVERFLOW) && !(flags & FE_INEXACT)) raise(FE_INEXACT);\n#endif\n#endif\n}\n\n/// Check and signal for any NaN.\n/// \\param x first half-precision value to check\n/// \\param y second half-precision value to check\n/// \\retval true if either \\a x or \\a y is NaN\n/// \\retval false else\n/// \\exception FE_INVALID if \\a x or \\a y is NaN\ninline HALF_CONSTEXPR_NOERR bool compsignal(unsigned int x, unsigned int y) {\n#if HALF_ERRHANDLING\n  raise(FE_INVALID, (x & 0x7FFF) > 0x7C00 || (y & 0x7FFF) > 0x7C00);\n#endif\n  return (x & 0x7FFF) > 0x7C00 || (y & 0x7FFF) > 0x7C00;\n}\n\n/// Signal and silence signaling NaN.\n/// \\param nan half-precision NaN value\n/// \\return quiet NaN\n/// \\exception FE_INVALID if \\a nan is signaling NaN\ninline HALF_CONSTEXPR_NOERR unsigned int signal(unsigned int nan) {\n#if HALF_ERRHANDLING\n  raise(FE_INVALID, !(nan & 0x200));\n#endif\n  return nan | 0x200;\n}\n\n/// Signal and silence signaling NaNs.\n/// \\param x first half-precision value to check\n/// \\param y second half-precision value to check\n/// \\return quiet NaN\n/// \\exception FE_INVALID if \\a x or \\a y is signaling NaN\ninline HALF_CONSTEXPR_NOERR unsigned int signal(unsigned int x,\n                                                unsigned int y) {\n#if HALF_ERRHANDLING\n  raise(FE_INVALID,\n        ((x & 0x7FFF) > 0x7C00 && !(x & 0x200)) ||\n            ((y & 0x7FFF) > 0x7C00 && !(y & 0x200)));\n#endif\n  return ((x & 0x7FFF) > 0x7C00) ? (x | 0x200) : (y | 0x200);\n}\n\n/// Signal and silence signaling NaNs.\n/// \\param x first half-precision value to check\n/// \\param y second half-precision value to check\n/// \\param z third half-precision value to check\n/// \\return quiet NaN\n/// \\exception FE_INVALID if \\a x, \\a y or \\a z is signaling NaN\ninline HALF_CONSTEXPR_NOERR unsigned int signal(unsigned int x, unsigned int y,\n                                                unsigned int z) {\n#if HALF_ERRHANDLING\n  raise(FE_INVALID,\n        ((x & 0x7FFF) > 0x7C00 && !(x & 0x200)) ||\n            ((y & 0x7FFF) > 0x7C00 && !(y & 0x200)) ||\n            ((z & 0x7FFF) > 0x7C00 && !(z & 0x200)));\n#endif\n  return ((x & 0x7FFF) > 0x7C00)\n             ? (x | 0x200)\n             : ((y & 0x7FFF) > 0x7C00) ? (y | 0x200) : (z | 0x200);\n}\n\n/// Select value or signaling NaN.\n/// \\param x preferred half-precision value\n/// \\param y ignored half-precision value except for signaling NaN\n/// \\return \\a y if signaling NaN, \\a x otherwise\n/// \\exception FE_INVALID if \\a y is signaling NaN\ninline HALF_CONSTEXPR_NOERR unsigned int select(\n    unsigned int x, unsigned int HALF_UNUSED_NOERR(y)) {\n#if HALF_ERRHANDLING\n  return (((y & 0x7FFF) > 0x7C00) && !(y & 0x200)) ? signal(y) : x;\n#else\n  return x;\n#endif\n}\n\n/// Raise domain error and return NaN.\n/// return quiet NaN\n/// \\exception FE_INVALID\ninline HALF_CONSTEXPR_NOERR unsigned int invalid() {\n#if HALF_ERRHANDLING\n  raise(FE_INVALID);\n#endif\n  return 0x7FFF;\n}\n\n/// Raise pole error and return infinity.\n/// \\param sign half-precision value with sign bit only\n/// \\return half-precision infinity with sign of \\a sign\n/// \\exception FE_DIVBYZERO\ninline HALF_CONSTEXPR_NOERR unsigned int pole(unsigned int sign = 0) {\n#if HALF_ERRHANDLING\n  raise(FE_DIVBYZERO);\n#endif\n  return sign | 0x7C00;\n}\n\n/// Check value for underflow.\n/// \\param arg non-zero half-precision value to check\n/// \\return \\a arg\n/// \\exception FE_UNDERFLOW if arg is subnormal\ninline HALF_CONSTEXPR_NOERR unsigned int check_underflow(unsigned int arg) {\n#if HALF_ERRHANDLING && !HALF_ERRHANDLING_UNDERFLOW_TO_INEXACT\n  raise(FE_UNDERFLOW, !(arg & 0x7C00));\n#endif\n  return arg;\n}\n\n/// \\}\n/// \\name Conversion and rounding\n/// \\{\n\n/// Half-precision overflow.\n/// \\tparam R rounding mode to use\n/// \\param sign half-precision value with sign bit only\n/// \\return rounded overflowing half-precision value\n/// \\exception FE_OVERFLOW\ntemplate <std::float_round_style R>\nHALF_CONSTEXPR_NOERR unsigned int overflow(unsigned int sign = 0) {\n#if HALF_ERRHANDLING\n  raise(FE_OVERFLOW);\n#endif\n  return (R == std::round_toward_infinity)\n             ? (sign + 0x7C00 - (sign >> 15))\n             : (R == std::round_toward_neg_infinity)\n                   ? (sign + 0x7BFF + (sign >> 15))\n                   : (R == std::round_toward_zero) ? (sign | 0x7BFF)\n                                                   : (sign | 0x7C00);\n}\n\n/// Half-precision underflow.\n/// \\tparam R rounding mode to use\n/// \\param sign half-precision value with sign bit only\n/// \\return rounded underflowing half-precision value\n/// \\exception FE_UNDERFLOW\ntemplate <std::float_round_style R>\nHALF_CONSTEXPR_NOERR unsigned int underflow(unsigned int sign = 0) {\n#if HALF_ERRHANDLING\n  raise(FE_UNDERFLOW);\n#endif\n  return (R == std::round_toward_infinity)\n             ? (sign + 1 - (sign >> 15))\n             : (R == std::round_toward_neg_infinity) ? (sign + (sign >> 15))\n                                                     : sign;\n}\n\n/// Round half-precision number.\n/// \\tparam R rounding mode to use\n/// \\tparam I `true` to always raise INEXACT exception, `false` to raise only\n/// for rounded results\n/// \\param value finite half-precision number to round\n/// \\param g guard bit (most significant discarded bit)\n/// \\param s sticky bit (or of all but the most significant discarded bits)\n/// \\return rounded half-precision value\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if value had to be rounded or \\a I is `true`\ntemplate <std::float_round_style R, bool I>\nHALF_CONSTEXPR_NOERR unsigned int rounded(unsigned int value, int g, int s) {\n#if HALF_ERRHANDLING\n  value += (R == std::round_to_nearest)\n               ? (g & (s | value))\n               : (R == std::round_toward_infinity)\n                     ? (~(value >> 15) & (g | s))\n                     : (R == std::round_toward_neg_infinity)\n                           ? ((value >> 15) & (g | s))\n                           : 0;\n  if ((value & 0x7C00) == 0x7C00)\n    raise(FE_OVERFLOW);\n  else if (value & 0x7C00)\n    raise(FE_INEXACT, I || (g | s) != 0);\n  else\n    raise(FE_UNDERFLOW,\n          !(HALF_ERRHANDLING_UNDERFLOW_TO_INEXACT) || I || (g | s) != 0);\n  return value;\n#else\n  return (R == std::round_to_nearest)\n             ? (value + (g & (s | value)))\n             : (R == std::round_toward_infinity)\n                   ? (value + (~(value >> 15) & (g | s)))\n                   : (R == std::round_toward_neg_infinity)\n                         ? (value + ((value >> 15) & (g | s)))\n                         : value;\n#endif\n}\n\n/// Round half-precision number to nearest integer value.\n/// \\tparam R rounding mode to use\n/// \\tparam E `true` for round to even, `false` for round away from zero\n/// \\tparam I `true` to raise INEXACT exception (if inexact), `false` to never\n/// raise it\n/// \\param value half-precision value to round\n/// \\return half-precision bits for nearest integral value\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_INEXACT if value had to be rounded and \\a I is `true`\ntemplate <std::float_round_style R, bool E, bool I>\nunsigned int integral(unsigned int value) {\n  unsigned int abs = value & 0x7FFF;\n  if (abs < 0x3C00) {\n    raise(FE_INEXACT, I);\n    return ((R == std::round_to_nearest)\n                ? (0x3C00 & -static_cast<unsigned>(abs >= (0x3800 + E)))\n                : (R == std::round_toward_infinity)\n                      ? (0x3C00 & -(~(value >> 15) & (abs != 0)))\n                      : (R == std::round_toward_neg_infinity)\n                            ? (0x3C00 & -static_cast<unsigned>(value > 0x8000))\n                            : 0) |\n           (value & 0x8000);\n  }\n  if (abs >= 0x6400) return (abs > 0x7C00) ? signal(value) : value;\n  unsigned int exp = 25 - (abs >> 10), mask = (1 << exp) - 1;\n  raise(FE_INEXACT, I && (value & mask));\n  return (((R == std::round_to_nearest)\n               ? ((1 << (exp - 1)) - (~(value >> exp) & E))\n               : (R == std::round_toward_infinity)\n                     ? (mask & ((value >> 15) - 1))\n                     : (R == std::round_toward_neg_infinity)\n                           ? (mask & -(value >> 15))\n                           : 0) +\n          value) &\n         ~mask;\n}\n\n/// Convert fixed point to half-precision floating-point.\n/// \\tparam R rounding mode to use\n/// \\tparam F number of fractional bits (at least 11)\n/// \\tparam S `true` for signed, `false` for unsigned\n/// \\tparam N `true` for additional normalization step, `false` if already\n/// normalized to 1.F\n/// \\tparam I `true` to always raise INEXACT exception, `false` to raise only\n/// for rounded results\n/// \\param m mantissa in Q1.F fixed point format\n/// \\param exp exponent\n/// \\param sign half-precision value with sign bit only\n/// \\param s sticky bit (or of all but the most significant already discarded\n/// bits)\n/// \\return value converted to half-precision\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if value had to be rounded or \\a I is `true`\ntemplate <std::float_round_style R, unsigned int F, bool S, bool N, bool I>\nunsigned int fixed2half(uint32 m, int exp = 14, unsigned int sign = 0,\n                        int s = 0) {\n  if (S) {\n    uint32 msign = sign_mask(m);\n    m = (m ^ msign) - msign;\n    sign = msign & 0x8000;\n  }\n  if (N)\n    for (; m < (static_cast<uint32>(1) << F) && exp; m <<= 1, --exp)\n      ;\n  else if (exp < 0)\n    return rounded<R, I>(\n        sign + (m >> (F - 10 - exp)), (m >> (F - 11 - exp)) & 1,\n        s | ((m & ((static_cast<uint32>(1) << (F - 11 - exp)) - 1)) != 0));\n  return rounded<R, I>(\n      sign + (exp << 10) + (m >> (F - 10)), (m >> (F - 11)) & 1,\n      s | ((m & ((static_cast<uint32>(1) << (F - 11)) - 1)) != 0));\n}\n\n/// Convert IEEE single-precision to half-precision.\n/// Credit for this goes to [Jeroen van der\n/// Zijp](ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf).\n/// \\tparam R rounding mode to use\n/// \\param value single-precision value to convert\n/// \\return rounded half-precision value\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if value had to be rounded\ntemplate <std::float_round_style R>\nunsigned int float2half_impl(float value, true_type) {\n#if HALF_ENABLE_F16C_INTRINSICS\n  return _mm_cvtsi128_si32(\n      _mm_cvtps_ph(_mm_set_ss(value),\n                   (R == std::round_to_nearest)\n                       ? _MM_FROUND_TO_NEAREST_INT\n                       : (R == std::round_toward_zero)\n                             ? _MM_FROUND_TO_ZERO\n                             : (R == std::round_toward_infinity)\n                                   ? _MM_FROUND_TO_POS_INF\n                                   : (R == std::round_toward_neg_infinity)\n                                         ? _MM_FROUND_TO_NEG_INF\n                                         : _MM_FROUND_CUR_DIRECTION));\n#else\n  bits<float>::type fbits;\n  std::memcpy(&fbits, &value, sizeof(float));\n#if 1\n  unsigned int sign = (fbits >> 16) & 0x8000;\n  fbits &= 0x7FFFFFFF;\n  if (fbits >= 0x7F800000)\n    return sign | 0x7C00 |\n           ((fbits > 0x7F800000) ? (0x200 | ((fbits >> 13) & 0x3FF)) : 0);\n  if (fbits >= 0x47800000) return overflow<R>(sign);\n  if (fbits >= 0x38800000)\n    return rounded<R, false>(\n        sign | (((fbits >> 23) - 112) << 10) | ((fbits >> 13) & 0x3FF),\n        (fbits >> 12) & 1, (fbits & 0xFFF) != 0);\n  if (fbits >= 0x33000000) {\n    int i = 125 - (fbits >> 23);\n    fbits = (fbits & 0x7FFFFF) | 0x800000;\n    return rounded<R, false>(\n        sign | (fbits >> (i + 1)), (fbits >> i) & 1,\n        (fbits & ((static_cast<uint32>(1) << i) - 1)) != 0);\n  }\n  if (fbits != 0) return underflow<R>(sign);\n  return sign;\n#else\n  static const uint16 base_table[512] = {\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\n      0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010,\n      0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0C00, 0x1000,\n      0x1400, 0x1800, 0x1C00, 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400,\n      0x3800, 0x3C00, 0x4000, 0x4400, 0x4800, 0x4C00, 0x5000, 0x5400, 0x5800,\n      0x5C00, 0x6000, 0x6400, 0x6800, 0x6C00, 0x7000, 0x7400, 0x7800, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF, 0x7BFF,\n      0x7BFF, 0x7BFF, 0x7BFF, 0x7C00, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000,\n      0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001,\n      0x8002, 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200,\n      0x8400, 0x8800, 0x8C00, 0x9000, 0x9400, 0x9800, 0x9C00, 0xA000, 0xA400,\n      0xA800, 0xAC00, 0xB000, 0xB400, 0xB800, 0xBC00, 0xC000, 0xC400, 0xC800,\n      0xCC00, 0xD000, 0xD400, 0xD800, 0xDC00, 0xE000, 0xE400, 0xE800, 0xEC00,\n      0xF000, 0xF400, 0xF800, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF,\n      0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFBFF, 0xFC00};\n  static const unsigned char shift_table[256] = {\n      24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,\n      25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,\n      25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,\n      25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,\n      25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,\n      25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 24, 23, 22, 21, 20, 19,\n      18, 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,\n      13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 24,\n      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,\n      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,\n      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,\n      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,\n      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,\n      24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,\n      24, 24, 24, 13};\n  int sexp = fbits >> 23, exp = sexp & 0xFF, i = shift_table[exp];\n  fbits &= 0x7FFFFF;\n  uint32 m = (fbits | ((exp != 0) << 23)) & -static_cast<uint32>(exp != 0xFF);\n  return rounded<R, false>(\n      base_table[sexp] + (fbits >> i), (m >> (i - 1)) & 1,\n      (((static_cast<uint32>(1) << (i - 1)) - 1) & m) != 0);\n#endif\n#endif\n}\n\n/// Convert IEEE double-precision to half-precision.\n/// \\tparam R rounding mode to use\n/// \\param value double-precision value to convert\n/// \\return rounded half-precision value\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if value had to be rounded\ntemplate <std::float_round_style R>\nunsigned int float2half_impl(double value, true_type) {\n#if HALF_ENABLE_F16C_INTRINSICS\n  if (R == std::round_indeterminate)\n    return _mm_cvtsi128_si32(_mm_cvtps_ph(_mm_cvtpd_ps(_mm_set_sd(value)),\n                                          _MM_FROUND_CUR_DIRECTION));\n#endif\n  bits<double>::type dbits;\n  std::memcpy(&dbits, &value, sizeof(double));\n  uint32 hi = dbits >> 32, lo = dbits & 0xFFFFFFFF;\n  unsigned int sign = (hi >> 16) & 0x8000;\n  hi &= 0x7FFFFFFF;\n  if (hi >= 0x7FF00000)\n    return sign | 0x7C00 |\n           ((dbits & 0xFFFFFFFFFFFFF) ? (0x200 | ((hi >> 10) & 0x3FF)) : 0);\n  if (hi >= 0x40F00000) return overflow<R>(sign);\n  if (hi >= 0x3F100000)\n    return rounded<R, false>(\n        sign | (((hi >> 20) - 1008) << 10) | ((hi >> 10) & 0x3FF),\n        (hi >> 9) & 1, ((hi & 0x1FF) | lo) != 0);\n  if (hi >= 0x3E600000) {\n    int i = 1018 - (hi >> 20);\n    hi = (hi & 0xFFFFF) | 0x100000;\n    return rounded<R, false>(\n        sign | (hi >> (i + 1)), (hi >> i) & 1,\n        ((hi & ((static_cast<uint32>(1) << i) - 1)) | lo) != 0);\n  }\n  if ((hi | lo) != 0) return underflow<R>(sign);\n  return sign;\n}\n\n/// Convert non-IEEE floating-point to half-precision.\n/// \\tparam R rounding mode to use\n/// \\tparam T source type (builtin floating-point type)\n/// \\param value floating-point value to convert\n/// \\return rounded half-precision value\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if value had to be rounded\ntemplate <std::float_round_style R, typename T>\nunsigned int float2half_impl(T value, ...) {\n  unsigned int hbits = static_cast<unsigned>(builtin_signbit(value)) << 15;\n  if (value == T()) return hbits;\n  if (builtin_isnan(value)) return hbits | 0x7FFF;\n  if (builtin_isinf(value)) return hbits | 0x7C00;\n  int exp;\n  std::frexp(value, &exp);\n  if (exp > 16) return overflow<R>(hbits);\n  if (exp < -13)\n    value = std::ldexp(value, 25);\n  else {\n    value = std::ldexp(value, 12 - exp);\n    hbits |= ((exp + 13) << 10);\n  }\n  T ival, frac = std::modf(value, &ival);\n  int m = std::abs(static_cast<int>(ival));\n  return rounded<R, false>(hbits + (m >> 1), m & 1, frac != T());\n}\n\n/// Convert floating-point to half-precision.\n/// \\tparam R rounding mode to use\n/// \\tparam T source type (builtin floating-point type)\n/// \\param value floating-point value to convert\n/// \\return rounded half-precision value\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if value had to be rounded\ntemplate <std::float_round_style R, typename T>\nunsigned int float2half(T value) {\n  return float2half_impl<R>(\n      value,\n      bool_type < std::numeric_limits<T>::is_iec559 &&\n          sizeof(typename bits<T>::type) == sizeof(T) > ());\n}\n\n/// Convert integer to half-precision floating-point.\n/// \\tparam R rounding mode to use\n/// \\tparam T type to convert (builtin integer type)\n/// \\param value integral value to convert\n/// \\return rounded half-precision value\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_INEXACT if value had to be rounded\ntemplate <std::float_round_style R, typename T>\nunsigned int int2half(T value) {\n  unsigned int bits = static_cast<unsigned>(value < 0) << 15;\n  if (!value) return bits;\n  if (bits) value = -value;\n  if (value > 0xFFFF) return overflow<R>(bits);\n  unsigned int m = static_cast<unsigned int>(value), exp = 24;\n  for (; m < 0x400; m <<= 1, --exp)\n    ;\n  for (; m > 0x7FF; m >>= 1, ++exp)\n    ;\n  bits |= (exp << 10) + m;\n  return (exp > 24) ? rounded<R, false>(bits, (value >> (exp - 25)) & 1,\n                                        (((1 << (exp - 25)) - 1) & value) != 0)\n                    : bits;\n}\n\n/// Convert half-precision to IEEE single-precision.\n/// Credit for this goes to [Jeroen van der\n/// Zijp](ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf).\n/// \\param value half-precision value to convert\n/// \\return single-precision value\ninline float half2float_impl(unsigned int value, float, true_type) {\n#if HALF_ENABLE_F16C_INTRINSICS\n  return _mm_cvtss_f32(_mm_cvtph_ps(_mm_cvtsi32_si128(value)));\n#else\n#if 0\n\t\t\tbits<float>::type fbits = static_cast<bits<float>::type>(value&0x8000) << 16;\n\t\t\tint abs = value & 0x7FFF;\n\t\t\tif(abs)\n\t\t\t{\n\t\t\t\tfbits |= 0x38000000 << static_cast<unsigned>(abs>=0x7C00);\n\t\t\t\tfor(; abs<0x400; abs<<=1,fbits-=0x800000) ;\n\t\t\t\tfbits += static_cast<bits<float>::type>(abs) << 13;\n\t\t\t}\n#else\n  static const bits<float>::type mantissa_table[2048] = {\n      0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34A00000,\n      0x34C00000, 0x34E00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000,\n      0x35400000, 0x35500000, 0x35600000, 0x35700000, 0x35800000, 0x35880000,\n      0x35900000, 0x35980000, 0x35A00000, 0x35A80000, 0x35B00000, 0x35B80000,\n      0x35C00000, 0x35C80000, 0x35D00000, 0x35D80000, 0x35E00000, 0x35E80000,\n      0x35F00000, 0x35F80000, 0x36000000, 0x36040000, 0x36080000, 0x360C0000,\n      0x36100000, 0x36140000, 0x36180000, 0x361C0000, 0x36200000, 0x36240000,\n      0x36280000, 0x362C0000, 0x36300000, 0x36340000, 0x36380000, 0x363C0000,\n      0x36400000, 0x36440000, 0x36480000, 0x364C0000, 0x36500000, 0x36540000,\n      0x36580000, 0x365C0000, 0x36600000, 0x36640000, 0x36680000, 0x366C0000,\n      0x36700000, 0x36740000, 0x36780000, 0x367C0000, 0x36800000, 0x36820000,\n      0x36840000, 0x36860000, 0x36880000, 0x368A0000, 0x368C0000, 0x368E0000,\n      0x36900000, 0x36920000, 0x36940000, 0x36960000, 0x36980000, 0x369A0000,\n      0x369C0000, 0x369E0000, 0x36A00000, 0x36A20000, 0x36A40000, 0x36A60000,\n      0x36A80000, 0x36AA0000, 0x36AC0000, 0x36AE0000, 0x36B00000, 0x36B20000,\n      0x36B40000, 0x36B60000, 0x36B80000, 0x36BA0000, 0x36BC0000, 0x36BE0000,\n      0x36C00000, 0x36C20000, 0x36C40000, 0x36C60000, 0x36C80000, 0x36CA0000,\n      0x36CC0000, 0x36CE0000, 0x36D00000, 0x36D20000, 0x36D40000, 0x36D60000,\n      0x36D80000, 0x36DA0000, 0x36DC0000, 0x36DE0000, 0x36E00000, 0x36E20000,\n      0x36E40000, 0x36E60000, 0x36E80000, 0x36EA0000, 0x36EC0000, 0x36EE0000,\n      0x36F00000, 0x36F20000, 0x36F40000, 0x36F60000, 0x36F80000, 0x36FA0000,\n      0x36FC0000, 0x36FE0000, 0x37000000, 0x37010000, 0x37020000, 0x37030000,\n      0x37040000, 0x37050000, 0x37060000, 0x37070000, 0x37080000, 0x37090000,\n      0x370A0000, 0x370B0000, 0x370C0000, 0x370D0000, 0x370E0000, 0x370F0000,\n      0x37100000, 0x37110000, 0x37120000, 0x37130000, 0x37140000, 0x37150000,\n      0x37160000, 0x37170000, 0x37180000, 0x37190000, 0x371A0000, 0x371B0000,\n      0x371C0000, 0x371D0000, 0x371E0000, 0x371F0000, 0x37200000, 0x37210000,\n      0x37220000, 0x37230000, 0x37240000, 0x37250000, 0x37260000, 0x37270000,\n      0x37280000, 0x37290000, 0x372A0000, 0x372B0000, 0x372C0000, 0x372D0000,\n      0x372E0000, 0x372F0000, 0x37300000, 0x37310000, 0x37320000, 0x37330000,\n      0x37340000, 0x37350000, 0x37360000, 0x37370000, 0x37380000, 0x37390000,\n      0x373A0000, 0x373B0000, 0x373C0000, 0x373D0000, 0x373E0000, 0x373F0000,\n      0x37400000, 0x37410000, 0x37420000, 0x37430000, 0x37440000, 0x37450000,\n      0x37460000, 0x37470000, 0x37480000, 0x37490000, 0x374A0000, 0x374B0000,\n      0x374C0000, 0x374D0000, 0x374E0000, 0x374F0000, 0x37500000, 0x37510000,\n      0x37520000, 0x37530000, 0x37540000, 0x37550000, 0x37560000, 0x37570000,\n      0x37580000, 0x37590000, 0x375A0000, 0x375B0000, 0x375C0000, 0x375D0000,\n      0x375E0000, 0x375F0000, 0x37600000, 0x37610000, 0x37620000, 0x37630000,\n      0x37640000, 0x37650000, 0x37660000, 0x37670000, 0x37680000, 0x37690000,\n      0x376A0000, 0x376B0000, 0x376C0000, 0x376D0000, 0x376E0000, 0x376F0000,\n      0x37700000, 0x37710000, 0x37720000, 0x37730000, 0x37740000, 0x37750000,\n      0x37760000, 0x37770000, 0x37780000, 0x37790000, 0x377A0000, 0x377B0000,\n      0x377C0000, 0x377D0000, 0x377E0000, 0x377F0000, 0x37800000, 0x37808000,\n      0x37810000, 0x37818000, 0x37820000, 0x37828000, 0x37830000, 0x37838000,\n      0x37840000, 0x37848000, 0x37850000, 0x37858000, 0x37860000, 0x37868000,\n      0x37870000, 0x37878000, 0x37880000, 0x37888000, 0x37890000, 0x37898000,\n      0x378A0000, 0x378A8000, 0x378B0000, 0x378B8000, 0x378C0000, 0x378C8000,\n      0x378D0000, 0x378D8000, 0x378E0000, 0x378E8000, 0x378F0000, 0x378F8000,\n      0x37900000, 0x37908000, 0x37910000, 0x37918000, 0x37920000, 0x37928000,\n      0x37930000, 0x37938000, 0x37940000, 0x37948000, 0x37950000, 0x37958000,\n      0x37960000, 0x37968000, 0x37970000, 0x37978000, 0x37980000, 0x37988000,\n      0x37990000, 0x37998000, 0x379A0000, 0x379A8000, 0x379B0000, 0x379B8000,\n      0x379C0000, 0x379C8000, 0x379D0000, 0x379D8000, 0x379E0000, 0x379E8000,\n      0x379F0000, 0x379F8000, 0x37A00000, 0x37A08000, 0x37A10000, 0x37A18000,\n      0x37A20000, 0x37A28000, 0x37A30000, 0x37A38000, 0x37A40000, 0x37A48000,\n      0x37A50000, 0x37A58000, 0x37A60000, 0x37A68000, 0x37A70000, 0x37A78000,\n      0x37A80000, 0x37A88000, 0x37A90000, 0x37A98000, 0x37AA0000, 0x37AA8000,\n      0x37AB0000, 0x37AB8000, 0x37AC0000, 0x37AC8000, 0x37AD0000, 0x37AD8000,\n      0x37AE0000, 0x37AE8000, 0x37AF0000, 0x37AF8000, 0x37B00000, 0x37B08000,\n      0x37B10000, 0x37B18000, 0x37B20000, 0x37B28000, 0x37B30000, 0x37B38000,\n      0x37B40000, 0x37B48000, 0x37B50000, 0x37B58000, 0x37B60000, 0x37B68000,\n      0x37B70000, 0x37B78000, 0x37B80000, 0x37B88000, 0x37B90000, 0x37B98000,\n      0x37BA0000, 0x37BA8000, 0x37BB0000, 0x37BB8000, 0x37BC0000, 0x37BC8000,\n      0x37BD0000, 0x37BD8000, 0x37BE0000, 0x37BE8000, 0x37BF0000, 0x37BF8000,\n      0x37C00000, 0x37C08000, 0x37C10000, 0x37C18000, 0x37C20000, 0x37C28000,\n      0x37C30000, 0x37C38000, 0x37C40000, 0x37C48000, 0x37C50000, 0x37C58000,\n      0x37C60000, 0x37C68000, 0x37C70000, 0x37C78000, 0x37C80000, 0x37C88000,\n      0x37C90000, 0x37C98000, 0x37CA0000, 0x37CA8000, 0x37CB0000, 0x37CB8000,\n      0x37CC0000, 0x37CC8000, 0x37CD0000, 0x37CD8000, 0x37CE0000, 0x37CE8000,\n      0x37CF0000, 0x37CF8000, 0x37D00000, 0x37D08000, 0x37D10000, 0x37D18000,\n      0x37D20000, 0x37D28000, 0x37D30000, 0x37D38000, 0x37D40000, 0x37D48000,\n      0x37D50000, 0x37D58000, 0x37D60000, 0x37D68000, 0x37D70000, 0x37D78000,\n      0x37D80000, 0x37D88000, 0x37D90000, 0x37D98000, 0x37DA0000, 0x37DA8000,\n      0x37DB0000, 0x37DB8000, 0x37DC0000, 0x37DC8000, 0x37DD0000, 0x37DD8000,\n      0x37DE0000, 0x37DE8000, 0x37DF0000, 0x37DF8000, 0x37E00000, 0x37E08000,\n      0x37E10000, 0x37E18000, 0x37E20000, 0x37E28000, 0x37E30000, 0x37E38000,\n      0x37E40000, 0x37E48000, 0x37E50000, 0x37E58000, 0x37E60000, 0x37E68000,\n      0x37E70000, 0x37E78000, 0x37E80000, 0x37E88000, 0x37E90000, 0x37E98000,\n      0x37EA0000, 0x37EA8000, 0x37EB0000, 0x37EB8000, 0x37EC0000, 0x37EC8000,\n      0x37ED0000, 0x37ED8000, 0x37EE0000, 0x37EE8000, 0x37EF0000, 0x37EF8000,\n      0x37F00000, 0x37F08000, 0x37F10000, 0x37F18000, 0x37F20000, 0x37F28000,\n      0x37F30000, 0x37F38000, 0x37F40000, 0x37F48000, 0x37F50000, 0x37F58000,\n      0x37F60000, 0x37F68000, 0x37F70000, 0x37F78000, 0x37F80000, 0x37F88000,\n      0x37F90000, 0x37F98000, 0x37FA0000, 0x37FA8000, 0x37FB0000, 0x37FB8000,\n      0x37FC0000, 0x37FC8000, 0x37FD0000, 0x37FD8000, 0x37FE0000, 0x37FE8000,\n      0x37FF0000, 0x37FF8000, 0x38000000, 0x38004000, 0x38008000, 0x3800C000,\n      0x38010000, 0x38014000, 0x38018000, 0x3801C000, 0x38020000, 0x38024000,\n      0x38028000, 0x3802C000, 0x38030000, 0x38034000, 0x38038000, 0x3803C000,\n      0x38040000, 0x38044000, 0x38048000, 0x3804C000, 0x38050000, 0x38054000,\n      0x38058000, 0x3805C000, 0x38060000, 0x38064000, 0x38068000, 0x3806C000,\n      0x38070000, 0x38074000, 0x38078000, 0x3807C000, 0x38080000, 0x38084000,\n      0x38088000, 0x3808C000, 0x38090000, 0x38094000, 0x38098000, 0x3809C000,\n      0x380A0000, 0x380A4000, 0x380A8000, 0x380AC000, 0x380B0000, 0x380B4000,\n      0x380B8000, 0x380BC000, 0x380C0000, 0x380C4000, 0x380C8000, 0x380CC000,\n      0x380D0000, 0x380D4000, 0x380D8000, 0x380DC000, 0x380E0000, 0x380E4000,\n      0x380E8000, 0x380EC000, 0x380F0000, 0x380F4000, 0x380F8000, 0x380FC000,\n      0x38100000, 0x38104000, 0x38108000, 0x3810C000, 0x38110000, 0x38114000,\n      0x38118000, 0x3811C000, 0x38120000, 0x38124000, 0x38128000, 0x3812C000,\n      0x38130000, 0x38134000, 0x38138000, 0x3813C000, 0x38140000, 0x38144000,\n      0x38148000, 0x3814C000, 0x38150000, 0x38154000, 0x38158000, 0x3815C000,\n      0x38160000, 0x38164000, 0x38168000, 0x3816C000, 0x38170000, 0x38174000,\n      0x38178000, 0x3817C000, 0x38180000, 0x38184000, 0x38188000, 0x3818C000,\n      0x38190000, 0x38194000, 0x38198000, 0x3819C000, 0x381A0000, 0x381A4000,\n      0x381A8000, 0x381AC000, 0x381B0000, 0x381B4000, 0x381B8000, 0x381BC000,\n      0x381C0000, 0x381C4000, 0x381C8000, 0x381CC000, 0x381D0000, 0x381D4000,\n      0x381D8000, 0x381DC000, 0x381E0000, 0x381E4000, 0x381E8000, 0x381EC000,\n      0x381F0000, 0x381F4000, 0x381F8000, 0x381FC000, 0x38200000, 0x38204000,\n      0x38208000, 0x3820C000, 0x38210000, 0x38214000, 0x38218000, 0x3821C000,\n      0x38220000, 0x38224000, 0x38228000, 0x3822C000, 0x38230000, 0x38234000,\n      0x38238000, 0x3823C000, 0x38240000, 0x38244000, 0x38248000, 0x3824C000,\n      0x38250000, 0x38254000, 0x38258000, 0x3825C000, 0x38260000, 0x38264000,\n      0x38268000, 0x3826C000, 0x38270000, 0x38274000, 0x38278000, 0x3827C000,\n      0x38280000, 0x38284000, 0x38288000, 0x3828C000, 0x38290000, 0x38294000,\n      0x38298000, 0x3829C000, 0x382A0000, 0x382A4000, 0x382A8000, 0x382AC000,\n      0x382B0000, 0x382B4000, 0x382B8000, 0x382BC000, 0x382C0000, 0x382C4000,\n      0x382C8000, 0x382CC000, 0x382D0000, 0x382D4000, 0x382D8000, 0x382DC000,\n      0x382E0000, 0x382E4000, 0x382E8000, 0x382EC000, 0x382F0000, 0x382F4000,\n      0x382F8000, 0x382FC000, 0x38300000, 0x38304000, 0x38308000, 0x3830C000,\n      0x38310000, 0x38314000, 0x38318000, 0x3831C000, 0x38320000, 0x38324000,\n      0x38328000, 0x3832C000, 0x38330000, 0x38334000, 0x38338000, 0x3833C000,\n      0x38340000, 0x38344000, 0x38348000, 0x3834C000, 0x38350000, 0x38354000,\n      0x38358000, 0x3835C000, 0x38360000, 0x38364000, 0x38368000, 0x3836C000,\n      0x38370000, 0x38374000, 0x38378000, 0x3837C000, 0x38380000, 0x38384000,\n      0x38388000, 0x3838C000, 0x38390000, 0x38394000, 0x38398000, 0x3839C000,\n      0x383A0000, 0x383A4000, 0x383A8000, 0x383AC000, 0x383B0000, 0x383B4000,\n      0x383B8000, 0x383BC000, 0x383C0000, 0x383C4000, 0x383C8000, 0x383CC000,\n      0x383D0000, 0x383D4000, 0x383D8000, 0x383DC000, 0x383E0000, 0x383E4000,\n      0x383E8000, 0x383EC000, 0x383F0000, 0x383F4000, 0x383F8000, 0x383FC000,\n      0x38400000, 0x38404000, 0x38408000, 0x3840C000, 0x38410000, 0x38414000,\n      0x38418000, 0x3841C000, 0x38420000, 0x38424000, 0x38428000, 0x3842C000,\n      0x38430000, 0x38434000, 0x38438000, 0x3843C000, 0x38440000, 0x38444000,\n      0x38448000, 0x3844C000, 0x38450000, 0x38454000, 0x38458000, 0x3845C000,\n      0x38460000, 0x38464000, 0x38468000, 0x3846C000, 0x38470000, 0x38474000,\n      0x38478000, 0x3847C000, 0x38480000, 0x38484000, 0x38488000, 0x3848C000,\n      0x38490000, 0x38494000, 0x38498000, 0x3849C000, 0x384A0000, 0x384A4000,\n      0x384A8000, 0x384AC000, 0x384B0000, 0x384B4000, 0x384B8000, 0x384BC000,\n      0x384C0000, 0x384C4000, 0x384C8000, 0x384CC000, 0x384D0000, 0x384D4000,\n      0x384D8000, 0x384DC000, 0x384E0000, 0x384E4000, 0x384E8000, 0x384EC000,\n      0x384F0000, 0x384F4000, 0x384F8000, 0x384FC000, 0x38500000, 0x38504000,\n      0x38508000, 0x3850C000, 0x38510000, 0x38514000, 0x38518000, 0x3851C000,\n      0x38520000, 0x38524000, 0x38528000, 0x3852C000, 0x38530000, 0x38534000,\n      0x38538000, 0x3853C000, 0x38540000, 0x38544000, 0x38548000, 0x3854C000,\n      0x38550000, 0x38554000, 0x38558000, 0x3855C000, 0x38560000, 0x38564000,\n      0x38568000, 0x3856C000, 0x38570000, 0x38574000, 0x38578000, 0x3857C000,\n      0x38580000, 0x38584000, 0x38588000, 0x3858C000, 0x38590000, 0x38594000,\n      0x38598000, 0x3859C000, 0x385A0000, 0x385A4000, 0x385A8000, 0x385AC000,\n      0x385B0000, 0x385B4000, 0x385B8000, 0x385BC000, 0x385C0000, 0x385C4000,\n      0x385C8000, 0x385CC000, 0x385D0000, 0x385D4000, 0x385D8000, 0x385DC000,\n      0x385E0000, 0x385E4000, 0x385E8000, 0x385EC000, 0x385F0000, 0x385F4000,\n      0x385F8000, 0x385FC000, 0x38600000, 0x38604000, 0x38608000, 0x3860C000,\n      0x38610000, 0x38614000, 0x38618000, 0x3861C000, 0x38620000, 0x38624000,\n      0x38628000, 0x3862C000, 0x38630000, 0x38634000, 0x38638000, 0x3863C000,\n      0x38640000, 0x38644000, 0x38648000, 0x3864C000, 0x38650000, 0x38654000,\n      0x38658000, 0x3865C000, 0x38660000, 0x38664000, 0x38668000, 0x3866C000,\n      0x38670000, 0x38674000, 0x38678000, 0x3867C000, 0x38680000, 0x38684000,\n      0x38688000, 0x3868C000, 0x38690000, 0x38694000, 0x38698000, 0x3869C000,\n      0x386A0000, 0x386A4000, 0x386A8000, 0x386AC000, 0x386B0000, 0x386B4000,\n      0x386B8000, 0x386BC000, 0x386C0000, 0x386C4000, 0x386C8000, 0x386CC000,\n      0x386D0000, 0x386D4000, 0x386D8000, 0x386DC000, 0x386E0000, 0x386E4000,\n      0x386E8000, 0x386EC000, 0x386F0000, 0x386F4000, 0x386F8000, 0x386FC000,\n      0x38700000, 0x38704000, 0x38708000, 0x3870C000, 0x38710000, 0x38714000,\n      0x38718000, 0x3871C000, 0x38720000, 0x38724000, 0x38728000, 0x3872C000,\n      0x38730000, 0x38734000, 0x38738000, 0x3873C000, 0x38740000, 0x38744000,\n      0x38748000, 0x3874C000, 0x38750000, 0x38754000, 0x38758000, 0x3875C000,\n      0x38760000, 0x38764000, 0x38768000, 0x3876C000, 0x38770000, 0x38774000,\n      0x38778000, 0x3877C000, 0x38780000, 0x38784000, 0x38788000, 0x3878C000,\n      0x38790000, 0x38794000, 0x38798000, 0x3879C000, 0x387A0000, 0x387A4000,\n      0x387A8000, 0x387AC000, 0x387B0000, 0x387B4000, 0x387B8000, 0x387BC000,\n      0x387C0000, 0x387C4000, 0x387C8000, 0x387CC000, 0x387D0000, 0x387D4000,\n      0x387D8000, 0x387DC000, 0x387E0000, 0x387E4000, 0x387E8000, 0x387EC000,\n      0x387F0000, 0x387F4000, 0x387F8000, 0x387FC000, 0x38000000, 0x38002000,\n      0x38004000, 0x38006000, 0x38008000, 0x3800A000, 0x3800C000, 0x3800E000,\n      0x38010000, 0x38012000, 0x38014000, 0x38016000, 0x38018000, 0x3801A000,\n      0x3801C000, 0x3801E000, 0x38020000, 0x38022000, 0x38024000, 0x38026000,\n      0x38028000, 0x3802A000, 0x3802C000, 0x3802E000, 0x38030000, 0x38032000,\n      0x38034000, 0x38036000, 0x38038000, 0x3803A000, 0x3803C000, 0x3803E000,\n      0x38040000, 0x38042000, 0x38044000, 0x38046000, 0x38048000, 0x3804A000,\n      0x3804C000, 0x3804E000, 0x38050000, 0x38052000, 0x38054000, 0x38056000,\n      0x38058000, 0x3805A000, 0x3805C000, 0x3805E000, 0x38060000, 0x38062000,\n      0x38064000, 0x38066000, 0x38068000, 0x3806A000, 0x3806C000, 0x3806E000,\n      0x38070000, 0x38072000, 0x38074000, 0x38076000, 0x38078000, 0x3807A000,\n      0x3807C000, 0x3807E000, 0x38080000, 0x38082000, 0x38084000, 0x38086000,\n      0x38088000, 0x3808A000, 0x3808C000, 0x3808E000, 0x38090000, 0x38092000,\n      0x38094000, 0x38096000, 0x38098000, 0x3809A000, 0x3809C000, 0x3809E000,\n      0x380A0000, 0x380A2000, 0x380A4000, 0x380A6000, 0x380A8000, 0x380AA000,\n      0x380AC000, 0x380AE000, 0x380B0000, 0x380B2000, 0x380B4000, 0x380B6000,\n      0x380B8000, 0x380BA000, 0x380BC000, 0x380BE000, 0x380C0000, 0x380C2000,\n      0x380C4000, 0x380C6000, 0x380C8000, 0x380CA000, 0x380CC000, 0x380CE000,\n      0x380D0000, 0x380D2000, 0x380D4000, 0x380D6000, 0x380D8000, 0x380DA000,\n      0x380DC000, 0x380DE000, 0x380E0000, 0x380E2000, 0x380E4000, 0x380E6000,\n      0x380E8000, 0x380EA000, 0x380EC000, 0x380EE000, 0x380F0000, 0x380F2000,\n      0x380F4000, 0x380F6000, 0x380F8000, 0x380FA000, 0x380FC000, 0x380FE000,\n      0x38100000, 0x38102000, 0x38104000, 0x38106000, 0x38108000, 0x3810A000,\n      0x3810C000, 0x3810E000, 0x38110000, 0x38112000, 0x38114000, 0x38116000,\n      0x38118000, 0x3811A000, 0x3811C000, 0x3811E000, 0x38120000, 0x38122000,\n      0x38124000, 0x38126000, 0x38128000, 0x3812A000, 0x3812C000, 0x3812E000,\n      0x38130000, 0x38132000, 0x38134000, 0x38136000, 0x38138000, 0x3813A000,\n      0x3813C000, 0x3813E000, 0x38140000, 0x38142000, 0x38144000, 0x38146000,\n      0x38148000, 0x3814A000, 0x3814C000, 0x3814E000, 0x38150000, 0x38152000,\n      0x38154000, 0x38156000, 0x38158000, 0x3815A000, 0x3815C000, 0x3815E000,\n      0x38160000, 0x38162000, 0x38164000, 0x38166000, 0x38168000, 0x3816A000,\n      0x3816C000, 0x3816E000, 0x38170000, 0x38172000, 0x38174000, 0x38176000,\n      0x38178000, 0x3817A000, 0x3817C000, 0x3817E000, 0x38180000, 0x38182000,\n      0x38184000, 0x38186000, 0x38188000, 0x3818A000, 0x3818C000, 0x3818E000,\n      0x38190000, 0x38192000, 0x38194000, 0x38196000, 0x38198000, 0x3819A000,\n      0x3819C000, 0x3819E000, 0x381A0000, 0x381A2000, 0x381A4000, 0x381A6000,\n      0x381A8000, 0x381AA000, 0x381AC000, 0x381AE000, 0x381B0000, 0x381B2000,\n      0x381B4000, 0x381B6000, 0x381B8000, 0x381BA000, 0x381BC000, 0x381BE000,\n      0x381C0000, 0x381C2000, 0x381C4000, 0x381C6000, 0x381C8000, 0x381CA000,\n      0x381CC000, 0x381CE000, 0x381D0000, 0x381D2000, 0x381D4000, 0x381D6000,\n      0x381D8000, 0x381DA000, 0x381DC000, 0x381DE000, 0x381E0000, 0x381E2000,\n      0x381E4000, 0x381E6000, 0x381E8000, 0x381EA000, 0x381EC000, 0x381EE000,\n      0x381F0000, 0x381F2000, 0x381F4000, 0x381F6000, 0x381F8000, 0x381FA000,\n      0x381FC000, 0x381FE000, 0x38200000, 0x38202000, 0x38204000, 0x38206000,\n      0x38208000, 0x3820A000, 0x3820C000, 0x3820E000, 0x38210000, 0x38212000,\n      0x38214000, 0x38216000, 0x38218000, 0x3821A000, 0x3821C000, 0x3821E000,\n      0x38220000, 0x38222000, 0x38224000, 0x38226000, 0x38228000, 0x3822A000,\n      0x3822C000, 0x3822E000, 0x38230000, 0x38232000, 0x38234000, 0x38236000,\n      0x38238000, 0x3823A000, 0x3823C000, 0x3823E000, 0x38240000, 0x38242000,\n      0x38244000, 0x38246000, 0x38248000, 0x3824A000, 0x3824C000, 0x3824E000,\n      0x38250000, 0x38252000, 0x38254000, 0x38256000, 0x38258000, 0x3825A000,\n      0x3825C000, 0x3825E000, 0x38260000, 0x38262000, 0x38264000, 0x38266000,\n      0x38268000, 0x3826A000, 0x3826C000, 0x3826E000, 0x38270000, 0x38272000,\n      0x38274000, 0x38276000, 0x38278000, 0x3827A000, 0x3827C000, 0x3827E000,\n      0x38280000, 0x38282000, 0x38284000, 0x38286000, 0x38288000, 0x3828A000,\n      0x3828C000, 0x3828E000, 0x38290000, 0x38292000, 0x38294000, 0x38296000,\n      0x38298000, 0x3829A000, 0x3829C000, 0x3829E000, 0x382A0000, 0x382A2000,\n      0x382A4000, 0x382A6000, 0x382A8000, 0x382AA000, 0x382AC000, 0x382AE000,\n      0x382B0000, 0x382B2000, 0x382B4000, 0x382B6000, 0x382B8000, 0x382BA000,\n      0x382BC000, 0x382BE000, 0x382C0000, 0x382C2000, 0x382C4000, 0x382C6000,\n      0x382C8000, 0x382CA000, 0x382CC000, 0x382CE000, 0x382D0000, 0x382D2000,\n      0x382D4000, 0x382D6000, 0x382D8000, 0x382DA000, 0x382DC000, 0x382DE000,\n      0x382E0000, 0x382E2000, 0x382E4000, 0x382E6000, 0x382E8000, 0x382EA000,\n      0x382EC000, 0x382EE000, 0x382F0000, 0x382F2000, 0x382F4000, 0x382F6000,\n      0x382F8000, 0x382FA000, 0x382FC000, 0x382FE000, 0x38300000, 0x38302000,\n      0x38304000, 0x38306000, 0x38308000, 0x3830A000, 0x3830C000, 0x3830E000,\n      0x38310000, 0x38312000, 0x38314000, 0x38316000, 0x38318000, 0x3831A000,\n      0x3831C000, 0x3831E000, 0x38320000, 0x38322000, 0x38324000, 0x38326000,\n      0x38328000, 0x3832A000, 0x3832C000, 0x3832E000, 0x38330000, 0x38332000,\n      0x38334000, 0x38336000, 0x38338000, 0x3833A000, 0x3833C000, 0x3833E000,\n      0x38340000, 0x38342000, 0x38344000, 0x38346000, 0x38348000, 0x3834A000,\n      0x3834C000, 0x3834E000, 0x38350000, 0x38352000, 0x38354000, 0x38356000,\n      0x38358000, 0x3835A000, 0x3835C000, 0x3835E000, 0x38360000, 0x38362000,\n      0x38364000, 0x38366000, 0x38368000, 0x3836A000, 0x3836C000, 0x3836E000,\n      0x38370000, 0x38372000, 0x38374000, 0x38376000, 0x38378000, 0x3837A000,\n      0x3837C000, 0x3837E000, 0x38380000, 0x38382000, 0x38384000, 0x38386000,\n      0x38388000, 0x3838A000, 0x3838C000, 0x3838E000, 0x38390000, 0x38392000,\n      0x38394000, 0x38396000, 0x38398000, 0x3839A000, 0x3839C000, 0x3839E000,\n      0x383A0000, 0x383A2000, 0x383A4000, 0x383A6000, 0x383A8000, 0x383AA000,\n      0x383AC000, 0x383AE000, 0x383B0000, 0x383B2000, 0x383B4000, 0x383B6000,\n      0x383B8000, 0x383BA000, 0x383BC000, 0x383BE000, 0x383C0000, 0x383C2000,\n      0x383C4000, 0x383C6000, 0x383C8000, 0x383CA000, 0x383CC000, 0x383CE000,\n      0x383D0000, 0x383D2000, 0x383D4000, 0x383D6000, 0x383D8000, 0x383DA000,\n      0x383DC000, 0x383DE000, 0x383E0000, 0x383E2000, 0x383E4000, 0x383E6000,\n      0x383E8000, 0x383EA000, 0x383EC000, 0x383EE000, 0x383F0000, 0x383F2000,\n      0x383F4000, 0x383F6000, 0x383F8000, 0x383FA000, 0x383FC000, 0x383FE000,\n      0x38400000, 0x38402000, 0x38404000, 0x38406000, 0x38408000, 0x3840A000,\n      0x3840C000, 0x3840E000, 0x38410000, 0x38412000, 0x38414000, 0x38416000,\n      0x38418000, 0x3841A000, 0x3841C000, 0x3841E000, 0x38420000, 0x38422000,\n      0x38424000, 0x38426000, 0x38428000, 0x3842A000, 0x3842C000, 0x3842E000,\n      0x38430000, 0x38432000, 0x38434000, 0x38436000, 0x38438000, 0x3843A000,\n      0x3843C000, 0x3843E000, 0x38440000, 0x38442000, 0x38444000, 0x38446000,\n      0x38448000, 0x3844A000, 0x3844C000, 0x3844E000, 0x38450000, 0x38452000,\n      0x38454000, 0x38456000, 0x38458000, 0x3845A000, 0x3845C000, 0x3845E000,\n      0x38460000, 0x38462000, 0x38464000, 0x38466000, 0x38468000, 0x3846A000,\n      0x3846C000, 0x3846E000, 0x38470000, 0x38472000, 0x38474000, 0x38476000,\n      0x38478000, 0x3847A000, 0x3847C000, 0x3847E000, 0x38480000, 0x38482000,\n      0x38484000, 0x38486000, 0x38488000, 0x3848A000, 0x3848C000, 0x3848E000,\n      0x38490000, 0x38492000, 0x38494000, 0x38496000, 0x38498000, 0x3849A000,\n      0x3849C000, 0x3849E000, 0x384A0000, 0x384A2000, 0x384A4000, 0x384A6000,\n      0x384A8000, 0x384AA000, 0x384AC000, 0x384AE000, 0x384B0000, 0x384B2000,\n      0x384B4000, 0x384B6000, 0x384B8000, 0x384BA000, 0x384BC000, 0x384BE000,\n      0x384C0000, 0x384C2000, 0x384C4000, 0x384C6000, 0x384C8000, 0x384CA000,\n      0x384CC000, 0x384CE000, 0x384D0000, 0x384D2000, 0x384D4000, 0x384D6000,\n      0x384D8000, 0x384DA000, 0x384DC000, 0x384DE000, 0x384E0000, 0x384E2000,\n      0x384E4000, 0x384E6000, 0x384E8000, 0x384EA000, 0x384EC000, 0x384EE000,\n      0x384F0000, 0x384F2000, 0x384F4000, 0x384F6000, 0x384F8000, 0x384FA000,\n      0x384FC000, 0x384FE000, 0x38500000, 0x38502000, 0x38504000, 0x38506000,\n      0x38508000, 0x3850A000, 0x3850C000, 0x3850E000, 0x38510000, 0x38512000,\n      0x38514000, 0x38516000, 0x38518000, 0x3851A000, 0x3851C000, 0x3851E000,\n      0x38520000, 0x38522000, 0x38524000, 0x38526000, 0x38528000, 0x3852A000,\n      0x3852C000, 0x3852E000, 0x38530000, 0x38532000, 0x38534000, 0x38536000,\n      0x38538000, 0x3853A000, 0x3853C000, 0x3853E000, 0x38540000, 0x38542000,\n      0x38544000, 0x38546000, 0x38548000, 0x3854A000, 0x3854C000, 0x3854E000,\n      0x38550000, 0x38552000, 0x38554000, 0x38556000, 0x38558000, 0x3855A000,\n      0x3855C000, 0x3855E000, 0x38560000, 0x38562000, 0x38564000, 0x38566000,\n      0x38568000, 0x3856A000, 0x3856C000, 0x3856E000, 0x38570000, 0x38572000,\n      0x38574000, 0x38576000, 0x38578000, 0x3857A000, 0x3857C000, 0x3857E000,\n      0x38580000, 0x38582000, 0x38584000, 0x38586000, 0x38588000, 0x3858A000,\n      0x3858C000, 0x3858E000, 0x38590000, 0x38592000, 0x38594000, 0x38596000,\n      0x38598000, 0x3859A000, 0x3859C000, 0x3859E000, 0x385A0000, 0x385A2000,\n      0x385A4000, 0x385A6000, 0x385A8000, 0x385AA000, 0x385AC000, 0x385AE000,\n      0x385B0000, 0x385B2000, 0x385B4000, 0x385B6000, 0x385B8000, 0x385BA000,\n      0x385BC000, 0x385BE000, 0x385C0000, 0x385C2000, 0x385C4000, 0x385C6000,\n      0x385C8000, 0x385CA000, 0x385CC000, 0x385CE000, 0x385D0000, 0x385D2000,\n      0x385D4000, 0x385D6000, 0x385D8000, 0x385DA000, 0x385DC000, 0x385DE000,\n      0x385E0000, 0x385E2000, 0x385E4000, 0x385E6000, 0x385E8000, 0x385EA000,\n      0x385EC000, 0x385EE000, 0x385F0000, 0x385F2000, 0x385F4000, 0x385F6000,\n      0x385F8000, 0x385FA000, 0x385FC000, 0x385FE000, 0x38600000, 0x38602000,\n      0x38604000, 0x38606000, 0x38608000, 0x3860A000, 0x3860C000, 0x3860E000,\n      0x38610000, 0x38612000, 0x38614000, 0x38616000, 0x38618000, 0x3861A000,\n      0x3861C000, 0x3861E000, 0x38620000, 0x38622000, 0x38624000, 0x38626000,\n      0x38628000, 0x3862A000, 0x3862C000, 0x3862E000, 0x38630000, 0x38632000,\n      0x38634000, 0x38636000, 0x38638000, 0x3863A000, 0x3863C000, 0x3863E000,\n      0x38640000, 0x38642000, 0x38644000, 0x38646000, 0x38648000, 0x3864A000,\n      0x3864C000, 0x3864E000, 0x38650000, 0x38652000, 0x38654000, 0x38656000,\n      0x38658000, 0x3865A000, 0x3865C000, 0x3865E000, 0x38660000, 0x38662000,\n      0x38664000, 0x38666000, 0x38668000, 0x3866A000, 0x3866C000, 0x3866E000,\n      0x38670000, 0x38672000, 0x38674000, 0x38676000, 0x38678000, 0x3867A000,\n      0x3867C000, 0x3867E000, 0x38680000, 0x38682000, 0x38684000, 0x38686000,\n      0x38688000, 0x3868A000, 0x3868C000, 0x3868E000, 0x38690000, 0x38692000,\n      0x38694000, 0x38696000, 0x38698000, 0x3869A000, 0x3869C000, 0x3869E000,\n      0x386A0000, 0x386A2000, 0x386A4000, 0x386A6000, 0x386A8000, 0x386AA000,\n      0x386AC000, 0x386AE000, 0x386B0000, 0x386B2000, 0x386B4000, 0x386B6000,\n      0x386B8000, 0x386BA000, 0x386BC000, 0x386BE000, 0x386C0000, 0x386C2000,\n      0x386C4000, 0x386C6000, 0x386C8000, 0x386CA000, 0x386CC000, 0x386CE000,\n      0x386D0000, 0x386D2000, 0x386D4000, 0x386D6000, 0x386D8000, 0x386DA000,\n      0x386DC000, 0x386DE000, 0x386E0000, 0x386E2000, 0x386E4000, 0x386E6000,\n      0x386E8000, 0x386EA000, 0x386EC000, 0x386EE000, 0x386F0000, 0x386F2000,\n      0x386F4000, 0x386F6000, 0x386F8000, 0x386FA000, 0x386FC000, 0x386FE000,\n      0x38700000, 0x38702000, 0x38704000, 0x38706000, 0x38708000, 0x3870A000,\n      0x3870C000, 0x3870E000, 0x38710000, 0x38712000, 0x38714000, 0x38716000,\n      0x38718000, 0x3871A000, 0x3871C000, 0x3871E000, 0x38720000, 0x38722000,\n      0x38724000, 0x38726000, 0x38728000, 0x3872A000, 0x3872C000, 0x3872E000,\n      0x38730000, 0x38732000, 0x38734000, 0x38736000, 0x38738000, 0x3873A000,\n      0x3873C000, 0x3873E000, 0x38740000, 0x38742000, 0x38744000, 0x38746000,\n      0x38748000, 0x3874A000, 0x3874C000, 0x3874E000, 0x38750000, 0x38752000,\n      0x38754000, 0x38756000, 0x38758000, 0x3875A000, 0x3875C000, 0x3875E000,\n      0x38760000, 0x38762000, 0x38764000, 0x38766000, 0x38768000, 0x3876A000,\n      0x3876C000, 0x3876E000, 0x38770000, 0x38772000, 0x38774000, 0x38776000,\n      0x38778000, 0x3877A000, 0x3877C000, 0x3877E000, 0x38780000, 0x38782000,\n      0x38784000, 0x38786000, 0x38788000, 0x3878A000, 0x3878C000, 0x3878E000,\n      0x38790000, 0x38792000, 0x38794000, 0x38796000, 0x38798000, 0x3879A000,\n      0x3879C000, 0x3879E000, 0x387A0000, 0x387A2000, 0x387A4000, 0x387A6000,\n      0x387A8000, 0x387AA000, 0x387AC000, 0x387AE000, 0x387B0000, 0x387B2000,\n      0x387B4000, 0x387B6000, 0x387B8000, 0x387BA000, 0x387BC000, 0x387BE000,\n      0x387C0000, 0x387C2000, 0x387C4000, 0x387C6000, 0x387C8000, 0x387CA000,\n      0x387CC000, 0x387CE000, 0x387D0000, 0x387D2000, 0x387D4000, 0x387D6000,\n      0x387D8000, 0x387DA000, 0x387DC000, 0x387DE000, 0x387E0000, 0x387E2000,\n      0x387E4000, 0x387E6000, 0x387E8000, 0x387EA000, 0x387EC000, 0x387EE000,\n      0x387F0000, 0x387F2000, 0x387F4000, 0x387F6000, 0x387F8000, 0x387FA000,\n      0x387FC000, 0x387FE000};\n  static const bits<float>::type exponent_table[64] = {\n      0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000,\n      0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000,\n      0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000,\n      0x09000000, 0x09800000, 0x0A000000, 0x0A800000, 0x0B000000, 0x0B800000,\n      0x0C000000, 0x0C800000, 0x0D000000, 0x0D800000, 0x0E000000, 0x0E800000,\n      0x0F000000, 0x47800000, 0x80000000, 0x80800000, 0x81000000, 0x81800000,\n      0x82000000, 0x82800000, 0x83000000, 0x83800000, 0x84000000, 0x84800000,\n      0x85000000, 0x85800000, 0x86000000, 0x86800000, 0x87000000, 0x87800000,\n      0x88000000, 0x88800000, 0x89000000, 0x89800000, 0x8A000000, 0x8A800000,\n      0x8B000000, 0x8B800000, 0x8C000000, 0x8C800000, 0x8D000000, 0x8D800000,\n      0x8E000000, 0x8E800000, 0x8F000000, 0xC7800000};\n  static const unsigned short offset_table[64] = {\n      0,    1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,\n      1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,\n      1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 0,\n      1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,\n      1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024,\n      1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024};\n  bits<float>::type fbits =\n      mantissa_table[offset_table[value >> 10] + (value & 0x3FF)] +\n      exponent_table[value >> 10];\n#endif\n  float out;\n  std::memcpy(&out, &fbits, sizeof(float));\n  return out;\n#endif\n}\n\n/// Convert half-precision to IEEE double-precision.\n/// \\param value half-precision value to convert\n/// \\return double-precision value\ninline double half2float_impl(unsigned int value, double, true_type) {\n#if HALF_ENABLE_F16C_INTRINSICS\n  return _mm_cvtsd_f64(_mm_cvtps_pd(_mm_cvtph_ps(_mm_cvtsi32_si128(value))));\n#else\n  uint32 hi = static_cast<uint32>(value & 0x8000) << 16;\n  unsigned int abs = value & 0x7FFF;\n  if (abs) {\n    hi |= 0x3F000000 << static_cast<unsigned>(abs >= 0x7C00);\n    for (; abs < 0x400; abs <<= 1, hi -= 0x100000)\n      ;\n    hi += static_cast<uint32>(abs) << 10;\n  }\n  bits<double>::type dbits = static_cast<bits<double>::type>(hi) << 32;\n  double out;\n  std::memcpy(&out, &dbits, sizeof(double));\n  return out;\n#endif\n}\n\n/// Convert half-precision to non-IEEE floating-point.\n/// \\tparam T type to convert to (builtin integer type)\n/// \\param value half-precision value to convert\n/// \\return floating-point value\ntemplate <typename T>\nT half2float_impl(unsigned int value, T, ...) {\n  T out;\n  unsigned int abs = value & 0x7FFF;\n  if (abs > 0x7C00)\n    out = (std::numeric_limits<T>::has_signaling_NaN && !(abs & 0x200))\n              ? std::numeric_limits<T>::signaling_NaN()\n              : std::numeric_limits<T>::has_quiet_NaN\n                    ? std::numeric_limits<T>::quiet_NaN()\n                    : T();\n  else if (abs == 0x7C00)\n    out = std::numeric_limits<T>::has_infinity\n              ? std::numeric_limits<T>::infinity()\n              : std::numeric_limits<T>::max();\n  else if (abs > 0x3FF)\n    out = std::ldexp(static_cast<T>((abs & 0x3FF) | 0x400), (abs >> 10) - 25);\n  else\n    out = std::ldexp(static_cast<T>(abs), -24);\n  return (value & 0x8000) ? -out : out;\n}\n\n/// Convert half-precision to floating-point.\n/// \\tparam T type to convert to (builtin integer type)\n/// \\param value half-precision value to convert\n/// \\return floating-point value\ntemplate <typename T>\nT half2float(unsigned int value) {\n  return half2float_impl(value, T(),\n                         bool_type < std::numeric_limits<T>::is_iec559 &&\n                             sizeof(typename bits<T>::type) == sizeof(T) > ());\n}\n\n/// Convert half-precision floating-point to integer.\n/// \\tparam R rounding mode to use\n/// \\tparam E `true` for round to even, `false` for round away from zero\n/// \\tparam I `true` to raise INEXACT exception (if inexact), `false` to never\n/// raise it\n/// \\tparam T type to convert to (buitlin integer type with at least 16 bits\n/// precision, excluding any implicit sign bits)\n/// \\param value half-precision value to convert\n/// \\return rounded integer value\n/// \\exception FE_INVALID if value is not representable in type \\a T\n/// \\exception FE_INEXACT if value had to be rounded and \\a I is `true`\ntemplate <std::float_round_style R, bool E, bool I, typename T>\nT half2int(unsigned int value) {\n  unsigned int abs = value & 0x7FFF;\n  if (abs >= 0x7C00) {\n    raise(FE_INVALID);\n    return (value & 0x8000) ? std::numeric_limits<T>::min()\n                            : std::numeric_limits<T>::max();\n  }\n  if (abs < 0x3800) {\n    raise(FE_INEXACT, I);\n    return (R == std::round_toward_infinity)\n               ? T(~(value >> 15) & (abs != 0))\n               : (R == std::round_toward_neg_infinity) ? -T(value > 0x8000)\n                                                       : T();\n  }\n  int exp = 25 - (abs >> 10);\n  unsigned int m = (value & 0x3FF) | 0x400;\n  int32 i = static_cast<int32>(\n      (exp <= 0) ? (m << -exp)\n                 : ((m + ((R == std::round_to_nearest)\n                              ? ((1 << (exp - 1)) - (~(m >> exp) & E))\n                              : (R == std::round_toward_infinity)\n                                    ? (((1 << exp) - 1) & ((value >> 15) - 1))\n                                    : (R == std::round_toward_neg_infinity)\n                                          ? (((1 << exp) - 1) & -(value >> 15))\n                                          : 0)) >>\n                    exp));\n  if ((!std::numeric_limits<T>::is_signed && (value & 0x8000)) ||\n      (std::numeric_limits<T>::digits < 16 &&\n       ((value & 0x8000) ? (-i < std::numeric_limits<T>::min())\n                         : (i > std::numeric_limits<T>::max()))))\n    raise(FE_INVALID);\n  else if (I && exp > 0 && (m & ((1 << exp) - 1)))\n    raise(FE_INEXACT);\n  return static_cast<T>((value & 0x8000) ? -i : i);\n}\n\n/// \\}\n/// \\name Mathematics\n/// \\{\n\n/// upper part of 64-bit multiplication.\n/// \\tparam R rounding mode to use\n/// \\param x first factor\n/// \\param y second factor\n/// \\return upper 32 bit of \\a x * \\a y\ntemplate <std::float_round_style R>\nuint32 mulhi(uint32 x, uint32 y) {\n  uint32 xy = (x >> 16) * (y & 0xFFFF), yx = (x & 0xFFFF) * (y >> 16),\n         c = (xy & 0xFFFF) + (yx & 0xFFFF) +\n             (((x & 0xFFFF) * (y & 0xFFFF)) >> 16);\n  return (x >> 16) * (y >> 16) + (xy >> 16) + (yx >> 16) + (c >> 16) +\n         ((R == std::round_to_nearest)\n              ? ((c >> 15) & 1)\n              : (R == std::round_toward_infinity) ? ((c & 0xFFFF) != 0) : 0);\n}\n\n/// 64-bit multiplication.\n/// \\param x first factor\n/// \\param y second factor\n/// \\return upper 32 bit of \\a x * \\a y rounded to nearest\ninline uint32 multiply64(uint32 x, uint32 y) {\n#if HALF_ENABLE_CPP11_LONG_LONG\n  return static_cast<uint32>(\n      (static_cast<unsigned long long>(x) * static_cast<unsigned long long>(y) +\n       0x80000000) >>\n      32);\n#else\n  return mulhi<std::round_to_nearest>(x, y);\n#endif\n}\n\n/// 64-bit division.\n/// \\param x upper 32 bit of dividend\n/// \\param y divisor\n/// \\param s variable to store sticky bit for rounding\n/// \\return (\\a x << 32) / \\a y\ninline uint32 divide64(uint32 x, uint32 y, int &s) {\n#if HALF_ENABLE_CPP11_LONG_LONG\n  unsigned long long xx = static_cast<unsigned long long>(x) << 32;\n  return s = (xx % y != 0), static_cast<uint32>(xx / y);\n#else\n  y >>= 1;\n  uint32 rem = x, div = 0;\n  for (unsigned int i = 0; i < 32; ++i) {\n    div <<= 1;\n    if (rem >= y) {\n      rem -= y;\n      div |= 1;\n    }\n    rem <<= 1;\n  }\n  return s = rem > 1, div;\n#endif\n}\n\n/// Half precision positive modulus.\n/// \\tparam Q `true` to compute full quotient, `false` else\n/// \\tparam R `true` to compute signed remainder, `false` for positive remainder\n/// \\param x first operand as positive finite half-precision value\n/// \\param y second operand as positive finite half-precision value\n/// \\param quo adress to store quotient at, `nullptr` if \\a Q `false`\n/// \\return modulus of \\a x / \\a y\ntemplate <bool Q, bool R>\nunsigned int mod(unsigned int x, unsigned int y, int *quo = NULL) {\n  unsigned int q = 0;\n  if (x > y) {\n    int absx = x, absy = y, expx = 0, expy = 0;\n    for (; absx < 0x400; absx <<= 1, --expx)\n      ;\n    for (; absy < 0x400; absy <<= 1, --expy)\n      ;\n    expx += absx >> 10;\n    expy += absy >> 10;\n    int mx = (absx & 0x3FF) | 0x400, my = (absy & 0x3FF) | 0x400;\n    for (int d = expx - expy; d; --d) {\n      if (!Q && mx == my) return 0;\n      if (mx >= my) {\n        mx -= my;\n        q += Q;\n      }\n      mx <<= 1;\n      q <<= static_cast<int>(Q);\n    }\n    if (!Q && mx == my) return 0;\n    if (mx >= my) {\n      mx -= my;\n      ++q;\n    }\n    if (Q) {\n      q &= (1 << (std::numeric_limits<int>::digits - 1)) - 1;\n      if (!mx) return *quo = q, 0;\n    }\n    for (; mx < 0x400; mx <<= 1, --expy)\n      ;\n    x = (expy > 0) ? ((expy << 10) | (mx & 0x3FF)) : (mx >> (1 - expy));\n  }\n  if (R) {\n    unsigned int a, b;\n    if (y < 0x800) {\n      a = (x < 0x400) ? (x << 1) : (x + 0x400);\n      b = y;\n    } else {\n      a = x;\n      b = y - 0x400;\n    }\n    if (a > b || (a == b && (q & 1))) {\n      int exp = (y >> 10) + (y <= 0x3FF), d = exp - (x >> 10) - (x <= 0x3FF);\n      int m = (((y & 0x3FF) | ((y > 0x3FF) << 10)) << 1) -\n              (((x & 0x3FF) | ((x > 0x3FF) << 10)) << (1 - d));\n      for (; m < 0x800 && exp > 1; m <<= 1, --exp)\n        ;\n      x = 0x8000 + ((exp - 1) << 10) + (m >> 1);\n      q += Q;\n    }\n  }\n  if (Q) *quo = q;\n  return x;\n}\n\n/// Fixed point square root.\n/// \\tparam F number of fractional bits\n/// \\param r radicand in Q1.F fixed point format\n/// \\param exp exponent\n/// \\return square root as Q1.F/2\ntemplate <unsigned int F>\nuint32 sqrt(uint32 &r, int &exp) {\n  int i = exp & 1;\n  r <<= i;\n  exp = (exp - i) / 2;\n  uint32 m = 0;\n  for (uint32 bit = static_cast<uint32>(1) << F; bit; bit >>= 2) {\n    if (r < m + bit)\n      m >>= 1;\n    else {\n      r -= m + bit;\n      m = (m >> 1) + bit;\n    }\n  }\n  return m;\n}\n\n/// Fixed point binary exponential.\n/// This uses the BKM algorithm in E-mode.\n/// \\param m exponent in [0,1) as Q0.31\n/// \\param n number of iterations (at most 32)\n/// \\return 2 ^ \\a m as Q1.31\ninline uint32 exp2(uint32 m, unsigned int n = 32) {\n  static const uint32 logs[] = {\n      0x80000000, 0x4AE00D1D, 0x2934F098, 0x15C01A3A, 0x0B31FB7D, 0x05AEB4DD,\n      0x02DCF2D1, 0x016FE50B, 0x00B84E23, 0x005C3E10, 0x002E24CA, 0x001713D6,\n      0x000B8A47, 0x0005C53B, 0x0002E2A3, 0x00017153, 0x0000B8AA, 0x00005C55,\n      0x00002E2B, 0x00001715, 0x00000B8B, 0x000005C5, 0x000002E3, 0x00000171,\n      0x000000B9, 0x0000005C, 0x0000002E, 0x00000017, 0x0000000C, 0x00000006,\n      0x00000003, 0x00000001};\n  if (!m) return 0x80000000;\n  uint32 mx = 0x80000000, my = 0;\n  for (unsigned int i = 1; i < n; ++i) {\n    uint32 mz = my + logs[i];\n    if (mz <= m) {\n      my = mz;\n      mx += mx >> i;\n    }\n  }\n  return mx;\n}\n\n/// Fixed point binary logarithm.\n/// This uses the BKM algorithm in L-mode.\n/// \\param m mantissa in [1,2) as Q1.30\n/// \\param n number of iterations (at most 32)\n/// \\return log2(\\a m) as Q0.31\ninline uint32 log2(uint32 m, unsigned int n = 32) {\n  static const uint32 logs[] = {\n      0x80000000, 0x4AE00D1D, 0x2934F098, 0x15C01A3A, 0x0B31FB7D, 0x05AEB4DD,\n      0x02DCF2D1, 0x016FE50B, 0x00B84E23, 0x005C3E10, 0x002E24CA, 0x001713D6,\n      0x000B8A47, 0x0005C53B, 0x0002E2A3, 0x00017153, 0x0000B8AA, 0x00005C55,\n      0x00002E2B, 0x00001715, 0x00000B8B, 0x000005C5, 0x000002E3, 0x00000171,\n      0x000000B9, 0x0000005C, 0x0000002E, 0x00000017, 0x0000000C, 0x00000006,\n      0x00000003, 0x00000001};\n  if (m == 0x40000000) return 0;\n  uint32 mx = 0x40000000, my = 0;\n  for (unsigned int i = 1; i < n; ++i) {\n    uint32 mz = mx + (mx >> i);\n    if (mz <= m) {\n      mx = mz;\n      my += logs[i];\n    }\n  }\n  return my;\n}\n\n/// Fixed point sine and cosine.\n/// This uses the CORDIC algorithm in rotation mode.\n/// \\param mz angle in [-pi/2,pi/2] as Q1.30\n/// \\param n number of iterations (at most 31)\n/// \\return sine and cosine of \\a mz as Q1.30\ninline std::pair<uint32, uint32> sincos(uint32 mz, unsigned int n = 31) {\n  static const uint32 angles[] = {\n      0x3243F6A9, 0x1DAC6705, 0x0FADBAFD, 0x07F56EA7, 0x03FEAB77, 0x01FFD55C,\n      0x00FFFAAB, 0x007FFF55, 0x003FFFEB, 0x001FFFFD, 0x00100000, 0x00080000,\n      0x00040000, 0x00020000, 0x00010000, 0x00008000, 0x00004000, 0x00002000,\n      0x00001000, 0x00000800, 0x00000400, 0x00000200, 0x00000100, 0x00000080,\n      0x00000040, 0x00000020, 0x00000010, 0x00000008, 0x00000004, 0x00000002,\n      0x00000001};\n  uint32 mx = 0x26DD3B6A, my = 0;\n  for (unsigned int i = 0; i < n; ++i) {\n    uint32 sign = sign_mask(mz);\n    uint32 tx = mx - (arithmetic_shift(my, i) ^ sign) + sign;\n    uint32 ty = my + (arithmetic_shift(mx, i) ^ sign) - sign;\n    mx = tx;\n    my = ty;\n    mz -= (angles[i] ^ sign) - sign;\n  }\n  return std::make_pair(my, mx);\n}\n\n/// Fixed point arc tangent.\n/// This uses the CORDIC algorithm in vectoring mode.\n/// \\param my y coordinate as Q0.30\n/// \\param mx x coordinate as Q0.30\n/// \\param n number of iterations (at most 31)\n/// \\return arc tangent of \\a my / \\a mx as Q1.30\ninline uint32 atan2(uint32 my, uint32 mx, unsigned int n = 31) {\n  static const uint32 angles[] = {\n      0x3243F6A9, 0x1DAC6705, 0x0FADBAFD, 0x07F56EA7, 0x03FEAB77, 0x01FFD55C,\n      0x00FFFAAB, 0x007FFF55, 0x003FFFEB, 0x001FFFFD, 0x00100000, 0x00080000,\n      0x00040000, 0x00020000, 0x00010000, 0x00008000, 0x00004000, 0x00002000,\n      0x00001000, 0x00000800, 0x00000400, 0x00000200, 0x00000100, 0x00000080,\n      0x00000040, 0x00000020, 0x00000010, 0x00000008, 0x00000004, 0x00000002,\n      0x00000001};\n  uint32 mz = 0;\n  for (unsigned int i = 0; i < n; ++i) {\n    uint32 sign = sign_mask(my);\n    uint32 tx = mx + (arithmetic_shift(my, i) ^ sign) - sign;\n    uint32 ty = my - (arithmetic_shift(mx, i) ^ sign) + sign;\n    mx = tx;\n    my = ty;\n    mz += (angles[i] ^ sign) - sign;\n  }\n  return mz;\n}\n\n/// Reduce argument for trigonometric functions.\n/// \\param abs half-precision floating-point value\n/// \\param k value to take quarter period\n/// \\return \\a abs reduced to [-pi/4,pi/4] as Q0.30\ninline uint32 angle_arg(unsigned int abs, int &k) {\n  uint32 m = (abs & 0x3FF) | ((abs > 0x3FF) << 10);\n  int exp = (abs >> 10) + (abs <= 0x3FF) - 15;\n  if (abs < 0x3A48) return k = 0, m << (exp + 20);\n#if HALF_ENABLE_CPP11_LONG_LONG\n  unsigned long long y = m * 0xA2F9836E4E442, mask = (1ULL << (62 - exp)) - 1,\n                     yi = (y + (mask >> 1)) & ~mask, f = y - yi;\n  uint32 sign = -static_cast<uint32>(f >> 63);\n  k = static_cast<int>(yi >> (62 - exp));\n  return (multiply64(static_cast<uint32>((sign ? -f : f) >> (31 - exp)),\n                     0xC90FDAA2) ^\n          sign) -\n         sign;\n#else\n  uint32 yh = m * 0xA2F98 + mulhi<std::round_toward_zero>(m, 0x36E4E442),\n         yl = (m * 0x36E4E442) & 0xFFFFFFFF;\n  uint32 mask = (static_cast<uint32>(1) << (30 - exp)) - 1,\n         yi = (yh + (mask >> 1)) & ~mask, sign = -static_cast<uint32>(yi > yh);\n  k = static_cast<int>(yi >> (30 - exp));\n  uint32 fh = (yh ^ sign) + (yi ^ ~sign) - ~sign, fl = (yl ^ sign) - sign;\n  return (multiply64((exp > -1) ? (((fh << (1 + exp)) & 0xFFFFFFFF) |\n                                   ((fl & 0xFFFFFFFF) >> (31 - exp)))\n                                : fh,\n                     0xC90FDAA2) ^\n          sign) -\n         sign;\n#endif\n}\n\n/// Get arguments for atan2 function.\n/// \\param abs half-precision floating-point value\n/// \\return \\a abs and sqrt(1 - \\a abs^2) as Q0.30\ninline std::pair<uint32, uint32> atan2_args(unsigned int abs) {\n  int exp = -15;\n  for (; abs < 0x400; abs <<= 1, --exp)\n    ;\n  exp += abs >> 10;\n  uint32 my = ((abs & 0x3FF) | 0x400) << 5, r = my * my;\n  int rexp = 2 * exp;\n  r = 0x40000000 - ((rexp > -31)\n                        ? ((r >> -rexp) |\n                           ((r & ((static_cast<uint32>(1) << -rexp) - 1)) != 0))\n                        : 1);\n  for (rexp = 0; r < 0x40000000; r <<= 1, --rexp)\n    ;\n  uint32 mx = sqrt<30>(r, rexp);\n  int d = exp - rexp;\n  if (d < 0)\n    return std::make_pair((d < -14)\n                              ? ((my >> (-d - 14)) + ((my >> (-d - 15)) & 1))\n                              : (my << (14 + d)),\n                          (mx << 14) + (r << 13) / mx);\n  if (d > 0)\n    return std::make_pair(\n        my << 14,\n        (d > 14)\n            ? ((mx >> (d - 14)) + ((mx >> (d - 15)) & 1))\n            : ((d == 14) ? mx : ((mx << (14 - d)) + (r << (13 - d)) / mx)));\n  return std::make_pair(my << 13, (mx << 13) + (r << 12) / mx);\n}\n\n/// Get exponentials for hyperbolic computation\n/// \\param abs half-precision floating-point value\n/// \\param exp variable to take unbiased exponent of larger result\n/// \\param n number of BKM iterations (at most 32)\n/// \\return exp(abs) and exp(-\\a abs) as Q1.31 with same exponent\ninline std::pair<uint32, uint32> hyperbolic_args(unsigned int abs, int &exp,\n                                                 unsigned int n = 32) {\n  uint32 mx = detail::multiply64(\n             static_cast<uint32>((abs & 0x3FF) + ((abs > 0x3FF) << 10)) << 21,\n             0xB8AA3B29),\n         my;\n  int e = (abs >> 10) + (abs <= 0x3FF);\n  if (e < 14) {\n    exp = 0;\n    mx >>= 14 - e;\n  } else {\n    exp = mx >> (45 - e);\n    mx = (mx << (e - 14)) & 0x7FFFFFFF;\n  }\n  mx = exp2(mx, n);\n  int d = exp << 1, s;\n  if (mx > 0x80000000) {\n    my = divide64(0x80000000, mx, s);\n    my |= s;\n    ++d;\n  } else\n    my = mx;\n  return std::make_pair(\n      mx,\n      (d < 31) ? ((my >> d) | ((my & ((static_cast<uint32>(1) << d) - 1)) != 0))\n               : 1);\n}\n\n/// Postprocessing for binary exponential.\n/// \\tparam R rounding mode to use\n/// \\tparam I `true` to always raise INEXACT exception, `false` to raise only\n/// for rounded results\n/// \\param m mantissa as Q1.31\n/// \\param exp absolute value of unbiased exponent\n/// \\param esign sign of actual exponent\n/// \\param sign sign bit of result\n/// \\return value converted to half-precision\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if value had to be rounded or \\a I is `true`\ntemplate <std::float_round_style R, bool I>\nunsigned int exp2_post(uint32 m, int exp, bool esign, unsigned int sign = 0) {\n  int s = 0;\n  if (esign) {\n    if (m > 0x80000000) {\n      m = divide64(0x80000000, m, s);\n      ++exp;\n    }\n    if (exp > 25)\n      return underflow<R>(sign);\n    else if (exp == 25)\n      return rounded<R, I>(sign, 1, (m & 0x7FFFFFFF) != 0);\n    exp = -exp;\n  } else if (exp > 15)\n    return overflow<R>(sign);\n  return fixed2half<R, 31, false, false, I>(m, exp + 14, sign, s);\n}\n\n/// Postprocessing for binary logarithm.\n/// \\tparam R rounding mode to use\n/// \\tparam L logarithm for base transformation as Q1.31\n/// \\param m fractional part of logarithm as Q0.31\n/// \\param ilog signed integer part of logarithm\n/// \\param exp biased exponent of result\n/// \\param sign sign bit of result\n/// \\return value base-transformed and converted to half-precision\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if no other exception occurred\ntemplate <std::float_round_style R, uint32 L>\nunsigned int log2_post(uint32 m, int ilog, int exp, unsigned int sign = 0) {\n  uint32 msign = sign_mask(ilog);\n  m = (((static_cast<uint32>(ilog) << 27) + (m >> 4)) ^ msign) - msign;\n  if (!m) return 0;\n  for (; m < 0x80000000; m <<= 1, --exp)\n    ;\n  int i = m >= L, s;\n  exp += i;\n  m >>= 1 + i;\n  sign ^= msign & 0x8000;\n  if (exp < -11) return underflow<R>(sign);\n  m = divide64(m, L, s);\n  return fixed2half<R, 30, false, false, true>(m, exp, sign, 1);\n}\n\n/// Hypotenuse square root and postprocessing.\n/// \\tparam R rounding mode to use\n/// \\param r mantissa as Q2.30\n/// \\param exp unbiased exponent\n/// \\return square root converted to half-precision\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if value had to be rounded\ntemplate <std::float_round_style R>\nunsigned int hypot_post(uint32 r, int exp) {\n  int i = r >> 31;\n  if ((exp += i) > 46) return overflow<R>();\n  if (exp < -34) return underflow<R>();\n  r = (r >> i) | (r & i);\n  uint32 m = sqrt<30>(r, exp += 15);\n  return fixed2half<R, 15, false, false, false>(m, exp - 1, 0, r != 0);\n}\n\n/// Division and postprocessing for tangents.\n/// \\tparam R rounding mode to use\n/// \\param my dividend as Q1.31\n/// \\param mx divisor as Q1.31\n/// \\param exp biased exponent of result\n/// \\param sign sign bit of result\n/// \\return quotient converted to half-precision\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if no other exception occurred\ntemplate <std::float_round_style R>\nunsigned int tangent_post(uint32 my, uint32 mx, int exp,\n                          unsigned int sign = 0) {\n  int i = my >= mx, s;\n  exp += i;\n  if (exp > 29) return overflow<R>(sign);\n  if (exp < -11) return underflow<R>(sign);\n  uint32 m = divide64(my >> (i + 1), mx, s);\n  return fixed2half<R, 30, false, false, true>(m, exp, sign, s);\n}\n\n/// Area function and postprocessing.\n/// This computes the value directly in Q2.30 using the representation\n/// `asinh|acosh(x) = log(x+sqrt(x^2+|-1))`.\n/// \\tparam R rounding mode to use\n/// \\tparam S `true` for asinh, `false` for acosh\n/// \\param arg half-precision argument\n/// \\return asinh|acosh(\\a arg) converted to half-precision\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if no other exception occurred\ntemplate <std::float_round_style R, bool S>\nunsigned int area(unsigned int arg) {\n  int abs = arg & 0x7FFF, expx = (abs >> 10) + (abs <= 0x3FF) - 15, expy = -15,\n      ilog, i;\n  uint32 mx = static_cast<uint32>((abs & 0x3FF) | ((abs > 0x3FF) << 10)) << 20,\n         my, r;\n  for (; abs < 0x400; abs <<= 1, --expy)\n    ;\n  expy += abs >> 10;\n  r = ((abs & 0x3FF) | 0x400) << 5;\n  r *= r;\n  i = r >> 31;\n  expy = 2 * expy + i;\n  r >>= i;\n  if (S) {\n    if (expy < 0) {\n      r = 0x40000000 +\n          ((expy > -30) ? ((r >> -expy) |\n                           ((r & ((static_cast<uint32>(1) << -expy) - 1)) != 0))\n                        : 1);\n      expy = 0;\n    } else {\n      r += 0x40000000 >> expy;\n      i = r >> 31;\n      r = (r >> i) | (r & i);\n      expy += i;\n    }\n  } else {\n    r -= 0x40000000 >> expy;\n    for (; r < 0x40000000; r <<= 1, --expy)\n      ;\n  }\n  my = sqrt<30>(r, expy);\n  my = (my << 15) + (r << 14) / my;\n  if (S) {\n    mx >>= expy - expx;\n    ilog = expy;\n  } else {\n    my >>= expx - expy;\n    ilog = expx;\n  }\n  my += mx;\n  i = my >> 31;\n  static const int G = S && (R == std::round_to_nearest);\n  return log2_post<R, 0xB8AA3B2A>(log2(my >> i, 26 + S + G) + (G << 3),\n                                  ilog + i, 17,\n                                  arg & (static_cast<unsigned>(S) << 15));\n}\n\n/// Class for 1.31 unsigned floating-point computation\nstruct f31 {\n  /// Constructor.\n  /// \\param mant mantissa as 1.31\n  /// \\param e exponent\n  HALF_CONSTEXPR f31(uint32 mant, int e) : m(mant), exp(e) {}\n\n  /// Constructor.\n  /// \\param abs unsigned half-precision value\n  f31(unsigned int abs) : exp(-15) {\n    for (; abs < 0x400; abs <<= 1, --exp)\n      ;\n    m = static_cast<uint32>((abs & 0x3FF) | 0x400) << 21;\n    exp += (abs >> 10);\n  }\n\n  /// Addition operator.\n  /// \\param a first operand\n  /// \\param b second operand\n  /// \\return \\a a + \\a b\n  friend f31 operator+(f31 a, f31 b) {\n    if (b.exp > a.exp) std::swap(a, b);\n    int d = a.exp - b.exp;\n    uint32 m = a.m + ((d < 32) ? (b.m >> d) : 0);\n    int i = (m & 0xFFFFFFFF) < a.m;\n    return f31(((m + i) >> i) | 0x80000000, a.exp + i);\n  }\n\n  /// Subtraction operator.\n  /// \\param a first operand\n  /// \\param b second operand\n  /// \\return \\a a - \\a b\n  friend f31 operator-(f31 a, f31 b) {\n    int d = a.exp - b.exp, exp = a.exp;\n    uint32 m = a.m - ((d < 32) ? (b.m >> d) : 0);\n    if (!m) return f31(0, -32);\n    for (; m < 0x80000000; m <<= 1, --exp)\n      ;\n    return f31(m, exp);\n  }\n\n  /// Multiplication operator.\n  /// \\param a first operand\n  /// \\param b second operand\n  /// \\return \\a a * \\a b\n  friend f31 operator*(f31 a, f31 b) {\n    uint32 m = multiply64(a.m, b.m);\n    int i = m >> 31;\n    return f31(m << (1 - i), a.exp + b.exp + i);\n  }\n\n  /// Division operator.\n  /// \\param a first operand\n  /// \\param b second operand\n  /// \\return \\a a / \\a b\n  friend f31 operator/(f31 a, f31 b) {\n    int i = a.m >= b.m, s;\n    uint32 m = divide64((a.m + i) >> i, b.m, s);\n    return f31(m, a.exp - b.exp + i - 1);\n  }\n\n  uint32 m;  ///< mantissa as 1.31.\n  int exp;   ///< exponent.\n};\n\n/// Error function and postprocessing.\n/// This computes the value directly in Q1.31 using the approximations given\n/// [here](https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions).\n/// \\tparam R rounding mode to use\n/// \\tparam C `true` for comlementary error function, `false` else\n/// \\param arg half-precision function argument\n/// \\return approximated value of error function in half-precision\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if no other exception occurred\ntemplate <std::float_round_style R, bool C>\nunsigned int erf(unsigned int arg) {\n  unsigned int abs = arg & 0x7FFF, sign = arg & 0x8000;\n  f31 x(abs),\n      x2 = x * x * f31(0xB8AA3B29, 0),\n      t = f31(0x80000000, 0) / (f31(0x80000000, 0) + f31(0xA7BA054A, -2) * x),\n      t2 = t * t;\n  f31 e = ((f31(0x87DC2213, 0) * t2 + f31(0xB5F0E2AE, 0)) * t2 +\n           f31(0x82790637, -2) -\n           (f31(0xBA00E2B8, 0) * t2 + f31(0x91A98E62, -2)) * t) *\n          t / ((x2.exp < 0)\n                   ? f31(exp2((x2.exp > -32) ? (x2.m >> -x2.exp) : 0, 30), 0)\n                   : f31(exp2((x2.m << x2.exp) & 0x7FFFFFFF, 22),\n                         x2.m >> (31 - x2.exp)));\n  return (!C || sign)\n             ? fixed2half<R, 31, false, true, true>(\n                   0x80000000 - (e.m >> (C - e.exp)), 14 + C, sign & (C - 1U))\n             : (e.exp < -25) ? underflow<R>()\n                             : fixed2half<R, 30, false, false, true>(\n                                   e.m >> 1, e.exp + 14, 0, e.m & 1);\n}\n\n/// Gamma function and postprocessing.\n/// This approximates the value of either the gamma function or its logarithm\n/// directly in Q1.31.\n/// \\tparam R rounding mode to use\n/// \\tparam L `true` for lograithm of gamma function, `false` for gamma function\n/// \\param arg half-precision floating-point value\n/// \\return lgamma/tgamma(\\a arg) in half-precision\n/// \\exception FE_OVERFLOW on overflows\n/// \\exception FE_UNDERFLOW on underflows\n/// \\exception FE_INEXACT if \\a arg is not a positive integer\ntemplate <std::float_round_style R, bool L>\nunsigned int gamma(unsigned int arg) {\n  /*\t\t\tstatic const double p[] ={ 2.50662827563479526904, 225.525584619175212544, -268.295973841304927459, 80.9030806934622512966, -5.00757863970517583837, 0.0114684895434781459556 };\n\t\t\tdouble t = arg + 4.65, s = p[0];\n\t\t\tfor(unsigned int i=0; i<5; ++i)\n\t\t\t\ts += p[i+1] / (arg+i);\n\t\t\treturn std::log(s) + (arg-0.5)*std::log(t) - t;\n*/ static const f31 pi(0xC90FDAA2, 1),\n      lbe(0xB8AA3B29, 0);\n  unsigned int abs = arg & 0x7FFF, sign = arg & 0x8000;\n  bool bsign = sign != 0;\n  f31 z(abs),\n      x = sign ? (z + f31(0x80000000, 0)) : z, t = x + f31(0x94CCCCCD, 2),\n      s = f31(0xA06C9901, 1) + f31(0xBBE654E2, -7) / (x + f31(0x80000000, 2)) +\n          f31(0xA1CE6098, 6) / (x + f31(0x80000000, 1)) +\n          f31(0xE1868CB7, 7) / x -\n          f31(0x8625E279, 8) / (x + f31(0x80000000, 0)) -\n          f31(0xA03E158F, 2) / (x + f31(0xC0000000, 1));\n  int i = (s.exp >= 2) + (s.exp >= 4) + (s.exp >= 8) + (s.exp >= 16);\n  s = f31((static_cast<uint32>(s.exp) << (31 - i)) + (log2(s.m >> 1, 28) >> i),\n          i) /\n      lbe;\n  if (x.exp != -1 || x.m != 0x80000000) {\n    i = (t.exp >= 2) + (t.exp >= 4) + (t.exp >= 8);\n    f31 l = f31((static_cast<uint32>(t.exp) << (31 - i)) +\n                    (log2(t.m >> 1, 30) >> i),\n                i) /\n            lbe;\n    s = (x.exp < -1) ? (s - (f31(0x80000000, -1) - x) * l)\n                     : (s + (x - f31(0x80000000, -1)) * l);\n  }\n  s = x.exp ? (s - t) : (t - s);\n  if (bsign) {\n    if (z.exp >= 0) {\n      sign &= (L | ((z.m >> (31 - z.exp)) & 1)) - 1;\n      for (z = f31((z.m << (1 + z.exp)) & 0xFFFFFFFF, -1); z.m < 0x80000000;\n           z.m <<= 1, --z.exp)\n        ;\n    }\n    if (z.exp == -1) z = f31(0x80000000, 0) - z;\n    if (z.exp < -1) {\n      z = z * pi;\n      z.m = sincos(z.m >> (1 - z.exp), 30).first;\n      for (z.exp = 1; z.m < 0x80000000; z.m <<= 1, --z.exp)\n        ;\n    } else\n      z = f31(0x80000000, 0);\n  }\n  if (L) {\n    if (bsign) {\n      f31 l(0x92868247, 0);\n      if (z.exp < 0) {\n        uint32 m = log2((z.m + 1) >> 1, 27);\n        z = f31(-((static_cast<uint32>(z.exp) << 26) + (m >> 5)), 5);\n        for (; z.m < 0x80000000; z.m <<= 1, --z.exp)\n          ;\n        l = l + z / lbe;\n      }\n      sign = static_cast<unsigned>(\n                 x.exp && (l.exp < s.exp || (l.exp == s.exp && l.m < s.m)))\n             << 15;\n      s = sign ? (s - l) : x.exp ? (l - s) : (l + s);\n    } else {\n      sign = static_cast<unsigned>(x.exp == 0) << 15;\n      if (s.exp < -24) return underflow<R>(sign);\n      if (s.exp > 15) return overflow<R>(sign);\n    }\n  } else {\n    s = s * lbe;\n    uint32 m;\n    if (s.exp < 0) {\n      m = s.m >> -s.exp;\n      s.exp = 0;\n    } else {\n      m = (s.m << s.exp) & 0x7FFFFFFF;\n      s.exp = (s.m >> (31 - s.exp));\n    }\n    s.m = exp2(m, 27);\n    if (!x.exp) s = f31(0x80000000, 0) / s;\n    if (bsign) {\n      if (z.exp < 0) s = s * z;\n      s = pi / s;\n      if (s.exp < -24) return underflow<R>(sign);\n    } else if (z.exp > 0 && !(z.m & ((1 << (31 - z.exp)) - 1)))\n      return ((s.exp + 14) << 10) + (s.m >> 21);\n    if (s.exp > 15) return overflow<R>(sign);\n  }\n  return fixed2half<R, 31, false, false, true>(s.m, s.exp + 14, sign);\n}\n/// \\}\n\ntemplate <typename, typename, std::float_round_style>\nstruct half_caster;\n}\n\n/// Half-precision floating-point type.\n/// This class implements an IEEE-conformant half-precision floating-point type\n/// with the usual arithmetic\n/// operators and conversions. It is implicitly convertible to single-precision\n/// floating-point, which makes artihmetic\n/// expressions and functions with mixed-type operands to be of the most precise\n/// operand type.\n///\n/// According to the C++98/03 definition, the half type is not a POD type. But\n/// according to C++11's less strict and\n/// extended definitions it is both a standard layout type and a trivially\n/// copyable type (even if not a POD type), which\n/// means it can be standard-conformantly copied using raw binary copies. But in\n/// this context some more words about the\n/// actual size of the type. Although the half is representing an IEEE 16-bit\n/// type, it does not neccessarily have to be of\n/// exactly 16-bits size. But on any reasonable implementation the actual binary\n/// representation of this type will most\n/// probably not ivolve any additional \"magic\" or padding beyond the simple\n/// binary representation of the underlying 16-bit\n/// IEEE number, even if not strictly guaranteed by the standard. But even then\n/// it only has an actual size of 16 bits if\n/// your C++ implementation supports an unsigned integer type of exactly 16 bits\n/// width. But this should be the case on\n/// nearly any reasonable platform.\n///\n/// So if your C++ implementation is not totally exotic or imposes special\n/// alignment requirements, it is a reasonable\n/// assumption that the data of a half is just comprised of the 2 bytes of the\n/// underlying IEEE representation.\nclass half {\n public:\n  /// \\name Construction and assignment\n  /// \\{\n\n  /// Default constructor.\n  /// This initializes the half to 0. Although this does not match the builtin\n  /// types' default-initialization semantics\n  /// and may be less efficient than no initialization, it is needed to provide\n  /// proper value-initialization semantics.\n  HALF_CONSTEXPR half() HALF_NOEXCEPT : data_() {}\n\n  /// Conversion constructor.\n  /// \\param rhs float to convert\n  /// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\n  explicit half(float rhs)\n      : data_(\n            static_cast<detail::uint16>(detail::float2half<round_style>(rhs))) {\n  }\n\n  /// Conversion to single-precision.\n  /// \\return single precision value representing expression value\n  operator float() const { return detail::half2float<float>(data_); }\n\n  /// Assignment operator.\n  /// \\param rhs single-precision value to copy from\n  /// \\return reference to this half\n  /// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\n  half &operator=(float rhs) {\n    data_ = static_cast<detail::uint16>(detail::float2half<round_style>(rhs));\n    return *this;\n  }\n\n  /// \\}\n  /// \\name Arithmetic updates\n  /// \\{\n\n  /// Arithmetic assignment.\n  /// \\tparam T type of concrete half expression\n  /// \\param rhs half expression to add\n  /// \\return reference to this half\n  /// \\exception FE_... according to operator+(half,half)\n  half &operator+=(half rhs) { return *this = *this + rhs; }\n\n  /// Arithmetic assignment.\n  /// \\tparam T type of concrete half expression\n  /// \\param rhs half expression to subtract\n  /// \\return reference to this half\n  /// \\exception FE_... according to operator-(half,half)\n  half &operator-=(half rhs) { return *this = *this - rhs; }\n\n  /// Arithmetic assignment.\n  /// \\tparam T type of concrete half expression\n  /// \\param rhs half expression to multiply with\n  /// \\return reference to this half\n  /// \\exception FE_... according to operator*(half,half)\n  half &operator*=(half rhs) { return *this = *this * rhs; }\n\n  /// Arithmetic assignment.\n  /// \\tparam T type of concrete half expression\n  /// \\param rhs half expression to divide by\n  /// \\return reference to this half\n  /// \\exception FE_... according to operator/(half,half)\n  half &operator/=(half rhs) { return *this = *this / rhs; }\n\n  /// Arithmetic assignment.\n  /// \\param rhs single-precision value to add\n  /// \\return reference to this half\n  /// \\exception FE_... according to operator=()\n  half &operator+=(float rhs) { return *this = *this + rhs; }\n\n  /// Arithmetic assignment.\n  /// \\param rhs single-precision value to subtract\n  /// \\return reference to this half\n  /// \\exception FE_... according to operator=()\n  half &operator-=(float rhs) { return *this = *this - rhs; }\n\n  /// Arithmetic assignment.\n  /// \\param rhs single-precision value to multiply with\n  /// \\return reference to this half\n  /// \\exception FE_... according to operator=()\n  half &operator*=(float rhs) { return *this = *this * rhs; }\n\n  /// Arithmetic assignment.\n  /// \\param rhs single-precision value to divide by\n  /// \\return reference to this half\n  /// \\exception FE_... according to operator=()\n  half &operator/=(float rhs) { return *this = *this / rhs; }\n\n  /// \\}\n  /// \\name Increment and decrement\n  /// \\{\n\n  /// Prefix increment.\n  /// \\return incremented half value\n  /// \\exception FE_... according to operator+(half,half)\n  half &operator++() { return *this = *this + half(detail::binary, 0x3C00); }\n\n  /// Prefix decrement.\n  /// \\return decremented half value\n  /// \\exception FE_... according to operator-(half,half)\n  half &operator--() { return *this = *this + half(detail::binary, 0xBC00); }\n\n  /// Postfix increment.\n  /// \\return non-incremented half value\n  /// \\exception FE_... according to operator+(half,half)\n  half operator++(int) {\n    half out(*this);\n    ++*this;\n    return out;\n  }\n\n  /// Postfix decrement.\n  /// \\return non-decremented half value\n  /// \\exception FE_... according to operator-(half,half)\n  half operator--(int) {\n    half out(*this);\n    --*this;\n    return out;\n  }\n  /// \\}\n\n  detail::uint16 get_data() const { return data_; }\n\n private:\n  /// Rounding mode to use\n  static const std::float_round_style round_style =\n      (std::float_round_style)(HALF_ROUND_STYLE);\n\n  /// Constructor.\n  /// \\param bits binary representation to set half to\n  HALF_CONSTEXPR half(detail::binary_t, unsigned int bits) HALF_NOEXCEPT\n      : data_(static_cast<detail::uint16>(bits)) {}\n\n  /// Internal binary representation\n  detail::uint16 data_;\n\n#ifndef HALF_DOXYGEN_ONLY\n  friend HALF_CONSTEXPR_NOERR bool operator==(half, half);\n  friend HALF_CONSTEXPR_NOERR bool operator!=(half, half);\n  friend HALF_CONSTEXPR_NOERR bool operator<(half, half);\n  friend HALF_CONSTEXPR_NOERR bool operator>(half, half);\n  friend HALF_CONSTEXPR_NOERR bool operator<=(half, half);\n  friend HALF_CONSTEXPR_NOERR bool operator>=(half, half);\n  friend HALF_CONSTEXPR half operator-(half);\n  friend half operator+(half, half);\n  friend half operator-(half, half);\n  friend half operator*(half, half);\n  friend half operator/(half, half);\n  template <typename charT, typename traits>\n  friend std::basic_ostream<charT, traits> &operator<<(\n      std::basic_ostream<charT, traits> &, half);\n  template <typename charT, typename traits>\n  friend std::basic_istream<charT, traits> &operator>>(\n      std::basic_istream<charT, traits> &, half &);\n  friend HALF_CONSTEXPR half fabs(half);\n  friend half fmod(half, half);\n  friend half remainder(half, half);\n  friend half remquo(half, half, int *);\n  friend half fma(half, half, half);\n  friend HALF_CONSTEXPR_NOERR half fmax(half, half);\n  friend HALF_CONSTEXPR_NOERR half fmin(half, half);\n  friend half fdim(half, half);\n  friend half nanh(const char *);\n  friend half exp(half);\n  friend half exp2(half);\n  friend half expm1(half);\n  friend half log(half);\n  friend half log10(half);\n  friend half log2(half);\n  friend half log1p(half);\n  friend half sqrt(half);\n  friend half cbrt(half);\n  friend half hypot(half, half);\n  friend half hypot(half, half, half);\n  friend half pow(half, half);\n  friend void sincos(half, half *, half *);\n  friend half sin(half);\n  friend half cos(half);\n  friend half tan(half);\n  friend half asin(half);\n  friend half acos(half);\n  friend half atan(half);\n  friend half atan2(half, half);\n  friend half sinh(half);\n  friend half cosh(half);\n  friend half tanh(half);\n  friend half asinh(half);\n  friend half acosh(half);\n  friend half atanh(half);\n  friend half erf(half);\n  friend half erfc(half);\n  friend half lgamma(half);\n  friend half tgamma(half);\n  friend half ceil(half);\n  friend half floor(half);\n  friend half trunc(half);\n  friend half round(half);\n  friend long lround(half);\n  friend half rint(half);\n  friend long lrint(half);\n  friend half nearbyint(half);\n#ifdef HALF_ENABLE_CPP11_LONG_LONG\n  friend long long llround(half);\n  friend long long llrint(half);\n#endif\n  friend half frexp(half, int *);\n  friend half scalbln(half, long);\n  friend half modf(half, half *);\n  friend int ilogb(half);\n  friend half logb(half);\n  friend half nextafter(half, half);\n  friend half nexttoward(half, long double);\n  friend HALF_CONSTEXPR half copysign(half, half);\n  friend HALF_CONSTEXPR int fpclassify(half);\n  friend HALF_CONSTEXPR bool isfinite(half);\n  friend HALF_CONSTEXPR bool isinf(half);\n  friend HALF_CONSTEXPR bool isnan(half);\n  friend HALF_CONSTEXPR bool isnormal(half);\n  friend HALF_CONSTEXPR bool signbit(half);\n  friend HALF_CONSTEXPR bool isgreater(half, half);\n  friend HALF_CONSTEXPR bool isgreaterequal(half, half);\n  friend HALF_CONSTEXPR bool isless(half, half);\n  friend HALF_CONSTEXPR bool islessequal(half, half);\n  friend HALF_CONSTEXPR bool islessgreater(half, half);\n  template <typename, typename, std::float_round_style>\n  friend struct detail::half_caster;\n  friend class std::numeric_limits<half>;\n#if HALF_ENABLE_CPP11_HASH\n  friend struct std::hash<half>;\n#endif\n#if HALF_ENABLE_CPP11_USER_LITERALS\n  friend half literal::operator\"\" _h(long double);\n#endif\n#endif\n};\n\n#if HALF_ENABLE_CPP11_USER_LITERALS\nnamespace literal {\n/// Half literal.\n/// While this returns a properly rounded half-precision value, half literals\n/// can unfortunately not be constant\n/// expressions due to rather involved conversions. So don't expect this to be a\n/// literal literal without involving\n/// conversion operations at runtime. It is a convenience feature, not a\n/// performance optimization.\n/// \\param value literal value\n/// \\return half with of given value (possibly rounded)\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half operator\"\" _h(long double value) {\n  return half(detail::binary, detail::float2half<half::round_style>(value));\n}\n}\n#endif\n\nnamespace detail {\n/// Helper class for half casts.\n/// This class template has to be specialized for all valid cast arguments to\n/// define an appropriate static\n/// `cast` member function and a corresponding `type` member denoting its return\n/// type.\n/// \\tparam T destination type\n/// \\tparam U source type\n/// \\tparam R rounding mode to use\ntemplate <typename T, typename U,\n          std::float_round_style R = (std::float_round_style)(HALF_ROUND_STYLE)>\nstruct half_caster {};\ntemplate <typename U, std::float_round_style R>\nstruct half_caster<half, U, R> {\n#if HALF_ENABLE_CPP11_STATIC_ASSERT && HALF_ENABLE_CPP11_TYPE_TRAITS\n  static_assert(std::is_arithmetic<U>::value,\n                \"half_cast from non-arithmetic type unsupported\");\n#endif\n\n  static half cast(U arg) { return cast_impl(arg, is_float<U>()); };\n\n private:\n  static half cast_impl(U arg, true_type) {\n    return half(binary, float2half<R>(arg));\n  }\n  static half cast_impl(U arg, false_type) {\n    return half(binary, int2half<R>(arg));\n  }\n};\ntemplate <typename T, std::float_round_style R>\nstruct half_caster<T, half, R> {\n#if HALF_ENABLE_CPP11_STATIC_ASSERT && HALF_ENABLE_CPP11_TYPE_TRAITS\n  static_assert(std::is_arithmetic<T>::value,\n                \"half_cast to non-arithmetic type unsupported\");\n#endif\n\n  static T cast(half arg) { return cast_impl(arg, is_float<T>()); }\n\n private:\n  static T cast_impl(half arg, true_type) { return half2float<T>(arg.data_); }\n  static T cast_impl(half arg, false_type) {\n    return half2int<R, true, true, T>(arg.data_);\n  }\n};\ntemplate <std::float_round_style R>\nstruct half_caster<half, half, R> {\n  static half cast(half arg) { return arg; }\n};\n}\n}\n\n/// Extensions to the C++ standard library.\nnamespace std {\n/// Numeric limits for half-precision floats.\n/// **See also:** Documentation for\n/// [std::numeric_limits](https://en.cppreference.com/w/cpp/types/numeric_limits)\ntemplate <>\nclass numeric_limits<half_float::half> {\n public:\n  /// Is template specialization.\n  static HALF_CONSTEXPR_CONST bool is_specialized = true;\n\n  /// Supports signed values.\n  static HALF_CONSTEXPR_CONST bool is_signed = true;\n\n  /// Is not an integer type.\n  static HALF_CONSTEXPR_CONST bool is_integer = false;\n\n  /// Is not exact.\n  static HALF_CONSTEXPR_CONST bool is_exact = false;\n\n  /// Doesn't provide modulo arithmetic.\n  static HALF_CONSTEXPR_CONST bool is_modulo = false;\n\n  /// Has a finite set of values.\n  static HALF_CONSTEXPR_CONST bool is_bounded = true;\n\n  /// IEEE conformant.\n  static HALF_CONSTEXPR_CONST bool is_iec559 = true;\n\n  /// Supports infinity.\n  static HALF_CONSTEXPR_CONST bool has_infinity = true;\n\n  /// Supports quiet NaNs.\n  static HALF_CONSTEXPR_CONST bool has_quiet_NaN = true;\n\n  /// Supports signaling NaNs.\n  static HALF_CONSTEXPR_CONST bool has_signaling_NaN = true;\n\n  /// Supports subnormal values.\n  static HALF_CONSTEXPR_CONST float_denorm_style has_denorm = denorm_present;\n\n  /// Supports no denormalization detection.\n  static HALF_CONSTEXPR_CONST bool has_denorm_loss = false;\n\n#if HALF_ERRHANDLING_THROWS\n  static HALF_CONSTEXPR_CONST bool traps = true;\n#else\n  /// Traps only if [HALF_ERRHANDLING_THROW_...](\\ref\n  /// HALF_ERRHANDLING_THROW_INVALID) is acitvated.\n  static HALF_CONSTEXPR_CONST bool traps = false;\n#endif\n\n  /// Does not support no pre-rounding underflow detection.\n  static HALF_CONSTEXPR_CONST bool tinyness_before = false;\n\n  /// Rounding mode.\n  static HALF_CONSTEXPR_CONST float_round_style round_style =\n      half_float::half::round_style;\n\n  /// Significant digits.\n  static HALF_CONSTEXPR_CONST int digits = 11;\n\n  /// Significant decimal digits.\n  static HALF_CONSTEXPR_CONST int digits10 = 3;\n\n  /// Required decimal digits to represent all possible values.\n  static HALF_CONSTEXPR_CONST int max_digits10 = 5;\n\n  /// Number base.\n  static HALF_CONSTEXPR_CONST int radix = 2;\n\n  /// One more than smallest exponent.\n  static HALF_CONSTEXPR_CONST int min_exponent = -13;\n\n  /// Smallest normalized representable power of 10.\n  static HALF_CONSTEXPR_CONST int min_exponent10 = -4;\n\n  /// One more than largest exponent\n  static HALF_CONSTEXPR_CONST int max_exponent = 16;\n\n  /// Largest finitely representable power of 10.\n  static HALF_CONSTEXPR_CONST int max_exponent10 = 4;\n\n  /// Smallest positive normal value.\n  static HALF_CONSTEXPR half_float::half min() HALF_NOTHROW {\n    return half_float::half(half_float::detail::binary, 0x0400);\n  }\n\n  /// Smallest finite value.\n  static HALF_CONSTEXPR half_float::half lowest() HALF_NOTHROW {\n    return half_float::half(half_float::detail::binary, 0xFBFF);\n  }\n\n  /// Largest finite value.\n  static HALF_CONSTEXPR half_float::half max() HALF_NOTHROW {\n    return half_float::half(half_float::detail::binary, 0x7BFF);\n  }\n\n  /// Difference between 1 and next representable value.\n  static HALF_CONSTEXPR half_float::half epsilon() HALF_NOTHROW {\n    return half_float::half(half_float::detail::binary, 0x1400);\n  }\n\n  /// Maximum rounding error in ULP (units in the last place).\n  static HALF_CONSTEXPR half_float::half round_error() HALF_NOTHROW {\n    return half_float::half(\n        half_float::detail::binary,\n        (round_style == std::round_to_nearest) ? 0x3800 : 0x3C00);\n  }\n\n  /// Positive infinity.\n  static HALF_CONSTEXPR half_float::half infinity() HALF_NOTHROW {\n    return half_float::half(half_float::detail::binary, 0x7C00);\n  }\n\n  /// Quiet NaN.\n  static HALF_CONSTEXPR half_float::half quiet_NaN() HALF_NOTHROW {\n    return half_float::half(half_float::detail::binary, 0x7FFF);\n  }\n\n  /// Signaling NaN.\n  static HALF_CONSTEXPR half_float::half signaling_NaN() HALF_NOTHROW {\n    return half_float::half(half_float::detail::binary, 0x7DFF);\n  }\n\n  /// Smallest positive subnormal value.\n  static HALF_CONSTEXPR half_float::half denorm_min() HALF_NOTHROW {\n    return half_float::half(half_float::detail::binary, 0x0001);\n  }\n};\n\n#if HALF_ENABLE_CPP11_HASH\n/// Hash function for half-precision floats.\n/// This is only defined if C++11 `std::hash` is supported and enabled.\n///\n/// **See also:** Documentation for\n/// [std::hash](https://en.cppreference.com/w/cpp/utility/hash)\ntemplate <>\nstruct hash<half_float::half> {\n  /// Type of function argument.\n  typedef half_float::half argument_type;\n\n  /// Function return type.\n  typedef size_t result_type;\n\n  /// Compute hash function.\n  /// \\param arg half to hash\n  /// \\return hash value\n  result_type operator()(argument_type arg) const {\n    return hash<half_float::detail::uint16>()(\n        arg.data_ & -static_cast<unsigned>(arg.data_ != 0x8000));\n  }\n};\n#endif\n}\n\nnamespace half_float {\n/// \\anchor compop\n/// \\name Comparison operators\n/// \\{\n\n/// Comparison for equality.\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if operands equal\n/// \\retval false else\n/// \\exception FE_INVALID if \\a x or \\a y is NaN\ninline HALF_CONSTEXPR_NOERR bool operator==(half x, half y) {\n  return !detail::compsignal(x.data_, y.data_) &&\n         (x.data_ == y.data_ || !((x.data_ | y.data_) & 0x7FFF));\n}\n\n/// Comparison for inequality.\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if operands not equal\n/// \\retval false else\n/// \\exception FE_INVALID if \\a x or \\a y is NaN\ninline HALF_CONSTEXPR_NOERR bool operator!=(half x, half y) {\n  return detail::compsignal(x.data_, y.data_) ||\n         (x.data_ != y.data_ && ((x.data_ | y.data_) & 0x7FFF));\n}\n\n/// Comparison for less than.\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if \\a x less than \\a y\n/// \\retval false else\n/// \\exception FE_INVALID if \\a x or \\a y is NaN\ninline HALF_CONSTEXPR_NOERR bool operator<(half x, half y) {\n  return !detail::compsignal(x.data_, y.data_) &&\n         ((x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) + (x.data_ >> 15)) <\n             ((y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15)))) +\n              (y.data_ >> 15));\n}\n\n/// Comparison for greater than.\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if \\a x greater than \\a y\n/// \\retval false else\n/// \\exception FE_INVALID if \\a x or \\a y is NaN\ninline HALF_CONSTEXPR_NOERR bool operator>(half x, half y) {\n  return !detail::compsignal(x.data_, y.data_) &&\n         ((x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) + (x.data_ >> 15)) >\n             ((y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15)))) +\n              (y.data_ >> 15));\n}\n\n/// Comparison for less equal.\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if \\a x less equal \\a y\n/// \\retval false else\n/// \\exception FE_INVALID if \\a x or \\a y is NaN\ninline HALF_CONSTEXPR_NOERR bool operator<=(half x, half y) {\n  return !detail::compsignal(x.data_, y.data_) &&\n         ((x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) +\n          (x.data_ >> 15)) <=\n             ((y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15)))) +\n              (y.data_ >> 15));\n}\n\n/// Comparison for greater equal.\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if \\a x greater equal \\a y\n/// \\retval false else\n/// \\exception FE_INVALID if \\a x or \\a y is NaN\ninline HALF_CONSTEXPR_NOERR bool operator>=(half x, half y) {\n  return !detail::compsignal(x.data_, y.data_) &&\n         ((x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) +\n          (x.data_ >> 15)) >=\n             ((y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15)))) +\n              (y.data_ >> 15));\n}\n\n/// \\}\n/// \\anchor arithmetics\n/// \\name Arithmetic operators\n/// \\{\n\n/// Identity.\n/// \\param arg operand\n/// \\return unchanged operand\ninline HALF_CONSTEXPR half operator+(half arg) { return arg; }\n\n/// Negation.\n/// \\param arg operand\n/// \\return negated operand\ninline HALF_CONSTEXPR half operator-(half arg) {\n  return half(detail::binary, arg.data_ ^ 0x8000);\n}\n\n/// Addition.\n/// This operation is exact to rounding for all rounding modes.\n/// \\param x left operand\n/// \\param y right operand\n/// \\return sum of half expressions\n/// \\exception FE_INVALID if \\a x and \\a y are infinities with different signs\n/// or signaling NaNs\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half operator+(half x, half y) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  detail::half2float<detail::internal_t>(x.data_) +\n                  detail::half2float<detail::internal_t>(y.data_)));\n#else\n  int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF;\n  bool sub = ((x.data_ ^ y.data_) & 0x8000) != 0;\n  if (absx >= 0x7C00 || absy >= 0x7C00)\n    return half(\n        detail::binary,\n        (absx > 0x7C00 || absy > 0x7C00)\n            ? detail::signal(x.data_, y.data_)\n            : (absy != 0x7C00)\n                  ? x.data_\n                  : (sub && absx == 0x7C00) ? detail::invalid() : y.data_);\n  if (!absx)\n    return absy ? y : half(detail::binary,\n                           (half::round_style == std::round_toward_neg_infinity)\n                               ? (x.data_ | y.data_)\n                               : (x.data_ & y.data_));\n  if (!absy) return x;\n  unsigned int sign = ((sub && absy > absx) ? y.data_ : x.data_) & 0x8000;\n  if (absy > absx) std::swap(absx, absy);\n  int exp = (absx >> 10) + (absx <= 0x3FF),\n      d = exp - (absy >> 10) - (absy <= 0x3FF),\n      mx = ((absx & 0x3FF) | ((absx > 0x3FF) << 10)) << 3, my;\n  if (d < 13) {\n    my = ((absy & 0x3FF) | ((absy > 0x3FF) << 10)) << 3;\n    my = (my >> d) | ((my & ((1 << d) - 1)) != 0);\n  } else\n    my = 1;\n  if (sub) {\n    if (!(mx -= my))\n      return half(detail::binary,\n                  static_cast<unsigned>(half::round_style ==\n                                        std::round_toward_neg_infinity)\n                      << 15);\n    for (; mx < 0x2000 && exp > 1; mx <<= 1, --exp)\n      ;\n  } else {\n    mx += my;\n    int i = mx >> 14;\n    if ((exp += i) > 30)\n      return half(detail::binary, detail::overflow<half::round_style>(sign));\n    mx = (mx >> i) | (mx & i);\n  }\n  return half(detail::binary, detail::rounded<half::round_style, false>(\n                                  sign + ((exp - 1) << 10) + (mx >> 3),\n                                  (mx >> 2) & 1, (mx & 0x3) != 0));\n#endif\n}\n\n/// Subtraction.\n/// This operation is exact to rounding for all rounding modes.\n/// \\param x left operand\n/// \\param y right operand\n/// \\return difference of half expressions\n/// \\exception FE_INVALID if \\a x and \\a y are infinities with equal signs or\n/// signaling NaNs\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half operator-(half x, half y) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  detail::half2float<detail::internal_t>(x.data_) -\n                  detail::half2float<detail::internal_t>(y.data_)));\n#else\n  return x + -y;\n#endif\n}\n\n/// Multiplication.\n/// This operation is exact to rounding for all rounding modes.\n/// \\param x left operand\n/// \\param y right operand\n/// \\return product of half expressions\n/// \\exception FE_INVALID if multiplying 0 with infinity or if \\a x or \\a y is\n/// signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half operator*(half x, half y) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  detail::half2float<detail::internal_t>(x.data_) *\n                  detail::half2float<detail::internal_t>(y.data_)));\n#else\n  int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF, exp = -16;\n  unsigned int sign = (x.data_ ^ y.data_) & 0x8000;\n  if (absx >= 0x7C00 || absy >= 0x7C00)\n    return half(detail::binary,\n                (absx > 0x7C00 || absy > 0x7C00)\n                    ? detail::signal(x.data_, y.data_)\n                    : ((absx == 0x7C00 && !absy) || (absy == 0x7C00 && !absx))\n                          ? detail::invalid()\n                          : (sign | 0x7C00));\n  if (!absx || !absy) return half(detail::binary, sign);\n  for (; absx < 0x400; absx <<= 1, --exp)\n    ;\n  for (; absy < 0x400; absy <<= 1, --exp)\n    ;\n  detail::uint32 m = static_cast<detail::uint32>((absx & 0x3FF) | 0x400) *\n                     static_cast<detail::uint32>((absy & 0x3FF) | 0x400);\n  int i = m >> 21, s = m & i;\n  exp += (absx >> 10) + (absy >> 10) + i;\n  if (exp > 29)\n    return half(detail::binary, detail::overflow<half::round_style>(sign));\n  else if (exp < -11)\n    return half(detail::binary, detail::underflow<half::round_style>(sign));\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 20, false, false, false>(\n                  m >> i, exp, sign, s));\n#endif\n}\n\n/// Division.\n/// This operation is exact to rounding for all rounding modes.\n/// \\param x left operand\n/// \\param y right operand\n/// \\return quotient of half expressions\n/// \\exception FE_INVALID if dividing 0s or infinities with each other or if \\a\n/// x or \\a y is signaling NaN\n/// \\exception FE_DIVBYZERO if dividing finite value by 0\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half operator/(half x, half y) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  detail::half2float<detail::internal_t>(x.data_) /\n                  detail::half2float<detail::internal_t>(y.data_)));\n#else\n  int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF, exp = 14;\n  unsigned int sign = (x.data_ ^ y.data_) & 0x8000;\n  if (absx >= 0x7C00 || absy >= 0x7C00)\n    return half(detail::binary,\n                (absx > 0x7C00 || absy > 0x7C00)\n                    ? detail::signal(x.data_, y.data_)\n                    : (absx == absy)\n                          ? detail::invalid()\n                          : (sign | ((absx == 0x7C00) ? 0x7C00 : 0)));\n  if (!absx) return half(detail::binary, absy ? sign : detail::invalid());\n  if (!absy) return half(detail::binary, detail::pole(sign));\n  for (; absx < 0x400; absx <<= 1, --exp)\n    ;\n  for (; absy < 0x400; absy <<= 1, ++exp)\n    ;\n  detail::uint32 mx = (absx & 0x3FF) | 0x400, my = (absy & 0x3FF) | 0x400;\n  int i = mx < my;\n  exp += (absx >> 10) - (absy >> 10) - i;\n  if (exp > 29)\n    return half(detail::binary, detail::overflow<half::round_style>(sign));\n  else if (exp < -11)\n    return half(detail::binary, detail::underflow<half::round_style>(sign));\n  mx <<= 12 + i;\n  my <<= 1;\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 11, false, false, false>(\n                  mx / my, exp, sign, mx % my != 0));\n#endif\n}\n\n/// \\}\n/// \\anchor streaming\n/// \\name Input and output\n/// \\{\n\n/// Output operator.\n///\tThis uses the built-in functionality for streaming out floating-point\n///numbers.\n/// \\param out output stream to write into\n/// \\param arg half expression to write\n/// \\return reference to output stream\ntemplate <typename charT, typename traits>\nstd::basic_ostream<charT, traits> &operator<<(\n    std::basic_ostream<charT, traits> &out, half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return out << detail::half2float<detail::internal_t>(arg.data_);\n#else\n  return out << detail::half2float<float>(arg.data_);\n#endif\n}\n\n/// Input operator.\n///\tThis uses the built-in functionality for streaming in floating-point\n///numbers, specifically double precision floating\n/// point numbers (unless overridden with [HALF_ARITHMETIC_TYPE](\\ref\n/// HALF_ARITHMETIC_TYPE)). So the input string is first\n/// rounded to double precision using the underlying platform's current\n/// floating-point rounding mode before being rounded\n/// to half-precision using the library's half-precision rounding mode.\n/// \\param in input stream to read from\n/// \\param arg half to read into\n/// \\return reference to input stream\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ntemplate <typename charT, typename traits>\nstd::basic_istream<charT, traits> &operator>>(\n    std::basic_istream<charT, traits> &in, half &arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  detail::internal_t f;\n#else\n  double f;\n#endif\n  if (in >> f) arg.data_ = detail::float2half<half::round_style>(f);\n  return in;\n}\n\n/// \\}\n/// \\anchor basic\n/// \\name Basic mathematical operations\n/// \\{\n\n/// Absolute value.\n/// **See also:** Documentation for\n/// [std::fabs](https://en.cppreference.com/w/cpp/numeric/math/fabs).\n/// \\param arg operand\n/// \\return absolute value of \\a arg\ninline HALF_CONSTEXPR half fabs(half arg) {\n  return half(detail::binary, arg.data_ & 0x7FFF);\n}\n\n/// Absolute value.\n/// **See also:** Documentation for\n/// [std::abs](https://en.cppreference.com/w/cpp/numeric/math/fabs).\n/// \\param arg operand\n/// \\return absolute value of \\a arg\ninline HALF_CONSTEXPR half abs(half arg) { return fabs(arg); }\n\n/// Remainder of division.\n/// **See also:** Documentation for\n/// [std::fmod](https://en.cppreference.com/w/cpp/numeric/math/fmod).\n/// \\param x first operand\n/// \\param y second operand\n/// \\return remainder of floating-point division.\n/// \\exception FE_INVALID if \\a x is infinite or \\a y is 0 or if \\a x or \\a y is\n/// signaling NaN\ninline half fmod(half x, half y) {\n  unsigned int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF,\n               sign = x.data_ & 0x8000;\n  if (absx >= 0x7C00 || absy >= 0x7C00)\n    return half(detail::binary,\n                (absx > 0x7C00 || absy > 0x7C00)\n                    ? detail::signal(x.data_, y.data_)\n                    : (absx == 0x7C00) ? detail::invalid() : x.data_);\n  if (!absy) return half(detail::binary, detail::invalid());\n  if (!absx) return x;\n  if (absx == absy) return half(detail::binary, sign);\n  return half(detail::binary, sign | detail::mod<false, false>(absx, absy));\n}\n\n/// Remainder of division.\n/// **See also:** Documentation for\n/// [std::remainder](https://en.cppreference.com/w/cpp/numeric/math/remainder).\n/// \\param x first operand\n/// \\param y second operand\n/// \\return remainder of floating-point division.\n/// \\exception FE_INVALID if \\a x is infinite or \\a y is 0 or if \\a x or \\a y is\n/// signaling NaN\ninline half remainder(half x, half y) {\n  unsigned int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF,\n               sign = x.data_ & 0x8000;\n  if (absx >= 0x7C00 || absy >= 0x7C00)\n    return half(detail::binary,\n                (absx > 0x7C00 || absy > 0x7C00)\n                    ? detail::signal(x.data_, y.data_)\n                    : (absx == 0x7C00) ? detail::invalid() : x.data_);\n  if (!absy) return half(detail::binary, detail::invalid());\n  if (absx == absy) return half(detail::binary, sign);\n  return half(detail::binary, sign ^ detail::mod<false, true>(absx, absy));\n}\n\n/// Remainder of division.\n/// **See also:** Documentation for\n/// [std::remquo](https://en.cppreference.com/w/cpp/numeric/math/remquo).\n/// \\param x first operand\n/// \\param y second operand\n/// \\param quo address to store some bits of quotient at\n/// \\return remainder of floating-point division.\n/// \\exception FE_INVALID if \\a x is infinite or \\a y is 0 or if \\a x or \\a y is\n/// signaling NaN\ninline half remquo(half x, half y, int *quo) {\n  unsigned int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF,\n               value = x.data_ & 0x8000;\n  if (absx >= 0x7C00 || absy >= 0x7C00)\n    return half(\n        detail::binary,\n        (absx > 0x7C00 || absy > 0x7C00)\n            ? detail::signal(x.data_, y.data_)\n            : (absx == 0x7C00) ? detail::invalid() : (*quo = 0, x.data_));\n  if (!absy) return half(detail::binary, detail::invalid());\n  bool qsign = ((value ^ y.data_) & 0x8000) != 0;\n  int q = 1;\n  if (absx != absy) value ^= detail::mod<true, true>(absx, absy, &q);\n  return *quo = qsign ? -q : q, half(detail::binary, value);\n}\n\n/// Fused multiply add.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::fma](https://en.cppreference.com/w/cpp/numeric/math/fma).\n/// \\param x first operand\n/// \\param y second operand\n/// \\param z third operand\n/// \\return ( \\a x * \\a y ) + \\a z rounded as one operation.\n/// \\exception FE_INVALID according to operator*() and operator+() unless any\n/// argument is a quiet NaN and no argument is a signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding the\n/// final addition\ninline half fma(half x, half y, half z) {\n#ifdef HALF_ARITHMETIC_TYPE\n  detail::internal_t fx = detail::half2float<detail::internal_t>(x.data_),\n                     fy = detail::half2float<detail::internal_t>(y.data_),\n                     fz = detail::half2float<detail::internal_t>(z.data_);\n#if HALF_ENABLE_CPP11_CMATH && FP_FAST_FMA\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::fma(fx, fy, fz)));\n#else\n  return half(detail::binary,\n              detail::float2half<half::round_style>(fx * fy + fz));\n#endif\n#else\n  int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF, absz = z.data_ & 0x7FFF,\n      exp = -15;\n  unsigned int sign = (x.data_ ^ y.data_) & 0x8000;\n  bool sub = ((sign ^ z.data_) & 0x8000) != 0;\n  if (absx >= 0x7C00 || absy >= 0x7C00 || absz >= 0x7C00)\n    return (absx > 0x7C00 || absy > 0x7C00 || absz > 0x7C00)\n               ? half(detail::binary, detail::signal(x.data_, y.data_, z.data_))\n               : (absx == 0x7C00)\n                     ? half(detail::binary,\n                            (!absy || (sub && absz == 0x7C00))\n                                ? detail::invalid()\n                                : (sign | 0x7C00))\n                     : (absy == 0x7C00)\n                           ? half(detail::binary,\n                                  (!absx || (sub && absz == 0x7C00))\n                                      ? detail::invalid()\n                                      : (sign | 0x7C00))\n                           : z;\n  if (!absx || !absy)\n    return absz ? z : half(detail::binary,\n                           (half::round_style == std::round_toward_neg_infinity)\n                               ? (z.data_ | sign)\n                               : (z.data_ & sign));\n  for (; absx < 0x400; absx <<= 1, --exp)\n    ;\n  for (; absy < 0x400; absy <<= 1, --exp)\n    ;\n  detail::uint32 m = static_cast<detail::uint32>((absx & 0x3FF) | 0x400) *\n                     static_cast<detail::uint32>((absy & 0x3FF) | 0x400);\n  int i = m >> 21;\n  exp += (absx >> 10) + (absy >> 10) + i;\n  m <<= 3 - i;\n  if (absz) {\n    int expz = 0;\n    for (; absz < 0x400; absz <<= 1, --expz)\n      ;\n    expz += absz >> 10;\n    detail::uint32 mz = static_cast<detail::uint32>((absz & 0x3FF) | 0x400)\n                        << 13;\n    if (expz > exp || (expz == exp && mz > m)) {\n      std::swap(m, mz);\n      std::swap(exp, expz);\n      if (sub) sign = z.data_ & 0x8000;\n    }\n    int d = exp - expz;\n    mz = (d < 23) ? ((mz >> d) |\n                     ((mz & ((static_cast<detail::uint32>(1) << d) - 1)) != 0))\n                  : 1;\n    if (sub) {\n      m = m - mz;\n      if (!m)\n        return half(detail::binary,\n                    static_cast<unsigned>(half::round_style ==\n                                          std::round_toward_neg_infinity)\n                        << 15);\n      for (; m < 0x800000; m <<= 1, --exp)\n        ;\n    } else {\n      m += mz;\n      i = m >> 24;\n      m = (m >> i) | (m & i);\n      exp += i;\n    }\n  }\n  if (exp > 30)\n    return half(detail::binary, detail::overflow<half::round_style>(sign));\n  else if (exp < -10)\n    return half(detail::binary, detail::underflow<half::round_style>(sign));\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 23, false, false, false>(\n                  m, exp - 1, sign));\n#endif\n}\n\n/// Maximum of half expressions.\n/// **See also:** Documentation for\n/// [std::fmax](https://en.cppreference.com/w/cpp/numeric/math/fmax).\n/// \\param x first operand\n/// \\param y second operand\n/// \\return maximum of operands, ignoring quiet NaNs\n/// \\exception FE_INVALID if \\a x or \\a y is signaling NaN\ninline HALF_CONSTEXPR_NOERR half fmax(half x, half y) {\n  return half(\n      detail::binary,\n      (!isnan(y) && (isnan(x) ||\n                     (x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) <\n                         (y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15))))))\n          ? detail::select(y.data_, x.data_)\n          : detail::select(x.data_, y.data_));\n}\n\n/// Minimum of half expressions.\n/// **See also:** Documentation for\n/// [std::fmin](https://en.cppreference.com/w/cpp/numeric/math/fmin).\n/// \\param x first operand\n/// \\param y second operand\n/// \\return minimum of operands, ignoring quiet NaNs\n/// \\exception FE_INVALID if \\a x or \\a y is signaling NaN\ninline HALF_CONSTEXPR_NOERR half fmin(half x, half y) {\n  return half(\n      detail::binary,\n      (!isnan(y) && (isnan(x) ||\n                     (x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) >\n                         (y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15))))))\n          ? detail::select(y.data_, x.data_)\n          : detail::select(x.data_, y.data_));\n}\n\n/// Positive difference.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::fdim](https://en.cppreference.com/w/cpp/numeric/math/fdim).\n/// \\param x first operand\n/// \\param y second operand\n/// \\return \\a x - \\a y or 0 if difference negative\n/// \\exception FE_... according to operator-(half,half)\ninline half fdim(half x, half y) {\n  if (isnan(x) || isnan(y))\n    return half(detail::binary, detail::signal(x.data_, y.data_));\n  return (x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) <=\n                 (y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15))))\n             ? half(detail::binary, 0)\n             : (x - y);\n}\n\n/// Get NaN value.\n/// **See also:** Documentation for\n/// [std::nan](https://en.cppreference.com/w/cpp/numeric/math/nan).\n/// \\param arg string code\n/// \\return quiet NaN\ninline half nanh(const char *arg) {\n  unsigned int value = 0x7FFF;\n  while (*arg) value ^= static_cast<unsigned>(*arg++) & 0xFF;\n  return half(detail::binary, value);\n}\n\n/// \\}\n/// \\anchor exponential\n/// \\name Exponential functions\n/// \\{\n\n/// Exponential function.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::exp](https://en.cppreference.com/w/cpp/numeric/math/exp).\n/// \\param arg function argument\n/// \\return e raised to \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half exp(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  std::exp(detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF;\n  if (!abs) return half(detail::binary, 0x3C00);\n  if (abs >= 0x7C00)\n    return half(detail::binary,\n                (abs == 0x7C00) ? (0x7C00 & ((arg.data_ >> 15) - 1U))\n                                : detail::signal(arg.data_));\n  if (abs >= 0x4C80)\n    return half(detail::binary,\n                (arg.data_ & 0x8000) ? detail::underflow<half::round_style>()\n                                     : detail::overflow<half::round_style>());\n  detail::uint32 m = detail::multiply64(\n      static_cast<detail::uint32>((abs & 0x3FF) + ((abs > 0x3FF) << 10)) << 21,\n      0xB8AA3B29);\n  int e = (abs >> 10) + (abs <= 0x3FF), exp;\n  if (e < 14) {\n    exp = 0;\n    m >>= 14 - e;\n  } else {\n    exp = m >> (45 - e);\n    m = (m << (e - 14)) & 0x7FFFFFFF;\n  }\n  return half(detail::binary,\n              detail::exp2_post<half::round_style, true>(\n                  detail::exp2(m, 26), exp, (arg.data_ & 0x8000) != 0));\n#endif\n}\n\n/// Binary exponential.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::exp2](https://en.cppreference.com/w/cpp/numeric/math/exp2).\n/// \\param arg function argument\n/// \\return 2 raised to \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half exp2(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::exp2(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF;\n  if (!abs) return half(detail::binary, 0x3C00);\n  if (abs >= 0x7C00)\n    return half(detail::binary,\n                (abs == 0x7C00) ? (0x7C00 & ((arg.data_ >> 15) - 1U))\n                                : detail::signal(arg.data_));\n  if (abs >= 0x4E40)\n    return half(detail::binary,\n                (arg.data_ & 0x8000) ? detail::underflow<half::round_style>()\n                                     : detail::overflow<half::round_style>());\n  int e = (abs >> 10) + (abs <= 0x3FF),\n      exp = (abs & 0x3FF) + ((abs > 0x3FF) << 10);\n  detail::uint32 m = detail::exp2(\n      (static_cast<detail::uint32>(exp) << (6 + e)) & 0x7FFFFFFF, 28);\n  exp >>= 25 - e;\n  if (m == 0x80000000) {\n    if (arg.data_ & 0x8000)\n      exp = -exp;\n    else if (exp > 15)\n      return half(detail::binary, detail::overflow<half::round_style>());\n    return half(detail::binary,\n                detail::fixed2half<half::round_style, 31, false, false, false>(\n                    m, exp + 14));\n  }\n  return half(detail::binary, detail::exp2_post<half::round_style, true>(\n                                  m, exp, (arg.data_ & 0x8000) != 0));\n#endif\n}\n\n/// Exponential minus one.\n/// This function may be 1 ULP off the correctly rounded exact result in <0.05%\n/// of inputs for `std::round_to_nearest`\n/// and in <1% of inputs for any other rounding mode.\n///\n/// **See also:** Documentation for\n/// [std::expm1](https://en.cppreference.com/w/cpp/numeric/math/expm1).\n/// \\param arg function argument\n/// \\return e raised to \\a arg and subtracted by 1\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half expm1(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::expm1(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  unsigned int abs = arg.data_ & 0x7FFF, sign = arg.data_ & 0x8000;\n  if (!abs) return arg;\n  if (abs >= 0x7C00)\n    return half(\n        detail::binary,\n        (abs == 0x7C00) ? (0x7C00 + (sign >> 1)) : detail::signal(arg.data_));\n  if (abs >= 0x4A00)\n    return half(detail::binary,\n                (arg.data_ & 0x8000)\n                    ? detail::rounded<half::round_style, true>(0xBBFF, 1, 1)\n                    : detail::overflow<half::round_style>());\n  detail::uint32 m = detail::multiply64(\n      static_cast<detail::uint32>((abs & 0x3FF) + ((abs > 0x3FF) << 10)) << 21,\n      0xB8AA3B29);\n  int e = (abs >> 10) + (abs <= 0x3FF), exp;\n  if (e < 14) {\n    exp = 0;\n    m >>= 14 - e;\n  } else {\n    exp = m >> (45 - e);\n    m = (m << (e - 14)) & 0x7FFFFFFF;\n  }\n  m = detail::exp2(m);\n  if (sign) {\n    int s = 0;\n    if (m > 0x80000000) {\n      ++exp;\n      m = detail::divide64(0x80000000, m, s);\n    }\n    m = 0x80000000 -\n        ((m >> exp) |\n         ((m & ((static_cast<detail::uint32>(1) << exp) - 1)) != 0) | s);\n    exp = 0;\n  } else\n    m -= (exp < 31) ? (0x80000000 >> exp) : 1;\n  for (exp += 14; m < 0x80000000 && exp; m <<= 1, --exp)\n    ;\n  if (exp > 29)\n    return half(detail::binary, detail::overflow<half::round_style>());\n  return half(detail::binary, detail::rounded<half::round_style, true>(\n                                  sign + (exp << 10) + (m >> 21), (m >> 20) & 1,\n                                  (m & 0xFFFFF) != 0));\n#endif\n}\n\n/// Natural logarithm.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::log](https://en.cppreference.com/w/cpp/numeric/math/log).\n/// \\param arg function argument\n/// \\return logarithm of \\a arg to base e\n/// \\exception FE_INVALID for signaling NaN or negative argument\n/// \\exception FE_DIVBYZERO for 0\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half log(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  std::log(detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, exp = -15;\n  if (!abs) return half(detail::binary, detail::pole(0x8000));\n  if (arg.data_ & 0x8000)\n    return half(\n        detail::binary,\n        (arg.data_ <= 0xFC00) ? detail::invalid() : detail::signal(arg.data_));\n  if (abs >= 0x7C00)\n    return (abs == 0x7C00) ? arg\n                           : half(detail::binary, detail::signal(arg.data_));\n  for (; abs < 0x400; abs <<= 1, --exp)\n    ;\n  exp += abs >> 10;\n  return half(\n      detail::binary,\n      detail::log2_post<half::round_style, 0xB8AA3B2A>(\n          detail::log2(static_cast<detail::uint32>((abs & 0x3FF) | 0x400) << 20,\n                       27) +\n              8,\n          exp, 17));\n#endif\n}\n\n/// Common logarithm.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::log10](https://en.cppreference.com/w/cpp/numeric/math/log10).\n/// \\param arg function argument\n/// \\return logarithm of \\a arg to base 10\n/// \\exception FE_INVALID for signaling NaN or negative argument\n/// \\exception FE_DIVBYZERO for 0\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half log10(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::log10(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, exp = -15;\n  if (!abs) return half(detail::binary, detail::pole(0x8000));\n  if (arg.data_ & 0x8000)\n    return half(\n        detail::binary,\n        (arg.data_ <= 0xFC00) ? detail::invalid() : detail::signal(arg.data_));\n  if (abs >= 0x7C00)\n    return (abs == 0x7C00) ? arg\n                           : half(detail::binary, detail::signal(arg.data_));\n  switch (abs) {\n    case 0x4900:\n      return half(detail::binary, 0x3C00);\n    case 0x5640:\n      return half(detail::binary, 0x4000);\n    case 0x63D0:\n      return half(detail::binary, 0x4200);\n    case 0x70E2:\n      return half(detail::binary, 0x4400);\n  }\n  for (; abs < 0x400; abs <<= 1, --exp)\n    ;\n  exp += abs >> 10;\n  return half(\n      detail::binary,\n      detail::log2_post<half::round_style, 0xD49A784C>(\n          detail::log2(static_cast<detail::uint32>((abs & 0x3FF) | 0x400) << 20,\n                       27) +\n              8,\n          exp, 16));\n#endif\n}\n\n/// Binary logarithm.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::log2](https://en.cppreference.com/w/cpp/numeric/math/log2).\n/// \\param arg function argument\n/// \\return logarithm of \\a arg to base 2\n/// \\exception FE_INVALID for signaling NaN or negative argument\n/// \\exception FE_DIVBYZERO for 0\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half log2(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::log2(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, exp = -15, s = 0;\n  if (!abs) return half(detail::binary, detail::pole(0x8000));\n  if (arg.data_ & 0x8000)\n    return half(\n        detail::binary,\n        (arg.data_ <= 0xFC00) ? detail::invalid() : detail::signal(arg.data_));\n  if (abs >= 0x7C00)\n    return (abs == 0x7C00) ? arg\n                           : half(detail::binary, detail::signal(arg.data_));\n  if (abs == 0x3C00) return half(detail::binary, 0);\n  for (; abs < 0x400; abs <<= 1, --exp)\n    ;\n  exp += (abs >> 10);\n  if (!(abs & 0x3FF)) {\n    unsigned int value = static_cast<unsigned>(exp < 0) << 15,\n                 m = std::abs(exp) << 6;\n    for (exp = 18; m < 0x400; m <<= 1, --exp)\n      ;\n    return half(detail::binary, value + (exp << 10) + m);\n  }\n  detail::uint32 ilog = exp, sign = detail::sign_mask(ilog),\n                 m = (((ilog << 27) +\n                       (detail::log2(\n                            static_cast<detail::uint32>((abs & 0x3FF) | 0x400)\n                                << 20,\n                            28) >>\n                        4)) ^\n                      sign) -\n                     sign;\n  if (!m) return half(detail::binary, 0);\n  for (exp = 14; m < 0x8000000 && exp; m <<= 1, --exp)\n    ;\n  for (; m > 0xFFFFFFF; m >>= 1, ++exp) s |= m & 1;\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 27, false, false, true>(\n                  m, exp, sign & 0x8000, s));\n#endif\n}\n\n/// Natural logarithm plus one.\n/// This function may be 1 ULP off the correctly rounded exact result in <0.05%\n/// of inputs for `std::round_to_nearest`\n/// and in ~1% of inputs for any other rounding mode.\n///\n/// **See also:** Documentation for\n/// [std::log1p](https://en.cppreference.com/w/cpp/numeric/math/log1p).\n/// \\param arg function argument\n/// \\return logarithm of \\a arg plus 1 to base e\n/// \\exception FE_INVALID for signaling NaN or argument <-1\n/// \\exception FE_DIVBYZERO for -1\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half log1p(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::log1p(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  if (arg.data_ >= 0xBC00)\n    return half(detail::binary,\n                (arg.data_ == 0xBC00)\n                    ? detail::pole(0x8000)\n                    : (arg.data_ <= 0xFC00) ? detail::invalid()\n                                            : detail::signal(arg.data_));\n  int abs = arg.data_ & 0x7FFF, exp = -15;\n  if (!abs || abs >= 0x7C00)\n    return (abs > 0x7C00) ? half(detail::binary, detail::signal(arg.data_))\n                          : arg;\n  for (; abs < 0x400; abs <<= 1, --exp)\n    ;\n  exp += abs >> 10;\n  detail::uint32 m = static_cast<detail::uint32>((abs & 0x3FF) | 0x400) << 20;\n  if (arg.data_ & 0x8000) {\n    m = 0x40000000 - (m >> -exp);\n    for (exp = 0; m < 0x40000000; m <<= 1, --exp)\n      ;\n  } else {\n    if (exp < 0) {\n      m = 0x40000000 + (m >> -exp);\n      exp = 0;\n    } else {\n      m += 0x40000000 >> exp;\n      int i = m >> 31;\n      m >>= i;\n      exp += i;\n    }\n  }\n  return half(detail::binary, detail::log2_post<half::round_style, 0xB8AA3B2A>(\n                                  detail::log2(m), exp, 17));\n#endif\n}\n\n/// \\}\n/// \\anchor power\n/// \\name Power functions\n/// \\{\n\n/// Square root.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::sqrt](https://en.cppreference.com/w/cpp/numeric/math/sqrt).\n/// \\param arg function argument\n/// \\return square root of \\a arg\n/// \\exception FE_INVALID for signaling NaN and negative arguments\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half sqrt(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::sqrt(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, exp = 15;\n  if (!abs || arg.data_ >= 0x7C00)\n    return half(detail::binary,\n                (abs > 0x7C00)\n                    ? detail::signal(arg.data_)\n                    : (arg.data_ > 0x8000) ? detail::invalid() : arg.data_);\n  for (; abs < 0x400; abs <<= 1, --exp)\n    ;\n  detail::uint32 r = static_cast<detail::uint32>((abs & 0x3FF) | 0x400) << 10,\n                 m = detail::sqrt<20>(r, exp += abs >> 10);\n  return half(detail::binary, detail::rounded<half::round_style, false>(\n                                  (exp << 10) + (m & 0x3FF), r > m, r != 0));\n#endif\n}\n\n/// Cubic root.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::cbrt](https://en.cppreference.com/w/cpp/numeric/math/cbrt).\n/// \\param arg function argument\n/// \\return cubic root of \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half cbrt(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::cbrt(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, exp = -15;\n  if (!abs || abs == 0x3C00 || abs >= 0x7C00)\n    return (abs > 0x7C00) ? half(detail::binary, detail::signal(arg.data_))\n                          : arg;\n  for (; abs < 0x400; abs <<= 1, --exp)\n    ;\n  detail::uint32 ilog = exp + (abs >> 10), sign = detail::sign_mask(ilog), f,\n                 m = (((ilog << 27) +\n                       (detail::log2(\n                            static_cast<detail::uint32>((abs & 0x3FF) | 0x400)\n                                << 20,\n                            24) >>\n                        4)) ^\n                      sign) -\n                     sign;\n  for (exp = 2; m < 0x80000000; m <<= 1, --exp)\n    ;\n  m = detail::multiply64(m, 0xAAAAAAAB);\n  int i = m >> 31, s;\n  exp += i;\n  m <<= 1 - i;\n  if (exp < 0) {\n    f = m >> -exp;\n    exp = 0;\n  } else {\n    f = (m << exp) & 0x7FFFFFFF;\n    exp = m >> (31 - exp);\n  }\n  m = detail::exp2(f, (half::round_style == std::round_to_nearest) ? 29 : 26);\n  if (sign) {\n    if (m > 0x80000000) {\n      m = detail::divide64(0x80000000, m, s);\n      ++exp;\n    }\n    exp = -exp;\n  }\n  return half(\n      detail::binary,\n      (half::round_style == std::round_to_nearest)\n          ? detail::fixed2half<half::round_style, 31, false, false, false>(\n                m, exp + 14, arg.data_ & 0x8000)\n          : detail::fixed2half<half::round_style, 23, false, false, false>(\n                (m + 0x80) >> 8, exp + 14, arg.data_ & 0x8000));\n#endif\n}\n\n/// Hypotenuse function.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::hypot](https://en.cppreference.com/w/cpp/numeric/math/hypot).\n/// \\param x first argument\n/// \\param y second argument\n/// \\return square root of sum of squares without internal over- or underflows\n/// \\exception FE_INVALID if \\a x or \\a y is signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding of\n/// the final square root\ninline half hypot(half x, half y) {\n#ifdef HALF_ARITHMETIC_TYPE\n  detail::internal_t fx = detail::half2float<detail::internal_t>(x.data_),\n                     fy = detail::half2float<detail::internal_t>(y.data_);\n#if HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::hypot(fx, fy)));\n#else\n  return half(detail::binary, detail::float2half<half::round_style>(\n                                  std::sqrt(fx * fx + fy * fy)));\n#endif\n#else\n  int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF, expx = 0, expy = 0;\n  if (absx >= 0x7C00 || absy >= 0x7C00)\n    return half(detail::binary,\n                (absx == 0x7C00)\n                    ? detail::select(0x7C00, y.data_)\n                    : (absy == 0x7C00) ? detail::select(0x7C00, x.data_)\n                                       : detail::signal(x.data_, y.data_));\n  if (!absx)\n    return half(detail::binary, absy ? detail::check_underflow(absy) : 0);\n  if (!absy) return half(detail::binary, detail::check_underflow(absx));\n  if (absy > absx) std::swap(absx, absy);\n  for (; absx < 0x400; absx <<= 1, --expx)\n    ;\n  for (; absy < 0x400; absy <<= 1, --expy)\n    ;\n  detail::uint32 mx = (absx & 0x3FF) | 0x400, my = (absy & 0x3FF) | 0x400;\n  mx *= mx;\n  my *= my;\n  int ix = mx >> 21, iy = my >> 21;\n  expx = 2 * (expx + (absx >> 10)) - 15 + ix;\n  expy = 2 * (expy + (absy >> 10)) - 15 + iy;\n  mx <<= 10 - ix;\n  my <<= 10 - iy;\n  int d = expx - expy;\n  my = (d < 30) ? ((my >> d) |\n                   ((my & ((static_cast<detail::uint32>(1) << d) - 1)) != 0))\n                : 1;\n  return half(detail::binary,\n              detail::hypot_post<half::round_style>(mx + my, expx));\n#endif\n}\n\n/// Hypotenuse function.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::hypot](https://en.cppreference.com/w/cpp/numeric/math/hypot).\n/// \\param x first argument\n/// \\param y second argument\n/// \\param z third argument\n/// \\return square root of sum of squares without internal over- or underflows\n/// \\exception FE_INVALID if \\a x, \\a y or \\a z is signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding of\n/// the final square root\ninline half hypot(half x, half y, half z) {\n#ifdef HALF_ARITHMETIC_TYPE\n  detail::internal_t fx = detail::half2float<detail::internal_t>(x.data_),\n                     fy = detail::half2float<detail::internal_t>(y.data_),\n                     fz = detail::half2float<detail::internal_t>(z.data_);\n  return half(detail::binary, detail::float2half<half::round_style>(\n                                  std::sqrt(fx * fx + fy * fy + fz * fz)));\n#else\n  int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF, absz = z.data_ & 0x7FFF,\n      expx = 0, expy = 0, expz = 0;\n  if (!absx) return hypot(y, z);\n  if (!absy) return hypot(x, z);\n  if (!absz) return hypot(x, y);\n  if (absx >= 0x7C00 || absy >= 0x7C00 || absz >= 0x7C00)\n    return half(\n        detail::binary,\n        (absx == 0x7C00)\n            ? detail::select(0x7C00, detail::select(y.data_, z.data_))\n            : (absy == 0x7C00)\n                  ? detail::select(0x7C00, detail::select(x.data_, z.data_))\n                  : (absz == 0x7C00)\n                        ? detail::select(0x7C00,\n                                         detail::select(x.data_, y.data_))\n                        : detail::signal(x.data_, y.data_, z.data_));\n  if (absz > absy) std::swap(absy, absz);\n  if (absy > absx) std::swap(absx, absy);\n  if (absz > absy) std::swap(absy, absz);\n  for (; absx < 0x400; absx <<= 1, --expx)\n    ;\n  for (; absy < 0x400; absy <<= 1, --expy)\n    ;\n  for (; absz < 0x400; absz <<= 1, --expz)\n    ;\n  detail::uint32 mx = (absx & 0x3FF) | 0x400, my = (absy & 0x3FF) | 0x400,\n                 mz = (absz & 0x3FF) | 0x400;\n  mx *= mx;\n  my *= my;\n  mz *= mz;\n  int ix = mx >> 21, iy = my >> 21, iz = mz >> 21;\n  expx = 2 * (expx + (absx >> 10)) - 15 + ix;\n  expy = 2 * (expy + (absy >> 10)) - 15 + iy;\n  expz = 2 * (expz + (absz >> 10)) - 15 + iz;\n  mx <<= 10 - ix;\n  my <<= 10 - iy;\n  mz <<= 10 - iz;\n  int d = expy - expz;\n  mz = (d < 30) ? ((mz >> d) |\n                   ((mz & ((static_cast<detail::uint32>(1) << d) - 1)) != 0))\n                : 1;\n  my += mz;\n  if (my & 0x80000000) {\n    my = (my >> 1) | (my & 1);\n    if (++expy > expx) {\n      std::swap(mx, my);\n      std::swap(expx, expy);\n    }\n  }\n  d = expx - expy;\n  my = (d < 30) ? ((my >> d) |\n                   ((my & ((static_cast<detail::uint32>(1) << d) - 1)) != 0))\n                : 1;\n  return half(detail::binary,\n              detail::hypot_post<half::round_style>(mx + my, expx));\n#endif\n}\n\n/// Power function.\n/// This function may be 1 ULP off the correctly rounded exact result for any\n/// rounding mode in ~0.00025% of inputs.\n///\n/// **See also:** Documentation for\n/// [std::pow](https://en.cppreference.com/w/cpp/numeric/math/pow).\n/// \\param x base\n/// \\param y exponent\n/// \\return \\a x raised to \\a y\n/// \\exception FE_INVALID if \\a x or \\a y is signaling NaN or if \\a x is finite\n/// an negative and \\a y is finite and not integral\n/// \\exception FE_DIVBYZERO if \\a x is 0 and \\a y is negative\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half pow(half x, half y) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  std::pow(detail::half2float<detail::internal_t>(x.data_),\n                           detail::half2float<detail::internal_t>(y.data_))));\n#else\n  int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF, exp = -15;\n  if (!absy || x.data_ == 0x3C00)\n    return half(\n        detail::binary,\n        detail::select(0x3C00, (x.data_ == 0x3C00) ? y.data_ : x.data_));\n  bool is_int = absy >= 0x6400 ||\n                (absy >= 0x3C00 && !(absy & ((1 << (25 - (absy >> 10))) - 1)));\n  unsigned int sign =\n      x.data_ & (static_cast<unsigned>((absy < 0x6800) && is_int &&\n                                       ((absy >> (25 - (absy >> 10))) & 1))\n                 << 15);\n  if (absx >= 0x7C00 || absy >= 0x7C00)\n    return half(detail::binary,\n                (absx > 0x7C00 || absy > 0x7C00)\n                    ? detail::signal(x.data_, y.data_)\n                    : (absy == 0x7C00)\n                          ? ((absx == 0x3C00)\n                                 ? 0x3C00\n                                 : (!absx && y.data_ == 0xFC00)\n                                       ? detail::pole()\n                                       : (0x7C00 &\n                                          -((y.data_ >> 15) ^ (absx > 0x3C00))))\n                          : (sign | (0x7C00 & ((y.data_ >> 15) - 1U))));\n  if (!absx)\n    return half(detail::binary, (y.data_ & 0x8000) ? detail::pole(sign) : sign);\n  if ((x.data_ & 0x8000) && !is_int)\n    return half(detail::binary, detail::invalid());\n  if (x.data_ == 0xBC00) return half(detail::binary, sign | 0x3C00);\n  if (y.data_ == 0x3800) return sqrt(x);\n  if (y.data_ == 0x3C00)\n    return half(detail::binary, detail::check_underflow(x.data_));\n  if (y.data_ == 0x4000) return x * x;\n  for (; absx < 0x400; absx <<= 1, --exp)\n    ;\n  detail::uint32 ilog = exp + (absx >> 10), msign = detail::sign_mask(ilog), f,\n                 m = (((ilog << 27) +\n                       ((detail::log2(\n                             static_cast<detail::uint32>((absx & 0x3FF) | 0x400)\n                             << 20) +\n                         8) >>\n                        4)) ^\n                      msign) -\n                     msign;\n  for (exp = -11; m < 0x80000000; m <<= 1, --exp)\n    ;\n  for (; absy < 0x400; absy <<= 1, --exp)\n    ;\n  m = detail::multiply64(\n      m, static_cast<detail::uint32>((absy & 0x3FF) | 0x400) << 21);\n  int i = m >> 31;\n  exp += (absy >> 10) + i;\n  m <<= 1 - i;\n  if (exp < 0) {\n    f = m >> -exp;\n    exp = 0;\n  } else {\n    f = (m << exp) & 0x7FFFFFFF;\n    exp = m >> (31 - exp);\n  }\n  return half(detail::binary, detail::exp2_post<half::round_style, false>(\n                                  detail::exp2(f), exp,\n                                  ((msign & 1) ^ (y.data_ >> 15)) != 0, sign));\n#endif\n}\n\n/// \\}\n/// \\anchor trigonometric\n/// \\name Trigonometric functions\n/// \\{\n\n/// Compute sine and cosine simultaneously.\n///\tThis returns the same results as sin() and cos() but is faster than\n///calling each function individually.\n///\n/// This function is exact to rounding for all rounding modes.\n/// \\param arg function argument\n/// \\param sin variable to take sine of \\a arg\n/// \\param cos variable to take cosine of \\a arg\n/// \\exception FE_INVALID for signaling NaN or infinity\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline void sincos(half arg, half *sin, half *cos) {\n#ifdef HALF_ARITHMETIC_TYPE\n  detail::internal_t f = detail::half2float<detail::internal_t>(arg.data_);\n  *sin =\n      half(detail::binary, detail::float2half<half::round_style>(std::sin(f)));\n  *cos =\n      half(detail::binary, detail::float2half<half::round_style>(std::cos(f)));\n#else\n  int abs = arg.data_ & 0x7FFF, sign = arg.data_ >> 15, k;\n  if (abs >= 0x7C00)\n    *sin = *cos =\n        half(detail::binary,\n             (abs == 0x7C00) ? detail::invalid() : detail::signal(arg.data_));\n  else if (!abs) {\n    *sin = arg;\n    *cos = half(detail::binary, 0x3C00);\n  } else if (abs < 0x2500) {\n    *sin = half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_ - 1, 1, 1));\n    *cos = half(detail::binary,\n                detail::rounded<half::round_style, true>(0x3BFF, 1, 1));\n  } else {\n    if (half::round_style != std::round_to_nearest) {\n      switch (abs) {\n        case 0x48B7:\n          *sin =\n              half(detail::binary, detail::rounded<half::round_style, true>(\n                                       (~arg.data_ & 0x8000) | 0x1D07, 1, 1));\n          *cos = half(detail::binary,\n                      detail::rounded<half::round_style, true>(0xBBFF, 1, 1));\n          return;\n        case 0x598C:\n          *sin = half(detail::binary, detail::rounded<half::round_style, true>(\n                                          (arg.data_ & 0x8000) | 0x3BFF, 1, 1));\n          *cos = half(detail::binary,\n                      detail::rounded<half::round_style, true>(0x80FC, 1, 1));\n          return;\n        case 0x6A64:\n          *sin =\n              half(detail::binary, detail::rounded<half::round_style, true>(\n                                       (~arg.data_ & 0x8000) | 0x3BFE, 1, 1));\n          *cos = half(detail::binary,\n                      detail::rounded<half::round_style, true>(0x27FF, 1, 1));\n          return;\n        case 0x6D8C:\n          *sin = half(detail::binary, detail::rounded<half::round_style, true>(\n                                          (arg.data_ & 0x8000) | 0x0FE6, 1, 1));\n          *cos = half(detail::binary,\n                      detail::rounded<half::round_style, true>(0x3BFF, 1, 1));\n          return;\n      }\n    }\n    std::pair<detail::uint32, detail::uint32> sc =\n        detail::sincos(detail::angle_arg(abs, k), 28);\n    switch (k & 3) {\n      case 1:\n        sc = std::make_pair(sc.second, -sc.first);\n        break;\n      case 2:\n        sc = std::make_pair(-sc.first, -sc.second);\n        break;\n      case 3:\n        sc = std::make_pair(-sc.second, sc.first);\n        break;\n    }\n    *sin = half(detail::binary,\n                detail::fixed2half<half::round_style, 30, true, true, true>(\n                    (sc.first ^ -static_cast<detail::uint32>(sign)) + sign));\n    *cos = half(\n        detail::binary,\n        detail::fixed2half<half::round_style, 30, true, true, true>(sc.second));\n  }\n#endif\n}\n\n/// Sine function.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::sin](https://en.cppreference.com/w/cpp/numeric/math/sin).\n/// \\param arg function argument\n/// \\return sine value of \\a arg\n/// \\exception FE_INVALID for signaling NaN or infinity\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half sin(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  std::sin(detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, k;\n  if (!abs) return arg;\n  if (abs >= 0x7C00)\n    return half(\n        detail::binary,\n        (abs == 0x7C00) ? detail::invalid() : detail::signal(arg.data_));\n  if (abs < 0x2900)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_ - 1, 1, 1));\n  if (half::round_style != std::round_to_nearest) switch (abs) {\n      case 0x48B7:\n        return half(detail::binary, detail::rounded<half::round_style, true>(\n                                        (~arg.data_ & 0x8000) | 0x1D07, 1, 1));\n      case 0x6A64:\n        return half(detail::binary, detail::rounded<half::round_style, true>(\n                                        (~arg.data_ & 0x8000) | 0x3BFE, 1, 1));\n      case 0x6D8C:\n        return half(detail::binary, detail::rounded<half::round_style, true>(\n                                        (arg.data_ & 0x8000) | 0x0FE6, 1, 1));\n    }\n  std::pair<detail::uint32, detail::uint32> sc =\n      detail::sincos(detail::angle_arg(abs, k), 28);\n  detail::uint32 sign =\n      -static_cast<detail::uint32>(((k >> 1) & 1) ^ (arg.data_ >> 15));\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 30, true, true, true>(\n                  (((k & 1) ? sc.second : sc.first) ^ sign) - sign));\n#endif\n}\n\n/// Cosine function.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::cos](https://en.cppreference.com/w/cpp/numeric/math/cos).\n/// \\param arg function argument\n/// \\return cosine value of \\a arg\n/// \\exception FE_INVALID for signaling NaN or infinity\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half cos(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  std::cos(detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, k;\n  if (!abs) return half(detail::binary, 0x3C00);\n  if (abs >= 0x7C00)\n    return half(\n        detail::binary,\n        (abs == 0x7C00) ? detail::invalid() : detail::signal(arg.data_));\n  if (abs < 0x2500)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(0x3BFF, 1, 1));\n  if (half::round_style != std::round_to_nearest && abs == 0x598C)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(0x80FC, 1, 1));\n  std::pair<detail::uint32, detail::uint32> sc =\n      detail::sincos(detail::angle_arg(abs, k), 28);\n  detail::uint32 sign = -static_cast<detail::uint32>(((k >> 1) ^ k) & 1);\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 30, true, true, true>(\n                  (((k & 1) ? sc.first : sc.second) ^ sign) - sign));\n#endif\n}\n\n/// Tangent function.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::tan](https://en.cppreference.com/w/cpp/numeric/math/tan).\n/// \\param arg function argument\n/// \\return tangent value of \\a arg\n/// \\exception FE_INVALID for signaling NaN or infinity\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half tan(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  std::tan(detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, exp = 13, k;\n  if (!abs) return arg;\n  if (abs >= 0x7C00)\n    return half(\n        detail::binary,\n        (abs == 0x7C00) ? detail::invalid() : detail::signal(arg.data_));\n  if (abs < 0x2700)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_, 0, 1));\n  if (half::round_style != std::round_to_nearest) switch (abs) {\n      case 0x658C:\n        return half(detail::binary, detail::rounded<half::round_style, true>(\n                                        (arg.data_ & 0x8000) | 0x07E6, 1, 1));\n      case 0x7330:\n        return half(detail::binary, detail::rounded<half::round_style, true>(\n                                        (~arg.data_ & 0x8000) | 0x4B62, 1, 1));\n    }\n  std::pair<detail::uint32, detail::uint32> sc =\n      detail::sincos(detail::angle_arg(abs, k), 30);\n  if (k & 1) sc = std::make_pair(-sc.second, sc.first);\n  detail::uint32 signy = detail::sign_mask(sc.first),\n                 signx = detail::sign_mask(sc.second);\n  detail::uint32 my = (sc.first ^ signy) - signy,\n                 mx = (sc.second ^ signx) - signx;\n  for (; my < 0x80000000; my <<= 1, --exp)\n    ;\n  for (; mx < 0x80000000; mx <<= 1, ++exp)\n    ;\n  return half(detail::binary,\n              detail::tangent_post<half::round_style>(\n                  my, mx, exp, (signy ^ signx ^ arg.data_) & 0x8000));\n#endif\n}\n\n/// Arc sine.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::asin](https://en.cppreference.com/w/cpp/numeric/math/asin).\n/// \\param arg function argument\n/// \\return arc sine value of \\a arg\n/// \\exception FE_INVALID for signaling NaN or if abs(\\a arg) > 1\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half asin(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::asin(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  unsigned int abs = arg.data_ & 0x7FFF, sign = arg.data_ & 0x8000;\n  if (!abs) return arg;\n  if (abs >= 0x3C00)\n    return half(detail::binary,\n                (abs > 0x7C00)\n                    ? detail::signal(arg.data_)\n                    : (abs > 0x3C00) ? detail::invalid()\n                                     : detail::rounded<half::round_style, true>(\n                                           sign | 0x3E48, 0, 1));\n  if (abs < 0x2900)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_, 0, 1));\n  if (half::round_style != std::round_to_nearest &&\n      (abs == 0x2B44 || abs == 0x2DC3))\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_ + 1, 1, 1));\n  std::pair<detail::uint32, detail::uint32> sc = detail::atan2_args(abs);\n  detail::uint32 m =\n      detail::atan2(sc.first, sc.second,\n                    (half::round_style == std::round_to_nearest) ? 27 : 26);\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 30, false, true, true>(\n                  m, 14, sign));\n#endif\n}\n\n/// Arc cosine function.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::acos](https://en.cppreference.com/w/cpp/numeric/math/acos).\n/// \\param arg function argument\n/// \\return arc cosine value of \\a arg\n/// \\exception FE_INVALID for signaling NaN or if abs(\\a arg) > 1\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half acos(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::acos(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  unsigned int abs = arg.data_ & 0x7FFF, sign = arg.data_ >> 15;\n  if (!abs)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(0x3E48, 0, 1));\n  if (abs >= 0x3C00)\n    return half(detail::binary,\n                (abs > 0x7C00)\n                    ? detail::signal(arg.data_)\n                    : (abs > 0x3C00)\n                          ? detail::invalid()\n                          : sign ? detail::rounded<half::round_style, true>(\n                                       0x4248, 0, 1)\n                                 : 0);\n  std::pair<detail::uint32, detail::uint32> cs = detail::atan2_args(abs);\n  detail::uint32 m = detail::atan2(cs.second, cs.first, 28);\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 31, false, true, true>(\n                  sign ? (0xC90FDAA2 - m) : m, 15, 0, sign));\n#endif\n}\n\n/// Arc tangent function.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::atan](https://en.cppreference.com/w/cpp/numeric/math/atan).\n/// \\param arg function argument\n/// \\return arc tangent value of \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half atan(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::atan(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  unsigned int abs = arg.data_ & 0x7FFF, sign = arg.data_ & 0x8000;\n  if (!abs) return arg;\n  if (abs >= 0x7C00)\n    return half(detail::binary,\n                (abs == 0x7C00) ? detail::rounded<half::round_style, true>(\n                                      sign | 0x3E48, 0, 1)\n                                : detail::signal(arg.data_));\n  if (abs <= 0x2700)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_ - 1, 1, 1));\n  int exp = (abs >> 10) + (abs <= 0x3FF);\n  detail::uint32 my = (abs & 0x3FF) | ((abs > 0x3FF) << 10);\n  detail::uint32 m =\n      (exp > 15) ? detail::atan2(\n                       my << 19, 0x20000000 >> (exp - 15),\n                       (half::round_style == std::round_to_nearest) ? 26 : 24)\n                 : detail::atan2(\n                       my << (exp + 4), 0x20000000,\n                       (half::round_style == std::round_to_nearest) ? 30 : 28);\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 30, false, true, true>(\n                  m, 14, sign));\n#endif\n}\n\n/// Arc tangent function.\n/// This function may be 1 ULP off the correctly rounded exact result in ~0.005%\n/// of inputs for `std::round_to_nearest`,\n/// in ~0.1% of inputs for `std::round_toward_zero` and in ~0.02% of inputs for\n/// any other rounding mode.\n///\n/// **See also:** Documentation for\n/// [std::atan2](https://en.cppreference.com/w/cpp/numeric/math/atan2).\n/// \\param y numerator\n/// \\param x denominator\n/// \\return arc tangent value\n/// \\exception FE_INVALID if \\a x or \\a y is signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half atan2(half y, half x) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  std::atan2(detail::half2float<detail::internal_t>(y.data_),\n                             detail::half2float<detail::internal_t>(x.data_))));\n#else\n  unsigned int absx = x.data_ & 0x7FFF, absy = y.data_ & 0x7FFF,\n               signx = x.data_ >> 15, signy = y.data_ & 0x8000;\n  if (absx >= 0x7C00 || absy >= 0x7C00) {\n    if (absx > 0x7C00 || absy > 0x7C00)\n      return half(detail::binary, detail::signal(x.data_, y.data_));\n    if (absy == 0x7C00)\n      return half(\n          detail::binary,\n          (absx < 0x7C00)\n              ? detail::rounded<half::round_style, true>(signy | 0x3E48, 0, 1)\n              : signx ? detail::rounded<half::round_style, true>(signy | 0x40B6,\n                                                                 0, 1)\n                      : detail::rounded<half::round_style, true>(signy | 0x3A48,\n                                                                 0, 1));\n    return (x.data_ == 0x7C00)\n               ? half(detail::binary, signy)\n               : half(detail::binary, detail::rounded<half::round_style, true>(\n                                          signy | 0x4248, 0, 1));\n  }\n  if (!absy)\n    return signx\n               ? half(detail::binary, detail::rounded<half::round_style, true>(\n                                          signy | 0x4248, 0, 1))\n               : y;\n  if (!absx)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(signy | 0x3E48, 0, 1));\n  int d = (absy >> 10) + (absy <= 0x3FF) - (absx >> 10) - (absx <= 0x3FF);\n  if (d > (signx ? 18 : 12))\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(signy | 0x3E48, 0, 1));\n  if (signx && d < -11)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(signy | 0x4248, 0, 1));\n  if (!signx &&\n      d < ((half::round_style == std::round_toward_zero) ? -15 : -9)) {\n    for (; absy < 0x400; absy <<= 1, --d)\n      ;\n    detail::uint32 mx = ((absx << 1) & 0x7FF) | 0x800,\n                   my = ((absy << 1) & 0x7FF) | 0x800;\n    int i = my < mx;\n    d -= i;\n    if (d < -25)\n      return half(detail::binary, detail::underflow<half::round_style>(signy));\n    my <<= 11 + i;\n    return half(detail::binary,\n                detail::fixed2half<half::round_style, 11, false, false, true>(\n                    my / mx, d + 14, signy, my % mx != 0));\n  }\n  detail::uint32 m =\n      detail::atan2(((absy & 0x3FF) | ((absy > 0x3FF) << 10))\n                        << (19 + ((d < 0) ? d : (d > 0) ? 0 : -1)),\n                    ((absx & 0x3FF) | ((absx > 0x3FF) << 10))\n                        << (19 - ((d > 0) ? d : (d < 0) ? 0 : 1)));\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 31, false, true, true>(\n                  signx ? (0xC90FDAA2 - m) : m, 15, signy, signx));\n#endif\n}\n\n/// \\}\n/// \\anchor hyperbolic\n/// \\name Hyperbolic functions\n/// \\{\n\n/// Hyperbolic sine.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::sinh](https://en.cppreference.com/w/cpp/numeric/math/sinh).\n/// \\param arg function argument\n/// \\return hyperbolic sine value of \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half sinh(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::sinh(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, exp;\n  if (!abs || abs >= 0x7C00)\n    return (abs > 0x7C00) ? half(detail::binary, detail::signal(arg.data_))\n                          : arg;\n  if (abs <= 0x2900)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_, 0, 1));\n  std::pair<detail::uint32, detail::uint32> mm = detail::hyperbolic_args(\n      abs, exp, (half::round_style == std::round_to_nearest) ? 29 : 27);\n  detail::uint32 m = mm.first - mm.second;\n  for (exp += 13; m < 0x80000000 && exp; m <<= 1, --exp)\n    ;\n  unsigned int sign = arg.data_ & 0x8000;\n  if (exp > 29)\n    return half(detail::binary, detail::overflow<half::round_style>(sign));\n  return half(detail::binary,\n              detail::fixed2half<half::round_style, 31, false, false, true>(\n                  m, exp, sign));\n#endif\n}\n\n/// Hyperbolic cosine.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::cosh](https://en.cppreference.com/w/cpp/numeric/math/cosh).\n/// \\param arg function argument\n/// \\return hyperbolic cosine value of \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half cosh(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::cosh(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, exp;\n  if (!abs) return half(detail::binary, 0x3C00);\n  if (abs >= 0x7C00)\n    return half(detail::binary,\n                (abs > 0x7C00) ? detail::signal(arg.data_) : 0x7C00);\n  std::pair<detail::uint32, detail::uint32> mm = detail::hyperbolic_args(\n      abs, exp, (half::round_style == std::round_to_nearest) ? 23 : 26);\n  detail::uint32 m = mm.first + mm.second, i = (~m & 0xFFFFFFFF) >> 31;\n  m = (m >> i) | (m & i) | 0x80000000;\n  if ((exp += 13 + i) > 29)\n    return half(detail::binary, detail::overflow<half::round_style>());\n  return half(\n      detail::binary,\n      detail::fixed2half<half::round_style, 31, false, false, true>(m, exp));\n#endif\n}\n\n/// Hyperbolic tangent.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::tanh](https://en.cppreference.com/w/cpp/numeric/math/tanh).\n/// \\param arg function argument\n/// \\return hyperbolic tangent value of \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half tanh(half arg) {\n#ifdef HALF_ARITHMETIC_TYPE\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::tanh(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, exp;\n  if (!abs) return arg;\n  if (abs >= 0x7C00)\n    return half(\n        detail::binary,\n        (abs > 0x7C00) ? detail::signal(arg.data_) : (arg.data_ - 0x4000));\n  if (abs >= 0x4500)\n    return half(detail::binary, detail::rounded<half::round_style, true>(\n                                    (arg.data_ & 0x8000) | 0x3BFF, 1, 1));\n  if (abs < 0x2700)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_ - 1, 1, 1));\n  if (half::round_style != std::round_to_nearest && abs == 0x2D3F)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_ - 3, 0, 1));\n  std::pair<detail::uint32, detail::uint32> mm =\n      detail::hyperbolic_args(abs, exp, 27);\n  detail::uint32 my = mm.first - mm.second -\n                      (half::round_style != std::round_to_nearest),\n                 mx = mm.first + mm.second, i = (~mx & 0xFFFFFFFF) >> 31;\n  for (exp = 13; my < 0x80000000; my <<= 1, --exp)\n    ;\n  mx = (mx >> i) | 0x80000000;\n  return half(detail::binary, detail::tangent_post<half::round_style>(\n                                  my, mx, exp - i, arg.data_ & 0x8000));\n#endif\n}\n\n/// Hyperbolic area sine.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::asinh](https://en.cppreference.com/w/cpp/numeric/math/asinh).\n/// \\param arg function argument\n/// \\return area sine value of \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half asinh(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::asinh(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF;\n  if (!abs || abs >= 0x7C00)\n    return (abs > 0x7C00) ? half(detail::binary, detail::signal(arg.data_))\n                          : arg;\n  if (abs <= 0x2900)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_ - 1, 1, 1));\n  if (half::round_style != std::round_to_nearest) switch (abs) {\n      case 0x32D4:\n        return half(detail::binary, detail::rounded<half::round_style, true>(\n                                        arg.data_ - 13, 1, 1));\n      case 0x3B5B:\n        return half(detail::binary, detail::rounded<half::round_style, true>(\n                                        arg.data_ - 197, 1, 1));\n    }\n  return half(detail::binary, detail::area<half::round_style, true>(arg.data_));\n#endif\n}\n\n/// Hyperbolic area cosine.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::acosh](https://en.cppreference.com/w/cpp/numeric/math/acosh).\n/// \\param arg function argument\n/// \\return area cosine value of \\a arg\n/// \\exception FE_INVALID for signaling NaN or arguments <1\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half acosh(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::acosh(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF;\n  if ((arg.data_ & 0x8000) || abs < 0x3C00)\n    return half(\n        detail::binary,\n        (abs <= 0x7C00) ? detail::invalid() : detail::signal(arg.data_));\n  if (abs == 0x3C00) return half(detail::binary, 0);\n  if (arg.data_ >= 0x7C00)\n    return (abs > 0x7C00) ? half(detail::binary, detail::signal(arg.data_))\n                          : arg;\n  return half(detail::binary,\n              detail::area<half::round_style, false>(arg.data_));\n#endif\n}\n\n/// Hyperbolic area tangent.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::atanh](https://en.cppreference.com/w/cpp/numeric/math/atanh).\n/// \\param arg function argument\n/// \\return area tangent value of \\a arg\n/// \\exception FE_INVALID for signaling NaN or if abs(\\a arg) > 1\n/// \\exception FE_DIVBYZERO for +/-1\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half atanh(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::atanh(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF, exp = 0;\n  if (!abs) return arg;\n  if (abs >= 0x3C00)\n    return half(detail::binary,\n                (abs == 0x3C00) ? detail::pole(arg.data_ & 0x8000)\n                                : (abs <= 0x7C00) ? detail::invalid()\n                                                  : detail::signal(arg.data_));\n  if (abs < 0x2700)\n    return half(detail::binary,\n                detail::rounded<half::round_style, true>(arg.data_, 0, 1));\n  detail::uint32 m = static_cast<detail::uint32>((abs & 0x3FF) |\n                                                 ((abs > 0x3FF) << 10))\n                     << ((abs >> 10) + (abs <= 0x3FF) + 6),\n                 my = 0x80000000 + m, mx = 0x80000000 - m;\n  for (; mx < 0x80000000; mx <<= 1, ++exp)\n    ;\n  int i = my >= mx, s;\n  return half(\n      detail::binary,\n      detail::log2_post<half::round_style, 0xB8AA3B2A>(\n          detail::log2((detail::divide64(my >> i, mx, s) + 1) >> 1, 27) + 0x10,\n          exp + i - 1, 16, arg.data_ & 0x8000));\n#endif\n}\n\n/// \\}\n/// \\anchor special\n/// \\name Error and gamma functions\n/// \\{\n\n/// Error function.\n/// This function may be 1 ULP off the correctly rounded exact result for any\n/// rounding mode in <0.5% of inputs.\n///\n/// **See also:** Documentation for\n/// [std::erf](https://en.cppreference.com/w/cpp/numeric/math/erf).\n/// \\param arg function argument\n/// \\return error function value of \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half erf(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(\n                  std::erf(detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  unsigned int abs = arg.data_ & 0x7FFF;\n  if (!abs || abs >= 0x7C00)\n    return (abs >= 0x7C00) ? half(detail::binary,\n                                  (abs == 0x7C00) ? (arg.data_ - 0x4000)\n                                                  : detail::signal(arg.data_))\n                           : arg;\n  if (abs >= 0x4200)\n    return half(detail::binary, detail::rounded<half::round_style, true>(\n                                    (arg.data_ & 0x8000) | 0x3BFF, 1, 1));\n  return half(detail::binary, detail::erf<half::round_style, false>(arg.data_));\n#endif\n}\n\n/// Complementary error function.\n/// This function may be 1 ULP off the correctly rounded exact result for any\n/// rounding mode in <0.5% of inputs.\n///\n/// **See also:** Documentation for\n/// [std::erfc](https://en.cppreference.com/w/cpp/numeric/math/erfc).\n/// \\param arg function argument\n/// \\return 1 minus error function value of \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half erfc(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::erfc(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  unsigned int abs = arg.data_ & 0x7FFF, sign = arg.data_ & 0x8000;\n  if (abs >= 0x7C00)\n    return (abs >= 0x7C00)\n               ? half(detail::binary,\n                      (abs == 0x7C00) ? (sign >> 1) : detail::signal(arg.data_))\n               : arg;\n  if (!abs) return half(detail::binary, 0x3C00);\n  if (abs >= 0x4400)\n    return half(detail::binary, detail::rounded<half::round_style, true>(\n                                    (sign >> 1) - (sign >> 15), sign >> 15, 1));\n  return half(detail::binary, detail::erf<half::round_style, true>(arg.data_));\n#endif\n}\n\n/// Natural logarithm of gamma function.\n/// This function may be 1 ULP off the correctly rounded exact result for any\n/// rounding mode in ~0.025% of inputs.\n///\n/// **See also:** Documentation for\n/// [std::lgamma](https://en.cppreference.com/w/cpp/numeric/math/lgamma).\n/// \\param arg function argument\n/// \\return natural logarith of gamma function for \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_DIVBYZERO for 0 or negative integer arguments\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half lgamma(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::lgamma(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  int abs = arg.data_ & 0x7FFF;\n  if (abs >= 0x7C00)\n    return half(detail::binary,\n                (abs == 0x7C00) ? 0x7C00 : detail::signal(arg.data_));\n  if (!abs || arg.data_ >= 0xE400 ||\n      (arg.data_ >= 0xBC00 && !(abs & ((1 << (25 - (abs >> 10))) - 1))))\n    return half(detail::binary, detail::pole());\n  if (arg.data_ == 0x3C00 || arg.data_ == 0x4000)\n    return half(detail::binary, 0);\n  return half(detail::binary,\n              detail::gamma<half::round_style, true>(arg.data_));\n#endif\n}\n\n/// Gamma function.\n/// This function may be 1 ULP off the correctly rounded exact result for any\n/// rounding mode in <0.25% of inputs.\n///\n/// **See also:** Documentation for\n/// [std::tgamma](https://en.cppreference.com/w/cpp/numeric/math/tgamma).\n/// \\param arg function argument\n/// \\return gamma function value of \\a arg\n/// \\exception FE_INVALID for signaling NaN, negative infinity or negative\n/// integer arguments\n/// \\exception FE_DIVBYZERO for 0\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half tgamma(half arg) {\n#if defined(HALF_ARITHMETIC_TYPE) && HALF_ENABLE_CPP11_CMATH\n  return half(detail::binary,\n              detail::float2half<half::round_style>(std::tgamma(\n                  detail::half2float<detail::internal_t>(arg.data_))));\n#else\n  unsigned int abs = arg.data_ & 0x7FFF;\n  if (!abs) return half(detail::binary, detail::pole(arg.data_));\n  if (abs >= 0x7C00)\n    return (arg.data_ == 0x7C00) ? arg : half(detail::binary,\n                                              detail::signal(arg.data_));\n  if (arg.data_ >= 0xE400 ||\n      (arg.data_ >= 0xBC00 && !(abs & ((1 << (25 - (abs >> 10))) - 1))))\n    return half(detail::binary, detail::invalid());\n  if (arg.data_ >= 0xCA80)\n    return half(detail::binary,\n                detail::underflow<half::round_style>(\n                    (1 - ((abs >> (25 - (abs >> 10))) & 1)) << 15));\n  if (arg.data_ <= 0x100 || (arg.data_ >= 0x4900 && arg.data_ < 0x8000))\n    return half(detail::binary, detail::overflow<half::round_style>());\n  if (arg.data_ == 0x3C00) return arg;\n  return half(detail::binary,\n              detail::gamma<half::round_style, false>(arg.data_));\n#endif\n}\n\n/// \\}\n/// \\anchor rounding\n/// \\name Rounding\n/// \\{\n\n/// Nearest integer not less than half value.\n/// **See also:** Documentation for\n/// [std::ceil](https://en.cppreference.com/w/cpp/numeric/math/ceil).\n/// \\param arg half to round\n/// \\return nearest integer not less than \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_INEXACT if value had to be rounded\ninline half ceil(half arg) {\n  return half(\n      detail::binary,\n      detail::integral<std::round_toward_infinity, true, true>(arg.data_));\n}\n\n/// Nearest integer not greater than half value.\n/// **See also:** Documentation for\n/// [std::floor](https://en.cppreference.com/w/cpp/numeric/math/floor).\n/// \\param arg half to round\n/// \\return nearest integer not greater than \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_INEXACT if value had to be rounded\ninline half floor(half arg) {\n  return half(\n      detail::binary,\n      detail::integral<std::round_toward_neg_infinity, true, true>(arg.data_));\n}\n\n/// Nearest integer not greater in magnitude than half value.\n/// **See also:** Documentation for\n/// [std::trunc](https://en.cppreference.com/w/cpp/numeric/math/trunc).\n/// \\param arg half to round\n/// \\return nearest integer not greater in magnitude than \\a arg\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_INEXACT if value had to be rounded\ninline half trunc(half arg) {\n  return half(detail::binary,\n              detail::integral<std::round_toward_zero, true, true>(arg.data_));\n}\n\n/// Nearest integer.\n/// **See also:** Documentation for\n/// [std::round](https://en.cppreference.com/w/cpp/numeric/math/round).\n/// \\param arg half to round\n/// \\return nearest integer, rounded away from zero in half-way cases\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_INEXACT if value had to be rounded\ninline half round(half arg) {\n  return half(detail::binary,\n              detail::integral<std::round_to_nearest, false, true>(arg.data_));\n}\n\n/// Nearest integer.\n/// **See also:** Documentation for\n/// [std::lround](https://en.cppreference.com/w/cpp/numeric/math/round).\n/// \\param arg half to round\n/// \\return nearest integer, rounded away from zero in half-way cases\n/// \\exception FE_INVALID if value is not representable as `long`\ninline long lround(half arg) {\n  return detail::half2int<std::round_to_nearest, false, false, long>(arg.data_);\n}\n\n/// Nearest integer using half's internal rounding mode.\n/// **See also:** Documentation for\n/// [std::rint](https://en.cppreference.com/w/cpp/numeric/math/rint).\n/// \\param arg half expression to round\n/// \\return nearest integer using default rounding mode\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_INEXACT if value had to be rounded\ninline half rint(half arg) {\n  return half(detail::binary,\n              detail::integral<half::round_style, true, true>(arg.data_));\n}\n\n/// Nearest integer using half's internal rounding mode.\n/// **See also:** Documentation for\n/// [std::lrint](https://en.cppreference.com/w/cpp/numeric/math/rint).\n/// \\param arg half expression to round\n/// \\return nearest integer using default rounding mode\n/// \\exception FE_INVALID if value is not representable as `long`\n/// \\exception FE_INEXACT if value had to be rounded\ninline long lrint(half arg) {\n  return detail::half2int<half::round_style, true, true, long>(arg.data_);\n}\n\n/// Nearest integer using half's internal rounding mode.\n/// **See also:** Documentation for\n/// [std::nearbyint](https://en.cppreference.com/w/cpp/numeric/math/nearbyint).\n/// \\param arg half expression to round\n/// \\return nearest integer using default rounding mode\n/// \\exception FE_INVALID for signaling NaN\ninline half nearbyint(half arg) {\n  return half(detail::binary,\n              detail::integral<half::round_style, true, false>(arg.data_));\n}\n#if HALF_ENABLE_CPP11_LONG_LONG\n/// Nearest integer.\n/// **See also:** Documentation for\n/// [std::llround](https://en.cppreference.com/w/cpp/numeric/math/round).\n/// \\param arg half to round\n/// \\return nearest integer, rounded away from zero in half-way cases\n/// \\exception FE_INVALID if value is not representable as `long long`\ninline long long llround(half arg) {\n  return detail::half2int<std::round_to_nearest, false, false, long long>(\n      arg.data_);\n}\n\n/// Nearest integer using half's internal rounding mode.\n/// **See also:** Documentation for\n/// [std::llrint](https://en.cppreference.com/w/cpp/numeric/math/rint).\n/// \\param arg half expression to round\n/// \\return nearest integer using default rounding mode\n/// \\exception FE_INVALID if value is not representable as `long long`\n/// \\exception FE_INEXACT if value had to be rounded\ninline long long llrint(half arg) {\n  return detail::half2int<half::round_style, true, true, long long>(arg.data_);\n}\n#endif\n\n/// \\}\n/// \\anchor float\n/// \\name Floating point manipulation\n/// \\{\n\n/// Decompress floating-point number.\n/// **See also:** Documentation for\n/// [std::frexp](https://en.cppreference.com/w/cpp/numeric/math/frexp).\n/// \\param arg number to decompress\n/// \\param exp address to store exponent at\n/// \\return significant in range [0.5, 1)\n/// \\exception FE_INVALID for signaling NaN\ninline half frexp(half arg, int *exp) {\n  *exp = 0;\n  unsigned int abs = arg.data_ & 0x7FFF;\n  if (abs >= 0x7C00 || !abs)\n    return (abs > 0x7C00) ? half(detail::binary, detail::signal(arg.data_))\n                          : arg;\n  for (; abs < 0x400; abs <<= 1, --*exp)\n    ;\n  *exp += (abs >> 10) - 14;\n  return half(detail::binary, (arg.data_ & 0x8000) | 0x3800 | (abs & 0x3FF));\n}\n\n/// Multiply by power of two.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::scalbln](https://en.cppreference.com/w/cpp/numeric/math/scalbn).\n/// \\param arg number to modify\n/// \\param exp power of two to multiply with\n/// \\return \\a arg multplied by 2 raised to \\a exp\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half scalbln(half arg, long exp) {\n  unsigned int abs = arg.data_ & 0x7FFF, sign = arg.data_ & 0x8000;\n  if (abs >= 0x7C00 || !abs)\n    return (abs > 0x7C00) ? half(detail::binary, detail::signal(arg.data_))\n                          : arg;\n  for (; abs < 0x400; abs <<= 1, --exp)\n    ;\n  exp += abs >> 10;\n  if (exp > 30)\n    return half(detail::binary, detail::overflow<half::round_style>(sign));\n  else if (exp < -10)\n    return half(detail::binary, detail::underflow<half::round_style>(sign));\n  else if (exp > 0)\n    return half(detail::binary, sign | (exp << 10) | (abs & 0x3FF));\n  unsigned int m = (abs & 0x3FF) | 0x400;\n  return half(detail::binary, detail::rounded<half::round_style, false>(\n                                  sign | (m >> (1 - exp)), (m >> -exp) & 1,\n                                  (m & ((1 << -exp) - 1)) != 0));\n}\n\n/// Multiply by power of two.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::scalbn](https://en.cppreference.com/w/cpp/numeric/math/scalbn).\n/// \\param arg number to modify\n/// \\param exp power of two to multiply with\n/// \\return \\a arg multplied by 2 raised to \\a exp\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half scalbn(half arg, int exp) { return scalbln(arg, exp); }\n\n/// Multiply by power of two.\n/// This function is exact to rounding for all rounding modes.\n///\n/// **See also:** Documentation for\n/// [std::ldexp](https://en.cppreference.com/w/cpp/numeric/math/ldexp).\n/// \\param arg number to modify\n/// \\param exp power of two to multiply with\n/// \\return \\a arg multplied by 2 raised to \\a exp\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ninline half ldexp(half arg, int exp) { return scalbln(arg, exp); }\n\n/// Extract integer and fractional parts.\n/// **See also:** Documentation for\n/// [std::modf](https://en.cppreference.com/w/cpp/numeric/math/modf).\n/// \\param arg number to decompress\n/// \\param iptr address to store integer part at\n/// \\return fractional part\n/// \\exception FE_INVALID for signaling NaN\ninline half modf(half arg, half *iptr) {\n  unsigned int abs = arg.data_ & 0x7FFF;\n  if (abs > 0x7C00) {\n    arg = half(detail::binary, detail::signal(arg.data_));\n    return *iptr = arg, arg;\n  }\n  if (abs >= 0x6400)\n    return *iptr = arg, half(detail::binary, arg.data_ & 0x8000);\n  if (abs < 0x3C00) return iptr->data_ = arg.data_ & 0x8000, arg;\n  unsigned int exp = abs >> 10, mask = (1 << (25 - exp)) - 1,\n               m = arg.data_ & mask;\n  iptr->data_ = arg.data_ & ~mask;\n  if (!m) return half(detail::binary, arg.data_ & 0x8000);\n  for (; m < 0x400; m <<= 1, --exp)\n    ;\n  return half(detail::binary, (arg.data_ & 0x8000) | (exp << 10) | (m & 0x3FF));\n}\n\n/// Extract exponent.\n/// **See also:** Documentation for\n/// [std::ilogb](https://en.cppreference.com/w/cpp/numeric/math/ilogb).\n/// \\param arg number to query\n/// \\return floating-point exponent\n/// \\retval FP_ILOGB0 for zero\n/// \\retval FP_ILOGBNAN for NaN\n/// \\retval INT_MAX for infinity\n/// \\exception FE_INVALID for 0 or infinite values\ninline int ilogb(half arg) {\n  int abs = arg.data_ & 0x7FFF, exp;\n  if (!abs || abs >= 0x7C00) {\n    detail::raise(FE_INVALID);\n    return !abs ? FP_ILOGB0 : (abs == 0x7C00) ? INT_MAX : FP_ILOGBNAN;\n  }\n  for (exp = (abs >> 10) - 15; abs < 0x200; abs <<= 1, --exp)\n    ;\n  return exp;\n}\n\n/// Extract exponent.\n/// **See also:** Documentation for\n/// [std::logb](https://en.cppreference.com/w/cpp/numeric/math/logb).\n/// \\param arg number to query\n/// \\return floating-point exponent\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_DIVBYZERO for 0\ninline half logb(half arg) {\n  int abs = arg.data_ & 0x7FFF, exp;\n  if (!abs) return half(detail::binary, detail::pole(0x8000));\n  if (abs >= 0x7C00)\n    return half(detail::binary,\n                (abs == 0x7C00) ? 0x7C00 : detail::signal(arg.data_));\n  for (exp = (abs >> 10) - 15; abs < 0x200; abs <<= 1, --exp)\n    ;\n  unsigned int value = static_cast<unsigned>(exp < 0) << 15;\n  if (exp) {\n    unsigned int m = std::abs(exp) << 6;\n    for (exp = 18; m < 0x400; m <<= 1, --exp)\n      ;\n    value |= (exp << 10) + m;\n  }\n  return half(detail::binary, value);\n}\n\n/// Next representable value.\n/// **See also:** Documentation for\n/// [std::nextafter](https://en.cppreference.com/w/cpp/numeric/math/nextafter).\n/// \\param from value to compute next representable value for\n/// \\param to direction towards which to compute next value\n/// \\return next representable value after \\a from in direction towards \\a to\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW for infinite result from finite argument\n/// \\exception FE_UNDERFLOW for subnormal result\ninline half nextafter(half from, half to) {\n  int fabs = from.data_ & 0x7FFF, tabs = to.data_ & 0x7FFF;\n  if (fabs > 0x7C00 || tabs > 0x7C00)\n    return half(detail::binary, detail::signal(from.data_, to.data_));\n  if (from.data_ == to.data_ || !(fabs | tabs)) return to;\n  if (!fabs) {\n    detail::raise(FE_UNDERFLOW, !HALF_ERRHANDLING_UNDERFLOW_TO_INEXACT);\n    return half(detail::binary, (to.data_ & 0x8000) + 1);\n  }\n  unsigned int out =\n      from.data_ +\n      (((from.data_ >> 15) ^\n        static_cast<unsigned>(\n            (from.data_ ^ (0x8000 | (0x8000 - (from.data_ >> 15)))) <\n            (to.data_ ^ (0x8000 | (0x8000 - (to.data_ >> 15))))))\n       << 1) -\n      1;\n  detail::raise(FE_OVERFLOW, fabs < 0x7C00 && (out & 0x7C00) == 0x7C00);\n  detail::raise(\n      FE_UNDERFLOW,\n      !HALF_ERRHANDLING_UNDERFLOW_TO_INEXACT && (out & 0x7C00) < 0x400);\n  return half(detail::binary, out);\n}\n\n/// Next representable value.\n/// **See also:** Documentation for\n/// [std::nexttoward](https://en.cppreference.com/w/cpp/numeric/math/nexttoward).\n/// \\param from value to compute next representable value for\n/// \\param to direction towards which to compute next value\n/// \\return next representable value after \\a from in direction towards \\a to\n/// \\exception FE_INVALID for signaling NaN\n/// \\exception FE_OVERFLOW for infinite result from finite argument\n/// \\exception FE_UNDERFLOW for subnormal result\ninline half nexttoward(half from, long double to) {\n  int fabs = from.data_ & 0x7FFF;\n  if (fabs > 0x7C00) return half(detail::binary, detail::signal(from.data_));\n  long double lfrom = static_cast<long double>(from);\n  if (detail::builtin_isnan(to) || lfrom == to)\n    return half(static_cast<float>(to));\n  if (!fabs) {\n    detail::raise(FE_UNDERFLOW, !HALF_ERRHANDLING_UNDERFLOW_TO_INEXACT);\n    return half(detail::binary,\n                (static_cast<unsigned>(detail::builtin_signbit(to)) << 15) + 1);\n  }\n  unsigned int out =\n      from.data_ +\n      (((from.data_ >> 15) ^ static_cast<unsigned>(lfrom < to)) << 1) - 1;\n  detail::raise(FE_OVERFLOW, (out & 0x7FFF) == 0x7C00);\n  detail::raise(\n      FE_UNDERFLOW,\n      !HALF_ERRHANDLING_UNDERFLOW_TO_INEXACT && (out & 0x7FFF) < 0x400);\n  return half(detail::binary, out);\n}\n\n/// Take sign.\n/// **See also:** Documentation for\n/// [std::copysign](https://en.cppreference.com/w/cpp/numeric/math/copysign).\n/// \\param x value to change sign for\n/// \\param y value to take sign from\n/// \\return value equal to \\a x in magnitude and to \\a y in sign\ninline HALF_CONSTEXPR half copysign(half x, half y) {\n  return half(detail::binary, x.data_ ^ ((x.data_ ^ y.data_) & 0x8000));\n}\n\n/// \\}\n/// \\anchor classification\n/// \\name Floating point classification\n/// \\{\n\n/// Classify floating-point value.\n/// **See also:** Documentation for\n/// [std::fpclassify](https://en.cppreference.com/w/cpp/numeric/math/fpclassify).\n/// \\param arg number to classify\n/// \\retval FP_ZERO for positive and negative zero\n/// \\retval FP_SUBNORMAL for subnormal numbers\n/// \\retval FP_INFINITY for positive and negative infinity\n/// \\retval FP_NAN for NaNs\n/// \\retval FP_NORMAL for all other (normal) values\ninline HALF_CONSTEXPR int fpclassify(half arg) {\n  return !(arg.data_ & 0x7FFF)\n             ? FP_ZERO\n             : ((arg.data_ & 0x7FFF) < 0x400)\n                   ? FP_SUBNORMAL\n                   : ((arg.data_ & 0x7FFF) < 0x7C00)\n                         ? FP_NORMAL\n                         : ((arg.data_ & 0x7FFF) == 0x7C00) ? FP_INFINITE\n                                                            : FP_NAN;\n}\n\n/// Check if finite number.\n/// **See also:** Documentation for\n/// [std::isfinite](https://en.cppreference.com/w/cpp/numeric/math/isfinite).\n/// \\param arg number to check\n/// \\retval true if neither infinity nor NaN\n/// \\retval false else\ninline HALF_CONSTEXPR bool isfinite(half arg) {\n  return (arg.data_ & 0x7C00) != 0x7C00;\n}\n\n/// Check for infinity.\n/// **See also:** Documentation for\n/// [std::isinf](https://en.cppreference.com/w/cpp/numeric/math/isinf).\n/// \\param arg number to check\n/// \\retval true for positive or negative infinity\n/// \\retval false else\ninline HALF_CONSTEXPR bool isinf(half arg) {\n  return (arg.data_ & 0x7FFF) == 0x7C00;\n}\n\n/// Check for NaN.\n/// **See also:** Documentation for\n/// [std::isnan](https://en.cppreference.com/w/cpp/numeric/math/isnan).\n/// \\param arg number to check\n/// \\retval true for NaNs\n/// \\retval false else\ninline HALF_CONSTEXPR bool isnan(half arg) {\n  return (arg.data_ & 0x7FFF) > 0x7C00;\n}\n\n/// Check if normal number.\n/// **See also:** Documentation for\n/// [std::isnormal](https://en.cppreference.com/w/cpp/numeric/math/isnormal).\n/// \\param arg number to check\n/// \\retval true if normal number\n/// \\retval false if either subnormal, zero, infinity or NaN\ninline HALF_CONSTEXPR bool isnormal(half arg) {\n  return ((arg.data_ & 0x7C00) != 0) & ((arg.data_ & 0x7C00) != 0x7C00);\n}\n\n/// Check sign.\n/// **See also:** Documentation for\n/// [std::signbit](https://en.cppreference.com/w/cpp/numeric/math/signbit).\n/// \\param arg number to check\n/// \\retval true for negative number\n/// \\retval false for positive number\ninline HALF_CONSTEXPR bool signbit(half arg) {\n  return (arg.data_ & 0x8000) != 0;\n}\n\n/// \\}\n/// \\anchor compfunc\n/// \\name Comparison\n/// \\{\n\n/// Quiet comparison for greater than.\n/// **See also:** Documentation for\n/// [std::isgreater](https://en.cppreference.com/w/cpp/numeric/math/isgreater).\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if \\a x greater than \\a y\n/// \\retval false else\ninline HALF_CONSTEXPR bool isgreater(half x, half y) {\n  return ((x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) + (x.data_ >> 15)) >\n             ((y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15)))) +\n              (y.data_ >> 15)) &&\n         !isnan(x) && !isnan(y);\n}\n\n/// Quiet comparison for greater equal.\n/// **See also:** Documentation for\n/// [std::isgreaterequal](https://en.cppreference.com/w/cpp/numeric/math/isgreaterequal).\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if \\a x greater equal \\a y\n/// \\retval false else\ninline HALF_CONSTEXPR bool isgreaterequal(half x, half y) {\n  return ((x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) +\n          (x.data_ >> 15)) >=\n             ((y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15)))) +\n              (y.data_ >> 15)) &&\n         !isnan(x) && !isnan(y);\n}\n\n/// Quiet comparison for less than.\n/// **See also:** Documentation for\n/// [std::isless](https://en.cppreference.com/w/cpp/numeric/math/isless).\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if \\a x less than \\a y\n/// \\retval false else\ninline HALF_CONSTEXPR bool isless(half x, half y) {\n  return ((x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) + (x.data_ >> 15)) <\n             ((y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15)))) +\n              (y.data_ >> 15)) &&\n         !isnan(x) && !isnan(y);\n}\n\n/// Quiet comparison for less equal.\n/// **See also:** Documentation for\n/// [std::islessequal](https://en.cppreference.com/w/cpp/numeric/math/islessequal).\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if \\a x less equal \\a y\n/// \\retval false else\ninline HALF_CONSTEXPR bool islessequal(half x, half y) {\n  return ((x.data_ ^ (0x8000 | (0x8000 - (x.data_ >> 15)))) +\n          (x.data_ >> 15)) <=\n             ((y.data_ ^ (0x8000 | (0x8000 - (y.data_ >> 15)))) +\n              (y.data_ >> 15)) &&\n         !isnan(x) && !isnan(y);\n}\n\n/// Quiet comarison for less or greater.\n/// **See also:** Documentation for\n/// [std::islessgreater](https://en.cppreference.com/w/cpp/numeric/math/islessgreater).\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if either less or greater\n/// \\retval false else\ninline HALF_CONSTEXPR bool islessgreater(half x, half y) {\n  return x.data_ != y.data_ && ((x.data_ | y.data_) & 0x7FFF) && !isnan(x) &&\n         !isnan(y);\n}\n\n/// Quiet check if unordered.\n/// **See also:** Documentation for\n/// [std::isunordered](https://en.cppreference.com/w/cpp/numeric/math/isunordered).\n/// \\param x first operand\n/// \\param y second operand\n/// \\retval true if unordered (one or two NaN operands)\n/// \\retval false else\ninline HALF_CONSTEXPR bool isunordered(half x, half y) {\n  return isnan(x) || isnan(y);\n}\n\n/// \\}\n/// \\anchor casting\n/// \\name Casting\n/// \\{\n\n/// Cast to or from half-precision floating-point number.\n/// This casts between [half](\\ref half_float::half) and any built-in arithmetic\n/// type. The values are converted\n/// directly using the default rounding mode, without any roundtrip over `float`\n/// that a `static_cast` would otherwise do.\n///\n/// Using this cast with neither of the two types being a [half](\\ref\n/// half_float::half) or with any of the two types\n/// not being a built-in arithmetic type (apart from [half](\\ref\n/// half_float::half), of course) results in a compiler\n/// error and casting between [half](\\ref half_float::half)s returns the\n/// argument unmodified.\n/// \\tparam T destination type (half or built-in arithmetic type)\n/// \\tparam U source type (half or built-in arithmetic type)\n/// \\param arg value to cast\n/// \\return \\a arg converted to destination type\n/// \\exception FE_INVALID if \\a T is integer type and result is not\n/// representable as \\a T\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ntemplate <typename T, typename U>\nT half_cast(U arg) {\n  return detail::half_caster<T, U>::cast(arg);\n}\n\n/// Cast to or from half-precision floating-point number.\n/// This casts between [half](\\ref half_float::half) and any built-in arithmetic\n/// type. The values are converted\n/// directly using the specified rounding mode, without any roundtrip over\n/// `float` that a `static_cast` would otherwise do.\n///\n/// Using this cast with neither of the two types being a [half](\\ref\n/// half_float::half) or with any of the two types\n/// not being a built-in arithmetic type (apart from [half](\\ref\n/// half_float::half), of course) results in a compiler\n/// error and casting between [half](\\ref half_float::half)s returns the\n/// argument unmodified.\n/// \\tparam T destination type (half or built-in arithmetic type)\n/// \\tparam R rounding mode to use.\n/// \\tparam U source type (half or built-in arithmetic type)\n/// \\param arg value to cast\n/// \\return \\a arg converted to destination type\n/// \\exception FE_INVALID if \\a T is integer type and result is not\n/// representable as \\a T\n/// \\exception FE_OVERFLOW, ...UNDERFLOW, ...INEXACT according to rounding\ntemplate <typename T, std::float_round_style R, typename U>\nT half_cast(U arg) {\n  return detail::half_caster<T, U, R>::cast(arg);\n}\n/// \\}\n\n/// \\}\n/// \\anchor errors\n/// \\name Error handling\n/// \\{\n\n/// Clear exception flags.\n/// This function works even if [automatic exception flag handling](\\ref\n/// HALF_ERRHANDLING_FLAGS) is disabled,\n/// but in that case manual flag management is the only way to raise flags.\n///\n/// **See also:** Documentation for\n/// [std::feclearexcept](https://en.cppreference.com/w/cpp/numeric/fenv/feclearexcept).\n/// \\param excepts OR of exceptions to clear\n/// \\retval 0 all selected flags cleared successfully\ninline int feclearexcept(int excepts) {\n  detail::errflags() &= ~excepts;\n  return 0;\n}\n\n/// Test exception flags.\n/// This function works even if [automatic exception flag handling](\\ref\n/// HALF_ERRHANDLING_FLAGS) is disabled,\n/// but in that case manual flag management is the only way to raise flags.\n///\n/// **See also:** Documentation for\n/// [std::fetestexcept](https://en.cppreference.com/w/cpp/numeric/fenv/fetestexcept).\n/// \\param excepts OR of exceptions to test\n/// \\return OR of selected exceptions if raised\ninline int fetestexcept(int excepts) { return detail::errflags() & excepts; }\n\n/// Raise exception flags.\n/// This raises the specified floating point exceptions and also invokes any\n/// additional automatic exception handling as\n/// configured with the [HALF_ERRHANDLIG_...](\\ref HALF_ERRHANDLING_ERRNO)\n/// preprocessor symbols.\n/// This function works even if [automatic exception flag handling](\\ref\n/// HALF_ERRHANDLING_FLAGS) is disabled,\n/// but in that case manual flag management is the only way to raise flags.\n///\n/// **See also:** Documentation for\n/// [std::feraiseexcept](https://en.cppreference.com/w/cpp/numeric/fenv/feraiseexcept).\n/// \\param excepts OR of exceptions to raise\n/// \\retval 0 all selected exceptions raised successfully\ninline int feraiseexcept(int excepts) {\n  detail::errflags() |= excepts;\n  detail::raise(excepts);\n  return 0;\n}\n\n/// Save exception flags.\n/// This function works even if [automatic exception flag handling](\\ref\n/// HALF_ERRHANDLING_FLAGS) is disabled,\n/// but in that case manual flag management is the only way to raise flags.\n///\n/// **See also:** Documentation for\n/// [std::fegetexceptflag](https://en.cppreference.com/w/cpp/numeric/fenv/feexceptflag).\n/// \\param flagp adress to store flag state at\n/// \\param excepts OR of flags to save\n/// \\retval 0 for success\ninline int fegetexceptflag(int *flagp, int excepts) {\n  *flagp = detail::errflags() & excepts;\n  return 0;\n}\n\n/// Restore exception flags.\n/// This only copies the specified exception state (including unset flags)\n/// without incurring any additional exception handling.\n/// This function works even if [automatic exception flag handling](\\ref\n/// HALF_ERRHANDLING_FLAGS) is disabled,\n/// but in that case manual flag management is the only way to raise flags.\n///\n/// **See also:** Documentation for\n/// [std::fesetexceptflag](https://en.cppreference.com/w/cpp/numeric/fenv/feexceptflag).\n/// \\param flagp adress to take flag state from\n/// \\param excepts OR of flags to restore\n/// \\retval 0 for success\ninline int fesetexceptflag(const int *flagp, int excepts) {\n  detail::errflags() =\n      (detail::errflags() | (*flagp & excepts)) & (*flagp | ~excepts);\n  return 0;\n}\n\n/// Throw C++ exceptions based on set exception flags.\n/// This function manually throws a corresponding C++ exception if one of the\n/// specified flags is set,\n/// no matter if automatic throwing (via [HALF_ERRHANDLING_THROW_...](\\ref\n/// HALF_ERRHANDLING_THROW_INVALID)) is enabled or not.\n/// This function works even if [automatic exception flag handling](\\ref\n/// HALF_ERRHANDLING_FLAGS) is disabled,\n/// but in that case manual flag management is the only way to raise flags.\n/// \\param excepts OR of exceptions to test\n/// \\param msg error message to use for exception description\n/// \\throw std::domain_error if `FE_INVALID` or `FE_DIVBYZERO` is selected and\n/// set\n/// \\throw std::overflow_error if `FE_OVERFLOW` is selected and set\n/// \\throw std::underflow_error if `FE_UNDERFLOW` is selected and set\n/// \\throw std::range_error if `FE_INEXACT` is selected and set\ninline void fethrowexcept(int excepts, const char *msg = \"\") {\n  excepts &= detail::errflags();\n  if (excepts & (FE_INVALID | FE_DIVBYZERO)) throw std::domain_error(msg);\n  if (excepts & FE_OVERFLOW) throw std::overflow_error(msg);\n  if (excepts & FE_UNDERFLOW) throw std::underflow_error(msg);\n  if (excepts & FE_INEXACT) throw std::range_error(msg);\n}\n/// \\}\n}\n\n#undef HALF_UNUSED_NOERR\n#undef HALF_CONSTEXPR\n#undef HALF_CONSTEXPR_CONST\n#undef HALF_CONSTEXPR_NOERR\n#undef HALF_NOEXCEPT\n#undef HALF_NOTHROW\n#undef HALF_THREAD_LOCAL\n#undef HALF_TWOS_COMPLEMENT_INT\n#ifdef HALF_POP_WARNINGS\n#pragma warning(pop)\n#undef HALF_POP_WARNINGS\n#endif\n\n#endif\n"
  },
  {
    "path": "third_party/jemalloc/VERSION",
    "content": "5.2.1\n"
  },
  {
    "path": "third_party/jemalloc/jeprof",
    "content": "#! /usr/bin/env perl\n\n# Copyright (c) 1998-2007, Google Inc.\n# All rights reserved.\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are\n# met:\n#\n#     * Redistributions of source code must retain the above copyright\n# notice, this list of conditions and the following disclaimer.\n#     * Redistributions in binary form must reproduce the above\n# copyright notice, this list of conditions and the following disclaimer\n# in the documentation and/or other materials provided with the\n# distribution.\n#     * Neither the name of Google Inc. nor the names of its\n# contributors may be used to endorse or promote products derived from\n# this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n# ---\n# Program for printing the profile generated by common/profiler.cc,\n# or by the heap profiler (common/debugallocation.cc)\n#\n# The profile contains a sequence of entries of the form:\n#       <count> <stack trace>\n# This program parses the profile, and generates user-readable\n# output.\n#\n# Examples:\n#\n# % tools/jeprof \"program\" \"profile\"\n#   Enters \"interactive\" mode\n#\n# % tools/jeprof --text \"program\" \"profile\"\n#   Generates one line per procedure\n#\n# % tools/jeprof --gv \"program\" \"profile\"\n#   Generates annotated call-graph and displays via \"gv\"\n#\n# % tools/jeprof --gv --focus=Mutex \"program\" \"profile\"\n#   Restrict to code paths that involve an entry that matches \"Mutex\"\n#\n# % tools/jeprof --gv --focus=Mutex --ignore=string \"program\" \"profile\"\n#   Restrict to code paths that involve an entry that matches \"Mutex\"\n#   and does not match \"string\"\n#\n# % tools/jeprof --list=IBF_CheckDocid \"program\" \"profile\"\n#   Generates disassembly listing of all routines with at least one\n#   sample that match the --list=<regexp> pattern.  The listing is\n#   annotated with the flat and cumulative sample counts at each line.\n#\n# % tools/jeprof --disasm=IBF_CheckDocid \"program\" \"profile\"\n#   Generates disassembly listing of all routines with at least one\n#   sample that match the --disasm=<regexp> pattern.  The listing is\n#   annotated with the flat and cumulative sample counts at each PC value.\n#\n# TODO: Use color to indicate files?\n\nuse strict;\nuse warnings;\nuse Getopt::Long;\nuse Cwd;\n\nmy $JEPROF_VERSION = \"0.0.0-0-g0000000000000000000000000000000000000000\";\nmy $PPROF_VERSION = \"2.0\";\n\n# These are the object tools we use which can come from a\n# user-specified location using --tools, from the JEPROF_TOOLS\n# environment variable, or from the environment.\nmy %obj_tool_map = (\n  \"objdump\" => \"objdump\",\n  \"nm\" => \"nm\",\n  \"addr2line\" => \"addr2line\",\n  \"c++filt\" => \"c++filt\",\n  ## ConfigureObjTools may add architecture-specific entries:\n  #\"nm_pdb\" => \"nm-pdb\",       # for reading windows (PDB-format) executables\n  #\"addr2line_pdb\" => \"addr2line-pdb\",                                # ditto\n  #\"otool\" => \"otool\",         # equivalent of objdump on OS X\n);\n# NOTE: these are lists, so you can put in commandline flags if you want.\nmy @DOT = (\"dot\");          # leave non-absolute, since it may be in /usr/local\nmy @GV = (\"gv\");\nmy @EVINCE = (\"evince\");    # could also be xpdf or perhaps acroread\nmy @KCACHEGRIND = (\"kcachegrind\");\nmy @PS2PDF = (\"ps2pdf\");\n# These are used for dynamic profiles\nmy @URL_FETCHER = (\"curl\", \"-s\", \"--fail\");\n\n# These are the web pages that servers need to support for dynamic profiles\nmy $HEAP_PAGE = \"/pprof/heap\";\nmy $PROFILE_PAGE = \"/pprof/profile\";   # must support cgi-param \"?seconds=#\"\nmy $PMUPROFILE_PAGE = \"/pprof/pmuprofile(?:\\\\?.*)?\"; # must support cgi-param\n                                                # ?seconds=#&event=x&period=n\nmy $GROWTH_PAGE = \"/pprof/growth\";\nmy $CONTENTION_PAGE = \"/pprof/contention\";\nmy $WALL_PAGE = \"/pprof/wall(?:\\\\?.*)?\";  # accepts options like namefilter\nmy $FILTEREDPROFILE_PAGE = \"/pprof/filteredprofile(?:\\\\?.*)?\";\nmy $CENSUSPROFILE_PAGE = \"/pprof/censusprofile(?:\\\\?.*)?\"; # must support cgi-param\n                                                       # \"?seconds=#\",\n                                                       # \"?tags_regexp=#\" and\n                                                       # \"?type=#\".\nmy $SYMBOL_PAGE = \"/pprof/symbol\";     # must support symbol lookup via POST\nmy $PROGRAM_NAME_PAGE = \"/pprof/cmdline\";\n\n# These are the web pages that can be named on the command line.\n# All the alternatives must begin with /.\nmy $PROFILES = \"($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|\" .\n               \"$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|\" .\n               \"$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)\";\n\n# default binary name\nmy $UNKNOWN_BINARY = \"(unknown)\";\n\n# There is a pervasive dependency on the length (in hex characters,\n# i.e., nibbles) of an address, distinguishing between 32-bit and\n# 64-bit profiles.  To err on the safe size, default to 64-bit here:\nmy $address_length = 16;\n\nmy $dev_null = \"/dev/null\";\nif (! -e $dev_null && $^O =~ /MSWin/) {    # $^O is the OS perl was built for\n  $dev_null = \"nul\";\n}\n\n# A list of paths to search for shared object files\nmy @prefix_list = ();\n\n# Special routine name that should not have any symbols.\n# Used as separator to parse \"addr2line -i\" output.\nmy $sep_symbol = '_fini';\nmy $sep_address = undef;\n\n##### Argument parsing #####\n\nsub usage_string {\n  return <<EOF;\nUsage:\njeprof [options] <program> <profiles>\n   <profiles> is a space separated list of profile names.\njeprof [options] <symbolized-profiles>\n   <symbolized-profiles> is a list of profile files where each file contains\n   the necessary symbol mappings  as well as profile data (likely generated\n   with --raw).\njeprof [options] <profile>\n   <profile> is a remote form.  Symbols are obtained from host:port$SYMBOL_PAGE\n\n   Each name can be:\n   /path/to/profile        - a path to a profile file\n   host:port[/<service>]   - a location of a service to get profile from\n\n   The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,\n                         $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,\n                         $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.\n   For instance:\n     jeprof http://myserver.com:80$HEAP_PAGE\n   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).\njeprof --symbols <program>\n   Maps addresses to symbol names.  In this mode, stdin should be a\n   list of library mappings, in the same format as is found in the heap-\n   and cpu-profile files (this loosely matches that of /proc/self/maps\n   on linux), followed by a list of hex addresses to map, one per line.\n\n   For more help with querying remote servers, including how to add the\n   necessary server-side support code, see this filename (or one like it):\n\n   /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html\n\nOptions:\n   --cum               Sort by cumulative data\n   --base=<base>       Subtract <base> from <profile> before display\n   --interactive       Run in interactive mode (interactive \"help\" gives help) [default]\n   --seconds=<n>       Length of time for dynamic profiles [default=30 secs]\n   --add_lib=<file>    Read additional symbols and line info from the given library\n   --lib_prefix=<dir>  Comma separated list of library path prefixes\n\nReporting Granularity:\n   --addresses         Report at address level\n   --lines             Report at source line level\n   --functions         Report at function level [default]\n   --files             Report at source file level\n\nOutput type:\n   --text              Generate text report\n   --callgrind         Generate callgrind format to stdout\n   --gv                Generate Postscript and display\n   --evince            Generate PDF and display\n   --web               Generate SVG and display\n   --list=<regexp>     Generate source listing of matching routines\n   --disasm=<regexp>   Generate disassembly of matching routines\n   --symbols           Print demangled symbol names found at given addresses\n   --dot               Generate DOT file to stdout\n   --ps                Generate Postcript to stdout\n   --pdf               Generate PDF to stdout\n   --svg               Generate SVG to stdout\n   --gif               Generate GIF to stdout\n   --raw               Generate symbolized jeprof data (useful with remote fetch)\n\nHeap-Profile Options:\n   --inuse_space       Display in-use (mega)bytes [default]\n   --inuse_objects     Display in-use objects\n   --alloc_space       Display allocated (mega)bytes\n   --alloc_objects     Display allocated objects\n   --show_bytes        Display space in bytes\n   --drop_negative     Ignore negative differences\n\nContention-profile options:\n   --total_delay       Display total delay at each region [default]\n   --contentions       Display number of delays at each region\n   --mean_delay        Display mean delay at each region\n\nCall-graph Options:\n   --nodecount=<n>     Show at most so many nodes [default=80]\n   --nodefraction=<f>  Hide nodes below <f>*total [default=.005]\n   --edgefraction=<f>  Hide edges below <f>*total [default=.001]\n   --maxdegree=<n>     Max incoming/outgoing edges per node [default=8]\n   --focus=<regexp>    Focus on backtraces with nodes matching <regexp>\n   --thread=<n>        Show profile for thread <n>\n   --ignore=<regexp>   Ignore backtraces with nodes matching <regexp>\n   --scale=<n>         Set GV scaling [default=0]\n   --heapcheck         Make nodes with non-0 object counts\n                       (i.e. direct leak generators) more visible\n   --retain=<regexp>   Retain only nodes that match <regexp>\n   --exclude=<regexp>  Exclude all nodes that match <regexp>\n\nMiscellaneous:\n   --tools=<prefix or binary:fullpath>[,...]   \\$PATH for object tool pathnames\n   --test              Run unit tests\n   --help              This message\n   --version           Version information\n\nEnvironment Variables:\n   JEPROF_TMPDIR        Profiles directory. Defaults to \\$HOME/jeprof\n   JEPROF_TOOLS         Prefix for object tools pathnames\n\nExamples:\n\njeprof /bin/ls ls.prof\n                       Enters \"interactive\" mode\njeprof --text /bin/ls ls.prof\n                       Outputs one line per procedure\njeprof --web /bin/ls ls.prof\n                       Displays annotated call-graph in web browser\njeprof --gv /bin/ls ls.prof\n                       Displays annotated call-graph via 'gv'\njeprof --gv --focus=Mutex /bin/ls ls.prof\n                       Restricts to code paths including a .*Mutex.* entry\njeprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof\n                       Code paths including Mutex but not string\njeprof --list=getdir /bin/ls ls.prof\n                       (Per-line) annotated source listing for getdir()\njeprof --disasm=getdir /bin/ls ls.prof\n                       (Per-PC) annotated disassembly for getdir()\n\njeprof http://localhost:1234/\n                       Enters \"interactive\" mode\njeprof --text localhost:1234\n                       Outputs one line per procedure for localhost:1234\njeprof --raw localhost:1234 > ./local.raw\njeprof --text ./local.raw\n                       Fetches a remote profile for later analysis and then\n                       analyzes it in text mode.\nEOF\n}\n\nsub version_string {\n  return <<EOF\njeprof (part of jemalloc $JEPROF_VERSION)\nbased on pprof (part of gperftools $PPROF_VERSION)\n\nCopyright 1998-2007 Google Inc.\n\nThis is BSD licensed software; see the source for copying conditions\nand license information.\nThere is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\nPARTICULAR PURPOSE.\nEOF\n}\n\nsub usage {\n  my $msg = shift;\n  print STDERR \"$msg\\n\\n\";\n  print STDERR usage_string();\n  print STDERR \"\\nFATAL ERROR: $msg\\n\";    # just as a reminder\n  exit(1);\n}\n\nsub Init() {\n  # Setup tmp-file name and handler to clean it up.\n  # We do this in the very beginning so that we can use\n  # error() and cleanup() function anytime here after.\n  $main::tmpfile_sym = \"/tmp/jeprof$$.sym\";\n  $main::tmpfile_ps = \"/tmp/jeprof$$\";\n  $main::next_tmpfile = 0;\n  $SIG{'INT'} = \\&sighandler;\n\n  # Cache from filename/linenumber to source code\n  $main::source_cache = ();\n\n  $main::opt_help = 0;\n  $main::opt_version = 0;\n\n  $main::opt_cum = 0;\n  $main::opt_base = '';\n  $main::opt_addresses = 0;\n  $main::opt_lines = 0;\n  $main::opt_functions = 0;\n  $main::opt_files = 0;\n  $main::opt_lib_prefix = \"\";\n\n  $main::opt_text = 0;\n  $main::opt_callgrind = 0;\n  $main::opt_list = \"\";\n  $main::opt_disasm = \"\";\n  $main::opt_symbols = 0;\n  $main::opt_gv = 0;\n  $main::opt_evince = 0;\n  $main::opt_web = 0;\n  $main::opt_dot = 0;\n  $main::opt_ps = 0;\n  $main::opt_pdf = 0;\n  $main::opt_gif = 0;\n  $main::opt_svg = 0;\n  $main::opt_raw = 0;\n\n  $main::opt_nodecount = 80;\n  $main::opt_nodefraction = 0.005;\n  $main::opt_edgefraction = 0.001;\n  $main::opt_maxdegree = 8;\n  $main::opt_focus = '';\n  $main::opt_thread = undef;\n  $main::opt_ignore = '';\n  $main::opt_scale = 0;\n  $main::opt_heapcheck = 0;\n  $main::opt_retain = '';\n  $main::opt_exclude = '';\n  $main::opt_seconds = 30;\n  $main::opt_lib = \"\";\n\n  $main::opt_inuse_space   = 0;\n  $main::opt_inuse_objects = 0;\n  $main::opt_alloc_space   = 0;\n  $main::opt_alloc_objects = 0;\n  $main::opt_show_bytes    = 0;\n  $main::opt_drop_negative = 0;\n  $main::opt_interactive   = 0;\n\n  $main::opt_total_delay = 0;\n  $main::opt_contentions = 0;\n  $main::opt_mean_delay = 0;\n\n  $main::opt_tools   = \"\";\n  $main::opt_debug   = 0;\n  $main::opt_test    = 0;\n\n  # These are undocumented flags used only by unittests.\n  $main::opt_test_stride = 0;\n\n  # Are we using $SYMBOL_PAGE?\n  $main::use_symbol_page = 0;\n\n  # Files returned by TempName.\n  %main::tempnames = ();\n\n  # Type of profile we are dealing with\n  # Supported types:\n  #     cpu\n  #     heap\n  #     growth\n  #     contention\n  $main::profile_type = '';     # Empty type means \"unknown\"\n\n  GetOptions(\"help!\"          => \\$main::opt_help,\n             \"version!\"       => \\$main::opt_version,\n             \"cum!\"           => \\$main::opt_cum,\n             \"base=s\"         => \\$main::opt_base,\n             \"seconds=i\"      => \\$main::opt_seconds,\n             \"add_lib=s\"      => \\$main::opt_lib,\n             \"lib_prefix=s\"   => \\$main::opt_lib_prefix,\n             \"functions!\"     => \\$main::opt_functions,\n             \"lines!\"         => \\$main::opt_lines,\n             \"addresses!\"     => \\$main::opt_addresses,\n             \"files!\"         => \\$main::opt_files,\n             \"text!\"          => \\$main::opt_text,\n             \"callgrind!\"     => \\$main::opt_callgrind,\n             \"list=s\"         => \\$main::opt_list,\n             \"disasm=s\"       => \\$main::opt_disasm,\n             \"symbols!\"       => \\$main::opt_symbols,\n             \"gv!\"            => \\$main::opt_gv,\n             \"evince!\"        => \\$main::opt_evince,\n             \"web!\"           => \\$main::opt_web,\n             \"dot!\"           => \\$main::opt_dot,\n             \"ps!\"            => \\$main::opt_ps,\n             \"pdf!\"           => \\$main::opt_pdf,\n             \"svg!\"           => \\$main::opt_svg,\n             \"gif!\"           => \\$main::opt_gif,\n             \"raw!\"           => \\$main::opt_raw,\n             \"interactive!\"   => \\$main::opt_interactive,\n             \"nodecount=i\"    => \\$main::opt_nodecount,\n             \"nodefraction=f\" => \\$main::opt_nodefraction,\n             \"edgefraction=f\" => \\$main::opt_edgefraction,\n             \"maxdegree=i\"    => \\$main::opt_maxdegree,\n             \"focus=s\"        => \\$main::opt_focus,\n             \"thread=s\"       => \\$main::opt_thread,\n             \"ignore=s\"       => \\$main::opt_ignore,\n             \"scale=i\"        => \\$main::opt_scale,\n             \"heapcheck\"      => \\$main::opt_heapcheck,\n             \"retain=s\"       => \\$main::opt_retain,\n             \"exclude=s\"      => \\$main::opt_exclude,\n             \"inuse_space!\"   => \\$main::opt_inuse_space,\n             \"inuse_objects!\" => \\$main::opt_inuse_objects,\n             \"alloc_space!\"   => \\$main::opt_alloc_space,\n             \"alloc_objects!\" => \\$main::opt_alloc_objects,\n             \"show_bytes!\"    => \\$main::opt_show_bytes,\n             \"drop_negative!\" => \\$main::opt_drop_negative,\n             \"total_delay!\"   => \\$main::opt_total_delay,\n             \"contentions!\"   => \\$main::opt_contentions,\n             \"mean_delay!\"    => \\$main::opt_mean_delay,\n             \"tools=s\"        => \\$main::opt_tools,\n             \"test!\"          => \\$main::opt_test,\n             \"debug!\"         => \\$main::opt_debug,\n             # Undocumented flags used only by unittests:\n             \"test_stride=i\"  => \\$main::opt_test_stride,\n      ) || usage(\"Invalid option(s)\");\n\n  # Deal with the standard --help and --version\n  if ($main::opt_help) {\n    print usage_string();\n    exit(0);\n  }\n\n  if ($main::opt_version) {\n    print version_string();\n    exit(0);\n  }\n\n  # Disassembly/listing/symbols mode requires address-level info\n  if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {\n    $main::opt_functions = 0;\n    $main::opt_lines = 0;\n    $main::opt_addresses = 1;\n    $main::opt_files = 0;\n  }\n\n  # Check heap-profiling flags\n  if ($main::opt_inuse_space +\n      $main::opt_inuse_objects +\n      $main::opt_alloc_space +\n      $main::opt_alloc_objects > 1) {\n    usage(\"Specify at most on of --inuse/--alloc options\");\n  }\n\n  # Check output granularities\n  my $grains =\n      $main::opt_functions +\n      $main::opt_lines +\n      $main::opt_addresses +\n      $main::opt_files +\n      0;\n  if ($grains > 1) {\n    usage(\"Only specify one output granularity option\");\n  }\n  if ($grains == 0) {\n    $main::opt_functions = 1;\n  }\n\n  # Check output modes\n  my $modes =\n      $main::opt_text +\n      $main::opt_callgrind +\n      ($main::opt_list eq '' ? 0 : 1) +\n      ($main::opt_disasm eq '' ? 0 : 1) +\n      ($main::opt_symbols == 0 ? 0 : 1) +\n      $main::opt_gv +\n      $main::opt_evince +\n      $main::opt_web +\n      $main::opt_dot +\n      $main::opt_ps +\n      $main::opt_pdf +\n      $main::opt_svg +\n      $main::opt_gif +\n      $main::opt_raw +\n      $main::opt_interactive +\n      0;\n  if ($modes > 1) {\n    usage(\"Only specify one output mode\");\n  }\n  if ($modes == 0) {\n    if (-t STDOUT) {  # If STDOUT is a tty, activate interactive mode\n      $main::opt_interactive = 1;\n    } else {\n      $main::opt_text = 1;\n    }\n  }\n\n  if ($main::opt_test) {\n    RunUnitTests();\n    # Should not return\n    exit(1);\n  }\n\n  # Binary name and profile arguments list\n  $main::prog = \"\";\n  @main::pfile_args = ();\n\n  # Remote profiling without a binary (using $SYMBOL_PAGE instead)\n  if (@ARGV > 0) {\n    if (IsProfileURL($ARGV[0])) {\n      $main::use_symbol_page = 1;\n    } elsif (IsSymbolizedProfileFile($ARGV[0])) {\n      $main::use_symbolized_profile = 1;\n      $main::prog = $UNKNOWN_BINARY;  # will be set later from the profile file\n    }\n  }\n\n  if ($main::use_symbol_page || $main::use_symbolized_profile) {\n    # We don't need a binary!\n    my %disabled = ('--lines' => $main::opt_lines,\n                    '--disasm' => $main::opt_disasm);\n    for my $option (keys %disabled) {\n      usage(\"$option cannot be used without a binary\") if $disabled{$option};\n    }\n    # Set $main::prog later...\n    scalar(@ARGV) || usage(\"Did not specify profile file\");\n  } elsif ($main::opt_symbols) {\n    # --symbols needs a binary-name (to run nm on, etc) but not profiles\n    $main::prog = shift(@ARGV) || usage(\"Did not specify program\");\n  } else {\n    $main::prog = shift(@ARGV) || usage(\"Did not specify program\");\n    scalar(@ARGV) || usage(\"Did not specify profile file\");\n  }\n\n  # Parse profile file/location arguments\n  foreach my $farg (@ARGV) {\n    if ($farg =~ m/(.*)\\@([0-9]+)(|\\/.*)$/ ) {\n      my $machine = $1;\n      my $num_machines = $2;\n      my $path = $3;\n      for (my $i = 0; $i < $num_machines; $i++) {\n        unshift(@main::pfile_args, \"$i.$machine$path\");\n      }\n    } else {\n      unshift(@main::pfile_args, $farg);\n    }\n  }\n\n  if ($main::use_symbol_page) {\n    unless (IsProfileURL($main::pfile_args[0])) {\n      error(\"The first profile should be a remote form to use $SYMBOL_PAGE\\n\");\n    }\n    CheckSymbolPage();\n    $main::prog = FetchProgramName();\n  } elsif (!$main::use_symbolized_profile) {  # may not need objtools!\n    ConfigureObjTools($main::prog)\n  }\n\n  # Break the opt_lib_prefix into the prefix_list array\n  @prefix_list = split (',', $main::opt_lib_prefix);\n\n  # Remove trailing / from the prefixes, in the list to prevent\n  # searching things like /my/path//lib/mylib.so\n  foreach (@prefix_list) {\n    s|/+$||;\n  }\n}\n\nsub FilterAndPrint {\n  my ($profile, $symbols, $libs, $thread) = @_;\n\n  # Get total data in profile\n  my $total = TotalProfile($profile);\n\n  # Remove uniniteresting stack items\n  $profile = RemoveUninterestingFrames($symbols, $profile);\n\n  # Focus?\n  if ($main::opt_focus ne '') {\n    $profile = FocusProfile($symbols, $profile, $main::opt_focus);\n  }\n\n  # Ignore?\n  if ($main::opt_ignore ne '') {\n    $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);\n  }\n\n  my $calls = ExtractCalls($symbols, $profile);\n\n  # Reduce profiles to required output granularity, and also clean\n  # each stack trace so a given entry exists at most once.\n  my $reduced = ReduceProfile($symbols, $profile);\n\n  # Get derived profiles\n  my $flat = FlatProfile($reduced);\n  my $cumulative = CumulativeProfile($reduced);\n\n  # Print\n  if (!$main::opt_interactive) {\n    if ($main::opt_disasm) {\n      PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);\n    } elsif ($main::opt_list) {\n      PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);\n    } elsif ($main::opt_text) {\n      # Make sure the output is empty when have nothing to report\n      # (only matters when --heapcheck is given but we must be\n      # compatible with old branches that did not pass --heapcheck always):\n      if ($total != 0) {\n        printf(\"Total%s: %s %s\\n\",\n               (defined($thread) ? \" (t$thread)\" : \"\"),\n               Unparse($total), Units());\n      }\n      PrintText($symbols, $flat, $cumulative, -1);\n    } elsif ($main::opt_raw) {\n      PrintSymbolizedProfile($symbols, $profile, $main::prog);\n    } elsif ($main::opt_callgrind) {\n      PrintCallgrind($calls);\n    } else {\n      if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {\n        if ($main::opt_gv) {\n          RunGV(TempName($main::next_tmpfile, \"ps\"), \"\");\n        } elsif ($main::opt_evince) {\n          RunEvince(TempName($main::next_tmpfile, \"pdf\"), \"\");\n        } elsif ($main::opt_web) {\n          my $tmp = TempName($main::next_tmpfile, \"svg\");\n          RunWeb($tmp);\n          # The command we run might hand the file name off\n          # to an already running browser instance and then exit.\n          # Normally, we'd remove $tmp on exit (right now),\n          # but fork a child to remove $tmp a little later, so that the\n          # browser has time to load it first.\n          delete $main::tempnames{$tmp};\n          if (fork() == 0) {\n            sleep 5;\n            unlink($tmp);\n            exit(0);\n          }\n        }\n      } else {\n        cleanup();\n        exit(1);\n      }\n    }\n  } else {\n    InteractiveMode($profile, $symbols, $libs, $total);\n  }\n}\n\nsub Main() {\n  Init();\n  $main::collected_profile = undef;\n  @main::profile_files = ();\n  $main::op_time = time();\n\n  # Printing symbols is special and requires a lot less info that most.\n  if ($main::opt_symbols) {\n    PrintSymbols(*STDIN);   # Get /proc/maps and symbols output from stdin\n    return;\n  }\n\n  # Fetch all profile data\n  FetchDynamicProfiles();\n\n  # this will hold symbols that we read from the profile files\n  my $symbol_map = {};\n\n  # Read one profile, pick the last item on the list\n  my $data = ReadProfile($main::prog, pop(@main::profile_files));\n  my $profile = $data->{profile};\n  my $pcs = $data->{pcs};\n  my $libs = $data->{libs};   # Info about main program and shared libraries\n  $symbol_map = MergeSymbols($symbol_map, $data->{symbols});\n\n  # Add additional profiles, if available.\n  if (scalar(@main::profile_files) > 0) {\n    foreach my $pname (@main::profile_files) {\n      my $data2 = ReadProfile($main::prog, $pname);\n      $profile = AddProfile($profile, $data2->{profile});\n      $pcs = AddPcs($pcs, $data2->{pcs});\n      $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});\n    }\n  }\n\n  # Subtract base from profile, if specified\n  if ($main::opt_base ne '') {\n    my $base = ReadProfile($main::prog, $main::opt_base);\n    $profile = SubtractProfile($profile, $base->{profile});\n    $pcs = AddPcs($pcs, $base->{pcs});\n    $symbol_map = MergeSymbols($symbol_map, $base->{symbols});\n  }\n\n  # Collect symbols\n  my $symbols;\n  if ($main::use_symbolized_profile) {\n    $symbols = FetchSymbols($pcs, $symbol_map);\n  } elsif ($main::use_symbol_page) {\n    $symbols = FetchSymbols($pcs);\n  } else {\n    # TODO(csilvers): $libs uses the /proc/self/maps data from profile1,\n    # which may differ from the data from subsequent profiles, especially\n    # if they were run on different machines.  Use appropriate libs for\n    # each pc somehow.\n    $symbols = ExtractSymbols($libs, $pcs);\n  }\n\n  if (!defined($main::opt_thread)) {\n    FilterAndPrint($profile, $symbols, $libs);\n  }\n  if (defined($data->{threads})) {\n    foreach my $thread (sort { $a <=> $b } keys(%{$data->{threads}})) {\n      if (defined($main::opt_thread) &&\n          ($main::opt_thread eq '*' || $main::opt_thread == $thread)) {\n        my $thread_profile = $data->{threads}{$thread};\n        FilterAndPrint($thread_profile, $symbols, $libs, $thread);\n      }\n    }\n  }\n\n  cleanup();\n  exit(0);\n}\n\n##### Entry Point #####\n\nMain();\n\n# Temporary code to detect if we're running on a Goobuntu system.\n# These systems don't have the right stuff installed for the special\n# Readline libraries to work, so as a temporary workaround, we default\n# to using the normal stdio code, rather than the fancier readline-based\n# code\nsub ReadlineMightFail {\n  if (-e '/lib/libtermcap.so.2') {\n    return 0;  # libtermcap exists, so readline should be okay\n  } else {\n    return 1;\n  }\n}\n\nsub RunGV {\n  my $fname = shift;\n  my $bg = shift;       # \"\" or \" &\" if we should run in background\n  if (!system(ShellEscape(@GV, \"--version\") . \" >$dev_null 2>&1\")) {\n    # Options using double dash are supported by this gv version.\n    # Also, turn on noantialias to better handle bug in gv for\n    # postscript files with large dimensions.\n    # TODO: Maybe we should not pass the --noantialias flag\n    # if the gv version is known to work properly without the flag.\n    system(ShellEscape(@GV, \"--scale=$main::opt_scale\", \"--noantialias\", $fname)\n           . $bg);\n  } else {\n    # Old gv version - only supports options that use single dash.\n    print STDERR ShellEscape(@GV, \"-scale\", $main::opt_scale) . \"\\n\";\n    system(ShellEscape(@GV, \"-scale\", \"$main::opt_scale\", $fname) . $bg);\n  }\n}\n\nsub RunEvince {\n  my $fname = shift;\n  my $bg = shift;       # \"\" or \" &\" if we should run in background\n  system(ShellEscape(@EVINCE, $fname) . $bg);\n}\n\nsub RunWeb {\n  my $fname = shift;\n  print STDERR \"Loading web page file:///$fname\\n\";\n\n  if (`uname` =~ /Darwin/) {\n    # OS X: open will use standard preference for SVG files.\n    system(\"/usr/bin/open\", $fname);\n    return;\n  }\n\n  # Some kind of Unix; try generic symlinks, then specific browsers.\n  # (Stop once we find one.)\n  # Works best if the browser is already running.\n  my @alt = (\n    \"/etc/alternatives/gnome-www-browser\",\n    \"/etc/alternatives/x-www-browser\",\n    \"google-chrome\",\n    \"firefox\",\n  );\n  foreach my $b (@alt) {\n    if (system($b, $fname) == 0) {\n      return;\n    }\n  }\n\n  print STDERR \"Could not load web browser.\\n\";\n}\n\nsub RunKcachegrind {\n  my $fname = shift;\n  my $bg = shift;       # \"\" or \" &\" if we should run in background\n  print STDERR \"Starting '@KCACHEGRIND \" . $fname . $bg . \"'\\n\";\n  system(ShellEscape(@KCACHEGRIND, $fname) . $bg);\n}\n\n\n##### Interactive helper routines #####\n\nsub InteractiveMode {\n  $| = 1;  # Make output unbuffered for interactive mode\n  my ($orig_profile, $symbols, $libs, $total) = @_;\n\n  print STDERR \"Welcome to jeprof!  For help, type 'help'.\\n\";\n\n  # Use ReadLine if it's installed and input comes from a console.\n  if ( -t STDIN &&\n       !ReadlineMightFail() &&\n       defined(eval {require Term::ReadLine}) ) {\n    my $term = new Term::ReadLine 'jeprof';\n    while ( defined ($_ = $term->readline('(jeprof) '))) {\n      $term->addhistory($_) if /\\S/;\n      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {\n        last;    # exit when we get an interactive command to quit\n      }\n    }\n  } else {       # don't have readline\n    while (1) {\n      print STDERR \"(jeprof) \";\n      $_ = <STDIN>;\n      last if ! defined $_ ;\n      s/\\r//g;         # turn windows-looking lines into unix-looking lines\n\n      # Save some flags that might be reset by InteractiveCommand()\n      my $save_opt_lines = $main::opt_lines;\n\n      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {\n        last;    # exit when we get an interactive command to quit\n      }\n\n      # Restore flags\n      $main::opt_lines = $save_opt_lines;\n    }\n  }\n}\n\n# Takes two args: orig profile, and command to run.\n# Returns 1 if we should keep going, or 0 if we were asked to quit\nsub InteractiveCommand {\n  my($orig_profile, $symbols, $libs, $total, $command) = @_;\n  $_ = $command;                # just to make future m//'s easier\n  if (!defined($_)) {\n    print STDERR \"\\n\";\n    return 0;\n  }\n  if (m/^\\s*quit/) {\n    return 0;\n  }\n  if (m/^\\s*help/) {\n    InteractiveHelpMessage();\n    return 1;\n  }\n  # Clear all the mode options -- mode is controlled by \"$command\"\n  $main::opt_text = 0;\n  $main::opt_callgrind = 0;\n  $main::opt_disasm = 0;\n  $main::opt_list = 0;\n  $main::opt_gv = 0;\n  $main::opt_evince = 0;\n  $main::opt_cum = 0;\n\n  if (m/^\\s*(text|top)(\\d*)\\s*(.*)/) {\n    $main::opt_text = 1;\n\n    my $line_limit = ($2 ne \"\") ? int($2) : 10;\n\n    my $routine;\n    my $ignore;\n    ($routine, $ignore) = ParseInteractiveArgs($3);\n\n    my $profile = ProcessProfile($total, $orig_profile, $symbols, \"\", $ignore);\n    my $reduced = ReduceProfile($symbols, $profile);\n\n    # Get derived profiles\n    my $flat = FlatProfile($reduced);\n    my $cumulative = CumulativeProfile($reduced);\n\n    PrintText($symbols, $flat, $cumulative, $line_limit);\n    return 1;\n  }\n  if (m/^\\s*callgrind\\s*([^ \\n]*)/) {\n    $main::opt_callgrind = 1;\n\n    # Get derived profiles\n    my $calls = ExtractCalls($symbols, $orig_profile);\n    my $filename = $1;\n    if ( $1 eq '' ) {\n      $filename = TempName($main::next_tmpfile, \"callgrind\");\n    }\n    PrintCallgrind($calls, $filename);\n    if ( $1 eq '' ) {\n      RunKcachegrind($filename, \" & \");\n      $main::next_tmpfile++;\n    }\n\n    return 1;\n  }\n  if (m/^\\s*(web)?list\\s*(.+)/) {\n    my $html = (defined($1) && ($1 eq \"web\"));\n    $main::opt_list = 1;\n\n    my $routine;\n    my $ignore;\n    ($routine, $ignore) = ParseInteractiveArgs($2);\n\n    my $profile = ProcessProfile($total, $orig_profile, $symbols, \"\", $ignore);\n    my $reduced = ReduceProfile($symbols, $profile);\n\n    # Get derived profiles\n    my $flat = FlatProfile($reduced);\n    my $cumulative = CumulativeProfile($reduced);\n\n    PrintListing($total, $libs, $flat, $cumulative, $routine, $html);\n    return 1;\n  }\n  if (m/^\\s*disasm\\s*(.+)/) {\n    $main::opt_disasm = 1;\n\n    my $routine;\n    my $ignore;\n    ($routine, $ignore) = ParseInteractiveArgs($1);\n\n    # Process current profile to account for various settings\n    my $profile = ProcessProfile($total, $orig_profile, $symbols, \"\", $ignore);\n    my $reduced = ReduceProfile($symbols, $profile);\n\n    # Get derived profiles\n    my $flat = FlatProfile($reduced);\n    my $cumulative = CumulativeProfile($reduced);\n\n    PrintDisassembly($libs, $flat, $cumulative, $routine);\n    return 1;\n  }\n  if (m/^\\s*(gv|web|evince)\\s*(.*)/) {\n    $main::opt_gv = 0;\n    $main::opt_evince = 0;\n    $main::opt_web = 0;\n    if ($1 eq \"gv\") {\n      $main::opt_gv = 1;\n    } elsif ($1 eq \"evince\") {\n      $main::opt_evince = 1;\n    } elsif ($1 eq \"web\") {\n      $main::opt_web = 1;\n    }\n\n    my $focus;\n    my $ignore;\n    ($focus, $ignore) = ParseInteractiveArgs($2);\n\n    # Process current profile to account for various settings\n    my $profile = ProcessProfile($total, $orig_profile, $symbols,\n                                 $focus, $ignore);\n    my $reduced = ReduceProfile($symbols, $profile);\n\n    # Get derived profiles\n    my $flat = FlatProfile($reduced);\n    my $cumulative = CumulativeProfile($reduced);\n\n    if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {\n      if ($main::opt_gv) {\n        RunGV(TempName($main::next_tmpfile, \"ps\"), \" &\");\n      } elsif ($main::opt_evince) {\n        RunEvince(TempName($main::next_tmpfile, \"pdf\"), \" &\");\n      } elsif ($main::opt_web) {\n        RunWeb(TempName($main::next_tmpfile, \"svg\"));\n      }\n      $main::next_tmpfile++;\n    }\n    return 1;\n  }\n  if (m/^\\s*$/) {\n    return 1;\n  }\n  print STDERR \"Unknown command: try 'help'.\\n\";\n  return 1;\n}\n\n\nsub ProcessProfile {\n  my $total_count = shift;\n  my $orig_profile = shift;\n  my $symbols = shift;\n  my $focus = shift;\n  my $ignore = shift;\n\n  # Process current profile to account for various settings\n  my $profile = $orig_profile;\n  printf(\"Total: %s %s\\n\", Unparse($total_count), Units());\n  if ($focus ne '') {\n    $profile = FocusProfile($symbols, $profile, $focus);\n    my $focus_count = TotalProfile($profile);\n    printf(\"After focusing on '%s': %s %s of %s (%0.1f%%)\\n\",\n           $focus,\n           Unparse($focus_count), Units(),\n           Unparse($total_count), ($focus_count*100.0) / $total_count);\n  }\n  if ($ignore ne '') {\n    $profile = IgnoreProfile($symbols, $profile, $ignore);\n    my $ignore_count = TotalProfile($profile);\n    printf(\"After ignoring '%s': %s %s of %s (%0.1f%%)\\n\",\n           $ignore,\n           Unparse($ignore_count), Units(),\n           Unparse($total_count),\n           ($ignore_count*100.0) / $total_count);\n  }\n\n  return $profile;\n}\n\nsub InteractiveHelpMessage {\n  print STDERR <<ENDOFHELP;\nInteractive jeprof mode\n\nCommands:\n  gv\n  gv [focus] [-ignore1] [-ignore2]\n      Show graphical hierarchical display of current profile.  Without\n      any arguments, shows all samples in the profile.  With the optional\n      \"focus\" argument, restricts the samples shown to just those where\n      the \"focus\" regular expression matches a routine name on the stack\n      trace.\n\n  web\n  web [focus] [-ignore1] [-ignore2]\n      Like GV, but displays profile in your web browser instead of using\n      Ghostview. Works best if your web browser is already running.\n      To change the browser that gets used:\n      On Linux, set the /etc/alternatives/gnome-www-browser symlink.\n      On OS X, change the Finder association for SVG files.\n\n  list [routine_regexp] [-ignore1] [-ignore2]\n      Show source listing of routines whose names match \"routine_regexp\"\n\n  weblist [routine_regexp] [-ignore1] [-ignore2]\n     Displays a source listing of routines whose names match \"routine_regexp\"\n     in a web browser.  You can click on source lines to view the\n     corresponding disassembly.\n\n  top [--cum] [-ignore1] [-ignore2]\n  top20 [--cum] [-ignore1] [-ignore2]\n  top37 [--cum] [-ignore1] [-ignore2]\n      Show top lines ordered by flat profile count, or cumulative count\n      if --cum is specified.  If a number is present after 'top', the\n      top K routines will be shown (defaults to showing the top 10)\n\n  disasm [routine_regexp] [-ignore1] [-ignore2]\n      Show disassembly of routines whose names match \"routine_regexp\",\n      annotated with sample counts.\n\n  callgrind\n  callgrind [filename]\n      Generates callgrind file. If no filename is given, kcachegrind is called.\n\n  help - This listing\n  quit or ^D - End jeprof\n\nFor commands that accept optional -ignore tags, samples where any routine in\nthe stack trace matches the regular expression in any of the -ignore\nparameters will be ignored.\n\nFurther pprof details are available at this location (or one similar):\n\n /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html\n /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html\n\nENDOFHELP\n}\nsub ParseInteractiveArgs {\n  my $args = shift;\n  my $focus = \"\";\n  my $ignore = \"\";\n  my @x = split(/ +/, $args);\n  foreach $a (@x) {\n    if ($a =~ m/^(--|-)lines$/) {\n      $main::opt_lines = 1;\n    } elsif ($a =~ m/^(--|-)cum$/) {\n      $main::opt_cum = 1;\n    } elsif ($a =~ m/^-(.*)/) {\n      $ignore .= (($ignore ne \"\") ? \"|\" : \"\" ) . $1;\n    } else {\n      $focus .= (($focus ne \"\") ? \"|\" : \"\" ) . $a;\n    }\n  }\n  if ($ignore ne \"\") {\n    print STDERR \"Ignoring samples in call stacks that match '$ignore'\\n\";\n  }\n  return ($focus, $ignore);\n}\n\n##### Output code #####\n\nsub TempName {\n  my $fnum = shift;\n  my $ext = shift;\n  my $file = \"$main::tmpfile_ps.$fnum.$ext\";\n  $main::tempnames{$file} = 1;\n  return $file;\n}\n\n# Print profile data in packed binary format (64-bit) to standard out\nsub PrintProfileData {\n  my $profile = shift;\n\n  # print header (64-bit style)\n  # (zero) (header-size) (version) (sample-period) (zero)\n  print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);\n\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    if ($#addrs >= 0) {\n      my $depth = $#addrs + 1;\n      # int(foo / 2**32) is the only reliable way to get rid of bottom\n      # 32 bits on both 32- and 64-bit systems.\n      print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));\n      print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));\n\n      foreach my $full_addr (@addrs) {\n        my $addr = $full_addr;\n        $addr =~ s/0x0*//;  # strip off leading 0x, zeroes\n        if (length($addr) > 16) {\n          print STDERR \"Invalid address in profile: $full_addr\\n\";\n          next;\n        }\n        my $low_addr = substr($addr, -8);       # get last 8 hex chars\n        my $high_addr = substr($addr, -16, 8);  # get up to 8 more hex chars\n        print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));\n      }\n    }\n  }\n}\n\n# Print symbols and profile data\nsub PrintSymbolizedProfile {\n  my $symbols = shift;\n  my $profile = shift;\n  my $prog = shift;\n\n  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $symbol_marker = $&;\n\n  print '--- ', $symbol_marker, \"\\n\";\n  if (defined($prog)) {\n    print 'binary=', $prog, \"\\n\";\n  }\n  while (my ($pc, $name) = each(%{$symbols})) {\n    my $sep = ' ';\n    print '0x', $pc;\n    # We have a list of function names, which include the inlined\n    # calls.  They are separated (and terminated) by --, which is\n    # illegal in function names.\n    for (my $j = 2; $j <= $#{$name}; $j += 3) {\n      print $sep, $name->[$j];\n      $sep = '--';\n    }\n    print \"\\n\";\n  }\n  print '---', \"\\n\";\n\n  my $profile_marker;\n  if ($main::profile_type eq 'heap') {\n    $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  } elsif ($main::profile_type eq 'growth') {\n    $GROWTH_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  } elsif ($main::profile_type eq 'contention') {\n    $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  } else { # elsif ($main::profile_type eq 'cpu')\n    $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n    $profile_marker = $&;\n  }\n\n  print '--- ', $profile_marker, \"\\n\";\n  if (defined($main::collected_profile)) {\n    # if used with remote fetch, simply dump the collected profile to output.\n    open(SRC, \"<$main::collected_profile\");\n    while (<SRC>) {\n      print $_;\n    }\n    close(SRC);\n  } else {\n    # --raw/http: For everything to work correctly for non-remote profiles, we\n    # would need to extend PrintProfileData() to handle all possible profile\n    # types, re-enable the code that is currently disabled in ReadCPUProfile()\n    # and FixCallerAddresses(), and remove the remote profile dumping code in\n    # the block above.\n    die \"--raw/http: jeprof can only dump remote profiles for --raw\\n\";\n    # dump a cpu-format profile to standard out\n    PrintProfileData($profile);\n  }\n}\n\n# Print text output\nsub PrintText {\n  my $symbols = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $line_limit = shift;\n\n  my $total = TotalProfile($flat);\n\n  # Which profile to sort by?\n  my $s = $main::opt_cum ? $cumulative : $flat;\n\n  my $running_sum = 0;\n  my $lines = 0;\n  foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }\n                 keys(%{$cumulative})) {\n    my $f = GetEntry($flat, $k);\n    my $c = GetEntry($cumulative, $k);\n    $running_sum += $f;\n\n    my $sym = $k;\n    if (exists($symbols->{$k})) {\n      $sym = $symbols->{$k}->[0] . \" \" . $symbols->{$k}->[1];\n      if ($main::opt_addresses) {\n        $sym = $k . \" \" . $sym;\n      }\n    }\n\n    if ($f != 0 || $c != 0) {\n      printf(\"%8s %6s %6s %8s %6s %s\\n\",\n             Unparse($f),\n             Percent($f, $total),\n             Percent($running_sum, $total),\n             Unparse($c),\n             Percent($c, $total),\n             $sym);\n    }\n    $lines++;\n    last if ($line_limit >= 0 && $lines >= $line_limit);\n  }\n}\n\n# Callgrind format has a compression for repeated function and file\n# names.  You show the name the first time, and just use its number\n# subsequently.  This can cut down the file to about a third or a\n# quarter of its uncompressed size.  $key and $val are the key/value\n# pair that would normally be printed by callgrind; $map is a map from\n# value to number.\nsub CompressedCGName {\n  my($key, $val, $map) = @_;\n  my $idx = $map->{$val};\n  # For very short keys, providing an index hurts rather than helps.\n  if (length($val) <= 3) {\n    return \"$key=$val\\n\";\n  } elsif (defined($idx)) {\n    return \"$key=($idx)\\n\";\n  } else {\n    # scalar(keys $map) gives the number of items in the map.\n    $idx = scalar(keys(%{$map})) + 1;\n    $map->{$val} = $idx;\n    return \"$key=($idx) $val\\n\";\n  }\n}\n\n# Print the call graph in a way that's suiteable for callgrind.\nsub PrintCallgrind {\n  my $calls = shift;\n  my $filename;\n  my %filename_to_index_map;\n  my %fnname_to_index_map;\n\n  if ($main::opt_interactive) {\n    $filename = shift;\n    print STDERR \"Writing callgrind file to '$filename'.\\n\"\n  } else {\n    $filename = \"&STDOUT\";\n  }\n  open(CG, \">$filename\");\n  printf CG (\"events: Hits\\n\\n\");\n  foreach my $call ( map { $_->[0] }\n                     sort { $a->[1] cmp $b ->[1] ||\n                            $a->[2] <=> $b->[2] }\n                     map { /([^:]+):(\\d+):([^ ]+)( -> ([^:]+):(\\d+):(.+))?/;\n                           [$_, $1, $2] }\n                     keys %$calls ) {\n    my $count = int($calls->{$call});\n    $call =~ /([^:]+):(\\d+):([^ ]+)( -> ([^:]+):(\\d+):(.+))?/;\n    my ( $caller_file, $caller_line, $caller_function,\n         $callee_file, $callee_line, $callee_function ) =\n       ( $1, $2, $3, $5, $6, $7 );\n\n    # TODO(csilvers): for better compression, collect all the\n    # caller/callee_files and functions first, before printing\n    # anything, and only compress those referenced more than once.\n    printf CG CompressedCGName(\"fl\", $caller_file, \\%filename_to_index_map);\n    printf CG CompressedCGName(\"fn\", $caller_function, \\%fnname_to_index_map);\n    if (defined $6) {\n      printf CG CompressedCGName(\"cfl\", $callee_file, \\%filename_to_index_map);\n      printf CG CompressedCGName(\"cfn\", $callee_function, \\%fnname_to_index_map);\n      printf CG (\"calls=$count $callee_line\\n\");\n    }\n    printf CG (\"$caller_line $count\\n\\n\");\n  }\n}\n\n# Print disassembly for all all routines that match $main::opt_disasm\nsub PrintDisassembly {\n  my $libs = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $disasm_opts = shift;\n\n  my $total = TotalProfile($flat);\n\n  foreach my $lib (@{$libs}) {\n    my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);\n    my $offset = AddressSub($lib->[1], $lib->[3]);\n    foreach my $routine (sort ByName keys(%{$symbol_table})) {\n      my $start_addr = $symbol_table->{$routine}->[0];\n      my $end_addr = $symbol_table->{$routine}->[1];\n      # See if there are any samples in this routine\n      my $length = hex(AddressSub($end_addr, $start_addr));\n      my $addr = AddressAdd($start_addr, $offset);\n      for (my $i = 0; $i < $length; $i++) {\n        if (defined($cumulative->{$addr})) {\n          PrintDisassembledFunction($lib->[0], $offset,\n                                    $routine, $flat, $cumulative,\n                                    $start_addr, $end_addr, $total);\n          last;\n        }\n        $addr = AddressInc($addr);\n      }\n    }\n  }\n}\n\n# Return reference to array of tuples of the form:\n#       [start_address, filename, linenumber, instruction, limit_address]\n# E.g.,\n#       [\"0x806c43d\", \"/foo/bar.cc\", 131, \"ret\", \"0x806c440\"]\nsub Disassemble {\n  my $prog = shift;\n  my $offset = shift;\n  my $start_addr = shift;\n  my $end_addr = shift;\n\n  my $objdump = $obj_tool_map{\"objdump\"};\n  my $cmd = ShellEscape($objdump, \"-C\", \"-d\", \"-l\", \"--no-show-raw-insn\",\n                        \"--start-address=0x$start_addr\",\n                        \"--stop-address=0x$end_addr\", $prog);\n  open(OBJDUMP, \"$cmd |\") || error(\"$cmd: $!\\n\");\n  my @result = ();\n  my $filename = \"\";\n  my $linenumber = -1;\n  my $last = [\"\", \"\", \"\", \"\"];\n  while (<OBJDUMP>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    chop;\n    if (m|\\s*([^:\\s]+):(\\d+)\\s*$|) {\n      # Location line of the form:\n      #   <filename>:<linenumber>\n      $filename = $1;\n      $linenumber = $2;\n    } elsif (m/^ +([0-9a-f]+):\\s*(.*)/) {\n      # Disassembly line -- zero-extend address to full length\n      my $addr = HexExtend($1);\n      my $k = AddressAdd($addr, $offset);\n      $last->[4] = $k;   # Store ending address for previous instruction\n      $last = [$k, $filename, $linenumber, $2, $end_addr];\n      push(@result, $last);\n    }\n  }\n  close(OBJDUMP);\n  return @result;\n}\n\n# The input file should contain lines of the form /proc/maps-like\n# output (same format as expected from the profiles) or that looks\n# like hex addresses (like \"0xDEADBEEF\").  We will parse all\n# /proc/maps output, and for all the hex addresses, we will output\n# \"short\" symbol names, one per line, in the same order as the input.\nsub PrintSymbols {\n  my $maps_and_symbols_file = shift;\n\n  # ParseLibraries expects pcs to be in a set.  Fine by us...\n  my @pclist = ();   # pcs in sorted order\n  my $pcs = {};\n  my $map = \"\";\n  foreach my $line (<$maps_and_symbols_file>) {\n    $line =~ s/\\r//g;    # turn windows-looking lines into unix-looking lines\n    if ($line =~ /\\b(0x[0-9a-f]+)\\b/i) {\n      push(@pclist, HexExtend($1));\n      $pcs->{$pclist[-1]} = 1;\n    } else {\n      $map .= $line;\n    }\n  }\n\n  my $libs = ParseLibraries($main::prog, $map, $pcs);\n  my $symbols = ExtractSymbols($libs, $pcs);\n\n  foreach my $pc (@pclist) {\n    # ->[0] is the shortname, ->[2] is the full name\n    print(($symbols->{$pc}->[0] || \"??\") . \"\\n\");\n  }\n}\n\n\n# For sorting functions by name\nsub ByName {\n  return ShortFunctionName($a) cmp ShortFunctionName($b);\n}\n\n# Print source-listing for all all routines that match $list_opts\nsub PrintListing {\n  my $total = shift;\n  my $libs = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $list_opts = shift;\n  my $html = shift;\n\n  my $output = \\*STDOUT;\n  my $fname = \"\";\n\n  if ($html) {\n    # Arrange to write the output to a temporary file\n    $fname = TempName($main::next_tmpfile, \"html\");\n    $main::next_tmpfile++;\n    if (!open(TEMP, \">$fname\")) {\n      print STDERR \"$fname: $!\\n\";\n      return;\n    }\n    $output = \\*TEMP;\n    print $output HtmlListingHeader();\n    printf $output (\"<div class=\\\"legend\\\">%s<br>Total: %s %s</div>\\n\",\n                    $main::prog, Unparse($total), Units());\n  }\n\n  my $listed = 0;\n  foreach my $lib (@{$libs}) {\n    my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);\n    my $offset = AddressSub($lib->[1], $lib->[3]);\n    foreach my $routine (sort ByName keys(%{$symbol_table})) {\n      # Print if there are any samples in this routine\n      my $start_addr = $symbol_table->{$routine}->[0];\n      my $end_addr = $symbol_table->{$routine}->[1];\n      my $length = hex(AddressSub($end_addr, $start_addr));\n      my $addr = AddressAdd($start_addr, $offset);\n      for (my $i = 0; $i < $length; $i++) {\n        if (defined($cumulative->{$addr})) {\n          $listed += PrintSource(\n            $lib->[0], $offset,\n            $routine, $flat, $cumulative,\n            $start_addr, $end_addr,\n            $html,\n            $output);\n          last;\n        }\n        $addr = AddressInc($addr);\n      }\n    }\n  }\n\n  if ($html) {\n    if ($listed > 0) {\n      print $output HtmlListingFooter();\n      close($output);\n      RunWeb($fname);\n    } else {\n      close($output);\n      unlink($fname);\n    }\n  }\n}\n\nsub HtmlListingHeader {\n  return <<'EOF';\n<DOCTYPE html>\n<html>\n<head>\n<title>Pprof listing</title>\n<style type=\"text/css\">\nbody {\n  font-family: sans-serif;\n}\nh1 {\n  font-size: 1.5em;\n  margin-bottom: 4px;\n}\n.legend {\n  font-size: 1.25em;\n}\n.line {\n  color: #aaaaaa;\n}\n.nop {\n  color: #aaaaaa;\n}\n.unimportant {\n  color: #cccccc;\n}\n.disasmloc {\n  color: #000000;\n}\n.deadsrc {\n  cursor: pointer;\n}\n.deadsrc:hover {\n  background-color: #eeeeee;\n}\n.livesrc {\n  color: #0000ff;\n  cursor: pointer;\n}\n.livesrc:hover {\n  background-color: #eeeeee;\n}\n.asm {\n  color: #008800;\n  display: none;\n}\n</style>\n<script type=\"text/javascript\">\nfunction jeprof_toggle_asm(e) {\n  var target;\n  if (!e) e = window.event;\n  if (e.target) target = e.target;\n  else if (e.srcElement) target = e.srcElement;\n\n  if (target) {\n    var asm = target.nextSibling;\n    if (asm && asm.className == \"asm\") {\n      asm.style.display = (asm.style.display == \"block\" ? \"\" : \"block\");\n      e.preventDefault();\n      return false;\n    }\n  }\n}\n</script>\n</head>\n<body>\nEOF\n}\n\nsub HtmlListingFooter {\n  return <<'EOF';\n</body>\n</html>\nEOF\n}\n\nsub HtmlEscape {\n  my $text = shift;\n  $text =~ s/&/&amp;/g;\n  $text =~ s/</&lt;/g;\n  $text =~ s/>/&gt;/g;\n  return $text;\n}\n\n# Returns the indentation of the line, if it has any non-whitespace\n# characters.  Otherwise, returns -1.\nsub Indentation {\n  my $line = shift;\n  if (m/^(\\s*)\\S/) {\n    return length($1);\n  } else {\n    return -1;\n  }\n}\n\n# If the symbol table contains inlining info, Disassemble() may tag an\n# instruction with a location inside an inlined function.  But for\n# source listings, we prefer to use the location in the function we\n# are listing.  So use MapToSymbols() to fetch full location\n# information for each instruction and then pick out the first\n# location from a location list (location list contains callers before\n# callees in case of inlining).\n#\n# After this routine has run, each entry in $instructions contains:\n#   [0] start address\n#   [1] filename for function we are listing\n#   [2] line number for function we are listing\n#   [3] disassembly\n#   [4] limit address\n#   [5] most specific filename (may be different from [1] due to inlining)\n#   [6] most specific line number (may be different from [2] due to inlining)\nsub GetTopLevelLineNumbers {\n  my ($lib, $offset, $instructions) = @_;\n  my $pcs = [];\n  for (my $i = 0; $i <= $#{$instructions}; $i++) {\n    push(@{$pcs}, $instructions->[$i]->[0]);\n  }\n  my $symbols = {};\n  MapToSymbols($lib, $offset, $pcs, $symbols);\n  for (my $i = 0; $i <= $#{$instructions}; $i++) {\n    my $e = $instructions->[$i];\n    push(@{$e}, $e->[1]);\n    push(@{$e}, $e->[2]);\n    my $addr = $e->[0];\n    my $sym = $symbols->{$addr};\n    if (defined($sym)) {\n      if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\\d+)$/) {\n        $e->[1] = $1;  # File name\n        $e->[2] = $2;  # Line number\n      }\n    }\n  }\n}\n\n# Print source-listing for one routine\nsub PrintSource {\n  my $prog = shift;\n  my $offset = shift;\n  my $routine = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $start_addr = shift;\n  my $end_addr = shift;\n  my $html = shift;\n  my $output = shift;\n\n  # Disassemble all instructions (just to get line numbers)\n  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);\n  GetTopLevelLineNumbers($prog, $offset, \\@instructions);\n\n  # Hack 1: assume that the first source file encountered in the\n  # disassembly contains the routine\n  my $filename = undef;\n  for (my $i = 0; $i <= $#instructions; $i++) {\n    if ($instructions[$i]->[2] >= 0) {\n      $filename = $instructions[$i]->[1];\n      last;\n    }\n  }\n  if (!defined($filename)) {\n    print STDERR \"no filename found in $routine\\n\";\n    return 0;\n  }\n\n  # Hack 2: assume that the largest line number from $filename is the\n  # end of the procedure.  This is typically safe since if P1 contains\n  # an inlined call to P2, then P2 usually occurs earlier in the\n  # source file.  If this does not work, we might have to compute a\n  # density profile or just print all regions we find.\n  my $lastline = 0;\n  for (my $i = 0; $i <= $#instructions; $i++) {\n    my $f = $instructions[$i]->[1];\n    my $l = $instructions[$i]->[2];\n    if (($f eq $filename) && ($l > $lastline)) {\n      $lastline = $l;\n    }\n  }\n\n  # Hack 3: assume the first source location from \"filename\" is the start of\n  # the source code.\n  my $firstline = 1;\n  for (my $i = 0; $i <= $#instructions; $i++) {\n    if ($instructions[$i]->[1] eq $filename) {\n      $firstline = $instructions[$i]->[2];\n      last;\n    }\n  }\n\n  # Hack 4: Extend last line forward until its indentation is less than\n  # the indentation we saw on $firstline\n  my $oldlastline = $lastline;\n  {\n    if (!open(FILE, \"<$filename\")) {\n      print STDERR \"$filename: $!\\n\";\n      return 0;\n    }\n    my $l = 0;\n    my $first_indentation = -1;\n    while (<FILE>) {\n      s/\\r//g;         # turn windows-looking lines into unix-looking lines\n      $l++;\n      my $indent = Indentation($_);\n      if ($l >= $firstline) {\n        if ($first_indentation < 0 && $indent >= 0) {\n          $first_indentation = $indent;\n          last if ($first_indentation == 0);\n        }\n      }\n      if ($l >= $lastline && $indent >= 0) {\n        if ($indent >= $first_indentation) {\n          $lastline = $l+1;\n        } else {\n          last;\n        }\n      }\n    }\n    close(FILE);\n  }\n\n  # Assign all samples to the range $firstline,$lastline,\n  # Hack 4: If an instruction does not occur in the range, its samples\n  # are moved to the next instruction that occurs in the range.\n  my $samples1 = {};        # Map from line number to flat count\n  my $samples2 = {};        # Map from line number to cumulative count\n  my $running1 = 0;         # Unassigned flat counts\n  my $running2 = 0;         # Unassigned cumulative counts\n  my $total1 = 0;           # Total flat counts\n  my $total2 = 0;           # Total cumulative counts\n  my %disasm = ();          # Map from line number to disassembly\n  my $running_disasm = \"\";  # Unassigned disassembly\n  my $skip_marker = \"---\\n\";\n  if ($html) {\n    $skip_marker = \"\";\n    for (my $l = $firstline; $l <= $lastline; $l++) {\n      $disasm{$l} = \"\";\n    }\n  }\n  my $last_dis_filename = '';\n  my $last_dis_linenum = -1;\n  my $last_touched_line = -1;  # To detect gaps in disassembly for a line\n  foreach my $e (@instructions) {\n    # Add up counts for all address that fall inside this instruction\n    my $c1 = 0;\n    my $c2 = 0;\n    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {\n      $c1 += GetEntry($flat, $a);\n      $c2 += GetEntry($cumulative, $a);\n    }\n\n    if ($html) {\n      my $dis = sprintf(\"      %6s %6s \\t\\t%8s: %s \",\n                        HtmlPrintNumber($c1),\n                        HtmlPrintNumber($c2),\n                        UnparseAddress($offset, $e->[0]),\n                        CleanDisassembly($e->[3]));\n\n      # Append the most specific source line associated with this instruction\n      if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };\n      $dis = HtmlEscape($dis);\n      my $f = $e->[5];\n      my $l = $e->[6];\n      if ($f ne $last_dis_filename) {\n        $dis .= sprintf(\"<span class=disasmloc>%s:%d</span>\",\n                        HtmlEscape(CleanFileName($f)), $l);\n      } elsif ($l ne $last_dis_linenum) {\n        # De-emphasize the unchanged file name portion\n        $dis .= sprintf(\"<span class=unimportant>%s</span>\" .\n                        \"<span class=disasmloc>:%d</span>\",\n                        HtmlEscape(CleanFileName($f)), $l);\n      } else {\n        # De-emphasize the entire location\n        $dis .= sprintf(\"<span class=unimportant>%s:%d</span>\",\n                        HtmlEscape(CleanFileName($f)), $l);\n      }\n      $last_dis_filename = $f;\n      $last_dis_linenum = $l;\n      $running_disasm .= $dis;\n      $running_disasm .= \"\\n\";\n    }\n\n    $running1 += $c1;\n    $running2 += $c2;\n    $total1 += $c1;\n    $total2 += $c2;\n    my $file = $e->[1];\n    my $line = $e->[2];\n    if (($file eq $filename) &&\n        ($line >= $firstline) &&\n        ($line <= $lastline)) {\n      # Assign all accumulated samples to this line\n      AddEntry($samples1, $line, $running1);\n      AddEntry($samples2, $line, $running2);\n      $running1 = 0;\n      $running2 = 0;\n      if ($html) {\n        if ($line != $last_touched_line && $disasm{$line} ne '') {\n          $disasm{$line} .= \"\\n\";\n        }\n        $disasm{$line} .= $running_disasm;\n        $running_disasm = '';\n        $last_touched_line = $line;\n      }\n    }\n  }\n\n  # Assign any leftover samples to $lastline\n  AddEntry($samples1, $lastline, $running1);\n  AddEntry($samples2, $lastline, $running2);\n  if ($html) {\n    if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {\n      $disasm{$lastline} .= \"\\n\";\n    }\n    $disasm{$lastline} .= $running_disasm;\n  }\n\n  if ($html) {\n    printf $output (\n      \"<h1>%s</h1>%s\\n<pre onClick=\\\"jeprof_toggle_asm()\\\">\\n\" .\n      \"Total:%6s %6s (flat / cumulative %s)\\n\",\n      HtmlEscape(ShortFunctionName($routine)),\n      HtmlEscape(CleanFileName($filename)),\n      Unparse($total1),\n      Unparse($total2),\n      Units());\n  } else {\n    printf $output (\n      \"ROUTINE ====================== %s in %s\\n\" .\n      \"%6s %6s Total %s (flat / cumulative)\\n\",\n      ShortFunctionName($routine),\n      CleanFileName($filename),\n      Unparse($total1),\n      Unparse($total2),\n      Units());\n  }\n  if (!open(FILE, \"<$filename\")) {\n    print STDERR \"$filename: $!\\n\";\n    return 0;\n  }\n  my $l = 0;\n  while (<FILE>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    $l++;\n    if ($l >= $firstline - 5 &&\n        (($l <= $oldlastline + 5) || ($l <= $lastline))) {\n      chop;\n      my $text = $_;\n      if ($l == $firstline) { print $output $skip_marker; }\n      my $n1 = GetEntry($samples1, $l);\n      my $n2 = GetEntry($samples2, $l);\n      if ($html) {\n        # Emit a span that has one of the following classes:\n        #    livesrc -- has samples\n        #    deadsrc -- has disassembly, but with no samples\n        #    nop     -- has no matching disasembly\n        # Also emit an optional span containing disassembly.\n        my $dis = $disasm{$l};\n        my $asm = \"\";\n        if (defined($dis) && $dis ne '') {\n          $asm = \"<span class=\\\"asm\\\">\" . $dis . \"</span>\";\n        }\n        my $source_class = (($n1 + $n2 > 0)\n                            ? \"livesrc\"\n                            : (($asm ne \"\") ? \"deadsrc\" : \"nop\"));\n        printf $output (\n          \"<span class=\\\"line\\\">%5d</span> \" .\n          \"<span class=\\\"%s\\\">%6s %6s %s</span>%s\\n\",\n          $l, $source_class,\n          HtmlPrintNumber($n1),\n          HtmlPrintNumber($n2),\n          HtmlEscape($text),\n          $asm);\n      } else {\n        printf $output(\n          \"%6s %6s %4d: %s\\n\",\n          UnparseAlt($n1),\n          UnparseAlt($n2),\n          $l,\n          $text);\n      }\n      if ($l == $lastline)  { print $output $skip_marker; }\n    };\n  }\n  close(FILE);\n  if ($html) {\n    print $output \"</pre>\\n\";\n  }\n  return 1;\n}\n\n# Return the source line for the specified file/linenumber.\n# Returns undef if not found.\nsub SourceLine {\n  my $file = shift;\n  my $line = shift;\n\n  # Look in cache\n  if (!defined($main::source_cache{$file})) {\n    if (100 < scalar keys(%main::source_cache)) {\n      # Clear the cache when it gets too big\n      $main::source_cache = ();\n    }\n\n    # Read all lines from the file\n    if (!open(FILE, \"<$file\")) {\n      print STDERR \"$file: $!\\n\";\n      $main::source_cache{$file} = [];  # Cache the negative result\n      return undef;\n    }\n    my $lines = [];\n    push(@{$lines}, \"\");        # So we can use 1-based line numbers as indices\n    while (<FILE>) {\n      push(@{$lines}, $_);\n    }\n    close(FILE);\n\n    # Save the lines in the cache\n    $main::source_cache{$file} = $lines;\n  }\n\n  my $lines = $main::source_cache{$file};\n  if (($line < 0) || ($line > $#{$lines})) {\n    return undef;\n  } else {\n    return $lines->[$line];\n  }\n}\n\n# Print disassembly for one routine with interspersed source if available\nsub PrintDisassembledFunction {\n  my $prog = shift;\n  my $offset = shift;\n  my $routine = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $start_addr = shift;\n  my $end_addr = shift;\n  my $total = shift;\n\n  # Disassemble all instructions\n  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);\n\n  # Make array of counts per instruction\n  my @flat_count = ();\n  my @cum_count = ();\n  my $flat_total = 0;\n  my $cum_total = 0;\n  foreach my $e (@instructions) {\n    # Add up counts for all address that fall inside this instruction\n    my $c1 = 0;\n    my $c2 = 0;\n    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {\n      $c1 += GetEntry($flat, $a);\n      $c2 += GetEntry($cumulative, $a);\n    }\n    push(@flat_count, $c1);\n    push(@cum_count, $c2);\n    $flat_total += $c1;\n    $cum_total += $c2;\n  }\n\n  # Print header with total counts\n  printf(\"ROUTINE ====================== %s\\n\" .\n         \"%6s %6s %s (flat, cumulative) %.1f%% of total\\n\",\n         ShortFunctionName($routine),\n         Unparse($flat_total),\n         Unparse($cum_total),\n         Units(),\n         ($cum_total * 100.0) / $total);\n\n  # Process instructions in order\n  my $current_file = \"\";\n  for (my $i = 0; $i <= $#instructions; ) {\n    my $e = $instructions[$i];\n\n    # Print the new file name whenever we switch files\n    if ($e->[1] ne $current_file) {\n      $current_file = $e->[1];\n      my $fname = $current_file;\n      $fname =~ s|^\\./||;   # Trim leading \"./\"\n\n      # Shorten long file names\n      if (length($fname) >= 58) {\n        $fname = \"...\" . substr($fname, -55);\n      }\n      printf(\"-------------------- %s\\n\", $fname);\n    }\n\n    # TODO: Compute range of lines to print together to deal with\n    # small reorderings.\n    my $first_line = $e->[2];\n    my $last_line = $first_line;\n    my %flat_sum = ();\n    my %cum_sum = ();\n    for (my $l = $first_line; $l <= $last_line; $l++) {\n      $flat_sum{$l} = 0;\n      $cum_sum{$l} = 0;\n    }\n\n    # Find run of instructions for this range of source lines\n    my $first_inst = $i;\n    while (($i <= $#instructions) &&\n           ($instructions[$i]->[2] >= $first_line) &&\n           ($instructions[$i]->[2] <= $last_line)) {\n      $e = $instructions[$i];\n      $flat_sum{$e->[2]} += $flat_count[$i];\n      $cum_sum{$e->[2]} += $cum_count[$i];\n      $i++;\n    }\n    my $last_inst = $i - 1;\n\n    # Print source lines\n    for (my $l = $first_line; $l <= $last_line; $l++) {\n      my $line = SourceLine($current_file, $l);\n      if (!defined($line)) {\n        $line = \"?\\n\";\n        next;\n      } else {\n        $line =~ s/^\\s+//;\n      }\n      printf(\"%6s %6s %5d: %s\",\n             UnparseAlt($flat_sum{$l}),\n             UnparseAlt($cum_sum{$l}),\n             $l,\n             $line);\n    }\n\n    # Print disassembly\n    for (my $x = $first_inst; $x <= $last_inst; $x++) {\n      my $e = $instructions[$x];\n      printf(\"%6s %6s    %8s: %6s\\n\",\n             UnparseAlt($flat_count[$x]),\n             UnparseAlt($cum_count[$x]),\n             UnparseAddress($offset, $e->[0]),\n             CleanDisassembly($e->[3]));\n    }\n  }\n}\n\n# Print DOT graph\nsub PrintDot {\n  my $prog = shift;\n  my $symbols = shift;\n  my $raw = shift;\n  my $flat = shift;\n  my $cumulative = shift;\n  my $overall_total = shift;\n\n  # Get total\n  my $local_total = TotalProfile($flat);\n  my $nodelimit = int($main::opt_nodefraction * $local_total);\n  my $edgelimit = int($main::opt_edgefraction * $local_total);\n  my $nodecount = $main::opt_nodecount;\n\n  # Find nodes to include\n  my @list = (sort { abs(GetEntry($cumulative, $b)) <=>\n                     abs(GetEntry($cumulative, $a))\n                     || $a cmp $b }\n              keys(%{$cumulative}));\n  my $last = $nodecount - 1;\n  if ($last > $#list) {\n    $last = $#list;\n  }\n  while (($last >= 0) &&\n         (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {\n    $last--;\n  }\n  if ($last < 0) {\n    print STDERR \"No nodes to print\\n\";\n    return 0;\n  }\n\n  if ($nodelimit > 0 || $edgelimit > 0) {\n    printf STDERR (\"Dropping nodes with <= %s %s; edges with <= %s abs(%s)\\n\",\n                   Unparse($nodelimit), Units(),\n                   Unparse($edgelimit), Units());\n  }\n\n  # Open DOT output file\n  my $output;\n  my $escaped_dot = ShellEscape(@DOT);\n  my $escaped_ps2pdf = ShellEscape(@PS2PDF);\n  if ($main::opt_gv) {\n    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, \"ps\"));\n    $output = \"| $escaped_dot -Tps2 >$escaped_outfile\";\n  } elsif ($main::opt_evince) {\n    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, \"pdf\"));\n    $output = \"| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile\";\n  } elsif ($main::opt_ps) {\n    $output = \"| $escaped_dot -Tps2\";\n  } elsif ($main::opt_pdf) {\n    $output = \"| $escaped_dot -Tps2 | $escaped_ps2pdf - -\";\n  } elsif ($main::opt_web || $main::opt_svg) {\n    # We need to post-process the SVG, so write to a temporary file always.\n    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, \"svg\"));\n    $output = \"| $escaped_dot -Tsvg >$escaped_outfile\";\n  } elsif ($main::opt_gif) {\n    $output = \"| $escaped_dot -Tgif\";\n  } else {\n    $output = \">&STDOUT\";\n  }\n  open(DOT, $output) || error(\"$output: $!\\n\");\n\n  # Title\n  printf DOT (\"digraph \\\"%s; %s %s\\\" {\\n\",\n              $prog,\n              Unparse($overall_total),\n              Units());\n  if ($main::opt_pdf) {\n    # The output is more printable if we set the page size for dot.\n    printf DOT (\"size=\\\"8,11\\\"\\n\");\n  }\n  printf DOT (\"node [width=0.375,height=0.25];\\n\");\n\n  # Print legend\n  printf DOT (\"Legend [shape=box,fontsize=24,shape=plaintext,\" .\n              \"label=\\\"%s\\\\l%s\\\\l%s\\\\l%s\\\\l%s\\\\l\\\"];\\n\",\n              $prog,\n              sprintf(\"Total %s: %s\", Units(), Unparse($overall_total)),\n              sprintf(\"Focusing on: %s\", Unparse($local_total)),\n              sprintf(\"Dropped nodes with <= %s abs(%s)\",\n                      Unparse($nodelimit), Units()),\n              sprintf(\"Dropped edges with <= %s %s\",\n                      Unparse($edgelimit), Units())\n              );\n\n  # Print nodes\n  my %node = ();\n  my $nextnode = 1;\n  foreach my $a (@list[0..$last]) {\n    # Pick font size\n    my $f = GetEntry($flat, $a);\n    my $c = GetEntry($cumulative, $a);\n\n    my $fs = 8;\n    if ($local_total > 0) {\n      $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));\n    }\n\n    $node{$a} = $nextnode++;\n    my $sym = $a;\n    $sym =~ s/\\s+/\\\\n/g;\n    $sym =~ s/::/\\\\n/g;\n\n    # Extra cumulative info to print for non-leaves\n    my $extra = \"\";\n    if ($f != $c) {\n      $extra = sprintf(\"\\\\rof %s (%s)\",\n                       Unparse($c),\n                       Percent($c, $local_total));\n    }\n    my $style = \"\";\n    if ($main::opt_heapcheck) {\n      if ($f > 0) {\n        # make leak-causing nodes more visible (add a background)\n        $style = \",style=filled,fillcolor=gray\"\n      } elsif ($f < 0) {\n        # make anti-leak-causing nodes (which almost never occur)\n        # stand out as well (triple border)\n        $style = \",peripheries=3\"\n      }\n    }\n\n    printf DOT (\"N%d [label=\\\"%s\\\\n%s (%s)%s\\\\r\" .\n                \"\\\",shape=box,fontsize=%.1f%s];\\n\",\n                $node{$a},\n                $sym,\n                Unparse($f),\n                Percent($f, $local_total),\n                $extra,\n                $fs,\n                $style,\n               );\n  }\n\n  # Get edges and counts per edge\n  my %edge = ();\n  my $n;\n  my $fullname_to_shortname_map = {};\n  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);\n  foreach my $k (keys(%{$raw})) {\n    # TODO: omit low %age edges\n    $n = $raw->{$k};\n    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);\n    for (my $i = 1; $i <= $#translated; $i++) {\n      my $src = $translated[$i];\n      my $dst = $translated[$i-1];\n      #next if ($src eq $dst);  # Avoid self-edges?\n      if (exists($node{$src}) && exists($node{$dst})) {\n        my $edge_label = \"$src\\001$dst\";\n        if (!exists($edge{$edge_label})) {\n          $edge{$edge_label} = 0;\n        }\n        $edge{$edge_label} += $n;\n      }\n    }\n  }\n\n  # Print edges (process in order of decreasing counts)\n  my %indegree = ();   # Number of incoming edges added per node so far\n  my %outdegree = ();  # Number of outgoing edges added per node so far\n  foreach my $e (sort { $edge{$b} <=> $edge{$a} } keys(%edge)) {\n    my @x = split(/\\001/, $e);\n    $n = $edge{$e};\n\n    # Initialize degree of kept incoming and outgoing edges if necessary\n    my $src = $x[0];\n    my $dst = $x[1];\n    if (!exists($outdegree{$src})) { $outdegree{$src} = 0; }\n    if (!exists($indegree{$dst})) { $indegree{$dst} = 0; }\n\n    my $keep;\n    if ($indegree{$dst} == 0) {\n      # Keep edge if needed for reachability\n      $keep = 1;\n    } elsif (abs($n) <= $edgelimit) {\n      # Drop if we are below --edgefraction\n      $keep = 0;\n    } elsif ($outdegree{$src} >= $main::opt_maxdegree ||\n             $indegree{$dst} >= $main::opt_maxdegree) {\n      # Keep limited number of in/out edges per node\n      $keep = 0;\n    } else {\n      $keep = 1;\n    }\n\n    if ($keep) {\n      $outdegree{$src}++;\n      $indegree{$dst}++;\n\n      # Compute line width based on edge count\n      my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);\n      if ($fraction > 1) { $fraction = 1; }\n      my $w = $fraction * 2;\n      if ($w < 1 && ($main::opt_web || $main::opt_svg)) {\n        # SVG output treats line widths < 1 poorly.\n        $w = 1;\n      }\n\n      # Dot sometimes segfaults if given edge weights that are too large, so\n      # we cap the weights at a large value\n      my $edgeweight = abs($n) ** 0.7;\n      if ($edgeweight > 100000) { $edgeweight = 100000; }\n      $edgeweight = int($edgeweight);\n\n      my $style = sprintf(\"setlinewidth(%f)\", $w);\n      if ($x[1] =~ m/\\(inline\\)/) {\n        $style .= \",dashed\";\n      }\n\n      # Use a slightly squashed function of the edge count as the weight\n      printf DOT (\"N%s -> N%s [label=%s, weight=%d, style=\\\"%s\\\"];\\n\",\n                  $node{$x[0]},\n                  $node{$x[1]},\n                  Unparse($n),\n                  $edgeweight,\n                  $style);\n    }\n  }\n\n  print DOT (\"}\\n\");\n  close(DOT);\n\n  if ($main::opt_web || $main::opt_svg) {\n    # Rewrite SVG to be more usable inside web browser.\n    RewriteSvg(TempName($main::next_tmpfile, \"svg\"));\n  }\n\n  return 1;\n}\n\nsub RewriteSvg {\n  my $svgfile = shift;\n\n  open(SVG, $svgfile) || die \"open temp svg: $!\";\n  my @svg = <SVG>;\n  close(SVG);\n  unlink $svgfile;\n  my $svg = join('', @svg);\n\n  # Dot's SVG output is\n  #\n  #    <svg width=\"___\" height=\"___\"\n  #     viewBox=\"___\" xmlns=...>\n  #    <g id=\"graph0\" transform=\"...\">\n  #    ...\n  #    </g>\n  #    </svg>\n  #\n  # Change it to\n  #\n  #    <svg width=\"100%\" height=\"100%\"\n  #     xmlns=...>\n  #    $svg_javascript\n  #    <g id=\"viewport\" transform=\"translate(0,0)\">\n  #    <g id=\"graph0\" transform=\"...\">\n  #    ...\n  #    </g>\n  #    </g>\n  #    </svg>\n\n  # Fix width, height; drop viewBox.\n  $svg =~ s/(?s)<svg width=\"[^\"]+\" height=\"[^\"]+\"(.*?)viewBox=\"[^\"]+\"/<svg width=\"100%\" height=\"100%\"$1/;\n\n  # Insert script, viewport <g> above first <g>\n  my $svg_javascript = SvgJavascript();\n  my $viewport = \"<g id=\\\"viewport\\\" transform=\\\"translate(0,0)\\\">\\n\";\n  $svg =~ s/<g id=\"graph\\d\"/$svg_javascript$viewport$&/;\n\n  # Insert final </g> above </svg>.\n  $svg =~ s/(.*)(<\\/svg>)/$1<\\/g>$2/;\n  $svg =~ s/<g id=\"graph\\d\"(.*?)/<g id=\"viewport\"$1/;\n\n  if ($main::opt_svg) {\n    # --svg: write to standard output.\n    print $svg;\n  } else {\n    # Write back to temporary file.\n    open(SVG, \">$svgfile\") || die \"open $svgfile: $!\";\n    print SVG $svg;\n    close(SVG);\n  }\n}\n\nsub SvgJavascript {\n  return <<'EOF';\n<script type=\"text/ecmascript\"><![CDATA[\n// SVGPan\n// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/\n// Local modification: if(true || ...) below to force panning, never moving.\n\n/**\n *  SVGPan library 1.2\n * ====================\n *\n * Given an unique existing element with id \"viewport\", including the\n * the library into any SVG adds the following capabilities:\n *\n *  - Mouse panning\n *  - Mouse zooming (using the wheel)\n *  - Object dargging\n *\n * Known issues:\n *\n *  - Zooming (while panning) on Safari has still some issues\n *\n * Releases:\n *\n * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui\n *\tFixed a bug with browser mouse handler interaction\n *\n * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui\n *\tUpdated the zoom code to support the mouse wheel on Safari/Chrome\n *\n * 1.0, Andrea Leofreddi\n *\tFirst release\n *\n * This code is licensed under the following BSD license:\n *\n * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification, are\n * permitted provided that the following conditions are met:\n *\n *    1. Redistributions of source code must retain the above copyright notice, this list of\n *       conditions and the following disclaimer.\n *\n *    2. Redistributions in binary form must reproduce the above copyright notice, this list\n *       of conditions and the following disclaimer in the documentation and/or other materials\n *       provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND\n * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n *\n * The views and conclusions contained in the software and documentation are those of the\n * authors and should not be interpreted as representing official policies, either expressed\n * or implied, of Andrea Leofreddi.\n */\n\nvar root = document.documentElement;\n\nvar state = 'none', stateTarget, stateOrigin, stateTf;\n\nsetupHandlers(root);\n\n/**\n * Register handlers\n */\nfunction setupHandlers(root){\n\tsetAttributes(root, {\n\t\t\"onmouseup\" : \"add(evt)\",\n\t\t\"onmousedown\" : \"handleMouseDown(evt)\",\n\t\t\"onmousemove\" : \"handleMouseMove(evt)\",\n\t\t\"onmouseup\" : \"handleMouseUp(evt)\",\n\t\t//\"onmouseout\" : \"handleMouseUp(evt)\", // Decomment this to stop the pan functionality when dragging out of the SVG element\n\t});\n\n\tif(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)\n\t\twindow.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari\n\telse\n\t\twindow.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others\n\n\tvar g = svgDoc.getElementById(\"svg\");\n\tg.width = \"100%\";\n\tg.height = \"100%\";\n}\n\n/**\n * Instance an SVGPoint object with given event coordinates.\n */\nfunction getEventPoint(evt) {\n\tvar p = root.createSVGPoint();\n\n\tp.x = evt.clientX;\n\tp.y = evt.clientY;\n\n\treturn p;\n}\n\n/**\n * Sets the current transform matrix of an element.\n */\nfunction setCTM(element, matrix) {\n\tvar s = \"matrix(\" + matrix.a + \",\" + matrix.b + \",\" + matrix.c + \",\" + matrix.d + \",\" + matrix.e + \",\" + matrix.f + \")\";\n\n\telement.setAttribute(\"transform\", s);\n}\n\n/**\n * Dumps a matrix to a string (useful for debug).\n */\nfunction dumpMatrix(matrix) {\n\tvar s = \"[ \" + matrix.a + \", \" + matrix.c + \", \" + matrix.e + \"\\n  \" + matrix.b + \", \" + matrix.d + \", \" + matrix.f + \"\\n  0, 0, 1 ]\";\n\n\treturn s;\n}\n\n/**\n * Sets attributes of an element.\n */\nfunction setAttributes(element, attributes){\n\tfor (i in attributes)\n\t\telement.setAttributeNS(null, i, attributes[i]);\n}\n\n/**\n * Handle mouse move event.\n */\nfunction handleMouseWheel(evt) {\n\tif(evt.preventDefault)\n\t\tevt.preventDefault();\n\n\tevt.returnValue = false;\n\n\tvar svgDoc = evt.target.ownerDocument;\n\n\tvar delta;\n\n\tif(evt.wheelDelta)\n\t\tdelta = evt.wheelDelta / 3600; // Chrome/Safari\n\telse\n\t\tdelta = evt.detail / -90; // Mozilla\n\n\tvar z = 1 + delta; // Zoom factor: 0.9/1.1\n\n\tvar g = svgDoc.getElementById(\"viewport\");\n\n\tvar p = getEventPoint(evt);\n\n\tp = p.matrixTransform(g.getCTM().inverse());\n\n\t// Compute new scale matrix in current mouse position\n\tvar k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);\n\n        setCTM(g, g.getCTM().multiply(k));\n\n\tstateTf = stateTf.multiply(k.inverse());\n}\n\n/**\n * Handle mouse move event.\n */\nfunction handleMouseMove(evt) {\n\tif(evt.preventDefault)\n\t\tevt.preventDefault();\n\n\tevt.returnValue = false;\n\n\tvar svgDoc = evt.target.ownerDocument;\n\n\tvar g = svgDoc.getElementById(\"viewport\");\n\n\tif(state == 'pan') {\n\t\t// Pan mode\n\t\tvar p = getEventPoint(evt).matrixTransform(stateTf);\n\n\t\tsetCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));\n\t} else if(state == 'move') {\n\t\t// Move mode\n\t\tvar p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());\n\n\t\tsetCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));\n\n\t\tstateOrigin = p;\n\t}\n}\n\n/**\n * Handle click event.\n */\nfunction handleMouseDown(evt) {\n\tif(evt.preventDefault)\n\t\tevt.preventDefault();\n\n\tevt.returnValue = false;\n\n\tvar svgDoc = evt.target.ownerDocument;\n\n\tvar g = svgDoc.getElementById(\"viewport\");\n\n\tif(true || evt.target.tagName == \"svg\") {\n\t\t// Pan mode\n\t\tstate = 'pan';\n\n\t\tstateTf = g.getCTM().inverse();\n\n\t\tstateOrigin = getEventPoint(evt).matrixTransform(stateTf);\n\t} else {\n\t\t// Move mode\n\t\tstate = 'move';\n\n\t\tstateTarget = evt.target;\n\n\t\tstateTf = g.getCTM().inverse();\n\n\t\tstateOrigin = getEventPoint(evt).matrixTransform(stateTf);\n\t}\n}\n\n/**\n * Handle mouse button release event.\n */\nfunction handleMouseUp(evt) {\n\tif(evt.preventDefault)\n\t\tevt.preventDefault();\n\n\tevt.returnValue = false;\n\n\tvar svgDoc = evt.target.ownerDocument;\n\n\tif(state == 'pan' || state == 'move') {\n\t\t// Quit pan mode\n\t\tstate = '';\n\t}\n}\n\n]]></script>\nEOF\n}\n\n# Provides a map from fullname to shortname for cases where the\n# shortname is ambiguous.  The symlist has both the fullname and\n# shortname for all symbols, which is usually fine, but sometimes --\n# such as overloaded functions -- two different fullnames can map to\n# the same shortname.  In that case, we use the address of the\n# function to disambiguate the two.  This function fills in a map that\n# maps fullnames to modified shortnames in such cases.  If a fullname\n# is not present in the map, the 'normal' shortname provided by the\n# symlist is the appropriate one to use.\nsub FillFullnameToShortnameMap {\n  my $symbols = shift;\n  my $fullname_to_shortname_map = shift;\n  my $shortnames_seen_once = {};\n  my $shortnames_seen_more_than_once = {};\n\n  foreach my $symlist (values(%{$symbols})) {\n    # TODO(csilvers): deal with inlined symbols too.\n    my $shortname = $symlist->[0];\n    my $fullname = $symlist->[2];\n    if ($fullname !~ /<[0-9a-fA-F]+>$/) {  # fullname doesn't end in an address\n      next;       # the only collisions we care about are when addresses differ\n    }\n    if (defined($shortnames_seen_once->{$shortname}) &&\n        $shortnames_seen_once->{$shortname} ne $fullname) {\n      $shortnames_seen_more_than_once->{$shortname} = 1;\n    } else {\n      $shortnames_seen_once->{$shortname} = $fullname;\n    }\n  }\n\n  foreach my $symlist (values(%{$symbols})) {\n    my $shortname = $symlist->[0];\n    my $fullname = $symlist->[2];\n    # TODO(csilvers): take in a list of addresses we care about, and only\n    # store in the map if $symlist->[1] is in that list.  Saves space.\n    next if defined($fullname_to_shortname_map->{$fullname});\n    if (defined($shortnames_seen_more_than_once->{$shortname})) {\n      if ($fullname =~ /<0*([^>]*)>$/) {   # fullname has address at end of it\n        $fullname_to_shortname_map->{$fullname} = \"$shortname\\@$1\";\n      }\n    }\n  }\n}\n\n# Return a small number that identifies the argument.\n# Multiple calls with the same argument will return the same number.\n# Calls with different arguments will return different numbers.\nsub ShortIdFor {\n  my $key = shift;\n  my $id = $main::uniqueid{$key};\n  if (!defined($id)) {\n    $id = keys(%main::uniqueid) + 1;\n    $main::uniqueid{$key} = $id;\n  }\n  return $id;\n}\n\n# Translate a stack of addresses into a stack of symbols\nsub TranslateStack {\n  my $symbols = shift;\n  my $fullname_to_shortname_map = shift;\n  my $k = shift;\n\n  my @addrs = split(/\\n/, $k);\n  my @result = ();\n  for (my $i = 0; $i <= $#addrs; $i++) {\n    my $a = $addrs[$i];\n\n    # Skip large addresses since they sometimes show up as fake entries on RH9\n    if (length($a) > 8 && $a gt \"7fffffffffffffff\") {\n      next;\n    }\n\n    if ($main::opt_disasm || $main::opt_list) {\n      # We want just the address for the key\n      push(@result, $a);\n      next;\n    }\n\n    my $symlist = $symbols->{$a};\n    if (!defined($symlist)) {\n      $symlist = [$a, \"\", $a];\n    }\n\n    # We can have a sequence of symbols for a particular entry\n    # (more than one symbol in the case of inlining).  Callers\n    # come before callees in symlist, so walk backwards since\n    # the translated stack should contain callees before callers.\n    for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {\n      my $func = $symlist->[$j-2];\n      my $fileline = $symlist->[$j-1];\n      my $fullfunc = $symlist->[$j];\n      if (defined($fullname_to_shortname_map->{$fullfunc})) {\n        $func = $fullname_to_shortname_map->{$fullfunc};\n      }\n      if ($j > 2) {\n        $func = \"$func (inline)\";\n      }\n\n      # Do not merge nodes corresponding to Callback::Run since that\n      # causes confusing cycles in dot display.  Instead, we synthesize\n      # a unique name for this frame per caller.\n      if ($func =~ m/Callback.*::Run$/) {\n        my $caller = ($i > 0) ? $addrs[$i-1] : 0;\n        $func = \"Run#\" . ShortIdFor($caller);\n      }\n\n      if ($main::opt_addresses) {\n        push(@result, \"$a $func $fileline\");\n      } elsif ($main::opt_lines) {\n        if ($func eq '??' && $fileline eq '??:0') {\n          push(@result, \"$a\");\n        } else {\n          push(@result, \"$func $fileline\");\n        }\n      } elsif ($main::opt_functions) {\n        if ($func eq '??') {\n          push(@result, \"$a\");\n        } else {\n          push(@result, $func);\n        }\n      } elsif ($main::opt_files) {\n        if ($fileline eq '??:0' || $fileline eq '') {\n          push(@result, \"$a\");\n        } else {\n          my $f = $fileline;\n          $f =~ s/:\\d+$//;\n          push(@result, $f);\n        }\n      } else {\n        push(@result, $a);\n        last;  # Do not print inlined info\n      }\n    }\n  }\n\n  # print join(\",\", @addrs), \" => \", join(\",\", @result), \"\\n\";\n  return @result;\n}\n\n# Generate percent string for a number and a total\nsub Percent {\n  my $num = shift;\n  my $tot = shift;\n  if ($tot != 0) {\n    return sprintf(\"%.1f%%\", $num * 100.0 / $tot);\n  } else {\n    return ($num == 0) ? \"nan\" : (($num > 0) ? \"+inf\" : \"-inf\");\n  }\n}\n\n# Generate pretty-printed form of number\nsub Unparse {\n  my $num = shift;\n  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {\n    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {\n      return sprintf(\"%d\", $num);\n    } else {\n      if ($main::opt_show_bytes) {\n        return sprintf(\"%d\", $num);\n      } else {\n        return sprintf(\"%.1f\", $num / 1048576.0);\n      }\n    }\n  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {\n    return sprintf(\"%.3f\", $num / 1e9); # Convert nanoseconds to seconds\n  } else {\n    return sprintf(\"%d\", $num);\n  }\n}\n\n# Alternate pretty-printed form: 0 maps to \".\"\nsub UnparseAlt {\n  my $num = shift;\n  if ($num == 0) {\n    return \".\";\n  } else {\n    return Unparse($num);\n  }\n}\n\n# Alternate pretty-printed form: 0 maps to \"\"\nsub HtmlPrintNumber {\n  my $num = shift;\n  if ($num == 0) {\n    return \"\";\n  } else {\n    return Unparse($num);\n  }\n}\n\n# Return output units\nsub Units {\n  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {\n    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {\n      return \"objects\";\n    } else {\n      if ($main::opt_show_bytes) {\n        return \"B\";\n      } else {\n        return \"MB\";\n      }\n    }\n  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {\n    return \"seconds\";\n  } else {\n    return \"samples\";\n  }\n}\n\n##### Profile manipulation code #####\n\n# Generate flattened profile:\n# If count is charged to stack [a,b,c,d], in generated profile,\n# it will be charged to [a]\nsub FlatProfile {\n  my $profile = shift;\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    if ($#addrs >= 0) {\n      AddEntry($result, $addrs[0], $count);\n    }\n  }\n  return $result;\n}\n\n# Generate cumulative profile:\n# If count is charged to stack [a,b,c,d], in generated profile,\n# it will be charged to [a], [b], [c], [d]\nsub CumulativeProfile {\n  my $profile = shift;\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    foreach my $a (@addrs) {\n      AddEntry($result, $a, $count);\n    }\n  }\n  return $result;\n}\n\n# If the second-youngest PC on the stack is always the same, returns\n# that pc.  Otherwise, returns undef.\nsub IsSecondPcAlwaysTheSame {\n  my $profile = shift;\n\n  my $second_pc = undef;\n  foreach my $k (keys(%{$profile})) {\n    my @addrs = split(/\\n/, $k);\n    if ($#addrs < 1) {\n      return undef;\n    }\n    if (not defined $second_pc) {\n      $second_pc = $addrs[1];\n    } else {\n      if ($second_pc ne $addrs[1]) {\n        return undef;\n      }\n    }\n  }\n  return $second_pc;\n}\n\nsub ExtractSymbolLocation {\n  my $symbols = shift;\n  my $address = shift;\n  # 'addr2line' outputs \"??:0\" for unknown locations; we do the\n  # same to be consistent.\n  my $location = \"??:0:unknown\";\n  if (exists $symbols->{$address}) {\n    my $file = $symbols->{$address}->[1];\n    if ($file eq \"?\") {\n      $file = \"??:0\"\n    }\n    $location = $file . \":\" . $symbols->{$address}->[0];\n  }\n  return $location;\n}\n\n# Extracts a graph of calls.\nsub ExtractCalls {\n  my $symbols = shift;\n  my $profile = shift;\n\n  my $calls = {};\n  while( my ($stack_trace, $count) = each %$profile ) {\n    my @address = split(/\\n/, $stack_trace);\n    my $destination = ExtractSymbolLocation($symbols, $address[0]);\n    AddEntry($calls, $destination, $count);\n    for (my $i = 1; $i <= $#address; $i++) {\n      my $source = ExtractSymbolLocation($symbols, $address[$i]);\n      my $call = \"$source -> $destination\";\n      AddEntry($calls, $call, $count);\n      $destination = $source;\n    }\n  }\n\n  return $calls;\n}\n\nsub FilterFrames {\n  my $symbols = shift;\n  my $profile = shift;\n\n  if ($main::opt_retain eq '' && $main::opt_exclude eq '') {\n    return $profile;\n  }\n\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    my @path = ();\n    foreach my $a (@addrs) {\n      my $sym;\n      if (exists($symbols->{$a})) {\n        $sym = $symbols->{$a}->[0];\n      } else {\n        $sym = $a;\n      }\n      if ($main::opt_retain ne '' && $sym !~ m/$main::opt_retain/) {\n        next;\n      }\n      if ($main::opt_exclude ne '' && $sym =~ m/$main::opt_exclude/) {\n        next;\n      }\n      push(@path, $a);\n    }\n    if (scalar(@path) > 0) {\n      my $reduced_path = join(\"\\n\", @path);\n      AddEntry($result, $reduced_path, $count);\n    }\n  }\n\n  return $result;\n}\n\nsub RemoveUninterestingFrames {\n  my $symbols = shift;\n  my $profile = shift;\n\n  # List of function names to skip\n  my %skip = ();\n  my $skip_regexp = 'NOMATCH';\n  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {\n    foreach my $name ('calloc',\n                      'cfree',\n                      'malloc',\n                      'newImpl',\n                      'void* newImpl',\n                      'free',\n                      'memalign',\n                      'posix_memalign',\n                      'aligned_alloc',\n                      'pvalloc',\n                      'valloc',\n                      'realloc',\n                      'mallocx',\n                      'rallocx',\n                      'xallocx',\n                      'dallocx',\n                      'sdallocx',\n                      'sdallocx_noflags',\n                      'tc_calloc',\n                      'tc_cfree',\n                      'tc_malloc',\n                      'tc_free',\n                      'tc_memalign',\n                      'tc_posix_memalign',\n                      'tc_pvalloc',\n                      'tc_valloc',\n                      'tc_realloc',\n                      'tc_new',\n                      'tc_delete',\n                      'tc_newarray',\n                      'tc_deletearray',\n                      'tc_new_nothrow',\n                      'tc_newarray_nothrow',\n                      'do_malloc',\n                      '::do_malloc',   # new name -- got moved to an unnamed ns\n                      '::do_malloc_or_cpp_alloc',\n                      'DoSampledAllocation',\n                      'simple_alloc::allocate',\n                      '__malloc_alloc_template::allocate',\n                      '__builtin_delete',\n                      '__builtin_new',\n                      '__builtin_vec_delete',\n                      '__builtin_vec_new',\n                      'operator new',\n                      'operator new[]',\n                      # The entry to our memory-allocation routines on OS X\n                      'malloc_zone_malloc',\n                      'malloc_zone_calloc',\n                      'malloc_zone_valloc',\n                      'malloc_zone_realloc',\n                      'malloc_zone_memalign',\n                      'malloc_zone_free',\n                      # These mark the beginning/end of our custom sections\n                      '__start_google_malloc',\n                      '__stop_google_malloc',\n                      '__start_malloc_hook',\n                      '__stop_malloc_hook') {\n      $skip{$name} = 1;\n      $skip{\"_\" . $name} = 1;   # Mach (OS X) adds a _ prefix to everything\n    }\n    # TODO: Remove TCMalloc once everything has been\n    # moved into the tcmalloc:: namespace and we have flushed\n    # old code out of the system.\n    $skip_regexp = \"TCMalloc|^tcmalloc::\";\n  } elsif ($main::profile_type eq 'contention') {\n    foreach my $vname ('base::RecordLockProfileData',\n                       'base::SubmitMutexProfileData',\n                       'base::SubmitSpinLockProfileData',\n                       'Mutex::Unlock',\n                       'Mutex::UnlockSlow',\n                       'Mutex::ReaderUnlock',\n                       'MutexLock::~MutexLock',\n                       'SpinLock::Unlock',\n                       'SpinLock::SlowUnlock',\n                       'SpinLockHolder::~SpinLockHolder') {\n      $skip{$vname} = 1;\n    }\n  } elsif ($main::profile_type eq 'cpu') {\n    # Drop signal handlers used for CPU profile collection\n    # TODO(dpeng): this should not be necessary; it's taken\n    # care of by the general 2nd-pc mechanism below.\n    foreach my $name ('ProfileData::Add',           # historical\n                      'ProfileData::prof_handler',  # historical\n                      'CpuProfiler::prof_handler',\n                      '__FRAME_END__',\n                      '__pthread_sighandler',\n                      '__restore') {\n      $skip{$name} = 1;\n    }\n  } else {\n    # Nothing skipped for unknown types\n  }\n\n  if ($main::profile_type eq 'cpu') {\n    # If all the second-youngest program counters are the same,\n    # this STRONGLY suggests that it is an artifact of measurement,\n    # i.e., stack frames pushed by the CPU profiler signal handler.\n    # Hence, we delete them.\n    # (The topmost PC is read from the signal structure, not from\n    # the stack, so it does not get involved.)\n    while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {\n      my $result = {};\n      my $func = '';\n      if (exists($symbols->{$second_pc})) {\n        $second_pc = $symbols->{$second_pc}->[0];\n      }\n      print STDERR \"Removing $second_pc from all stack traces.\\n\";\n      foreach my $k (keys(%{$profile})) {\n        my $count = $profile->{$k};\n        my @addrs = split(/\\n/, $k);\n        splice @addrs, 1, 1;\n        my $reduced_path = join(\"\\n\", @addrs);\n        AddEntry($result, $reduced_path, $count);\n      }\n      $profile = $result;\n    }\n  }\n\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    my @path = ();\n    foreach my $a (@addrs) {\n      if (exists($symbols->{$a})) {\n        my $func = $symbols->{$a}->[0];\n        if ($skip{$func} || ($func =~ m/$skip_regexp/)) {\n          # Throw away the portion of the backtrace seen so far, under the\n          # assumption that previous frames were for functions internal to the\n          # allocator.\n          @path = ();\n          next;\n        }\n      }\n      push(@path, $a);\n    }\n    my $reduced_path = join(\"\\n\", @path);\n    AddEntry($result, $reduced_path, $count);\n  }\n\n  $result = FilterFrames($symbols, $result);\n\n  return $result;\n}\n\n# Reduce profile to granularity given by user\nsub ReduceProfile {\n  my $symbols = shift;\n  my $profile = shift;\n  my $result = {};\n  my $fullname_to_shortname_map = {};\n  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);\n    my @path = ();\n    my %seen = ();\n    $seen{''} = 1;      # So that empty keys are skipped\n    foreach my $e (@translated) {\n      # To avoid double-counting due to recursion, skip a stack-trace\n      # entry if it has already been seen\n      if (!$seen{$e}) {\n        $seen{$e} = 1;\n        push(@path, $e);\n      }\n    }\n    my $reduced_path = join(\"\\n\", @path);\n    AddEntry($result, $reduced_path, $count);\n  }\n  return $result;\n}\n\n# Does the specified symbol array match the regexp?\nsub SymbolMatches {\n  my $sym = shift;\n  my $re = shift;\n  if (defined($sym)) {\n    for (my $i = 0; $i < $#{$sym}; $i += 3) {\n      if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {\n        return 1;\n      }\n    }\n  }\n  return 0;\n}\n\n# Focus only on paths involving specified regexps\nsub FocusProfile {\n  my $symbols = shift;\n  my $profile = shift;\n  my $focus = shift;\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    foreach my $a (@addrs) {\n      # Reply if it matches either the address/shortname/fileline\n      if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {\n        AddEntry($result, $k, $count);\n        last;\n      }\n    }\n  }\n  return $result;\n}\n\n# Focus only on paths not involving specified regexps\nsub IgnoreProfile {\n  my $symbols = shift;\n  my $profile = shift;\n  my $ignore = shift;\n  my $result = {};\n  foreach my $k (keys(%{$profile})) {\n    my $count = $profile->{$k};\n    my @addrs = split(/\\n/, $k);\n    my $matched = 0;\n    foreach my $a (@addrs) {\n      # Reply if it matches either the address/shortname/fileline\n      if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {\n        $matched = 1;\n        last;\n      }\n    }\n    if (!$matched) {\n      AddEntry($result, $k, $count);\n    }\n  }\n  return $result;\n}\n\n# Get total count in profile\nsub TotalProfile {\n  my $profile = shift;\n  my $result = 0;\n  foreach my $k (keys(%{$profile})) {\n    $result += $profile->{$k};\n  }\n  return $result;\n}\n\n# Add A to B\nsub AddProfile {\n  my $A = shift;\n  my $B = shift;\n\n  my $R = {};\n  # add all keys in A\n  foreach my $k (keys(%{$A})) {\n    my $v = $A->{$k};\n    AddEntry($R, $k, $v);\n  }\n  # add all keys in B\n  foreach my $k (keys(%{$B})) {\n    my $v = $B->{$k};\n    AddEntry($R, $k, $v);\n  }\n  return $R;\n}\n\n# Merges symbol maps\nsub MergeSymbols {\n  my $A = shift;\n  my $B = shift;\n\n  my $R = {};\n  foreach my $k (keys(%{$A})) {\n    $R->{$k} = $A->{$k};\n  }\n  if (defined($B)) {\n    foreach my $k (keys(%{$B})) {\n      $R->{$k} = $B->{$k};\n    }\n  }\n  return $R;\n}\n\n\n# Add A to B\nsub AddPcs {\n  my $A = shift;\n  my $B = shift;\n\n  my $R = {};\n  # add all keys in A\n  foreach my $k (keys(%{$A})) {\n    $R->{$k} = 1\n  }\n  # add all keys in B\n  foreach my $k (keys(%{$B})) {\n    $R->{$k} = 1\n  }\n  return $R;\n}\n\n# Subtract B from A\nsub SubtractProfile {\n  my $A = shift;\n  my $B = shift;\n\n  my $R = {};\n  foreach my $k (keys(%{$A})) {\n    my $v = $A->{$k} - GetEntry($B, $k);\n    if ($v < 0 && $main::opt_drop_negative) {\n      $v = 0;\n    }\n    AddEntry($R, $k, $v);\n  }\n  if (!$main::opt_drop_negative) {\n    # Take care of when subtracted profile has more entries\n    foreach my $k (keys(%{$B})) {\n      if (!exists($A->{$k})) {\n        AddEntry($R, $k, 0 - $B->{$k});\n      }\n    }\n  }\n  return $R;\n}\n\n# Get entry from profile; zero if not present\nsub GetEntry {\n  my $profile = shift;\n  my $k = shift;\n  if (exists($profile->{$k})) {\n    return $profile->{$k};\n  } else {\n    return 0;\n  }\n}\n\n# Add entry to specified profile\nsub AddEntry {\n  my $profile = shift;\n  my $k = shift;\n  my $n = shift;\n  if (!exists($profile->{$k})) {\n    $profile->{$k} = 0;\n  }\n  $profile->{$k} += $n;\n}\n\n# Add a stack of entries to specified profile, and add them to the $pcs\n# list.\nsub AddEntries {\n  my $profile = shift;\n  my $pcs = shift;\n  my $stack = shift;\n  my $count = shift;\n  my @k = ();\n\n  foreach my $e (split(/\\s+/, $stack)) {\n    my $pc = HexExtend($e);\n    $pcs->{$pc} = 1;\n    push @k, $pc;\n  }\n  AddEntry($profile, (join \"\\n\", @k), $count);\n}\n\n##### Code to profile a server dynamically #####\n\nsub CheckSymbolPage {\n  my $url = SymbolPageURL();\n  my $command = ShellEscape(@URL_FETCHER, $url);\n  open(SYMBOL, \"$command |\") or error($command);\n  my $line = <SYMBOL>;\n  $line =~ s/\\r//g;         # turn windows-looking lines into unix-looking lines\n  close(SYMBOL);\n  unless (defined($line)) {\n    error(\"$url doesn't exist\\n\");\n  }\n\n  if ($line =~ /^num_symbols:\\s+(\\d+)$/) {\n    if ($1 == 0) {\n      error(\"Stripped binary. No symbols available.\\n\");\n    }\n  } else {\n    error(\"Failed to get the number of symbols from $url\\n\");\n  }\n}\n\nsub IsProfileURL {\n  my $profile_name = shift;\n  if (-f $profile_name) {\n    printf STDERR \"Using local file $profile_name.\\n\";\n    return 0;\n  }\n  return 1;\n}\n\nsub ParseProfileURL {\n  my $profile_name = shift;\n\n  if (!defined($profile_name) || $profile_name eq \"\") {\n    return ();\n  }\n\n  # Split profile URL - matches all non-empty strings, so no test.\n  $profile_name =~ m,^(https?://)?([^/]+)(.*?)(/|$PROFILES)?$,;\n\n  my $proto = $1 || \"http://\";\n  my $hostport = $2;\n  my $prefix = $3;\n  my $profile = $4 || \"/\";\n\n  my $host = $hostport;\n  $host =~ s/:.*//;\n\n  my $baseurl = \"$proto$hostport$prefix\";\n  return ($host, $baseurl, $profile);\n}\n\n# We fetch symbols from the first profile argument.\nsub SymbolPageURL {\n  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);\n  return \"$baseURL$SYMBOL_PAGE\";\n}\n\nsub FetchProgramName() {\n  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);\n  my $url = \"$baseURL$PROGRAM_NAME_PAGE\";\n  my $command_line = ShellEscape(@URL_FETCHER, $url);\n  open(CMDLINE, \"$command_line |\") or error($command_line);\n  my $cmdline = <CMDLINE>;\n  $cmdline =~ s/\\r//g;   # turn windows-looking lines into unix-looking lines\n  close(CMDLINE);\n  error(\"Failed to get program name from $url\\n\") unless defined($cmdline);\n  $cmdline =~ s/\\x00.+//;  # Remove argv[1] and latters.\n  $cmdline =~ s!\\n!!g;  # Remove LFs.\n  return $cmdline;\n}\n\n# Gee, curl's -L (--location) option isn't reliable at least\n# with its 7.12.3 version.  Curl will forget to post data if\n# there is a redirection.  This function is a workaround for\n# curl.  Redirection happens on borg hosts.\nsub ResolveRedirectionForCurl {\n  my $url = shift;\n  my $command_line = ShellEscape(@URL_FETCHER, \"--head\", $url);\n  open(CMDLINE, \"$command_line |\") or error($command_line);\n  while (<CMDLINE>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    if (/^Location: (.*)/) {\n      $url = $1;\n    }\n  }\n  close(CMDLINE);\n  return $url;\n}\n\n# Add a timeout flat to URL_FETCHER.  Returns a new list.\nsub AddFetchTimeout {\n  my $timeout = shift;\n  my @fetcher = @_;\n  if (defined($timeout)) {\n    if (join(\" \", @fetcher) =~ m/\\bcurl -s/) {\n      push(@fetcher, \"--max-time\", sprintf(\"%d\", $timeout));\n    } elsif (join(\" \", @fetcher) =~ m/\\brpcget\\b/) {\n      push(@fetcher, sprintf(\"--deadline=%d\", $timeout));\n    }\n  }\n  return @fetcher;\n}\n\n# Reads a symbol map from the file handle name given as $1, returning\n# the resulting symbol map.  Also processes variables relating to symbols.\n# Currently, the only variable processed is 'binary=<value>' which updates\n# $main::prog to have the correct program name.\nsub ReadSymbols {\n  my $in = shift;\n  my $map = {};\n  while (<$in>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    # Removes all the leading zeroes from the symbols, see comment below.\n    if (m/^0x0*([0-9a-f]+)\\s+(.+)/) {\n      $map->{$1} = $2;\n    } elsif (m/^---/) {\n      last;\n    } elsif (m/^([a-z][^=]*)=(.*)$/ ) {\n      my ($variable, $value) = ($1, $2);\n      for ($variable, $value) {\n        s/^\\s+//;\n        s/\\s+$//;\n      }\n      if ($variable eq \"binary\") {\n        if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {\n          printf STDERR (\"Warning: Mismatched binary name '%s', using '%s'.\\n\",\n                         $main::prog, $value);\n        }\n        $main::prog = $value;\n      } else {\n        printf STDERR (\"Ignoring unknown variable in symbols list: \" .\n            \"'%s' = '%s'\\n\", $variable, $value);\n      }\n    }\n  }\n  return $map;\n}\n\nsub URLEncode {\n  my $str = shift;\n  $str =~ s/([^A-Za-z0-9\\-_.!~*'()])/ sprintf \"%%%02x\", ord $1 /eg;\n  return $str;\n}\n\nsub AppendSymbolFilterParams {\n  my $url = shift;\n  my @params = ();\n  if ($main::opt_retain ne '') {\n    push(@params, sprintf(\"retain=%s\", URLEncode($main::opt_retain)));\n  }\n  if ($main::opt_exclude ne '') {\n    push(@params, sprintf(\"exclude=%s\", URLEncode($main::opt_exclude)));\n  }\n  if (scalar @params > 0) {\n    $url = sprintf(\"%s?%s\", $url, join(\"&\", @params));\n  }\n  return $url;\n}\n\n# Fetches and processes symbols to prepare them for use in the profile output\n# code.  If the optional 'symbol_map' arg is not given, fetches symbols from\n# $SYMBOL_PAGE for all PC values found in profile.  Otherwise, the raw symbols\n# are assumed to have already been fetched into 'symbol_map' and are simply\n# extracted and processed.\nsub FetchSymbols {\n  my $pcset = shift;\n  my $symbol_map = shift;\n\n  my %seen = ();\n  my @pcs = grep { !$seen{$_}++ } keys(%$pcset);  # uniq\n\n  if (!defined($symbol_map)) {\n    my $post_data = join(\"+\", sort((map {\"0x\" . \"$_\"} @pcs)));\n\n    open(POSTFILE, \">$main::tmpfile_sym\");\n    print POSTFILE $post_data;\n    close(POSTFILE);\n\n    my $url = SymbolPageURL();\n\n    my $command_line;\n    if (join(\" \", @URL_FETCHER) =~ m/\\bcurl -s/) {\n      $url = ResolveRedirectionForCurl($url);\n      $url = AppendSymbolFilterParams($url);\n      $command_line = ShellEscape(@URL_FETCHER, \"-d\", \"\\@$main::tmpfile_sym\",\n                                  $url);\n    } else {\n      $url = AppendSymbolFilterParams($url);\n      $command_line = (ShellEscape(@URL_FETCHER, \"--post\", $url)\n                       . \" < \" . ShellEscape($main::tmpfile_sym));\n    }\n    # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.\n    my $escaped_cppfilt = ShellEscape($obj_tool_map{\"c++filt\"});\n    open(SYMBOL, \"$command_line | $escaped_cppfilt |\") or error($command_line);\n    $symbol_map = ReadSymbols(*SYMBOL{IO});\n    close(SYMBOL);\n  }\n\n  my $symbols = {};\n  foreach my $pc (@pcs) {\n    my $fullname;\n    # For 64 bits binaries, symbols are extracted with 8 leading zeroes.\n    # Then /symbol reads the long symbols in as uint64, and outputs\n    # the result with a \"0x%08llx\" format which get rid of the zeroes.\n    # By removing all the leading zeroes in both $pc and the symbols from\n    # /symbol, the symbols match and are retrievable from the map.\n    my $shortpc = $pc;\n    $shortpc =~ s/^0*//;\n    # Each line may have a list of names, which includes the function\n    # and also other functions it has inlined.  They are separated (in\n    # PrintSymbolizedProfile), by --, which is illegal in function names.\n    my $fullnames;\n    if (defined($symbol_map->{$shortpc})) {\n      $fullnames = $symbol_map->{$shortpc};\n    } else {\n      $fullnames = \"0x\" . $pc;  # Just use addresses\n    }\n    my $sym = [];\n    $symbols->{$pc} = $sym;\n    foreach my $fullname (split(\"--\", $fullnames)) {\n      my $name = ShortFunctionName($fullname);\n      push(@{$sym}, $name, \"?\", $fullname);\n    }\n  }\n  return $symbols;\n}\n\nsub BaseName {\n  my $file_name = shift;\n  $file_name =~ s!^.*/!!;  # Remove directory name\n  return $file_name;\n}\n\nsub MakeProfileBaseName {\n  my ($binary_name, $profile_name) = @_;\n  my ($host, $baseURL, $path) = ParseProfileURL($profile_name);\n  my $binary_shortname = BaseName($binary_name);\n  return sprintf(\"%s.%s.%s\",\n                 $binary_shortname, $main::op_time, $host);\n}\n\nsub FetchDynamicProfile {\n  my $binary_name = shift;\n  my $profile_name = shift;\n  my $fetch_name_only = shift;\n  my $encourage_patience = shift;\n\n  if (!IsProfileURL($profile_name)) {\n    return $profile_name;\n  } else {\n    my ($host, $baseURL, $path) = ParseProfileURL($profile_name);\n    if ($path eq \"\" || $path eq \"/\") {\n      # Missing type specifier defaults to cpu-profile\n      $path = $PROFILE_PAGE;\n    }\n\n    my $profile_file = MakeProfileBaseName($binary_name, $profile_name);\n\n    my $url = \"$baseURL$path\";\n    my $fetch_timeout = undef;\n    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE/) {\n      if ($path =~ m/[?]/) {\n        $url .= \"&\";\n      } else {\n        $url .= \"?\";\n      }\n      $url .= sprintf(\"seconds=%d\", $main::opt_seconds);\n      $fetch_timeout = $main::opt_seconds * 1.01 + 60;\n      # Set $profile_type for consumption by PrintSymbolizedProfile.\n      $main::profile_type = 'cpu';\n    } else {\n      # For non-CPU profiles, we add a type-extension to\n      # the target profile file name.\n      my $suffix = $path;\n      $suffix =~ s,/,.,g;\n      $profile_file .= $suffix;\n      # Set $profile_type for consumption by PrintSymbolizedProfile.\n      if ($path =~ m/$HEAP_PAGE/) {\n        $main::profile_type = 'heap';\n      } elsif ($path =~ m/$GROWTH_PAGE/) {\n        $main::profile_type = 'growth';\n      } elsif ($path =~ m/$CONTENTION_PAGE/) {\n        $main::profile_type = 'contention';\n      }\n    }\n\n    my $profile_dir = $ENV{\"JEPROF_TMPDIR\"} || ($ENV{HOME} . \"/jeprof\");\n    if (! -d $profile_dir) {\n      mkdir($profile_dir)\n          || die(\"Unable to create profile directory $profile_dir: $!\\n\");\n    }\n    my $tmp_profile = \"$profile_dir/.tmp.$profile_file\";\n    my $real_profile = \"$profile_dir/$profile_file\";\n\n    if ($fetch_name_only > 0) {\n      return $real_profile;\n    }\n\n    my @fetcher = AddFetchTimeout($fetch_timeout, @URL_FETCHER);\n    my $cmd = ShellEscape(@fetcher, $url) . \" > \" . ShellEscape($tmp_profile);\n    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){\n      print STDERR \"Gathering CPU profile from $url for $main::opt_seconds seconds to\\n  ${real_profile}\\n\";\n      if ($encourage_patience) {\n        print STDERR \"Be patient...\\n\";\n      }\n    } else {\n      print STDERR \"Fetching $path profile from $url to\\n  ${real_profile}\\n\";\n    }\n\n    (system($cmd) == 0) || error(\"Failed to get profile: $cmd: $!\\n\");\n    (system(\"mv\", $tmp_profile, $real_profile) == 0) || error(\"Unable to rename profile\\n\");\n    print STDERR \"Wrote profile to $real_profile\\n\";\n    $main::collected_profile = $real_profile;\n    return $main::collected_profile;\n  }\n}\n\n# Collect profiles in parallel\nsub FetchDynamicProfiles {\n  my $items = scalar(@main::pfile_args);\n  my $levels = log($items) / log(2);\n\n  if ($items == 1) {\n    $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);\n  } else {\n    # math rounding issues\n    if ((2 ** $levels) < $items) {\n     $levels++;\n    }\n    my $count = scalar(@main::pfile_args);\n    for (my $i = 0; $i < $count; $i++) {\n      $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);\n    }\n    print STDERR \"Fetching $count profiles, Be patient...\\n\";\n    FetchDynamicProfilesRecurse($levels, 0, 0);\n    $main::collected_profile = join(\" \\\\\\n    \", @main::profile_files);\n  }\n}\n\n# Recursively fork a process to get enough processes\n# collecting profiles\nsub FetchDynamicProfilesRecurse {\n  my $maxlevel = shift;\n  my $level = shift;\n  my $position = shift;\n\n  if (my $pid = fork()) {\n    $position = 0 | ($position << 1);\n    TryCollectProfile($maxlevel, $level, $position);\n    wait;\n  } else {\n    $position = 1 | ($position << 1);\n    TryCollectProfile($maxlevel, $level, $position);\n    cleanup();\n    exit(0);\n  }\n}\n\n# Collect a single profile\nsub TryCollectProfile {\n  my $maxlevel = shift;\n  my $level = shift;\n  my $position = shift;\n\n  if ($level >= ($maxlevel - 1)) {\n    if ($position < scalar(@main::pfile_args)) {\n      FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);\n    }\n  } else {\n    FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);\n  }\n}\n\n##### Parsing code #####\n\n# Provide a small streaming-read module to handle very large\n# cpu-profile files.  Stream in chunks along a sliding window.\n# Provides an interface to get one 'slot', correctly handling\n# endian-ness differences.  A slot is one 32-bit or 64-bit word\n# (depending on the input profile).  We tell endianness and bit-size\n# for the profile by looking at the first 8 bytes: in cpu profiles,\n# the second slot is always 3 (we'll accept anything that's not 0).\nBEGIN {\n  package CpuProfileStream;\n\n  sub new {\n    my ($class, $file, $fname) = @_;\n    my $self = { file        => $file,\n                 base        => 0,\n                 stride      => 512 * 1024,   # must be a multiple of bitsize/8\n                 slots       => [],\n                 unpack_code => \"\",           # N for big-endian, V for little\n                 perl_is_64bit => 1,          # matters if profile is 64-bit\n    };\n    bless $self, $class;\n    # Let unittests adjust the stride\n    if ($main::opt_test_stride > 0) {\n      $self->{stride} = $main::opt_test_stride;\n    }\n    # Read the first two slots to figure out bitsize and endianness.\n    my $slots = $self->{slots};\n    my $str;\n    read($self->{file}, $str, 8);\n    # Set the global $address_length based on what we see here.\n    # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).\n    $address_length = ($str eq (chr(0)x8)) ? 16 : 8;\n    if ($address_length == 8) {\n      if (substr($str, 6, 2) eq chr(0)x2) {\n        $self->{unpack_code} = 'V';  # Little-endian.\n      } elsif (substr($str, 4, 2) eq chr(0)x2) {\n        $self->{unpack_code} = 'N';  # Big-endian\n      } else {\n        ::error(\"$fname: header size >= 2**16\\n\");\n      }\n      @$slots = unpack($self->{unpack_code} . \"*\", $str);\n    } else {\n      # If we're a 64-bit profile, check if we're a 64-bit-capable\n      # perl.  Otherwise, each slot will be represented as a float\n      # instead of an int64, losing precision and making all the\n      # 64-bit addresses wrong.  We won't complain yet, but will\n      # later if we ever see a value that doesn't fit in 32 bits.\n      my $has_q = 0;\n      eval { $has_q = pack(\"Q\", \"1\") ? 1 : 1; };\n      if (!$has_q) {\n        $self->{perl_is_64bit} = 0;\n      }\n      read($self->{file}, $str, 8);\n      if (substr($str, 4, 4) eq chr(0)x4) {\n        # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.\n        $self->{unpack_code} = 'V';  # Little-endian.\n      } elsif (substr($str, 0, 4) eq chr(0)x4) {\n        $self->{unpack_code} = 'N';  # Big-endian\n      } else {\n        ::error(\"$fname: header size >= 2**32\\n\");\n      }\n      my @pair = unpack($self->{unpack_code} . \"*\", $str);\n      # Since we know one of the pair is 0, it's fine to just add them.\n      @$slots = (0, $pair[0] + $pair[1]);\n    }\n    return $self;\n  }\n\n  # Load more data when we access slots->get(X) which is not yet in memory.\n  sub overflow {\n    my ($self) = @_;\n    my $slots = $self->{slots};\n    $self->{base} += $#$slots + 1;   # skip over data we're replacing\n    my $str;\n    read($self->{file}, $str, $self->{stride});\n    if ($address_length == 8) {      # the 32-bit case\n      # This is the easy case: unpack provides 32-bit unpacking primitives.\n      @$slots = unpack($self->{unpack_code} . \"*\", $str);\n    } else {\n      # We need to unpack 32 bits at a time and combine.\n      my @b32_values = unpack($self->{unpack_code} . \"*\", $str);\n      my @b64_values = ();\n      for (my $i = 0; $i < $#b32_values; $i += 2) {\n        # TODO(csilvers): if this is a 32-bit perl, the math below\n        #    could end up in a too-large int, which perl will promote\n        #    to a double, losing necessary precision.  Deal with that.\n        #    Right now, we just die.\n        my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]);\n        if ($self->{unpack_code} eq 'N') {    # big-endian\n          ($lo, $hi) = ($hi, $lo);\n        }\n        my $value = $lo + $hi * (2**32);\n        if (!$self->{perl_is_64bit} &&   # check value is exactly represented\n            (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) {\n          ::error(\"Need a 64-bit perl to process this 64-bit profile.\\n\");\n        }\n        push(@b64_values, $value);\n      }\n      @$slots = @b64_values;\n    }\n  }\n\n  # Access the i-th long in the file (logically), or -1 at EOF.\n  sub get {\n    my ($self, $idx) = @_;\n    my $slots = $self->{slots};\n    while ($#$slots >= 0) {\n      if ($idx < $self->{base}) {\n        # The only time we expect a reference to $slots[$i - something]\n        # after referencing $slots[$i] is reading the very first header.\n        # Since $stride > |header|, that shouldn't cause any lookback\n        # errors.  And everything after the header is sequential.\n        print STDERR \"Unexpected look-back reading CPU profile\";\n        return -1;   # shrug, don't know what better to return\n      } elsif ($idx > $self->{base} + $#$slots) {\n        $self->overflow();\n      } else {\n        return $slots->[$idx - $self->{base}];\n      }\n    }\n    # If we get here, $slots is [], which means we've reached EOF\n    return -1;  # unique since slots is supposed to hold unsigned numbers\n  }\n}\n\n# Reads the top, 'header' section of a profile, and returns the last\n# line of the header, commonly called a 'header line'.  The header\n# section of a profile consists of zero or more 'command' lines that\n# are instructions to jeprof, which jeprof executes when reading the\n# header.  All 'command' lines start with a %.  After the command\n# lines is the 'header line', which is a profile-specific line that\n# indicates what type of profile it is, and perhaps other global\n# information about the profile.  For instance, here's a header line\n# for a heap profile:\n#   heap profile:     53:    38236 [  5525:  1284029] @ heapprofile\n# For historical reasons, the CPU profile does not contain a text-\n# readable header line.  If the profile looks like a CPU profile,\n# this function returns \"\".  If no header line could be found, this\n# function returns undef.\n#\n# The following commands are recognized:\n#   %warn -- emit the rest of this line to stderr, prefixed by 'WARNING:'\n#\n# The input file should be in binmode.\nsub ReadProfileHeader {\n  local *PROFILE = shift;\n  my $firstchar = \"\";\n  my $line = \"\";\n  read(PROFILE, $firstchar, 1);\n  seek(PROFILE, -1, 1);                    # unread the firstchar\n  if ($firstchar !~ /[[:print:]]/) {       # is not a text character\n    return \"\";\n  }\n  while (defined($line = <PROFILE>)) {\n    $line =~ s/\\r//g;   # turn windows-looking lines into unix-looking lines\n    if ($line =~ /^%warn\\s+(.*)/) {        # 'warn' command\n      # Note this matches both '%warn blah\\n' and '%warn\\n'.\n      print STDERR \"WARNING: $1\\n\";        # print the rest of the line\n    } elsif ($line =~ /^%/) {\n      print STDERR \"Ignoring unknown command from profile header: $line\";\n    } else {\n      # End of commands, must be the header line.\n      return $line;\n    }\n  }\n  return undef;     # got to EOF without seeing a header line\n}\n\nsub IsSymbolizedProfileFile {\n  my $file_name = shift;\n  if (!(-e $file_name) || !(-r $file_name)) {\n    return 0;\n  }\n  # Check if the file contains a symbol-section marker.\n  open(TFILE, \"<$file_name\");\n  binmode TFILE;\n  my $firstline = ReadProfileHeader(*TFILE);\n  close(TFILE);\n  if (!$firstline) {\n    return 0;\n  }\n  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $symbol_marker = $&;\n  return $firstline =~ /^--- *$symbol_marker/;\n}\n\n# Parse profile generated by common/profiler.cc and return a reference\n# to a map:\n#      $result->{version}     Version number of profile file\n#      $result->{period}      Sampling period (in microseconds)\n#      $result->{profile}     Profile object\n#      $result->{threads}     Map of thread IDs to profile objects\n#      $result->{map}         Memory map info from profile\n#      $result->{pcs}         Hash of all PC values seen, key is hex address\nsub ReadProfile {\n  my $prog = shift;\n  my $fname = shift;\n  my $result;            # return value\n\n  $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $contention_marker = $&;\n  $GROWTH_PAGE  =~ m,[^/]+$,;    # matches everything after the last slash\n  my $growth_marker = $&;\n  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $symbol_marker = $&;\n  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $profile_marker = $&;\n  $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash\n  my $heap_marker = $&;\n\n  # Look at first line to see if it is a heap or a CPU profile.\n  # CPU profile may start with no header at all, and just binary data\n  # (starting with \\0\\0\\0\\0) -- in that case, don't try to read the\n  # whole firstline, since it may be gigabytes(!) of data.\n  open(PROFILE, \"<$fname\") || error(\"$fname: $!\\n\");\n  binmode PROFILE;      # New perls do UTF-8 processing\n  my $header = ReadProfileHeader(*PROFILE);\n  if (!defined($header)) {   # means \"at EOF\"\n    error(\"Profile is empty.\\n\");\n  }\n\n  my $symbols;\n  if ($header =~ m/^--- *$symbol_marker/o) {\n    # Verify that the user asked for a symbolized profile\n    if (!$main::use_symbolized_profile) {\n      # we have both a binary and symbolized profiles, abort\n      error(\"FATAL ERROR: Symbolized profile\\n   $fname\\ncannot be used with \" .\n            \"a binary arg. Try again without passing\\n   $prog\\n\");\n    }\n    # Read the symbol section of the symbolized profile file.\n    $symbols = ReadSymbols(*PROFILE{IO});\n    # Read the next line to get the header for the remaining profile.\n    $header = ReadProfileHeader(*PROFILE) || \"\";\n  }\n\n  if ($header =~ m/^--- *($heap_marker|$growth_marker)/o) {\n    # Skip \"--- ...\" line for profile types that have their own headers.\n    $header = ReadProfileHeader(*PROFILE) || \"\";\n  }\n\n  $main::profile_type = '';\n\n  if ($header =~ m/^heap profile:.*$growth_marker/o) {\n    $main::profile_type = 'growth';\n    $result =  ReadHeapProfile($prog, *PROFILE, $header);\n  } elsif ($header =~ m/^heap profile:/) {\n    $main::profile_type = 'heap';\n    $result =  ReadHeapProfile($prog, *PROFILE, $header);\n  } elsif ($header =~ m/^heap/) {\n    $main::profile_type = 'heap';\n    $result = ReadThreadedHeapProfile($prog, $fname, $header);\n  } elsif ($header =~ m/^--- *$contention_marker/o) {\n    $main::profile_type = 'contention';\n    $result = ReadSynchProfile($prog, *PROFILE);\n  } elsif ($header =~ m/^--- *Stacks:/) {\n    print STDERR\n      \"Old format contention profile: mistakenly reports \" .\n      \"condition variable signals as lock contentions.\\n\";\n    $main::profile_type = 'contention';\n    $result = ReadSynchProfile($prog, *PROFILE);\n  } elsif ($header =~ m/^--- *$profile_marker/) {\n    # the binary cpu profile data starts immediately after this line\n    $main::profile_type = 'cpu';\n    $result = ReadCPUProfile($prog, $fname, *PROFILE);\n  } else {\n    if (defined($symbols)) {\n      # a symbolized profile contains a format we don't recognize, bail out\n      error(\"$fname: Cannot recognize profile section after symbols.\\n\");\n    }\n    # no ascii header present -- must be a CPU profile\n    $main::profile_type = 'cpu';\n    $result = ReadCPUProfile($prog, $fname, *PROFILE);\n  }\n\n  close(PROFILE);\n\n  # if we got symbols along with the profile, return those as well\n  if (defined($symbols)) {\n    $result->{symbols} = $symbols;\n  }\n\n  return $result;\n}\n\n# Subtract one from caller pc so we map back to call instr.\n# However, don't do this if we're reading a symbolized profile\n# file, in which case the subtract-one was done when the file\n# was written.\n#\n# We apply the same logic to all readers, though ReadCPUProfile uses an\n# independent implementation.\nsub FixCallerAddresses {\n  my $stack = shift;\n  # --raw/http: Always subtract one from pc's, because PrintSymbolizedProfile()\n  # dumps unadjusted profiles.\n  {\n    $stack =~ /(\\s)/;\n    my $delimiter = $1;\n    my @addrs = split(' ', $stack);\n    my @fixedaddrs;\n    $#fixedaddrs = $#addrs;\n    if ($#addrs >= 0) {\n      $fixedaddrs[0] = $addrs[0];\n    }\n    for (my $i = 1; $i <= $#addrs; $i++) {\n      $fixedaddrs[$i] = AddressSub($addrs[$i], \"0x1\");\n    }\n    return join $delimiter, @fixedaddrs;\n  }\n}\n\n# CPU profile reader\nsub ReadCPUProfile {\n  my $prog = shift;\n  my $fname = shift;       # just used for logging\n  local *PROFILE = shift;\n  my $version;\n  my $period;\n  my $i;\n  my $profile = {};\n  my $pcs = {};\n\n  # Parse string into array of slots.\n  my $slots = CpuProfileStream->new(*PROFILE, $fname);\n\n  # Read header.  The current header version is a 5-element structure\n  # containing:\n  #   0: header count (always 0)\n  #   1: header \"words\" (after this one: 3)\n  #   2: format version (0)\n  #   3: sampling period (usec)\n  #   4: unused padding (always 0)\n  if ($slots->get(0) != 0 ) {\n    error(\"$fname: not a profile file, or old format profile file\\n\");\n  }\n  $i = 2 + $slots->get(1);\n  $version = $slots->get(2);\n  $period = $slots->get(3);\n  # Do some sanity checking on these header values.\n  if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {\n    error(\"$fname: not a profile file, or corrupted profile file\\n\");\n  }\n\n  # Parse profile\n  while ($slots->get($i) != -1) {\n    my $n = $slots->get($i++);\n    my $d = $slots->get($i++);\n    if ($d > (2**16)) {  # TODO(csilvers): what's a reasonable max-stack-depth?\n      my $addr = sprintf(\"0%o\", $i * ($address_length == 8 ? 4 : 8));\n      print STDERR \"At index $i (address $addr):\\n\";\n      error(\"$fname: stack trace depth >= 2**32\\n\");\n    }\n    if ($slots->get($i) == 0) {\n      # End of profile data marker\n      $i += $d;\n      last;\n    }\n\n    # Make key out of the stack entries\n    my @k = ();\n    for (my $j = 0; $j < $d; $j++) {\n      my $pc = $slots->get($i+$j);\n      # Subtract one from caller pc so we map back to call instr.\n      $pc--;\n      $pc = sprintf(\"%0*x\", $address_length, $pc);\n      $pcs->{$pc} = 1;\n      push @k, $pc;\n    }\n\n    AddEntry($profile, (join \"\\n\", @k), $n);\n    $i += $d;\n  }\n\n  # Parse map\n  my $map = '';\n  seek(PROFILE, $i * 4, 0);\n  read(PROFILE, $map, (stat PROFILE)[7]);\n\n  my $r = {};\n  $r->{version} = $version;\n  $r->{period} = $period;\n  $r->{profile} = $profile;\n  $r->{libs} = ParseLibraries($prog, $map, $pcs);\n  $r->{pcs} = $pcs;\n\n  return $r;\n}\n\nsub HeapProfileIndex {\n  my $index = 1;\n  if ($main::opt_inuse_space) {\n    $index = 1;\n  } elsif ($main::opt_inuse_objects) {\n    $index = 0;\n  } elsif ($main::opt_alloc_space) {\n    $index = 3;\n  } elsif ($main::opt_alloc_objects) {\n    $index = 2;\n  }\n  return $index;\n}\n\nsub ReadMappedLibraries {\n  my $fh = shift;\n  my $map = \"\";\n  # Read the /proc/self/maps data\n  while (<$fh>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    $map .= $_;\n  }\n  return $map;\n}\n\nsub ReadMemoryMap {\n  my $fh = shift;\n  my $map = \"\";\n  # Read /proc/self/maps data as formatted by DumpAddressMap()\n  my $buildvar = \"\";\n  while (<PROFILE>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    # Parse \"build=<dir>\" specification if supplied\n    if (m/^\\s*build=(.*)\\n/) {\n      $buildvar = $1;\n    }\n\n    # Expand \"$build\" variable if available\n    $_ =~ s/\\$build\\b/$buildvar/g;\n\n    $map .= $_;\n  }\n  return $map;\n}\n\nsub AdjustSamples {\n  my ($sample_adjustment, $sampling_algorithm, $n1, $s1, $n2, $s2) = @_;\n  if ($sample_adjustment) {\n    if ($sampling_algorithm == 2) {\n      # Remote-heap version 2\n      # The sampling frequency is the rate of a Poisson process.\n      # This means that the probability of sampling an allocation of\n      # size X with sampling rate Y is 1 - exp(-X/Y)\n      if ($n1 != 0) {\n        my $ratio = (($s1*1.0)/$n1)/($sample_adjustment);\n        my $scale_factor = 1/(1 - exp(-$ratio));\n        $n1 *= $scale_factor;\n        $s1 *= $scale_factor;\n      }\n      if ($n2 != 0) {\n        my $ratio = (($s2*1.0)/$n2)/($sample_adjustment);\n        my $scale_factor = 1/(1 - exp(-$ratio));\n        $n2 *= $scale_factor;\n        $s2 *= $scale_factor;\n      }\n    } else {\n      # Remote-heap version 1\n      my $ratio;\n      $ratio = (($s1*1.0)/$n1)/($sample_adjustment);\n      if ($ratio < 1) {\n        $n1 /= $ratio;\n        $s1 /= $ratio;\n      }\n      $ratio = (($s2*1.0)/$n2)/($sample_adjustment);\n      if ($ratio < 1) {\n        $n2 /= $ratio;\n        $s2 /= $ratio;\n      }\n    }\n  }\n  return ($n1, $s1, $n2, $s2);\n}\n\nsub ReadHeapProfile {\n  my $prog = shift;\n  local *PROFILE = shift;\n  my $header = shift;\n\n  my $index = HeapProfileIndex();\n\n  # Find the type of this profile.  The header line looks like:\n  #    heap profile:   1246:  8800744 [  1246:  8800744] @ <heap-url>/266053\n  # There are two pairs <count: size>, the first inuse objects/space, and the\n  # second allocated objects/space.  This is followed optionally by a profile\n  # type, and if that is present, optionally by a sampling frequency.\n  # For remote heap profiles (v1):\n  # The interpretation of the sampling frequency is that the profiler, for\n  # each sample, calculates a uniformly distributed random integer less than\n  # the given value, and records the next sample after that many bytes have\n  # been allocated.  Therefore, the expected sample interval is half of the\n  # given frequency.  By default, if not specified, the expected sample\n  # interval is 128KB.  Only remote-heap-page profiles are adjusted for\n  # sample size.\n  # For remote heap profiles (v2):\n  # The sampling frequency is the rate of a Poisson process. This means that\n  # the probability of sampling an allocation of size X with sampling rate Y\n  # is 1 - exp(-X/Y)\n  # For version 2, a typical header line might look like this:\n  # heap profile:   1922: 127792360 [  1922: 127792360] @ <heap-url>_v2/524288\n  # the trailing number (524288) is the sampling rate. (Version 1 showed\n  # double the 'rate' here)\n  my $sampling_algorithm = 0;\n  my $sample_adjustment = 0;\n  chomp($header);\n  my $type = \"unknown\";\n  if ($header =~ m\"^heap profile:\\s*(\\d+):\\s+(\\d+)\\s+\\[\\s*(\\d+):\\s+(\\d+)\\](\\s*@\\s*([^/]*)(/(\\d+))?)?\") {\n    if (defined($6) && ($6 ne '')) {\n      $type = $6;\n      my $sample_period = $8;\n      # $type is \"heapprofile\" for profiles generated by the\n      # heap-profiler, and either \"heap\" or \"heap_v2\" for profiles\n      # generated by sampling directly within tcmalloc.  It can also\n      # be \"growth\" for heap-growth profiles.  The first is typically\n      # found for profiles generated locally, and the others for\n      # remote profiles.\n      if (($type eq \"heapprofile\") || ($type !~ /heap/) ) {\n        # No need to adjust for the sampling rate with heap-profiler-derived data\n        $sampling_algorithm = 0;\n      } elsif ($type =~ /_v2/) {\n        $sampling_algorithm = 2;     # version 2 sampling\n        if (defined($sample_period) && ($sample_period ne '')) {\n          $sample_adjustment = int($sample_period);\n        }\n      } else {\n        $sampling_algorithm = 1;     # version 1 sampling\n        if (defined($sample_period) && ($sample_period ne '')) {\n          $sample_adjustment = int($sample_period)/2;\n        }\n      }\n    } else {\n      # We detect whether or not this is a remote-heap profile by checking\n      # that the total-allocated stats ($n2,$s2) are exactly the\n      # same as the in-use stats ($n1,$s1).  It is remotely conceivable\n      # that a non-remote-heap profile may pass this check, but it is hard\n      # to imagine how that could happen.\n      # In this case it's so old it's guaranteed to be remote-heap version 1.\n      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);\n      if (($n1 == $n2) && ($s1 == $s2)) {\n        # This is likely to be a remote-heap based sample profile\n        $sampling_algorithm = 1;\n      }\n    }\n  }\n\n  if ($sampling_algorithm > 0) {\n    # For remote-heap generated profiles, adjust the counts and sizes to\n    # account for the sample rate (we sample once every 128KB by default).\n    if ($sample_adjustment == 0) {\n      # Turn on profile adjustment.\n      $sample_adjustment = 128*1024;\n      print STDERR \"Adjusting heap profiles for 1-in-128KB sampling rate\\n\";\n    } else {\n      printf STDERR (\"Adjusting heap profiles for 1-in-%d sampling rate\\n\",\n                     $sample_adjustment);\n    }\n    if ($sampling_algorithm > 1) {\n      # We don't bother printing anything for the original version (version 1)\n      printf STDERR \"Heap version $sampling_algorithm\\n\";\n    }\n  }\n\n  my $profile = {};\n  my $pcs = {};\n  my $map = \"\";\n\n  while (<PROFILE>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    if (/^MAPPED_LIBRARIES:/) {\n      $map .= ReadMappedLibraries(*PROFILE);\n      last;\n    }\n\n    if (/^--- Memory map:/) {\n      $map .= ReadMemoryMap(*PROFILE);\n      last;\n    }\n\n    # Read entry of the form:\n    #  <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an\n    s/^\\s*//;\n    s/\\s*$//;\n    if (m/^\\s*(\\d+):\\s+(\\d+)\\s+\\[\\s*(\\d+):\\s+(\\d+)\\]\\s+@\\s+(.*)$/) {\n      my $stack = $5;\n      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);\n      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,\n                                 $n1, $s1, $n2, $s2);\n      AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);\n    }\n  }\n\n  my $r = {};\n  $r->{version} = \"heap\";\n  $r->{period} = 1;\n  $r->{profile} = $profile;\n  $r->{libs} = ParseLibraries($prog, $map, $pcs);\n  $r->{pcs} = $pcs;\n  return $r;\n}\n\nsub ReadThreadedHeapProfile {\n  my ($prog, $fname, $header) = @_;\n\n  my $index = HeapProfileIndex();\n  my $sampling_algorithm = 0;\n  my $sample_adjustment = 0;\n  chomp($header);\n  my $type = \"unknown\";\n  # Assuming a very specific type of header for now.\n  if ($header =~ m\"^heap_v2/(\\d+)\") {\n    $type = \"_v2\";\n    $sampling_algorithm = 2;\n    $sample_adjustment = int($1);\n  }\n  if ($type ne \"_v2\" || !defined($sample_adjustment)) {\n    die \"Threaded heap profiles require v2 sampling with a sample rate\\n\";\n  }\n\n  my $profile = {};\n  my $thread_profiles = {};\n  my $pcs = {};\n  my $map = \"\";\n  my $stack = \"\";\n\n  while (<PROFILE>) {\n    s/\\r//g;\n    if (/^MAPPED_LIBRARIES:/) {\n      $map .= ReadMappedLibraries(*PROFILE);\n      last;\n    }\n\n    if (/^--- Memory map:/) {\n      $map .= ReadMemoryMap(*PROFILE);\n      last;\n    }\n\n    # Read entry of the form:\n    # @ a1 a2 ... an\n    #   t*: <count1>: <bytes1> [<count2>: <bytes2>]\n    #   t1: <count1>: <bytes1> [<count2>: <bytes2>]\n    #     ...\n    #   tn: <count1>: <bytes1> [<count2>: <bytes2>]\n    s/^\\s*//;\n    s/\\s*$//;\n    if (m/^@\\s+(.*)$/) {\n      $stack = $1;\n    } elsif (m/^\\s*(t(\\*|\\d+)):\\s+(\\d+):\\s+(\\d+)\\s+\\[\\s*(\\d+):\\s+(\\d+)\\]$/) {\n      if ($stack eq \"\") {\n        # Still in the header, so this is just a per-thread summary.\n        next;\n      }\n      my $thread = $2;\n      my ($n1, $s1, $n2, $s2) = ($3, $4, $5, $6);\n      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,\n                                 $n1, $s1, $n2, $s2);\n      if ($thread eq \"*\") {\n        AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);\n      } else {\n        if (!exists($thread_profiles->{$thread})) {\n          $thread_profiles->{$thread} = {};\n        }\n        AddEntries($thread_profiles->{$thread}, $pcs,\n                   FixCallerAddresses($stack), $counts[$index]);\n      }\n    }\n  }\n\n  my $r = {};\n  $r->{version} = \"heap\";\n  $r->{period} = 1;\n  $r->{profile} = $profile;\n  $r->{threads} = $thread_profiles;\n  $r->{libs} = ParseLibraries($prog, $map, $pcs);\n  $r->{pcs} = $pcs;\n  return $r;\n}\n\nsub ReadSynchProfile {\n  my $prog = shift;\n  local *PROFILE = shift;\n  my $header = shift;\n\n  my $map = '';\n  my $profile = {};\n  my $pcs = {};\n  my $sampling_period = 1;\n  my $cyclespernanosec = 2.8;   # Default assumption for old binaries\n  my $seen_clockrate = 0;\n  my $line;\n\n  my $index = 0;\n  if ($main::opt_total_delay) {\n    $index = 0;\n  } elsif ($main::opt_contentions) {\n    $index = 1;\n  } elsif ($main::opt_mean_delay) {\n    $index = 2;\n  }\n\n  while ( $line = <PROFILE> ) {\n    $line =~ s/\\r//g;      # turn windows-looking lines into unix-looking lines\n    if ( $line =~ /^\\s*(\\d+)\\s+(\\d+) \\@\\s*(.*?)\\s*$/ ) {\n      my ($cycles, $count, $stack) = ($1, $2, $3);\n\n      # Convert cycles to nanoseconds\n      $cycles /= $cyclespernanosec;\n\n      # Adjust for sampling done by application\n      $cycles *= $sampling_period;\n      $count *= $sampling_period;\n\n      my @values = ($cycles, $count, $cycles / $count);\n      AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);\n\n    } elsif ( $line =~ /^(slow release).*thread \\d+  \\@\\s*(.*?)\\s*$/ ||\n              $line =~ /^\\s*(\\d+) \\@\\s*(.*?)\\s*$/ ) {\n      my ($cycles, $stack) = ($1, $2);\n      if ($cycles !~ /^\\d+$/) {\n        next;\n      }\n\n      # Convert cycles to nanoseconds\n      $cycles /= $cyclespernanosec;\n\n      # Adjust for sampling done by application\n      $cycles *= $sampling_period;\n\n      AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);\n\n    } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {\n      my ($variable, $value) = ($1,$2);\n      for ($variable, $value) {\n        s/^\\s+//;\n        s/\\s+$//;\n      }\n      if ($variable eq \"cycles/second\") {\n        $cyclespernanosec = $value / 1e9;\n        $seen_clockrate = 1;\n      } elsif ($variable eq \"sampling period\") {\n        $sampling_period = $value;\n      } elsif ($variable eq \"ms since reset\") {\n        # Currently nothing is done with this value in jeprof\n        # So we just silently ignore it for now\n      } elsif ($variable eq \"discarded samples\") {\n        # Currently nothing is done with this value in jeprof\n        # So we just silently ignore it for now\n      } else {\n        printf STDERR (\"Ignoring unnknown variable in /contention output: \" .\n                       \"'%s' = '%s'\\n\",$variable,$value);\n      }\n    } else {\n      # Memory map entry\n      $map .= $line;\n    }\n  }\n\n  if (!$seen_clockrate) {\n    printf STDERR (\"No cycles/second entry in profile; Guessing %.1f GHz\\n\",\n                   $cyclespernanosec);\n  }\n\n  my $r = {};\n  $r->{version} = 0;\n  $r->{period} = $sampling_period;\n  $r->{profile} = $profile;\n  $r->{libs} = ParseLibraries($prog, $map, $pcs);\n  $r->{pcs} = $pcs;\n  return $r;\n}\n\n# Given a hex value in the form \"0x1abcd\" or \"1abcd\", return either\n# \"0001abcd\" or \"000000000001abcd\", depending on the current (global)\n# address length.\nsub HexExtend {\n  my $addr = shift;\n\n  $addr =~ s/^(0x)?0*//;\n  my $zeros_needed = $address_length - length($addr);\n  if ($zeros_needed < 0) {\n    printf STDERR \"Warning: address $addr is longer than address length $address_length\\n\";\n    return $addr;\n  }\n  return (\"0\" x $zeros_needed) . $addr;\n}\n\n##### Symbol extraction #####\n\n# Aggressively search the lib_prefix values for the given library\n# If all else fails, just return the name of the library unmodified.\n# If the lib_prefix is \"/my/path,/other/path\" and $file is \"/lib/dir/mylib.so\"\n# it will search the following locations in this order, until it finds a file:\n#   /my/path/lib/dir/mylib.so\n#   /other/path/lib/dir/mylib.so\n#   /my/path/dir/mylib.so\n#   /other/path/dir/mylib.so\n#   /my/path/mylib.so\n#   /other/path/mylib.so\n#   /lib/dir/mylib.so              (returned as last resort)\nsub FindLibrary {\n  my $file = shift;\n  my $suffix = $file;\n\n  # Search for the library as described above\n  do {\n    foreach my $prefix (@prefix_list) {\n      my $fullpath = $prefix . $suffix;\n      if (-e $fullpath) {\n        return $fullpath;\n      }\n    }\n  } while ($suffix =~ s|^/[^/]+/|/|);\n  return $file;\n}\n\n# Return path to library with debugging symbols.\n# For libc libraries, the copy in /usr/lib/debug contains debugging symbols\nsub DebuggingLibrary {\n  my $file = shift;\n  if ($file =~ m|^/|) {\n      if (-f \"/usr/lib/debug$file\") {\n        return \"/usr/lib/debug$file\";\n      } elsif (-f \"/usr/lib/debug$file.debug\") {\n        return \"/usr/lib/debug$file.debug\";\n      }\n  }\n  return undef;\n}\n\n# Parse text section header of a library using objdump\nsub ParseTextSectionHeaderFromObjdump {\n  my $lib = shift;\n\n  my $size = undef;\n  my $vma;\n  my $file_offset;\n  # Get objdump output from the library file to figure out how to\n  # map between mapped addresses and addresses in the library.\n  my $cmd = ShellEscape($obj_tool_map{\"objdump\"}, \"-h\", $lib);\n  open(OBJDUMP, \"$cmd |\") || error(\"$cmd: $!\\n\");\n  while (<OBJDUMP>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    # Idx Name          Size      VMA       LMA       File off  Algn\n    #  10 .text         00104b2c  420156f0  420156f0  000156f0  2**4\n    # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file\n    # offset may still be 8.  But AddressSub below will still handle that.\n    my @x = split;\n    if (($#x >= 6) && ($x[1] eq '.text')) {\n      $size = $x[2];\n      $vma = $x[3];\n      $file_offset = $x[5];\n      last;\n    }\n  }\n  close(OBJDUMP);\n\n  if (!defined($size)) {\n    return undef;\n  }\n\n  my $r = {};\n  $r->{size} = $size;\n  $r->{vma} = $vma;\n  $r->{file_offset} = $file_offset;\n\n  return $r;\n}\n\n# Parse text section header of a library using otool (on OS X)\nsub ParseTextSectionHeaderFromOtool {\n  my $lib = shift;\n\n  my $size = undef;\n  my $vma = undef;\n  my $file_offset = undef;\n  # Get otool output from the library file to figure out how to\n  # map between mapped addresses and addresses in the library.\n  my $command = ShellEscape($obj_tool_map{\"otool\"}, \"-l\", $lib);\n  open(OTOOL, \"$command |\") || error(\"$command: $!\\n\");\n  my $cmd = \"\";\n  my $sectname = \"\";\n  my $segname = \"\";\n  foreach my $line (<OTOOL>) {\n    $line =~ s/\\r//g;      # turn windows-looking lines into unix-looking lines\n    # Load command <#>\n    #       cmd LC_SEGMENT\n    # [...]\n    # Section\n    #   sectname __text\n    #    segname __TEXT\n    #       addr 0x000009f8\n    #       size 0x00018b9e\n    #     offset 2552\n    #      align 2^2 (4)\n    # We will need to strip off the leading 0x from the hex addresses,\n    # and convert the offset into hex.\n    if ($line =~ /Load command/) {\n      $cmd = \"\";\n      $sectname = \"\";\n      $segname = \"\";\n    } elsif ($line =~ /Section/) {\n      $sectname = \"\";\n      $segname = \"\";\n    } elsif ($line =~ /cmd (\\w+)/) {\n      $cmd = $1;\n    } elsif ($line =~ /sectname (\\w+)/) {\n      $sectname = $1;\n    } elsif ($line =~ /segname (\\w+)/) {\n      $segname = $1;\n    } elsif (!(($cmd eq \"LC_SEGMENT\" || $cmd eq \"LC_SEGMENT_64\") &&\n               $sectname eq \"__text\" &&\n               $segname eq \"__TEXT\")) {\n      next;\n    } elsif ($line =~ /\\baddr 0x([0-9a-fA-F]+)/) {\n      $vma = $1;\n    } elsif ($line =~ /\\bsize 0x([0-9a-fA-F]+)/) {\n      $size = $1;\n    } elsif ($line =~ /\\boffset ([0-9]+)/) {\n      $file_offset = sprintf(\"%016x\", $1);\n    }\n    if (defined($vma) && defined($size) && defined($file_offset)) {\n      last;\n    }\n  }\n  close(OTOOL);\n\n  if (!defined($vma) || !defined($size) || !defined($file_offset)) {\n     return undef;\n  }\n\n  my $r = {};\n  $r->{size} = $size;\n  $r->{vma} = $vma;\n  $r->{file_offset} = $file_offset;\n\n  return $r;\n}\n\nsub ParseTextSectionHeader {\n  # obj_tool_map(\"otool\") is only defined if we're in a Mach-O environment\n  if (defined($obj_tool_map{\"otool\"})) {\n    my $r = ParseTextSectionHeaderFromOtool(@_);\n    if (defined($r)){\n      return $r;\n    }\n  }\n  # If otool doesn't work, or we don't have it, fall back to objdump\n  return ParseTextSectionHeaderFromObjdump(@_);\n}\n\n# Split /proc/pid/maps dump into a list of libraries\nsub ParseLibraries {\n  return if $main::use_symbol_page;  # We don't need libraries info.\n  my $prog = Cwd::abs_path(shift);\n  my $map = shift;\n  my $pcs = shift;\n\n  my $result = [];\n  my $h = \"[a-f0-9]+\";\n  my $zero_offset = HexExtend(\"0\");\n\n  my $buildvar = \"\";\n  foreach my $l (split(\"\\n\", $map)) {\n    if ($l =~ m/^\\s*build=(.*)$/) {\n      $buildvar = $1;\n    }\n\n    my $start;\n    my $finish;\n    my $offset;\n    my $lib;\n    if ($l =~ /^($h)-($h)\\s+..x.\\s+($h)\\s+\\S+:\\S+\\s+\\d+\\s+(\\S+\\.(so|dll|dylib|bundle)((\\.\\d+)+\\w*(\\.\\d+){0,3})?)$/i) {\n      # Full line from /proc/self/maps.  Example:\n      #   40000000-40015000 r-xp 00000000 03:01 12845071   /lib/ld-2.3.2.so\n      $start = HexExtend($1);\n      $finish = HexExtend($2);\n      $offset = HexExtend($3);\n      $lib = $4;\n      $lib =~ s|\\\\|/|g;     # turn windows-style paths into unix-style paths\n    } elsif ($l =~ /^\\s*($h)-($h):\\s*(\\S+\\.so(\\.\\d+)*)/) {\n      # Cooked line from DumpAddressMap.  Example:\n      #   40000000-40015000: /lib/ld-2.3.2.so\n      $start = HexExtend($1);\n      $finish = HexExtend($2);\n      $offset = $zero_offset;\n      $lib = $3;\n    } elsif (($l =~ /^($h)-($h)\\s+..x.\\s+($h)\\s+\\S+:\\S+\\s+\\d+\\s+(\\S+)$/i) && ($4 eq $prog)) {\n      # PIEs and address space randomization do not play well with our\n      # default assumption that main executable is at lowest\n      # addresses. So we're detecting main executable in\n      # /proc/self/maps as well.\n      $start = HexExtend($1);\n      $finish = HexExtend($2);\n      $offset = HexExtend($3);\n      $lib = $4;\n      $lib =~ s|\\\\|/|g;     # turn windows-style paths into unix-style paths\n    }\n    # FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in\n    # function procfs_doprocmap (sys/fs/procfs/procfs_map.c)\n    #\n    # Example:\n    # 0x800600000 0x80061a000 26 0 0xfffff800035a0000 r-x 75 33 0x1004 COW NC vnode /libexec/ld-elf.s\n    # o.1 NCH -1\n    elsif ($l =~ /^(0x$h)\\s(0x$h)\\s\\d+\\s\\d+\\s0x$h\\sr-x\\s\\d+\\s\\d+\\s0x\\d+\\s(COW|NCO)\\s(NC|NNC)\\svnode\\s(\\S+\\.so(\\.\\d+)*)/) {\n      $start = HexExtend($1);\n      $finish = HexExtend($2);\n      $offset = $zero_offset;\n      $lib = FindLibrary($5);\n\n    } else {\n      next;\n    }\n\n    # Expand \"$build\" variable if available\n    $lib =~ s/\\$build\\b/$buildvar/g;\n\n    $lib = FindLibrary($lib);\n\n    # Check for pre-relocated libraries, which use pre-relocated symbol tables\n    # and thus require adjusting the offset that we'll use to translate\n    # VM addresses into symbol table addresses.\n    # Only do this if we're not going to fetch the symbol table from a\n    # debugging copy of the library.\n    if (!DebuggingLibrary($lib)) {\n      my $text = ParseTextSectionHeader($lib);\n      if (defined($text)) {\n         my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});\n         $offset = AddressAdd($offset, $vma_offset);\n      }\n    }\n\n    if($main::opt_debug) { printf STDERR \"$start:$finish ($offset) $lib\\n\"; }\n    push(@{$result}, [$lib, $start, $finish, $offset]);\n  }\n\n  # Append special entry for additional library (not relocated)\n  if ($main::opt_lib ne \"\") {\n    my $text = ParseTextSectionHeader($main::opt_lib);\n    if (defined($text)) {\n       my $start = $text->{vma};\n       my $finish = AddressAdd($start, $text->{size});\n\n       push(@{$result}, [$main::opt_lib, $start, $finish, $start]);\n    }\n  }\n\n  # Append special entry for the main program.  This covers\n  # 0..max_pc_value_seen, so that we assume pc values not found in one\n  # of the library ranges will be treated as coming from the main\n  # program binary.\n  my $min_pc = HexExtend(\"0\");\n  my $max_pc = $min_pc;          # find the maximal PC value in any sample\n  foreach my $pc (keys(%{$pcs})) {\n    if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }\n  }\n  push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);\n\n  return $result;\n}\n\n# Add two hex addresses of length $address_length.\n# Run jeprof --test for unit test if this is changed.\nsub AddressAdd {\n  my $addr1 = shift;\n  my $addr2 = shift;\n  my $sum;\n\n  if ($address_length == 8) {\n    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:\n    $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);\n    return sprintf(\"%08x\", $sum);\n\n  } else {\n    # Do the addition in 7-nibble chunks to trivialize carry handling.\n\n    if ($main::opt_debug and $main::opt_test) {\n      print STDERR \"AddressAdd $addr1 + $addr2 = \";\n    }\n\n    my $a1 = substr($addr1,-7);\n    $addr1 = substr($addr1,0,-7);\n    my $a2 = substr($addr2,-7);\n    $addr2 = substr($addr2,0,-7);\n    $sum = hex($a1) + hex($a2);\n    my $c = 0;\n    if ($sum > 0xfffffff) {\n      $c = 1;\n      $sum -= 0x10000000;\n    }\n    my $r = sprintf(\"%07x\", $sum);\n\n    $a1 = substr($addr1,-7);\n    $addr1 = substr($addr1,0,-7);\n    $a2 = substr($addr2,-7);\n    $addr2 = substr($addr2,0,-7);\n    $sum = hex($a1) + hex($a2) + $c;\n    $c = 0;\n    if ($sum > 0xfffffff) {\n      $c = 1;\n      $sum -= 0x10000000;\n    }\n    $r = sprintf(\"%07x\", $sum) . $r;\n\n    $sum = hex($addr1) + hex($addr2) + $c;\n    if ($sum > 0xff) { $sum -= 0x100; }\n    $r = sprintf(\"%02x\", $sum) . $r;\n\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"$r\\n\"; }\n\n    return $r;\n  }\n}\n\n\n# Subtract two hex addresses of length $address_length.\n# Run jeprof --test for unit test if this is changed.\nsub AddressSub {\n  my $addr1 = shift;\n  my $addr2 = shift;\n  my $diff;\n\n  if ($address_length == 8) {\n    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:\n    $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);\n    return sprintf(\"%08x\", $diff);\n\n  } else {\n    # Do the addition in 7-nibble chunks to trivialize borrow handling.\n    # if ($main::opt_debug) { print STDERR \"AddressSub $addr1 - $addr2 = \"; }\n\n    my $a1 = hex(substr($addr1,-7));\n    $addr1 = substr($addr1,0,-7);\n    my $a2 = hex(substr($addr2,-7));\n    $addr2 = substr($addr2,0,-7);\n    my $b = 0;\n    if ($a2 > $a1) {\n      $b = 1;\n      $a1 += 0x10000000;\n    }\n    $diff = $a1 - $a2;\n    my $r = sprintf(\"%07x\", $diff);\n\n    $a1 = hex(substr($addr1,-7));\n    $addr1 = substr($addr1,0,-7);\n    $a2 = hex(substr($addr2,-7)) + $b;\n    $addr2 = substr($addr2,0,-7);\n    $b = 0;\n    if ($a2 > $a1) {\n      $b = 1;\n      $a1 += 0x10000000;\n    }\n    $diff = $a1 - $a2;\n    $r = sprintf(\"%07x\", $diff) . $r;\n\n    $a1 = hex($addr1);\n    $a2 = hex($addr2) + $b;\n    if ($a2 > $a1) { $a1 += 0x100; }\n    $diff = $a1 - $a2;\n    $r = sprintf(\"%02x\", $diff) . $r;\n\n    # if ($main::opt_debug) { print STDERR \"$r\\n\"; }\n\n    return $r;\n  }\n}\n\n# Increment a hex addresses of length $address_length.\n# Run jeprof --test for unit test if this is changed.\nsub AddressInc {\n  my $addr = shift;\n  my $sum;\n\n  if ($address_length == 8) {\n    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:\n    $sum = (hex($addr)+1) % (0x10000000 * 16);\n    return sprintf(\"%08x\", $sum);\n\n  } else {\n    # Do the addition in 7-nibble chunks to trivialize carry handling.\n    # We are always doing this to step through the addresses in a function,\n    # and will almost never overflow the first chunk, so we check for this\n    # case and exit early.\n\n    # if ($main::opt_debug) { print STDERR \"AddressInc $addr1 = \"; }\n\n    my $a1 = substr($addr,-7);\n    $addr = substr($addr,0,-7);\n    $sum = hex($a1) + 1;\n    my $r = sprintf(\"%07x\", $sum);\n    if ($sum <= 0xfffffff) {\n      $r = $addr . $r;\n      # if ($main::opt_debug) { print STDERR \"$r\\n\"; }\n      return HexExtend($r);\n    } else {\n      $r = \"0000000\";\n    }\n\n    $a1 = substr($addr,-7);\n    $addr = substr($addr,0,-7);\n    $sum = hex($a1) + 1;\n    $r = sprintf(\"%07x\", $sum) . $r;\n    if ($sum <= 0xfffffff) {\n      $r = $addr . $r;\n      # if ($main::opt_debug) { print STDERR \"$r\\n\"; }\n      return HexExtend($r);\n    } else {\n      $r = \"00000000000000\";\n    }\n\n    $sum = hex($addr) + 1;\n    if ($sum > 0xff) { $sum -= 0x100; }\n    $r = sprintf(\"%02x\", $sum) . $r;\n\n    # if ($main::opt_debug) { print STDERR \"$r\\n\"; }\n    return $r;\n  }\n}\n\n# Extract symbols for all PC values found in profile\nsub ExtractSymbols {\n  my $libs = shift;\n  my $pcset = shift;\n\n  my $symbols = {};\n\n  # Map each PC value to the containing library.  To make this faster,\n  # we sort libraries by their starting pc value (highest first), and\n  # advance through the libraries as we advance the pc.  Sometimes the\n  # addresses of libraries may overlap with the addresses of the main\n  # binary, so to make sure the libraries 'win', we iterate over the\n  # libraries in reverse order (which assumes the binary doesn't start\n  # in the middle of a library, which seems a fair assumption).\n  my @pcs = (sort { $a cmp $b } keys(%{$pcset}));  # pcset is 0-extended strings\n  foreach my $lib (sort {$b->[1] cmp $a->[1]} @{$libs}) {\n    my $libname = $lib->[0];\n    my $start = $lib->[1];\n    my $finish = $lib->[2];\n    my $offset = $lib->[3];\n\n    # Use debug library if it exists\n    my $debug_libname = DebuggingLibrary($libname);\n    if ($debug_libname) {\n        $libname = $debug_libname;\n    }\n\n    # Get list of pcs that belong in this library.\n    my $contained = [];\n    my ($start_pc_index, $finish_pc_index);\n    # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index].\n    for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0;\n         $finish_pc_index--) {\n      last if $pcs[$finish_pc_index - 1] le $finish;\n    }\n    # Find smallest start_pc_index such that $start <= $pc[$start_pc_index].\n    for ($start_pc_index = $finish_pc_index; $start_pc_index > 0;\n         $start_pc_index--) {\n      last if $pcs[$start_pc_index - 1] lt $start;\n    }\n    # This keeps PC values higher than $pc[$finish_pc_index] in @pcs,\n    # in case there are overlaps in libraries and the main binary.\n    @{$contained} = splice(@pcs, $start_pc_index,\n                           $finish_pc_index - $start_pc_index);\n    # Map to symbols\n    MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);\n  }\n\n  return $symbols;\n}\n\n# Map list of PC values to symbols for a given image\nsub MapToSymbols {\n  my $image = shift;\n  my $offset = shift;\n  my $pclist = shift;\n  my $symbols = shift;\n\n  my $debug = 0;\n\n  # Ignore empty binaries\n  if ($#{$pclist} < 0) { return; }\n\n  # Figure out the addr2line command to use\n  my $addr2line = $obj_tool_map{\"addr2line\"};\n  my $cmd = ShellEscape($addr2line, \"-f\", \"-C\", \"-e\", $image);\n  if (exists $obj_tool_map{\"addr2line_pdb\"}) {\n    $addr2line = $obj_tool_map{\"addr2line_pdb\"};\n    $cmd = ShellEscape($addr2line, \"--demangle\", \"-f\", \"-C\", \"-e\", $image);\n  }\n\n  # If \"addr2line\" isn't installed on the system at all, just use\n  # nm to get what info we can (function names, but not line numbers).\n  if (system(ShellEscape($addr2line, \"--help\") . \" >$dev_null 2>&1\") != 0) {\n    MapSymbolsWithNM($image, $offset, $pclist, $symbols);\n    return;\n  }\n\n  # \"addr2line -i\" can produce a variable number of lines per input\n  # address, with no separator that allows us to tell when data for\n  # the next address starts.  So we find the address for a special\n  # symbol (_fini) and interleave this address between all real\n  # addresses passed to addr2line.  The name of this special symbol\n  # can then be used as a separator.\n  $sep_address = undef;  # May be filled in by MapSymbolsWithNM()\n  my $nm_symbols = {};\n  MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);\n  if (defined($sep_address)) {\n    # Only add \" -i\" to addr2line if the binary supports it.\n    # addr2line --help returns 0, but not if it sees an unknown flag first.\n    if (system(\"$cmd -i --help >$dev_null 2>&1\") == 0) {\n      $cmd .= \" -i\";\n    } else {\n      $sep_address = undef;   # no need for sep_address if we don't support -i\n    }\n  }\n\n  # Make file with all PC values with intervening 'sep_address' so\n  # that we can reliably detect the end of inlined function list\n  open(ADDRESSES, \">$main::tmpfile_sym\") || error(\"$main::tmpfile_sym: $!\\n\");\n  if ($debug) { print(\"---- $image ---\\n\"); }\n  for (my $i = 0; $i <= $#{$pclist}; $i++) {\n    # addr2line always reads hex addresses, and does not need '0x' prefix.\n    if ($debug) { printf STDERR (\"%s\\n\", $pclist->[$i]); }\n    printf ADDRESSES (\"%s\\n\", AddressSub($pclist->[$i], $offset));\n    if (defined($sep_address)) {\n      printf ADDRESSES (\"%s\\n\", $sep_address);\n    }\n  }\n  close(ADDRESSES);\n  if ($debug) {\n    print(\"----\\n\");\n    system(\"cat\", $main::tmpfile_sym);\n    print(\"----\\n\");\n    system(\"$cmd < \" . ShellEscape($main::tmpfile_sym));\n    print(\"----\\n\");\n  }\n\n  open(SYMBOLS, \"$cmd <\" . ShellEscape($main::tmpfile_sym) . \" |\")\n      || error(\"$cmd: $!\\n\");\n  my $count = 0;   # Index in pclist\n  while (<SYMBOLS>) {\n    # Read fullfunction and filelineinfo from next pair of lines\n    s/\\r?\\n$//g;\n    my $fullfunction = $_;\n    $_ = <SYMBOLS>;\n    s/\\r?\\n$//g;\n    my $filelinenum = $_;\n\n    if (defined($sep_address) && $fullfunction eq $sep_symbol) {\n      # Terminating marker for data for this address\n      $count++;\n      next;\n    }\n\n    $filelinenum =~ s|\\\\|/|g; # turn windows-style paths into unix-style paths\n\n    my $pcstr = $pclist->[$count];\n    my $function = ShortFunctionName($fullfunction);\n    my $nms = $nm_symbols->{$pcstr};\n    if (defined($nms)) {\n      if ($fullfunction eq '??') {\n        # nm found a symbol for us.\n        $function = $nms->[0];\n        $fullfunction = $nms->[2];\n      } else {\n\t# MapSymbolsWithNM tags each routine with its starting address,\n\t# useful in case the image has multiple occurrences of this\n\t# routine.  (It uses a syntax that resembles template paramters,\n\t# that are automatically stripped out by ShortFunctionName().)\n\t# addr2line does not provide the same information.  So we check\n\t# if nm disambiguated our symbol, and if so take the annotated\n\t# (nm) version of the routine-name.  TODO(csilvers): this won't\n\t# catch overloaded, inlined symbols, which nm doesn't see.\n\t# Better would be to do a check similar to nm's, in this fn.\n\tif ($nms->[2] =~ m/^\\Q$function\\E/) {  # sanity check it's the right fn\n\t  $function = $nms->[0];\n\t  $fullfunction = $nms->[2];\n\t}\n      }\n    }\n\n    # Prepend to accumulated symbols for pcstr\n    # (so that caller comes before callee)\n    my $sym = $symbols->{$pcstr};\n    if (!defined($sym)) {\n      $sym = [];\n      $symbols->{$pcstr} = $sym;\n    }\n    unshift(@{$sym}, $function, $filelinenum, $fullfunction);\n    if ($debug) { printf STDERR (\"%s => [%s]\\n\", $pcstr, join(\" \", @{$sym})); }\n    if (!defined($sep_address)) {\n      # Inlining is off, so this entry ends immediately\n      $count++;\n    }\n  }\n  close(SYMBOLS);\n}\n\n# Use nm to map the list of referenced PCs to symbols.  Return true iff we\n# are able to read procedure information via nm.\nsub MapSymbolsWithNM {\n  my $image = shift;\n  my $offset = shift;\n  my $pclist = shift;\n  my $symbols = shift;\n\n  # Get nm output sorted by increasing address\n  my $symbol_table = GetProcedureBoundaries($image, \".\");\n  if (!%{$symbol_table}) {\n    return 0;\n  }\n  # Start addresses are already the right length (8 or 16 hex digits).\n  my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }\n    keys(%{$symbol_table});\n\n  if ($#names < 0) {\n    # No symbols: just use addresses\n    foreach my $pc (@{$pclist}) {\n      my $pcstr = \"0x\" . $pc;\n      $symbols->{$pc} = [$pcstr, \"?\", $pcstr];\n    }\n    return 0;\n  }\n\n  # Sort addresses so we can do a join against nm output\n  my $index = 0;\n  my $fullname = $names[0];\n  my $name = ShortFunctionName($fullname);\n  foreach my $pc (sort { $a cmp $b } @{$pclist}) {\n    # Adjust for mapped offset\n    my $mpc = AddressSub($pc, $offset);\n    while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){\n      $index++;\n      $fullname = $names[$index];\n      $name = ShortFunctionName($fullname);\n    }\n    if ($mpc lt $symbol_table->{$fullname}->[1]) {\n      $symbols->{$pc} = [$name, \"?\", $fullname];\n    } else {\n      my $pcstr = \"0x\" . $pc;\n      $symbols->{$pc} = [$pcstr, \"?\", $pcstr];\n    }\n  }\n  return 1;\n}\n\nsub ShortFunctionName {\n  my $function = shift;\n  while ($function =~ s/\\([^()]*\\)(\\s*const)?//g) { }   # Argument types\n  while ($function =~ s/<[^<>]*>//g)  { }    # Remove template arguments\n  $function =~ s/^.*\\s+(\\w+::)/$1/;          # Remove leading type\n  return $function;\n}\n\n# Trim overly long symbols found in disassembler output\nsub CleanDisassembly {\n  my $d = shift;\n  while ($d =~ s/\\([^()%]*\\)(\\s*const)?//g) { } # Argument types, not (%rax)\n  while ($d =~ s/(\\w+)<[^<>]*>/$1/g)  { }       # Remove template arguments\n  return $d;\n}\n\n# Clean file name for display\nsub CleanFileName {\n  my ($f) = @_;\n  $f =~ s|^/proc/self/cwd/||;\n  $f =~ s|^\\./||;\n  return $f;\n}\n\n# Make address relative to section and clean up for display\nsub UnparseAddress {\n  my ($offset, $address) = @_;\n  $address = AddressSub($address, $offset);\n  $address =~ s/^0x//;\n  $address =~ s/^0*//;\n  return $address;\n}\n\n##### Miscellaneous #####\n\n# Find the right versions of the above object tools to use.  The\n# argument is the program file being analyzed, and should be an ELF\n# 32-bit or ELF 64-bit executable file.  The location of the tools\n# is determined by considering the following options in this order:\n#   1) --tools option, if set\n#   2) JEPROF_TOOLS environment variable, if set\n#   3) the environment\nsub ConfigureObjTools {\n  my $prog_file = shift;\n\n  # Check for the existence of $prog_file because /usr/bin/file does not\n  # predictably return error status in prod.\n  (-e $prog_file)  || error(\"$prog_file does not exist.\\n\");\n\n  my $file_type = undef;\n  if (-e \"/usr/bin/file\") {\n    # Follow symlinks (at least for systems where \"file\" supports that).\n    my $escaped_prog_file = ShellEscape($prog_file);\n    $file_type = `/usr/bin/file -L $escaped_prog_file 2>$dev_null ||\n                  /usr/bin/file $escaped_prog_file`;\n  } elsif ($^O == \"MSWin32\") {\n    $file_type = \"MS Windows\";\n  } else {\n    print STDERR \"WARNING: Can't determine the file type of $prog_file\";\n  }\n\n  if ($file_type =~ /64-bit/) {\n    # Change $address_length to 16 if the program file is ELF 64-bit.\n    # We can't detect this from many (most?) heap or lock contention\n    # profiles, since the actual addresses referenced are generally in low\n    # memory even for 64-bit programs.\n    $address_length = 16;\n  }\n\n  if ($file_type =~ /MS Windows/) {\n    # For windows, we provide a version of nm and addr2line as part of\n    # the opensource release, which is capable of parsing\n    # Windows-style PDB executables.  It should live in the path, or\n    # in the same directory as jeprof.\n    $obj_tool_map{\"nm_pdb\"} = \"nm-pdb\";\n    $obj_tool_map{\"addr2line_pdb\"} = \"addr2line-pdb\";\n  }\n\n  if ($file_type =~ /Mach-O/) {\n    # OS X uses otool to examine Mach-O files, rather than objdump.\n    $obj_tool_map{\"otool\"} = \"otool\";\n    $obj_tool_map{\"addr2line\"} = \"false\";  # no addr2line\n    $obj_tool_map{\"objdump\"} = \"false\";  # no objdump\n  }\n\n  # Go fill in %obj_tool_map with the pathnames to use:\n  foreach my $tool (keys %obj_tool_map) {\n    $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});\n  }\n}\n\n# Returns the path of a caller-specified object tool.  If --tools or\n# JEPROF_TOOLS are specified, then returns the full path to the tool\n# with that prefix.  Otherwise, returns the path unmodified (which\n# means we will look for it on PATH).\nsub ConfigureTool {\n  my $tool = shift;\n  my $path;\n\n  # --tools (or $JEPROF_TOOLS) is a comma separated list, where each\n  # item is either a) a pathname prefix, or b) a map of the form\n  # <tool>:<path>.  First we look for an entry of type (b) for our\n  # tool.  If one is found, we use it.  Otherwise, we consider all the\n  # pathname prefixes in turn, until one yields an existing file.  If\n  # none does, we use a default path.\n  my $tools = $main::opt_tools || $ENV{\"JEPROF_TOOLS\"} || \"\";\n  if ($tools =~ m/(,|^)\\Q$tool\\E:([^,]*)/) {\n    $path = $2;\n    # TODO(csilvers): sanity-check that $path exists?  Hard if it's relative.\n  } elsif ($tools ne '') {\n    foreach my $prefix (split(',', $tools)) {\n      next if ($prefix =~ /:/);    # ignore \"tool:fullpath\" entries in the list\n      if (-x $prefix . $tool) {\n        $path = $prefix . $tool;\n        last;\n      }\n    }\n    if (!$path) {\n      error(\"No '$tool' found with prefix specified by \" .\n            \"--tools (or \\$JEPROF_TOOLS) '$tools'\\n\");\n    }\n  } else {\n    # ... otherwise use the version that exists in the same directory as\n    # jeprof.  If there's nothing there, use $PATH.\n    $0 =~ m,[^/]*$,;     # this is everything after the last slash\n    my $dirname = $`;    # this is everything up to and including the last slash\n    if (-x \"$dirname$tool\") {\n      $path = \"$dirname$tool\";\n    } else {\n      $path = $tool;\n    }\n  }\n  if ($main::opt_debug) { print STDERR \"Using '$path' for '$tool'.\\n\"; }\n  return $path;\n}\n\nsub ShellEscape {\n  my @escaped_words = ();\n  foreach my $word (@_) {\n    my $escaped_word = $word;\n    if ($word =~ m![^a-zA-Z0-9/.,_=-]!) {  # check for anything not in whitelist\n      $escaped_word =~ s/'/'\\\\''/;\n      $escaped_word = \"'$escaped_word'\";\n    }\n    push(@escaped_words, $escaped_word);\n  }\n  return join(\" \", @escaped_words);\n}\n\nsub cleanup {\n  unlink($main::tmpfile_sym);\n  unlink(keys %main::tempnames);\n\n  # We leave any collected profiles in $HOME/jeprof in case the user wants\n  # to look at them later.  We print a message informing them of this.\n  if ((scalar(@main::profile_files) > 0) &&\n      defined($main::collected_profile)) {\n    if (scalar(@main::profile_files) == 1) {\n      print STDERR \"Dynamically gathered profile is in $main::collected_profile\\n\";\n    }\n    print STDERR \"If you want to investigate this profile further, you can do:\\n\";\n    print STDERR \"\\n\";\n    print STDERR \"  jeprof \\\\\\n\";\n    print STDERR \"    $main::prog \\\\\\n\";\n    print STDERR \"    $main::collected_profile\\n\";\n    print STDERR \"\\n\";\n  }\n}\n\nsub sighandler {\n  cleanup();\n  exit(1);\n}\n\nsub error {\n  my $msg = shift;\n  print STDERR $msg;\n  cleanup();\n  exit(1);\n}\n\n\n# Run $nm_command and get all the resulting procedure boundaries whose\n# names match \"$regexp\" and returns them in a hashtable mapping from\n# procedure name to a two-element vector of [start address, end address]\nsub GetProcedureBoundariesViaNm {\n  my $escaped_nm_command = shift;    # shell-escaped\n  my $regexp = shift;\n\n  my $symbol_table = {};\n  open(NM, \"$escaped_nm_command |\") || error(\"$escaped_nm_command: $!\\n\");\n  my $last_start = \"0\";\n  my $routine = \"\";\n  while (<NM>) {\n    s/\\r//g;         # turn windows-looking lines into unix-looking lines\n    if (m/^\\s*([0-9a-f]+) (.) (..*)/) {\n      my $start_val = $1;\n      my $type = $2;\n      my $this_routine = $3;\n\n      # It's possible for two symbols to share the same address, if\n      # one is a zero-length variable (like __start_google_malloc) or\n      # one symbol is a weak alias to another (like __libc_malloc).\n      # In such cases, we want to ignore all values except for the\n      # actual symbol, which in nm-speak has type \"T\".  The logic\n      # below does this, though it's a bit tricky: what happens when\n      # we have a series of lines with the same address, is the first\n      # one gets queued up to be processed.  However, it won't\n      # *actually* be processed until later, when we read a line with\n      # a different address.  That means that as long as we're reading\n      # lines with the same address, we have a chance to replace that\n      # item in the queue, which we do whenever we see a 'T' entry --\n      # that is, a line with type 'T'.  If we never see a 'T' entry,\n      # we'll just go ahead and process the first entry (which never\n      # got touched in the queue), and ignore the others.\n      if ($start_val eq $last_start && $type =~ /t/i) {\n        # We are the 'T' symbol at this address, replace previous symbol.\n        $routine = $this_routine;\n        next;\n      } elsif ($start_val eq $last_start) {\n        # We're not the 'T' symbol at this address, so ignore us.\n        next;\n      }\n\n      if ($this_routine eq $sep_symbol) {\n        $sep_address = HexExtend($start_val);\n      }\n\n      # Tag this routine with the starting address in case the image\n      # has multiple occurrences of this routine.  We use a syntax\n      # that resembles template parameters that are automatically\n      # stripped out by ShortFunctionName()\n      $this_routine .= \"<$start_val>\";\n\n      if (defined($routine) && $routine =~ m/$regexp/) {\n        $symbol_table->{$routine} = [HexExtend($last_start),\n                                     HexExtend($start_val)];\n      }\n      $last_start = $start_val;\n      $routine = $this_routine;\n    } elsif (m/^Loaded image name: (.+)/) {\n      # The win32 nm workalike emits information about the binary it is using.\n      if ($main::opt_debug) { print STDERR \"Using Image $1\\n\"; }\n    } elsif (m/^PDB file name: (.+)/) {\n      # The win32 nm workalike emits information about the pdb it is using.\n      if ($main::opt_debug) { print STDERR \"Using PDB $1\\n\"; }\n    }\n  }\n  close(NM);\n  # Handle the last line in the nm output.  Unfortunately, we don't know\n  # how big this last symbol is, because we don't know how big the file\n  # is.  For now, we just give it a size of 0.\n  # TODO(csilvers): do better here.\n  if (defined($routine) && $routine =~ m/$regexp/) {\n    $symbol_table->{$routine} = [HexExtend($last_start),\n                                 HexExtend($last_start)];\n  }\n  return $symbol_table;\n}\n\n# Gets the procedure boundaries for all routines in \"$image\" whose names\n# match \"$regexp\" and returns them in a hashtable mapping from procedure\n# name to a two-element vector of [start address, end address].\n# Will return an empty map if nm is not installed or not working properly.\nsub GetProcedureBoundaries {\n  my $image = shift;\n  my $regexp = shift;\n\n  # If $image doesn't start with /, then put ./ in front of it.  This works\n  # around an obnoxious bug in our probing of nm -f behavior.\n  # \"nm -f $image\" is supposed to fail on GNU nm, but if:\n  #\n  # a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND\n  # b. you have a.out in your current directory (a not uncommon occurence)\n  #\n  # then \"nm -f $image\" succeeds because -f only looks at the first letter of\n  # the argument, which looks valid because it's [BbSsPp], and then since\n  # there's no image provided, it looks for a.out and finds it.\n  #\n  # This regex makes sure that $image starts with . or /, forcing the -f\n  # parsing to fail since . and / are not valid formats.\n  $image =~ s#^[^/]#./$&#;\n\n  # For libc libraries, the copy in /usr/lib/debug contains debugging symbols\n  my $debugging = DebuggingLibrary($image);\n  if ($debugging) {\n    $image = $debugging;\n  }\n\n  my $nm = $obj_tool_map{\"nm\"};\n  my $cppfilt = $obj_tool_map{\"c++filt\"};\n\n  # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm\n  # binary doesn't support --demangle.  In addition, for OS X we need\n  # to use the -f flag to get 'flat' nm output (otherwise we don't sort\n  # properly and get incorrect results).  Unfortunately, GNU nm uses -f\n  # in an incompatible way.  So first we test whether our nm supports\n  # --demangle and -f.\n  my $demangle_flag = \"\";\n  my $cppfilt_flag = \"\";\n  my $to_devnull = \">$dev_null 2>&1\";\n  if (system(ShellEscape($nm, \"--demangle\", $image) . $to_devnull) == 0) {\n    # In this mode, we do \"nm --demangle <foo>\"\n    $demangle_flag = \"--demangle\";\n    $cppfilt_flag = \"\";\n  } elsif (system(ShellEscape($cppfilt, $image) . $to_devnull) == 0) {\n    # In this mode, we do \"nm <foo> | c++filt\"\n    $cppfilt_flag = \" | \" . ShellEscape($cppfilt);\n  };\n  my $flatten_flag = \"\";\n  if (system(ShellEscape($nm, \"-f\", $image) . $to_devnull) == 0) {\n    $flatten_flag = \"-f\";\n  }\n\n  # Finally, in the case $imagie isn't a debug library, we try again with\n  # -D to at least get *exported* symbols.  If we can't use --demangle,\n  # we use c++filt instead, if it exists on this system.\n  my @nm_commands = (ShellEscape($nm, \"-n\", $flatten_flag, $demangle_flag,\n                                 $image) . \" 2>$dev_null $cppfilt_flag\",\n                     ShellEscape($nm, \"-D\", \"-n\", $flatten_flag, $demangle_flag,\n                                 $image) . \" 2>$dev_null $cppfilt_flag\",\n                     # 6nm is for Go binaries\n                     ShellEscape(\"6nm\", \"$image\") . \" 2>$dev_null | sort\",\n                     );\n\n  # If the executable is an MS Windows PDB-format executable, we'll\n  # have set up obj_tool_map(\"nm_pdb\").  In this case, we actually\n  # want to use both unix nm and windows-specific nm_pdb, since\n  # PDB-format executables can apparently include dwarf .o files.\n  if (exists $obj_tool_map{\"nm_pdb\"}) {\n    push(@nm_commands,\n         ShellEscape($obj_tool_map{\"nm_pdb\"}, \"--demangle\", $image)\n         . \" 2>$dev_null\");\n  }\n\n  foreach my $nm_command (@nm_commands) {\n    my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);\n    return $symbol_table if (%{$symbol_table});\n  }\n  my $symbol_table = {};\n  return $symbol_table;\n}\n\n\n# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.\n# To make them more readable, we add underscores at interesting places.\n# This routine removes the underscores, producing the canonical representation\n# used by jeprof to represent addresses, particularly in the tested routines.\nsub CanonicalHex {\n  my $arg = shift;\n  return join '', (split '_',$arg);\n}\n\n\n# Unit test for AddressAdd:\nsub AddressAddUnitTest {\n  my $test_data_8 = shift;\n  my $test_data_16 = shift;\n  my $error_count = 0;\n  my $fail_count = 0;\n  my $pass_count = 0;\n  # print STDERR \"AddressAddUnitTest: \", 1+$#{$test_data_8}, \" tests\\n\";\n\n  # First a few 8-nibble addresses.  Note that this implementation uses\n  # plain old arithmetic, so a quick sanity check along with verifying what\n  # happens to overflow (we want it to wrap):\n  $address_length = 8;\n  foreach my $row (@{$test_data_8}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressAdd ($row->[0], $row->[1]);\n    if ($sum ne $row->[2]) {\n      printf STDERR \"ERROR: %s != %s + %s = %s\\n\", $sum,\n             $row->[0], $row->[1], $row->[2];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressAdd 32-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count = $fail_count;\n  $fail_count = 0;\n  $pass_count = 0;\n\n  # Now 16-nibble addresses.\n  $address_length = 16;\n  foreach my $row (@{$test_data_16}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));\n    my $expected = join '', (split '_',$row->[2]);\n    if ($sum ne CanonicalHex($row->[2])) {\n      printf STDERR \"ERROR: %s != %s + %s = %s\\n\", $sum,\n             $row->[0], $row->[1], $row->[2];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressAdd 64-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count += $fail_count;\n\n  return $error_count;\n}\n\n\n# Unit test for AddressSub:\nsub AddressSubUnitTest {\n  my $test_data_8 = shift;\n  my $test_data_16 = shift;\n  my $error_count = 0;\n  my $fail_count = 0;\n  my $pass_count = 0;\n  # print STDERR \"AddressSubUnitTest: \", 1+$#{$test_data_8}, \" tests\\n\";\n\n  # First a few 8-nibble addresses.  Note that this implementation uses\n  # plain old arithmetic, so a quick sanity check along with verifying what\n  # happens to overflow (we want it to wrap):\n  $address_length = 8;\n  foreach my $row (@{$test_data_8}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressSub ($row->[0], $row->[1]);\n    if ($sum ne $row->[3]) {\n      printf STDERR \"ERROR: %s != %s - %s = %s\\n\", $sum,\n             $row->[0], $row->[1], $row->[3];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressSub 32-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count = $fail_count;\n  $fail_count = 0;\n  $pass_count = 0;\n\n  # Now 16-nibble addresses.\n  $address_length = 16;\n  foreach my $row (@{$test_data_16}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));\n    if ($sum ne CanonicalHex($row->[3])) {\n      printf STDERR \"ERROR: %s != %s - %s = %s\\n\", $sum,\n             $row->[0], $row->[1], $row->[3];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressSub 64-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count += $fail_count;\n\n  return $error_count;\n}\n\n\n# Unit test for AddressInc:\nsub AddressIncUnitTest {\n  my $test_data_8 = shift;\n  my $test_data_16 = shift;\n  my $error_count = 0;\n  my $fail_count = 0;\n  my $pass_count = 0;\n  # print STDERR \"AddressIncUnitTest: \", 1+$#{$test_data_8}, \" tests\\n\";\n\n  # First a few 8-nibble addresses.  Note that this implementation uses\n  # plain old arithmetic, so a quick sanity check along with verifying what\n  # happens to overflow (we want it to wrap):\n  $address_length = 8;\n  foreach my $row (@{$test_data_8}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressInc ($row->[0]);\n    if ($sum ne $row->[4]) {\n      printf STDERR \"ERROR: %s != %s + 1 = %s\\n\", $sum,\n             $row->[0], $row->[4];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressInc 32-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count = $fail_count;\n  $fail_count = 0;\n  $pass_count = 0;\n\n  # Now 16-nibble addresses.\n  $address_length = 16;\n  foreach my $row (@{$test_data_16}) {\n    if ($main::opt_debug and $main::opt_test) { print STDERR \"@{$row}\\n\"; }\n    my $sum = AddressInc (CanonicalHex($row->[0]));\n    if ($sum ne CanonicalHex($row->[4])) {\n      printf STDERR \"ERROR: %s != %s + 1 = %s\\n\", $sum,\n             $row->[0], $row->[4];\n      ++$fail_count;\n    } else {\n      ++$pass_count;\n    }\n  }\n  printf STDERR \"AddressInc 64-bit tests: %d passes, %d failures\\n\",\n         $pass_count, $fail_count;\n  $error_count += $fail_count;\n\n  return $error_count;\n}\n\n\n# Driver for unit tests.\n# Currently just the address add/subtract/increment routines for 64-bit.\nsub RunUnitTests {\n  my $error_count = 0;\n\n  # This is a list of tuples [a, b, a+b, a-b, a+1]\n  my $unit_test_data_8 = [\n    [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],\n    [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],\n    [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],\n    [qw(00000001 ffffffff 00000000 00000002 00000002)],\n    [qw(00000001 fffffff0 fffffff1 00000011 00000002)],\n  ];\n  my $unit_test_data_16 = [\n    # The implementation handles data in 7-nibble chunks, so those are the\n    # interesting boundaries.\n    [qw(aaaaaaaa 50505050\n        00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],\n    [qw(50505050 aaaaaaaa\n        00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],\n    [qw(ffffffff aaaaaaaa\n        00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],\n    [qw(00000001 ffffffff\n        00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],\n    [qw(00000001 fffffff0\n        00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],\n\n    [qw(00_a00000a_aaaaaaa 50505050\n        00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],\n    [qw(0f_fff0005_0505050 aaaaaaaa\n        0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],\n    [qw(00_000000f_fffffff 01_800000a_aaaaaaa\n        01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],\n    [qw(00_0000000_0000001 ff_fffffff_fffffff\n        00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],\n    [qw(00_0000000_0000001 ff_fffffff_ffffff0\n        ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],\n  ];\n\n  $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);\n  $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);\n  $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);\n  if ($error_count > 0) {\n    print STDERR $error_count, \" errors: FAILED\\n\";\n  } else {\n    print STDERR \"PASS\\n\";\n  }\n  exit ($error_count);\n}\n"
  },
  {
    "path": "third_party/kafka.BUILD",
    "content": "# Description:\n#   Kafka C/C++ (librdkafka) client library\n\nlicenses([\"notice\"])  # 2-clause BSD license\n\nexports_files([\"LICENSE\"])\n\ncc_library(\n    name = \"kafka\",\n    srcs = glob(\n        [\n            \"src-cpp/*.h\",\n            \"src-cpp/*.cpp\",\n            \"src/*.c\",\n            \"src/*.h\",\n        ],\n        exclude = [\n            \"src/lz4.c\",\n            \"src/lz4.h\",\n            \"src/lz4frame.c\",\n            \"src/lz4frame.h\",\n            \"src/lz4frame_static.h\",\n            \"src/lz4hc.c\",\n            \"src/lz4hc.h\",\n            \"src/lz4opt.h\",\n            \"src/rddl.c\",\n            \"src/rddl.h\",\n            \"src/rdkafka_plugin.c\",\n            \"src/rdkafka_plugin.h\",\n            \"src/rdkafka_sasl_cyrus.c\",\n            \"src/rdkafka_sasl_win32.c\",\n            \"src/rdxxhash.c\",\n            \"src/rdxxhash.h\",\n            \"src/win32_config.h\",\n        ],\n    ) + [\n        \"config/config.h\",\n        \"config/src/set1_host.c\",\n        \"config/src/win32_config.h\",\n    ] + select({\n        \"@bazel_tools//src/conditions:windows\": [\n            \"src/rdkafka_sasl_win32.c\",\n        ],\n        \"//conditions:default\": [],\n    }),\n    hdrs = [\n        \"config/config.h\",\n        \"config/src/set1_host.c\",\n        \"config/src/win32_config.h\",\n        \"src/rdxxhash.c\",\n        \"src/rdxxhash.h\",\n    ],\n    defines = [\n        \"LIBRDKAFKA_STATICLIB\",\n        \"WIN32_LEAN_AND_MEAN\",\n        \"XXH_PRIVATE_API\",\n    ],\n    includes = [\n        \"config/src\",\n        \"src\",\n        \"src-cpp\",\n    ],\n    linkopts = [],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \"@boringssl//:ssl\",\n        \"@lz4\",\n        \"@zlib\",\n        \"@zstd\",\n    ],\n)\n\ngenrule(\n    name = \"set1_host_c\",\n    outs = [\"config/src/set1_host.c\"],\n    cmd = \"\\n\".join([\n        \"cat <<'EOF' >$@\",\n        \"#include <openssl/ssl.h>\",\n        \"int SSL_set1_host(SSL *s, const char *hostname) {\",\n        \"  return X509_VERIFY_PARAM_set1_host(SSL_get0_param(s), hostname, 0);\",\n        \"}\",\n        \"EOF\",\n    ]),\n)\n\ngenrule(\n    name = \"win32_config_h\",\n    outs = [\"config/src/win32_config.h\"],\n    cmd = \"\\n\".join([\n        \"cat <<'EOF' >$@\",\n        \"#define WITH_SSL 1\",\n        \"#define WITH_ZLIB 1\",\n        \"#define WITH_SNAPPY 1\",\n        \"#define WITH_ZSTD 1\",\n        \"#define WITH_ZSTD_STATIC 1\",\n        \"#define WITH_LZ4_EXT 1\",\n        \"#define WITH_SASL 1\",\n        \"#define WITH_SASL_SCRAM 1\",\n        \"#define WITH_SASL_OAUTHBEARER 1\",\n        \"#define WITH_HDRHISTOGRAM 1\",\n        \"#define WITH_PLUGINS 0\",\n        \"#define ENABLE_DEVEL 0\",\n        \"#define BUILT_WITH \\\"SSL ZLIB SNAPPY ZSTD LZ4 SASL SASL_SCRAM SASL_OAUTHBEARER HDRHISTOGRAM\\\"\",\n        \"// maintain the order below\",\n        \"#include <windows.h>\",\n        \"#include <openssl/x509.h>\",\n        \"#include <wincrypt.h>\",\n        \"EOF\",\n    ]),\n)\n\ngenrule(\n    name = \"config_h\",\n    outs = [\"config/config.h\"],\n    cmd = \"\\n\".join([\n        \"cat <<'EOF' >$@\",\n        \"#define WITH_SSL 1\",\n        \"#define WITH_ZLIB 1\",\n        \"#define WITH_SNAPPY 1\",\n        \"#define WITH_ZSTD 1\",\n        \"#define WITH_ZSTD_STATIC 1\",\n        \"#define WITH_LZ4_EXT 1\",\n        \"#define WITH_SASL 1\",\n        \"#define WITH_SASL_SCRAM 1\",\n        \"#define WITH_SASL_OAUTHBEARER 1\",\n        \"#define WITH_HDRHISTOGRAM 1\",\n        \"#define WITH_PLUGINS 0\",\n        \"#define ENABLE_DEVEL 0\",\n        \"#define BUILT_WITH \\\"SSL ZLIB SNAPPY ZSTD LZ4 SASL SASL_SCRAM SASL_OAUTHBEARER HDRHISTOGRAM\\\"\",\n        \"EOF\",\n    ]),\n)\n"
  },
  {
    "path": "third_party/libdata_java_model_training.BUILD",
    "content": "exports_files(glob([\"**\"]))\n\n# The target will be\n#\n# - java_model_training-1.0-SNAPSHOT.jar\n"
  },
  {
    "path": "third_party/lz4.BUILD",
    "content": "# Description:\n#   LZ4 library\n\nlicenses([\"notice\"])  # BSD license\n\nexports_files([\"LICENSE\"])\n\ncc_library(\n    name = \"lz4\",\n    srcs = glob([\n        \"lib/lz4.c\",\n        \"lib/lz4.h\",\n        \"lib/lz4frame.c\",\n        \"lib/lz4frame.h\",\n        \"lib/lz4hc.h\",\n        \"lib/lz4hc.c\",\n        \"lib/xxhash.h\",\n    ]),\n    hdrs = [],\n    defines = [\n        \"XXH_PRIVATE_API\",\n    ],\n    includes = [\n        \"lib\",\n    ],\n    linkopts = [],\n    textual_hdrs = [\n        \"lib/xxhash.c\",\n        \"lib/lz4.c\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "third_party/msgpack/msgpack.BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_library(\n    name = \"msgpack\",\n    hdrs = glob([\"include/**\"]),\n    includes = [\"include\"],\n)\n"
  },
  {
    "path": "third_party/nlohmann/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\ncc_library(\n    name = \"json\",\n    hdrs = [\"json.hpp\"],\n)\n"
  },
  {
    "path": "third_party/nlohmann/json.hpp",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/*\n    __ _____ _____ _____\n __|  |   __|     |   | |  JSON for Modern C++\n|  |  |__   |  |  | | | |  version 3.9.1\n|_____|_____|_____|_|___|  https://github.com/nlohmann/json\n\nLicensed under the MIT License <http://opensource.org/licenses/MIT>.\nSPDX-License-Identifier: MIT\nCopyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.\n\nPermission is hereby  granted, free of charge, to any  person obtaining a copy\nof this software and associated  documentation files (the \"Software\"), to deal\nin the Software  without restriction, including without  limitation the rights\nto  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell\ncopies  of  the Software,  and  to  permit persons  to  whom  the Software  is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE  IS PROVIDED \"AS  IS\", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR\nIMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,\nFITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE\nAUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER\nLIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n#ifndef INCLUDE_NLOHMANN_JSON_HPP_\n#define INCLUDE_NLOHMANN_JSON_HPP_\n\n#define NLOHMANN_JSON_VERSION_MAJOR 3\n#define NLOHMANN_JSON_VERSION_MINOR 9\n#define NLOHMANN_JSON_VERSION_PATCH 1\n\n#include <algorithm> // all_of, find, for_each\n#include <cstddef> // nullptr_t, ptrdiff_t, size_t\n#include <functional> // hash, less\n#include <initializer_list> // initializer_list\n#include <iosfwd> // istream, ostream\n#include <iterator> // random_access_iterator_tag\n#include <memory> // unique_ptr\n#include <numeric> // accumulate\n#include <string> // string, stoi, to_string\n#include <utility> // declval, forward, move, pair, swap\n#include <vector> // vector\n\n// #include <nlohmann/adl_serializer.hpp>\n\n\n#include <utility>\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n\n#include <algorithm> // transform\n#include <array> // array\n#include <forward_list> // forward_list\n#include <iterator> // inserter, front_inserter, end\n#include <map> // map\n#include <string> // string\n#include <tuple> // tuple, make_tuple\n#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible\n#include <unordered_map> // unordered_map\n#include <utility> // pair, declval\n#include <valarray> // valarray\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n\n#include <exception> // exception\n#include <stdexcept> // runtime_error\n#include <string> // to_string\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n\n#include <cstddef> // size_t\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// struct to capture the start position of the current token\nstruct position_t\n{\n    /// the total number of characters read\n    std::size_t chars_read_total = 0;\n    /// the number of characters read in the current line\n    std::size_t chars_read_current_line = 0;\n    /// the number of lines read\n    std::size_t lines_read = 0;\n\n    /// conversion to size_t to preserve SAX interface\n    constexpr operator size_t() const\n    {\n        return chars_read_total;\n    }\n};\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\n#include <utility> // pair\n// #include <nlohmann/thirdparty/hedley/hedley.hpp>\n/* Hedley - https://nemequ.github.io/hedley\n * Created by Evan Nemerson <evan@nemerson.com>\n *\n * To the extent possible under law, the author(s) have dedicated all\n * copyright and related and neighboring rights to this software to\n * the public domain worldwide. This software is distributed without\n * any warranty.\n *\n * For details, see <http://creativecommons.org/publicdomain/zero/1.0/>.\n * SPDX-License-Identifier: CC0-1.0\n */\n\n#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 13)\n#if defined(JSON_HEDLEY_VERSION)\n    #undef JSON_HEDLEY_VERSION\n#endif\n#define JSON_HEDLEY_VERSION 13\n\n#if defined(JSON_HEDLEY_STRINGIFY_EX)\n    #undef JSON_HEDLEY_STRINGIFY_EX\n#endif\n#define JSON_HEDLEY_STRINGIFY_EX(x) #x\n\n#if defined(JSON_HEDLEY_STRINGIFY)\n    #undef JSON_HEDLEY_STRINGIFY\n#endif\n#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)\n\n#if defined(JSON_HEDLEY_CONCAT_EX)\n    #undef JSON_HEDLEY_CONCAT_EX\n#endif\n#define JSON_HEDLEY_CONCAT_EX(a,b) a##b\n\n#if defined(JSON_HEDLEY_CONCAT)\n    #undef JSON_HEDLEY_CONCAT\n#endif\n#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)\n\n#if defined(JSON_HEDLEY_CONCAT3_EX)\n    #undef JSON_HEDLEY_CONCAT3_EX\n#endif\n#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c\n\n#if defined(JSON_HEDLEY_CONCAT3)\n    #undef JSON_HEDLEY_CONCAT3\n#endif\n#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)\n\n#if defined(JSON_HEDLEY_VERSION_ENCODE)\n    #undef JSON_HEDLEY_VERSION_ENCODE\n#endif\n#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)\n    #undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)\n\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #undef JSON_HEDLEY_GNUC_VERSION\n#endif\n#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)\n#elif defined(__GNUC__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION)\n    #undef JSON_HEDLEY_MSVC_VERSION\n#endif\n#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)\n#elif defined(_MSC_FULL_VER)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)\n#elif defined(_MSC_VER)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#endif\n#if !defined(_MSC_VER)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)\n#elif defined(_MSC_VER) && (_MSC_VER >= 1400)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))\n#elif defined(_MSC_VER) && (_MSC_VER >= 1200)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))\n#else\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #undef JSON_HEDLEY_INTEL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)\n#elif defined(__INTEL_COMPILER)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #undef JSON_HEDLEY_PGI_VERSION\n#endif\n#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)\n    #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)\n    #undef JSON_HEDLEY_PGI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #undef JSON_HEDLEY_SUNPRO_VERSION\n#endif\n#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)\n#elif defined(__SUNPRO_C)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)\n#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)\n#elif defined(__SUNPRO_CC)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)\n    #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#endif\n#if defined(__EMSCRIPTEN__)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #undef JSON_HEDLEY_ARM_VERSION\n#endif\n#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)\n#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)\n    #undef JSON_HEDLEY_ARM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #undef JSON_HEDLEY_IBM_VERSION\n#endif\n#if defined(__ibmxl__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)\n#elif defined(__xlC__) && defined(__xlC_ver__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)\n#elif defined(__xlC__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)\n    #undef JSON_HEDLEY_IBM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #undef JSON_HEDLEY_TI_VERSION\n#endif\n#if \\\n    defined(__TI_COMPILER_VERSION__) && \\\n    ( \\\n      defined(__TMS470__) || defined(__TI_ARM__) || \\\n      defined(__MSP430__) || \\\n      defined(__TMS320C2000__) \\\n    )\n#if (__TI_COMPILER_VERSION__ >= 16000000)\n    #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)\n    #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #undef JSON_HEDLEY_TI_CL430_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)\n    #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))\n    #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)\n    #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)\n    #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #undef JSON_HEDLEY_CRAY_VERSION\n#endif\n#if defined(_CRAYC)\n    #if defined(_RELEASE_PATCHLEVEL)\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)\n    #else\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)\n    #undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #undef JSON_HEDLEY_IAR_VERSION\n#endif\n#if defined(__IAR_SYSTEMS_ICC__)\n    #if __VER__ > 1000\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))\n    #else\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)\n    #undef JSON_HEDLEY_IAR_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #undef JSON_HEDLEY_TINYC_VERSION\n#endif\n#if defined(__TINYC__)\n    #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)\n    #undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #undef JSON_HEDLEY_DMC_VERSION\n#endif\n#if defined(__DMC__)\n    #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)\n    #undef JSON_HEDLEY_DMC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #undef JSON_HEDLEY_COMPCERT_VERSION\n#endif\n#if defined(__COMPCERT_VERSION__)\n    #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)\n    #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #undef JSON_HEDLEY_PELLES_VERSION\n#endif\n#if defined(__POCC__)\n    #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)\n    #undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #undef JSON_HEDLEY_GCC_VERSION\n#endif\n#if \\\n    defined(JSON_HEDLEY_GNUC_VERSION) && \\\n    !defined(__clang__) && \\\n    !defined(JSON_HEDLEY_INTEL_VERSION) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_ARM_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL430_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \\\n    !defined(__COMPCERT__)\n    #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#endif\n#if \\\n    defined(__has_cpp_attribute) && \\\n    defined(__cplusplus) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#endif\n#if !defined(__cplusplus) || !defined(__has_cpp_attribute)\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#elif \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_IAR_VERSION) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \\\n    (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_BUILTIN)\n    #undef JSON_HEDLEY_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_FEATURE)\n    #undef JSON_HEDLEY_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_HAS_FEATURE(feature) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GCC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_EXTENSION)\n    #undef JSON_HEDLEY_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_WARNING)\n    #undef JSON_HEDLEY_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_HAS_WARNING(warning) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)\n    #undef JSON_HEDLEY_GNUC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_WARNING)\n    #undef JSON_HEDLEY_GCC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat\")\n#    if JSON_HEDLEY_HAS_WARNING(\"-Wc++17-extensions\")\n#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++17-extensions\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    else\n#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    endif\n#  endif\n#endif\n#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x\n#endif\n\n#if defined(JSON_HEDLEY_CONST_CAST)\n    #undef JSON_HEDLEY_CONST_CAST\n#endif\n#if defined(__cplusplus)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))\n#elif \\\n  JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\") || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_REINTERPRET_CAST)\n    #undef JSON_HEDLEY_REINTERPRET_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_CAST)\n    #undef JSON_HEDLEY_STATIC_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_CPP_CAST)\n    #undef JSON_HEDLEY_CPP_CAST\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wold-style-cast\")\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wold-style-cast\\\"\") \\\n    ((T) (expr)) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"diag_suppress=Pe137\") \\\n    JSON_HEDLEY_DIAGNOSTIC_POP \\\n#  else\n#    define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))\n#  endif\n#else\n#  define JSON_HEDLEY_CPP_CAST(T, expr) (expr)\n#endif\n\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))\n    #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_PRAGMA(value) __pragma(value)\n#else\n    #define JSON_HEDLEY_PRAGMA(value)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)\n    #undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#endif\n#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)\n    #undef JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"clang diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"clang diagnostic pop\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"GCC diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"GCC diagnostic pop\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))\n    #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))\n#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"pop\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"diag_push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"diag_pop\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH\n    #define JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wdeprecated-declarations\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"clang diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warning(disable:1478 1786)\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1291,1718\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,symdeprecated,symdeprecated2)\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress=Pe1444,Pe1215\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warn(disable:2241)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"clang diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"warning(disable:161)\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 1675\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"GCC diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress=Pe161\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-attributes\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"clang diagnostic ignored \\\"-Wunknown-attributes\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"warning(disable:1292)\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"error_messages(off,attrskipunsup)\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1173\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress=Pe1097\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"clang diagnostic ignored \\\"-Wcast-qual\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"warning(disable:2203 2331)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"GCC diagnostic ignored \\\"-Wcast-qual\\\"\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n\n#if defined(JSON_HEDLEY_DEPRECATED)\n    #undef JSON_HEDLEY_DEPRECATED\n#endif\n#if defined(JSON_HEDLEY_DEPRECATED_FOR)\n    #undef JSON_HEDLEY_DEPRECATED_FOR\n#endif\n#if JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated(\"Since \" # since))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated(\"Since \" #since \"; use \" #replacement))\n#elif defined(__cplusplus) && (__cplusplus >= 201402L)\n    #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since)]])\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since \"; use \" #replacement)]])\n#elif \\\n    JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__(\"Since \" #since)))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__(\"Since \" #since \"; use \" #replacement)))\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DEPRECATED(since) _Pragma(\"deprecated\")\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma(\"deprecated\")\n#else\n    #define JSON_HEDLEY_DEPRECATED(since)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)\n#endif\n\n#if defined(JSON_HEDLEY_UNAVAILABLE)\n    #undef JSON_HEDLEY_UNAVAILABLE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__(\"Not available until \" #available_since)))\n#else\n    #define JSON_HEDLEY_UNAVAILABLE(available_since)\n#endif\n\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#endif\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#endif\n#if (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))\n#elif defined(_Check_return_) /* SAL */\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_\n#else\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)\n#endif\n\n#if defined(JSON_HEDLEY_SENTINEL)\n    #undef JSON_HEDLEY_SENTINEL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0)\n    #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))\n#else\n    #define JSON_HEDLEY_SENTINEL(position)\n#endif\n\n#if defined(JSON_HEDLEY_NO_RETURN)\n    #undef JSON_HEDLEY_NO_RETURN\n#endif\n#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NO_RETURN __noreturn\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L\n    #define JSON_HEDLEY_NO_RETURN _Noreturn\n#elif defined(__cplusplus) && (__cplusplus >= 201103L)\n    #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"does_not_return\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"FUNC_NEVER_RETURNS;\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#else\n    #define JSON_HEDLEY_NO_RETURN\n#endif\n\n#if defined(JSON_HEDLEY_NO_ESCAPE)\n    #undef JSON_HEDLEY_NO_ESCAPE\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)\n    #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))\n#else\n    #define JSON_HEDLEY_NO_ESCAPE\n#endif\n\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #undef JSON_HEDLEY_UNREACHABLE\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)\n    #undef JSON_HEDLEY_UNREACHABLE_RETURN\n#endif\n#if defined(JSON_HEDLEY_ASSUME)\n    #undef JSON_HEDLEY_ASSUME\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_ASSUME(expr) __assume(expr)\n#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)\n    #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)\n#elif \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n    #if defined(__cplusplus)\n        #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)\n    #endif\n#endif\n#if \\\n    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5)\n    #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()\n#elif defined(JSON_HEDLEY_ASSUME)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n#if !defined(JSON_HEDLEY_ASSUME)\n    #if defined(JSON_HEDLEY_UNREACHABLE)\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)\n    #endif\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #if  \\\n        JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))\n    #else\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()\n    #endif\n#else\n    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)\n#endif\n#if !defined(JSON_HEDLEY_UNREACHABLE)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n\nJSON_HEDLEY_DIAGNOSTIC_PUSH\n#if JSON_HEDLEY_HAS_WARNING(\"-Wpedantic\")\n    #pragma clang diagnostic ignored \"-Wpedantic\"\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat-pedantic\") && defined(__cplusplus)\n    #pragma clang diagnostic ignored \"-Wc++98-compat-pedantic\"\n#endif\n#if JSON_HEDLEY_GCC_HAS_WARNING(\"-Wvariadic-macros\",4,0,0)\n    #if defined(__clang__)\n        #pragma clang diagnostic ignored \"-Wvariadic-macros\"\n    #elif defined(JSON_HEDLEY_GCC_VERSION)\n        #pragma GCC diagnostic ignored \"-Wvariadic-macros\"\n    #endif\n#endif\n#if defined(JSON_HEDLEY_NON_NULL)\n    #undef JSON_HEDLEY_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))\n#else\n    #define JSON_HEDLEY_NON_NULL(...)\n#endif\nJSON_HEDLEY_DIAGNOSTIC_POP\n\n#if defined(JSON_HEDLEY_PRINTF_FORMAT)\n    #undef JSON_HEDLEY_PRINTF_FORMAT\n#endif\n#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))\n#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(format) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))\n#else\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)\n#endif\n\n#if defined(JSON_HEDLEY_CONSTEXPR)\n    #undef JSON_HEDLEY_CONSTEXPR\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)\n    #endif\n#endif\n#if !defined(JSON_HEDLEY_CONSTEXPR)\n    #define JSON_HEDLEY_CONSTEXPR\n#endif\n\n#if defined(JSON_HEDLEY_PREDICT)\n    #undef JSON_HEDLEY_PREDICT\n#endif\n#if defined(JSON_HEDLEY_LIKELY)\n    #undef JSON_HEDLEY_LIKELY\n#endif\n#if defined(JSON_HEDLEY_UNLIKELY)\n    #undef JSON_HEDLEY_UNLIKELY\n#endif\n#if defined(JSON_HEDLEY_UNPREDICTABLE)\n    #undef JSON_HEDLEY_UNPREDICTABLE\n#endif\n#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))\n#endif\n#if \\\n  JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0)\n#  define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(  (expr), (value), (probability))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability)   __builtin_expect_with_probability(!!(expr),    1   , (probability))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability)  __builtin_expect_with_probability(!!(expr),    0   , (probability))\n#  define JSON_HEDLEY_LIKELY(expr)                      __builtin_expect                 (!!(expr),    1                  )\n#  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )\n#elif \\\n  JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \\\n  JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0)\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) \\\n    (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)\n#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)\n#else\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_LIKELY(expr) (!!(expr))\n#  define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))\n#endif\n#if !defined(JSON_HEDLEY_UNPREDICTABLE)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)\n#endif\n\n#if defined(JSON_HEDLEY_MALLOC)\n    #undef JSON_HEDLEY_MALLOC\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_MALLOC _Pragma(\"returns_new_memory\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0)\n    #define JSON_HEDLEY_MALLOC __declspec(restrict)\n#else\n    #define JSON_HEDLEY_MALLOC\n#endif\n\n#if defined(JSON_HEDLEY_PURE)\n    #undef JSON_HEDLEY_PURE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n#  define JSON_HEDLEY_PURE __attribute__((__pure__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n#  define JSON_HEDLEY_PURE _Pragma(\"does_not_write_global_data\")\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \\\n    )\n#  define JSON_HEDLEY_PURE _Pragma(\"FUNC_IS_PURE;\")\n#else\n#  define JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_CONST)\n    #undef JSON_HEDLEY_CONST\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(const) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_CONST __attribute__((__const__))\n#elif \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_CONST _Pragma(\"no_side_effect\")\n#else\n    #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_RESTRICT)\n    #undef JSON_HEDLEY_RESTRICT\n#endif\n#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT restrict\n#elif \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    defined(__clang__)\n    #define JSON_HEDLEY_RESTRICT __restrict\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT _Restrict\n#else\n    #define JSON_HEDLEY_RESTRICT\n#endif\n\n#if defined(JSON_HEDLEY_INLINE)\n    #undef JSON_HEDLEY_INLINE\n#endif\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    (defined(__cplusplus) && (__cplusplus >= 199711L))\n    #define JSON_HEDLEY_INLINE inline\n#elif \\\n    defined(JSON_HEDLEY_GCC_VERSION) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)\n    #define JSON_HEDLEY_INLINE __inline__\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_INLINE __inline\n#else\n    #define JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_ALWAYS_INLINE)\n    #undef JSON_HEDLEY_ALWAYS_INLINE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __forceinline\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n      JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n      JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \\\n    )\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"FUNC_ALWAYS_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"inline=forced\")\n#else\n#  define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_NEVER_INLINE)\n    #undef JSON_HEDLEY_NEVER_INLINE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"noinline\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"FUNC_CANNOT_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"inline=never\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#else\n    #define JSON_HEDLEY_NEVER_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_PRIVATE)\n    #undef JSON_HEDLEY_PRIVATE\n#endif\n#if defined(JSON_HEDLEY_PUBLIC)\n    #undef JSON_HEDLEY_PUBLIC\n#endif\n#if defined(JSON_HEDLEY_IMPORT)\n    #undef JSON_HEDLEY_IMPORT\n#endif\n#if defined(_WIN32) || defined(__CYGWIN__)\n#  define JSON_HEDLEY_PRIVATE\n#  define JSON_HEDLEY_PUBLIC   __declspec(dllexport)\n#  define JSON_HEDLEY_IMPORT   __declspec(dllimport)\n#else\n#  if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    ( \\\n      defined(__TI_EABI__) && \\\n      ( \\\n        (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \\\n      ) \\\n    )\n#    define JSON_HEDLEY_PRIVATE __attribute__((__visibility__(\"hidden\")))\n#    define JSON_HEDLEY_PUBLIC  __attribute__((__visibility__(\"default\")))\n#  else\n#    define JSON_HEDLEY_PRIVATE\n#    define JSON_HEDLEY_PUBLIC\n#  endif\n#  define JSON_HEDLEY_IMPORT    extern\n#endif\n\n#if defined(JSON_HEDLEY_NO_THROW)\n    #undef JSON_HEDLEY_NO_THROW\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NO_THROW __declspec(nothrow)\n#else\n    #define JSON_HEDLEY_NO_THROW\n#endif\n\n#if defined(JSON_HEDLEY_FALL_THROUGH)\n    #undef JSON_HEDLEY_FALL_THROUGH\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0)\n    #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])\n#elif defined(__fallthrough) /* SAL */\n    #define JSON_HEDLEY_FALL_THROUGH __fallthrough\n#else\n    #define JSON_HEDLEY_FALL_THROUGH\n#endif\n\n#if defined(JSON_HEDLEY_RETURNS_NON_NULL)\n    #undef JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)\n    #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))\n#elif defined(_Ret_notnull_) /* SAL */\n    #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_\n#else\n    #define JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n\n#if defined(JSON_HEDLEY_ARRAY_PARAM)\n    #undef JSON_HEDLEY_ARRAY_PARAM\n#endif\n#if \\\n    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \\\n    !defined(__STDC_NO_VLA__) && \\\n    !defined(__cplusplus) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_ARRAY_PARAM(name) (name)\n#else\n    #define JSON_HEDLEY_ARRAY_PARAM(name)\n#endif\n\n#if defined(JSON_HEDLEY_IS_CONSTANT)\n    #undef JSON_HEDLEY_IS_CONSTANT\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)\n    #undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#endif\n/* JSON_HEDLEY_IS_CONSTEXPR_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #undef JSON_HEDLEY_IS_CONSTEXPR_\n#endif\n#if \\\n    JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0)\n    #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)\n#endif\n#if !defined(__cplusplus)\n#  if \\\n       JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n       JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n       JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)\n#endif\n#  elif \\\n       ( \\\n          defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \\\n          !defined(JSON_HEDLEY_SUNPRO_VERSION) && \\\n          !defined(JSON_HEDLEY_PGI_VERSION) && \\\n          !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n       JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)\n#endif\n#  elif \\\n       defined(JSON_HEDLEY_GCC_VERSION) || \\\n       defined(JSON_HEDLEY_INTEL_VERSION) || \\\n       defined(JSON_HEDLEY_TINYC_VERSION) || \\\n       defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \\\n       JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \\\n       defined(JSON_HEDLEY_TI_CL2000_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL6X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL7X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \\\n       defined(__clang__)\n#    define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \\\n        sizeof(void) != \\\n        sizeof(*( \\\n                  1 ? \\\n                  ((void*) ((expr) * 0L) ) : \\\n((struct { char v[sizeof(void) * 2]; } *) 1) \\\n                ) \\\n              ) \\\n                                            )\n#  endif\n#endif\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))\n#else\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) (0)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_BEGIN_C_DECLS)\n    #undef JSON_HEDLEY_BEGIN_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_END_C_DECLS)\n    #undef JSON_HEDLEY_END_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_C_DECL)\n    #undef JSON_HEDLEY_C_DECL\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_BEGIN_C_DECLS extern \"C\" {\n    #define JSON_HEDLEY_END_C_DECLS }\n    #define JSON_HEDLEY_C_DECL extern \"C\"\n#else\n    #define JSON_HEDLEY_BEGIN_C_DECLS\n    #define JSON_HEDLEY_END_C_DECLS\n    #define JSON_HEDLEY_C_DECL\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_ASSERT)\n    #undef JSON_HEDLEY_STATIC_ASSERT\n#endif\n#if \\\n  !defined(__cplusplus) && ( \\\n      (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \\\n      JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \\\n      JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \\\n      JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n      defined(_Static_assert) \\\n    )\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)\n#elif \\\n  (defined(__cplusplus) && (__cplusplus >= 201103L)) || \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0)\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))\n#else\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message)\n#endif\n\n#if defined(JSON_HEDLEY_NULL)\n    #undef JSON_HEDLEY_NULL\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)\n    #elif defined(NULL)\n        #define JSON_HEDLEY_NULL NULL\n    #else\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)\n    #endif\n#elif defined(NULL)\n    #define JSON_HEDLEY_NULL NULL\n#else\n    #define JSON_HEDLEY_NULL ((void*) 0)\n#endif\n\n#if defined(JSON_HEDLEY_MESSAGE)\n    #undef JSON_HEDLEY_MESSAGE\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_MESSAGE(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(message msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)\n#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_WARNING)\n    #undef JSON_HEDLEY_WARNING\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_WARNING(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(clang warning msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_REQUIRE)\n    #undef JSON_HEDLEY_REQUIRE\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_MSG)\n    #undef JSON_HEDLEY_REQUIRE_MSG\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wgcc-compat\")\n#    define JSON_HEDLEY_REQUIRE(expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), #expr, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), msg, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, \"error\")))\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, \"error\")))\n#  endif\n#else\n#  define JSON_HEDLEY_REQUIRE(expr)\n#  define JSON_HEDLEY_REQUIRE_MSG(expr,msg)\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS)\n    #undef JSON_HEDLEY_FLAGS\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum)\n    #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS_CAST)\n    #undef JSON_HEDLEY_FLAGS_CAST\n#endif\n#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        _Pragma(\"warning(disable:188)\") \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)\n#endif\n\n#if defined(JSON_HEDLEY_EMPTY_BASES)\n    #undef JSON_HEDLEY_EMPTY_BASES\n#endif\n#if JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)\n    #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)\n#else\n    #define JSON_HEDLEY_EMPTY_BASES\n#endif\n\n/* Remaining macros are deprecated. */\n\n#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)\n#else\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)\n    #undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#endif\n#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)\n    #undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)\n    #undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#endif\n#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)\n    #undef JSON_HEDLEY_CLANG_HAS_WARNING\n#endif\n#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)\n\n#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */\n\n\n// This file contains all internal macro definitions\n// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them\n\n// exclude unsupported compilers\n#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)\n    #if defined(__clang__)\n        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400\n            #error \"unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))\n        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800\n            #error \"unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #endif\n#endif\n\n// C++ language standard detection\n#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)\n    #define JSON_HAS_CPP_20\n    #define JSON_HAS_CPP_17\n    #define JSON_HAS_CPP_14\n#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464\n    #define JSON_HAS_CPP_17\n    #define JSON_HAS_CPP_14\n#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)\n    #define JSON_HAS_CPP_14\n#endif\n\n// disable float-equal warnings on GCC/clang\n#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n\n// disable documentation warnings on clang\n#if defined(__clang__)\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wdocumentation\"\n#endif\n\n// allow to disable exceptions\n#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)\n    #define JSON_THROW(exception) throw exception\n    #define JSON_TRY try\n    #define JSON_CATCH(exception) catch(exception)\n    #define JSON_INTERNAL_CATCH(exception) catch(exception)\n#else\n    #include <cstdlib>\n    #define JSON_THROW(exception) std::abort()\n    #define JSON_TRY if(true)\n    #define JSON_CATCH(exception) if(false)\n    #define JSON_INTERNAL_CATCH(exception) if(false)\n#endif\n\n// override exception macros\n#if defined(JSON_THROW_USER)\n    #undef JSON_THROW\n    #define JSON_THROW JSON_THROW_USER\n#endif\n#if defined(JSON_TRY_USER)\n    #undef JSON_TRY\n    #define JSON_TRY JSON_TRY_USER\n#endif\n#if defined(JSON_CATCH_USER)\n    #undef JSON_CATCH\n    #define JSON_CATCH JSON_CATCH_USER\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_CATCH_USER\n#endif\n#if defined(JSON_INTERNAL_CATCH_USER)\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER\n#endif\n\n// allow to override assert\n#if !defined(JSON_ASSERT)\n    #include <cassert> // assert\n    #define JSON_ASSERT(x) assert(x)\n#endif\n\n/*!\n@brief macro to briefly define a mapping between an enum and JSON\n@def NLOHMANN_JSON_SERIALIZE_ENUM\n@since version 3.4.0\n*/\n#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                            \\\n    template<typename BasicJsonType>                                                            \\\n    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                   \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool  \\\n        {                                                                                       \\\n            return ej_pair.first == e;                                                          \\\n        });                                                                                     \\\n        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                 \\\n    }                                                                                           \\\n    template<typename BasicJsonType>                                                            \\\n    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                 \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \\\n        {                                                                                       \\\n            return ej_pair.second == j;                                                         \\\n        });                                                                                     \\\n        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                  \\\n    }\n\n// Ugly macros to avoid uglier copy-paste when specializing basic_json. They\n// may be removed in the future once the class is split.\n\n#define NLOHMANN_BASIC_JSON_TPL_DECLARATION                                \\\n    template<template<typename, typename, typename...> class ObjectType,   \\\n             template<typename, typename...> class ArrayType,              \\\n             class StringType, class BooleanType, class NumberIntegerType, \\\n             class NumberUnsignedType, class NumberFloatType,              \\\n             template<typename> class AllocatorType,                       \\\n             template<typename, typename = void> class JSONSerializer,     \\\n             class BinaryType>\n\n#define NLOHMANN_BASIC_JSON_TPL                                            \\\n    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \\\n    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \\\n    AllocatorType, JSONSerializer, BinaryType>\n\n// Macros to simplify conversion from/to types\n\n#define NLOHMANN_JSON_EXPAND( x ) x\n#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME\n#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \\\n        NLOHMANN_JSON_PASTE64, \\\n        NLOHMANN_JSON_PASTE63, \\\n        NLOHMANN_JSON_PASTE62, \\\n        NLOHMANN_JSON_PASTE61, \\\n        NLOHMANN_JSON_PASTE60, \\\n        NLOHMANN_JSON_PASTE59, \\\n        NLOHMANN_JSON_PASTE58, \\\n        NLOHMANN_JSON_PASTE57, \\\n        NLOHMANN_JSON_PASTE56, \\\n        NLOHMANN_JSON_PASTE55, \\\n        NLOHMANN_JSON_PASTE54, \\\n        NLOHMANN_JSON_PASTE53, \\\n        NLOHMANN_JSON_PASTE52, \\\n        NLOHMANN_JSON_PASTE51, \\\n        NLOHMANN_JSON_PASTE50, \\\n        NLOHMANN_JSON_PASTE49, \\\n        NLOHMANN_JSON_PASTE48, \\\n        NLOHMANN_JSON_PASTE47, \\\n        NLOHMANN_JSON_PASTE46, \\\n        NLOHMANN_JSON_PASTE45, \\\n        NLOHMANN_JSON_PASTE44, \\\n        NLOHMANN_JSON_PASTE43, \\\n        NLOHMANN_JSON_PASTE42, \\\n        NLOHMANN_JSON_PASTE41, \\\n        NLOHMANN_JSON_PASTE40, \\\n        NLOHMANN_JSON_PASTE39, \\\n        NLOHMANN_JSON_PASTE38, \\\n        NLOHMANN_JSON_PASTE37, \\\n        NLOHMANN_JSON_PASTE36, \\\n        NLOHMANN_JSON_PASTE35, \\\n        NLOHMANN_JSON_PASTE34, \\\n        NLOHMANN_JSON_PASTE33, \\\n        NLOHMANN_JSON_PASTE32, \\\n        NLOHMANN_JSON_PASTE31, \\\n        NLOHMANN_JSON_PASTE30, \\\n        NLOHMANN_JSON_PASTE29, \\\n        NLOHMANN_JSON_PASTE28, \\\n        NLOHMANN_JSON_PASTE27, \\\n        NLOHMANN_JSON_PASTE26, \\\n        NLOHMANN_JSON_PASTE25, \\\n        NLOHMANN_JSON_PASTE24, \\\n        NLOHMANN_JSON_PASTE23, \\\n        NLOHMANN_JSON_PASTE22, \\\n        NLOHMANN_JSON_PASTE21, \\\n        NLOHMANN_JSON_PASTE20, \\\n        NLOHMANN_JSON_PASTE19, \\\n        NLOHMANN_JSON_PASTE18, \\\n        NLOHMANN_JSON_PASTE17, \\\n        NLOHMANN_JSON_PASTE16, \\\n        NLOHMANN_JSON_PASTE15, \\\n        NLOHMANN_JSON_PASTE14, \\\n        NLOHMANN_JSON_PASTE13, \\\n        NLOHMANN_JSON_PASTE12, \\\n        NLOHMANN_JSON_PASTE11, \\\n        NLOHMANN_JSON_PASTE10, \\\n        NLOHMANN_JSON_PASTE9, \\\n        NLOHMANN_JSON_PASTE8, \\\n        NLOHMANN_JSON_PASTE7, \\\n        NLOHMANN_JSON_PASTE6, \\\n        NLOHMANN_JSON_PASTE5, \\\n        NLOHMANN_JSON_PASTE4, \\\n        NLOHMANN_JSON_PASTE3, \\\n        NLOHMANN_JSON_PASTE2, \\\n        NLOHMANN_JSON_PASTE1)(__VA_ARGS__))\n#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)\n#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)\n#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)\n#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)\n#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)\n#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)\n#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)\n#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)\n#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)\n#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)\n#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)\n#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)\n#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)\n#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)\n#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)\n#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)\n#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)\n#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)\n#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)\n#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)\n#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)\n#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)\n#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)\n#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)\n#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)\n#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)\n#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)\n#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)\n#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)\n#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)\n#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)\n#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)\n#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)\n#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)\n#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)\n#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)\n#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)\n#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)\n#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)\n#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)\n#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)\n#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)\n#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)\n#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)\n#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)\n#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)\n#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)\n#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)\n#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)\n#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)\n#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)\n#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)\n#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)\n#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)\n#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)\n#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)\n#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)\n#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)\n#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)\n#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)\n#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)\n#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)\n#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)\n\n#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;\n#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);\n\n/*!\n@brief macro\n@def NLOHMANN_DEFINE_TYPE_INTRUSIVE\n@since version 3.9.0\n*/\n#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \\\n    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }\n\n/*!\n@brief macro\n@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE\n@since version 3.9.0\n*/\n#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \\\n    inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }\n\n#ifndef JSON_USE_IMPLICIT_CONVERSIONS\n    #define JSON_USE_IMPLICIT_CONVERSIONS 1\n#endif\n\n#if JSON_USE_IMPLICIT_CONVERSIONS\n    #define JSON_EXPLICIT\n#else\n    #define JSON_EXPLICIT explicit\n#endif\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////////\n// exceptions //\n////////////////\n\n/*!\n@brief general exception of the @ref basic_json class\n\nThis class is an extension of `std::exception` objects with a member @a id for\nexception ids. It is used as the base class for all exceptions thrown by the\n@ref basic_json class. This class can hence be used as \"wildcard\" to catch\nexceptions.\n\nSubclasses:\n- @ref parse_error for exceptions indicating a parse error\n- @ref invalid_iterator for exceptions indicating errors with iterators\n- @ref type_error for exceptions indicating executing a member function with\n                  a wrong type\n- @ref out_of_range for exceptions indicating access out of the defined range\n- @ref other_error for exceptions indicating other library errors\n\n@internal\n@note To have nothrow-copy-constructible exceptions, we internally use\n      `std::runtime_error` which can cope with arbitrary-length error messages.\n      Intermediate strings are built with static functions and then passed to\n      the actual constructor.\n@endinternal\n\n@liveexample{The following code shows how arbitrary library exceptions can be\ncaught.,exception}\n\n@since version 3.0.0\n*/\nclass exception : public std::exception\n{\n  public:\n    /// returns the explanatory string\n    JSON_HEDLEY_RETURNS_NON_NULL\n    const char* what() const noexcept override\n    {\n        return m.what();\n    }\n\n    /// the id of the exception\n    const int id;\n\n  protected:\n    JSON_HEDLEY_NON_NULL(3)\n    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}\n\n    static std::string name(const std::string& ename, int id_)\n    {\n        return \"[json.exception.\" + ename + \".\" + std::to_string(id_) + \"] \";\n    }\n\n  private:\n    /// an exception object as storage for error messages\n    std::runtime_error m;\n};\n\n/*!\n@brief exception indicating a parse error\n\nThis exception is thrown by the library when a parse error occurs. Parse errors\ncan occur during the deserialization of JSON text, CBOR, MessagePack, as well\nas when using JSON Patch.\n\nMember @a byte holds the byte index of the last read character in the input\nfile.\n\nExceptions have ids 1xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.\njson.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\\uxxxx` entries (\"surrogate pairs\"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.\njson.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.\njson.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.\njson.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one \"op\" member, whose value indicates the operation to perform. Its value must be one of \"add\", \"remove\", \"replace\", \"move\", \"copy\", or \"test\"; other values are errors.\njson.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.\njson.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.\njson.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.\njson.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.\njson.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.\njson.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.\njson.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.\njson.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet).\njson.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed.\n\n@note For an input with n bytes, 1 is the index of the first character and n+1\n      is the index of the terminating null byte or the end of file. This also\n      holds true when reading a byte vector (CBOR or MessagePack).\n\n@liveexample{The following code shows how a `parse_error` exception can be\ncaught.,parse_error}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass parse_error : public exception\n{\n  public:\n    /*!\n    @brief create a parse error exception\n    @param[in] id_       the id of the exception\n    @param[in] pos       the position where the error occurred (or with\n                         chars_read_total=0 if the position cannot be\n                         determined)\n    @param[in] what_arg  the explanatory string\n    @return parse_error object\n    */\n    static parse_error create(int id_, const position_t& pos, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        position_string(pos) + \": \" + what_arg;\n        return parse_error(id_, pos.chars_read_total, w.c_str());\n    }\n\n    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        (byte_ != 0 ? (\" at byte \" + std::to_string(byte_)) : \"\") +\n                        \": \" + what_arg;\n        return parse_error(id_, byte_, w.c_str());\n    }\n\n    /*!\n    @brief byte index of the parse error\n\n    The byte index of the last read character in the input file.\n\n    @note For an input with n bytes, 1 is the index of the first character and\n          n+1 is the index of the terminating null byte or the end of file.\n          This also holds true when reading a byte vector (CBOR or MessagePack).\n    */\n    const std::size_t byte;\n\n  private:\n    parse_error(int id_, std::size_t byte_, const char* what_arg)\n        : exception(id_, what_arg), byte(byte_) {}\n\n    static std::string position_string(const position_t& pos)\n    {\n        return \" at line \" + std::to_string(pos.lines_read + 1) +\n               \", column \" + std::to_string(pos.chars_read_current_line);\n    }\n};\n\n/*!\n@brief exception indicating errors with iterators\n\nThis exception is thrown if iterators passed to a library function do not match\nthe expected semantics.\n\nExceptions have ids 2xx.\n\nname / id                           | example message | description\n----------------------------------- | --------------- | -------------------------\njson.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.\njson.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.\njson.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.\njson.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.\njson.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.\njson.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.\njson.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.\njson.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.\njson.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.\njson.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().\n\n@liveexample{The following code shows how an `invalid_iterator` exception can be\ncaught.,invalid_iterator}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass invalid_iterator : public exception\n{\n  public:\n    static invalid_iterator create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"invalid_iterator\", id_) + what_arg;\n        return invalid_iterator(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    invalid_iterator(int id_, const char* what_arg)\n        : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating executing a member function with a wrong type\n\nThis exception is thrown in case of a type error; that is, a library function is\nexecuted on a JSON value whose type does not match the expected semantics.\n\nExceptions have ids 3xx.\n\nname / id                     | example message | description\n----------------------------- | --------------- | -------------------------\njson.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.\njson.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.\njson.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &.\njson.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.\njson.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.\njson.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.\njson.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.\njson.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.\njson.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.\njson.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.\njson.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.\njson.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.\njson.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.\njson.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.\njson.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.\njson.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |\njson.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |\n\n@liveexample{The following code shows how a `type_error` exception can be\ncaught.,type_error}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass type_error : public exception\n{\n  public:\n    static type_error create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"type_error\", id_) + what_arg;\n        return type_error(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating access out of the defined range\n\nThis exception is thrown in case a library function is called on an input\nparameter that exceeds the expected range, for instance in case of array\nindices or nonexisting object keys.\n\nExceptions have ids 4xx.\n\nname / id                       | example message | description\n------------------------------- | --------------- | -------------------------\njson.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.\njson.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.\njson.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.\njson.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.\njson.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.\njson.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.\njson.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) |\njson.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |\njson.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |\n\n@liveexample{The following code shows how an `out_of_range` exception can be\ncaught.,out_of_range}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass out_of_range : public exception\n{\n  public:\n    static out_of_range create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"out_of_range\", id_) + what_arg;\n        return out_of_range(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating other library errors\n\nThis exception is thrown in case of errors that cannot be classified with the\nother exception types.\n\nExceptions have ids 5xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.other_error.501 | unsuccessful: {\"op\":\"test\",\"path\":\"/baz\", \"value\":\"bar\"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n\n@liveexample{The following code shows how an `other_error` exception can be\ncaught.,other_error}\n\n@since version 3.0.0\n*/\nclass other_error : public exception\n{\n  public:\n    static other_error create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"other_error\", id_) + what_arg;\n        return other_error(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n\n#include <cstddef> // size_t\n#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// alias templates to reduce boilerplate\ntemplate<bool B, typename T = void>\nusing enable_if_t = typename std::enable_if<B, T>::type;\n\ntemplate<typename T>\nusing uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;\n\n// implementation of C++14 index_sequence and affiliates\n// source: https://stackoverflow.com/a/32223343\ntemplate<std::size_t... Ints>\nstruct index_sequence\n{\n    using type = index_sequence;\n    using value_type = std::size_t;\n    static constexpr std::size_t size() noexcept\n    {\n        return sizeof...(Ints);\n    }\n};\n\ntemplate<class Sequence1, class Sequence2>\nstruct merge_and_renumber;\n\ntemplate<std::size_t... I1, std::size_t... I2>\nstruct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>\n        : index_sequence < I1..., (sizeof...(I1) + I2)... > {};\n\ntemplate<std::size_t N>\nstruct make_index_sequence\n    : merge_and_renumber < typename make_index_sequence < N / 2 >::type,\n      typename make_index_sequence < N - N / 2 >::type > {};\n\ntemplate<> struct make_index_sequence<0> : index_sequence<> {};\ntemplate<> struct make_index_sequence<1> : index_sequence<0> {};\n\ntemplate<typename... Ts>\nusing index_sequence_for = make_index_sequence<sizeof...(Ts)>;\n\n// dispatch utility (taken from ranges-v3)\ntemplate<unsigned N> struct priority_tag : priority_tag < N - 1 > {};\ntemplate<> struct priority_tag<0> {};\n\n// taken from ranges-v3\ntemplate<typename T>\nstruct static_const\n{\n    static constexpr T value{};\n};\n\ntemplate<typename T>\nconstexpr T static_const<T>::value;\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\n#include <limits> // numeric_limits\n#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type\n#include <utility> // declval\n\n// #include <nlohmann/detail/iterators/iterator_traits.hpp>\n\n\n#include <iterator> // random_access_iterator_tag\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename ...Ts> struct make_void\n{\n    using type = void;\n};\ntemplate<typename ...Ts> using void_t = typename make_void<Ts...>::type;\n} // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename It, typename = void>\nstruct iterator_types {};\n\ntemplate<typename It>\nstruct iterator_types <\n    It,\n    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,\n    typename It::reference, typename It::iterator_category >>\n{\n    using difference_type = typename It::difference_type;\n    using value_type = typename It::value_type;\n    using pointer = typename It::pointer;\n    using reference = typename It::reference;\n    using iterator_category = typename It::iterator_category;\n};\n\n// This is required as some compilers implement std::iterator_traits in a way that\n// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.\ntemplate<typename T, typename = void>\nstruct iterator_traits\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>\n            : iterator_types<T>\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>\n{\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = T;\n    using difference_type = ptrdiff_t;\n    using pointer = T*;\n    using reference = T&;\n};\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n\n#include <type_traits>\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n\n\n// https://en.cppreference.com/w/cpp/experimental/is_detected\nnamespace nlohmann\n{\nnamespace detail\n{\nstruct nonesuch\n{\n    nonesuch() = delete;\n    ~nonesuch() = delete;\n    nonesuch(nonesuch const&) = delete;\n    nonesuch(nonesuch const&&) = delete;\n    void operator=(nonesuch const&) = delete;\n    void operator=(nonesuch&&) = delete;\n};\n\ntemplate<class Default,\n         class AlwaysVoid,\n         template<class...> class Op,\n         class... Args>\nstruct detector\n{\n    using value_t = std::false_type;\n    using type = Default;\n};\n\ntemplate<class Default, template<class...> class Op, class... Args>\nstruct detector<Default, void_t<Op<Args...>>, Op, Args...>\n{\n    using value_t = std::true_type;\n    using type = Op<Args...>;\n};\n\ntemplate<template<class...> class Op, class... Args>\nusing is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;\n\ntemplate<template<class...> class Op, class... Args>\nusing detected_t = typename detector<nonesuch, void, Op, Args...>::type;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or = detector<Default, void, Op, Args...>;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or_t = typename detected_or<Default, Op, Args...>::type;\n\ntemplate<class Expected, template<class...> class Op, class... Args>\nusing is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;\n\ntemplate<class To, template<class...> class Op, class... Args>\nusing is_detected_convertible =\n    std::is_convertible<detected_t<Op, Args...>, To>;\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/json_fwd.hpp>\n#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_\n#define INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n#include <cstdint> // int64_t, uint64_t\n#include <map> // map\n#include <memory> // allocator\n#include <string> // string\n#include <vector> // vector\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n/*!\n@brief default JSONSerializer template argument\n\nThis serializer ignores the template arguments and uses ADL\n([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))\nfor serialization.\n*/\ntemplate<typename T = void, typename SFINAE = void>\nstruct adl_serializer;\n\ntemplate<template<typename U, typename V, typename... Args> class ObjectType =\n         std::map,\n         template<typename U, typename... Args> class ArrayType = std::vector,\n         class StringType = std::string, class BooleanType = bool,\n         class NumberIntegerType = std::int64_t,\n         class NumberUnsignedType = std::uint64_t,\n         class NumberFloatType = double,\n         template<typename U> class AllocatorType = std::allocator,\n         template<typename T, typename SFINAE = void> class JSONSerializer =\n         adl_serializer,\n         class BinaryType = std::vector<std::uint8_t>>\nclass basic_json;\n\n/*!\n@brief JSON Pointer\n\nA JSON pointer defines a string syntax for identifying a specific value\nwithin a JSON document. It can be used with functions `at` and\n`operator[]`. Furthermore, JSON pointers are the base for JSON patches.\n\n@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)\n\n@since version 2.0.0\n*/\ntemplate<typename BasicJsonType>\nclass json_pointer;\n\n/*!\n@brief default JSON class\n\nThis type is the default specialization of the @ref basic_json class which\nuses the standard template types.\n\n@since version 1.0.0\n*/\nusing json = basic_json<>;\n\ntemplate<class Key, class T, class IgnoredLess, class Allocator>\nstruct ordered_map;\n\n/*!\n@brief ordered JSON class\n\nThis type preserves the insertion order of object keys.\n\n@since version 3.9.0\n*/\nusing ordered_json = basic_json<nlohmann::ordered_map>;\n\n}  // namespace nlohmann\n\n#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n\nnamespace nlohmann\n{\n/*!\n@brief detail namespace with internal helper functions\n\nThis namespace collects functions that should not be exposed,\nimplementations of some @ref basic_json methods, and meta-programming helpers.\n\n@since version 2.1.0\n*/\nnamespace detail\n{\n/////////////\n// helpers //\n/////////////\n\n// Note to maintainers:\n//\n// Every trait in this file expects a non CV-qualified type.\n// The only exceptions are in the 'aliases for detected' section\n// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))\n//\n// In this case, T has to be properly CV-qualified to constraint the function arguments\n// (e.g. to_json(BasicJsonType&, const T&))\n\ntemplate<typename> struct is_basic_json : std::false_type {};\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstruct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};\n\n//////////////////////\n// json_ref helpers //\n//////////////////////\n\ntemplate<typename>\nclass json_ref;\n\ntemplate<typename>\nstruct is_json_ref : std::false_type {};\n\ntemplate<typename T>\nstruct is_json_ref<json_ref<T>> : std::true_type {};\n\n//////////////////////////\n// aliases for detected //\n//////////////////////////\n\ntemplate<typename T>\nusing mapped_type_t = typename T::mapped_type;\n\ntemplate<typename T>\nusing key_type_t = typename T::key_type;\n\ntemplate<typename T>\nusing value_type_t = typename T::value_type;\n\ntemplate<typename T>\nusing difference_type_t = typename T::difference_type;\n\ntemplate<typename T>\nusing pointer_t = typename T::pointer;\n\ntemplate<typename T>\nusing reference_t = typename T::reference;\n\ntemplate<typename T>\nusing iterator_category_t = typename T::iterator_category;\n\ntemplate<typename T>\nusing iterator_t = typename T::iterator;\n\ntemplate<typename T, typename... Args>\nusing to_json_function = decltype(T::to_json(std::declval<Args>()...));\n\ntemplate<typename T, typename... Args>\nusing from_json_function = decltype(T::from_json(std::declval<Args>()...));\n\ntemplate<typename T, typename U>\nusing get_template_function = decltype(std::declval<T>().template get<U>());\n\n// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_from_json : std::false_type {};\n\n// trait checking if j.get<T> is valid\n// use this trait instead of std::is_constructible or std::is_convertible,\n// both rely on, or make use of implicit conversions, and thus fail when T\n// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)\ntemplate <typename BasicJsonType, typename T>\nstruct is_getable\n{\n    static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;\n};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_from_json < BasicJsonType, T,\n           enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, from_json_function, serializer,\n        const BasicJsonType&, T&>::value;\n};\n\n// This trait checks if JSONSerializer<T>::from_json(json const&) exists\n// this overload is used for non-default-constructible user-defined-types\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_non_default_from_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<T, from_json_function, serializer,\n        const BasicJsonType&>::value;\n};\n\n// This trait checks if BasicJsonType::json_serializer<T>::to_json exists\n// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_to_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,\n        T>::value;\n};\n\n\n///////////////////\n// is_ functions //\n///////////////////\n\ntemplate<typename T, typename = void>\nstruct is_iterator_traits : std::false_type {};\n\ntemplate<typename T>\nstruct is_iterator_traits<iterator_traits<T>>\n{\n  private:\n    using traits = iterator_traits<T>;\n\n  public:\n    static constexpr auto value =\n        is_detected<value_type_t, traits>::value &&\n        is_detected<difference_type_t, traits>::value &&\n        is_detected<pointer_t, traits>::value &&\n        is_detected<iterator_category_t, traits>::value &&\n        is_detected<reference_t, traits>::value;\n};\n\n// source: https://stackoverflow.com/a/37193089/4116453\n\ntemplate<typename T, typename = void>\nstruct is_complete_type : std::false_type {};\n\ntemplate<typename T>\nstruct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType,\n         typename = void>\nstruct is_compatible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type_impl <\n    BasicJsonType, CompatibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&\n    is_detected<key_type_t, CompatibleObjectType>::value >>\n{\n\n    using object_t = typename BasicJsonType::object_t;\n\n    // macOS's is_constructible does not play well with nonesuch...\n    static constexpr bool value =\n        std::is_constructible<typename object_t::key_type,\n        typename CompatibleObjectType::key_type>::value &&\n        std::is_constructible<typename object_t::mapped_type,\n        typename CompatibleObjectType::mapped_type>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type\n    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         typename = void>\nstruct is_constructible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type_impl <\n    BasicJsonType, ConstructibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&\n    is_detected<key_type_t, ConstructibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    static constexpr bool value =\n        (std::is_default_constructible<ConstructibleObjectType>::value &&\n         (std::is_move_assignable<ConstructibleObjectType>::value ||\n          std::is_copy_assignable<ConstructibleObjectType>::value) &&\n         (std::is_constructible<typename ConstructibleObjectType::key_type,\n          typename object_t::key_type>::value &&\n          std::is_same <\n          typename object_t::mapped_type,\n          typename ConstructibleObjectType::mapped_type >::value)) ||\n        (has_from_json<BasicJsonType,\n         typename ConstructibleObjectType::mapped_type>::value ||\n         has_non_default_from_json <\n         BasicJsonType,\n         typename ConstructibleObjectType::mapped_type >::value);\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type\n    : is_constructible_object_type_impl<BasicJsonType,\n      ConstructibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleStringType,\n         typename = void>\nstruct is_compatible_string_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleStringType>\nstruct is_compatible_string_type_impl <\n    BasicJsonType, CompatibleStringType,\n    enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,\n    value_type_t, CompatibleStringType>::value >>\n{\n    static constexpr auto value =\n        std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType>\nstruct is_compatible_string_type\n    : is_compatible_string_type_impl<BasicJsonType, ConstructibleStringType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType,\n         typename = void>\nstruct is_constructible_string_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type_impl <\n    BasicJsonType, ConstructibleStringType,\n    enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,\n    value_type_t, ConstructibleStringType>::value >>\n{\n    static constexpr auto value =\n        std::is_constructible<ConstructibleStringType,\n        typename BasicJsonType::string_t>::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type\n    : is_constructible_string_type_impl<BasicJsonType, ConstructibleStringType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType, typename = void>\nstruct is_compatible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type_impl <\n    BasicJsonType, CompatibleArrayType,\n    enable_if_t < is_detected<value_type_t, CompatibleArrayType>::value&&\n    is_detected<iterator_t, CompatibleArrayType>::value&&\n// This is needed because json_reverse_iterator has a ::iterator type...\n// Therefore it is detected as a CompatibleArrayType.\n// The real fix would be to have an Iterable concept.\n    !is_iterator_traits <\n    iterator_traits<CompatibleArrayType >>::value >>\n{\n    static constexpr bool value =\n        std::is_constructible<BasicJsonType,\n        typename CompatibleArrayType::value_type>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type\n    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType, typename = void>\nstruct is_constructible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t<std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value >>\n            : std::true_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t < !std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value&&\n    std::is_default_constructible<ConstructibleArrayType>::value&&\n(std::is_move_assignable<ConstructibleArrayType>::value ||\n std::is_copy_assignable<ConstructibleArrayType>::value)&&\nis_detected<value_type_t, ConstructibleArrayType>::value&&\nis_detected<iterator_t, ConstructibleArrayType>::value&&\nis_complete_type <\ndetected_t<value_type_t, ConstructibleArrayType >>::value >>\n{\n    static constexpr bool value =\n        // This is needed because json_reverse_iterator has a ::iterator type,\n        // furthermore, std::back_insert_iterator (and other iterators) have a\n        // base class `iterator`... Therefore it is detected as a\n        // ConstructibleArrayType. The real fix would be to have an Iterable\n        // concept.\n        !is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value &&\n\n        (std::is_same<typename ConstructibleArrayType::value_type,\n         typename BasicJsonType::array_t::value_type>::value ||\n         has_from_json<BasicJsonType,\n         typename ConstructibleArrayType::value_type>::value ||\n         has_non_default_from_json <\n         BasicJsonType, typename ConstructibleArrayType::value_type >::value);\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type\n    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType,\n         typename = void>\nstruct is_compatible_integer_type_impl : std::false_type {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type_impl <\n    RealIntegerType, CompatibleNumberIntegerType,\n    enable_if_t < std::is_integral<RealIntegerType>::value&&\n    std::is_integral<CompatibleNumberIntegerType>::value&&\n    !std::is_same<bool, CompatibleNumberIntegerType>::value >>\n{\n    // is there an assert somewhere on overflows?\n    using RealLimits = std::numeric_limits<RealIntegerType>;\n    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;\n\n    static constexpr auto value =\n        std::is_constructible<RealIntegerType,\n        CompatibleNumberIntegerType>::value &&\n        CompatibleLimits::is_integer &&\n        RealLimits::is_signed == CompatibleLimits::is_signed;\n};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type\n    : is_compatible_integer_type_impl<RealIntegerType,\n      CompatibleNumberIntegerType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleType, typename = void>\nstruct is_compatible_type_impl: std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type_impl <\n    BasicJsonType, CompatibleType,\n    enable_if_t<is_complete_type<CompatibleType>::value >>\n{\n    static constexpr bool value =\n        has_to_json<BasicJsonType, CompatibleType>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type\n    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};\n\n// https://en.cppreference.com/w/cpp/types/conjunction\ntemplate<class...> struct conjunction : std::true_type { };\ntemplate<class B1> struct conjunction<B1> : B1 { };\ntemplate<class B1, class... Bn>\nstruct conjunction<B1, Bn...>\n: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};\n\ntemplate<typename T1, typename T2>\nstruct is_constructible_tuple : std::false_type {};\n\ntemplate<typename T1, typename... Args>\nstruct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<std::is_constructible<T1, Args>...> {};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t\n#include <string> // string\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////////////\n// JSON type enumeration //\n///////////////////////////\n\n/*!\n@brief the JSON type enumeration\n\nThis enumeration collects the different JSON types. It is internally used to\ndistinguish the stored values, and the functions @ref basic_json::is_null(),\n@ref basic_json::is_object(), @ref basic_json::is_array(),\n@ref basic_json::is_string(), @ref basic_json::is_boolean(),\n@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),\n@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),\n@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and\n@ref basic_json::is_structured() rely on it.\n\n@note There are three enumeration entries (number_integer, number_unsigned, and\nnumber_float), because the library distinguishes these three types for numbers:\n@ref basic_json::number_unsigned_t is used for unsigned integers,\n@ref basic_json::number_integer_t is used for signed integers, and\n@ref basic_json::number_float_t is used for floating-point numbers or to\napproximate integers which do not fit in the limits of their respective type.\n\n@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON\nvalue with the default value for a given type\n\n@since version 1.0.0\n*/\nenum class value_t : std::uint8_t\n{\n    null,             ///< null value\n    object,           ///< object (unordered set of name/value pairs)\n    array,            ///< array (ordered collection of values)\n    string,           ///< string value\n    boolean,          ///< boolean value\n    number_integer,   ///< number value (signed integer)\n    number_unsigned,  ///< number value (unsigned integer)\n    number_float,     ///< number value (floating-point)\n    binary,           ///< binary array (ordered collection of bytes)\n    discarded         ///< discarded by the parser callback function\n};\n\n/*!\n@brief comparison operator for JSON types\n\nReturns an ordering that is similar to Python:\n- order: null < boolean < number < object < array < string < binary\n- furthermore, each type is not smaller than itself\n- discarded values are not comparable\n- binary is represented as a b\"\" string in python and directly comparable to a\n  string; however, making a binary array directly comparable with a string would\n  be surprising behavior in a JSON file.\n\n@since version 1.0.0\n*/\ninline bool operator<(const value_t lhs, const value_t rhs) noexcept\n{\n    static constexpr std::array<std::uint8_t, 9> order = {{\n            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,\n            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,\n            6 /* binary */\n        }\n    };\n\n    const auto l_index = static_cast<std::size_t>(lhs);\n    const auto r_index = static_cast<std::size_t>(rhs);\n    return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];\n}\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename std::nullptr_t& n)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_null()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be null, but is \" + std::string(j.type_name())));\n    }\n    n = nullptr;\n}\n\n// overloads for basic_json template parameters\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t < std::is_arithmetic<ArithmeticType>::value&&\n                         !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n                         int > = 0 >\nvoid get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name())));\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(j.type_name())));\n    }\n    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name())));\n    }\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate <\n    typename BasicJsonType, typename ConstructibleStringType,\n    enable_if_t <\n        is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&&\n        !std::is_same<typename BasicJsonType::string_t,\n                      ConstructibleStringType>::value,\n        int > = 0 >\nvoid from_json(const BasicJsonType& j, ConstructibleStringType& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name())));\n    }\n\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, EnumType& e)\n{\n    typename std::underlying_type<EnumType>::type val;\n    get_arithmetic_value(j, val);\n    e = static_cast<EnumType>(val);\n}\n\n// forward_list doesn't have an insert method\ntemplate<typename BasicJsonType, typename T, typename Allocator,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    l.clear();\n    std::transform(j.rbegin(), j.rend(),\n                   std::front_inserter(l), [](const BasicJsonType & i)\n    {\n        return i.template get<T>();\n    });\n}\n\n// valarray doesn't have an insert method\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::valarray<T>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    l.resize(j.size());\n    std::transform(j.begin(), j.end(), std::begin(l),\n                   [](const BasicJsonType & elem)\n    {\n        return elem.template get<T>();\n    });\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json(const BasicJsonType& j, T (&arr)[N])\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)\n{\n    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,\n                          priority_tag<2> /*unused*/)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nauto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)\n-> decltype(\n    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),\n    j.template get<typename ConstructibleArrayType::value_type>(),\n    void())\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    ret.reserve(j.size());\n    std::transform(j.begin(), j.end(),\n                   std::inserter(ret, end(ret)), [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nvoid from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,\n                          priority_tag<0> /*unused*/)\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    std::transform(\n        j.begin(), j.end(), std::inserter(ret, end(ret)),\n        [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate < typename BasicJsonType, typename ConstructibleArrayType,\n           enable_if_t <\n               is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&\n               !is_basic_json<ConstructibleArrayType>::value,\n               int > = 0 >\nauto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)\n-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),\nj.template get<typename ConstructibleArrayType::value_type>(),\nvoid())\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" +\n                                      std::string(j.type_name())));\n    }\n\n    from_json_array_impl(j, arr, priority_tag<3> {});\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(j.type_name())));\n    }\n\n    bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, ConstructibleObjectType& obj)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_object()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be object, but is \" + std::string(j.type_name())));\n    }\n\n    ConstructibleObjectType ret;\n    auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();\n    using value_type = typename ConstructibleObjectType::value_type;\n    std::transform(\n        inner_object->begin(), inner_object->end(),\n        std::inserter(ret, ret.begin()),\n        [](typename BasicJsonType::object_t::value_type const & p)\n    {\n        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());\n    });\n    obj = std::move(ret);\n}\n\n// overload for arithmetic types, not chosen for basic_json template arguments\n// (BooleanType, etc..); note: Is it really necessary to provide explicit\n// overloads for boolean_t etc. in case of a custom BooleanType which is not\n// an arithmetic type?\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t <\n               std::is_arithmetic<ArithmeticType>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n               int > = 0 >\nvoid from_json(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n        case value_t::boolean:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());\n            break;\n        }\n\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name())));\n    }\n}\n\ntemplate<typename BasicJsonType, typename A1, typename A2>\nvoid from_json(const BasicJsonType& j, std::pair<A1, A2>& p)\n{\n    p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\nvoid from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nvoid from_json(const BasicJsonType& j, std::tuple<Args...>& t)\n{\n    from_json_tuple_impl(j, t, index_sequence_for<Args...> {});\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\nvoid from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name())));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\nvoid from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name())));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\nstruct from_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(const BasicJsonType& j, T& val) const\n    noexcept(noexcept(from_json(j, val)))\n    -> decltype(from_json(j, val), void())\n    {\n        return from_json(j, val);\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `from_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace\n{\nconstexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;\n} // namespace\n} // namespace nlohmann\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n\n#include <algorithm> // copy\n#include <iterator> // begin, end\n#include <string> // string\n#include <tuple> // tuple, get\n#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type\n#include <utility> // move, forward, declval, pair\n#include <valarray> // valarray\n#include <vector> // vector\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n\n#include <cstddef> // size_t\n#include <iterator> // input_iterator_tag\n#include <string> // string, to_string\n#include <tuple> // tuple_size, get, tuple_element\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename string_type>\nvoid int_to_string( string_type& target, std::size_t value )\n{\n    // For ADL\n    using std::to_string;\n    target = to_string(value);\n}\ntemplate<typename IteratorType> class iteration_proxy_value\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    using value_type = iteration_proxy_value;\n    using pointer = value_type * ;\n    using reference = value_type & ;\n    using iterator_category = std::input_iterator_tag;\n    using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;\n\n  private:\n    /// the iterator\n    IteratorType anchor;\n    /// an index for arrays (used to create key names)\n    std::size_t array_index = 0;\n    /// last stringified array index\n    mutable std::size_t array_index_last = 0;\n    /// a string representation of the array index\n    mutable string_type array_index_str = \"0\";\n    /// an empty string (to return a reference for primitive values)\n    const string_type empty_str = \"\";\n\n  public:\n    explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {}\n\n    /// dereference operator (needed for range-based for)\n    iteration_proxy_value& operator*()\n    {\n        return *this;\n    }\n\n    /// increment operator (needed for range-based for)\n    iteration_proxy_value& operator++()\n    {\n        ++anchor;\n        ++array_index;\n\n        return *this;\n    }\n\n    /// equality operator (needed for InputIterator)\n    bool operator==(const iteration_proxy_value& o) const\n    {\n        return anchor == o.anchor;\n    }\n\n    /// inequality operator (needed for range-based for)\n    bool operator!=(const iteration_proxy_value& o) const\n    {\n        return anchor != o.anchor;\n    }\n\n    /// return key of the iterator\n    const string_type& key() const\n    {\n        JSON_ASSERT(anchor.m_object != nullptr);\n\n        switch (anchor.m_object->type())\n        {\n            // use integer array index as key\n            case value_t::array:\n            {\n                if (array_index != array_index_last)\n                {\n                    int_to_string( array_index_str, array_index );\n                    array_index_last = array_index;\n                }\n                return array_index_str;\n            }\n\n            // use key from the object\n            case value_t::object:\n                return anchor.key();\n\n            // use an empty key for all primitive types\n            default:\n                return empty_str;\n        }\n    }\n\n    /// return value of the iterator\n    typename IteratorType::reference value() const\n    {\n        return anchor.value();\n    }\n};\n\n/// proxy class for the items() function\ntemplate<typename IteratorType> class iteration_proxy\n{\n  private:\n    /// the container to iterate\n    typename IteratorType::reference container;\n\n  public:\n    /// construct iteration proxy from a container\n    explicit iteration_proxy(typename IteratorType::reference cont) noexcept\n        : container(cont) {}\n\n    /// return iterator begin (needed for range-based for)\n    iteration_proxy_value<IteratorType> begin() noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container.begin());\n    }\n\n    /// return iterator end (needed for range-based for)\n    iteration_proxy_value<IteratorType> end() noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container.end());\n    }\n};\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())\n{\n    return i.key();\n}\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())\n{\n    return i.value();\n}\n}  // namespace detail\n}  // namespace nlohmann\n\n// The Addition to the STD Namespace is required to add\n// Structured Bindings Support to the iteration_proxy_value class\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\nnamespace std\n{\n#if defined(__clang__)\n    // Fix: https://github.com/nlohmann/json/issues/1401\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wmismatched-tags\"\n#endif\ntemplate<typename IteratorType>\nclass tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>\n            : public std::integral_constant<std::size_t, 2> {};\n\ntemplate<std::size_t N, typename IteratorType>\nclass tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>\n{\n  public:\n    using type = decltype(\n                     get<N>(std::declval <\n                            ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));\n};\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n} // namespace std\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////\n// constructors //\n//////////////////\n\ntemplate<value_t> struct external_constructor;\n\ntemplate<>\nstruct external_constructor<value_t::boolean>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept\n    {\n        j.m_type = value_t::boolean;\n        j.m_value = b;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::string>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)\n    {\n        j.m_type = value_t::string;\n        j.m_value = s;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n    {\n        j.m_type = value_t::string;\n        j.m_value = std::move(s);\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleStringType,\n               enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleStringType& str)\n    {\n        j.m_type = value_t::string;\n        j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::binary>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)\n    {\n        j.m_type = value_t::binary;\n        typename BasicJsonType::binary_t value{b};\n        j.m_value = value;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)\n    {\n        j.m_type = value_t::binary;\n        typename BasicJsonType::binary_t value{std::move(b)};\n        j.m_value = value;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_float>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept\n    {\n        j.m_type = value_t::number_float;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_unsigned>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept\n    {\n        j.m_type = value_t::number_unsigned;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_integer>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept\n    {\n        j.m_type = value_t::number_integer;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::array>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = arr;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = std::move(arr);\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleArrayType,\n               enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)\n    {\n        using std::begin;\n        using std::end;\n        j.m_type = value_t::array;\n        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const std::vector<bool>& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->reserve(arr.size());\n        for (const bool x : arr)\n        {\n            j.m_value.array->push_back(x);\n        }\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename T,\n             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\n    static void construct(BasicJsonType& j, const std::valarray<T>& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->resize(arr.size());\n        if (arr.size() > 0)\n        {\n            std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());\n        }\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::object>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)\n    {\n        j.m_type = value_t::object;\n        j.m_value = obj;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n    {\n        j.m_type = value_t::object;\n        j.m_value = std::move(obj);\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleObjectType,\n               enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_type = value_t::object;\n        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));\n        j.assert_invariant();\n    }\n};\n\n/////////////\n// to_json //\n/////////////\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>\nvoid to_json(BasicJsonType& j, T b) noexcept\n{\n    external_constructor<value_t::boolean>::construct(j, b);\n}\n\ntemplate<typename BasicJsonType, typename CompatibleString,\n         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const CompatibleString& s)\n{\n    external_constructor<value_t::string>::construct(j, s);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n{\n    external_constructor<value_t::string>::construct(j, std::move(s));\n}\n\ntemplate<typename BasicJsonType, typename FloatType,\n         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, FloatType val) noexcept\n{\n    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberUnsignedType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept\n{\n    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberIntegerType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept\n{\n    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, EnumType e) noexcept\n{\n    using underlying_type = typename std::underlying_type<EnumType>::type;\n    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const std::vector<bool>& e)\n{\n    external_constructor<value_t::array>::construct(j, e);\n}\n\ntemplate < typename BasicJsonType, typename CompatibleArrayType,\n           enable_if_t < is_compatible_array_type<BasicJsonType,\n                         CompatibleArrayType>::value&&\n                         !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&\n                         !is_basic_json<CompatibleArrayType>::value,\n                         int > = 0 >\nvoid to_json(BasicJsonType& j, const CompatibleArrayType& arr)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)\n{\n    external_constructor<value_t::binary>::construct(j, bin);\n}\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const std::valarray<T>& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate < typename BasicJsonType, typename CompatibleObjectType,\n           enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >\nvoid to_json(BasicJsonType& j, const CompatibleObjectType& obj)\n{\n    external_constructor<value_t::object>::construct(j, obj);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n{\n    external_constructor<value_t::object>::construct(j, std::move(obj));\n}\n\ntemplate <\n    typename BasicJsonType, typename T, std::size_t N,\n    enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,\n                  const T(&)[N]>::value,\n                  int > = 0 >\nvoid to_json(BasicJsonType& j, const T(&arr)[N])\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >\nvoid to_json(BasicJsonType& j, const std::pair<T1, T2>& p)\n{\n    j = { p.first, p.second };\n}\n\n// for https://github.com/nlohmann/json/pull/1134\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const T& b)\n{\n    j = { {b.key(), b.value()} };\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\nvoid to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    j = { std::get<Idx>(t)... };\n}\n\ntemplate<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>\nvoid to_json(BasicJsonType& j, const T& t)\n{\n    to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});\n}\n\nstruct to_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))\n    -> decltype(to_json(j, std::forward<T>(val)), void())\n    {\n        return to_json(j, std::forward<T>(val));\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `to_json` function\nnamespace\n{\nconstexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;\n} // namespace\n} // namespace nlohmann\n\n\nnamespace nlohmann\n{\n\ntemplate<typename, typename>\nstruct adl_serializer\n{\n    /*!\n    @brief convert a JSON value to any value type\n\n    This function is usually called by the `get()` function of the\n    @ref basic_json class (either explicit or via conversion operators).\n\n    @param[in] j        JSON value to read from\n    @param[in,out] val  value to write to\n    */\n    template<typename BasicJsonType, typename ValueType>\n    static auto from_json(BasicJsonType&& j, ValueType& val) noexcept(\n        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())\n    {\n        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);\n    }\n\n    /*!\n    @brief convert any value type to a JSON value\n\n    This function is usually called by the constructors of the @ref basic_json\n    class.\n\n    @param[in,out] j  JSON value to write to\n    @param[in] val    value to read from\n    */\n    template<typename BasicJsonType, typename ValueType>\n    static auto to_json(BasicJsonType& j, ValueType&& val) noexcept(\n        noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))\n    -> decltype(::nlohmann::to_json(j, std::forward<ValueType>(val)), void())\n    {\n        ::nlohmann::to_json(j, std::forward<ValueType>(val));\n    }\n};\n\n}  // namespace nlohmann\n\n// #include <nlohmann/byte_container_with_subtype.hpp>\n\n\n#include <cstdint> // uint8_t\n#include <tuple> // tie\n#include <utility> // move\n\nnamespace nlohmann\n{\n\n/*!\n@brief an internal type for a backed binary type\n\nThis type extends the template parameter @a BinaryType provided to `basic_json`\nwith a subtype used by BSON and MessagePack. This type exists so that the user\ndoes not have to specify a type themselves with a specific naming scheme in\norder to override the binary type.\n\n@tparam BinaryType container to store bytes (`std::vector<std::uint8_t>` by\n                   default)\n\n@since version 3.8.0\n*/\ntemplate<typename BinaryType>\nclass byte_container_with_subtype : public BinaryType\n{\n  public:\n    /// the type of the underlying container\n    using container_type = BinaryType;\n\n    byte_container_with_subtype() noexcept(noexcept(container_type()))\n        : container_type()\n    {}\n\n    byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n    {}\n\n    byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n    {}\n\n    byte_container_with_subtype(const container_type& b, std::uint8_t subtype) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n        , m_subtype(subtype)\n        , m_has_subtype(true)\n    {}\n\n    byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n        , m_subtype(subtype)\n        , m_has_subtype(true)\n    {}\n\n    bool operator==(const byte_container_with_subtype& rhs) const\n    {\n        return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==\n               std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);\n    }\n\n    bool operator!=(const byte_container_with_subtype& rhs) const\n    {\n        return !(rhs == *this);\n    }\n\n    /*!\n    @brief sets the binary subtype\n\n    Sets the binary subtype of the value, also flags a binary JSON value as\n    having a subtype, which has implications for serialization.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa @ref subtype() -- return the binary subtype\n    @sa @ref clear_subtype() -- clears the binary subtype\n    @sa @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0\n    */\n    void set_subtype(std::uint8_t subtype) noexcept\n    {\n        m_subtype = subtype;\n        m_has_subtype = true;\n    }\n\n    /*!\n    @brief return the binary subtype\n\n    Returns the numerical subtype of the value if it has a subtype. If it does\n    not have a subtype, this function will return size_t(-1) as a sentinel\n    value.\n\n    @return the numerical subtype of the binary value\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa @ref set_subtype() -- sets the binary subtype\n    @sa @ref clear_subtype() -- clears the binary subtype\n    @sa @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0\n    */\n    constexpr std::uint8_t subtype() const noexcept\n    {\n        return m_subtype;\n    }\n\n    /*!\n    @brief return whether the value has a subtype\n\n    @return whether the value has a subtype\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa @ref subtype() -- return the binary subtype\n    @sa @ref set_subtype() -- sets the binary subtype\n    @sa @ref clear_subtype() -- clears the binary subtype\n\n    @since version 3.8.0\n    */\n    constexpr bool has_subtype() const noexcept\n    {\n        return m_has_subtype;\n    }\n\n    /*!\n    @brief clears the binary subtype\n\n    Clears the binary subtype and flags the value as not having a subtype, which\n    has implications for serialization; for instance MessagePack will prefer the\n    bin family over the ext family.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa @ref subtype() -- return the binary subtype\n    @sa @ref set_subtype() -- sets the binary subtype\n    @sa @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0\n    */\n    void clear_subtype() noexcept\n    {\n        m_subtype = 0;\n        m_has_subtype = false;\n    }\n\n  private:\n    std::uint8_t m_subtype = 0;\n    bool m_has_subtype = false;\n};\n\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/hash.hpp>\n\n\n#include <cstddef> // size_t, uint8_t\n#include <functional> // hash\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n// boost::hash_combine\ninline std::size_t combine(std::size_t seed, std::size_t h) noexcept\n{\n    seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);\n    return seed;\n}\n\n/*!\n@brief hash a JSON value\n\nThe hash function tries to rely on std::hash where possible. Furthermore, the\ntype of the JSON value is taken into account to have different hash values for\nnull, 0, 0U, and false, etc.\n\n@tparam BasicJsonType basic_json specialization\n@param j JSON value to hash\n@return hash value of j\n*/\ntemplate<typename BasicJsonType>\nstd::size_t hash(const BasicJsonType& j)\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n    const auto type = static_cast<std::size_t>(j.type());\n    switch (j.type())\n    {\n        case BasicJsonType::value_t::null:\n        case BasicJsonType::value_t::discarded:\n        {\n            return combine(type, 0);\n        }\n\n        case BasicJsonType::value_t::object:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j.items())\n            {\n                const auto h = std::hash<string_t> {}(element.key());\n                seed = combine(seed, h);\n                seed = combine(seed, hash(element.value()));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::array:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j)\n            {\n                seed = combine(seed, hash(element));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::string:\n        {\n            const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::boolean:\n        {\n            const auto h = std::hash<bool> {}(j.template get<bool>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_integer:\n        {\n            const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());\n            return combine(type, h);\n        }\n\n        case nlohmann::detail::value_t::number_unsigned:\n        {\n            const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());\n            return combine(type, h);\n        }\n\n        case nlohmann::detail::value_t::number_float:\n        {\n            const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());\n            return combine(type, h);\n        }\n\n        case nlohmann::detail::value_t::binary:\n        {\n            auto seed = combine(type, j.get_binary().size());\n            const auto h = std::hash<bool> {}(j.get_binary().has_subtype());\n            seed = combine(seed, h);\n            seed = combine(seed, j.get_binary().subtype());\n            for (const auto byte : j.get_binary())\n            {\n                seed = combine(seed, std::hash<std::uint8_t> {}(byte));\n            }\n            return seed;\n        }\n\n        default: // LCOV_EXCL_LINE\n            JSON_ASSERT(false); // LCOV_EXCL_LINE\n    }\n}\n\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n\n#include <algorithm> // generate_n\n#include <array> // array\n#include <cmath> // ldexp\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstdio> // snprintf\n#include <cstring> // memcpy\n#include <iterator> // back_inserter\n#include <limits> // numeric_limits\n#include <string> // char_traits, string\n#include <utility> // make_pair, move\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstdio> //FILE *\n#include <cstring> // strlen\n#include <istream> // istream\n#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next\n#include <memory> // shared_ptr, make_shared, addressof\n#include <numeric> // accumulate\n#include <string> // string, char_traits\n#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer\n#include <utility> // pair, declval\n\n// #include <nlohmann/detail/iterators/iterator_traits.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// the supported input formats\nenum class input_format_t { json, cbor, msgpack, ubjson, bson };\n\n////////////////////\n// input adapters //\n////////////////////\n\n/*!\nInput adapter for stdio file access. This adapter read only 1 byte and do not use any\n buffer. This adapter is a very low level adapter.\n*/\nclass file_input_adapter\n{\n  public:\n    using char_type = char;\n\n    JSON_HEDLEY_NON_NULL(2)\n    explicit file_input_adapter(std::FILE* f) noexcept\n        : m_file(f)\n    {}\n\n    // make class move-only\n    file_input_adapter(const file_input_adapter&) = delete;\n    file_input_adapter(file_input_adapter&&) = default;\n    file_input_adapter& operator=(const file_input_adapter&) = delete;\n    file_input_adapter& operator=(file_input_adapter&&) = delete;\n\n    std::char_traits<char>::int_type get_character() noexcept\n    {\n        return std::fgetc(m_file);\n    }\n\n  private:\n    /// the file pointer to read from\n    std::FILE* m_file;\n};\n\n\n/*!\nInput adapter for a (caching) istream. Ignores a UFT Byte Order Mark at\nbeginning of input. Does not support changing the underlying std::streambuf\nin mid-input. Maintains underlying std::istream and std::streambuf to support\nsubsequent use of standard std::istream operations to process any input\ncharacters following those used in parsing the JSON input.  Clears the\nstd::istream flags; any input errors (e.g., EOF) will be detected by the first\nsubsequent call for input from the std::istream.\n*/\nclass input_stream_adapter\n{\n  public:\n    using char_type = char;\n\n    ~input_stream_adapter()\n    {\n        // clear stream flags; we use underlying streambuf I/O, do not\n        // maintain ifstream flags, except eof\n        if (is != nullptr)\n        {\n            is->clear(is->rdstate() & std::ios::eofbit);\n        }\n    }\n\n    explicit input_stream_adapter(std::istream& i)\n        : is(&i), sb(i.rdbuf())\n    {}\n\n    // delete because of pointer members\n    input_stream_adapter(const input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&& rhs) = delete;\n\n    input_stream_adapter(input_stream_adapter&& rhs) noexcept : is(rhs.is), sb(rhs.sb)\n    {\n        rhs.is = nullptr;\n        rhs.sb = nullptr;\n    }\n\n    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to\n    // ensure that std::char_traits<char>::eof() and the character 0xFF do not\n    // end up as the same value, eg. 0xFFFFFFFF.\n    std::char_traits<char>::int_type get_character()\n    {\n        auto res = sb->sbumpc();\n        // set eof manually, as we don't use the istream interface.\n        if (JSON_HEDLEY_UNLIKELY(res == EOF))\n        {\n            is->clear(is->rdstate() | std::ios::eofbit);\n        }\n        return res;\n    }\n\n  private:\n    /// the associated input stream\n    std::istream* is = nullptr;\n    std::streambuf* sb = nullptr;\n};\n\n// General-purpose iterator-based adapter. It might not be as fast as\n// theoretically possible for some containers, but it is extremely versatile.\ntemplate<typename IteratorType>\nclass iterator_input_adapter\n{\n  public:\n    using char_type = typename std::iterator_traits<IteratorType>::value_type;\n\n    iterator_input_adapter(IteratorType first, IteratorType last)\n        : current(std::move(first)), end(std::move(last)) {}\n\n    typename std::char_traits<char_type>::int_type get_character()\n    {\n        if (JSON_HEDLEY_LIKELY(current != end))\n        {\n            auto result = std::char_traits<char_type>::to_int_type(*current);\n            std::advance(current, 1);\n            return result;\n        }\n        else\n        {\n            return std::char_traits<char_type>::eof();\n        }\n    }\n\n  private:\n    IteratorType current;\n    IteratorType end;\n\n    template<typename BaseInputAdapter, size_t T>\n    friend struct wide_string_input_helper;\n\n    bool empty() const\n    {\n        return current == end;\n    }\n\n};\n\n\ntemplate<typename BaseInputAdapter, size_t T>\nstruct wide_string_input_helper;\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 4>\n{\n    // UTF-32\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-32 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (wc <= 0xFFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else if (wc <= 0x10FFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 4;\n            }\n            else\n            {\n                // unknown character\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n        }\n    }\n};\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 2>\n{\n    // UTF-16\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-16 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (0xD800 > wc || wc >= 0xE000)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else\n            {\n                if (JSON_HEDLEY_UNLIKELY(!input.empty()))\n                {\n                    const auto wc2 = static_cast<unsigned int>(input.get_character());\n                    const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));\n                    utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));\n                    utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));\n                    utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));\n                    utf8_bytes_filled = 4;\n                }\n                else\n                {\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                    utf8_bytes_filled = 1;\n                }\n            }\n        }\n    }\n};\n\n// Wraps another input apdater to convert wide character types into individual bytes.\ntemplate<typename BaseInputAdapter, typename WideCharType>\nclass wide_string_input_adapter\n{\n  public:\n    using char_type = char;\n\n    wide_string_input_adapter(BaseInputAdapter base)\n        : base_adapter(base) {}\n\n    typename std::char_traits<char>::int_type get_character() noexcept\n    {\n        // check if buffer needs to be filled\n        if (utf8_bytes_index == utf8_bytes_filled)\n        {\n            fill_buffer<sizeof(WideCharType)>();\n\n            JSON_ASSERT(utf8_bytes_filled > 0);\n            JSON_ASSERT(utf8_bytes_index == 0);\n        }\n\n        // use buffer\n        JSON_ASSERT(utf8_bytes_filled > 0);\n        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);\n        return utf8_bytes[utf8_bytes_index++];\n    }\n\n  private:\n    BaseInputAdapter base_adapter;\n\n    template<size_t T>\n    void fill_buffer()\n    {\n        wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);\n    }\n\n    /// a buffer for UTF-8 bytes\n    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};\n\n    /// index to the utf8_codes array for the next valid byte\n    std::size_t utf8_bytes_index = 0;\n    /// number of valid bytes in the utf8_codes array\n    std::size_t utf8_bytes_filled = 0;\n};\n\n\ntemplate<typename IteratorType, typename Enable = void>\nstruct iterator_input_adapter_factory\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using adapter_type = iterator_input_adapter<iterator_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(std::move(first), std::move(last));\n    }\n};\n\ntemplate<typename T>\nstruct is_iterator_of_multibyte\n{\n    using value_type = typename std::iterator_traits<T>::value_type;\n    enum\n    {\n        value = sizeof(value_type) > 1\n    };\n};\n\ntemplate<typename IteratorType>\nstruct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using base_adapter_type = iterator_input_adapter<iterator_type>;\n    using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(base_adapter_type(std::move(first), std::move(last)));\n    }\n};\n\n// General purpose iterator-based input\ntemplate<typename IteratorType>\ntypename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)\n{\n    using factory_type = iterator_input_adapter_factory<IteratorType>;\n    return factory_type::create(first, last);\n}\n\n// Convenience shorthand from container to iterator\ntemplate<typename ContainerType>\nauto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container)))\n{\n    // Enable ADL\n    using std::begin;\n    using std::end;\n\n    return input_adapter(begin(container), end(container));\n}\n\n// Special cases with fast paths\ninline file_input_adapter input_adapter(std::FILE* file)\n{\n    return file_input_adapter(file);\n}\n\ninline input_stream_adapter input_adapter(std::istream& stream)\n{\n    return input_stream_adapter(stream);\n}\n\ninline input_stream_adapter input_adapter(std::istream&& stream)\n{\n    return input_stream_adapter(stream);\n}\n\nusing contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));\n\n// Null-delimited strings, and the like.\ntemplate < typename CharT,\n           typename std::enable_if <\n               std::is_pointer<CharT>::value&&\n               !std::is_array<CharT>::value&&\n               std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n               sizeof(typename std::remove_pointer<CharT>::type) == 1,\n               int >::type = 0 >\ncontiguous_bytes_input_adapter input_adapter(CharT b)\n{\n    auto length = std::strlen(reinterpret_cast<const char*>(b));\n    const auto* ptr = reinterpret_cast<const char*>(b);\n    return input_adapter(ptr, ptr + length);\n}\n\ntemplate<typename T, std::size_t N>\nauto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N))\n{\n    return input_adapter(array, array + N);\n}\n\n// This class only handles inputs of input_buffer_adapter type.\n// It's required so that expressions like {ptr, len} can be implicitely casted\n// to the correct adapter.\nclass span_input_adapter\n{\n  public:\n    template < typename CharT,\n               typename std::enable_if <\n                   std::is_pointer<CharT>::value&&\n                   std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n                   sizeof(typename std::remove_pointer<CharT>::type) == 1,\n                   int >::type = 0 >\n    span_input_adapter(CharT b, std::size_t l)\n        : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}\n\n    template<class IteratorType,\n             typename std::enable_if<\n                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,\n                 int>::type = 0>\n    span_input_adapter(IteratorType first, IteratorType last)\n        : ia(input_adapter(first, last)) {}\n\n    contiguous_bytes_input_adapter&& get()\n    {\n        return std::move(ia);\n    }\n\n  private:\n    contiguous_bytes_input_adapter ia;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n\n#include <cstddef>\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\n\n/*!\n@brief SAX interface\n\nThis class describes the SAX interface used by @ref nlohmann::json::sax_parse.\nEach function is called in different situations while the input is parsed. The\nboolean return value informs the parser whether to continue processing the\ninput.\n*/\ntemplate<typename BasicJsonType>\nstruct json_sax\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @brief a null value was read\n    @return whether parsing should proceed\n    */\n    virtual bool null() = 0;\n\n    /*!\n    @brief a boolean value was read\n    @param[in] val  boolean value\n    @return whether parsing should proceed\n    */\n    virtual bool boolean(bool val) = 0;\n\n    /*!\n    @brief an integer number was read\n    @param[in] val  integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_integer(number_integer_t val) = 0;\n\n    /*!\n    @brief an unsigned integer number was read\n    @param[in] val  unsigned integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_unsigned(number_unsigned_t val) = 0;\n\n    /*!\n    @brief an floating-point number was read\n    @param[in] val  floating-point value\n    @param[in] s    raw token value\n    @return whether parsing should proceed\n    */\n    virtual bool number_float(number_float_t val, const string_t& s) = 0;\n\n    /*!\n    @brief a string was read\n    @param[in] val  string value\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool string(string_t& val) = 0;\n\n    /*!\n    @brief a binary string was read\n    @param[in] val  binary value\n    @return whether parsing should proceed\n    @note It is safe to move the passed binary.\n    */\n    virtual bool binary(binary_t& val) = 0;\n\n    /*!\n    @brief the beginning of an object was read\n    @param[in] elements  number of object elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_object(std::size_t elements) = 0;\n\n    /*!\n    @brief an object key was read\n    @param[in] val  object key\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool key(string_t& val) = 0;\n\n    /*!\n    @brief the end of an object was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_object() = 0;\n\n    /*!\n    @brief the beginning of an array was read\n    @param[in] elements  number of array elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_array(std::size_t elements) = 0;\n\n    /*!\n    @brief the end of an array was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_array() = 0;\n\n    /*!\n    @brief a parse error occurred\n    @param[in] position    the position in the input where the error occurs\n    @param[in] last_token  the last read token\n    @param[in] ex          an exception object describing the error\n    @return whether parsing should proceed (must return false)\n    */\n    virtual bool parse_error(std::size_t position,\n                             const std::string& last_token,\n                             const detail::exception& ex) = 0;\n\n    virtual ~json_sax() = default;\n};\n\n\nnamespace detail\n{\n/*!\n@brief SAX implementation to create a JSON value from SAX events\n\nThis class implements the @ref json_sax interface and processes the SAX events\nto create a JSON value which makes it basically a DOM parser. The structure or\nhierarchy of the JSON value is managed by the stack `ref_stack` which contains\na pointer to the respective array or object for each recursion depth.\n\nAfter successful parsing, the value that is passed by reference to the\nconstructor contains the parsed value.\n\n@tparam BasicJsonType  the JSON type\n*/\ntemplate<typename BasicJsonType>\nclass json_sax_dom_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @param[in, out] r  reference to a JSON value that is manipulated while\n                       parsing\n    @param[in] allow_exceptions_  whether parse errors yield exceptions\n    */\n    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)\n        : root(r), allow_exceptions(allow_exceptions_)\n    {}\n\n    // make class move-only\n    json_sax_dom_parser(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser(json_sax_dom_parser&&) = default;\n    json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default;\n    ~json_sax_dom_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));\n\n        if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408,\n                                            \"excessive object size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        // add null at given key and store the reference for later\n        object_element = &(ref_stack.back()->m_value.object->operator[](val));\n        return true;\n    }\n\n    bool end_object()\n    {\n        ref_stack.pop_back();\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));\n\n        if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408,\n                                            \"excessive array size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        ref_stack.pop_back();\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n    */\n    template<typename Value>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    BasicJsonType* handle_value(Value&& v)\n    {\n        if (ref_stack.empty())\n        {\n            root = BasicJsonType(std::forward<Value>(v));\n            return &root;\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));\n            return &(ref_stack.back()->m_value.array->back());\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_object());\n        JSON_ASSERT(object_element);\n        *object_element = BasicJsonType(std::forward<Value>(v));\n        return object_element;\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_dom_callback_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using parser_callback_t = typename BasicJsonType::parser_callback_t;\n    using parse_event_t = typename BasicJsonType::parse_event_t;\n\n    json_sax_dom_callback_parser(BasicJsonType& r,\n                                 const parser_callback_t cb,\n                                 const bool allow_exceptions_ = true)\n        : root(r), callback(cb), allow_exceptions(allow_exceptions_)\n    {\n        keep_stack.push_back(true);\n    }\n\n    // make class move-only\n    json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default;\n    json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default;\n    ~json_sax_dom_callback_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        // check callback for object start\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::object, true);\n        ref_stack.push_back(val.second);\n\n        // check object limit\n        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive object size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        BasicJsonType k = BasicJsonType(val);\n\n        // check callback for key\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);\n        key_keep_stack.push_back(keep);\n\n        // add discarded value at given key and store the reference for later\n        if (keep && ref_stack.back())\n        {\n            object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);\n        }\n\n        return true;\n    }\n\n    bool end_object()\n    {\n        if (ref_stack.back() && !callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))\n        {\n            // discard object\n            *ref_stack.back() = discarded;\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())\n        {\n            // remove discarded value\n            for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)\n            {\n                if (it->is_discarded())\n                {\n                    ref_stack.back()->erase(it);\n                    break;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::array, true);\n        ref_stack.push_back(val.second);\n\n        // check array limit\n        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive array size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        bool keep = true;\n\n        if (ref_stack.back())\n        {\n            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());\n            if (!keep)\n            {\n                // discard array\n                *ref_stack.back() = discarded;\n            }\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        // remove discarded value\n        if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->pop_back();\n        }\n\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @param[in] v  value to add to the JSON value we build during parsing\n    @param[in] skip_callback  whether we should skip calling the callback\n               function; this is required after start_array() and\n               start_object() SAX events, because otherwise we would call the\n               callback function with an empty array or object, respectively.\n\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n\n    @return pair of boolean (whether value should be kept) and pointer (to the\n            passed value in the ref_stack hierarchy; nullptr if not kept)\n    */\n    template<typename Value>\n    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)\n    {\n        JSON_ASSERT(!keep_stack.empty());\n\n        // do not handle this value if we know it would be added to a discarded\n        // container\n        if (!keep_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // create value\n        auto value = BasicJsonType(std::forward<Value>(v));\n\n        // check callback\n        const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);\n\n        // do not handle this value if we just learnt it shall be discarded\n        if (!keep)\n        {\n            return {false, nullptr};\n        }\n\n        if (ref_stack.empty())\n        {\n            root = std::move(value);\n            return {true, &root};\n        }\n\n        // skip this value if we already decided to skip the parent\n        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)\n        if (!ref_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // we now only expect arrays and objects\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        // array\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->push_back(std::move(value));\n            return {true, &(ref_stack.back()->m_value.array->back())};\n        }\n\n        // object\n        JSON_ASSERT(ref_stack.back()->is_object());\n        // check if we should store an element for the current key\n        JSON_ASSERT(!key_keep_stack.empty());\n        const bool store_element = key_keep_stack.back();\n        key_keep_stack.pop_back();\n\n        if (!store_element)\n        {\n            return {false, nullptr};\n        }\n\n        JSON_ASSERT(object_element);\n        *object_element = std::move(value);\n        return {true, object_element};\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// stack to manage which values to keep\n    std::vector<bool> keep_stack {};\n    /// stack to manage which object keys to keep\n    std::vector<bool> key_keep_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// callback function\n    const parser_callback_t callback = nullptr;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n    /// a discarded value for the callback\n    BasicJsonType discarded = BasicJsonType::value_t::discarded;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_acceptor\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    bool null()\n    {\n        return true;\n    }\n\n    bool boolean(bool /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_integer(number_integer_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool string(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool binary(binary_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool start_object(std::size_t /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool key(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool end_object()\n    {\n        return true;\n    }\n\n    bool start_array(std::size_t /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool end_array()\n    {\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)\n    {\n        return false;\n    }\n};\n}  // namespace detail\n\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n\n#include <array> // array\n#include <clocale> // localeconv\n#include <cstddef> // size_t\n#include <cstdio> // snprintf\n#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull\n#include <initializer_list> // initializer_list\n#include <string> // char_traits, string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////\n// lexer //\n///////////\n\ntemplate<typename BasicJsonType>\nclass lexer_base\n{\n  public:\n    /// token types for the parser\n    enum class token_type\n    {\n        uninitialized,    ///< indicating the scanner is uninitialized\n        literal_true,     ///< the `true` literal\n        literal_false,    ///< the `false` literal\n        literal_null,     ///< the `null` literal\n        value_string,     ///< a string -- use get_string() for actual value\n        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value\n        value_integer,    ///< a signed integer -- use get_number_integer() for actual value\n        value_float,      ///< an floating point number -- use get_number_float() for actual value\n        begin_array,      ///< the character for array begin `[`\n        begin_object,     ///< the character for object begin `{`\n        end_array,        ///< the character for array end `]`\n        end_object,       ///< the character for object end `}`\n        name_separator,   ///< the name separator `:`\n        value_separator,  ///< the value separator `,`\n        parse_error,      ///< indicating a parse error\n        end_of_input,     ///< indicating the end of the input buffer\n        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)\n    };\n\n    /// return name of values of type token_type (only used for errors)\n    JSON_HEDLEY_RETURNS_NON_NULL\n    JSON_HEDLEY_CONST\n    static const char* token_type_name(const token_type t) noexcept\n    {\n        switch (t)\n        {\n            case token_type::uninitialized:\n                return \"<uninitialized>\";\n            case token_type::literal_true:\n                return \"true literal\";\n            case token_type::literal_false:\n                return \"false literal\";\n            case token_type::literal_null:\n                return \"null literal\";\n            case token_type::value_string:\n                return \"string literal\";\n            case token_type::value_unsigned:\n            case token_type::value_integer:\n            case token_type::value_float:\n                return \"number literal\";\n            case token_type::begin_array:\n                return \"'['\";\n            case token_type::begin_object:\n                return \"'{'\";\n            case token_type::end_array:\n                return \"']'\";\n            case token_type::end_object:\n                return \"'}'\";\n            case token_type::name_separator:\n                return \"':'\";\n            case token_type::value_separator:\n                return \"','\";\n            case token_type::parse_error:\n                return \"<parse error>\";\n            case token_type::end_of_input:\n                return \"end of input\";\n            case token_type::literal_or_value:\n                return \"'[', '{', or a literal\";\n            // LCOV_EXCL_START\n            default: // catch non-enum values\n                return \"unknown token\";\n                // LCOV_EXCL_STOP\n        }\n    }\n};\n/*!\n@brief lexical analysis\n\nThis class organizes the lexical analysis during JSON deserialization.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass lexer : public lexer_base<BasicJsonType>\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename std::char_traits<char_type>::int_type;\n\n  public:\n    using token_type = typename lexer_base<BasicJsonType>::token_type;\n\n    explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false)\n        : ia(std::move(adapter))\n        , ignore_comments(ignore_comments_)\n        , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))\n    {}\n\n    // delete because of pointer members\n    lexer(const lexer&) = delete;\n    lexer(lexer&&) = default;\n    lexer& operator=(lexer&) = delete;\n    lexer& operator=(lexer&&) = default;\n    ~lexer() = default;\n\n  private:\n    /////////////////////\n    // locales\n    /////////////////////\n\n    /// return the locale-dependent decimal point\n    JSON_HEDLEY_PURE\n    static char get_decimal_point() noexcept\n    {\n        const auto* loc = localeconv();\n        JSON_ASSERT(loc != nullptr);\n        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);\n    }\n\n    /////////////////////\n    // scan functions\n    /////////////////////\n\n    /*!\n    @brief get codepoint from 4 hex characters following `\\u`\n\n    For input \"\\u c1 c2 c3 c4\" the codepoint is:\n      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4\n    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)\n\n    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'\n    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The\n    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)\n    between the ASCII value of the character and the desired integer value.\n\n    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or\n            non-hex character)\n    */\n    int get_codepoint()\n    {\n        // this function only makes sense after reading `\\u`\n        JSON_ASSERT(current == 'u');\n        int codepoint = 0;\n\n        const auto factors = { 12u, 8u, 4u, 0u };\n        for (const auto factor : factors)\n        {\n            get();\n\n            if (current >= '0' && current <= '9')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);\n            }\n            else if (current >= 'A' && current <= 'F')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);\n            }\n            else if (current >= 'a' && current <= 'f')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);\n            }\n            else\n            {\n                return -1;\n            }\n        }\n\n        JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);\n        return codepoint;\n    }\n\n    /*!\n    @brief check if the next byte(s) are inside a given range\n\n    Adds the current byte and, for each passed range, reads a new byte and\n    checks if it is inside the range. If a violation was detected, set up an\n    error message and return false. Otherwise, return true.\n\n    @param[in] ranges  list of integers; interpreted as list of pairs of\n                       inclusive lower and upper bound, respectively\n\n    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,\n         1, 2, or 3 pairs. This precondition is enforced by an assertion.\n\n    @return true if and only if no range violation was detected\n    */\n    bool next_byte_in_range(std::initializer_list<char_int_type> ranges)\n    {\n        JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);\n        add(current);\n\n        for (auto range = ranges.begin(); range != ranges.end(); ++range)\n        {\n            get();\n            if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range)))\n            {\n                add(current);\n            }\n            else\n            {\n                error_message = \"invalid string: ill-formed UTF-8 byte\";\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief scan a string literal\n\n    This function scans a string according to Sect. 7 of RFC 7159. While\n    scanning, bytes are escaped and copied into buffer token_buffer. Then the\n    function returns successfully, token_buffer is *not* null-terminated (as it\n    may contain \\0 bytes), and token_buffer.size() is the number of bytes in the\n    string.\n\n    @return token_type::value_string if string could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note In case of errors, variable error_message contains a textual\n          description.\n    */\n    token_type scan_string()\n    {\n        // reset token_buffer (ignore opening quote)\n        reset();\n\n        // we entered the function by reading an open quote\n        JSON_ASSERT(current == '\\\"');\n\n        while (true)\n        {\n            // get next character\n            switch (get())\n            {\n                // end of file while parsing string\n                case std::char_traits<char_type>::eof():\n                {\n                    error_message = \"invalid string: missing closing quote\";\n                    return token_type::parse_error;\n                }\n\n                // closing quote\n                case '\\\"':\n                {\n                    return token_type::value_string;\n                }\n\n                // escapes\n                case '\\\\':\n                {\n                    switch (get())\n                    {\n                        // quotation mark\n                        case '\\\"':\n                            add('\\\"');\n                            break;\n                        // reverse solidus\n                        case '\\\\':\n                            add('\\\\');\n                            break;\n                        // solidus\n                        case '/':\n                            add('/');\n                            break;\n                        // backspace\n                        case 'b':\n                            add('\\b');\n                            break;\n                        // form feed\n                        case 'f':\n                            add('\\f');\n                            break;\n                        // line feed\n                        case 'n':\n                            add('\\n');\n                            break;\n                        // carriage return\n                        case 'r':\n                            add('\\r');\n                            break;\n                        // tab\n                        case 't':\n                            add('\\t');\n                            break;\n\n                        // unicode escapes\n                        case 'u':\n                        {\n                            const int codepoint1 = get_codepoint();\n                            int codepoint = codepoint1; // start with codepoint1\n\n                            if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))\n                            {\n                                error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                return token_type::parse_error;\n                            }\n\n                            // check if code point is a high surrogate\n                            if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)\n                            {\n                                // expect next \\uxxxx entry\n                                if (JSON_HEDLEY_LIKELY(get() == '\\\\' && get() == 'u'))\n                                {\n                                    const int codepoint2 = get_codepoint();\n\n                                    if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))\n                                    {\n                                        error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                        return token_type::parse_error;\n                                    }\n\n                                    // check if codepoint2 is a low surrogate\n                                    if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))\n                                    {\n                                        // overwrite codepoint\n                                        codepoint = static_cast<int>(\n                                                        // high surrogate occupies the most significant 22 bits\n                                                        (static_cast<unsigned int>(codepoint1) << 10u)\n                                                        // low surrogate occupies the least significant 15 bits\n                                                        + static_cast<unsigned int>(codepoint2)\n                                                        // there is still the 0xD800, 0xDC00 and 0x10000 noise\n                                                        // in the result so we have to subtract with:\n                                                        // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00\n                                                        - 0x35FDC00u);\n                                    }\n                                    else\n                                    {\n                                        error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                        return token_type::parse_error;\n                                    }\n                                }\n                                else\n                                {\n                                    error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n                            else\n                            {\n                                if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))\n                                {\n                                    error_message = \"invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n\n                            // result of the above calculation yields a proper codepoint\n                            JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);\n\n                            // translate codepoint into bytes\n                            if (codepoint < 0x80)\n                            {\n                                // 1-byte characters: 0xxxxxxx (ASCII)\n                                add(static_cast<char_int_type>(codepoint));\n                            }\n                            else if (codepoint <= 0x7FF)\n                            {\n                                // 2-byte characters: 110xxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else if (codepoint <= 0xFFFF)\n                            {\n                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else\n                            {\n                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n\n                            break;\n                        }\n\n                        // other characters after escape\n                        default:\n                            error_message = \"invalid string: forbidden character after backslash\";\n                            return token_type::parse_error;\n                    }\n\n                    break;\n                }\n\n                // invalid control characters\n                case 0x00:\n                {\n                    error_message = \"invalid string: control character U+0000 (NUL) must be escaped to \\\\u0000\";\n                    return token_type::parse_error;\n                }\n\n                case 0x01:\n                {\n                    error_message = \"invalid string: control character U+0001 (SOH) must be escaped to \\\\u0001\";\n                    return token_type::parse_error;\n                }\n\n                case 0x02:\n                {\n                    error_message = \"invalid string: control character U+0002 (STX) must be escaped to \\\\u0002\";\n                    return token_type::parse_error;\n                }\n\n                case 0x03:\n                {\n                    error_message = \"invalid string: control character U+0003 (ETX) must be escaped to \\\\u0003\";\n                    return token_type::parse_error;\n                }\n\n                case 0x04:\n                {\n                    error_message = \"invalid string: control character U+0004 (EOT) must be escaped to \\\\u0004\";\n                    return token_type::parse_error;\n                }\n\n                case 0x05:\n                {\n                    error_message = \"invalid string: control character U+0005 (ENQ) must be escaped to \\\\u0005\";\n                    return token_type::parse_error;\n                }\n\n                case 0x06:\n                {\n                    error_message = \"invalid string: control character U+0006 (ACK) must be escaped to \\\\u0006\";\n                    return token_type::parse_error;\n                }\n\n                case 0x07:\n                {\n                    error_message = \"invalid string: control character U+0007 (BEL) must be escaped to \\\\u0007\";\n                    return token_type::parse_error;\n                }\n\n                case 0x08:\n                {\n                    error_message = \"invalid string: control character U+0008 (BS) must be escaped to \\\\u0008 or \\\\b\";\n                    return token_type::parse_error;\n                }\n\n                case 0x09:\n                {\n                    error_message = \"invalid string: control character U+0009 (HT) must be escaped to \\\\u0009 or \\\\t\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0A:\n                {\n                    error_message = \"invalid string: control character U+000A (LF) must be escaped to \\\\u000A or \\\\n\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0B:\n                {\n                    error_message = \"invalid string: control character U+000B (VT) must be escaped to \\\\u000B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0C:\n                {\n                    error_message = \"invalid string: control character U+000C (FF) must be escaped to \\\\u000C or \\\\f\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0D:\n                {\n                    error_message = \"invalid string: control character U+000D (CR) must be escaped to \\\\u000D or \\\\r\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0E:\n                {\n                    error_message = \"invalid string: control character U+000E (SO) must be escaped to \\\\u000E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0F:\n                {\n                    error_message = \"invalid string: control character U+000F (SI) must be escaped to \\\\u000F\";\n                    return token_type::parse_error;\n                }\n\n                case 0x10:\n                {\n                    error_message = \"invalid string: control character U+0010 (DLE) must be escaped to \\\\u0010\";\n                    return token_type::parse_error;\n                }\n\n                case 0x11:\n                {\n                    error_message = \"invalid string: control character U+0011 (DC1) must be escaped to \\\\u0011\";\n                    return token_type::parse_error;\n                }\n\n                case 0x12:\n                {\n                    error_message = \"invalid string: control character U+0012 (DC2) must be escaped to \\\\u0012\";\n                    return token_type::parse_error;\n                }\n\n                case 0x13:\n                {\n                    error_message = \"invalid string: control character U+0013 (DC3) must be escaped to \\\\u0013\";\n                    return token_type::parse_error;\n                }\n\n                case 0x14:\n                {\n                    error_message = \"invalid string: control character U+0014 (DC4) must be escaped to \\\\u0014\";\n                    return token_type::parse_error;\n                }\n\n                case 0x15:\n                {\n                    error_message = \"invalid string: control character U+0015 (NAK) must be escaped to \\\\u0015\";\n                    return token_type::parse_error;\n                }\n\n                case 0x16:\n                {\n                    error_message = \"invalid string: control character U+0016 (SYN) must be escaped to \\\\u0016\";\n                    return token_type::parse_error;\n                }\n\n                case 0x17:\n                {\n                    error_message = \"invalid string: control character U+0017 (ETB) must be escaped to \\\\u0017\";\n                    return token_type::parse_error;\n                }\n\n                case 0x18:\n                {\n                    error_message = \"invalid string: control character U+0018 (CAN) must be escaped to \\\\u0018\";\n                    return token_type::parse_error;\n                }\n\n                case 0x19:\n                {\n                    error_message = \"invalid string: control character U+0019 (EM) must be escaped to \\\\u0019\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1A:\n                {\n                    error_message = \"invalid string: control character U+001A (SUB) must be escaped to \\\\u001A\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1B:\n                {\n                    error_message = \"invalid string: control character U+001B (ESC) must be escaped to \\\\u001B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1C:\n                {\n                    error_message = \"invalid string: control character U+001C (FS) must be escaped to \\\\u001C\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1D:\n                {\n                    error_message = \"invalid string: control character U+001D (GS) must be escaped to \\\\u001D\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1E:\n                {\n                    error_message = \"invalid string: control character U+001E (RS) must be escaped to \\\\u001E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1F:\n                {\n                    error_message = \"invalid string: control character U+001F (US) must be escaped to \\\\u001F\";\n                    return token_type::parse_error;\n                }\n\n                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))\n                case 0x20:\n                case 0x21:\n                case 0x23:\n                case 0x24:\n                case 0x25:\n                case 0x26:\n                case 0x27:\n                case 0x28:\n                case 0x29:\n                case 0x2A:\n                case 0x2B:\n                case 0x2C:\n                case 0x2D:\n                case 0x2E:\n                case 0x2F:\n                case 0x30:\n                case 0x31:\n                case 0x32:\n                case 0x33:\n                case 0x34:\n                case 0x35:\n                case 0x36:\n                case 0x37:\n                case 0x38:\n                case 0x39:\n                case 0x3A:\n                case 0x3B:\n                case 0x3C:\n                case 0x3D:\n                case 0x3E:\n                case 0x3F:\n                case 0x40:\n                case 0x41:\n                case 0x42:\n                case 0x43:\n                case 0x44:\n                case 0x45:\n                case 0x46:\n                case 0x47:\n                case 0x48:\n                case 0x49:\n                case 0x4A:\n                case 0x4B:\n                case 0x4C:\n                case 0x4D:\n                case 0x4E:\n                case 0x4F:\n                case 0x50:\n                case 0x51:\n                case 0x52:\n                case 0x53:\n                case 0x54:\n                case 0x55:\n                case 0x56:\n                case 0x57:\n                case 0x58:\n                case 0x59:\n                case 0x5A:\n                case 0x5B:\n                case 0x5D:\n                case 0x5E:\n                case 0x5F:\n                case 0x60:\n                case 0x61:\n                case 0x62:\n                case 0x63:\n                case 0x64:\n                case 0x65:\n                case 0x66:\n                case 0x67:\n                case 0x68:\n                case 0x69:\n                case 0x6A:\n                case 0x6B:\n                case 0x6C:\n                case 0x6D:\n                case 0x6E:\n                case 0x6F:\n                case 0x70:\n                case 0x71:\n                case 0x72:\n                case 0x73:\n                case 0x74:\n                case 0x75:\n                case 0x76:\n                case 0x77:\n                case 0x78:\n                case 0x79:\n                case 0x7A:\n                case 0x7B:\n                case 0x7C:\n                case 0x7D:\n                case 0x7E:\n                case 0x7F:\n                {\n                    add(current);\n                    break;\n                }\n\n                // U+0080..U+07FF: bytes C2..DF 80..BF\n                case 0xC2:\n                case 0xC3:\n                case 0xC4:\n                case 0xC5:\n                case 0xC6:\n                case 0xC7:\n                case 0xC8:\n                case 0xC9:\n                case 0xCA:\n                case 0xCB:\n                case 0xCC:\n                case 0xCD:\n                case 0xCE:\n                case 0xCF:\n                case 0xD0:\n                case 0xD1:\n                case 0xD2:\n                case 0xD3:\n                case 0xD4:\n                case 0xD5:\n                case 0xD6:\n                case 0xD7:\n                case 0xD8:\n                case 0xD9:\n                case 0xDA:\n                case 0xDB:\n                case 0xDC:\n                case 0xDD:\n                case 0xDE:\n                case 0xDF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF\n                case 0xE0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF\n                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF\n                case 0xE1:\n                case 0xE2:\n                case 0xE3:\n                case 0xE4:\n                case 0xE5:\n                case 0xE6:\n                case 0xE7:\n                case 0xE8:\n                case 0xE9:\n                case 0xEA:\n                case 0xEB:\n                case 0xEC:\n                case 0xEE:\n                case 0xEF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+D000..U+D7FF: bytes ED 80..9F 80..BF\n                case 0xED:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF\n                case 0xF0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF\n                case 0xF1:\n                case 0xF2:\n                case 0xF3:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF\n                case 0xF4:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // remaining bytes (80..C1 and F5..FF) are ill-formed\n                default:\n                {\n                    error_message = \"invalid string: ill-formed UTF-8 byte\";\n                    return token_type::parse_error;\n                }\n            }\n        }\n    }\n\n    /*!\n     * @brief scan a comment\n     * @return whether comment could be scanned successfully\n     */\n    bool scan_comment()\n    {\n        switch (get())\n        {\n            // single-line comments skip input until a newline or EOF is read\n            case '/':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case '\\n':\n                        case '\\r':\n                        case std::char_traits<char_type>::eof():\n                        case '\\0':\n                            return true;\n\n                        default:\n                            break;\n                    }\n                }\n            }\n\n            // multi-line comments skip input until */ is read\n            case '*':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case std::char_traits<char_type>::eof():\n                        case '\\0':\n                        {\n                            error_message = \"invalid comment; missing closing '*/'\";\n                            return false;\n                        }\n\n                        case '*':\n                        {\n                            switch (get())\n                            {\n                                case '/':\n                                    return true;\n\n                                default:\n                                {\n                                    unget();\n                                    continue;\n                                }\n                            }\n                        }\n\n                        default:\n                            continue;\n                    }\n                }\n            }\n\n            // unexpected character after reading '/'\n            default:\n            {\n                error_message = \"invalid comment; expecting '/' or '*' after '/'\";\n                return false;\n            }\n        }\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(float& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtof(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtod(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(long double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtold(str, endptr);\n    }\n\n    /*!\n    @brief scan a number literal\n\n    This function scans a string according to Sect. 6 of RFC 7159.\n\n    The function is realized with a deterministic finite state machine derived\n    from the grammar described in RFC 7159. Starting in state \"init\", the\n    input is read and used to determined the next state. Only state \"done\"\n    accepts the number. State \"error\" is a trap state to model errors. In the\n    table below, \"anything\" means any character but the ones listed before.\n\n    state    | 0        | 1-9      | e E      | +       | -       | .        | anything\n    ---------|----------|----------|----------|---------|---------|----------|-----------\n    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]\n    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]\n    zero     | done     | done     | exponent | done    | done    | decimal1 | done\n    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done\n    decimal1 | decimal2 | decimal2 | [error]  | [error] | [error] | [error]  | [error]\n    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done\n    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]\n    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]\n    any2     | any2     | any2     | done     | done    | done    | done     | done\n\n    The state machine is realized with one label per state (prefixed with\n    \"scan_number_\") and `goto` statements between them. The state machine\n    contains cycles, but any cycle can be left when EOF is read. Therefore,\n    the function is guaranteed to terminate.\n\n    During scanning, the read bytes are stored in token_buffer. This string is\n    then converted to a signed integer, an unsigned integer, or a\n    floating-point number.\n\n    @return token_type::value_unsigned, token_type::value_integer, or\n            token_type::value_float if number could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note The scanner is independent of the current locale. Internally, the\n          locale's decimal point is used instead of `.` to work with the\n          locale-dependent converters.\n    */\n    token_type scan_number()  // lgtm [cpp/use-of-goto]\n    {\n        // reset token_buffer to store the number's bytes\n        reset();\n\n        // the type of the parsed number; initially set to unsigned; will be\n        // changed if minus sign, decimal point or exponent is read\n        token_type number_type = token_type::value_unsigned;\n\n        // state (init): we just found out we need to scan a number\n        switch (current)\n        {\n            case '-':\n            {\n                add(current);\n                goto scan_number_minus;\n            }\n\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            // all other characters are rejected outside scan_number()\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false);  // LCOV_EXCL_LINE\n        }\n\nscan_number_minus:\n        // state: we just parsed a leading minus sign\n        number_type = token_type::value_integer;\n        switch (get())\n        {\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '-'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_zero:\n        // state: we just parse a zero (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_any1:\n        // state: we just parsed a number 0-9 (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_decimal1:\n        // state: we just parsed a decimal point\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '.'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_decimal2:\n        // we just parsed at least one number after a decimal point\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_exponent:\n        // we just parsed an exponent\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '+':\n            case '-':\n            {\n                add(current);\n                goto scan_number_sign;\n            }\n\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message =\n                    \"invalid number; expected '+', '-', or digit after exponent\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_sign:\n        // we just parsed an exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after exponent sign\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_any2:\n        // we just parsed a number after the exponent or exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_done:\n        // unget the character after the number (we only read it to know that\n        // we are done scanning a number)\n        unget();\n\n        char* endptr = nullptr;\n        errno = 0;\n\n        // try to parse integers first and fall back to floats\n        if (number_type == token_type::value_unsigned)\n        {\n            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_unsigned = static_cast<number_unsigned_t>(x);\n                if (value_unsigned == x)\n                {\n                    return token_type::value_unsigned;\n                }\n            }\n        }\n        else if (number_type == token_type::value_integer)\n        {\n            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_integer = static_cast<number_integer_t>(x);\n                if (value_integer == x)\n                {\n                    return token_type::value_integer;\n                }\n            }\n        }\n\n        // this code is reached if we parse a floating-point number or if an\n        // integer conversion above failed\n        strtof(value_float, token_buffer.data(), &endptr);\n\n        // we checked the number format before\n        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n        return token_type::value_float;\n    }\n\n    /*!\n    @param[in] literal_text  the literal text to expect\n    @param[in] length        the length of the passed literal text\n    @param[in] return_type   the token type to return on success\n    */\n    JSON_HEDLEY_NON_NULL(2)\n    token_type scan_literal(const char_type* literal_text, const std::size_t length,\n                            token_type return_type)\n    {\n        JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);\n        for (std::size_t i = 1; i < length; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))\n            {\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n            }\n        }\n        return return_type;\n    }\n\n    /////////////////////\n    // input management\n    /////////////////////\n\n    /// reset token_buffer; current character is beginning of token\n    void reset() noexcept\n    {\n        token_buffer.clear();\n        token_string.clear();\n        token_string.push_back(std::char_traits<char_type>::to_char_type(current));\n    }\n\n    /*\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a\n    `std::char_traits<char>::eof()` in that case.  Stores the scanned characters\n    for use in error messages.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++position.chars_read_total;\n        ++position.chars_read_current_line;\n\n        if (next_unget)\n        {\n            // just reset the next_unget variable and work with current\n            next_unget = false;\n        }\n        else\n        {\n            current = ia.get_character();\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))\n        {\n            token_string.push_back(std::char_traits<char_type>::to_char_type(current));\n        }\n\n        if (current == '\\n')\n        {\n            ++position.lines_read;\n            position.chars_read_current_line = 0;\n        }\n\n        return current;\n    }\n\n    /*!\n    @brief unget current character (read it again on next get)\n\n    We implement unget by setting variable next_unget to true. The input is not\n    changed - we just simulate ungetting by modifying chars_read_total,\n    chars_read_current_line, and token_string. The next call to get() will\n    behave as if the unget character is read again.\n    */\n    void unget()\n    {\n        next_unget = true;\n\n        --position.chars_read_total;\n\n        // in case we \"unget\" a newline, we have to also decrement the lines_read\n        if (position.chars_read_current_line == 0)\n        {\n            if (position.lines_read > 0)\n            {\n                --position.lines_read;\n            }\n        }\n        else\n        {\n            --position.chars_read_current_line;\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))\n        {\n            JSON_ASSERT(!token_string.empty());\n            token_string.pop_back();\n        }\n    }\n\n    /// add a character to token_buffer\n    void add(char_int_type c)\n    {\n        token_buffer.push_back(static_cast<typename string_t::value_type>(c));\n    }\n\n  public:\n    /////////////////////\n    // value getters\n    /////////////////////\n\n    /// return integer value\n    constexpr number_integer_t get_number_integer() const noexcept\n    {\n        return value_integer;\n    }\n\n    /// return unsigned integer value\n    constexpr number_unsigned_t get_number_unsigned() const noexcept\n    {\n        return value_unsigned;\n    }\n\n    /// return floating-point value\n    constexpr number_float_t get_number_float() const noexcept\n    {\n        return value_float;\n    }\n\n    /// return current string value (implicitly resets the token; useful only once)\n    string_t& get_string()\n    {\n        return token_buffer;\n    }\n\n    /////////////////////\n    // diagnostics\n    /////////////////////\n\n    /// return position of last read token\n    constexpr position_t get_position() const noexcept\n    {\n        return position;\n    }\n\n    /// return the last read token (for errors only).  Will never contain EOF\n    /// (an arbitrary value that is not a valid char value, often -1), because\n    /// 255 may legitimately occur.  May contain NUL, which should be escaped.\n    std::string get_token_string() const\n    {\n        // escape control characters\n        std::string result;\n        for (const auto c : token_string)\n        {\n            if (static_cast<unsigned char>(c) <= '\\x1F')\n            {\n                // escape control characters\n                std::array<char, 9> cs{{}};\n                (std::snprintf)(cs.data(), cs.size(), \"<U+%.4X>\", static_cast<unsigned char>(c));\n                result += cs.data();\n            }\n            else\n            {\n                // add character as is\n                result.push_back(static_cast<std::string::value_type>(c));\n            }\n        }\n\n        return result;\n    }\n\n    /// return syntax error message\n    JSON_HEDLEY_RETURNS_NON_NULL\n    constexpr const char* get_error_message() const noexcept\n    {\n        return error_message;\n    }\n\n    /////////////////////\n    // actual scanner\n    /////////////////////\n\n    /*!\n    @brief skip the UTF-8 byte order mark\n    @return true iff there is no BOM or the correct BOM has been skipped\n    */\n    bool skip_bom()\n    {\n        if (get() == 0xEF)\n        {\n            // check if we completely parse the BOM\n            return get() == 0xBB && get() == 0xBF;\n        }\n\n        // the first character is not the beginning of the BOM; unget it to\n        // process is later\n        unget();\n        return true;\n    }\n\n    void skip_whitespace()\n    {\n        do\n        {\n            get();\n        }\n        while (current == ' ' || current == '\\t' || current == '\\n' || current == '\\r');\n    }\n\n    token_type scan()\n    {\n        // initially, skip the BOM\n        if (position.chars_read_total == 0 && !skip_bom())\n        {\n            error_message = \"invalid BOM; must be 0xEF 0xBB 0xBF if given\";\n            return token_type::parse_error;\n        }\n\n        // read next character and ignore whitespace\n        skip_whitespace();\n\n        // ignore comments\n        while (ignore_comments && current == '/')\n        {\n            if (!scan_comment())\n            {\n                return token_type::parse_error;\n            }\n\n            // skip following whitespace\n            skip_whitespace();\n        }\n\n        switch (current)\n        {\n            // structural characters\n            case '[':\n                return token_type::begin_array;\n            case ']':\n                return token_type::end_array;\n            case '{':\n                return token_type::begin_object;\n            case '}':\n                return token_type::end_object;\n            case ':':\n                return token_type::name_separator;\n            case ',':\n                return token_type::value_separator;\n\n            // literals\n            case 't':\n            {\n                std::array<char_type, 4> true_literal = {{'t', 'r', 'u', 'e'}};\n                return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);\n            }\n            case 'f':\n            {\n                std::array<char_type, 5> false_literal = {{'f', 'a', 'l', 's', 'e'}};\n                return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);\n            }\n            case 'n':\n            {\n                std::array<char_type, 4> null_literal = {{'n', 'u', 'l', 'l'}};\n                return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);\n            }\n\n            // string\n            case '\\\"':\n                return scan_string();\n\n            // number\n            case '-':\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n                return scan_number();\n\n            // end of input (the null byte is needed when parsing from\n            // string literals)\n            case '\\0':\n            case std::char_traits<char_type>::eof():\n                return token_type::end_of_input;\n\n            // error\n            default:\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n        }\n    }\n\n  private:\n    /// input adapter\n    InputAdapterType ia;\n\n    /// whether comments should be ignored (true) or signaled as errors (false)\n    const bool ignore_comments = false;\n\n    /// the current character\n    char_int_type current = std::char_traits<char_type>::eof();\n\n    /// whether the next get() call should just return current\n    bool next_unget = false;\n\n    /// the start position of the current token\n    position_t position {};\n\n    /// raw input token string (for error messages)\n    std::vector<char_type> token_string {};\n\n    /// buffer for variable-length tokens (numbers, strings)\n    string_t token_buffer {};\n\n    /// a description of occurred lexer errors\n    const char* error_message = \"\";\n\n    // number values\n    number_integer_t value_integer = 0;\n    number_unsigned_t value_unsigned = 0;\n    number_float_t value_float = 0;\n\n    /// the decimal point\n    const char_int_type decimal_point_char = '.';\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n\n#include <cstdint> // size_t\n#include <utility> // declval\n#include <string> // string\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename T>\nusing null_function_t = decltype(std::declval<T&>().null());\n\ntemplate<typename T>\nusing boolean_function_t =\n    decltype(std::declval<T&>().boolean(std::declval<bool>()));\n\ntemplate<typename T, typename Integer>\nusing number_integer_function_t =\n    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));\n\ntemplate<typename T, typename Unsigned>\nusing number_unsigned_function_t =\n    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));\n\ntemplate<typename T, typename Float, typename String>\nusing number_float_function_t = decltype(std::declval<T&>().number_float(\n                                    std::declval<Float>(), std::declval<const String&>()));\n\ntemplate<typename T, typename String>\nusing string_function_t =\n    decltype(std::declval<T&>().string(std::declval<String&>()));\n\ntemplate<typename T, typename Binary>\nusing binary_function_t =\n    decltype(std::declval<T&>().binary(std::declval<Binary&>()));\n\ntemplate<typename T>\nusing start_object_function_t =\n    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));\n\ntemplate<typename T, typename String>\nusing key_function_t =\n    decltype(std::declval<T&>().key(std::declval<String&>()));\n\ntemplate<typename T>\nusing end_object_function_t = decltype(std::declval<T&>().end_object());\n\ntemplate<typename T>\nusing start_array_function_t =\n    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));\n\ntemplate<typename T>\nusing end_array_function_t = decltype(std::declval<T&>().end_array());\n\ntemplate<typename T, typename Exception>\nusing parse_error_function_t = decltype(std::declval<T&>().parse_error(\n        std::declval<std::size_t>(), std::declval<const std::string&>(),\n        std::declval<const Exception&>()));\n\ntemplate<typename SAX, typename BasicJsonType>\nstruct is_sax\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static constexpr bool value =\n        is_detected_exact<bool, null_function_t, SAX>::value &&\n        is_detected_exact<bool, boolean_function_t, SAX>::value &&\n        is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&\n        is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&\n        is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&\n        is_detected_exact<bool, start_object_function_t, SAX>::value &&\n        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, end_object_function_t, SAX>::value &&\n        is_detected_exact<bool, start_array_function_t, SAX>::value &&\n        is_detected_exact<bool, end_array_function_t, SAX>::value &&\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;\n};\n\ntemplate<typename SAX, typename BasicJsonType>\nstruct is_sax_static_asserts\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,\n                  \"Missing/invalid function: bool null()\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(\n        is_detected_exact<bool, number_integer_function_t, SAX,\n        number_integer_t>::value,\n        \"Missing/invalid function: bool number_integer(number_integer_t)\");\n    static_assert(\n        is_detected_exact<bool, number_unsigned_function_t, SAX,\n        number_unsigned_t>::value,\n        \"Missing/invalid function: bool number_unsigned(number_unsigned_t)\");\n    static_assert(is_detected_exact<bool, number_float_function_t, SAX,\n                  number_float_t, string_t>::value,\n                  \"Missing/invalid function: bool number_float(number_float_t, const string_t&)\");\n    static_assert(\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value,\n        \"Missing/invalid function: bool string(string_t&)\");\n    static_assert(\n        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,\n        \"Missing/invalid function: bool binary(binary_t&)\");\n    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_object(std::size_t)\");\n    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,\n                  \"Missing/invalid function: bool key(string_t&)\");\n    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_object()\");\n    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_array(std::size_t)\");\n    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_array()\");\n    static_assert(\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,\n        \"Missing/invalid function: bool parse_error(std::size_t, const \"\n        \"std::string&, const exception&)\");\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/// how to treat CBOR tags\nenum class cbor_tag_handler_t\n{\n    error,  ///< throw a parse_error exception in case of a tag\n    ignore   ///< ignore tags\n};\n\n/*!\n@brief determine system byte order\n\n@return true if and only if system's byte order is little endian\n\n@note from https://stackoverflow.com/a/1001328/266378\n*/\nstatic inline bool little_endianess(int num = 1) noexcept\n{\n    return *reinterpret_cast<char*>(&num) == 1;\n}\n\n\n///////////////////\n// binary reader //\n///////////////////\n\n/*!\n@brief deserialization of CBOR, MessagePack, and UBJSON values\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>>\nclass binary_reader\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using json_sax_t = SAX;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename std::char_traits<char_type>::int_type;\n\n  public:\n    /*!\n    @brief create a binary reader\n\n    @param[in] adapter  input adapter to read from\n    */\n    explicit binary_reader(InputAdapterType&& adapter) : ia(std::move(adapter))\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n    }\n\n    // make class move-only\n    binary_reader(const binary_reader&) = delete;\n    binary_reader(binary_reader&&) = default;\n    binary_reader& operator=(const binary_reader&) = delete;\n    binary_reader& operator=(binary_reader&&) = default;\n    ~binary_reader() = default;\n\n    /*!\n    @param[in] format  the binary format to parse\n    @param[in] sax_    a SAX event processor\n    @param[in] strict  whether to expect the input to be consumed completed\n    @param[in] tag_handler  how to treat CBOR tags\n\n    @return\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool sax_parse(const input_format_t format,\n                   json_sax_t* sax_,\n                   const bool strict = true,\n                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        sax = sax_;\n        bool result = false;\n\n        switch (format)\n        {\n            case input_format_t::bson:\n                result = parse_bson_internal();\n                break;\n\n            case input_format_t::cbor:\n                result = parse_cbor_internal(true, tag_handler);\n                break;\n\n            case input_format_t::msgpack:\n                result = parse_msgpack_internal();\n                break;\n\n            case input_format_t::ubjson:\n                result = parse_ubjson_internal();\n                break;\n\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false);  // LCOV_EXCL_LINE\n        }\n\n        // strict mode: next byte must be EOF\n        if (result && strict)\n        {\n            if (format == input_format_t::ubjson)\n            {\n                get_ignore_noop();\n            }\n            else\n            {\n                get();\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))\n            {\n                return sax->parse_error(chars_read, get_token_string(),\n                                        parse_error::create(110, chars_read, exception_message(format, \"expected end of input; last byte: 0x\" + get_token_string(), \"value\")));\n            }\n        }\n\n        return result;\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @brief Reads in a BSON-object and passes it to the SAX-parser.\n    @return whether a valid BSON-value was passed to the SAX parser\n    */\n    bool parse_bson_internal()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))\n        {\n            return false;\n        }\n\n        return sax->end_object();\n    }\n\n    /*!\n    @brief Parses a C-style string from the BSON input.\n    @param[in, out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @return `true` if the \\x00-byte indicating the end of the string was\n             encountered before the EOF; false` indicates an unexpected EOF.\n    */\n    bool get_bson_cstr(string_t& result)\n    {\n        auto out = std::back_inserter(result);\n        while (true)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"cstring\")))\n            {\n                return false;\n            }\n            if (current == 0x00)\n            {\n                return true;\n            }\n            *out++ = static_cast<typename string_t::value_type>(current);\n        }\n    }\n\n    /*!\n    @brief Parses a zero-terminated string of length @a len from the BSON\n           input.\n    @param[in] len  The length (including the zero-byte at the end) of the\n                    string to be read.\n    @param[in, out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 1\n    @return `true` if the string was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_string(const NumberType len, string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 1))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, \"string length must be at least 1, is \" + std::to_string(len), \"string\")));\n        }\n\n        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();\n    }\n\n    /*!\n    @brief Parses a byte array input of length @a len from the BSON input.\n    @param[in] len  The length of the byte array to be read.\n    @param[in, out] result  A reference to the binary variable where the read\n                            array is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 0\n    @return `true` if the byte array was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_binary(const NumberType len, binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 0))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, \"byte array length cannot be negative, is \" + std::to_string(len), \"binary\")));\n        }\n\n        // All BSON binary values have a subtype\n        std::uint8_t subtype{};\n        get_number<std::uint8_t>(input_format_t::bson, subtype);\n        result.set_subtype(subtype);\n\n        return get_binary(input_format_t::bson, len, result);\n    }\n\n    /*!\n    @brief Read a BSON document element of the given @a element_type.\n    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html\n    @param[in] element_type_parse_position The position in the input stream,\n               where the `element_type` was read.\n    @warning Not all BSON element types are supported yet. An unsupported\n             @a element_type will give rise to a parse_error.114:\n             Unsupported BSON record type 0x...\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_internal(const char_int_type element_type,\n                                     const std::size_t element_type_parse_position)\n    {\n        switch (element_type)\n        {\n            case 0x01: // double\n            {\n                double number{};\n                return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0x02: // string\n            {\n                std::int32_t len{};\n                string_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);\n            }\n\n            case 0x03: // object\n            {\n                return parse_bson_internal();\n            }\n\n            case 0x04: // array\n            {\n                return parse_bson_array();\n            }\n\n            case 0x05: // binary\n            {\n                std::int32_t len{};\n                binary_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);\n            }\n\n            case 0x08: // boolean\n            {\n                return sax->boolean(get() != 0);\n            }\n\n            case 0x0A: // null\n            {\n                return sax->null();\n            }\n\n            case 0x10: // int32\n            {\n                std::int32_t value{};\n                return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            case 0x12: // int64\n            {\n                std::int64_t value{};\n                return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            default: // anything else not supported (yet)\n            {\n                std::array<char, 3> cr{{}};\n                (std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(element_type));\n                return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, \"Unsupported BSON record type 0x\" + std::string(cr.data())));\n            }\n        }\n    }\n\n    /*!\n    @brief Read a BSON element list (as specified in the BSON-spec)\n\n    The same binary layout is used for objects and arrays, hence it must be\n    indicated with the argument @a is_array which one is expected\n    (true --> array, false --> object).\n\n    @param[in] is_array Determines if the element list being read is to be\n                        treated as an object (@a is_array == false), or as an\n                        array (@a is_array == true).\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_list(const bool is_array)\n    {\n        string_t key;\n\n        while (auto element_type = get())\n        {\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"element list\")))\n            {\n                return false;\n            }\n\n            const std::size_t element_type_parse_position = chars_read;\n            if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))\n            {\n                return false;\n            }\n\n            if (!is_array && !sax->key(key))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))\n            {\n                return false;\n            }\n\n            // get_bson_cstr only appends\n            key.clear();\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief Reads an array from the BSON input and passes it to the SAX-parser.\n    @return whether a valid BSON-array was passed to the SAX parser\n    */\n    bool parse_bson_array()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))\n        {\n            return false;\n        }\n\n        return sax->end_array();\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true) or whether the last read character should\n                         be considered instead (false)\n    @param[in] tag_handler how CBOR tags should be treated\n\n    @return whether a valid CBOR value was passed to the SAX parser\n    */\n    bool parse_cbor_internal(const bool get_char,\n                             const cbor_tag_handler_t tag_handler)\n    {\n        switch (get_char ? get() : current)\n        {\n            // EOF\n            case std::char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::cbor, \"value\");\n\n            // Integer 0x00..0x17 (0..23)\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            case 0x18: // Unsigned integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x19: // Unsigned integer (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1A: // Unsigned integer (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            // Negative integer -1-0x00..-1-0x17 (-1..-24)\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n                return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));\n\n            case 0x38: // Negative integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)\n                        - static_cast<number_integer_t>(number));\n            }\n\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            case 0x5F: // Binary data (indefinite length)\n            {\n                binary_t b;\n                return get_cbor_binary(b) && sax->binary(b);\n            }\n\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                string_t s;\n                return get_cbor_string(s) && sax->string(s);\n            }\n\n            // array (0x00..0x17 data items follow)\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n                return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0x98: // array (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x99: // array (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9A: // array (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9B: // array (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9F: // array (indefinite length)\n                return get_cbor_array(std::size_t(-1), tag_handler);\n\n            // map (0x00..0x17 pairs of data items follow)\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n                return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0xB8: // map (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xB9: // map (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBA: // map (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBB: // map (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBF: // map (indefinite length)\n                return get_cbor_object(std::size_t(-1), tag_handler);\n\n            case 0xC6: // tagged item\n            case 0xC7:\n            case 0xC8:\n            case 0xC9:\n            case 0xCA:\n            case 0xCB:\n            case 0xCC:\n            case 0xCD:\n            case 0xCE:\n            case 0xCF:\n            case 0xD0:\n            case 0xD1:\n            case 0xD2:\n            case 0xD3:\n            case 0xD4:\n            case 0xD8: // tagged item (1 bytes follow)\n            case 0xD9: // tagged item (2 bytes follow)\n            case 0xDA: // tagged item (4 bytes follow)\n            case 0xDB: // tagged item (8 bytes follow)\n            {\n                switch (tag_handler)\n                {\n                    case cbor_tag_handler_t::error:\n                    {\n                        auto last_token = get_token_string();\n                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, \"invalid byte: 0x\" + last_token, \"value\")));\n                    }\n\n                    case cbor_tag_handler_t::ignore:\n                    {\n                        switch (current)\n                        {\n                            case 0xD8:\n                            {\n                                std::uint8_t len{};\n                                get_number(input_format_t::cbor, len);\n                                break;\n                            }\n                            case 0xD9:\n                            {\n                                std::uint16_t len{};\n                                get_number(input_format_t::cbor, len);\n                                break;\n                            }\n                            case 0xDA:\n                            {\n                                std::uint32_t len{};\n                                get_number(input_format_t::cbor, len);\n                                break;\n                            }\n                            case 0xDB:\n                            {\n                                std::uint64_t len{};\n                                get_number(input_format_t::cbor, len);\n                                break;\n                            }\n                            default:\n                                break;\n                        }\n                        return parse_cbor_internal(true, tag_handler);\n                    }\n\n                    default:            // LCOV_EXCL_LINE\n                        JSON_ASSERT(false);  // LCOV_EXCL_LINE\n                }\n            }\n\n            case 0xF4: // false\n                return sax->boolean(false);\n\n            case 0xF5: // true\n                return sax->boolean(true);\n\n            case 0xF6: // null\n                return sax->null();\n\n            case 0xF9: // Half-Precision Float (two-byte IEEE 754)\n            {\n                const auto byte1_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n                const auto byte2_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n\n                const auto byte1 = static_cast<unsigned char>(byte1_raw);\n                const auto byte2 = static_cast<unsigned char>(byte2_raw);\n\n                // code from RFC 7049, Appendix D, Figure 3:\n                // As half-precision floating-point numbers were only added\n                // to IEEE 754 in 2008, today's programming platforms often\n                // still only have limited support for them. It is very\n                // easy to include at least decoding support for them even\n                // without such support. An example of a small decoder for\n                // half-precision floating-point numbers in the C language\n                // is shown in Fig. 3.\n                const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);\n                const double val = [&half]\n                {\n                    const int exp = (half >> 10u) & 0x1Fu;\n                    const unsigned int mant = half & 0x3FFu;\n                    JSON_ASSERT(0 <= exp&& exp <= 32);\n                    JSON_ASSERT(mant <= 1024);\n                    switch (exp)\n                    {\n                        case 0:\n                            return std::ldexp(mant, -24);\n                        case 31:\n                            return (mant == 0)\n                            ? std::numeric_limits<double>::infinity()\n                            : std::numeric_limits<double>::quiet_NaN();\n                        default:\n                            return std::ldexp(mant + 1024, exp - 25);\n                    }\n                }();\n                return sax->number_float((half & 0x8000u) != 0\n                                         ? static_cast<number_float_t>(-val)\n                                         : static_cast<number_float_t>(val), \"\");\n            }\n\n            case 0xFA: // Single-Precision Float (four-byte IEEE 754)\n            {\n                float number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)\n            {\n                double number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            default: // anything else (0xFF is handled inside the other types)\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n    Additionally, CBOR's strings with indefinite lengths are supported.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_cbor_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            {\n                return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    string_t chunk;\n                    if (!get_cbor_string(chunk))\n                    {\n                        return false;\n                    }\n                    result.append(chunk);\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, \"expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x\" + last_token, \"string\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into the byte array.\n    Additionally, CBOR's byte arrays with indefinite lengths are supported.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_cbor_binary(binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"binary\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            {\n                return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5F: // Binary data (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    binary_t chunk;\n                    if (!get_cbor_binary(chunk))\n                    {\n                        return false;\n                    }\n                    result.insert(result.end(), chunk.begin(), chunk.end());\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, \"expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x\" + last_token, \"binary\")));\n            }\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array or std::size_t(-1) for an\n                    array of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether array creation completed\n    */\n    bool get_cbor_array(const std::size_t len,\n                        const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        if (len != std::size_t(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object or std::size_t(-1) for an\n                    object of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether object creation completed\n    */\n    bool get_cbor_object(const std::size_t len,\n                         const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        if (len != std::size_t(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                get();\n                if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                {\n                    return false;\n                }\n\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                {\n                    return false;\n                }\n                key.clear();\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                {\n                    return false;\n                }\n\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                {\n                    return false;\n                }\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    /*!\n    @return whether a valid MessagePack value was passed to the SAX parser\n    */\n    bool parse_msgpack_internal()\n    {\n        switch (get())\n        {\n            // EOF\n            case std::char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::msgpack, \"value\");\n\n            // positive fixint\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n            case 0x18:\n            case 0x19:\n            case 0x1A:\n            case 0x1B:\n            case 0x1C:\n            case 0x1D:\n            case 0x1E:\n            case 0x1F:\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n            case 0x38:\n            case 0x39:\n            case 0x3A:\n            case 0x3B:\n            case 0x3C:\n            case 0x3D:\n            case 0x3E:\n            case 0x3F:\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58:\n            case 0x59:\n            case 0x5A:\n            case 0x5B:\n            case 0x5C:\n            case 0x5D:\n            case 0x5E:\n            case 0x5F:\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78:\n            case 0x79:\n            case 0x7A:\n            case 0x7B:\n            case 0x7C:\n            case 0x7D:\n            case 0x7E:\n            case 0x7F:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            // fixmap\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n                return get_msgpack_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixarray\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n            case 0x98:\n            case 0x99:\n            case 0x9A:\n            case 0x9B:\n            case 0x9C:\n            case 0x9D:\n            case 0x9E:\n            case 0x9F:\n                return get_msgpack_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            case 0xD9: // str 8\n            case 0xDA: // str 16\n            case 0xDB: // str 32\n            {\n                string_t s;\n                return get_msgpack_string(s) && sax->string(s);\n            }\n\n            case 0xC0: // nil\n                return sax->null();\n\n            case 0xC2: // false\n                return sax->boolean(false);\n\n            case 0xC3: // true\n                return sax->boolean(true);\n\n            case 0xC4: // bin 8\n            case 0xC5: // bin 16\n            case 0xC6: // bin 32\n            case 0xC7: // ext 8\n            case 0xC8: // ext 16\n            case 0xC9: // ext 32\n            case 0xD4: // fixext 1\n            case 0xD5: // fixext 2\n            case 0xD6: // fixext 4\n            case 0xD7: // fixext 8\n            case 0xD8: // fixext 16\n            {\n                binary_t b;\n                return get_msgpack_binary(b) && sax->binary(b);\n            }\n\n            case 0xCA: // float 32\n            {\n                float number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCB: // float 64\n            {\n                double number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCC: // uint 8\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCD: // uint 16\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCE: // uint 32\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCF: // uint 64\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xD0: // int 8\n            {\n                std::int8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD1: // int 16\n            {\n                std::int16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD2: // int 32\n            {\n                std::int32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD3: // int 64\n            {\n                std::int64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xDC: // array 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDD: // array 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDE: // map 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xDF: // map 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            // negative fixint\n            case 0xE0:\n            case 0xE1:\n            case 0xE2:\n            case 0xE3:\n            case 0xE4:\n            case 0xE5:\n            case 0xE6:\n            case 0xE7:\n            case 0xE8:\n            case 0xE9:\n            case 0xEA:\n            case 0xEB:\n            case 0xEC:\n            case 0xED:\n            case 0xEE:\n            case 0xEF:\n            case 0xF0:\n            case 0xF1:\n            case 0xF2:\n            case 0xF3:\n            case 0xF4:\n            case 0xF5:\n            case 0xF6:\n            case 0xF7:\n            case 0xF8:\n            case 0xF9:\n            case 0xFA:\n            case 0xFB:\n            case 0xFC:\n            case 0xFD:\n            case 0xFE:\n            case 0xFF:\n                return sax->number_integer(static_cast<std::int8_t>(current));\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_msgpack_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            {\n                return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0xD9: // str 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDA: // str 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDB: // str 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, \"expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x\" + last_token, \"string\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into a byte array.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_msgpack_binary(binary_t& result)\n    {\n        // helper function to set the subtype\n        auto assign_and_return_true = [&result](std::int8_t subtype)\n        {\n            result.set_subtype(static_cast<std::uint8_t>(subtype));\n            return true;\n        };\n\n        switch (current)\n        {\n            case 0xC4: // bin 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC5: // bin 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC6: // bin 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC7: // ext 8\n            {\n                std::uint8_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC8: // ext 16\n            {\n                std::uint16_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC9: // ext 32\n            {\n                std::uint32_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD4: // fixext 1\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 1, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD5: // fixext 2\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 2, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD6: // fixext 4\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 4, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD7: // fixext 8\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 8, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD8: // fixext 16\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 16, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            default:           // LCOV_EXCL_LINE\n                return false;  // LCOV_EXCL_LINE\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array\n    @return whether array creation completed\n    */\n    bool get_msgpack_array(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object\n    @return whether object creation completed\n    */\n    bool get_msgpack_object(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n            key.clear();\n        }\n\n        return sax->end_object();\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether a valid UBJSON value was passed to the SAX parser\n    */\n    bool parse_ubjson_internal(const bool get_char = true)\n    {\n        return get_ubjson_value(get_char ? get_ignore_noop() : current);\n    }\n\n    /*!\n    @brief reads a UBJSON string\n\n    This function is either called after reading the 'S' byte explicitly\n    indicating a string, or in case of an object key where the 'S' byte can be\n    left out.\n\n    @param[out] result   created string\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether string creation completed\n    */\n    bool get_ubjson_string(string_t& result, const bool get_char = true)\n    {\n        if (get_char)\n        {\n            get();  // TODO(niels): may we ignore N here?\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"value\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            case 'U':\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'i':\n            {\n                std::int8_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'I':\n            {\n                std::int16_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'l':\n            {\n                std::int32_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'L':\n            {\n                std::int64_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            default:\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L); last byte: 0x\" + last_token, \"string\")));\n        }\n    }\n\n    /*!\n    @param[out] result  determined size\n    @return whether size determination completed\n    */\n    bool get_ubjson_size_value(std::size_t& result)\n    {\n        switch (get_ignore_noop())\n        {\n            case 'U':\n            {\n                std::uint8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L) after '#'; last byte: 0x\" + last_token, \"size\")));\n            }\n        }\n    }\n\n    /*!\n    @brief determine the type and size for a container\n\n    In the optimized UBJSON format, a type and a size can be provided to allow\n    for a more compact representation.\n\n    @param[out] result  pair of the size and the type\n\n    @return whether pair creation completed\n    */\n    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result)\n    {\n        result.first = string_t::npos; // size\n        result.second = 0; // type\n\n        get_ignore_noop();\n\n        if (current == '$')\n        {\n            result.second = get();  // must not ignore 'N', because 'N' maybe the type\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"type\")))\n            {\n                return false;\n            }\n\n            get_ignore_noop();\n            if (JSON_HEDLEY_UNLIKELY(current != '#'))\n            {\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"value\")))\n                {\n                    return false;\n                }\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"expected '#' after type information; last byte: 0x\" + last_token, \"size\")));\n            }\n\n            return get_ubjson_size_value(result.first);\n        }\n\n        if (current == '#')\n        {\n            return get_ubjson_size_value(result.first);\n        }\n\n        return true;\n    }\n\n    /*!\n    @param prefix  the previously read or set type prefix\n    @return whether value creation completed\n    */\n    bool get_ubjson_value(const char_int_type prefix)\n    {\n        switch (prefix)\n        {\n            case std::char_traits<char_type>::eof():  // EOF\n                return unexpect_eof(input_format_t::ubjson, \"value\");\n\n            case 'T':  // true\n                return sax->boolean(true);\n            case 'F':  // false\n                return sax->boolean(false);\n\n            case 'Z':  // null\n                return sax->null();\n\n            case 'U':\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number);\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'd':\n            {\n                float number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'D':\n            {\n                double number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'H':\n            {\n                return get_ubjson_high_precision_number();\n            }\n\n            case 'C':  // char\n            {\n                get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"char\")))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(current > 127))\n                {\n                    auto last_token = get_token_string();\n                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"byte after 'C' must be in range 0x00..0x7F; last byte: 0x\" + last_token, \"char\")));\n                }\n                string_t s(1, static_cast<typename string_t::value_type>(current));\n                return sax->string(s);\n            }\n\n            case 'S':  // string\n            {\n                string_t s;\n                return get_ubjson_string(s) && sax->string(s);\n            }\n\n            case '[':  // array\n                return get_ubjson_array();\n\n            case '{':  // object\n                return get_ubjson_object();\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @return whether array creation completed\n    */\n    bool get_ubjson_array()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                if (size_and_type.second != 'N')\n                {\n                    for (std::size_t i = 0; i < size_and_type.first; ++i)\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                        {\n                            return false;\n                        }\n                    }\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != ']')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @return whether object creation completed\n    */\n    bool get_ubjson_object()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        string_t key;\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != '}')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    // Note, no reader for UBJSON binary types is implemented because they do\n    // not exist\n\n    bool get_ubjson_high_precision_number()\n    {\n        // get size of following number string\n        std::size_t size{};\n        auto res = get_ubjson_size_value(size);\n        if (JSON_HEDLEY_UNLIKELY(!res))\n        {\n            return res;\n        }\n\n        // get number string\n        std::vector<char> number_vector;\n        for (std::size_t i = 0; i < size; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"number\")))\n            {\n                return false;\n            }\n            number_vector.push_back(static_cast<char>(current));\n        }\n\n        // parse number string\n        auto number_ia = detail::input_adapter(std::forward<decltype(number_vector)>(number_vector));\n        auto number_lexer = detail::lexer<BasicJsonType, decltype(number_ia)>(std::move(number_ia), false);\n        const auto result_number = number_lexer.scan();\n        const auto number_string = number_lexer.get_token_string();\n        const auto result_remainder = number_lexer.scan();\n\n        using token_type = typename detail::lexer_base<BasicJsonType>::token_type;\n\n        if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))\n        {\n            return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, \"invalid number text: \" + number_lexer.get_token_string(), \"high-precision number\")));\n        }\n\n        switch (result_number)\n        {\n            case token_type::value_integer:\n                return sax->number_integer(number_lexer.get_number_integer());\n            case token_type::value_unsigned:\n                return sax->number_unsigned(number_lexer.get_number_unsigned());\n            case token_type::value_float:\n                return sax->number_float(number_lexer.get_number_float(), std::move(number_string));\n            default:\n                return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, \"invalid number text: \" + number_lexer.get_token_string(), \"high-precision number\")));\n        }\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*!\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a -'ve valued\n    `std::char_traits<char_type>::eof()` in that case.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++chars_read;\n        return current = ia.get_character();\n    }\n\n    /*!\n    @return character read from the input after ignoring all 'N' entries\n    */\n    char_int_type get_ignore_noop()\n    {\n        do\n        {\n            get();\n        }\n        while (current == 'N');\n\n        return current;\n    }\n\n    /*\n    @brief read a number from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format   the current format (for diagnostics)\n    @param[out] result  number of type @a NumberType\n\n    @return whether conversion completed\n\n    @note This function needs to respect the system's endianess, because\n          bytes in CBOR, MessagePack, and UBJSON are stored in network order\n          (big endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool InputIsLittleEndian = false>\n    bool get_number(const input_format_t format, NumberType& result)\n    {\n        // step 1: read input into array with system's byte order\n        std::array<std::uint8_t, sizeof(NumberType)> vec;\n        for (std::size_t i = 0; i < sizeof(NumberType); ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"number\")))\n            {\n                return false;\n            }\n\n            // reverse byte order prior to conversion if necessary\n            if (is_little_endian != InputIsLittleEndian)\n            {\n                vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);\n            }\n            else\n            {\n                vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE\n            }\n        }\n\n        // step 2: convert array into number of type T and return\n        std::memcpy(&result, vec.data(), sizeof(NumberType));\n        return true;\n    }\n\n    /*!\n    @brief create a string by reading characters from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of characters to read\n    @param[out] result string created by reading @a len bytes\n\n    @return whether string creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of string memory.\n    */\n    template<typename NumberType>\n    bool get_string(const input_format_t format,\n                    const NumberType len,\n                    string_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"string\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<typename string_t::value_type>(current));\n        };\n        return success;\n    }\n\n    /*!\n    @brief create a byte array by reading bytes from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of bytes to read\n    @param[out] result byte array created by reading @a len bytes\n\n    @return whether byte array creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of memory.\n    */\n    template<typename NumberType>\n    bool get_binary(const input_format_t format,\n                    const NumberType len,\n                    binary_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"binary\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<std::uint8_t>(current));\n        }\n        return success;\n    }\n\n    /*!\n    @param[in] format   the current format (for diagnostics)\n    @param[in] context  further context information (for diagnostics)\n    @return whether the last read character is not EOF\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool unexpect_eof(const input_format_t format, const char* context) const\n    {\n        if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))\n        {\n            return sax->parse_error(chars_read, \"<end of file>\",\n                                    parse_error::create(110, chars_read, exception_message(format, \"unexpected end of input\", context)));\n        }\n        return true;\n    }\n\n    /*!\n    @return a string representation of the last read byte\n    */\n    std::string get_token_string() const\n    {\n        std::array<char, 3> cr{{}};\n        (std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(current));\n        return std::string{cr.data()};\n    }\n\n    /*!\n    @param[in] format   the current format\n    @param[in] detail   a detailed error message\n    @param[in] context  further context information\n    @return a message string to use in the parse_error exceptions\n    */\n    std::string exception_message(const input_format_t format,\n                                  const std::string& detail,\n                                  const std::string& context) const\n    {\n        std::string error_msg = \"syntax error while parsing \";\n\n        switch (format)\n        {\n            case input_format_t::cbor:\n                error_msg += \"CBOR\";\n                break;\n\n            case input_format_t::msgpack:\n                error_msg += \"MessagePack\";\n                break;\n\n            case input_format_t::ubjson:\n                error_msg += \"UBJSON\";\n                break;\n\n            case input_format_t::bson:\n                error_msg += \"BSON\";\n                break;\n\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false);  // LCOV_EXCL_LINE\n        }\n\n        return error_msg + \" \" + context + \": \" + detail;\n    }\n\n  private:\n    /// input adapter\n    InputAdapterType ia;\n\n    /// the current character\n    char_int_type current = std::char_traits<char_type>::eof();\n\n    /// the number of characters read\n    std::size_t chars_read = 0;\n\n    /// whether we can assume little endianess\n    const bool is_little_endian = little_endianess();\n\n    /// the SAX parser\n    json_sax_t* sax = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/input/parser.hpp>\n\n\n#include <cmath> // isfinite\n#include <cstdint> // uint8_t\n#include <functional> // function\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////\n// parser //\n////////////\n\nenum class parse_event_t : uint8_t\n{\n    /// the parser read `{` and started to process a JSON object\n    object_start,\n    /// the parser read `}` and finished processing a JSON object\n    object_end,\n    /// the parser read `[` and started to process a JSON array\n    array_start,\n    /// the parser read `]` and finished processing a JSON array\n    array_end,\n    /// the parser read a key of a value in an object\n    key,\n    /// the parser finished reading a JSON value\n    value\n};\n\ntemplate<typename BasicJsonType>\nusing parser_callback_t =\n    std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;\n\n/*!\n@brief syntax analysis\n\nThis class implements a recursive descent parser.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass parser\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using lexer_t = lexer<BasicJsonType, InputAdapterType>;\n    using token_type = typename lexer_t::token_type;\n\n  public:\n    /// a parser reading from an input adapter\n    explicit parser(InputAdapterType&& adapter,\n                    const parser_callback_t<BasicJsonType> cb = nullptr,\n                    const bool allow_exceptions_ = true,\n                    const bool skip_comments = false)\n        : callback(cb)\n        , m_lexer(std::move(adapter), skip_comments)\n        , allow_exceptions(allow_exceptions_)\n    {\n        // read first token\n        get_token();\n    }\n\n    /*!\n    @brief public parser interface\n\n    @param[in] strict      whether to expect the last token to be EOF\n    @param[in,out] result  parsed JSON value\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    void parse(const bool strict, BasicJsonType& result)\n    {\n        if (callback)\n        {\n            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);\n            sax_parse_internal(&sdp);\n            result.assert_invariant();\n\n            // in strict mode, input must be completely read\n            if (strict && (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\")));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n\n            // set top-level value to null if it was discarded by the callback\n            // function\n            if (result.is_discarded())\n            {\n                result = nullptr;\n            }\n        }\n        else\n        {\n            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);\n            sax_parse_internal(&sdp);\n            result.assert_invariant();\n\n            // in strict mode, input must be completely read\n            if (strict && (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\")));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n        }\n    }\n\n    /*!\n    @brief public accept interface\n\n    @param[in] strict  whether to expect the last token to be EOF\n    @return whether the input is a proper JSON text\n    */\n    bool accept(const bool strict = true)\n    {\n        json_sax_acceptor<BasicJsonType> sax_acceptor;\n        return sax_parse(&sax_acceptor, strict);\n    }\n\n    template<typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse(SAX* sax, const bool strict = true)\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n        const bool result = sax_parse_internal(sax);\n\n        // strict mode: next byte must be EOF\n        if (result && strict && (get_token() != token_type::end_of_input))\n        {\n            return sax->parse_error(m_lexer.get_position(),\n                                    m_lexer.get_token_string(),\n                                    parse_error::create(101, m_lexer.get_position(),\n                                            exception_message(token_type::end_of_input, \"value\")));\n        }\n\n        return result;\n    }\n\n  private:\n    template<typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse_internal(SAX* sax)\n    {\n        // stack to remember the hierarchy of structured values we are parsing\n        // true = array; false = object\n        std::vector<bool> states;\n        // value to avoid a goto (see comment where set to true)\n        bool skip_to_state_evaluation = false;\n\n        while (true)\n        {\n            if (!skip_to_state_evaluation)\n            {\n                // invariant: get_token() was called before each iteration\n                switch (last_token)\n                {\n                    case token_type::begin_object:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing } -> we are done\n                        if (get_token() == token_type::end_object)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // parse key\n                        if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(),\n                                                            exception_message(token_type::value_string, \"object key\")));\n                        }\n                        if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        // parse separator (:)\n                        if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(),\n                                                            exception_message(token_type::name_separator, \"object separator\")));\n                        }\n\n                        // remember we are now inside an object\n                        states.push_back(false);\n\n                        // parse values\n                        get_token();\n                        continue;\n                    }\n\n                    case token_type::begin_array:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing ] -> we are done\n                        if (get_token() == token_type::end_array)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // remember we are now inside an array\n                        states.push_back(true);\n\n                        // parse values (no need to call get_token)\n                        continue;\n                    }\n\n                    case token_type::value_float:\n                    {\n                        const auto res = m_lexer.get_number_float();\n\n                        if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    out_of_range::create(406, \"number overflow parsing '\" + m_lexer.get_token_string() + \"'\"));\n                        }\n\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        break;\n                    }\n\n                    case token_type::literal_false:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_null:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->null()))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_true:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_integer:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_string:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_unsigned:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::parse_error:\n                    {\n                        // using \"uninitialized\" to avoid \"expected\" message\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::uninitialized, \"value\")));\n                    }\n\n                    default: // the last token was unexpected\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::literal_or_value, \"value\")));\n                    }\n                }\n            }\n            else\n            {\n                skip_to_state_evaluation = false;\n            }\n\n            // we reached this line after we successfully parsed a value\n            if (states.empty())\n            {\n                // empty stack: we reached the end of the hierarchy: done\n                return true;\n            }\n\n            if (states.back())  // array\n            {\n                // comma -> next value\n                if (get_token() == token_type::value_separator)\n                {\n                    // parse a new value\n                    get_token();\n                    continue;\n                }\n\n                // closing ]\n                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))\n                    {\n                        return false;\n                    }\n\n                    // We are done with this array. Before we can parse a\n                    // new value, we need to evaluate the new state first.\n                    // By setting skip_to_state_evaluation to false, we\n                    // are effectively jumping to the beginning of this if.\n                    JSON_ASSERT(!states.empty());\n                    states.pop_back();\n                    skip_to_state_evaluation = true;\n                    continue;\n                }\n\n                return sax->parse_error(m_lexer.get_position(),\n                                        m_lexer.get_token_string(),\n                                        parse_error::create(101, m_lexer.get_position(),\n                                                exception_message(token_type::end_array, \"array\")));\n            }\n            else  // object\n            {\n                // comma -> next value\n                if (get_token() == token_type::value_separator)\n                {\n                    // parse key\n                    if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::value_string, \"object key\")));\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))\n                    {\n                        return false;\n                    }\n\n                    // parse separator (:)\n                    if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::name_separator, \"object separator\")));\n                    }\n\n                    // parse values\n                    get_token();\n                    continue;\n                }\n\n                // closing }\n                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))\n                    {\n                        return false;\n                    }\n\n                    // We are done with this object. Before we can parse a\n                    // new value, we need to evaluate the new state first.\n                    // By setting skip_to_state_evaluation to false, we\n                    // are effectively jumping to the beginning of this if.\n                    JSON_ASSERT(!states.empty());\n                    states.pop_back();\n                    skip_to_state_evaluation = true;\n                    continue;\n                }\n\n                return sax->parse_error(m_lexer.get_position(),\n                                        m_lexer.get_token_string(),\n                                        parse_error::create(101, m_lexer.get_position(),\n                                                exception_message(token_type::end_object, \"object\")));\n            }\n        }\n    }\n\n    /// get next token from lexer\n    token_type get_token()\n    {\n        return last_token = m_lexer.scan();\n    }\n\n    std::string exception_message(const token_type expected, const std::string& context)\n    {\n        std::string error_msg = \"syntax error \";\n\n        if (!context.empty())\n        {\n            error_msg += \"while parsing \" + context + \" \";\n        }\n\n        error_msg += \"- \";\n\n        if (last_token == token_type::parse_error)\n        {\n            error_msg += std::string(m_lexer.get_error_message()) + \"; last read: '\" +\n                         m_lexer.get_token_string() + \"'\";\n        }\n        else\n        {\n            error_msg += \"unexpected \" + std::string(lexer_t::token_type_name(last_token));\n        }\n\n        if (expected != token_type::uninitialized)\n        {\n            error_msg += \"; expected \" + std::string(lexer_t::token_type_name(expected));\n        }\n\n        return error_msg;\n    }\n\n  private:\n    /// callback function\n    const parser_callback_t<BasicJsonType> callback = nullptr;\n    /// the type of the last read token\n    token_type last_token = token_type::uninitialized;\n    /// the lexer\n    lexer_t m_lexer;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n\n#include <cstddef> // ptrdiff_t\n#include <limits>  // numeric_limits\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*\n@brief an iterator for primitive JSON types\n\nThis class models an iterator for primitive JSON types (boolean, number,\nstring). It's only purpose is to allow the iterator/const_iterator classes\nto \"iterate\" over primitive values. Internally, the iterator is modeled by\na `difference_type` variable. Value begin_value (`0`) models the begin,\nend_value (`1`) models past the end.\n*/\nclass primitive_iterator_t\n{\n  private:\n    using difference_type = std::ptrdiff_t;\n    static constexpr difference_type begin_value = 0;\n    static constexpr difference_type end_value = begin_value + 1;\n\n    /// iterator as signed integer type\n    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();\n\n  public:\n    constexpr difference_type get_value() const noexcept\n    {\n        return m_it;\n    }\n\n    /// set iterator to a defined beginning\n    void set_begin() noexcept\n    {\n        m_it = begin_value;\n    }\n\n    /// set iterator to a defined past the end\n    void set_end() noexcept\n    {\n        m_it = end_value;\n    }\n\n    /// return whether the iterator can be dereferenced\n    constexpr bool is_begin() const noexcept\n    {\n        return m_it == begin_value;\n    }\n\n    /// return whether the iterator is at end\n    constexpr bool is_end() const noexcept\n    {\n        return m_it == end_value;\n    }\n\n    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it == rhs.m_it;\n    }\n\n    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it < rhs.m_it;\n    }\n\n    primitive_iterator_t operator+(difference_type n) noexcept\n    {\n        auto result = *this;\n        result += n;\n        return result;\n    }\n\n    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it - rhs.m_it;\n    }\n\n    primitive_iterator_t& operator++() noexcept\n    {\n        ++m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator++(int) noexcept\n    {\n        auto result = *this;\n        ++m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator--() noexcept\n    {\n        --m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator--(int) noexcept\n    {\n        auto result = *this;\n        --m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator+=(difference_type n) noexcept\n    {\n        m_it += n;\n        return *this;\n    }\n\n    primitive_iterator_t& operator-=(difference_type n) noexcept\n    {\n        m_it -= n;\n        return *this;\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*!\n@brief an iterator value\n\n@note This structure could easily be a union, but MSVC currently does not allow\nunions members with complex constructors, see https://github.com/nlohmann/json/pull/105.\n*/\ntemplate<typename BasicJsonType> struct internal_iterator\n{\n    /// iterator for JSON objects\n    typename BasicJsonType::object_t::iterator object_iterator {};\n    /// iterator for JSON arrays\n    typename BasicJsonType::array_t::iterator array_iterator {};\n    /// generic iterator for all other types\n    primitive_iterator_t primitive_iterator {};\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/iter_impl.hpp>\n\n\n#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next\n#include <type_traits> // conditional, is_const, remove_const\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// forward declare, to be able to friend it later on\ntemplate<typename IteratorType> class iteration_proxy;\ntemplate<typename IteratorType> class iteration_proxy_value;\n\n/*!\n@brief a template for a bidirectional iterator for the @ref basic_json class\nThis class implements a both iterators (iterator and const_iterator) for the\n@ref basic_json class.\n@note An iterator is called *initialized* when a pointer to a JSON value has\n      been set (e.g., by a constructor or a copy assignment). If the iterator is\n      default-constructed, it is *uninitialized* and most methods are undefined.\n      **The library uses assertions to detect calls on uninitialized iterators.**\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n@since version 1.0.0, simplified in version 2.0.9, change to bidirectional\n       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)\n*/\ntemplate<typename BasicJsonType>\nclass iter_impl\n{\n    /// allow basic_json to access private members\n    friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;\n    friend BasicJsonType;\n    friend iteration_proxy<iter_impl>;\n    friend iteration_proxy_value<iter_impl>;\n\n    using object_t = typename BasicJsonType::object_t;\n    using array_t = typename BasicJsonType::array_t;\n    // make sure BasicJsonType is basic_json or const basic_json\n    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,\n                  \"iter_impl only accepts (const) basic_json\");\n\n  public:\n\n    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.\n    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.\n    /// A user-defined iterator should provide publicly accessible typedefs named\n    /// iterator_category, value_type, difference_type, pointer, and reference.\n    /// Note that value_type is required to be non-const, even for constant iterators.\n    using iterator_category = std::bidirectional_iterator_tag;\n\n    /// the type of the values when the iterator is dereferenced\n    using value_type = typename BasicJsonType::value_type;\n    /// a type to represent differences between iterators\n    using difference_type = typename BasicJsonType::difference_type;\n    /// defines a pointer to the type iterated over (value_type)\n    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,\n          typename BasicJsonType::const_pointer,\n          typename BasicJsonType::pointer>::type;\n    /// defines a reference to the type iterated over (value_type)\n    using reference =\n        typename std::conditional<std::is_const<BasicJsonType>::value,\n        typename BasicJsonType::const_reference,\n        typename BasicJsonType::reference>::type;\n\n    /// default constructor\n    iter_impl() = default;\n\n    /*!\n    @brief constructor for a given JSON instance\n    @param[in] object  pointer to a JSON object for this iterator\n    @pre object != nullptr\n    @post The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    explicit iter_impl(pointer object) noexcept : m_object(object)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = typename object_t::iterator();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = typename array_t::iterator();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator = primitive_iterator_t();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @note The conventional copy constructor and copy assignment are implicitly\n          defined. Combined with the following converting constructor and\n          assignment, they support: (1) copy from iterator to iterator, (2)\n          copy from const iterator to const iterator, and (3) conversion from\n          iterator to const iterator. However conversion from const iterator\n          to iterator is not defined.\n    */\n\n    /*!\n    @brief const copy constructor\n    @param[in] other const iterator to copy from\n    @note This copy constructor had to be defined explicitly to circumvent a bug\n          occurring on msvc v19.0 compiler (VS 2015) debug build. For more\n          information refer to: https://github.com/nlohmann/json/issues/1608\n    */\n    iter_impl(const iter_impl<const BasicJsonType>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n    /*!\n    @brief converting constructor\n    @param[in] other  non-const iterator to copy from\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other  non-const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n  private:\n    /*!\n    @brief set the iterator to the first value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_begin() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->begin();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->begin();\n                break;\n            }\n\n            case value_t::null:\n            {\n                // set to end so begin()==end() is true: null is empty\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator.set_begin();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @brief set the iterator past the last value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_end() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->end();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->end();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n        }\n    }\n\n  public:\n    /*!\n    @brief return a reference to the value pointed to by the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator*() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());\n                return m_it.object_iterator->second;\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());\n                return *m_it.array_iterator;\n            }\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief dereference the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    pointer operator->() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());\n                return &(m_it.object_iterator->second);\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());\n                return &*m_it.array_iterator;\n            }\n\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief post-increment (it++)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator++(int)\n    {\n        auto result = *this;\n        ++(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-increment (++it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator++()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, 1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, 1);\n                break;\n            }\n\n            default:\n            {\n                ++m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief post-decrement (it--)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator--(int)\n    {\n        auto result = *this;\n        --(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-decrement (--it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator--()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, -1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, -1);\n                break;\n            }\n\n            default:\n            {\n                --m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief  comparison: equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator==(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\"));\n        }\n\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                return (m_it.object_iterator == other.m_it.object_iterator);\n\n            case value_t::array:\n                return (m_it.array_iterator == other.m_it.array_iterator);\n\n            default:\n                return (m_it.primitive_iterator == other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief  comparison: not equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator!=(const iter_impl& other) const\n    {\n        return !operator==(other);\n    }\n\n    /*!\n    @brief  comparison: smaller\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\"));\n        }\n\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(213, \"cannot compare order of object iterators\"));\n\n            case value_t::array:\n                return (m_it.array_iterator < other.m_it.array_iterator);\n\n            default:\n                return (m_it.primitive_iterator < other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief  comparison: less than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<=(const iter_impl& other) const\n    {\n        return !other.operator < (*this);\n    }\n\n    /*!\n    @brief  comparison: greater than\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>(const iter_impl& other) const\n    {\n        return !operator<=(other);\n    }\n\n    /*!\n    @brief  comparison: greater than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>=(const iter_impl& other) const\n    {\n        return !operator<(other);\n    }\n\n    /*!\n    @brief  add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator+=(difference_type i)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\"));\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, i);\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator += i;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief  subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator-=(difference_type i)\n    {\n        return operator+=(-i);\n    }\n\n    /*!\n    @brief  add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator+(difference_type i) const\n    {\n        auto result = *this;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief  addition of distance and iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    friend iter_impl operator+(difference_type i, const iter_impl& it)\n    {\n        auto result = it;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief  subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator-(difference_type i) const\n    {\n        auto result = *this;\n        result -= i;\n        return result;\n    }\n\n    /*!\n    @brief  return difference\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    difference_type operator-(const iter_impl& other) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\"));\n\n            case value_t::array:\n                return m_it.array_iterator - other.m_it.array_iterator;\n\n            default:\n                return m_it.primitive_iterator - other.m_it.primitive_iterator;\n        }\n    }\n\n    /*!\n    @brief  access to successor\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator[](difference_type n) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(208, \"cannot use operator[] for object iterators\"));\n\n            case value_t::array:\n                return *std::next(m_it.array_iterator, n);\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief  return the key of an object iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    const typename object_t::key_type& key() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        if (JSON_HEDLEY_LIKELY(m_object->is_object()))\n        {\n            return m_it.object_iterator->first;\n        }\n\n        JSON_THROW(invalid_iterator::create(207, \"cannot use key() for non-object iterators\"));\n    }\n\n    /*!\n    @brief  return the value of an iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference value() const\n    {\n        return operator*();\n    }\n\n  private:\n    /// associated JSON instance\n    pointer m_object = nullptr;\n    /// the actual iterator of the associated instance\n    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};\n};\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>\n\n\n#include <cstddef> // ptrdiff_t\n#include <iterator> // reverse_iterator\n#include <utility> // declval\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////////\n// reverse_iterator //\n//////////////////////\n\n/*!\n@brief a template for a reverse iterator class\n\n@tparam Base the base iterator type to reverse. Valid types are @ref\niterator (to create @ref reverse_iterator) and @ref const_iterator (to\ncreate @ref const_reverse_iterator).\n\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):\n  It is possible to write to the pointed-to element (only if @a Base is\n  @ref iterator).\n\n@since version 1.0.0\n*/\ntemplate<typename Base>\nclass json_reverse_iterator : public std::reverse_iterator<Base>\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    /// shortcut to the reverse iterator adapter\n    using base_iterator = std::reverse_iterator<Base>;\n    /// the reference type for the pointed-to element\n    using reference = typename Base::reference;\n\n    /// create reverse iterator from iterator\n    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept\n        : base_iterator(it) {}\n\n    /// create reverse iterator from base class\n    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}\n\n    /// post-increment (it++)\n    json_reverse_iterator const operator++(int)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));\n    }\n\n    /// pre-increment (++it)\n    json_reverse_iterator& operator++()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator++());\n    }\n\n    /// post-decrement (it--)\n    json_reverse_iterator const operator--(int)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));\n    }\n\n    /// pre-decrement (--it)\n    json_reverse_iterator& operator--()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator--());\n    }\n\n    /// add to iterator\n    json_reverse_iterator& operator+=(difference_type i)\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));\n    }\n\n    /// add to iterator\n    json_reverse_iterator operator+(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));\n    }\n\n    /// subtract from iterator\n    json_reverse_iterator operator-(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));\n    }\n\n    /// return difference\n    difference_type operator-(const json_reverse_iterator& other) const\n    {\n        return base_iterator(*this) - base_iterator(other);\n    }\n\n    /// access to successor\n    reference operator[](difference_type n) const\n    {\n        return *(this->operator+(n));\n    }\n\n    /// return the key of an object iterator\n    auto key() const -> decltype(std::declval<Base>().key())\n    {\n        auto it = --this->base();\n        return it.key();\n    }\n\n    /// return the value of an iterator\n    reference value() const\n    {\n        auto it = --this->base();\n        return it.operator * ();\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/json_pointer.hpp>\n\n\n#include <algorithm> // all_of\n#include <cctype> // isdigit\n#include <limits> // max\n#include <numeric> // accumulate\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\ntemplate<typename BasicJsonType>\nclass json_pointer\n{\n    // allow basic_json to access private members\n    NLOHMANN_BASIC_JSON_TPL_DECLARATION\n    friend class basic_json;\n\n  public:\n    /*!\n    @brief create JSON pointer\n\n    Create a JSON pointer according to the syntax described in\n    [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).\n\n    @param[in] s  string representing the JSON pointer; if omitted, the empty\n                  string is assumed which references the whole JSON value\n\n    @throw parse_error.107 if the given JSON pointer @a s is nonempty and does\n                           not begin with a slash (`/`); see example below\n\n    @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is\n    not followed by `0` (representing `~`) or `1` (representing `/`); see\n    example below\n\n    @liveexample{The example shows the construction several valid JSON pointers\n    as well as the exceptional behavior.,json_pointer}\n\n    @since version 2.0.0\n    */\n    explicit json_pointer(const std::string& s = \"\")\n        : reference_tokens(split(s))\n    {}\n\n    /*!\n    @brief return a string representation of the JSON pointer\n\n    @invariant For each JSON pointer `ptr`, it holds:\n    @code {.cpp}\n    ptr == json_pointer(ptr.to_string());\n    @endcode\n\n    @return a string representation of the JSON pointer\n\n    @liveexample{The example shows the result of `to_string`.,json_pointer__to_string}\n\n    @since version 2.0.0\n    */\n    std::string to_string() const\n    {\n        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),\n                               std::string{},\n                               [](const std::string & a, const std::string & b)\n        {\n            return a + \"/\" + escape(b);\n        });\n    }\n\n    /// @copydoc to_string()\n    operator std::string() const\n    {\n        return to_string();\n    }\n\n    /*!\n    @brief append another JSON pointer at the end of this JSON pointer\n\n    @param[in] ptr  JSON pointer to append\n    @return JSON pointer with @a ptr appended\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa @ref operator/=(std::string) to append a reference token\n    @sa @ref operator/=(std::size_t) to append an array index\n    @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(const json_pointer& ptr)\n    {\n        reference_tokens.insert(reference_tokens.end(),\n                                ptr.reference_tokens.begin(),\n                                ptr.reference_tokens.end());\n        return *this;\n    }\n\n    /*!\n    @brief append an unescaped reference token at the end of this JSON pointer\n\n    @param[in] token  reference token to append\n    @return JSON pointer with @a token appended without escaping @a token\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Amortized constant.\n\n    @sa @ref operator/=(const json_pointer&) to append a JSON pointer\n    @sa @ref operator/=(std::size_t) to append an array index\n    @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(std::string token)\n    {\n        push_back(std::move(token));\n        return *this;\n    }\n\n    /*!\n    @brief append an array index at the end of this JSON pointer\n\n    @param[in] array_idx  array index to append\n    @return JSON pointer with @a array_idx appended\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Amortized constant.\n\n    @sa @ref operator/=(const json_pointer&) to append a JSON pointer\n    @sa @ref operator/=(std::string) to append a reference token\n    @sa @ref operator/(const json_pointer&, std::string) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(std::size_t array_idx)\n    {\n        return *this /= std::to_string(array_idx);\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer\n\n    @param[in] lhs  JSON pointer\n    @param[in] rhs  JSON pointer\n    @return a new JSON pointer with @a rhs appended to @a lhs\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a lhs and @a rhs.\n\n    @sa @ref operator/=(const json_pointer&) to append a JSON pointer\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& lhs,\n                                  const json_pointer& rhs)\n    {\n        return json_pointer(lhs) /= rhs;\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer\n\n    @param[in] ptr  JSON pointer\n    @param[in] token  reference token\n    @return a new JSON pointer with unescaped @a token appended to @a ptr\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa @ref operator/=(std::string) to append a reference token\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& ptr, std::string token)\n    {\n        return json_pointer(ptr) /= std::move(token);\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer\n\n    @param[in] ptr  JSON pointer\n    @param[in] array_idx  array index\n    @return a new JSON pointer with @a array_idx appended to @a ptr\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa @ref operator/=(std::size_t) to append an array index\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx)\n    {\n        return json_pointer(ptr) /= array_idx;\n    }\n\n    /*!\n    @brief returns the parent of this JSON pointer\n\n    @return parent of this JSON pointer; in case this JSON pointer is the root,\n            the root itself is returned\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @liveexample{The example shows the result of `parent_pointer` for different\n    JSON Pointers.,json_pointer__parent_pointer}\n\n    @since version 3.6.0\n    */\n    json_pointer parent_pointer() const\n    {\n        if (empty())\n        {\n            return *this;\n        }\n\n        json_pointer res = *this;\n        res.pop_back();\n        return res;\n    }\n\n    /*!\n    @brief remove last reference token\n\n    @pre not `empty()`\n\n    @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back}\n\n    @complexity Constant.\n\n    @throw out_of_range.405 if JSON pointer has no parent\n\n    @since version 3.6.0\n    */\n    void pop_back()\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        reference_tokens.pop_back();\n    }\n\n    /*!\n    @brief return last reference token\n\n    @pre not `empty()`\n    @return last reference token\n\n    @liveexample{The example shows the usage of `back`.,json_pointer__back}\n\n    @complexity Constant.\n\n    @throw out_of_range.405 if JSON pointer has no parent\n\n    @since version 3.6.0\n    */\n    const std::string& back() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        return reference_tokens.back();\n    }\n\n    /*!\n    @brief append an unescaped token at the end of the reference pointer\n\n    @param[in] token  token to add\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows the result of `push_back` for different\n    JSON Pointers.,json_pointer__push_back}\n\n    @since version 3.6.0\n    */\n    void push_back(const std::string& token)\n    {\n        reference_tokens.push_back(token);\n    }\n\n    /// @copydoc push_back(const std::string&)\n    void push_back(std::string&& token)\n    {\n        reference_tokens.push_back(std::move(token));\n    }\n\n    /*!\n    @brief return whether pointer points to the root document\n\n    @return true iff the JSON pointer points to the root document\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example shows the result of `empty` for different JSON\n    Pointers.,json_pointer__empty}\n\n    @since version 3.6.0\n    */\n    bool empty() const noexcept\n    {\n        return reference_tokens.empty();\n    }\n\n  private:\n    /*!\n    @param[in] s  reference token to be converted into an array index\n\n    @return integer representation of @a s\n\n    @throw parse_error.106  if an array index begins with '0'\n    @throw parse_error.109  if an array index begins not with a digit\n    @throw out_of_range.404 if string @a s could not be converted to an integer\n    @throw out_of_range.410 if an array index exceeds size_type\n    */\n    static typename BasicJsonType::size_type array_index(const std::string& s)\n    {\n        using size_type = typename BasicJsonType::size_type;\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))\n        {\n            JSON_THROW(detail::parse_error::create(106, 0,\n                                                   \"array index '\" + s +\n                                                   \"' must not begin with '0'\"));\n        }\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))\n        {\n            JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + s + \"' is not a number\"));\n        }\n\n        std::size_t processed_chars = 0;\n        unsigned long long res = 0;\n        JSON_TRY\n        {\n            res = std::stoull(s, &processed_chars);\n        }\n        JSON_CATCH(std::out_of_range&)\n        {\n            JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + s + \"'\"));\n        }\n\n        // check if the string was completely read\n        if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))\n        {\n            JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + s + \"'\"));\n        }\n\n        // only triggered on special platforms (like 32bit), see also\n        // https://github.com/nlohmann/json/pull/2203\n        if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))\n        {\n            JSON_THROW(detail::out_of_range::create(410, \"array index \" + s + \" exceeds size_type\")); // LCOV_EXCL_LINE\n        }\n\n        return static_cast<size_type>(res);\n    }\n\n    json_pointer top() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        json_pointer result = *this;\n        result.reference_tokens = {reference_tokens[0]};\n        return result;\n    }\n\n    /*!\n    @brief create and return a reference to the pointed to value\n\n    @complexity Linear in the number of reference tokens.\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.313 if value cannot be unflattened\n    */\n    BasicJsonType& get_and_create(BasicJsonType& j) const\n    {\n        auto result = &j;\n\n        // in case no reference tokens exist, return a reference to the JSON value\n        // j which will be overwritten by a primitive value\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (result->type())\n            {\n                case detail::value_t::null:\n                {\n                    if (reference_token == \"0\")\n                    {\n                        // start a new array if reference token is 0\n                        result = &result->operator[](0);\n                    }\n                    else\n                    {\n                        // start a new object otherwise\n                        result = &result->operator[](reference_token);\n                    }\n                    break;\n                }\n\n                case detail::value_t::object:\n                {\n                    // create an entry in the object\n                    result = &result->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    // create an entry in the array\n                    result = &result->operator[](array_index(reference_token));\n                    break;\n                }\n\n                /*\n                The following code is only reached if there exists a reference\n                token _and_ the current value is primitive. In this case, we have\n                an error situation, because primitive values may only occur as\n                single value; that is, with an empty list of reference tokens.\n                */\n                default:\n                    JSON_THROW(detail::type_error::create(313, \"invalid value to unflatten\"));\n            }\n        }\n\n        return *result;\n    }\n\n    /*!\n    @brief return a reference to the pointed to value\n\n    @note This version does not throw if a value is not present, but tries to\n          create nested values instead. For instance, calling this function\n          with pointer `\"/this/that\"` on a null value is equivalent to calling\n          `operator[](\"this\").operator[](\"that\")` on that value, effectively\n          changing the null value to an object.\n\n    @param[in] ptr  a JSON value\n\n    @return reference to the JSON value pointed to by the JSON pointer\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_unchecked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            // convert null values to arrays or objects before continuing\n            if (ptr->is_null())\n            {\n                // check if reference token is a number\n                const bool nums =\n                    std::all_of(reference_token.begin(), reference_token.end(),\n                                [](const unsigned char x)\n                {\n                    return std::isdigit(x);\n                });\n\n                // change value to array for numbers or \"-\" or to object otherwise\n                *ptr = (nums || reference_token == \"-\")\n                       ? detail::value_t::array\n                       : detail::value_t::object;\n            }\n\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (reference_token == \"-\")\n                    {\n                        // explicitly treat \"-\" as index beyond the end\n                        ptr = &ptr->operator[](ptr->m_value.array->size());\n                    }\n                    else\n                    {\n                        // convert array index to number; unchecked access\n                        ptr = &ptr->operator[](array_index(reference_token));\n                    }\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_checked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index(reference_token));\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @brief return a const reference to the pointed to value\n\n    @param[in] ptr  a JSON value\n\n    @return const reference to the JSON value pointed to by the JSON\n    pointer\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" cannot be used for const access\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // use unchecked array access\n                    ptr = &ptr->operator[](array_index(reference_token));\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_checked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index(reference_token));\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    */\n    bool contains(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    if (!ptr->contains(reference_token))\n                    {\n                        // we did not find the key in the object\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !(\"0\" <= reference_token && reference_token <= \"9\")))\n                    {\n                        // invalid char\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))\n                        {\n                            // first char should be between '1' and '9'\n                            return false;\n                        }\n                        for (std::size_t i = 1; i < reference_token.size(); i++)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))\n                            {\n                                // other char should be between '0' and '9'\n                                return false;\n                            }\n                        }\n                    }\n\n                    const auto idx = array_index(reference_token);\n                    if (idx >= ptr->size())\n                    {\n                        // index out of range\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](idx);\n                    break;\n                }\n\n                default:\n                {\n                    // we do not expect primitive values if there is still a\n                    // reference token to process\n                    return false;\n                }\n            }\n        }\n\n        // no reference token left means we found a primitive value\n        return true;\n    }\n\n    /*!\n    @brief split the string input to reference tokens\n\n    @note This function is only called by the json_pointer constructor.\n          All exceptions below are documented there.\n\n    @throw parse_error.107  if the pointer is not empty or begins with '/'\n    @throw parse_error.108  if character '~' is not followed by '0' or '1'\n    */\n    static std::vector<std::string> split(const std::string& reference_string)\n    {\n        std::vector<std::string> result;\n\n        // special case: empty reference string -> no reference tokens\n        if (reference_string.empty())\n        {\n            return result;\n        }\n\n        // check if nonempty reference string begins with slash\n        if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))\n        {\n            JSON_THROW(detail::parse_error::create(107, 1,\n                                                   \"JSON pointer must be empty or begin with '/' - was: '\" +\n                                                   reference_string + \"'\"));\n        }\n\n        // extract the reference tokens:\n        // - slash: position of the last read slash (or end of string)\n        // - start: position after the previous slash\n        for (\n            // search for the first slash after the first character\n            std::size_t slash = reference_string.find_first_of('/', 1),\n            // set the beginning of the first reference token\n            start = 1;\n            // we can stop if start == 0 (if slash == std::string::npos)\n            start != 0;\n            // set the beginning of the next reference token\n            // (will eventually be 0 if slash == std::string::npos)\n            start = (slash == std::string::npos) ? 0 : slash + 1,\n            // find next slash\n            slash = reference_string.find_first_of('/', start))\n        {\n            // use the text between the beginning of the reference token\n            // (start) and the last slash (slash).\n            auto reference_token = reference_string.substr(start, slash - start);\n\n            // check reference tokens are properly escaped\n            for (std::size_t pos = reference_token.find_first_of('~');\n                    pos != std::string::npos;\n                    pos = reference_token.find_first_of('~', pos + 1))\n            {\n                JSON_ASSERT(reference_token[pos] == '~');\n\n                // ~ must be followed by 0 or 1\n                if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||\n                                         (reference_token[pos + 1] != '0' &&\n                                          reference_token[pos + 1] != '1')))\n                {\n                    JSON_THROW(detail::parse_error::create(108, 0, \"escape character '~' must be followed with '0' or '1'\"));\n                }\n            }\n\n            // finally, store the reference token\n            unescape(reference_token);\n            result.push_back(reference_token);\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief replace all occurrences of a substring by another string\n\n    @param[in,out] s  the string to manipulate; changed so that all\n                   occurrences of @a f are replaced with @a t\n    @param[in]     f  the substring to replace with @a t\n    @param[in]     t  the string to replace @a f\n\n    @pre The search string @a f must not be empty. **This precondition is\n    enforced with an assertion.**\n\n    @since version 2.0.0\n    */\n    static void replace_substring(std::string& s, const std::string& f,\n                                  const std::string& t)\n    {\n        JSON_ASSERT(!f.empty());\n        for (auto pos = s.find(f);                // find first occurrence of f\n                pos != std::string::npos;         // make sure f was found\n                s.replace(pos, f.size(), t),      // replace with t, and\n                pos = s.find(f, pos + t.size()))  // find next occurrence of f\n        {}\n    }\n\n    /// escape \"~\" to \"~0\" and \"/\" to \"~1\"\n    static std::string escape(std::string s)\n    {\n        replace_substring(s, \"~\", \"~0\");\n        replace_substring(s, \"/\", \"~1\");\n        return s;\n    }\n\n    /// unescape \"~1\" to tilde and \"~0\" to slash (order is important!)\n    static void unescape(std::string& s)\n    {\n        replace_substring(s, \"~1\", \"/\");\n        replace_substring(s, \"~0\", \"~\");\n    }\n\n    /*!\n    @param[in] reference_string  the reference string to the current value\n    @param[in] value             the value to consider\n    @param[in,out] result        the result object to insert values to\n\n    @note Empty objects or arrays are flattened to `null`.\n    */\n    static void flatten(const std::string& reference_string,\n                        const BasicJsonType& value,\n                        BasicJsonType& result)\n    {\n        switch (value.type())\n        {\n            case detail::value_t::array:\n            {\n                if (value.m_value.array->empty())\n                {\n                    // flatten empty array as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate array and use index as reference string\n                    for (std::size_t i = 0; i < value.m_value.array->size(); ++i)\n                    {\n                        flatten(reference_string + \"/\" + std::to_string(i),\n                                value.m_value.array->operator[](i), result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::object:\n            {\n                if (value.m_value.object->empty())\n                {\n                    // flatten empty object as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate object and use keys as reference string\n                    for (const auto& element : *value.m_value.object)\n                    {\n                        flatten(reference_string + \"/\" + escape(element.first), element.second, result);\n                    }\n                }\n                break;\n            }\n\n            default:\n            {\n                // add primitive value with its reference string\n                result[reference_string] = value;\n                break;\n            }\n        }\n    }\n\n    /*!\n    @param[in] value  flattened JSON\n\n    @return unflattened JSON\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n    @throw type_error.313  if value cannot be unflattened\n    */\n    static BasicJsonType\n    unflatten(const BasicJsonType& value)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!value.is_object()))\n        {\n            JSON_THROW(detail::type_error::create(314, \"only objects can be unflattened\"));\n        }\n\n        BasicJsonType result;\n\n        // iterate the JSON object values\n        for (const auto& element : *value.m_value.object)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))\n            {\n                JSON_THROW(detail::type_error::create(315, \"values in object must be primitive\"));\n            }\n\n            // assign value to reference pointed to by JSON pointer; Note that if\n            // the JSON pointer is \"\" (i.e., points to the whole value), function\n            // get_and_create returns a reference to result itself. An assignment\n            // will then create a primitive value.\n            json_pointer(element.first).get_and_create(result) = element.second;\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief compares two JSON pointers for equality\n\n    @param[in] lhs  JSON pointer to compare\n    @param[in] rhs  JSON pointer to compare\n    @return whether @a lhs is equal to @a rhs\n\n    @complexity Linear in the length of the JSON pointer\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n    */\n    friend bool operator==(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return lhs.reference_tokens == rhs.reference_tokens;\n    }\n\n    /*!\n    @brief compares two JSON pointers for inequality\n\n    @param[in] lhs  JSON pointer to compare\n    @param[in] rhs  JSON pointer to compare\n    @return whether @a lhs is not equal @a rhs\n\n    @complexity Linear in the length of the JSON pointer\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n    */\n    friend bool operator!=(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return !(lhs == rhs);\n    }\n\n    /// the reference tokens\n    std::vector<std::string> reference_tokens;\n};\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/json_ref.hpp>\n\n\n#include <initializer_list>\n#include <utility>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nclass json_ref\n{\n  public:\n    using value_type = BasicJsonType;\n\n    json_ref(value_type&& value)\n        : owned_value(std::move(value))\n        , value_ref(&owned_value)\n        , is_rvalue(true)\n    {}\n\n    json_ref(const value_type& value)\n        : value_ref(const_cast<value_type*>(&value))\n        , is_rvalue(false)\n    {}\n\n    json_ref(std::initializer_list<json_ref> init)\n        : owned_value(init)\n        , value_ref(&owned_value)\n        , is_rvalue(true)\n    {}\n\n    template <\n        class... Args,\n        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >\n    json_ref(Args && ... args)\n        : owned_value(std::forward<Args>(args)...)\n        , value_ref(&owned_value)\n        , is_rvalue(true)\n    {}\n\n    // class should be movable only\n    json_ref(json_ref&&) = default;\n    json_ref(const json_ref&) = delete;\n    json_ref& operator=(const json_ref&) = delete;\n    json_ref& operator=(json_ref&&) = delete;\n    ~json_ref() = default;\n\n    value_type moved_or_copied() const\n    {\n        if (is_rvalue)\n        {\n            return std::move(*value_ref);\n        }\n        return *value_ref;\n    }\n\n    value_type const& operator*() const\n    {\n        return *static_cast<value_type const*>(value_ref);\n    }\n\n    value_type const* operator->() const\n    {\n        return static_cast<value_type const*>(value_ref);\n    }\n\n  private:\n    mutable value_type owned_value = nullptr;\n    value_type* value_ref = nullptr;\n    const bool is_rvalue = true;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n\n#include <algorithm> // reverse\n#include <array> // array\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstring> // memcpy\n#include <limits> // numeric_limits\n#include <string> // string\n#include <cmath> // isnan, isinf\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n\n#include <algorithm> // copy\n#include <cstddef> // size_t\n#include <ios> // streamsize\n#include <iterator> // back_inserter\n#include <memory> // shared_ptr, make_shared\n#include <ostream> // basic_ostream\n#include <string> // basic_string\n#include <vector> // vector\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// abstract output adapter interface\ntemplate<typename CharType> struct output_adapter_protocol\n{\n    virtual void write_character(CharType c) = 0;\n    virtual void write_characters(const CharType* s, std::size_t length) = 0;\n    virtual ~output_adapter_protocol() = default;\n};\n\n/// a type to simplify interfaces\ntemplate<typename CharType>\nusing output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;\n\n/// output adapter for byte vectors\ntemplate<typename CharType>\nclass output_vector_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_vector_adapter(std::vector<CharType>& vec) noexcept\n        : v(vec)\n    {}\n\n    void write_character(CharType c) override\n    {\n        v.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        std::copy(s, s + length, std::back_inserter(v));\n    }\n\n  private:\n    std::vector<CharType>& v;\n};\n\n/// output adapter for output streams\ntemplate<typename CharType>\nclass output_stream_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept\n        : stream(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        stream.put(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        stream.write(s, static_cast<std::streamsize>(length));\n    }\n\n  private:\n    std::basic_ostream<CharType>& stream;\n};\n\n/// output adapter for basic_string\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_string_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_string_adapter(StringType& s) noexcept\n        : str(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        str.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        str.append(s, length);\n    }\n\n  private:\n    StringType& str;\n};\n\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_adapter\n{\n  public:\n    output_adapter(std::vector<CharType>& vec)\n        : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}\n\n    output_adapter(std::basic_ostream<CharType>& s)\n        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}\n\n    output_adapter(StringType& s)\n        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}\n\n    operator output_adapter_t<CharType>()\n    {\n        return oa;\n    }\n\n  private:\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// binary writer //\n///////////////////\n\n/*!\n@brief serialization to CBOR and MessagePack values\n*/\ntemplate<typename BasicJsonType, typename CharType>\nclass binary_writer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n  public:\n    /*!\n    @brief create a binary writer\n\n    @param[in] adapter  output adapter to write to\n    */\n    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)\n    {\n        JSON_ASSERT(oa);\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n            {\n                write_bson_object(*j.m_value.object);\n                break;\n            }\n\n            default:\n            {\n                JSON_THROW(type_error::create(317, \"to serialize to BSON, top-level type must be object, but is \" + std::string(j.type_name())));\n            }\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_cbor(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                oa->write_character(to_char_type(0xF6));\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xF5)\n                                    : to_char_type(0xF4));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // CBOR does not differentiate between positive signed\n                    // integers and unsigned integers. Therefore, we used the\n                    // code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_integer <= 0x17)\n                    {\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x18));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x19));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x1A));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x1B));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    // The conversions below encode the sign in the first\n                    // byte, and the value is converted to a positive number.\n                    const auto positive_number = -1 - j.m_value.number_integer;\n                    if (j.m_value.number_integer >= -24)\n                    {\n                        write_number(static_cast<std::uint8_t>(0x20 + positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x38));\n                        write_number(static_cast<std::uint8_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x39));\n                        write_number(static_cast<std::uint16_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x3A));\n                        write_number(static_cast<std::uint32_t>(positive_number));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x3B));\n                        write_number(static_cast<std::uint64_t>(positive_number));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x18));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x19));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x1A));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));\n                }\n                else\n                {\n                    oa->write_character(to_char_type(0x1B));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                if (std::isnan(j.m_value.number_float))\n                {\n                    // NaN is 0xf97e00 in CBOR\n                    oa->write_character(to_char_type(0xF9));\n                    oa->write_character(to_char_type(0x7E));\n                    oa->write_character(to_char_type(0x00));\n                }\n                else if (std::isinf(j.m_value.number_float))\n                {\n                    // Infinity is 0xf97c00, -Infinity is 0xf9fc00\n                    oa->write_character(to_char_type(0xf9));\n                    oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));\n                    oa->write_character(to_char_type(0x00));\n                }\n                else\n                {\n                    write_compact_float(j.m_value.number_float, detail::input_format_t::cbor);\n                }\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x60 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x78));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x79));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x80 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x98));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x99));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_cbor(el);\n                }\n                break;\n            }\n\n            case value_t::binary:\n            {\n                if (j.m_value.binary->has_subtype())\n                {\n                    write_number(static_cast<std::uint8_t>(0xd8));\n                    write_number(j.m_value.binary->subtype());\n                }\n\n                // step 1: write control byte and the binary array size\n                const auto N = j.m_value.binary->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x40 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x58));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x59));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x5A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x5B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                    N);\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0xA0 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB8));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB9));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBA));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBB));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_cbor(el.first);\n                    write_cbor(el.second);\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_msgpack(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null: // nil\n            {\n                oa->write_character(to_char_type(0xC0));\n                break;\n            }\n\n            case value_t::boolean: // true and false\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xC3)\n                                    : to_char_type(0xC2));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // MessagePack does not differentiate between positive\n                    // signed integers and unsigned integers. Therefore, we used\n                    // the code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_unsigned < 128)\n                    {\n                        // positive fixnum\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        // uint 8\n                        oa->write_character(to_char_type(0xCC));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        // uint 16\n                        oa->write_character(to_char_type(0xCD));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        // uint 32\n                        oa->write_character(to_char_type(0xCE));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                    {\n                        // uint 64\n                        oa->write_character(to_char_type(0xCF));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    if (j.m_value.number_integer >= -32)\n                    {\n                        // negative fixnum\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                    {\n                        // int 8\n                        oa->write_character(to_char_type(0xD0));\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                    {\n                        // int 16\n                        oa->write_character(to_char_type(0xD1));\n                        write_number(static_cast<std::int16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                    {\n                        // int 32\n                        oa->write_character(to_char_type(0xD2));\n                        write_number(static_cast<std::int32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                    {\n                        // int 64\n                        oa->write_character(to_char_type(0xD3));\n                        write_number(static_cast<std::int64_t>(j.m_value.number_integer));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned < 128)\n                {\n                    // positive fixnum\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // uint 8\n                    oa->write_character(to_char_type(0xCC));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // uint 16\n                    oa->write_character(to_char_type(0xCD));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // uint 32\n                    oa->write_character(to_char_type(0xCE));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    // uint 64\n                    oa->write_character(to_char_type(0xCF));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack);\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 31)\n                {\n                    // fixstr\n                    write_number(static_cast<std::uint8_t>(0xA0 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // str 8\n                    oa->write_character(to_char_type(0xD9));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // str 16\n                    oa->write_character(to_char_type(0xDA));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // str 32\n                    oa->write_character(to_char_type(0xDB));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 15)\n                {\n                    // fixarray\n                    write_number(static_cast<std::uint8_t>(0x90 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // array 16\n                    oa->write_character(to_char_type(0xDC));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // array 32\n                    oa->write_character(to_char_type(0xDD));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_msgpack(el);\n                }\n                break;\n            }\n\n            case value_t::binary:\n            {\n                // step 0: determine if the binary type has a set subtype to\n                // determine whether or not to use the ext or fixext types\n                const bool use_ext = j.m_value.binary->has_subtype();\n\n                // step 1: write control byte and the byte string length\n                const auto N = j.m_value.binary->size();\n                if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    std::uint8_t output_type{};\n                    bool fixed = true;\n                    if (use_ext)\n                    {\n                        switch (N)\n                        {\n                            case 1:\n                                output_type = 0xD4; // fixext 1\n                                break;\n                            case 2:\n                                output_type = 0xD5; // fixext 2\n                                break;\n                            case 4:\n                                output_type = 0xD6; // fixext 4\n                                break;\n                            case 8:\n                                output_type = 0xD7; // fixext 8\n                                break;\n                            case 16:\n                                output_type = 0xD8; // fixext 16\n                                break;\n                            default:\n                                output_type = 0xC7; // ext 8\n                                fixed = false;\n                                break;\n                        }\n\n                    }\n                    else\n                    {\n                        output_type = 0xC4; // bin 8\n                        fixed = false;\n                    }\n\n                    oa->write_character(to_char_type(output_type));\n                    if (!fixed)\n                    {\n                        write_number(static_cast<std::uint8_t>(N));\n                    }\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    std::uint8_t output_type = use_ext\n                                               ? 0xC8 // ext 16\n                                               : 0xC5; // bin 16\n\n                    oa->write_character(to_char_type(output_type));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    std::uint8_t output_type = use_ext\n                                               ? 0xC9 // ext 32\n                                               : 0xC6; // bin 32\n\n                    oa->write_character(to_char_type(output_type));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 1.5: if this is an ext type, write the subtype\n                if (use_ext)\n                {\n                    write_number(static_cast<std::int8_t>(j.m_value.binary->subtype()));\n                }\n\n                // step 2: write the byte string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                    N);\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 15)\n                {\n                    // fixmap\n                    write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // map 16\n                    oa->write_character(to_char_type(0xDE));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // map 32\n                    oa->write_character(to_char_type(0xDF));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_msgpack(el.first);\n                    write_msgpack(el.second);\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @param[in] use_count   whether to use '#' prefixes (optimized format)\n    @param[in] use_type    whether to use '$' prefixes (optimized format)\n    @param[in] add_prefix  whether prefixes need to be used for this value\n    */\n    void write_ubjson(const BasicJsonType& j, const bool use_count,\n                      const bool use_type, const bool add_prefix = true)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('Z'));\n                }\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(j.m_value.boolean\n                                        ? to_char_type('T')\n                                        : to_char_type('F'));\n                }\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);\n                break;\n            }\n\n            case value_t::string:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('S'));\n                }\n                write_number_with_ubjson_prefix(j.m_value.string->size(), true);\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                bool prefix_required = true;\n                if (use_type && !j.m_value.array->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.array->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_ubjson(el, use_count, use_type, prefix_required);\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::binary:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                if (use_type && !j.m_value.binary->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    oa->write_character(to_char_type('$'));\n                    oa->write_character('U');\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.binary->size(), true);\n                }\n\n                if (use_type)\n                {\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                        j.m_value.binary->size());\n                }\n                else\n                {\n                    for (size_t i = 0; i < j.m_value.binary->size(); ++i)\n                    {\n                        oa->write_character(to_char_type('U'));\n                        oa->write_character(j.m_value.binary->data()[i]);\n                    }\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('{'));\n                }\n\n                bool prefix_required = true;\n                if (use_type && !j.m_value.object->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin(), j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.object->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_number_with_ubjson_prefix(el.first.size(), true);\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(el.first.c_str()),\n                        el.first.size());\n                    write_ubjson(el.second, use_count, use_type, prefix_required);\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type('}'));\n                }\n\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @return The size of a BSON document entry header, including the id marker\n            and the entry name size (and its null-terminator).\n    */\n    static std::size_t calc_bson_entry_header_size(const string_t& name)\n    {\n        const auto it = name.find(static_cast<typename string_t::value_type>(0));\n        if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))\n        {\n            JSON_THROW(out_of_range::create(409,\n                                            \"BSON key cannot contain code point U+0000 (at byte \" + std::to_string(it) + \")\"));\n        }\n\n        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;\n    }\n\n    /*!\n    @brief Writes the given @a element_type and @a name to the output adapter\n    */\n    void write_bson_entry_header(const string_t& name,\n                                 const std::uint8_t element_type)\n    {\n        oa->write_character(to_char_type(element_type)); // boolean\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(name.c_str()),\n            name.size() + 1u);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and boolean value @a value\n    */\n    void write_bson_boolean(const string_t& name,\n                            const bool value)\n    {\n        write_bson_entry_header(name, 0x08);\n        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and double value @a value\n    */\n    void write_bson_double(const string_t& name,\n                           const double value)\n    {\n        write_bson_entry_header(name, 0x01);\n        write_number<double, true>(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded string in @a value\n    */\n    static std::size_t calc_bson_string_size(const string_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and string value @a value\n    */\n    void write_bson_string(const string_t& name,\n                           const string_t& value)\n    {\n        write_bson_entry_header(name, 0x02);\n\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(value.c_str()),\n            value.size() + 1);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and null value\n    */\n    void write_bson_null(const string_t& name)\n    {\n        write_bson_entry_header(name, 0x0A);\n    }\n\n    /*!\n    @return The size of the BSON-encoded integer @a value\n    */\n    static std::size_t calc_bson_integer_size(const std::int64_t value)\n    {\n        return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and integer @a value\n    */\n    void write_bson_integer(const string_t& name,\n                            const std::int64_t value)\n    {\n        if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            write_bson_entry_header(name, 0x10); // int32\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));\n        }\n        else\n        {\n            write_bson_entry_header(name, 0x12); // int64\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));\n        }\n    }\n\n    /*!\n    @return The size of the BSON-encoded unsigned integer in @a j\n    */\n    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept\n    {\n        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and unsigned @a value\n    */\n    void write_bson_unsigned(const string_t& name,\n                             const std::uint64_t value)\n    {\n        if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x10 /* int32 */);\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));\n        }\n        else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x12 /* int64 */);\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));\n        }\n        else\n        {\n            JSON_THROW(out_of_range::create(407, \"integer number \" + std::to_string(value) + \" cannot be represented by BSON as it does not fit int64\"));\n        }\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and object @a value\n    */\n    void write_bson_object_entry(const string_t& name,\n                                 const typename BasicJsonType::object_t& value)\n    {\n        write_bson_entry_header(name, 0x03); // object\n        write_bson_object(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded array @a value\n    */\n    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)\n    {\n        std::size_t array_index = 0ul;\n\n        const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)\n        {\n            return result + calc_bson_element_size(std::to_string(array_index++), el);\n        });\n\n        return sizeof(std::int32_t) + embedded_document_size + 1ul;\n    }\n\n    /*!\n    @return The size of the BSON-encoded binary array @a value\n    */\n    static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and array @a value\n    */\n    void write_bson_array(const string_t& name,\n                          const typename BasicJsonType::array_t& value)\n    {\n        write_bson_entry_header(name, 0x04); // array\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value)));\n\n        std::size_t array_index = 0ul;\n\n        for (const auto& el : value)\n        {\n            write_bson_element(std::to_string(array_index++), el);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and binary value @a value\n    */\n    void write_bson_binary(const string_t& name,\n                           const binary_t& value)\n    {\n        write_bson_entry_header(name, 0x05);\n\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size()));\n        write_number(value.has_subtype() ? value.subtype() : std::uint8_t(0x00));\n\n        oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());\n    }\n\n    /*!\n    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name\n    @return The calculated size for the BSON document entry for @a j with the given @a name.\n    */\n    static std::size_t calc_bson_element_size(const string_t& name,\n            const BasicJsonType& j)\n    {\n        const auto header_size = calc_bson_entry_header_size(name);\n        switch (j.type())\n        {\n            case value_t::object:\n                return header_size + calc_bson_object_size(*j.m_value.object);\n\n            case value_t::array:\n                return header_size + calc_bson_array_size(*j.m_value.array);\n\n            case value_t::binary:\n                return header_size + calc_bson_binary_size(*j.m_value.binary);\n\n            case value_t::boolean:\n                return header_size + 1ul;\n\n            case value_t::number_float:\n                return header_size + 8ul;\n\n            case value_t::number_integer:\n                return header_size + calc_bson_integer_size(j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);\n\n            case value_t::string:\n                return header_size + calc_bson_string_size(*j.m_value.string);\n\n            case value_t::null:\n                return header_size + 0ul;\n\n            // LCOV_EXCL_START\n            default:\n                JSON_ASSERT(false);\n                return 0ul;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Serializes the JSON value @a j to BSON and associates it with the\n           key @a name.\n    @param name The name to associate with the JSON entity @a j within the\n                current BSON document\n    @return The size of the BSON entry\n    */\n    void write_bson_element(const string_t& name,\n                            const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n                return write_bson_object_entry(name, *j.m_value.object);\n\n            case value_t::array:\n                return write_bson_array(name, *j.m_value.array);\n\n            case value_t::binary:\n                return write_bson_binary(name, *j.m_value.binary);\n\n            case value_t::boolean:\n                return write_bson_boolean(name, j.m_value.boolean);\n\n            case value_t::number_float:\n                return write_bson_double(name, j.m_value.number_float);\n\n            case value_t::number_integer:\n                return write_bson_integer(name, j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return write_bson_unsigned(name, j.m_value.number_unsigned);\n\n            case value_t::string:\n                return write_bson_string(name, *j.m_value.string);\n\n            case value_t::null:\n                return write_bson_null(name);\n\n            // LCOV_EXCL_START\n            default:\n                JSON_ASSERT(false);\n                return;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Calculates the size of the BSON serialization of the given\n           JSON-object @a j.\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)\n    {\n        std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0),\n                                    [](size_t result, const typename BasicJsonType::object_t::value_type & el)\n        {\n            return result += calc_bson_element_size(el.first, el.second);\n        });\n\n        return sizeof(std::int32_t) + document_size + 1ul;\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson_object(const typename BasicJsonType::object_t& value)\n    {\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value)));\n\n        for (const auto& el : value)\n        {\n            write_bson_element(el.first, el.second);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    static constexpr CharType get_cbor_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xFA);  // Single-Precision Float\n    }\n\n    static constexpr CharType get_cbor_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xFB);  // Double-Precision Float\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xCA);  // float 32\n    }\n\n    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xCB);  // float 64\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    // UBJSON: write number (floating point)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_floating_point<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (add_prefix)\n        {\n            oa->write_character(get_ubjson_float_prefix(n));\n        }\n        write_number(n);\n    }\n\n    // UBJSON: write number (unsigned integer)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_unsigned<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if (n <= (std::numeric_limits<std::uint8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n));\n        }\n        else\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('H'));  // high-precision number\n            }\n\n            const auto number = BasicJsonType(n).dump();\n            write_number_with_ubjson_prefix(number.size(), true);\n            for (std::size_t i = 0; i < number.size(); ++i)\n            {\n                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));\n            }\n        }\n    }\n\n    // UBJSON: write number (signed integer)\n    template < typename NumberType, typename std::enable_if <\n                   std::is_signed<NumberType>::value&&\n                   !std::is_floating_point<NumberType>::value, int >::type = 0 >\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::int8_t>(n));\n        }\n        else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n));\n        }\n        else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n));\n        }\n        else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n));\n        }\n        // LCOV_EXCL_START\n        else\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('H'));  // high-precision number\n            }\n\n            const auto number = BasicJsonType(n).dump();\n            write_number_with_ubjson_prefix(number.size(), true);\n            for (std::size_t i = 0; i < number.size(); ++i)\n            {\n                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));\n            }\n        }\n        // LCOV_EXCL_STOP\n    }\n\n    /*!\n    @brief determine the type prefix of container values\n    */\n    CharType ubjson_prefix(const BasicJsonType& j) const noexcept\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n                return 'Z';\n\n            case value_t::boolean:\n                return j.m_value.boolean ? 'T' : 'F';\n\n            case value_t::number_integer:\n            {\n                if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                {\n                    return 'i';\n                }\n                if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    return 'U';\n                }\n                if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                {\n                    return 'I';\n                }\n                if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                {\n                    return 'l';\n                }\n                if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                {\n                    return 'L';\n                }\n                // anything else is treated as high-precision number\n                return 'H'; // LCOV_EXCL_LINE\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n                {\n                    return 'i';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))\n                {\n                    return 'U';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n                {\n                    return 'I';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n                {\n                    return 'l';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n                {\n                    return 'L';\n                }\n                // anything else is treated as high-precision number\n                return 'H'; // LCOV_EXCL_LINE\n            }\n\n            case value_t::number_float:\n                return get_ubjson_float_prefix(j.m_value.number_float);\n\n            case value_t::string:\n                return 'S';\n\n            case value_t::array: // fallthrough\n            case value_t::binary:\n                return '[';\n\n            case value_t::object:\n                return '{';\n\n            default:  // discarded values\n                return 'N';\n        }\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)\n    {\n        return 'd';  // float 32\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)\n    {\n        return 'D';  // float 64\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*\n    @brief write a number to output input\n    @param[in] n number of type @a NumberType\n    @tparam NumberType the type of the number\n    @tparam OutputIsLittleEndian Set to true if output data is\n                                 required to be little endian\n\n    @note This function needs to respect the system's endianess, because bytes\n          in CBOR, MessagePack, and UBJSON are stored in network order (big\n          endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool OutputIsLittleEndian = false>\n    void write_number(const NumberType n)\n    {\n        // step 1: write number to array of length NumberType\n        std::array<CharType, sizeof(NumberType)> vec;\n        std::memcpy(vec.data(), &n, sizeof(NumberType));\n\n        // step 2: write array to output (with possible reordering)\n        if (is_little_endian != OutputIsLittleEndian)\n        {\n            // reverse byte order prior to conversion if necessary\n            std::reverse(vec.begin(), vec.end());\n        }\n\n        oa->write_characters(vec.data(), sizeof(NumberType));\n    }\n\n    void write_compact_float(const number_float_t n, detail::input_format_t format)\n    {\n        if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&\n                static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&\n                static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))\n        {\n            oa->write_character(format == detail::input_format_t::cbor\n                                ? get_cbor_float_prefix(static_cast<float>(n))\n                                : get_msgpack_float_prefix(static_cast<float>(n)));\n            write_number(static_cast<float>(n));\n        }\n        else\n        {\n            oa->write_character(format == detail::input_format_t::cbor\n                                ? get_cbor_float_prefix(n)\n                                : get_msgpack_float_prefix(n));\n            write_number(n);\n        }\n    }\n\n  public:\n    // The following to_char_type functions are implement the conversion\n    // between uint8_t and CharType. In case CharType is not unsigned,\n    // such a conversion is required to allow values greater than 128.\n    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return *reinterpret_cast<char*>(&x);\n    }\n\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >\n    static CharType to_char_type(std::uint8_t x) noexcept\n    {\n        static_assert(sizeof(std::uint8_t) == sizeof(CharType), \"size of CharType must be equal to std::uint8_t\");\n        static_assert(std::is_trivial<CharType>::value, \"CharType must be trivial\");\n        CharType result;\n        std::memcpy(&result, &x, sizeof(x));\n        return result;\n    }\n\n    template<typename C = CharType,\n             enable_if_t<std::is_unsigned<C>::value>* = nullptr>\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return x;\n    }\n\n    template < typename InputCharType, typename C = CharType,\n               enable_if_t <\n                   std::is_signed<C>::value &&\n                   std::is_signed<char>::value &&\n                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value\n                   > * = nullptr >\n    static constexpr CharType to_char_type(InputCharType x) noexcept\n    {\n        return x;\n    }\n\n  private:\n    /// whether we can assume little endianess\n    const bool is_little_endian = little_endianess();\n\n    /// the output\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/output/serializer.hpp>\n\n\n#include <algorithm> // reverse, remove, fill, find, none_of\n#include <array> // array\n#include <clocale> // localeconv, lconv\n#include <cmath> // labs, isfinite, isnan, signbit\n#include <cstddef> // size_t, ptrdiff_t\n#include <cstdint> // uint8_t\n#include <cstdio> // snprintf\n#include <limits> // numeric_limits\n#include <string> // string, char_traits\n#include <type_traits> // is_same\n#include <utility> // move\n\n// #include <nlohmann/detail/conversions/to_chars.hpp>\n\n\n#include <array> // array\n#include <cmath>   // signbit, isfinite\n#include <cstdint> // intN_t, uintN_t\n#include <cstring> // memcpy, memmove\n#include <limits> // numeric_limits\n#include <type_traits> // conditional\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/*!\n@brief implements the Grisu2 algorithm for binary to decimal floating-point\nconversion.\n\nThis implementation is a slightly modified version of the reference\nimplementation which may be obtained from\nhttp://florian.loitsch.com/publications (bench.tar.gz).\n\nThe code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.\n\nFor a detailed description of the algorithm see:\n\n[1] Loitsch, \"Printing Floating-Point Numbers Quickly and Accurately with\n    Integers\", Proceedings of the ACM SIGPLAN 2010 Conference on Programming\n    Language Design and Implementation, PLDI 2010\n[2] Burger, Dybvig, \"Printing Floating-Point Numbers Quickly and Accurately\",\n    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language\n    Design and Implementation, PLDI 1996\n*/\nnamespace dtoa_impl\n{\n\ntemplate<typename Target, typename Source>\nTarget reinterpret_bits(const Source source)\n{\n    static_assert(sizeof(Target) == sizeof(Source), \"size mismatch\");\n\n    Target target;\n    std::memcpy(&target, &source, sizeof(Source));\n    return target;\n}\n\nstruct diyfp // f * 2^e\n{\n    static constexpr int kPrecision = 64; // = q\n\n    std::uint64_t f = 0;\n    int e = 0;\n\n    constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}\n\n    /*!\n    @brief returns x - y\n    @pre x.e == y.e and x.f >= y.f\n    */\n    static diyfp sub(const diyfp& x, const diyfp& y) noexcept\n    {\n        JSON_ASSERT(x.e == y.e);\n        JSON_ASSERT(x.f >= y.f);\n\n        return {x.f - y.f, x.e};\n    }\n\n    /*!\n    @brief returns x * y\n    @note The result is rounded. (Only the upper q bits are returned.)\n    */\n    static diyfp mul(const diyfp& x, const diyfp& y) noexcept\n    {\n        static_assert(kPrecision == 64, \"internal error\");\n\n        // Computes:\n        //  f = round((x.f * y.f) / 2^q)\n        //  e = x.e + y.e + q\n\n        // Emulate the 64-bit * 64-bit multiplication:\n        //\n        // p = u * v\n        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)\n        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )\n        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )\n        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )\n        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)\n        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )\n        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )\n        //\n        // (Since Q might be larger than 2^32 - 1)\n        //\n        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)\n        //\n        // (Q_hi + H does not overflow a 64-bit int)\n        //\n        //   = p_lo + 2^64 p_hi\n\n        const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;\n        const std::uint64_t u_hi = x.f >> 32u;\n        const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;\n        const std::uint64_t v_hi = y.f >> 32u;\n\n        const std::uint64_t p0 = u_lo * v_lo;\n        const std::uint64_t p1 = u_lo * v_hi;\n        const std::uint64_t p2 = u_hi * v_lo;\n        const std::uint64_t p3 = u_hi * v_hi;\n\n        const std::uint64_t p0_hi = p0 >> 32u;\n        const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;\n        const std::uint64_t p1_hi = p1 >> 32u;\n        const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;\n        const std::uint64_t p2_hi = p2 >> 32u;\n\n        std::uint64_t Q = p0_hi + p1_lo + p2_lo;\n\n        // The full product might now be computed as\n        //\n        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)\n        // p_lo = p0_lo + (Q << 32)\n        //\n        // But in this particular case here, the full p_lo is not required.\n        // Effectively we only need to add the highest bit in p_lo to p_hi (and\n        // Q_hi + 1 does not overflow).\n\n        Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up\n\n        const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);\n\n        return {h, x.e + y.e + 64};\n    }\n\n    /*!\n    @brief normalize x such that the significand is >= 2^(q-1)\n    @pre x.f != 0\n    */\n    static diyfp normalize(diyfp x) noexcept\n    {\n        JSON_ASSERT(x.f != 0);\n\n        while ((x.f >> 63u) == 0)\n        {\n            x.f <<= 1u;\n            x.e--;\n        }\n\n        return x;\n    }\n\n    /*!\n    @brief normalize x such that the result has the exponent E\n    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.\n    */\n    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept\n    {\n        const int delta = x.e - target_exponent;\n\n        JSON_ASSERT(delta >= 0);\n        JSON_ASSERT(((x.f << delta) >> delta) == x.f);\n\n        return {x.f << delta, target_exponent};\n    }\n};\n\nstruct boundaries\n{\n    diyfp w;\n    diyfp minus;\n    diyfp plus;\n};\n\n/*!\nCompute the (normalized) diyfp representing the input number 'value' and its\nboundaries.\n\n@pre value must be finite and positive\n*/\ntemplate<typename FloatType>\nboundaries compute_boundaries(FloatType value)\n{\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // Convert the IEEE representation into a diyfp.\n    //\n    // If v is denormal:\n    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))\n    // If v is normalized:\n    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))\n\n    static_assert(std::numeric_limits<FloatType>::is_iec559,\n                  \"internal error: dtoa_short requires an IEEE-754 floating-point implementation\");\n\n    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)\n    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);\n    constexpr int      kMinExp    = 1 - kBias;\n    constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)\n\n    using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;\n\n    const std::uint64_t bits = reinterpret_bits<bits_type>(value);\n    const std::uint64_t E = bits >> (kPrecision - 1);\n    const std::uint64_t F = bits & (kHiddenBit - 1);\n\n    const bool is_denormal = E == 0;\n    const diyfp v = is_denormal\n                    ? diyfp(F, kMinExp)\n                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);\n\n    // Compute the boundaries m- and m+ of the floating-point value\n    // v = f * 2^e.\n    //\n    // Determine v- and v+, the floating-point predecessor and successor if v,\n    // respectively.\n    //\n    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)\n    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)\n    //\n    //      v+ = v + 2^e\n    //\n    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_\n    // between m- and m+ round to v, regardless of how the input rounding\n    // algorithm breaks ties.\n    //\n    //      ---+-------------+-------------+-------------+-------------+---  (A)\n    //         v-            m-            v             m+            v+\n    //\n    //      -----------------+------+------+-------------+-------------+---  (B)\n    //                       v-     m-     v             m+            v+\n\n    const bool lower_boundary_is_closer = F == 0 && E > 1;\n    const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);\n    const diyfp m_minus = lower_boundary_is_closer\n                          ? diyfp(4 * v.f - 1, v.e - 2)  // (B)\n                          : diyfp(2 * v.f - 1, v.e - 1); // (A)\n\n    // Determine the normalized w+ = m+.\n    const diyfp w_plus = diyfp::normalize(m_plus);\n\n    // Determine w- = m- such that e_(w-) = e_(w+).\n    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);\n\n    return {diyfp::normalize(v), w_minus, w_plus};\n}\n\n// Given normalized diyfp w, Grisu needs to find a (normalized) cached\n// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies\n// within a certain range [alpha, gamma] (Definition 3.2 from [1])\n//\n//      alpha <= e = e_c + e_w + q <= gamma\n//\n// or\n//\n//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q\n//                          <= f_c * f_w * 2^gamma\n//\n// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies\n//\n//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma\n//\n// or\n//\n//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)\n//\n// The choice of (alpha,gamma) determines the size of the table and the form of\n// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well\n// in practice:\n//\n// The idea is to cut the number c * w = f * 2^e into two parts, which can be\n// processed independently: An integral part p1, and a fractional part p2:\n//\n//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e\n//              = (f div 2^-e) + (f mod 2^-e) * 2^e\n//              = p1 + p2 * 2^e\n//\n// The conversion of p1 into decimal form requires a series of divisions and\n// modulos by (a power of) 10. These operations are faster for 32-bit than for\n// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be\n// achieved by choosing\n//\n//      -e >= 32   or   e <= -32 := gamma\n//\n// In order to convert the fractional part\n//\n//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...\n//\n// into decimal form, the fraction is repeatedly multiplied by 10 and the digits\n// d[-i] are extracted in order:\n//\n//      (10 * p2) div 2^-e = d[-1]\n//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...\n//\n// The multiplication by 10 must not overflow. It is sufficient to choose\n//\n//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.\n//\n// Since p2 = f mod 2^-e < 2^-e,\n//\n//      -e <= 60   or   e >= -60 := alpha\n\nconstexpr int kAlpha = -60;\nconstexpr int kGamma = -32;\n\nstruct cached_power // c = f * 2^e ~= 10^k\n{\n    std::uint64_t f;\n    int e;\n    int k;\n};\n\n/*!\nFor a normalized diyfp w = f * 2^e, this function returns a (normalized) cached\npower-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c\nsatisfies (Definition 3.2 from [1])\n\n     alpha <= e_c + e + q <= gamma.\n*/\ninline cached_power get_cached_power_for_binary_exponent(int e)\n{\n    // Now\n    //\n    //      alpha <= e_c + e + q <= gamma                                    (1)\n    //      ==> f_c * 2^alpha <= c * 2^e * 2^q\n    //\n    // and since the c's are normalized, 2^(q-1) <= f_c,\n    //\n    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)\n    //      ==> 2^(alpha - e - 1) <= c\n    //\n    // If c were an exact power of ten, i.e. c = 10^k, one may determine k as\n    //\n    //      k = ceil( log_10( 2^(alpha - e - 1) ) )\n    //        = ceil( (alpha - e - 1) * log_10(2) )\n    //\n    // From the paper:\n    // \"In theory the result of the procedure could be wrong since c is rounded,\n    //  and the computation itself is approximated [...]. In practice, however,\n    //  this simple function is sufficient.\"\n    //\n    // For IEEE double precision floating-point numbers converted into\n    // normalized diyfp's w = f * 2^e, with q = 64,\n    //\n    //      e >= -1022      (min IEEE exponent)\n    //           -52        (p - 1)\n    //           -52        (p - 1, possibly normalize denormal IEEE numbers)\n    //           -11        (normalize the diyfp)\n    //         = -1137\n    //\n    // and\n    //\n    //      e <= +1023      (max IEEE exponent)\n    //           -52        (p - 1)\n    //           -11        (normalize the diyfp)\n    //         = 960\n    //\n    // This binary exponent range [-1137,960] results in a decimal exponent\n    // range [-307,324]. One does not need to store a cached power for each\n    // k in this range. For each such k it suffices to find a cached power\n    // such that the exponent of the product lies in [alpha,gamma].\n    // This implies that the difference of the decimal exponents of adjacent\n    // table entries must be less than or equal to\n    //\n    //      floor( (gamma - alpha) * log_10(2) ) = 8.\n    //\n    // (A smaller distance gamma-alpha would require a larger table.)\n\n    // NB:\n    // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.\n\n    constexpr int kCachedPowersMinDecExp = -300;\n    constexpr int kCachedPowersDecStep = 8;\n\n    static constexpr std::array<cached_power, 79> kCachedPowers =\n    {\n        {\n            { 0xAB70FE17C79AC6CA, -1060, -300 },\n            { 0xFF77B1FCBEBCDC4F, -1034, -292 },\n            { 0xBE5691EF416BD60C, -1007, -284 },\n            { 0x8DD01FAD907FFC3C,  -980, -276 },\n            { 0xD3515C2831559A83,  -954, -268 },\n            { 0x9D71AC8FADA6C9B5,  -927, -260 },\n            { 0xEA9C227723EE8BCB,  -901, -252 },\n            { 0xAECC49914078536D,  -874, -244 },\n            { 0x823C12795DB6CE57,  -847, -236 },\n            { 0xC21094364DFB5637,  -821, -228 },\n            { 0x9096EA6F3848984F,  -794, -220 },\n            { 0xD77485CB25823AC7,  -768, -212 },\n            { 0xA086CFCD97BF97F4,  -741, -204 },\n            { 0xEF340A98172AACE5,  -715, -196 },\n            { 0xB23867FB2A35B28E,  -688, -188 },\n            { 0x84C8D4DFD2C63F3B,  -661, -180 },\n            { 0xC5DD44271AD3CDBA,  -635, -172 },\n            { 0x936B9FCEBB25C996,  -608, -164 },\n            { 0xDBAC6C247D62A584,  -582, -156 },\n            { 0xA3AB66580D5FDAF6,  -555, -148 },\n            { 0xF3E2F893DEC3F126,  -529, -140 },\n            { 0xB5B5ADA8AAFF80B8,  -502, -132 },\n            { 0x87625F056C7C4A8B,  -475, -124 },\n            { 0xC9BCFF6034C13053,  -449, -116 },\n            { 0x964E858C91BA2655,  -422, -108 },\n            { 0xDFF9772470297EBD,  -396, -100 },\n            { 0xA6DFBD9FB8E5B88F,  -369,  -92 },\n            { 0xF8A95FCF88747D94,  -343,  -84 },\n            { 0xB94470938FA89BCF,  -316,  -76 },\n            { 0x8A08F0F8BF0F156B,  -289,  -68 },\n            { 0xCDB02555653131B6,  -263,  -60 },\n            { 0x993FE2C6D07B7FAC,  -236,  -52 },\n            { 0xE45C10C42A2B3B06,  -210,  -44 },\n            { 0xAA242499697392D3,  -183,  -36 },\n            { 0xFD87B5F28300CA0E,  -157,  -28 },\n            { 0xBCE5086492111AEB,  -130,  -20 },\n            { 0x8CBCCC096F5088CC,  -103,  -12 },\n            { 0xD1B71758E219652C,   -77,   -4 },\n            { 0x9C40000000000000,   -50,    4 },\n            { 0xE8D4A51000000000,   -24,   12 },\n            { 0xAD78EBC5AC620000,     3,   20 },\n            { 0x813F3978F8940984,    30,   28 },\n            { 0xC097CE7BC90715B3,    56,   36 },\n            { 0x8F7E32CE7BEA5C70,    83,   44 },\n            { 0xD5D238A4ABE98068,   109,   52 },\n            { 0x9F4F2726179A2245,   136,   60 },\n            { 0xED63A231D4C4FB27,   162,   68 },\n            { 0xB0DE65388CC8ADA8,   189,   76 },\n            { 0x83C7088E1AAB65DB,   216,   84 },\n            { 0xC45D1DF942711D9A,   242,   92 },\n            { 0x924D692CA61BE758,   269,  100 },\n            { 0xDA01EE641A708DEA,   295,  108 },\n            { 0xA26DA3999AEF774A,   322,  116 },\n            { 0xF209787BB47D6B85,   348,  124 },\n            { 0xB454E4A179DD1877,   375,  132 },\n            { 0x865B86925B9BC5C2,   402,  140 },\n            { 0xC83553C5C8965D3D,   428,  148 },\n            { 0x952AB45CFA97A0B3,   455,  156 },\n            { 0xDE469FBD99A05FE3,   481,  164 },\n            { 0xA59BC234DB398C25,   508,  172 },\n            { 0xF6C69A72A3989F5C,   534,  180 },\n            { 0xB7DCBF5354E9BECE,   561,  188 },\n            { 0x88FCF317F22241E2,   588,  196 },\n            { 0xCC20CE9BD35C78A5,   614,  204 },\n            { 0x98165AF37B2153DF,   641,  212 },\n            { 0xE2A0B5DC971F303A,   667,  220 },\n            { 0xA8D9D1535CE3B396,   694,  228 },\n            { 0xFB9B7CD9A4A7443C,   720,  236 },\n            { 0xBB764C4CA7A44410,   747,  244 },\n            { 0x8BAB8EEFB6409C1A,   774,  252 },\n            { 0xD01FEF10A657842C,   800,  260 },\n            { 0x9B10A4E5E9913129,   827,  268 },\n            { 0xE7109BFBA19C0C9D,   853,  276 },\n            { 0xAC2820D9623BF429,   880,  284 },\n            { 0x80444B5E7AA7CF85,   907,  292 },\n            { 0xBF21E44003ACDD2D,   933,  300 },\n            { 0x8E679C2F5E44FF8F,   960,  308 },\n            { 0xD433179D9C8CB841,   986,  316 },\n            { 0x9E19DB92B4E31BA9,  1013,  324 },\n        }\n    };\n\n    // This computation gives exactly the same results for k as\n    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)\n    // for |e| <= 1500, but doesn't require floating-point operations.\n    // NB: log_10(2) ~= 78913 / 2^18\n    JSON_ASSERT(e >= -1500);\n    JSON_ASSERT(e <=  1500);\n    const int f = kAlpha - e - 1;\n    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);\n\n    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;\n    JSON_ASSERT(index >= 0);\n    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());\n\n    const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];\n    JSON_ASSERT(kAlpha <= cached.e + e + 64);\n    JSON_ASSERT(kGamma >= cached.e + e + 64);\n\n    return cached;\n}\n\n/*!\nFor n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.\nFor n == 0, returns 1 and sets pow10 := 1.\n*/\ninline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)\n{\n    // LCOV_EXCL_START\n    if (n >= 1000000000)\n    {\n        pow10 = 1000000000;\n        return 10;\n    }\n    // LCOV_EXCL_STOP\n    else if (n >= 100000000)\n    {\n        pow10 = 100000000;\n        return  9;\n    }\n    else if (n >= 10000000)\n    {\n        pow10 = 10000000;\n        return  8;\n    }\n    else if (n >= 1000000)\n    {\n        pow10 = 1000000;\n        return  7;\n    }\n    else if (n >= 100000)\n    {\n        pow10 = 100000;\n        return  6;\n    }\n    else if (n >= 10000)\n    {\n        pow10 = 10000;\n        return  5;\n    }\n    else if (n >= 1000)\n    {\n        pow10 = 1000;\n        return  4;\n    }\n    else if (n >= 100)\n    {\n        pow10 = 100;\n        return  3;\n    }\n    else if (n >= 10)\n    {\n        pow10 = 10;\n        return  2;\n    }\n    else\n    {\n        pow10 = 1;\n        return 1;\n    }\n}\n\ninline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,\n                         std::uint64_t rest, std::uint64_t ten_k)\n{\n    JSON_ASSERT(len >= 1);\n    JSON_ASSERT(dist <= delta);\n    JSON_ASSERT(rest <= delta);\n    JSON_ASSERT(ten_k > 0);\n\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    //                                  ten_k\n    //                                <------>\n    //                                       <---- rest ---->\n    // --------------[------------------+----+--------------]--------------\n    //                                  w    V\n    //                                       = buf * 10^k\n    //\n    // ten_k represents a unit-in-the-last-place in the decimal representation\n    // stored in buf.\n    // Decrement buf by ten_k while this takes buf closer to w.\n\n    // The tests are written in this order to avoid overflow in unsigned\n    // integer arithmetic.\n\n    while (rest < dist\n            && delta - rest >= ten_k\n            && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))\n    {\n        JSON_ASSERT(buf[len - 1] != '0');\n        buf[len - 1]--;\n        rest += ten_k;\n    }\n}\n\n/*!\nGenerates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.\nM- and M+ must be normalized and share the same exponent -60 <= e <= -32.\n*/\ninline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,\n                             diyfp M_minus, diyfp w, diyfp M_plus)\n{\n    static_assert(kAlpha >= -60, \"internal error\");\n    static_assert(kGamma <= -32, \"internal error\");\n\n    // Generates the digits (and the exponent) of a decimal floating-point\n    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's\n    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.\n    //\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    // Grisu2 generates the digits of M+ from left to right and stops as soon as\n    // V is in [M-,M+].\n\n    JSON_ASSERT(M_plus.e >= kAlpha);\n    JSON_ASSERT(M_plus.e <= kGamma);\n\n    std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)\n    std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)\n\n    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):\n    //\n    //      M+ = f * 2^e\n    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e\n    //         = ((p1        ) * 2^-e + (p2        )) * 2^e\n    //         = p1 + p2 * 2^e\n\n    const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);\n\n    auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)\n    std::uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e\n\n    // 1)\n    //\n    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]\n\n    JSON_ASSERT(p1 > 0);\n\n    std::uint32_t pow10;\n    const int k = find_largest_pow10(p1, pow10);\n\n    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)\n    //\n    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))\n    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))\n    //\n    //      M+ = p1                                             + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e\n    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e\n    //\n    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)\n    //\n    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]\n    //\n    // but stop as soon as\n    //\n    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e\n\n    int n = k;\n    while (n > 0)\n    {\n        // Invariants:\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        //\n        const std::uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)\n        const std::uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)\n        //\n        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e\n        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)\n        //\n        p1 = r;\n        n--;\n        //\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)\n        //      pow10 = 10^n\n        //\n\n        // Now check if enough digits have been generated.\n        // Compute\n        //\n        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e\n        //\n        // Note:\n        // Since rest and delta share the same exponent e, it suffices to\n        // compare the significands.\n        const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;\n        if (rest <= delta)\n        {\n            // V = buffer * 10^n, with M- <= V <= M+.\n\n            decimal_exponent += n;\n\n            // We may now just stop. But instead look if the buffer could be\n            // decremented to bring V closer to w.\n            //\n            // pow10 = 10^n is now 1 ulp in the decimal representation V.\n            // The rounding procedure works with diyfp's with an implicit\n            // exponent of e.\n            //\n            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e\n            //\n            const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;\n            grisu2_round(buffer, length, dist, delta, rest, ten_n);\n\n            return;\n        }\n\n        pow10 /= 10;\n        //\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        // Invariants restored.\n    }\n\n    // 2)\n    //\n    // The digits of the integral part have been generated:\n    //\n    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e\n    //         = buffer            + p2 * 2^e\n    //\n    // Now generate the digits of the fractional part p2 * 2^e.\n    //\n    // Note:\n    // No decimal point is generated: the exponent is adjusted instead.\n    //\n    // p2 actually represents the fraction\n    //\n    //      p2 * 2^e\n    //          = p2 / 2^-e\n    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...\n    //\n    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)\n    //\n    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m\n    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)\n    //\n    // using\n    //\n    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)\n    //                = (                   d) * 2^-e + (                   r)\n    //\n    // or\n    //      10^m * p2 * 2^e = d + r * 2^e\n    //\n    // i.e.\n    //\n    //      M+ = buffer + p2 * 2^e\n    //         = buffer + 10^-m * (d + r * 2^e)\n    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e\n    //\n    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e\n\n    JSON_ASSERT(p2 > delta);\n\n    int m = 0;\n    for (;;)\n    {\n        // Invariant:\n        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e\n        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e\n        //\n        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);\n        p2 *= 10;\n        const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e\n        const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e\n        //\n        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))\n        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        p2 = r;\n        m++;\n        //\n        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e\n        // Invariant restored.\n\n        // Check if enough digits have been generated.\n        //\n        //      10^-m * p2 * 2^e <= delta * 2^e\n        //              p2 * 2^e <= 10^m * delta * 2^e\n        //                    p2 <= 10^m * delta\n        delta *= 10;\n        dist  *= 10;\n        if (p2 <= delta)\n        {\n            break;\n        }\n    }\n\n    // V = buffer * 10^-m, with M- <= V <= M+.\n\n    decimal_exponent -= m;\n\n    // 1 ulp in the decimal representation is now 10^-m.\n    // Since delta and dist are now scaled by 10^m, we need to do the\n    // same with ulp in order to keep the units in sync.\n    //\n    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e\n    //\n    const std::uint64_t ten_m = one.f;\n    grisu2_round(buffer, length, dist, delta, p2, ten_m);\n\n    // By construction this algorithm generates the shortest possible decimal\n    // number (Loitsch, Theorem 6.2) which rounds back to w.\n    // For an input number of precision p, at least\n    //\n    //      N = 1 + ceil(p * log_10(2))\n    //\n    // decimal digits are sufficient to identify all binary floating-point\n    // numbers (Matula, \"In-and-Out conversions\").\n    // This implies that the algorithm does not produce more than N decimal\n    // digits.\n    //\n    //      N = 17 for p = 53 (IEEE double precision)\n    //      N = 9  for p = 24 (IEEE single precision)\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline void grisu2(char* buf, int& len, int& decimal_exponent,\n                   diyfp m_minus, diyfp v, diyfp m_plus)\n{\n    JSON_ASSERT(m_plus.e == m_minus.e);\n    JSON_ASSERT(m_plus.e == v.e);\n\n    //  --------(-----------------------+-----------------------)--------    (A)\n    //          m-                      v                       m+\n    //\n    //  --------------------(-----------+-----------------------)--------    (B)\n    //                      m-          v                       m+\n    //\n    // First scale v (and m- and m+) such that the exponent is in the range\n    // [alpha, gamma].\n\n    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);\n\n    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k\n\n    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]\n    const diyfp w       = diyfp::mul(v,       c_minus_k);\n    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);\n    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);\n\n    //  ----(---+---)---------------(---+---)---------------(---+---)----\n    //          w-                      w                       w+\n    //          = c*m-                  = c*v                   = c*m+\n    //\n    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and\n    // w+ are now off by a small amount.\n    // In fact:\n    //\n    //      w - v * 10^k < 1 ulp\n    //\n    // To account for this inaccuracy, add resp. subtract 1 ulp.\n    //\n    //  --------+---[---------------(---+---)---------------]---+--------\n    //          w-  M-                  w                   M+  w+\n    //\n    // Now any number in [M-, M+] (bounds included) will round to w when input,\n    // regardless of how the input rounding algorithm breaks ties.\n    //\n    // And digit_gen generates the shortest possible such number in [M-, M+].\n    // Note that this does not mean that Grisu2 always generates the shortest\n    // possible number in the interval (m-, m+).\n    const diyfp M_minus(w_minus.f + 1, w_minus.e);\n    const diyfp M_plus (w_plus.f  - 1, w_plus.e );\n\n    decimal_exponent = -cached.k; // = -(-k) = k\n\n    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1)\nvoid grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)\n{\n    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,\n                  \"internal error: not enough precision\");\n\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // If the neighbors (and boundaries) of 'value' are always computed for double-precision\n    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting\n    // decimal representations are not exactly \"short\".\n    //\n    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)\n    // says \"value is converted to a string as if by std::sprintf in the default (\"C\") locale\"\n    // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars'\n    // does.\n    // On the other hand, the documentation for 'std::to_chars' requires that \"parsing the\n    // representation using the corresponding std::from_chars function recovers value exactly\". That\n    // indicates that single precision floating-point numbers should be recovered using\n    // 'std::strtof'.\n    //\n    // NB: If the neighbors are computed for single-precision numbers, there is a single float\n    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision\n    //     value is off by 1 ulp.\n#if 0\n    const boundaries w = compute_boundaries(static_cast<double>(value));\n#else\n    const boundaries w = compute_boundaries(value);\n#endif\n\n    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);\n}\n\n/*!\n@brief appends a decimal representation of e to buf\n@return a pointer to the element following the exponent.\n@pre -1000 < e < 1000\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* append_exponent(char* buf, int e)\n{\n    JSON_ASSERT(e > -1000);\n    JSON_ASSERT(e <  1000);\n\n    if (e < 0)\n    {\n        e = -e;\n        *buf++ = '-';\n    }\n    else\n    {\n        *buf++ = '+';\n    }\n\n    auto k = static_cast<std::uint32_t>(e);\n    if (k < 10)\n    {\n        // Always print at least two digits in the exponent.\n        // This is for compatibility with printf(\"%g\").\n        *buf++ = '0';\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else if (k < 100)\n    {\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else\n    {\n        *buf++ = static_cast<char>('0' + k / 100);\n        k %= 100;\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n\n    return buf;\n}\n\n/*!\n@brief prettify v = buf * 10^decimal_exponent\n\nIf v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point\nnotation. Otherwise it will be printed in exponential notation.\n\n@pre min_exp < 0\n@pre max_exp > 0\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* format_buffer(char* buf, int len, int decimal_exponent,\n                           int min_exp, int max_exp)\n{\n    JSON_ASSERT(min_exp < 0);\n    JSON_ASSERT(max_exp > 0);\n\n    const int k = len;\n    const int n = len + decimal_exponent;\n\n    // v = buf * 10^(n-k)\n    // k is the length of the buffer (number of decimal digits)\n    // n is the position of the decimal point relative to the start of the buffer.\n\n    if (k <= n && n <= max_exp)\n    {\n        // digits[000]\n        // len <= max_exp + 2\n\n        std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));\n        // Make it look like a floating-point number (#362, #378)\n        buf[n + 0] = '.';\n        buf[n + 1] = '0';\n        return buf + (static_cast<size_t>(n) + 2);\n    }\n\n    if (0 < n && n <= max_exp)\n    {\n        // dig.its\n        // len <= max_digits10 + 1\n\n        JSON_ASSERT(k > n);\n\n        std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));\n        buf[n] = '.';\n        return buf + (static_cast<size_t>(k) + 1U);\n    }\n\n    if (min_exp < n && n <= 0)\n    {\n        // 0.[000]digits\n        // len <= 2 + (-min_exp - 1) + max_digits10\n\n        std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));\n        buf[0] = '0';\n        buf[1] = '.';\n        std::memset(buf + 2, '0', static_cast<size_t>(-n));\n        return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));\n    }\n\n    if (k == 1)\n    {\n        // dE+123\n        // len <= 1 + 5\n\n        buf += 1;\n    }\n    else\n    {\n        // d.igitsE+123\n        // len <= max_digits10 + 1 + 5\n\n        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);\n        buf[1] = '.';\n        buf += 1 + static_cast<size_t>(k);\n    }\n\n    *buf++ = 'e';\n    return append_exponent(buf, n - 1);\n}\n\n} // namespace dtoa_impl\n\n/*!\n@brief generates a decimal representation of the floating-point number value in [first, last).\n\nThe format of the resulting decimal representation is similar to printf's %g\nformat. Returns an iterator pointing past-the-end of the decimal representation.\n\n@note The input number must be finite, i.e. NaN's and Inf's are not supported.\n@note The buffer must be large enough.\n@note The result is NOT null-terminated.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1, 2)\nJSON_HEDLEY_RETURNS_NON_NULL\nchar* to_chars(char* first, const char* last, FloatType value)\n{\n    static_cast<void>(last); // maybe unused - fix warning\n    JSON_ASSERT(std::isfinite(value));\n\n    // Use signbit(value) instead of (value < 0) since signbit works for -0.\n    if (std::signbit(value))\n    {\n        value = -value;\n        *first++ = '-';\n    }\n\n    if (value == 0) // +-0\n    {\n        *first++ = '0';\n        // Make it look like a floating-point number (#362, #378)\n        *first++ = '.';\n        *first++ = '0';\n        return first;\n    }\n\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);\n\n    // Compute v = buffer * 10^decimal_exponent.\n    // The decimal digits are stored in the buffer, which needs to be interpreted\n    // as an unsigned decimal integer.\n    // len is the length of the buffer, i.e. the number of decimal digits.\n    int len = 0;\n    int decimal_exponent = 0;\n    dtoa_impl::grisu2(first, len, decimal_exponent, value);\n\n    JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);\n\n    // Format the buffer like printf(\"%.*g\", prec, value)\n    constexpr int kMinExp = -4;\n    // Use digits10 here to increase compatibility with version 2.\n    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;\n\n    JSON_ASSERT(last - first >= kMaxExp + 2);\n    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);\n\n    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);\n}\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// serialization //\n///////////////////\n\n/// how to treat decoding errors\nenum class error_handler_t\n{\n    strict,  ///< throw a type_error exception in case of invalid UTF-8\n    replace, ///< replace invalid UTF-8 sequences with U+FFFD\n    ignore   ///< ignore invalid UTF-8 sequences\n};\n\ntemplate<typename BasicJsonType>\nclass serializer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using binary_char_t = typename BasicJsonType::binary_t::value_type;\n    static constexpr std::uint8_t UTF8_ACCEPT = 0;\n    static constexpr std::uint8_t UTF8_REJECT = 1;\n\n  public:\n    /*!\n    @param[in] s  output stream to serialize to\n    @param[in] ichar  indentation character to use\n    @param[in] error_handler_  how to react on decoding errors\n    */\n    serializer(output_adapter_t<char> s, const char ichar,\n               error_handler_t error_handler_ = error_handler_t::strict)\n        : o(std::move(s))\n        , loc(std::localeconv())\n        , thousands_sep(loc->thousands_sep == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))\n        , decimal_point(loc->decimal_point == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))\n        , indent_char(ichar)\n        , indent_string(512, indent_char)\n        , error_handler(error_handler_)\n    {}\n\n    // delete because of pointer members\n    serializer(const serializer&) = delete;\n    serializer& operator=(const serializer&) = delete;\n    serializer(serializer&&) = delete;\n    serializer& operator=(serializer&&) = delete;\n    ~serializer() = default;\n\n    /*!\n    @brief internal implementation of the serialization function\n\n    This function is called by the public member function dump and organizes\n    the serialization internally. The indentation level is propagated as\n    additional parameter. In case of arrays and objects, the function is\n    called recursively.\n\n    - strings and object keys are escaped using `escape_string()`\n    - integer numbers are converted implicitly via `operator<<`\n    - floating-point numbers are converted to a string using `\"%g\"` format\n    - binary values are serialized as objects containing the subtype and the\n      byte array\n\n    @param[in] val               value to serialize\n    @param[in] pretty_print      whether the output shall be pretty-printed\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] indent_step       the indent level\n    @param[in] current_indent    the current indent level (only used internally)\n    */\n    void dump(const BasicJsonType& val,\n              const bool pretty_print,\n              const bool ensure_ascii,\n              const unsigned int indent_step,\n              const unsigned int current_indent = 0)\n    {\n        switch (val.m_type)\n        {\n            case value_t::object:\n            {\n                if (val.m_value.object->empty())\n                {\n                    o->write_characters(\"{}\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\": \", 3);\n                        dump(i->second, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\": \", 3);\n                    dump(i->second, true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_character('{');\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\":\", 2);\n                        dump(i->second, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\":\", 2);\n                    dump(i->second, false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character('}');\n                }\n\n                return;\n            }\n\n            case value_t::array:\n            {\n                if (val.m_value.array->empty())\n                {\n                    o->write_characters(\"[]\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"[\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        dump(*i, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_value.array->empty());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character(']');\n                }\n                else\n                {\n                    o->write_character('[');\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        dump(*i, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_value.array->empty());\n                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character(']');\n                }\n\n                return;\n            }\n\n            case value_t::string:\n            {\n                o->write_character('\\\"');\n                dump_escaped(*val.m_value.string, ensure_ascii);\n                o->write_character('\\\"');\n                return;\n            }\n\n            case value_t::binary:\n            {\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"bytes\\\": [\", 10);\n\n                    if (!val.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_value.binary->cbegin();\n                                i != val.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_characters(\", \", 2);\n                        }\n                        dump_integer(val.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\n\", 3);\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"subtype\\\": \", 11);\n                    if (val.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_value.binary->subtype());\n                    }\n                    else\n                    {\n                        o->write_characters(\"null\", 4);\n                    }\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_characters(\"{\\\"bytes\\\":[\", 10);\n\n                    if (!val.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_value.binary->cbegin();\n                                i != val.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_character(',');\n                        }\n                        dump_integer(val.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\\"subtype\\\":\", 12);\n                    if (val.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_value.binary->subtype());\n                        o->write_character('}');\n                    }\n                    else\n                    {\n                        o->write_characters(\"null}\", 5);\n                    }\n                }\n                return;\n            }\n\n            case value_t::boolean:\n            {\n                if (val.m_value.boolean)\n                {\n                    o->write_characters(\"true\", 4);\n                }\n                else\n                {\n                    o->write_characters(\"false\", 5);\n                }\n                return;\n            }\n\n            case value_t::number_integer:\n            {\n                dump_integer(val.m_value.number_integer);\n                return;\n            }\n\n            case value_t::number_unsigned:\n            {\n                dump_integer(val.m_value.number_unsigned);\n                return;\n            }\n\n            case value_t::number_float:\n            {\n                dump_float(val.m_value.number_float);\n                return;\n            }\n\n            case value_t::discarded:\n            {\n                o->write_characters(\"<discarded>\", 11);\n                return;\n            }\n\n            case value_t::null:\n            {\n                o->write_characters(\"null\", 4);\n                return;\n            }\n\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false);  // LCOV_EXCL_LINE\n        }\n    }\n\n  private:\n    /*!\n    @brief dump escaped string\n\n    Escape a string by replacing certain special characters by a sequence of an\n    escape character (backslash) and another character and other control\n    characters by a sequence of \"\\u\" followed by a four-digit hex\n    representation. The escaped string is written to output stream @a o.\n\n    @param[in] s  the string to escape\n    @param[in] ensure_ascii  whether to escape non-ASCII characters with\n                             \\uXXXX sequences\n\n    @complexity Linear in the length of string @a s.\n    */\n    void dump_escaped(const string_t& s, const bool ensure_ascii)\n    {\n        std::uint32_t codepoint;\n        std::uint8_t state = UTF8_ACCEPT;\n        std::size_t bytes = 0;  // number of bytes written to string_buffer\n\n        // number of bytes written at the point of the last valid byte\n        std::size_t bytes_after_last_accept = 0;\n        std::size_t undumped_chars = 0;\n\n        for (std::size_t i = 0; i < s.size(); ++i)\n        {\n            const auto byte = static_cast<uint8_t>(s[i]);\n\n            switch (decode(state, codepoint, byte))\n            {\n                case UTF8_ACCEPT:  // decode found a new code point\n                {\n                    switch (codepoint)\n                    {\n                        case 0x08: // backspace\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'b';\n                            break;\n                        }\n\n                        case 0x09: // horizontal tab\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 't';\n                            break;\n                        }\n\n                        case 0x0A: // newline\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'n';\n                            break;\n                        }\n\n                        case 0x0C: // formfeed\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'f';\n                            break;\n                        }\n\n                        case 0x0D: // carriage return\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'r';\n                            break;\n                        }\n\n                        case 0x22: // quotation mark\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\"';\n                            break;\n                        }\n\n                        case 0x5C: // reverse solidus\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\\';\n                            break;\n                        }\n\n                        default:\n                        {\n                            // escape control characters (0x00..0x1F) or, if\n                            // ensure_ascii parameter is used, non-ASCII characters\n                            if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))\n                            {\n                                if (codepoint <= 0xFFFF)\n                                {\n                                    (std::snprintf)(string_buffer.data() + bytes, 7, \"\\\\u%04x\",\n                                                    static_cast<std::uint16_t>(codepoint));\n                                    bytes += 6;\n                                }\n                                else\n                                {\n                                    (std::snprintf)(string_buffer.data() + bytes, 13, \"\\\\u%04x\\\\u%04x\",\n                                                    static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),\n                                                    static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));\n                                    bytes += 12;\n                                }\n                            }\n                            else\n                            {\n                                // copy byte to buffer (all previous bytes\n                                // been copied have in default case above)\n                                string_buffer[bytes++] = s[i];\n                            }\n                            break;\n                        }\n                    }\n\n                    // write buffer and reset index; there must be 13 bytes\n                    // left, as this is the maximal number of bytes to be\n                    // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                    if (string_buffer.size() - bytes < 13)\n                    {\n                        o->write_characters(string_buffer.data(), bytes);\n                        bytes = 0;\n                    }\n\n                    // remember the byte position of this accept\n                    bytes_after_last_accept = bytes;\n                    undumped_chars = 0;\n                    break;\n                }\n\n                case UTF8_REJECT:  // decode found invalid UTF-8 byte\n                {\n                    switch (error_handler)\n                    {\n                        case error_handler_t::strict:\n                        {\n                            std::string sn(3, '\\0');\n                            (std::snprintf)(&sn[0], sn.size(), \"%.2X\", byte);\n                            JSON_THROW(type_error::create(316, \"invalid UTF-8 byte at index \" + std::to_string(i) + \": 0x\" + sn));\n                        }\n\n                        case error_handler_t::ignore:\n                        case error_handler_t::replace:\n                        {\n                            // in case we saw this character the first time, we\n                            // would like to read it again, because the byte\n                            // may be OK for itself, but just not OK for the\n                            // previous sequence\n                            if (undumped_chars > 0)\n                            {\n                                --i;\n                            }\n\n                            // reset length buffer to the last accepted index;\n                            // thus removing/ignoring the invalid characters\n                            bytes = bytes_after_last_accept;\n\n                            if (error_handler == error_handler_t::replace)\n                            {\n                                // add a replacement character\n                                if (ensure_ascii)\n                                {\n                                    string_buffer[bytes++] = '\\\\';\n                                    string_buffer[bytes++] = 'u';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'd';\n                                }\n                                else\n                                {\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xEF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBD');\n                                }\n\n                                // write buffer and reset index; there must be 13 bytes\n                                // left, as this is the maximal number of bytes to be\n                                // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                                if (string_buffer.size() - bytes < 13)\n                                {\n                                    o->write_characters(string_buffer.data(), bytes);\n                                    bytes = 0;\n                                }\n\n                                bytes_after_last_accept = bytes;\n                            }\n\n                            undumped_chars = 0;\n\n                            // continue processing the string\n                            state = UTF8_ACCEPT;\n                            break;\n                        }\n\n                        default:            // LCOV_EXCL_LINE\n                            JSON_ASSERT(false);  // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n\n                default:  // decode found yet incomplete multi-byte code point\n                {\n                    if (!ensure_ascii)\n                    {\n                        // code point will not be escaped - copy byte to buffer\n                        string_buffer[bytes++] = s[i];\n                    }\n                    ++undumped_chars;\n                    break;\n                }\n            }\n        }\n\n        // we finished processing the string\n        if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))\n        {\n            // write buffer\n            if (bytes > 0)\n            {\n                o->write_characters(string_buffer.data(), bytes);\n            }\n        }\n        else\n        {\n            // we finish reading, but do not accept: string was incomplete\n            switch (error_handler)\n            {\n                case error_handler_t::strict:\n                {\n                    std::string sn(3, '\\0');\n                    (std::snprintf)(&sn[0], sn.size(), \"%.2X\", static_cast<std::uint8_t>(s.back()));\n                    JSON_THROW(type_error::create(316, \"incomplete UTF-8 string; last byte: 0x\" + sn));\n                }\n\n                case error_handler_t::ignore:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    break;\n                }\n\n                case error_handler_t::replace:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    // add a replacement character\n                    if (ensure_ascii)\n                    {\n                        o->write_characters(\"\\\\ufffd\", 6);\n                    }\n                    else\n                    {\n                        o->write_characters(\"\\xEF\\xBF\\xBD\", 3);\n                    }\n                    break;\n                }\n\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false);  // LCOV_EXCL_LINE\n            }\n        }\n    }\n\n    /*!\n    @brief count digits\n\n    Count the number of decimal (base 10) digits for an input unsigned integer.\n\n    @param[in] x  unsigned integer number to count its digits\n    @return    number of decimal digits\n    */\n    inline unsigned int count_digits(number_unsigned_t x) noexcept\n    {\n        unsigned int n_digits = 1;\n        for (;;)\n        {\n            if (x < 10)\n            {\n                return n_digits;\n            }\n            if (x < 100)\n            {\n                return n_digits + 1;\n            }\n            if (x < 1000)\n            {\n                return n_digits + 2;\n            }\n            if (x < 10000)\n            {\n                return n_digits + 3;\n            }\n            x = x / 10000u;\n            n_digits += 4;\n        }\n    }\n\n    /*!\n    @brief dump an integer\n\n    Dump a given integer to output stream @a o. Works internally with\n    @a number_buffer.\n\n    @param[in] x  integer number (signed or unsigned) to dump\n    @tparam NumberType either @a number_integer_t or @a number_unsigned_t\n    */\n    template < typename NumberType, detail::enable_if_t <\n                   std::is_same<NumberType, number_unsigned_t>::value ||\n                   std::is_same<NumberType, number_integer_t>::value ||\n                   std::is_same<NumberType, binary_char_t>::value,\n                   int > = 0 >\n    void dump_integer(NumberType x)\n    {\n        static constexpr std::array<std::array<char, 2>, 100> digits_to_99\n        {\n            {\n                {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},\n                {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},\n                {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},\n                {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},\n                {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},\n                {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},\n                {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},\n                {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},\n                {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},\n                {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},\n            }\n        };\n\n        // special case for \"0\"\n        if (x == 0)\n        {\n            o->write_character('0');\n            return;\n        }\n\n        // use a pointer to fill the buffer\n        auto buffer_ptr = number_buffer.begin();\n\n        const bool is_negative = std::is_same<NumberType, number_integer_t>::value && !(x >= 0); // see issue #755\n        number_unsigned_t abs_value;\n\n        unsigned int n_chars;\n\n        if (is_negative)\n        {\n            *buffer_ptr = '-';\n            abs_value = remove_sign(static_cast<number_integer_t>(x));\n\n            // account one more byte for the minus sign\n            n_chars = 1 + count_digits(abs_value);\n        }\n        else\n        {\n            abs_value = static_cast<number_unsigned_t>(x);\n            n_chars = count_digits(abs_value);\n        }\n\n        // spare 1 byte for '\\0'\n        JSON_ASSERT(n_chars < number_buffer.size() - 1);\n\n        // jump to the end to generate the string from backward\n        // so we later avoid reversing the result\n        buffer_ptr += n_chars;\n\n        // Fast int2ascii implementation inspired by \"Fastware\" talk by Andrei Alexandrescu\n        // See: https://www.youtube.com/watch?v=o4-CwDo2zpg\n        while (abs_value >= 100)\n        {\n            const auto digits_index = static_cast<unsigned>((abs_value % 100));\n            abs_value /= 100;\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n\n        if (abs_value >= 10)\n        {\n            const auto digits_index = static_cast<unsigned>(abs_value);\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n        else\n        {\n            *(--buffer_ptr) = static_cast<char>('0' + abs_value);\n        }\n\n        o->write_characters(number_buffer.data(), n_chars);\n    }\n\n    /*!\n    @brief dump a floating-point number\n\n    Dump a given floating-point number to output stream @a o. Works internally\n    with @a number_buffer.\n\n    @param[in] x  floating-point number to dump\n    */\n    void dump_float(number_float_t x)\n    {\n        // NaN / inf\n        if (!std::isfinite(x))\n        {\n            o->write_characters(\"null\", 4);\n            return;\n        }\n\n        // If number_float_t is an IEEE-754 single or double precision number,\n        // use the Grisu2 algorithm to produce short numbers which are\n        // guaranteed to round-trip, using strtof and strtod, resp.\n        //\n        // NB: The test below works if <long double> == <double>.\n        static constexpr bool is_ieee_single_or_double\n            = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||\n              (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);\n\n        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());\n    }\n\n    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)\n    {\n        char* begin = number_buffer.data();\n        char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);\n\n        o->write_characters(begin, static_cast<size_t>(end - begin));\n    }\n\n    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)\n    {\n        // get number of digits for a float -> text -> float round-trip\n        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;\n\n        // the actual conversion\n        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), \"%.*g\", d, x);\n\n        // negative value indicates an error\n        JSON_ASSERT(len > 0);\n        // check if buffer was large enough\n        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());\n\n        // erase thousands separator\n        if (thousands_sep != '\\0')\n        {\n            const auto end = std::remove(number_buffer.begin(),\n                                         number_buffer.begin() + len, thousands_sep);\n            std::fill(end, number_buffer.end(), '\\0');\n            JSON_ASSERT((end - number_buffer.begin()) <= len);\n            len = (end - number_buffer.begin());\n        }\n\n        // convert decimal point to '.'\n        if (decimal_point != '\\0' && decimal_point != '.')\n        {\n            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);\n            if (dec_pos != number_buffer.end())\n            {\n                *dec_pos = '.';\n            }\n        }\n\n        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));\n\n        // determine if need to append \".0\"\n        const bool value_is_int_like =\n            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,\n                         [](char c)\n        {\n            return c == '.' || c == 'e';\n        });\n\n        if (value_is_int_like)\n        {\n            o->write_characters(\".0\", 2);\n        }\n    }\n\n    /*!\n    @brief check whether a string is UTF-8 encoded\n\n    The function checks each byte of a string whether it is UTF-8 encoded. The\n    result of the check is stored in the @a state parameter. The function must\n    be called initially with state 0 (accept). State 1 means the string must\n    be rejected, because the current byte is not allowed. If the string is\n    completely processed, but the state is non-zero, the string ended\n    prematurely; that is, the last byte indicated more bytes should have\n    followed.\n\n    @param[in,out] state  the state of the decoding\n    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)\n    @param[in] byte       next byte to decode\n    @return               new state\n\n    @note The function has been edited: a std::array is used.\n\n    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n    */\n    static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept\n    {\n        static const std::array<std::uint8_t, 400> utf8d =\n        {\n            {\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F\n                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF\n                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF\n                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF\n                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF\n                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2\n                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4\n                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6\n                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8\n            }\n        };\n\n        const std::uint8_t type = utf8d[byte];\n\n        codep = (state != UTF8_ACCEPT)\n                ? (byte & 0x3fu) | (codep << 6u)\n                : (0xFFu >> type) & (byte);\n\n        std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);\n        JSON_ASSERT(index < 400);\n        state = utf8d[index];\n        return state;\n    }\n\n    /*\n     * Overload to make the compiler happy while it is instantiating\n     * dump_integer for number_unsigned_t.\n     * Must never be called.\n     */\n    number_unsigned_t remove_sign(number_unsigned_t x)\n    {\n        JSON_ASSERT(false); // LCOV_EXCL_LINE\n        return x; // LCOV_EXCL_LINE\n    }\n\n    /*\n     * Helper function for dump_integer\n     *\n     * This function takes a negative signed integer and returns its absolute\n     * value as unsigned integer. The plus/minus shuffling is necessary as we can\n     * not directly remove the sign of an arbitrary signed integer as the\n     * absolute values of INT_MIN and INT_MAX are usually not the same. See\n     * #1708 for details.\n     */\n    inline number_unsigned_t remove_sign(number_integer_t x) noexcept\n    {\n        JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)());\n        return static_cast<number_unsigned_t>(-(x + 1)) + 1;\n    }\n\n  private:\n    /// the output of the serializer\n    output_adapter_t<char> o = nullptr;\n\n    /// a (hopefully) large enough character buffer\n    std::array<char, 64> number_buffer{{}};\n\n    /// the locale\n    const std::lconv* loc = nullptr;\n    /// the locale's thousand separator character\n    const char thousands_sep = '\\0';\n    /// the locale's decimal point character\n    const char decimal_point = '\\0';\n\n    /// string buffer\n    std::array<char, 512> string_buffer{{}};\n\n    /// the indentation character\n    const char indent_char;\n    /// the indentation string\n    string_t indent_string;\n\n    /// error_handler how to react on decoding errors\n    const error_handler_t error_handler;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n// #include <nlohmann/json_fwd.hpp>\n\n// #include <nlohmann/ordered_map.hpp>\n\n\n#include <functional> // less\n#include <memory> // allocator\n#include <utility> // pair\n#include <vector> // vector\n\nnamespace nlohmann\n{\n\n/// ordered_map: a minimal map-like container that preserves insertion order\n/// for use within nlohmann::basic_json<ordered_map>\ntemplate <class Key, class T, class IgnoredLess = std::less<Key>,\n          class Allocator = std::allocator<std::pair<const Key, T>>>\n                  struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>\n{\n    using key_type = Key;\n    using mapped_type = T;\n    using Container = std::vector<std::pair<const Key, T>, Allocator>;\n    using typename Container::iterator;\n    using typename Container::const_iterator;\n    using typename Container::size_type;\n    using typename Container::value_type;\n\n    // Explicit constructors instead of `using Container::Container`\n    // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)\n    ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {}\n    template <class It>\n    ordered_map(It first, It last, const Allocator& alloc = Allocator())\n        : Container{first, last, alloc} {}\n    ordered_map(std::initializer_list<T> init, const Allocator& alloc = Allocator() )\n        : Container{init, alloc} {}\n\n    std::pair<iterator, bool> emplace(const key_type& key, T&& t)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return {it, false};\n            }\n        }\n        Container::emplace_back(key, t);\n        return {--this->end(), true};\n    }\n\n    T& operator[](const Key& key)\n    {\n        return emplace(key, T{}).first->second;\n    }\n\n    const T& operator[](const Key& key) const\n    {\n        return at(key);\n    }\n\n    T& at(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it->second;\n            }\n        }\n\n        throw std::out_of_range(\"key not found\");\n    }\n\n    const T& at(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it->second;\n            }\n        }\n\n        throw std::out_of_range(\"key not found\");\n    }\n\n    size_type erase(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                // Since we cannot move const Keys, re-construct them in place\n                for (auto next = it; ++next != this->end(); ++it)\n                {\n                    it->~value_type(); // Destroy but keep allocation\n                    new (&*it) value_type{std::move(*next)};\n                }\n                Container::pop_back();\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    iterator erase(iterator pos)\n    {\n        auto it = pos;\n\n        // Since we cannot move const Keys, re-construct them in place\n        for (auto next = it; ++next != this->end(); ++it)\n        {\n            it->~value_type(); // Destroy but keep allocation\n            new (&*it) value_type{std::move(*next)};\n        }\n        Container::pop_back();\n        return pos;\n    }\n\n    size_type count(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    iterator find(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    const_iterator find(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    std::pair<iterator, bool> insert( value_type&& value )\n    {\n        return emplace(value.first, std::move(value.second));\n    }\n\n    std::pair<iterator, bool> insert( const value_type& value )\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == value.first)\n            {\n                return {it, false};\n            }\n        }\n        Container::push_back(value);\n        return {--this->end(), true};\n    }\n};\n\n}  // namespace nlohmann\n\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n\n/*!\n@brief a class to store JSON values\n\n@tparam ObjectType type for JSON objects (`std::map` by default; will be used\nin @ref object_t)\n@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used\nin @ref array_t)\n@tparam StringType type for JSON strings and object keys (`std::string` by\ndefault; will be used in @ref string_t)\n@tparam BooleanType type for JSON booleans (`bool` by default; will be used\nin @ref boolean_t)\n@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by\ndefault; will be used in @ref number_integer_t)\n@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c\n`uint64_t` by default; will be used in @ref number_unsigned_t)\n@tparam NumberFloatType type for JSON floating-point numbers (`double` by\ndefault; will be used in @ref number_float_t)\n@tparam BinaryType type for packed binary data for compatibility with binary\nserialization formats (`std::vector<std::uint8_t>` by default; will be used in\n@ref binary_t)\n@tparam AllocatorType type of the allocator to use (`std::allocator` by\ndefault)\n@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`\nand `from_json()` (@ref adl_serializer by default)\n\n@requirement The class satisfies the following concept requirements:\n- Basic\n - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible):\n   JSON values can be default constructed. The result will be a JSON null\n   value.\n - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible):\n   A JSON value can be constructed from an rvalue argument.\n - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible):\n   A JSON value can be copy-constructed from an lvalue expression.\n - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable):\n   A JSON value van be assigned from an rvalue argument.\n - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable):\n   A JSON value can be copy-assigned from an lvalue expression.\n - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible):\n   JSON values can be destructed.\n- Layout\n - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType):\n   JSON values have\n   [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout):\n   All non-static data members are private and standard layout types, the\n   class has no virtual functions or (virtual) base classes.\n- Library-wide\n - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable):\n   JSON values can be compared with `==`, see @ref\n   operator==(const_reference,const_reference).\n - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable):\n   JSON values can be compared with `<`, see @ref\n   operator<(const_reference,const_reference).\n - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable):\n   Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of\n   other compatible types, using unqualified function call @ref swap().\n - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer):\n   JSON values can be compared against `std::nullptr_t` objects which are used\n   to model the `null` value.\n- Container\n - [Container](https://en.cppreference.com/w/cpp/named_req/Container):\n   JSON values can be used like STL containers and provide iterator access.\n - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer);\n   JSON values can be used like STL containers and provide reverse iterator\n   access.\n\n@invariant The member variables @a m_value and @a m_type have the following\nrelationship:\n- If `m_type == value_t::object`, then `m_value.object != nullptr`.\n- If `m_type == value_t::array`, then `m_value.array != nullptr`.\n- If `m_type == value_t::string`, then `m_value.string != nullptr`.\nThe invariants are checked by member function assert_invariant().\n\n@internal\n@note ObjectType trick from https://stackoverflow.com/a/9860911\n@endinternal\n\n@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange\nFormat](http://rfc7159.net/rfc7159)\n\n@since version 1.0.0\n\n@nosubgrouping\n*/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nclass basic_json\n{\n  private:\n    template<detail::value_t> friend struct detail::external_constructor;\n    friend ::nlohmann::json_pointer<basic_json>;\n\n    template<typename BasicJsonType, typename InputType>\n    friend class ::nlohmann::detail::parser;\n    friend ::nlohmann::detail::serializer<basic_json>;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::iter_impl;\n    template<typename BasicJsonType, typename CharType>\n    friend class ::nlohmann::detail::binary_writer;\n    template<typename BasicJsonType, typename InputType, typename SAX>\n    friend class ::nlohmann::detail::binary_reader;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_parser;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_callback_parser;\n\n    /// workaround type for MSVC\n    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;\n\n    // convenience aliases for types residing in namespace detail;\n    using lexer = ::nlohmann::detail::lexer_base<basic_json>;\n\n    template<typename InputAdapterType>\n    static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(\n        InputAdapterType adapter,\n        detail::parser_callback_t<basic_json>cb = nullptr,\n        const bool allow_exceptions = true,\n        const bool ignore_comments = false\n                                 )\n    {\n        return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),\n                std::move(cb), allow_exceptions, ignore_comments);\n    }\n\n    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;\n    template<typename BasicJsonType>\n    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;\n    template<typename BasicJsonType>\n    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;\n    template<typename Iterator>\n    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;\n    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;\n\n    template<typename CharType>\n    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;\n\n    template<typename InputType>\n    using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;\n    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;\n\n    using serializer = ::nlohmann::detail::serializer<basic_json>;\n\n  public:\n    using value_t = detail::value_t;\n    /// JSON Pointer, see @ref nlohmann::json_pointer\n    using json_pointer = ::nlohmann::json_pointer<basic_json>;\n    template<typename T, typename SFINAE>\n    using json_serializer = JSONSerializer<T, SFINAE>;\n    /// how to treat decoding errors\n    using error_handler_t = detail::error_handler_t;\n    /// how to treat CBOR tags\n    using cbor_tag_handler_t = detail::cbor_tag_handler_t;\n    /// helper type for initializer lists of basic_json values\n    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;\n\n    using input_format_t = detail::input_format_t;\n    /// SAX interface type, see @ref nlohmann::json_sax\n    using json_sax_t = json_sax<basic_json>;\n\n    ////////////////\n    // exceptions //\n    ////////////////\n\n    /// @name exceptions\n    /// Classes to implement user-defined exceptions.\n    /// @{\n\n    /// @copydoc detail::exception\n    using exception = detail::exception;\n    /// @copydoc detail::parse_error\n    using parse_error = detail::parse_error;\n    /// @copydoc detail::invalid_iterator\n    using invalid_iterator = detail::invalid_iterator;\n    /// @copydoc detail::type_error\n    using type_error = detail::type_error;\n    /// @copydoc detail::out_of_range\n    using out_of_range = detail::out_of_range;\n    /// @copydoc detail::other_error\n    using other_error = detail::other_error;\n\n    /// @}\n\n\n    /////////////////////\n    // container types //\n    /////////////////////\n\n    /// @name container types\n    /// The canonic container types to use @ref basic_json like any other STL\n    /// container.\n    /// @{\n\n    /// the type of elements in a basic_json container\n    using value_type = basic_json;\n\n    /// the type of an element reference\n    using reference = value_type&;\n    /// the type of an element const reference\n    using const_reference = const value_type&;\n\n    /// a type to represent differences between iterators\n    using difference_type = std::ptrdiff_t;\n    /// a type to represent container sizes\n    using size_type = std::size_t;\n\n    /// the allocator type\n    using allocator_type = AllocatorType<basic_json>;\n\n    /// the type of an element pointer\n    using pointer = typename std::allocator_traits<allocator_type>::pointer;\n    /// the type of an element const pointer\n    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;\n\n    /// an iterator for a basic_json container\n    using iterator = iter_impl<basic_json>;\n    /// a const iterator for a basic_json container\n    using const_iterator = iter_impl<const basic_json>;\n    /// a reverse iterator for a basic_json container\n    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;\n    /// a const reverse iterator for a basic_json container\n    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;\n\n    /// @}\n\n\n    /*!\n    @brief returns the allocator associated with the container\n    */\n    static allocator_type get_allocator()\n    {\n        return allocator_type();\n    }\n\n    /*!\n    @brief returns version information on the library\n\n    This function returns a JSON object with information about the library,\n    including the version number and information on the platform and compiler.\n\n    @return JSON object holding version information\n    key         | description\n    ----------- | ---------------\n    `compiler`  | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).\n    `copyright` | The copyright line for the library as string.\n    `name`      | The name of the library as string.\n    `platform`  | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.\n    `url`       | The URL of the project as string.\n    `version`   | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).\n\n    @liveexample{The following code shows an example output of the `meta()`\n    function.,meta}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @complexity Constant.\n\n    @since 2.1.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json meta()\n    {\n        basic_json result;\n\n        result[\"copyright\"] = \"(C) 2013-2020 Niels Lohmann\";\n        result[\"name\"] = \"JSON for Modern C++\";\n        result[\"url\"] = \"https://github.com/nlohmann/json\";\n        result[\"version\"][\"string\"] =\n            std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_MINOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_PATCH);\n        result[\"version\"][\"major\"] = NLOHMANN_JSON_VERSION_MAJOR;\n        result[\"version\"][\"minor\"] = NLOHMANN_JSON_VERSION_MINOR;\n        result[\"version\"][\"patch\"] = NLOHMANN_JSON_VERSION_PATCH;\n\n#ifdef _WIN32\n        result[\"platform\"] = \"win32\";\n#elif defined __linux__\n        result[\"platform\"] = \"linux\";\n#elif defined __APPLE__\n        result[\"platform\"] = \"apple\";\n#elif defined __unix__\n        result[\"platform\"] = \"unix\";\n#else\n        result[\"platform\"] = \"unknown\";\n#endif\n\n#if defined(__ICC) || defined(__INTEL_COMPILER)\n        result[\"compiler\"] = {{\"family\", \"icc\"}, {\"version\", __INTEL_COMPILER}};\n#elif defined(__clang__)\n        result[\"compiler\"] = {{\"family\", \"clang\"}, {\"version\", __clang_version__}};\n#elif defined(__GNUC__) || defined(__GNUG__)\n        result[\"compiler\"] = {{\"family\", \"gcc\"}, {\"version\", std::to_string(__GNUC__) + \".\" + std::to_string(__GNUC_MINOR__) + \".\" + std::to_string(__GNUC_PATCHLEVEL__)}};\n#elif defined(__HP_cc) || defined(__HP_aCC)\n        result[\"compiler\"] = \"hp\"\n#elif defined(__IBMCPP__)\n        result[\"compiler\"] = {{\"family\", \"ilecpp\"}, {\"version\", __IBMCPP__}};\n#elif defined(_MSC_VER)\n        result[\"compiler\"] = {{\"family\", \"msvc\"}, {\"version\", _MSC_VER}};\n#elif defined(__PGI)\n        result[\"compiler\"] = {{\"family\", \"pgcpp\"}, {\"version\", __PGI}};\n#elif defined(__SUNPRO_CC)\n        result[\"compiler\"] = {{\"family\", \"sunpro\"}, {\"version\", __SUNPRO_CC}};\n#else\n        result[\"compiler\"] = {{\"family\", \"unknown\"}, {\"version\", \"unknown\"}};\n#endif\n\n#ifdef __cplusplus\n        result[\"compiler\"][\"c++\"] = std::to_string(__cplusplus);\n#else\n        result[\"compiler\"][\"c++\"] = \"unknown\";\n#endif\n        return result;\n    }\n\n\n    ///////////////////////////\n    // JSON value data types //\n    ///////////////////////////\n\n    /// @name JSON value data types\n    /// The data types to store a JSON value. These types are derived from\n    /// the template arguments passed to class @ref basic_json.\n    /// @{\n\n#if defined(JSON_HAS_CPP_14)\n    // Use transparent comparator if possible, combined with perfect forwarding\n    // on find() and count() calls prevents unnecessary string construction.\n    using object_comparator_t = std::less<>;\n#else\n    using object_comparator_t = std::less<StringType>;\n#endif\n\n    /*!\n    @brief a type for an object\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows:\n    > An object is an unordered collection of zero or more name/value pairs,\n    > where a name is a string and a value is a string, number, boolean, null,\n    > object, or array.\n\n    To store objects in C++, a type is defined by the template parameters\n    described below.\n\n    @tparam ObjectType  the container to store objects (e.g., `std::map` or\n    `std::unordered_map`)\n    @tparam StringType the type of the keys or names (e.g., `std::string`).\n    The comparison function `std::less<StringType>` is used to order elements\n    inside the container.\n    @tparam AllocatorType the allocator to use for objects (e.g.,\n    `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ObjectType (`std::map`), @a StringType\n    (`std::string`), and @a AllocatorType (`std::allocator`), the default\n    value for @a object_t is:\n\n    @code {.cpp}\n    std::map<\n      std::string, // key_type\n      basic_json, // value_type\n      std::less<std::string>, // key_compare\n      std::allocator<std::pair<const std::string, basic_json>> // allocator_type\n    >\n    @endcode\n\n    #### Behavior\n\n    The choice of @a object_t influences the behavior of the JSON class. With\n    the default type, objects have the following behavior:\n\n    - When all names are unique, objects will be interoperable in the sense\n      that all software implementations receiving that object will agree on\n      the name-value mappings.\n    - When the names within an object are not unique, it is unspecified which\n      one of the values for a given key will be chosen. For instance,\n      `{\"key\": 2, \"key\": 1}` could be equal to either `{\"key\": 1}` or\n      `{\"key\": 2}`.\n    - Internally, name/value pairs are stored in lexicographical order of the\n      names. Objects will also be serialized (see @ref dump) in this order.\n      For instance, `{\"b\": 1, \"a\": 2}` and `{\"a\": 2, \"b\": 1}` will be stored\n      and serialized as `{\"a\": 2, \"b\": 1}`.\n    - When comparing objects, the order of the name/value pairs is irrelevant.\n      This makes objects interoperable in the sense that they will not be\n      affected by these differences. For instance, `{\"b\": 1, \"a\": 2}` and\n      `{\"a\": 2, \"b\": 1}` will be treated as equal.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the object's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON object.\n\n    #### Storage\n\n    Objects are stored as pointers in a @ref basic_json type. That is, for any\n    access to object values, a pointer of type `object_t*` must be\n    dereferenced.\n\n    @sa @ref array_t -- type for an array value\n\n    @since version 1.0.0\n\n    @note The order name/value pairs are added to the object is *not*\n    preserved by the library. Therefore, iterating an object may return\n    name/value pairs in a different order than they were originally stored. In\n    fact, keys will be traversed in alphabetical order as `std::map` with\n    `std::less` is used by default. Please note this behavior conforms to [RFC\n    7159](http://rfc7159.net/rfc7159), because any order implements the\n    specified \"unordered\" nature of JSON objects.\n    */\n    using object_t = ObjectType<StringType,\n          basic_json,\n          object_comparator_t,\n          AllocatorType<std::pair<const StringType,\n          basic_json>>>;\n\n    /*!\n    @brief a type for an array\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows:\n    > An array is an ordered sequence of zero or more values.\n\n    To store objects in C++, a type is defined by the template parameters\n    explained below.\n\n    @tparam ArrayType  container type to store arrays (e.g., `std::vector` or\n    `std::list`)\n    @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ArrayType (`std::vector`) and @a\n    AllocatorType (`std::allocator`), the default value for @a array_t is:\n\n    @code {.cpp}\n    std::vector<\n      basic_json, // value_type\n      std::allocator<basic_json> // allocator_type\n    >\n    @endcode\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the array's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON array.\n\n    #### Storage\n\n    Arrays are stored as pointers in a @ref basic_json type. That is, for any\n    access to array values, a pointer of type `array_t*` must be dereferenced.\n\n    @sa @ref object_t -- type for an object value\n\n    @since version 1.0.0\n    */\n    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;\n\n    /*!\n    @brief a type for a string\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows:\n    > A string is a sequence of zero or more Unicode characters.\n\n    To store objects in C++, a type is defined by the template parameter\n    described below. Unicode values are split by the JSON class into\n    byte-sized characters during deserialization.\n\n    @tparam StringType  the container to store strings (e.g., `std::string`).\n    Note this container is used for keys/names in objects, see @ref object_t.\n\n    #### Default type\n\n    With the default values for @a StringType (`std::string`), the default\n    value for @a string_t is:\n\n    @code {.cpp}\n    std::string\n    @endcode\n\n    #### Encoding\n\n    Strings are stored in UTF-8 encoding. Therefore, functions like\n    `std::string::size()` or `std::string::length()` return the number of\n    bytes in the string rather than the number of characters or glyphs.\n\n    #### String comparison\n\n    [RFC 7159](http://rfc7159.net/rfc7159) states:\n    > Software implementations are typically required to test names of object\n    > members for equality. Implementations that transform the textual\n    > representation into sequences of Unicode code units and then perform the\n    > comparison numerically, code unit by code unit, are interoperable in the\n    > sense that implementations will agree in all cases on equality or\n    > inequality of two strings. For example, implementations that compare\n    > strings with escaped characters unconverted may incorrectly find that\n    > `\"a\\\\b\"` and `\"a\\u005Cb\"` are not equal.\n\n    This implementation is interoperable as it does compare strings code unit\n    by code unit.\n\n    #### Storage\n\n    String values are stored as pointers in a @ref basic_json type. That is,\n    for any access to string values, a pointer of type `string_t*` must be\n    dereferenced.\n\n    @since version 1.0.0\n    */\n    using string_t = StringType;\n\n    /*!\n    @brief a type for a boolean\n\n    [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a\n    type which differentiates the two literals `true` and `false`.\n\n    To store objects in C++, a type is defined by the template parameter @a\n    BooleanType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a BooleanType (`bool`), the default value for\n    @a boolean_t is:\n\n    @code {.cpp}\n    bool\n    @endcode\n\n    #### Storage\n\n    Boolean values are stored directly inside a @ref basic_json type.\n\n    @since version 1.0.0\n    */\n    using boolean_t = BooleanType;\n\n    /*!\n    @brief a type for a number (integer)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store integer numbers in C++, a type is defined by the template\n    parameter @a NumberIntegerType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberIntegerType (`int64_t`), the default\n    value for @a number_integer_t is:\n\n    @code {.cpp}\n    int64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `9223372036854775807` (INT64_MAX) and the minimal integer number\n    that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers\n    that are out of range will yield over/underflow when used in a\n    constructor. During deserialization, too large or small integer numbers\n    will be automatically be stored as @ref number_unsigned_t or @ref\n    number_float_t.\n\n    [RFC 7159](http://rfc7159.net/rfc7159) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange of the exactly supported range [INT64_MIN,\n    INT64_MAX], this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa @ref number_float_t -- type for number values (floating-point)\n\n    @sa @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_integer_t = NumberIntegerType;\n\n    /*!\n    @brief a type for a number (unsigned)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store unsigned integer numbers in C++, a type is defined by the\n    template parameter @a NumberUnsignedType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberUnsignedType (`uint64_t`), the\n    default value for @a number_unsigned_t is:\n\n    @code {.cpp}\n    uint64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `18446744073709551615` (UINT64_MAX) and the minimal integer\n    number that can be stored is `0`. Integer numbers that are out of range\n    will yield over/underflow when used in a constructor. During\n    deserialization, too large or small integer numbers will be automatically\n    be stored as @ref number_integer_t or @ref number_float_t.\n\n    [RFC 7159](http://rfc7159.net/rfc7159) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange (when considered in conjunction with the\n    number_integer_t type) of the exactly supported range [0, UINT64_MAX],\n    this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa @ref number_float_t -- type for number values (floating-point)\n    @sa @ref number_integer_t -- type for number values (integer)\n\n    @since version 2.0.0\n    */\n    using number_unsigned_t = NumberUnsignedType;\n\n    /*!\n    @brief a type for a number (floating-point)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store floating-point numbers in C++, a type is defined by the template\n    parameter @a NumberFloatType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberFloatType (`double`), the default\n    value for @a number_float_t is:\n\n    @code {.cpp}\n    double\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in floating-point literals will be ignored. Internally,\n      the value will be stored as decimal number. For instance, the C++\n      floating-point literal `01.2` will be serialized to `1.2`. During\n      deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) states:\n    > This specification allows implementations to set limits on the range and\n    > precision of numbers accepted. Since software that implements IEEE\n    > 754-2008 binary64 (double precision) numbers is generally available and\n    > widely used, good interoperability can be achieved by implementations\n    > that expect no more precision or range than these provide, in the sense\n    > that implementations will approximate JSON numbers within the expected\n    > precision.\n\n    This implementation does exactly follow this approach, as it uses double\n    precision floating-point numbers. Note values smaller than\n    `-1.79769313486232e+308` and values greater than `1.79769313486232e+308`\n    will be stored as NaN internally and be serialized to `null`.\n\n    #### Storage\n\n    Floating-point number values are stored directly inside a @ref basic_json\n    type.\n\n    @sa @ref number_integer_t -- type for number values (integer)\n\n    @sa @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_float_t = NumberFloatType;\n\n    /*!\n    @brief a type for a packed binary type\n\n    This type is a type designed to carry binary data that appears in various\n    serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and\n    BSON's generic binary subtype. This type is NOT a part of standard JSON and\n    exists solely for compatibility with these binary types. As such, it is\n    simply defined as an ordered sequence of zero or more byte values.\n\n    Additionally, as an implementation detail, the subtype of the binary data is\n    carried around as a `std::uint8_t`, which is compatible with both of the\n    binary data formats that use binary subtyping, (though the specific\n    numbering is incompatible with each other, and it is up to the user to\n    translate between them).\n\n    [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type\n    as:\n    > Major type 2: a byte string. The string's length in bytes is represented\n    > following the rules for positive integers (major type 0).\n\n    [MessagePack's documentation on the bin type\n    family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family)\n    describes this type as:\n    > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes\n    > in addition to the size of the byte array.\n\n    [BSON's specifications](http://bsonspec.org/spec.html) describe several\n    binary types; however, this type is intended to represent the generic binary\n    type which has the description:\n    > Generic binary subtype - This is the most commonly used binary subtype and\n    > should be the 'default' for drivers and tools.\n\n    None of these impose any limitations on the internal representation other\n    than the basic unit of storage be some type of array whose parts are\n    decomposable into bytes.\n\n    The default representation of this binary format is a\n    `std::vector<std::uint8_t>`, which is a very common way to represent a byte\n    array in modern C++.\n\n    #### Default type\n\n    The default values for @a BinaryType is `std::vector<std::uint8_t>`\n\n    #### Storage\n\n    Binary Arrays are stored as pointers in a @ref basic_json type. That is,\n    for any access to array values, a pointer of the type `binary_t*` must be\n    dereferenced.\n\n    #### Notes on subtypes\n\n    - CBOR\n       - Binary values are represented as byte strings. No subtypes are\n         supported and will be ignored when CBOR is written.\n    - MessagePack\n       - If a subtype is given and the binary array contains exactly 1, 2, 4, 8,\n         or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8)\n         is used. For other sizes, the ext family (ext8, ext16, ext32) is used.\n         The subtype is then added as singed 8-bit integer.\n       - If no subtype is given, the bin family (bin8, bin16, bin32) is used.\n    - BSON\n       - If a subtype is given, it is used and added as unsigned 8-bit integer.\n       - If no subtype is given, the generic binary subtype 0x00 is used.\n\n    @sa @ref binary -- create a binary array\n\n    @since version 3.8.0\n    */\n    using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;\n    /// @}\n\n  private:\n\n    /// helper for exception-safe object creation\n    template<typename T, typename... Args>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    static T* create(Args&& ... args)\n    {\n        AllocatorType<T> alloc;\n        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;\n\n        auto deleter = [&](T * object)\n        {\n            AllocatorTraits::deallocate(alloc, object, 1);\n        };\n        std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);\n        AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);\n        JSON_ASSERT(object != nullptr);\n        return object.release();\n    }\n\n    ////////////////////////\n    // JSON value storage //\n    ////////////////////////\n\n    /*!\n    @brief a JSON value\n\n    The actual storage for a JSON value of the @ref basic_json class. This\n    union combines the different storage types for the JSON value types\n    defined in @ref value_t.\n\n    JSON type | value_t type    | used type\n    --------- | --------------- | ------------------------\n    object    | object          | pointer to @ref object_t\n    array     | array           | pointer to @ref array_t\n    string    | string          | pointer to @ref string_t\n    boolean   | boolean         | @ref boolean_t\n    number    | number_integer  | @ref number_integer_t\n    number    | number_unsigned | @ref number_unsigned_t\n    number    | number_float    | @ref number_float_t\n    binary    | binary          | pointer to @ref binary_t\n    null      | null            | *no value is stored*\n\n    @note Variable-length types (objects, arrays, and strings) are stored as\n    pointers. The size of the union should not exceed 64 bits if the default\n    value types are used.\n\n    @since version 1.0.0\n    */\n    union json_value\n    {\n        /// object (stored with pointer to save storage)\n        object_t* object;\n        /// array (stored with pointer to save storage)\n        array_t* array;\n        /// string (stored with pointer to save storage)\n        string_t* string;\n        /// binary (stored with pointer to save storage)\n        binary_t* binary;\n        /// boolean\n        boolean_t boolean;\n        /// number (integer)\n        number_integer_t number_integer;\n        /// number (unsigned integer)\n        number_unsigned_t number_unsigned;\n        /// number (floating-point)\n        number_float_t number_float;\n\n        /// default constructor (for null values)\n        json_value() = default;\n        /// constructor for booleans\n        json_value(boolean_t v) noexcept : boolean(v) {}\n        /// constructor for numbers (integer)\n        json_value(number_integer_t v) noexcept : number_integer(v) {}\n        /// constructor for numbers (unsigned)\n        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}\n        /// constructor for numbers (floating-point)\n        json_value(number_float_t v) noexcept : number_float(v) {}\n        /// constructor for empty values of a given type\n        json_value(value_t t)\n        {\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    object = create<object_t>();\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    array = create<array_t>();\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    string = create<string_t>(\"\");\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    binary = create<binary_t>();\n                    break;\n                }\n\n                case value_t::boolean:\n                {\n                    boolean = boolean_t(false);\n                    break;\n                }\n\n                case value_t::number_integer:\n                {\n                    number_integer = number_integer_t(0);\n                    break;\n                }\n\n                case value_t::number_unsigned:\n                {\n                    number_unsigned = number_unsigned_t(0);\n                    break;\n                }\n\n                case value_t::number_float:\n                {\n                    number_float = number_float_t(0.0);\n                    break;\n                }\n\n                case value_t::null:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    break;\n                }\n\n                default:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    if (JSON_HEDLEY_UNLIKELY(t == value_t::null))\n                    {\n                        JSON_THROW(other_error::create(500, \"961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1\")); // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n            }\n        }\n\n        /// constructor for strings\n        json_value(const string_t& value)\n        {\n            string = create<string_t>(value);\n        }\n\n        /// constructor for rvalue strings\n        json_value(string_t&& value)\n        {\n            string = create<string_t>(std::move(value));\n        }\n\n        /// constructor for objects\n        json_value(const object_t& value)\n        {\n            object = create<object_t>(value);\n        }\n\n        /// constructor for rvalue objects\n        json_value(object_t&& value)\n        {\n            object = create<object_t>(std::move(value));\n        }\n\n        /// constructor for arrays\n        json_value(const array_t& value)\n        {\n            array = create<array_t>(value);\n        }\n\n        /// constructor for rvalue arrays\n        json_value(array_t&& value)\n        {\n            array = create<array_t>(std::move(value));\n        }\n\n        /// constructor for binary arrays\n        json_value(const typename binary_t::container_type& value)\n        {\n            binary = create<binary_t>(value);\n        }\n\n        /// constructor for rvalue binary arrays\n        json_value(typename binary_t::container_type&& value)\n        {\n            binary = create<binary_t>(std::move(value));\n        }\n\n        /// constructor for binary arrays (internal type)\n        json_value(const binary_t& value)\n        {\n            binary = create<binary_t>(value);\n        }\n\n        /// constructor for rvalue binary arrays (internal type)\n        json_value(binary_t&& value)\n        {\n            binary = create<binary_t>(std::move(value));\n        }\n\n        void destroy(value_t t) noexcept\n        {\n            // flatten the current json_value to a heap-allocated stack\n            std::vector<basic_json> stack;\n\n            // move the top-level items to stack\n            if (t == value_t::array)\n            {\n                stack.reserve(array->size());\n                std::move(array->begin(), array->end(), std::back_inserter(stack));\n            }\n            else if (t == value_t::object)\n            {\n                stack.reserve(object->size());\n                for (auto&& it : *object)\n                {\n                    stack.push_back(std::move(it.second));\n                }\n            }\n\n            while (!stack.empty())\n            {\n                // move the last item to local variable to be processed\n                basic_json current_item(std::move(stack.back()));\n                stack.pop_back();\n\n                // if current_item is array/object, move\n                // its children to the stack to be processed later\n                if (current_item.is_array())\n                {\n                    std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(),\n                              std::back_inserter(stack));\n\n                    current_item.m_value.array->clear();\n                }\n                else if (current_item.is_object())\n                {\n                    for (auto&& it : *current_item.m_value.object)\n                    {\n                        stack.push_back(std::move(it.second));\n                    }\n\n                    current_item.m_value.object->clear();\n                }\n\n                // it's now safe that current_item get destructed\n                // since it doesn't have any children\n            }\n\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    AllocatorType<object_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    AllocatorType<array_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);\n                    break;\n                }\n\n                default:\n                {\n                    break;\n                }\n            }\n        }\n    };\n\n    /*!\n    @brief checks the class invariants\n\n    This function asserts the class invariants. It needs to be called at the\n    end of every constructor to make sure that created objects respect the\n    invariant. Furthermore, it has to be called each time the type of a JSON\n    value is changed, because the invariant expresses a relationship between\n    @a m_type and @a m_value.\n    */\n    void assert_invariant() const noexcept\n    {\n        JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);\n        JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);\n        JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);\n        JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);\n    }\n\n  public:\n    //////////////////////////\n    // JSON parser callback //\n    //////////////////////////\n\n    /*!\n    @brief parser event types\n\n    The parser callback distinguishes the following events:\n    - `object_start`: the parser read `{` and started to process a JSON object\n    - `key`: the parser read a key of a value in an object\n    - `object_end`: the parser read `}` and finished processing a JSON object\n    - `array_start`: the parser read `[` and started to process a JSON array\n    - `array_end`: the parser read `]` and finished processing a JSON array\n    - `value`: the parser finished reading a JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    @sa @ref parser_callback_t for more information and examples\n    */\n    using parse_event_t = detail::parse_event_t;\n\n    /*!\n    @brief per-element parser callback type\n\n    With a parser callback function, the result of parsing a JSON text can be\n    influenced. When passed to @ref parse, it is called on certain events\n    (passed as @ref parse_event_t via parameter @a event) with a set recursion\n    depth @a depth and context JSON value @a parsed. The return value of the\n    callback function is a boolean indicating whether the element that emitted\n    the callback shall be kept or not.\n\n    We distinguish six scenarios (determined by the event type) in which the\n    callback function can be called. The following table describes the values\n    of the parameters @a depth, @a event, and @a parsed.\n\n    parameter @a event | description | parameter @a depth | parameter @a parsed\n    ------------------ | ----------- | ------------------ | -------------------\n    parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded\n    parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key\n    parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object\n    parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded\n    parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array\n    parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    Discarding a value (i.e., returning `false`) has different effects\n    depending on the context in which function was called:\n\n    - Discarded values in structured types are skipped. That is, the parser\n      will behave as if the discarded value was never read.\n    - In case a value outside a structured type is skipped, it is replaced\n      with `null`. This case happens if the top-level element is skipped.\n\n    @param[in] depth  the depth of the recursion during parsing\n\n    @param[in] event  an event of type parse_event_t indicating the context in\n    the callback function has been called\n\n    @param[in,out] parsed  the current intermediate parse result; note that\n    writing to this value has no effect for parse_event_t::key events\n\n    @return Whether the JSON value which called the function during parsing\n    should be kept (`true`) or not (`false`). In the latter case, it is either\n    skipped completely or replaced by an empty discarded object.\n\n    @sa @ref parse for examples\n\n    @since version 1.0.0\n    */\n    using parser_callback_t = detail::parser_callback_t<basic_json>;\n\n    //////////////////\n    // constructors //\n    //////////////////\n\n    /// @name constructors and destructors\n    /// Constructors of class @ref basic_json, copy/move constructor, copy\n    /// assignment, static functions creating objects, and the destructor.\n    /// @{\n\n    /*!\n    @brief create an empty value with a given type\n\n    Create an empty JSON value with a given type. The value will be default\n    initialized with an empty value which depends on the type:\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    object      | `{}`\n    array       | `[]`\n    binary      | empty array\n\n    @param[in] v  the type of the value to create\n\n    @complexity Constant.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows the constructor for different @ref\n    value_t values,basic_json__value_t}\n\n    @sa @ref clear() -- restores the postcondition of this constructor\n\n    @since version 1.0.0\n    */\n    basic_json(const value_t v)\n        : m_type(v), m_value(v)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a null object\n\n    Create a `null` JSON value. It either takes a null pointer as parameter\n    (explicitly creating `null`) or no parameter (implicitly creating `null`).\n    The passed null pointer itself is not read -- it is only used to choose\n    the right constructor.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @liveexample{The following code shows the constructor with and without a\n    null pointer parameter.,basic_json__nullptr_t}\n\n    @since version 1.0.0\n    */\n    basic_json(std::nullptr_t = nullptr) noexcept\n        : basic_json(value_t::null)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value\n\n    This is a \"catch all\" constructor for all compatible JSON types; that is,\n    types for which a `to_json()` method exists. The constructor forwards the\n    parameter @a val to that method (to `json_serializer<U>::to_json` method\n    with `U = uncvref_t<CompatibleType>`, to be exact).\n\n    Template type @a CompatibleType includes, but is not limited to, the\n    following types:\n    - **arrays**: @ref array_t and all kinds of compatible containers such as\n      `std::vector`, `std::deque`, `std::list`, `std::forward_list`,\n      `std::array`, `std::valarray`, `std::set`, `std::unordered_set`,\n      `std::multiset`, and `std::unordered_multiset` with a `value_type` from\n      which a @ref basic_json value can be constructed.\n    - **objects**: @ref object_t and all kinds of compatible associative\n      containers such as `std::map`, `std::unordered_map`, `std::multimap`,\n      and `std::unordered_multimap` with a `key_type` compatible to\n      @ref string_t and a `value_type` from which a @ref basic_json value can\n      be constructed.\n    - **strings**: @ref string_t, string literals, and all compatible string\n      containers can be used.\n    - **numbers**: @ref number_integer_t, @ref number_unsigned_t,\n      @ref number_float_t, and all convertible number types such as `int`,\n      `size_t`, `int64_t`, `float` or `double` can be used.\n    - **boolean**: @ref boolean_t / `bool` can be used.\n    - **binary**: @ref binary_t / `std::vector<uint8_t>` may be used,\n      unfortunately because string literals cannot be distinguished from binary\n      character arrays by the C++ type system, all types compatible with `const\n      char*` will be directed to the string constructor instead.  This is both\n      for backwards compatibility, and due to the fact that a binary type is not\n      a standard JSON type.\n\n    See the examples below.\n\n    @tparam CompatibleType a type such that:\n    - @a CompatibleType is not derived from `std::istream`,\n    - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move\n         constructors),\n    - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)\n    - @a CompatibleType is not a @ref basic_json nested type (e.g.,\n         @ref json_pointer, @ref iterator, etc ...)\n    - @ref @ref json_serializer<U> has a\n         `to_json(basic_json_t&, CompatibleType&&)` method\n\n    @tparam U = `uncvref_t<CompatibleType>`\n\n    @param[in] val the value to be forwarded to the respective constructor\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @liveexample{The following code shows the constructor with several\n    compatible types.,basic_json__CompatibleType}\n\n    @since version 2.1.0\n    */\n    template < typename CompatibleType,\n               typename U = detail::uncvref_t<CompatibleType>,\n               detail::enable_if_t <\n                   !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >\n    basic_json(CompatibleType && val) noexcept(noexcept(\n                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),\n                                           std::forward<CompatibleType>(val))))\n    {\n        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value from an existing one\n\n    This is a constructor for existing @ref basic_json types.\n    It does not hijack copy/move constructors, since the parameter has different\n    template arguments than the current ones.\n\n    The constructor tries to convert the internal @ref m_value of the parameter.\n\n    @tparam BasicJsonType a type such that:\n    - @a BasicJsonType is a @ref basic_json type.\n    - @a BasicJsonType has different template arguments than @ref basic_json_t.\n\n    @param[in] val the @ref basic_json value to be converted.\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @since version 3.2.0\n    */\n    template < typename BasicJsonType,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >\n    basic_json(const BasicJsonType& val)\n    {\n        using other_boolean_t = typename BasicJsonType::boolean_t;\n        using other_number_float_t = typename BasicJsonType::number_float_t;\n        using other_number_integer_t = typename BasicJsonType::number_integer_t;\n        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n        using other_string_t = typename BasicJsonType::string_t;\n        using other_object_t = typename BasicJsonType::object_t;\n        using other_array_t = typename BasicJsonType::array_t;\n        using other_binary_t = typename BasicJsonType::binary_t;\n\n        switch (val.type())\n        {\n            case value_t::boolean:\n                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());\n                break;\n            case value_t::number_float:\n                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());\n                break;\n            case value_t::number_integer:\n                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());\n                break;\n            case value_t::number_unsigned:\n                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());\n                break;\n            case value_t::string:\n                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());\n                break;\n            case value_t::object:\n                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());\n                break;\n            case value_t::array:\n                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());\n                break;\n            case value_t::binary:\n                JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());\n                break;\n            case value_t::null:\n                *this = nullptr;\n                break;\n            case value_t::discarded:\n                m_type = value_t::discarded;\n                break;\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false);  // LCOV_EXCL_LINE\n        }\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a container (array or object) from an initializer list\n\n    Creates a JSON value of type array or object from the passed initializer\n    list @a init. In case @a type_deduction is `true` (default), the type of\n    the JSON value to be created is deducted from the initializer list @a init\n    according to the following rules:\n\n    1. If the list is empty, an empty JSON object value `{}` is created.\n    2. If the list consists of pairs whose first element is a string, a JSON\n       object value is created where the first elements of the pairs are\n       treated as keys and the second elements are as values.\n    3. In all other cases, an array is created.\n\n    The rules aim to create the best fit between a C++ initializer list and\n    JSON values. The rationale is as follows:\n\n    1. The empty initializer list is written as `{}` which is exactly an empty\n       JSON object.\n    2. C++ has no way of describing mapped types other than to list a list of\n       pairs. As JSON requires that keys must be of type string, rule 2 is the\n       weakest constraint one can pose on initializer lists to interpret them\n       as an object.\n    3. In all other cases, the initializer list could not be interpreted as\n       JSON object type, so interpreting it as JSON array type is safe.\n\n    With the rules described above, the following JSON values cannot be\n    expressed by an initializer list:\n\n    - the empty array (`[]`): use @ref array(initializer_list_t)\n      with an empty initializer list in this case\n    - arrays whose elements satisfy rule 2: use @ref\n      array(initializer_list_t) with the same initializer list\n      in this case\n\n    @note When used without parentheses around an empty initializer list, @ref\n    basic_json() is called instead of this function, yielding the JSON null\n    value.\n\n    @param[in] init  initializer list with JSON values\n\n    @param[in] type_deduction internal parameter; when set to `true`, the type\n    of the JSON value is deducted from the initializer list @a init; when set\n    to `false`, the type provided via @a manual_type is forced. This mode is\n    used by the functions @ref array(initializer_list_t) and\n    @ref object(initializer_list_t).\n\n    @param[in] manual_type internal parameter; when @a type_deduction is set\n    to `false`, the created JSON value will use the provided type (only @ref\n    value_t::array and @ref value_t::object are valid); when @a type_deduction\n    is set to `true`, this parameter has no effect\n\n    @throw type_error.301 if @a type_deduction is `false`, @a manual_type is\n    `value_t::object`, but @a init contains an element which is not a pair\n    whose first element is a string. In this case, the constructor could not\n    create an object. If @a type_deduction would have be `true`, an array\n    would have been created. See @ref object(initializer_list_t)\n    for an example.\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows how JSON values are created from\n    initializer lists.,basic_json__list_init_t}\n\n    @sa @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n    @sa @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    basic_json(initializer_list_t init,\n               bool type_deduction = true,\n               value_t manual_type = value_t::array)\n    {\n        // check if each element is an array with two elements whose first\n        // element is a string\n        bool is_an_object = std::all_of(init.begin(), init.end(),\n                                        [](const detail::json_ref<basic_json>& element_ref)\n        {\n            return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();\n        });\n\n        // adjust type if type deduction is not wanted\n        if (!type_deduction)\n        {\n            // if array is wanted, do not create an object though possible\n            if (manual_type == value_t::array)\n            {\n                is_an_object = false;\n            }\n\n            // if object is wanted but impossible, throw an exception\n            if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))\n            {\n                JSON_THROW(type_error::create(301, \"cannot create object from initializer list\"));\n            }\n        }\n\n        if (is_an_object)\n        {\n            // the initializer list is a list of pairs -> create object\n            m_type = value_t::object;\n            m_value = value_t::object;\n\n            std::for_each(init.begin(), init.end(), [this](const detail::json_ref<basic_json>& element_ref)\n            {\n                auto element = element_ref.moved_or_copied();\n                m_value.object->emplace(\n                    std::move(*((*element.m_value.array)[0].m_value.string)),\n                    std::move((*element.m_value.array)[1]));\n            });\n        }\n        else\n        {\n            // the initializer list describes an array -> create array\n            m_type = value_t::array;\n            m_value.array = create<array_t>(init.begin(), init.end());\n        }\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief explicitly create a binary array (without subtype)\n\n    Creates a JSON binary array value from a given binary container. Binary\n    values are part of various binary formats, such as CBOR, MessagePack, and\n    BSON. This constructor is used to create a value for serialization to those\n    formats.\n\n    @note Note, this function exists because of the difficulty in correctly\n    specifying the correct template overload in the standard value ctor, as both\n    JSON arrays and JSON binary arrays are backed with some form of a\n    `std::vector`. Because JSON binary arrays are a non-standard extension it\n    was decided that it would be best to prevent automatic initialization of a\n    binary array type, for backwards compatibility and so it does not happen on\n    accident.\n\n    @param[in] init container containing bytes to use as binary type\n\n    @return JSON binary array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @since version 3.8.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = init;\n        return res;\n    }\n\n    /*!\n    @brief explicitly create a binary array (with subtype)\n\n    Creates a JSON binary array value from a given binary container. Binary\n    values are part of various binary formats, such as CBOR, MessagePack, and\n    BSON. This constructor is used to create a value for serialization to those\n    formats.\n\n    @note Note, this function exists because of the difficulty in correctly\n    specifying the correct template overload in the standard value ctor, as both\n    JSON arrays and JSON binary arrays are backed with some form of a\n    `std::vector`. Because JSON binary arrays are a non-standard extension it\n    was decided that it would be best to prevent automatic initialization of a\n    binary array type, for backwards compatibility and so it does not happen on\n    accident.\n\n    @param[in] init container containing bytes to use as binary type\n    @param[in] subtype subtype to use in MessagePack and BSON\n\n    @return JSON binary array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @since version 3.8.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init, std::uint8_t subtype)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = binary_t(init, subtype);\n        return res;\n    }\n\n    /// @copydoc binary(const typename binary_t::container_type&)\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = std::move(init);\n        return res;\n    }\n\n    /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t)\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init, std::uint8_t subtype)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = binary_t(std::move(init), subtype);\n        return res;\n    }\n\n    /*!\n    @brief explicitly create an array from an initializer list\n\n    Creates a JSON array value from a given initializer list. That is, given a\n    list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the\n    initializer list is empty, the empty array `[]` is created.\n\n    @note This function is only needed to express two edge cases that cannot\n    be realized with the initializer list constructor (@ref\n    basic_json(initializer_list_t, bool, value_t)). These cases\n    are:\n    1. creating an array whose elements are all pairs whose first element is a\n    string -- in this case, the initializer list constructor would create an\n    object, taking the first elements as keys\n    2. creating an empty array -- passing the empty initializer list to the\n    initializer list constructor yields an empty object\n\n    @param[in] init  initializer list with JSON values to create an array from\n    (optional)\n\n    @return JSON array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `array`\n    function.,array}\n\n    @sa @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json array(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::array);\n    }\n\n    /*!\n    @brief explicitly create an object from an initializer list\n\n    Creates a JSON object value from a given initializer list. The initializer\n    lists elements must be pairs, and their first elements must be strings. If\n    the initializer list is empty, the empty object `{}` is created.\n\n    @note This function is only added for symmetry reasons. In contrast to the\n    related function @ref array(initializer_list_t), there are\n    no cases which can only be expressed by this function. That is, any\n    initializer list @a init can also be passed to the initializer list\n    constructor @ref basic_json(initializer_list_t, bool, value_t).\n\n    @param[in] init  initializer list to create an object from (optional)\n\n    @return JSON object value\n\n    @throw type_error.301 if @a init is not a list of pairs whose first\n    elements are strings. In this case, no object can be created. When such a\n    value is passed to @ref basic_json(initializer_list_t, bool, value_t),\n    an array would have been created from the passed initializer list @a init.\n    See example below.\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `object`\n    function.,object}\n\n    @sa @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json object(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::object);\n    }\n\n    /*!\n    @brief construct an array with count copies of given value\n\n    Constructs a JSON array value by creating @a cnt copies of a passed value.\n    In case @a cnt is `0`, an empty array is created.\n\n    @param[in] cnt  the number of JSON copies of @a val to create\n    @param[in] val  the JSON value to copy\n\n    @post `std::distance(begin(),end()) == cnt` holds.\n\n    @complexity Linear in @a cnt.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows examples for the @ref\n    basic_json(size_type\\, const basic_json&)\n    constructor.,basic_json__size_type_basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(size_type cnt, const basic_json& val)\n        : m_type(value_t::array)\n    {\n        m_value.array = create<array_t>(cnt, val);\n        assert_invariant();\n    }\n\n    /*!\n    @brief construct a JSON container given an iterator range\n\n    Constructs the JSON value with the contents of the range `[first, last)`.\n    The semantics depends on the different types a JSON value can have:\n    - In case of a null type, invalid_iterator.206 is thrown.\n    - In case of other primitive types (number, boolean, or string), @a first\n      must be `begin()` and @a last must be `end()`. In this case, the value is\n      copied. Otherwise, invalid_iterator.204 is thrown.\n    - In case of structured types (array, object), the constructor behaves as\n      similar versions for `std::vector` or `std::map`; that is, a JSON array\n      or object is constructed from the values in the range.\n\n    @tparam InputIT an input iterator type (@ref iterator or @ref\n    const_iterator)\n\n    @param[in] first begin of the range to copy from (included)\n    @param[in] last end of the range to copy from (excluded)\n\n    @pre Iterators @a first and @a last must be initialized. **This\n         precondition is enforced with an assertion (see warning).** If\n         assertions are switched off, a violation of this precondition yields\n         undefined behavior.\n\n    @pre Range `[first, last)` is valid. Usually, this precondition cannot be\n         checked efficiently. Only certain edge cases are detected; see the\n         description of the exceptions below. A violation of this precondition\n         yields undefined behavior.\n\n    @warning A precondition is enforced with a runtime assertion that will\n             result in calling `std::abort` if this precondition is not met.\n             Assertions can be disabled by defining `NDEBUG` at compile time.\n             See https://en.cppreference.com/w/cpp/error/assert for more\n             information.\n\n    @throw invalid_iterator.201 if iterators @a first and @a last are not\n    compatible (i.e., do not belong to the same JSON value). In this case,\n    the range `[first, last)` is undefined.\n    @throw invalid_iterator.204 if iterators @a first and @a last belong to a\n    primitive type (number, boolean, or string), but @a first does not point\n    to the first element any more. In this case, the range `[first, last)` is\n    undefined. See example code below.\n    @throw invalid_iterator.206 if iterators @a first and @a last belong to a\n    null value. In this case, the range `[first, last)` is undefined.\n\n    @complexity Linear in distance between @a first and @a last.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows several ways to create JSON values by\n    specifying a subrange with iterators.,basic_json__InputIt_InputIt}\n\n    @since version 1.0.0\n    */\n    template < class InputIT, typename std::enable_if <\n                   std::is_same<InputIT, typename basic_json_t::iterator>::value ||\n                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >\n    basic_json(InputIT first, InputIT last)\n    {\n        JSON_ASSERT(first.m_object != nullptr);\n        JSON_ASSERT(last.m_object != nullptr);\n\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(201, \"iterators are not compatible\"));\n        }\n\n        // copy type from first iterator\n        m_type = first.m_object->m_type;\n\n        // check if iterator range is complete for primitive values\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()\n                                         || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\"));\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = first.m_object->m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = first.m_object->m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = first.m_object->m_value.number_float;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = first.m_object->m_value.boolean;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *first.m_object->m_value.string;\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object = create<object_t>(first.m_it.object_iterator,\n                                                  last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array = create<array_t>(first.m_it.array_iterator,\n                                                last.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value = *first.m_object->m_value.binary;\n                break;\n            }\n\n            default:\n                JSON_THROW(invalid_iterator::create(206, \"cannot construct with iterators from \" +\n                                                    std::string(first.m_object->type_name())));\n        }\n\n        assert_invariant();\n    }\n\n\n    ///////////////////////////////////////\n    // other constructors and destructor //\n    ///////////////////////////////////////\n\n    template<typename JsonRef,\n             detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,\n                                 std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >\n    basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}\n\n    /*!\n    @brief copy constructor\n\n    Creates a copy of a given JSON value.\n\n    @param[in] other  the JSON value to copy\n\n    @post `*this == other`\n\n    @complexity Linear in the size of @a other.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - As postcondition, it holds: `other == basic_json(other)`.\n\n    @liveexample{The following code shows an example for the copy\n    constructor.,basic_json__basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(const basic_json& other)\n        : m_type(other.m_type)\n    {\n        // check of passed value is valid\n        other.assert_invariant();\n\n        switch (m_type)\n        {\n            case value_t::object:\n            {\n                m_value = *other.m_value.object;\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value = *other.m_value.array;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *other.m_value.string;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value = other.m_value.boolean;\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                m_value = other.m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value = other.m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value = other.m_value.number_float;\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value = *other.m_value.binary;\n                break;\n            }\n\n            default:\n                break;\n        }\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief move constructor\n\n    Move constructor. Constructs a JSON value with the contents of the given\n    value @a other using move semantics. It \"steals\" the resources from @a\n    other and leaves it as JSON null value.\n\n    @param[in,out] other  value to move to this object\n\n    @post `*this` has the same value as @a other before the call.\n    @post @a other is a JSON null value.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible)\n    requirements.\n\n    @liveexample{The code below shows the move constructor explicitly called\n    via std::move.,basic_json__moveconstructor}\n\n    @since version 1.0.0\n    */\n    basic_json(basic_json&& other) noexcept\n        : m_type(std::move(other.m_type)),\n          m_value(std::move(other.m_value))\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        // invalidate payload\n        other.m_type = value_t::null;\n        other.m_value = {};\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief copy assignment\n\n    Copy assignment operator. Copies a JSON value via the \"copy and swap\"\n    strategy: It is expressed in terms of the copy constructor, destructor,\n    and the `swap()` member function.\n\n    @param[in] other  value to copy from\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n\n    @liveexample{The code below shows and example for the copy assignment. It\n    creates a copy of value `a` which is then swapped with `b`. Finally\\, the\n    copy of `a` (which is the null value after the swap) is\n    destroyed.,basic_json__copyassignment}\n\n    @since version 1.0.0\n    */\n    basic_json& operator=(basic_json other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        using std::swap;\n        swap(m_type, other.m_type);\n        swap(m_value, other.m_value);\n\n        assert_invariant();\n        return *this;\n    }\n\n    /*!\n    @brief destructor\n\n    Destroys the JSON value and frees all allocated memory.\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - All stored elements are destroyed and all memory is freed.\n\n    @since version 1.0.0\n    */\n    ~basic_json() noexcept\n    {\n        assert_invariant();\n        m_value.destroy(m_type);\n    }\n\n    /// @}\n\n  public:\n    ///////////////////////\n    // object inspection //\n    ///////////////////////\n\n    /// @name object inspection\n    /// Functions to inspect the type of a JSON value.\n    /// @{\n\n    /*!\n    @brief serialization\n\n    Serialization function for JSON values. The function tries to mimic\n    Python's `json.dumps()` function, and currently supports its @a indent\n    and @a ensure_ascii parameters.\n\n    @param[in] indent If indent is nonnegative, then array elements and object\n    members will be pretty-printed with that indent level. An indent level of\n    `0` will only insert newlines. `-1` (the default) selects the most compact\n    representation.\n    @param[in] indent_char The character to use for indentation if @a indent is\n    greater than `0`. The default is ` ` (space).\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] error_handler  how to react on decoding errors; there are three\n    possible values: `strict` (throws and exception in case a decoding error\n    occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),\n    and `ignore` (ignore invalid UTF-8 sequences during serialization; all\n    bytes are copied to the output unchanged).\n\n    @return string containing the serialization of the JSON value\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded and @a error_handler is set to strict\n\n    @note Binary values are serialized as object containing two keys:\n      - \"bytes\": an array of bytes as integers\n      - \"subtype\": the subtype as integer or \"null\" if the binary has no subtype\n\n    @complexity Linear.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @liveexample{The following example shows the effect of different @a indent\\,\n    @a indent_char\\, and @a ensure_ascii parameters to the result of the\n    serialization.,dump}\n\n    @see https://docs.python.org/2/library/json.html#json.dump\n\n    @since version 1.0.0; indentation character @a indent_char, option\n           @a ensure_ascii and exceptions added in version 3.0.0; error\n           handlers added in version 3.4.0; serialization of binary values added\n           in version 3.8.0.\n    */\n    string_t dump(const int indent = -1,\n                  const char indent_char = ' ',\n                  const bool ensure_ascii = false,\n                  const error_handler_t error_handler = error_handler_t::strict) const\n    {\n        string_t result;\n        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);\n\n        if (indent >= 0)\n        {\n            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));\n        }\n        else\n        {\n            s.dump(*this, false, ensure_ascii, 0);\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief return the type of the JSON value (explicit)\n\n    Return the type of the JSON value as a value from the @ref value_t\n    enumeration.\n\n    @return the type of the JSON value\n            Value type                | return value\n            ------------------------- | -------------------------\n            null                      | value_t::null\n            boolean                   | value_t::boolean\n            string                    | value_t::string\n            number (integer)          | value_t::number_integer\n            number (unsigned integer) | value_t::number_unsigned\n            number (floating-point)   | value_t::number_float\n            object                    | value_t::object\n            array                     | value_t::array\n            binary                    | value_t::binary\n            discarded                 | value_t::discarded\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `type()` for all JSON\n    types.,type}\n\n    @sa @ref operator value_t() -- return the type of the JSON value (implicit)\n    @sa @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr value_t type() const noexcept\n    {\n        return m_type;\n    }\n\n    /*!\n    @brief return whether type is primitive\n\n    This function returns true if and only if the JSON type is primitive\n    (string, number, boolean, or null).\n\n    @return `true` if type is primitive (string, number, boolean, or null),\n    `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_primitive()` for all JSON\n    types.,is_primitive}\n\n    @sa @ref is_structured() -- returns whether JSON value is structured\n    @sa @ref is_null() -- returns whether JSON value is `null`\n    @sa @ref is_string() -- returns whether JSON value is a string\n    @sa @ref is_boolean() -- returns whether JSON value is a boolean\n    @sa @ref is_number() -- returns whether JSON value is a number\n    @sa @ref is_binary() -- returns whether JSON value is a binary array\n\n    @since version 1.0.0\n    */\n    constexpr bool is_primitive() const noexcept\n    {\n        return is_null() || is_string() || is_boolean() || is_number() || is_binary();\n    }\n\n    /*!\n    @brief return whether type is structured\n\n    This function returns true if and only if the JSON type is structured\n    (array or object).\n\n    @return `true` if type is structured (array or object), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_structured()` for all JSON\n    types.,is_structured}\n\n    @sa @ref is_primitive() -- returns whether value is primitive\n    @sa @ref is_array() -- returns whether value is an array\n    @sa @ref is_object() -- returns whether value is an object\n\n    @since version 1.0.0\n    */\n    constexpr bool is_structured() const noexcept\n    {\n        return is_array() || is_object();\n    }\n\n    /*!\n    @brief return whether value is null\n\n    This function returns true if and only if the JSON value is null.\n\n    @return `true` if type is null, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_null()` for all JSON\n    types.,is_null}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_null() const noexcept\n    {\n        return m_type == value_t::null;\n    }\n\n    /*!\n    @brief return whether value is a boolean\n\n    This function returns true if and only if the JSON value is a boolean.\n\n    @return `true` if type is boolean, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_boolean()` for all JSON\n    types.,is_boolean}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_boolean() const noexcept\n    {\n        return m_type == value_t::boolean;\n    }\n\n    /*!\n    @brief return whether value is a number\n\n    This function returns true if and only if the JSON value is a number. This\n    includes both integer (signed and unsigned) and floating-point values.\n\n    @return `true` if type is number (regardless whether integer, unsigned\n    integer or floating-type), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number()` for all JSON\n    types.,is_number}\n\n    @sa @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number() const noexcept\n    {\n        return is_number_integer() || is_number_float();\n    }\n\n    /*!\n    @brief return whether value is an integer number\n\n    This function returns true if and only if the JSON value is a signed or\n    unsigned integer number. This excludes floating-point values.\n\n    @return `true` if type is an integer or unsigned integer number, `false`\n    otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_integer()` for all\n    JSON types.,is_number_integer}\n\n    @sa @ref is_number() -- check if value is a number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_integer() const noexcept\n    {\n        return m_type == value_t::number_integer || m_type == value_t::number_unsigned;\n    }\n\n    /*!\n    @brief return whether value is an unsigned integer number\n\n    This function returns true if and only if the JSON value is an unsigned\n    integer number. This excludes floating-point and signed integer values.\n\n    @return `true` if type is an unsigned integer number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_unsigned()` for all\n    JSON types.,is_number_unsigned}\n\n    @sa @ref is_number() -- check if value is a number\n    @sa @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 2.0.0\n    */\n    constexpr bool is_number_unsigned() const noexcept\n    {\n        return m_type == value_t::number_unsigned;\n    }\n\n    /*!\n    @brief return whether value is a floating-point number\n\n    This function returns true if and only if the JSON value is a\n    floating-point number. This excludes signed and unsigned integer values.\n\n    @return `true` if type is a floating-point number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_float()` for all\n    JSON types.,is_number_float}\n\n    @sa @ref is_number() -- check if value is number\n    @sa @ref is_number_integer() -- check if value is an integer number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_float() const noexcept\n    {\n        return m_type == value_t::number_float;\n    }\n\n    /*!\n    @brief return whether value is an object\n\n    This function returns true if and only if the JSON value is an object.\n\n    @return `true` if type is object, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_object()` for all JSON\n    types.,is_object}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_object() const noexcept\n    {\n        return m_type == value_t::object;\n    }\n\n    /*!\n    @brief return whether value is an array\n\n    This function returns true if and only if the JSON value is an array.\n\n    @return `true` if type is array, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_array()` for all JSON\n    types.,is_array}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_array() const noexcept\n    {\n        return m_type == value_t::array;\n    }\n\n    /*!\n    @brief return whether value is a string\n\n    This function returns true if and only if the JSON value is a string.\n\n    @return `true` if type is string, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_string()` for all JSON\n    types.,is_string}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_string() const noexcept\n    {\n        return m_type == value_t::string;\n    }\n\n    /*!\n    @brief return whether value is a binary array\n\n    This function returns true if and only if the JSON value is a binary array.\n\n    @return `true` if type is binary array, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_binary()` for all JSON\n    types.,is_binary}\n\n    @since version 3.8.0\n    */\n    constexpr bool is_binary() const noexcept\n    {\n        return m_type == value_t::binary;\n    }\n\n    /*!\n    @brief return whether value is discarded\n\n    This function returns true if and only if the JSON value was discarded\n    during parsing with a callback function (see @ref parser_callback_t).\n\n    @note This function will always be `false` for JSON values after parsing.\n    That is, discarded values can only occur during parsing, but will be\n    removed when inside a structured value or replaced by null in other cases.\n\n    @return `true` if type is discarded, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_discarded()` for all JSON\n    types.,is_discarded}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_discarded() const noexcept\n    {\n        return m_type == value_t::discarded;\n    }\n\n    /*!\n    @brief return the type of the JSON value (implicit)\n\n    Implicitly return the type of the JSON value as a value from the @ref\n    value_t enumeration.\n\n    @return the type of the JSON value\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies the @ref value_t operator for\n    all JSON types.,operator__value_t}\n\n    @sa @ref type() -- return the type of the JSON value (explicit)\n    @sa @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr operator value_t() const noexcept\n    {\n        return m_type;\n    }\n\n    /// @}\n\n  private:\n    //////////////////\n    // value access //\n    //////////////////\n\n    /// get a boolean (explicit)\n    boolean_t get_impl(boolean_t* /*unused*/) const\n    {\n        if (JSON_HEDLEY_LIKELY(is_boolean()))\n        {\n            return m_value.boolean;\n        }\n\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(type_name())));\n    }\n\n    /// get a pointer to the value (object)\n    object_t* get_impl_ptr(object_t* /*unused*/) noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (object)\n    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    array_t* get_impl_ptr(array_t* /*unused*/) noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    string_t* get_impl_ptr(string_t* /*unused*/) noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept\n    {\n        return is_binary() ? m_value.binary : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept\n    {\n        return is_binary() ? m_value.binary : nullptr;\n    }\n\n    /*!\n    @brief helper function to implement get_ref()\n\n    This function helps to implement get_ref() without code duplication for\n    const and non-const overloads\n\n    @tparam ThisType will be deduced as `basic_json` or `const basic_json`\n\n    @throw type_error.303 if ReferenceType does not match underlying value\n    type of the current JSON\n    */\n    template<typename ReferenceType, typename ThisType>\n    static ReferenceType get_ref_impl(ThisType& obj)\n    {\n        // delegate the call to get_ptr<>()\n        auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();\n\n        if (JSON_HEDLEY_LIKELY(ptr != nullptr))\n        {\n            return *ptr;\n        }\n\n        JSON_THROW(type_error::create(303, \"incompatible ReferenceType for get_ref, actual type is \" + std::string(obj.type_name())));\n    }\n\n  public:\n    /// @name value access\n    /// Direct access to the stored value of a JSON value.\n    /// @{\n\n    /*!\n    @brief get special-case overload\n\n    This overloads avoids a lot of template boilerplate, it can be seen as the\n    identity method\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this\n\n    @complexity Constant.\n\n    @since version 2.1.0\n    */\n    template<typename BasicJsonType, detail::enable_if_t<\n                 std::is_same<typename std::remove_const<BasicJsonType>::type, basic_json_t>::value,\n                 int> = 0>\n    basic_json get() const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads converts the current @ref basic_json in a different\n    @ref basic_json type\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this, converted into @tparam BasicJsonType\n\n    @complexity Depending on the implementation of the called `from_json()`\n                method.\n\n    @since version 3.2.0\n    */\n    template < typename BasicJsonType, detail::enable_if_t <\n                   !std::is_same<BasicJsonType, basic_json>::value&&\n                   detail::is_basic_json<BasicJsonType>::value, int > = 0 >\n    BasicJsonType get() const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType ret;\n    JSONSerializer<ValueType>::from_json(*this, ret);\n    return ret;\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n    - @ref json_serializer<ValueType> does not have a `from_json()` method of\n      the form `ValueType from_json(const basic_json&)`\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get__ValueType_const}\n\n    @since version 2.1.0\n    */\n    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,\n               detail::enable_if_t <\n                   !detail::is_basic_json<ValueType>::value &&\n                   detail::has_from_json<basic_json_t, ValueType>::value &&\n                   !detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType get() const noexcept(noexcept(\n                                       JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))\n    {\n        // we cannot static_assert on ValueTypeCV being non-const, because\n        // there is support for get<const basic_json_t>(), which is why we\n        // still need the uncvref\n        static_assert(!std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        static_assert(std::is_default_constructible<ValueType>::value,\n                      \"types must be DefaultConstructible when used with get()\");\n\n        ValueType ret;\n        JSONSerializer<ValueType>::from_json(*this, ret);\n        return ret;\n    }\n\n    /*!\n    @brief get a value (explicit); special case\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    return JSONSerializer<ValueTypeCV>::from_json(*this);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json and\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `ValueType from_json(const basic_json&)`\n\n    @note If @ref json_serializer<ValueType> has both overloads of\n    `from_json()`, this one is chosen.\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @since version 2.1.0\n    */\n    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,\n               detail::enable_if_t < !std::is_same<basic_json_t, ValueType>::value &&\n                                     detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                                     int > = 0 >\n    ValueType get() const noexcept(noexcept(\n                                       JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))\n    {\n        static_assert(!std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        return JSONSerializer<ValueType>::from_json(*this);\n    }\n\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value.\n    The value is filled into the input parameter by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType v;\n    JSONSerializer<ValueType>::from_json(*this, v);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n\n    @tparam ValueType the input parameter type.\n\n    @return the input parameter, allowing chaining calls.\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get_to}\n\n    @since version 3.3.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   !detail::is_basic_json<ValueType>::value&&\n                   detail::has_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType & get_to(ValueType& v) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<ValueType>::from_json(*this, v);\n        return v;\n    }\n\n    // specialization to allow to call get_to with a basic_json value\n    // see https://github.com/nlohmann/json/issues/2175\n    template<typename ValueType,\n             detail::enable_if_t <\n                 detail::is_basic_json<ValueType>::value,\n                 int> = 0>\n    ValueType & get_to(ValueType& v) const\n    {\n        v = *this;\n        return v;\n    }\n\n    template <\n        typename T, std::size_t N,\n        typename Array = T (&)[N],\n        detail::enable_if_t <\n            detail::has_from_json<basic_json_t, Array>::value, int > = 0 >\n    Array get_to(T (&v)[N]) const\n    noexcept(noexcept(JSONSerializer<Array>::from_json(\n                          std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<Array>::from_json(*this, v);\n        return v;\n    }\n\n\n    /*!\n    @brief get a pointer value (implicit)\n\n    Implicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning Writing data to the pointee of the result yields an undefined\n    state.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t. Enforced by a static\n    assertion.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get_ptr}\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>()\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /*!\n    @brief get a pointer value (implicit)\n    @copydoc get_ptr()\n    */\n    template < typename PointerType, typename std::enable_if <\n                   std::is_pointer<PointerType>::value&&\n                   std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >\n    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>() const\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n\n    Explicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning The pointer becomes invalid if the underlying JSON object\n    changes.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get__PointerType}\n\n    @sa @ref get_ptr() for explicit pointer-member access\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n    @copydoc get()\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n\n    Implicit reference access to the internally stored JSON value. No copies\n    are made.\n\n    @warning Writing data to the referee of the result yields an undefined\n    state.\n\n    @tparam ReferenceType reference type; must be a reference to @ref array_t,\n    @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or\n    @ref number_float_t. Enforced by static assertion.\n\n    @return reference to the internally stored JSON value if the requested\n    reference type @a ReferenceType fits to the JSON value; throws\n    type_error.303 otherwise\n\n    @throw type_error.303 in case passed type @a ReferenceType is incompatible\n    with the stored JSON value; see example below\n\n    @complexity Constant.\n\n    @liveexample{The example shows several calls to `get_ref()`.,get_ref}\n\n    @since version 1.1.0\n    */\n    template<typename ReferenceType, typename std::enable_if<\n                 std::is_reference<ReferenceType>::value, int>::type = 0>\n    ReferenceType get_ref()\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n    @copydoc get_ref()\n    */\n    template < typename ReferenceType, typename std::enable_if <\n                   std::is_reference<ReferenceType>::value&&\n                   std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >\n    ReferenceType get_ref() const\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a value (implicit)\n\n    Implicit type conversion between the JSON value and a compatible value.\n    The call is realized by calling @ref get() const.\n\n    @tparam ValueType non-pointer type compatible to the JSON value, for\n    instance `int` for JSON integer numbers, `bool` for JSON booleans, or\n    `std::vector` types for JSON arrays. The character type of @ref string_t\n    as well as an initializer list of this type is excluded to avoid\n    ambiguities as these types implicitly convert to `std::string`.\n\n    @return copy of the JSON value, converted to type @a ValueType\n\n    @throw type_error.302 in case passed type @a ValueType is incompatible\n    to the JSON value type (e.g., the JSON value is of type boolean, but a\n    string is requested); see example below\n\n    @complexity Linear in the size of the JSON value.\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,operator__ValueType}\n\n    @since version 1.0.0\n    */\n    template < typename ValueType, typename std::enable_if <\n                   !std::is_pointer<ValueType>::value&&\n                   !std::is_same<ValueType, detail::json_ref<basic_json>>::value&&\n                   !std::is_same<ValueType, typename string_t::value_type>::value&&\n                   !detail::is_basic_json<ValueType>::value\n                   && !std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value\n#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))\n                   && !std::is_same<ValueType, typename std::string_view>::value\n#endif\n                   && detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value\n                   , int >::type = 0 >\n    JSON_EXPLICIT operator ValueType() const\n    {\n        // delegate the call to get<>() const\n        return get<ValueType>();\n    }\n\n    /*!\n    @return reference to the binary value\n\n    @throw type_error.302 if the value is not binary\n\n    @sa @ref is_binary() to check if the value is binary\n\n    @since version 3.8.0\n    */\n    binary_t& get_binary()\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(type_name())));\n        }\n\n        return *get_ptr<binary_t*>();\n    }\n\n    /// @copydoc get_binary()\n    const binary_t& get_binary() const\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(type_name())));\n        }\n\n        return *get_ptr<const binary_t*>();\n    }\n\n    /// @}\n\n\n    ////////////////////\n    // element access //\n    ////////////////////\n\n    /// @name element access\n    /// Access to the JSON value.\n    /// @{\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a reference to the element at specified location @a idx, with\n    bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__size_type}\n    */\n    reference at(size_type idx)\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a const reference to the element at specified location @a idx,\n    with bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__size_type_const}\n    */\n    const_reference at(size_type idx) const\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a reference to the element at with specified key @a key, with\n    bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__object_t_key_type}\n    */\n    reference at(const typename object_t::key_type& key)\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return m_value.object->at(key);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a const reference to the element at with specified key @a key,\n    with bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__object_t_key_type_const}\n    */\n    const_reference at(const typename object_t::key_type& key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return m_value.object->at(key);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a reference to the element at specified location @a idx.\n\n    @note If @a idx is beyond the range of the array (i.e., `idx >= size()`),\n    then the array is silently filled up with `null` values to make `idx` a\n    valid reference to the last stored element.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array or null; in that\n    cases, using the [] operator with an index makes no sense.\n\n    @complexity Constant if @a idx is in the range of the array. Otherwise\n    linear in `idx - size()`.\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `[]` operator. Note the addition of `null`\n    values.,operatorarray__size_type}\n\n    @since version 1.0.0\n    */\n    reference operator[](size_type idx)\n    {\n        // implicitly convert null value to an empty array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value.array = create<array_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // fill up array with null values if given idx is outside range\n            if (idx >= m_value.array->size())\n            {\n                m_value.array->insert(m_value.array->end(),\n                                      idx - m_value.array->size() + 1,\n                                      basic_json());\n            }\n\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a const reference to the element at specified location @a idx.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array; in that case,\n    using the [] operator with an index makes no sense.\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how array elements can be read using\n    the `[]` operator.,operatorarray__size_type_const}\n\n    @since version 1.0.0\n    */\n    const_reference operator[](size_type idx) const\n    {\n        // const operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    reference operator[](const typename object_t::key_type& key)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->operator[](key);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    const_reference operator[](const typename object_t::key_type& key) const\n    {\n        // const operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    JSON_HEDLEY_NON_NULL(2)\n    reference operator[](T* key)\n    {\n        // implicitly convert null to object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->operator[](key);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    JSON_HEDLEY_NON_NULL(2)\n    const_reference operator[](T* key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(key);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const typename object_t::key_type&), this function\n    does not throw if the given key @a key was not found.\n\n    @note Unlike @ref operator[](const typename object_t::key_type& key), this\n    function does not implicitly add an element to the position defined by @a\n    key. This function is furthermore also applicable to const objects.\n\n    @param[in] key  key of the element to access\n    @param[in] default_value  the value to return if @a key is not found\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.302 if @a default_value does not match the type of the\n    value at @a key\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n\n    @since version 1.0.0\n    */\n    // using std::is_convertible in a std::enable_if will fail when using explicit conversions\n    template < class ValueType, typename std::enable_if <\n                   detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, ValueType>::value, int >::type = 0 >\n    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if key is found, return value and given default value otherwise\n            const auto it = find(key);\n            if (it != end())\n            {\n                return it->template get<ValueType>();\n            }\n\n            return default_value;\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const\n    */\n    string_t value(const typename object_t::key_type& key, const char* default_value) const\n    {\n        return value(key, string_t(default_value));\n    }\n\n    /*!\n    @brief access specified object element via JSON Pointer with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(ptr);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const json_pointer&), this function does not throw\n    if the given key @a key was not found.\n\n    @param[in] ptr  a JSON pointer to the element to access\n    @param[in] default_value  the value to return if @a ptr found no value\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.302 if @a default_value does not match the type of the\n    value at @a ptr\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value_ptr}\n\n    @sa @ref operator[](const json_pointer&) for unchecked access by reference\n\n    @since version 2.0.2\n    */\n    template<class ValueType, typename std::enable_if<\n                 detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>\n    ValueType value(const json_pointer& ptr, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if pointer resolves a value, return it or use default value\n            JSON_TRY\n            {\n                return ptr.get_checked(this).template get<ValueType>();\n            }\n            JSON_INTERNAL_CATCH (out_of_range&)\n            {\n                return default_value;\n            }\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const json_pointer&, ValueType) const\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    string_t value(const json_pointer& ptr, const char* default_value) const\n    {\n        return value(ptr, string_t(default_value));\n    }\n\n    /*!\n    @brief access the first element\n\n    Returns a reference to the first element in the container. For a JSON\n    container `c`, the expression `c.front()` is equivalent to `*c.begin()`.\n\n    @return In case of a structured type (array or object), a reference to the\n    first element is returned. In case of number, string, boolean, or binary\n    values, a reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on `null` value\n\n    @liveexample{The following code shows an example for `front()`.,front}\n\n    @sa @ref back() -- access the last element\n\n    @since version 1.0.0\n    */\n    reference front()\n    {\n        return *begin();\n    }\n\n    /*!\n    @copydoc basic_json::front()\n    */\n    const_reference front() const\n    {\n        return *cbegin();\n    }\n\n    /*!\n    @brief access the last element\n\n    Returns a reference to the last element in the container. For a JSON\n    container `c`, the expression `c.back()` is equivalent to\n    @code {.cpp}\n    auto tmp = c.end();\n    --tmp;\n    return *tmp;\n    @endcode\n\n    @return In case of a structured type (array or object), a reference to the\n    last element is returned. In case of number, string, boolean, or binary\n    values, a reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on a `null` value. See example\n    below.\n\n    @liveexample{The following code shows an example for `back()`.,back}\n\n    @sa @ref front() -- access the first element\n\n    @since version 1.0.0\n    */\n    reference back()\n    {\n        auto tmp = end();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @copydoc basic_json::back()\n    */\n    const_reference back() const\n    {\n        auto tmp = cend();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @brief remove element given an iterator\n\n    Removes the element specified by iterator @a pos. The iterator @a pos must\n    be valid and dereferenceable. Thus the `end()` iterator (which is valid,\n    but is not dereferenceable) cannot be used as a value for @a pos.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] pos iterator to the element to remove\n    @return Iterator following the last removed element. If the iterator @a\n    pos refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.202 if called on an iterator which does not belong\n    to the current JSON value; example: `\"iterator does not fit current\n    value\"`\n    @throw invalid_iterator.205 if called on a primitive type with invalid\n    iterator (i.e., any iterator which is not `begin()`); example: `\"iterator\n    out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: amortized constant\n    - arrays: linear in distance between @a pos and the end of the container\n    - strings and binary: linear in the length of the member\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType}\n\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template < class IteratorType, typename std::enable_if <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type\n               = 0 >\n    IteratorType erase(IteratorType pos)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))\n                {\n                    JSON_THROW(invalid_iterator::create(205, \"iterator out of range\"));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);\n                    m_value.binary = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);\n                break;\n            }\n\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove elements given an iterator range\n\n    Removes the element specified by the range `[first; last)`. The iterator\n    @a first does not need to be dereferenceable if `first == last`: erasing\n    an empty range is a no-op.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] first iterator to the beginning of the range to remove\n    @param[in] last iterator past the end of the range to remove\n    @return Iterator following the last removed element. If the iterator @a\n    second refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.203 if called on iterators which does not belong\n    to the current JSON value; example: `\"iterators do not fit current value\"`\n    @throw invalid_iterator.204 if called on a primitive type with invalid\n    iterators (i.e., if `first != begin()` and `last != end()`); example:\n    `\"iterators out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: `log(size()) + std::distance(first, last)`\n    - arrays: linear in the distance between @a first and @a last, plus linear\n      in the distance between @a last and end of the container\n    - strings and binary: linear in the length of the member\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType_IteratorType}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template < class IteratorType, typename std::enable_if <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type\n               = 0 >\n    IteratorType erase(IteratorType first, IteratorType last)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(203, \"iterators do not fit current value\"));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()\n                                       || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\"));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);\n                    m_value.binary = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,\n                                              last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,\n                                             last.m_it.array_iterator);\n                break;\n            }\n\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove element from a JSON object given a key\n\n    Removes elements from a JSON object with the key value @a key.\n\n    @param[in] key value of the elements to remove\n\n    @return Number of elements removed. If @a ObjectType is the default\n    `std::map` type, the return value will always be `0` (@a key was not\n    found) or `1` (@a key was found).\n\n    @post References and iterators to the erased elements are invalidated.\n    Other references and iterators are not affected.\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n\n    @complexity `log(size()) + count(key)`\n\n    @liveexample{The example shows the effect of `erase()`.,erase__key_type}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    size_type erase(const typename object_t::key_type& key)\n    {\n        // this erase only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->erase(key);\n        }\n\n        JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief remove element from a JSON array given an index\n\n    Removes element from a JSON array at the index @a idx.\n\n    @param[in] idx index of the element to remove\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n    @throw out_of_range.401 when `idx >= size()`; example: `\"array index 17\n    is out of range\"`\n\n    @complexity Linear in distance between @a idx and the end of the container.\n\n    @liveexample{The example shows the effect of `erase()`.,erase__size_type}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n\n    @since version 1.0.0\n    */\n    void erase(const size_type idx)\n    {\n        // this erase only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            if (JSON_HEDLEY_UNLIKELY(idx >= size()))\n            {\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n\n            m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));\n        }\n        else\n        {\n            JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n    }\n\n    /// @}\n\n\n    ////////////\n    // lookup //\n    ////////////\n\n    /// @name lookup\n    /// @{\n\n    /*!\n    @brief find an element in a JSON object\n\n    Finds an element in a JSON object with key equivalent to @a key. If the\n    element is not found or the JSON value is not an object, end() is\n    returned.\n\n    @note This method always returns @ref end() when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value of the element to search for.\n\n    @return Iterator to an element with key equivalent to @a key. If no such\n    element is found or the JSON value is not an object, past-the-end (see\n    @ref end()) iterator is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `find()` is used.,find__key_type}\n\n    @sa @ref contains(KeyT&&) const -- checks whether a key exists\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    iterator find(KeyT&& key)\n    {\n        auto result = end();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief find an element in a JSON object\n    @copydoc find(KeyT&&)\n    */\n    template<typename KeyT>\n    const_iterator find(KeyT&& key) const\n    {\n        auto result = cend();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief returns the number of occurrences of a key in a JSON object\n\n    Returns the number of elements with key @a key. If ObjectType is the\n    default `std::map` type, the return value will always be `0` (@a key was\n    not found) or `1` (@a key was found).\n\n    @note This method always returns `0` when executed on a JSON type that is\n          not an object.\n\n    @param[in] key key value of the element to count\n\n    @return Number of elements with key @a key. If the JSON value is not an\n    object, the return value will be `0`.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `count()` is used.,count}\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    size_type count(KeyT&& key) const\n    {\n        // return 0 for all nonobject types\n        return is_object() ? m_value.object->count(std::forward<KeyT>(key)) : 0;\n    }\n\n    /*!\n    @brief check the existence of an element in a JSON object\n\n    Check whether an element exists in a JSON object with key equivalent to\n    @a key. If the element is not found or the JSON value is not an object,\n    false is returned.\n\n    @note This method always returns false when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value to check its existence.\n\n    @return true if an element with specified @a key exists. If no such\n    element with such key is found or the JSON value is not an object,\n    false is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The following code shows an example for `contains()`.,contains}\n\n    @sa @ref find(KeyT&&) -- returns an iterator to an object element\n    @sa @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer\n\n    @since version 3.6.0\n    */\n    template < typename KeyT, typename std::enable_if <\n                   !std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int >::type = 0 >\n    bool contains(KeyT && key) const\n    {\n        return is_object() && m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end();\n    }\n\n    /*!\n    @brief check the existence of an element in a JSON object given a JSON pointer\n\n    Check whether the given JSON pointer @a ptr can be resolved in the current\n    JSON value.\n\n    @note This method can be executed on any JSON value type.\n\n    @param[in] ptr JSON pointer to check its existence.\n\n    @return true if the JSON pointer can be resolved to a stored value, false\n    otherwise.\n\n    @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The following code shows an example for `contains()`.,contains_json_pointer}\n\n    @sa @ref contains(KeyT &&) const -- checks the existence of a key\n\n    @since version 3.7.0\n    */\n    bool contains(const json_pointer& ptr) const\n    {\n        return ptr.contains(this);\n    }\n\n    /// @}\n\n\n    ///////////////\n    // iterators //\n    ///////////////\n\n    /// @name iterators\n    /// @{\n\n    /*!\n    @brief returns an iterator to the first element\n\n    Returns an iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `begin()`.,begin}\n\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    iterator begin() noexcept\n    {\n        iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cbegin()\n    */\n    const_iterator begin() const noexcept\n    {\n        return cbegin();\n    }\n\n    /*!\n    @brief returns a const iterator to the first element\n\n    Returns a const iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).begin()`.\n\n    @liveexample{The following code shows an example for `cbegin()`.,cbegin}\n\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    const_iterator cbegin() const noexcept\n    {\n        const_iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to one past the last element\n\n    Returns an iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `end()`.,end}\n\n    @sa @ref cend() -- returns a const iterator to the end\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    iterator end() noexcept\n    {\n        iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cend()\n    */\n    const_iterator end() const noexcept\n    {\n        return cend();\n    }\n\n    /*!\n    @brief returns a const iterator to one past the last element\n\n    Returns a const iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).end()`.\n\n    @liveexample{The following code shows an example for `cend()`.,cend}\n\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_iterator cend() const noexcept\n    {\n        const_iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-beginning\n\n    Returns an iterator to the reverse-beginning; that is, the last element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(end())`.\n\n    @liveexample{The following code shows an example for `rbegin()`.,rbegin}\n\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    reverse_iterator rbegin() noexcept\n    {\n        return reverse_iterator(end());\n    }\n\n    /*!\n    @copydoc basic_json::crbegin()\n    */\n    const_reverse_iterator rbegin() const noexcept\n    {\n        return crbegin();\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-end\n\n    Returns an iterator to the reverse-end; that is, one before the first\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(begin())`.\n\n    @liveexample{The following code shows an example for `rend()`.,rend}\n\n    @sa @ref crend() -- returns a const reverse iterator to the end\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    reverse_iterator rend() noexcept\n    {\n        return reverse_iterator(begin());\n    }\n\n    /*!\n    @copydoc basic_json::crend()\n    */\n    const_reverse_iterator rend() const noexcept\n    {\n        return crend();\n    }\n\n    /*!\n    @brief returns a const reverse iterator to the last element\n\n    Returns a const iterator to the reverse-beginning; that is, the last\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rbegin()`.\n\n    @liveexample{The following code shows an example for `crbegin()`.,crbegin}\n\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crbegin() const noexcept\n    {\n        return const_reverse_iterator(cend());\n    }\n\n    /*!\n    @brief returns a const reverse iterator to one before the first\n\n    Returns a const reverse iterator to the reverse-end; that is, one before\n    the first element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rend()`.\n\n    @liveexample{The following code shows an example for `crend()`.,crend}\n\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crend() const noexcept\n    {\n        return const_reverse_iterator(cbegin());\n    }\n\n  public:\n    /*!\n    @brief wrapper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without iterator_wrapper:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without iterator proxy:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with iterator proxy:\n\n    @code{cpp}\n    for (auto it : json::iterator_wrapper(j_object))\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example).\n\n    @param[in] ref  reference to a JSON value\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the wrapper is used,iterator_wrapper}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @note The name of this function is not yet final and may change in the\n    future.\n\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use @ref items() instead;\n                that is, replace `json::iterator_wrapper(j)` with `j.items()`.\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @copydoc iterator_wrapper(reference)\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @brief helper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without `items()` function:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without `items()` function:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with `items()` function:\n\n    @code{cpp}\n    for (auto& el : j_object.items())\n    {\n        std::cout << \"key: \" << el.key() << \", value:\" << el.value() << '\\n';\n    }\n    @endcode\n\n    The `items()` function also allows to use\n    [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding)\n    (C++17):\n\n    @code{cpp}\n    for (auto& [key, val] : j_object.items())\n    {\n        std::cout << \"key: \" << key << \", value:\" << val << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example). For primitive types (e.g., numbers),\n          `key()` returns an empty string.\n\n    @warning Using `items()` on temporary objects is dangerous. Make sure the\n             object's lifetime exeeds the iteration. See\n             <https://github.com/nlohmann/json/issues/2040> for more\n             information.\n\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the function is used.,items}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 3.1.0, structured bindings support since 3.5.0.\n    */\n    iteration_proxy<iterator> items() noexcept\n    {\n        return iteration_proxy<iterator>(*this);\n    }\n\n    /*!\n    @copydoc items()\n    */\n    iteration_proxy<const_iterator> items() const noexcept\n    {\n        return iteration_proxy<const_iterator>(*this);\n    }\n\n    /// @}\n\n\n    //////////////\n    // capacity //\n    //////////////\n\n    /// @name capacity\n    /// @{\n\n    /*!\n    @brief checks whether the container is empty.\n\n    Checks if a JSON value has no elements (i.e. whether its @ref size is `0`).\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `true`\n            boolean     | `false`\n            string      | `false`\n            number      | `false`\n            binary      | `false`\n            object      | result of function `object_t::empty()`\n            array       | result of function `array_t::empty()`\n\n    @liveexample{The following code uses `empty()` to check if a JSON\n    object contains any elements.,empty}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `empty()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return whether a string stored as JSON value\n    is empty - it returns whether the JSON container itself is empty which is\n    false in the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `begin() == end()`.\n\n    @sa @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    bool empty() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return true;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::empty()\n                return m_value.array->empty();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::empty()\n                return m_value.object->empty();\n            }\n\n            default:\n            {\n                // all other types are nonempty\n                return false;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the number of elements\n\n    Returns the number of elements in a JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0`\n            boolean     | `1`\n            string      | `1`\n            number      | `1`\n            binary      | `1`\n            object      | result of function object_t::size()\n            array       | result of function array_t::size()\n\n    @liveexample{The following code calls `size()` on the different value\n    types.,size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their size() functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return the length of a string stored as JSON\n    value - it returns the number of elements in the JSON value which is 1 in\n    the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `std::distance(begin(), end())`.\n\n    @sa @ref empty() -- checks whether the container is empty\n    @sa @ref max_size() -- returns the maximal number of elements\n\n    @since version 1.0.0\n    */\n    size_type size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return 0;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::size()\n                return m_value.array->size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::size()\n                return m_value.object->size();\n            }\n\n            default:\n            {\n                // all other types have size 1\n                return 1;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the maximum possible number of elements\n\n    Returns the maximum number of elements a JSON value is able to hold due to\n    system or library implementation limitations, i.e. `std::distance(begin(),\n    end())` for the JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0` (same as `size()`)\n            boolean     | `1` (same as `size()`)\n            string      | `1` (same as `size()`)\n            number      | `1` (same as `size()`)\n            binary      | `1` (same as `size()`)\n            object      | result of function `object_t::max_size()`\n            array       | result of function `array_t::max_size()`\n\n    @liveexample{The following code calls `max_size()` on the different value\n    types. Note the output is implementation specific.,max_size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `max_size()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of returning `b.size()` where `b` is the largest\n      possible JSON value.\n\n    @sa @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    size_type max_size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::array:\n            {\n                // delegate call to array_t::max_size()\n                return m_value.array->max_size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::max_size()\n                return m_value.object->max_size();\n            }\n\n            default:\n            {\n                // all other types have max_size() == size()\n                return size();\n            }\n        }\n    }\n\n    /// @}\n\n\n    ///////////////\n    // modifiers //\n    ///////////////\n\n    /// @name modifiers\n    /// @{\n\n    /*!\n    @brief clears the contents\n\n    Clears the content of a JSON value and resets it to the default value as\n    if @ref basic_json(value_t) would have been called with the current value\n    type from @ref type():\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    binary      | An empty byte vector\n    object      | `{}`\n    array       | `[]`\n\n    @post Has the same effect as calling\n    @code {.cpp}\n    *this = basic_json(type());\n    @endcode\n\n    @liveexample{The example below shows the effect of `clear()` to different\n    JSON types.,clear}\n\n    @complexity Linear in the size of the JSON value.\n\n    @iterators All iterators, pointers and references related to this container\n               are invalidated.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @sa @ref basic_json(value_t) -- constructor that creates an object with the\n        same value than calling `clear()`\n\n    @since version 1.0.0\n    */\n    void clear() noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = 0;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = 0;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = 0.0;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = false;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value.string->clear();\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value.binary->clear();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array->clear();\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object->clear();\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Appends the given element @a val to the end of the JSON value. If the\n    function is called on a JSON null value, an empty array is created before\n    appending @a val.\n\n    @param[in] val the value to add to the JSON array\n\n    @throw type_error.308 when called on a type other than JSON array or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON array. Note how the `null` value was silently\n    converted to a JSON array.,push_back}\n\n    @since version 1.0.0\n    */\n    void push_back(basic_json&& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (move semantics)\n        m_value.array->push_back(std::move(val));\n        // if val is moved from, basic_json move constructor marks it null so we do not call the destructor\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(basic_json&& val)\n    {\n        push_back(std::move(val));\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    void push_back(const basic_json& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array\n        m_value.array->push_back(val);\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(const basic_json& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    Inserts the given element @a val to the JSON object. If the function is\n    called on a JSON null value, an empty object is created before inserting\n    @a val.\n\n    @param[in] val the value to add to the JSON object\n\n    @throw type_error.308 when called on a type other than JSON object or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON object. Note how the `null` value was silently\n    converted to a JSON object.,push_back__object_t__value}\n\n    @since version 1.0.0\n    */\n    void push_back(const typename object_t::value_type& val)\n    {\n        // push_back only works for null objects or objects\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array\n        m_value.object->insert(val);\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(const typename object_t::value_type&)\n    */\n    reference operator+=(const typename object_t::value_type& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    This function allows to use `push_back` with an initializer list. In case\n\n    1. the current value is an object,\n    2. the initializer list @a init contains only two elements, and\n    3. the first element of @a init is a string,\n\n    @a init is converted into an object element and added using\n    @ref push_back(const typename object_t::value_type&). Otherwise, @a init\n    is converted to a JSON value and added using @ref push_back(basic_json&&).\n\n    @param[in] init  an initializer list\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @note This function is required to resolve an ambiguous overload error,\n          because pairs like `{\"key\", \"value\"}` can be both interpreted as\n          `object_t::value_type` or `std::initializer_list<basic_json>`, see\n          https://github.com/nlohmann/json/issues/235 for more information.\n\n    @liveexample{The example shows how initializer lists are treated as\n    objects when possible.,push_back__initializer_list}\n    */\n    void push_back(initializer_list_t init)\n    {\n        if (is_object() && init.size() == 2 && (*init.begin())->is_string())\n        {\n            basic_json&& key = init.begin()->moved_or_copied();\n            push_back(typename object_t::value_type(\n                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));\n        }\n        else\n        {\n            push_back(basic_json(init));\n        }\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(initializer_list_t)\n    */\n    reference operator+=(initializer_list_t init)\n    {\n        push_back(init);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Creates a JSON value from the passed parameters @a args to the end of the\n    JSON value. If the function is called on a JSON null value, an empty array\n    is created before appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return reference to the inserted element\n\n    @throw type_error.311 when called on a type other than JSON array or\n    null; example: `\"cannot use emplace_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` can be used to add\n    elements to a JSON array. Note how the `null` value was silently converted\n    to a JSON array.,emplace_back}\n\n    @since version 2.0.8, returns reference since 3.7.0\n    */\n    template<class... Args>\n    reference emplace_back(Args&& ... args)\n    {\n        // emplace_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n#ifdef JSON_HAS_CPP_17\n        return m_value.array->emplace_back(std::forward<Args>(args)...);\n#else\n        m_value.array->emplace_back(std::forward<Args>(args)...);\n        return m_value.array->back();\n#endif\n    }\n\n    /*!\n    @brief add an object to an object if key does not exist\n\n    Inserts a new element into a JSON object constructed in-place with the\n    given @a args if there is no element with the key in the container. If the\n    function is called on a JSON null value, an empty object is created before\n    appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return a pair consisting of an iterator to the inserted element, or the\n            already-existing element if no insertion happened, and a bool\n            denoting whether the insertion took place.\n\n    @throw type_error.311 when called on a type other than JSON object or\n    null; example: `\"cannot use emplace() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `emplace()` can be used to add elements\n    to a JSON object. Note how the `null` value was silently converted to a\n    JSON object. Further note how no value is added if there was already one\n    value stored with the same key.,emplace}\n\n    @since version 2.0.8\n    */\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&& ... args)\n    {\n        // emplace only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        auto res = m_value.object->emplace(std::forward<Args>(args)...);\n        // create result iterator and set iterator to the result of emplace\n        auto it = begin();\n        it.m_it.object_iterator = res.first;\n\n        // return pair of iterator and boolean\n        return {it, res.second};\n    }\n\n    /// Helper for insertion of an iterator\n    /// @note: This uses std::distance to support GCC 4.8,\n    ///        see https://github.com/nlohmann/json/pull/1257\n    template<typename... Args>\n    iterator insert_iterator(const_iterator pos, Args&& ... args)\n    {\n        iterator result(this);\n        JSON_ASSERT(m_value.array != nullptr);\n\n        auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);\n        m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);\n        result.m_it.array_iterator = m_value.array->begin() + insert_pos;\n\n        // This could have been written as:\n        // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);\n        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.\n\n        return result;\n    }\n\n    /*!\n    @brief inserts element\n\n    Inserts element @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] val element to insert\n    @return iterator pointing to the inserted @a val.\n\n    @throw type_error.309 if called on JSON values other than arrays;\n    example: `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Constant plus linear in the distance between @a pos and end of\n    the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief inserts element\n    @copydoc insert(const_iterator, const basic_json&)\n    */\n    iterator insert(const_iterator pos, basic_json&& val)\n    {\n        return insert(pos, val);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts @a cnt copies of @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] cnt number of copies of @a val to insert\n    @param[in] val element to insert\n    @return iterator pointing to the first element inserted, or @a pos if\n    `cnt==0`\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Linear in @a cnt plus linear in the distance between @a pos\n    and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__count}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, cnt, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)` before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n    @throw invalid_iterator.211 if @a first or @a last are iterators into\n    container for which insert is called; example: `\"passed iterators may not\n    belong to container\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `first==last`\n\n    @complexity Linear in `std::distance(first, last)` plus linear in the\n    distance between @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const_iterator first, const_iterator last)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(first.m_object == this))\n        {\n            JSON_THROW(invalid_iterator::create(211, \"passed iterators may not belong to container\"));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from initializer list @a ilist before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] ilist initializer list to insert the values from\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `ilist` is empty\n\n    @complexity Linear in `ilist.size()` plus linear in the distance between\n    @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__ilist}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, initializer_list_t ilist)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, ilist.begin(), ilist.end());\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)`.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than objects; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if iterator @a first or @a last does does not\n    point to an object; example: `\"iterators first and last must point to\n    objects\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number\n    of elements to insert.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range_object}\n\n    @since version 3.0.0\n    */\n    void insert(const_iterator first, const_iterator last)\n    {\n        // insert only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\"));\n        }\n\n        m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from JSON object @a j and overwrites existing keys.\n\n    @param[in] j  JSON object to read values from\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0\n    */\n    void update(const_reference j)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(type_name())));\n        }\n        if (JSON_HEDLEY_UNLIKELY(!j.is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(j.type_name())));\n        }\n\n        for (auto it = j.cbegin(); it != j.cend(); ++it)\n        {\n            m_value.object->operator[](it.key()) = it.value();\n        }\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from from range `[first, last)` and overwrites existing\n    keys.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n    @throw invalid_iterator.202 if iterator @a first or @a last does does not\n    point to an object; example: `\"iterators first and last must point to\n    objects\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used__range.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0\n    */\n    void update(const_iterator first, const_iterator last)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(type_name())));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()\n                                 || !last.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\"));\n        }\n\n        for (auto it = first; it != last; ++it)\n        {\n            m_value.object->operator[](it.key()) = it.value();\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of the JSON value with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other JSON value to exchange the contents with\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how JSON values can be swapped with\n    `swap()`.,swap__reference}\n\n    @since version 1.0.0\n    */\n    void swap(reference other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        std::swap(m_type, other.m_type);\n        std::swap(m_value, other.m_value);\n        assert_invariant();\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of the JSON value from @a left with those of @a right. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated. implemented as a friend function callable via ADL.\n\n    @param[in,out] left JSON value to exchange the contents with\n    @param[in,out] right JSON value to exchange the contents with\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how JSON values can be swapped with\n    `swap()`.,swap__reference}\n\n    @since version 1.0.0\n    */\n    friend void swap(reference left, reference right) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        left.swap(right);\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON array with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other array to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an array; example: `\"cannot\n    use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how arrays can be swapped with\n    `swap()`.,swap__array_t}\n\n    @since version 1.0.0\n    */\n    void swap(array_t& other)\n    {\n        // swap only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            std::swap(*(m_value.array), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON object with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other object to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an object; example:\n    `\"cannot use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how objects can be swapped with\n    `swap()`.,swap__object_t}\n\n    @since version 1.0.0\n    */\n    void swap(object_t& other)\n    {\n        // swap only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            std::swap(*(m_value.object), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON string with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other string to exchange the contents with\n\n    @throw type_error.310 when JSON value is not a string; example: `\"cannot\n    use swap() with boolean\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how strings can be swapped with\n    `swap()`.,swap__string_t}\n\n    @since version 1.0.0\n    */\n    void swap(string_t& other)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_string()))\n        {\n            std::swap(*(m_value.string), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON string with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other binary to exchange the contents with\n\n    @throw type_error.310 when JSON value is not a string; example: `\"cannot\n    use swap() with boolean\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how strings can be swapped with\n    `swap()`.,swap__binary_t}\n\n    @since version 3.8.0\n    */\n    void swap(binary_t& other)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            std::swap(*(m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /// @copydoc swap(binary_t)\n    void swap(typename binary_t::container_type& other)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            std::swap(*(m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /// @}\n\n  public:\n    //////////////////////////////////////////\n    // lexicographical comparison operators //\n    //////////////////////////////////////////\n\n    /// @name lexicographical comparison operators\n    /// @{\n\n    /*!\n    @brief comparison: equal\n\n    Compares two JSON values for equality according to the following rules:\n    - Two JSON values are equal if (1) they are from the same type and (2)\n      their stored values are the same according to their respective\n      `operator==`.\n    - Integer and floating-point numbers are automatically converted before\n      comparison. Note that two NaN values are always treated as unequal.\n    - Two JSON null values are equal.\n\n    @note Floating-point inside JSON values numbers are compared with\n    `json::number_float_t::operator==` which is `double::operator==` by\n    default. To compare floating-point while respecting an epsilon, an alternative\n    [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39)\n    could be used, for instance\n    @code {.cpp}\n    template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>\n    inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept\n    {\n        return std::abs(a - b) <= epsilon;\n    }\n    @endcode\n    Or you can self-defined operator equal function like this:\n    @code {.cpp}\n    bool my_equal(const_reference lhs, const_reference rhs) {\n    const auto lhs_type lhs.type();\n    const auto rhs_type rhs.type();\n    if (lhs_type == rhs_type) {\n        switch(lhs_type)\n            // self_defined case\n            case value_t::number_float:\n                return std::abs(lhs - rhs) <= std::numeric_limits<float>::epsilon();\n            // other cases remain the same with the original\n            ...\n    }\n    ...\n    }\n    @endcode\n\n    @note NaN values never compare equal to themselves or to other NaN values.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are equal\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Linear.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__equal}\n\n    @since version 1.0.0\n    */\n    friend bool operator==(const_reference lhs, const_reference rhs) noexcept\n    {\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    return *lhs.m_value.array == *rhs.m_value.array;\n\n                case value_t::object:\n                    return *lhs.m_value.object == *rhs.m_value.object;\n\n                case value_t::null:\n                    return true;\n\n                case value_t::string:\n                    return *lhs.m_value.string == *rhs.m_value.string;\n\n                case value_t::boolean:\n                    return lhs.m_value.boolean == rhs.m_value.boolean;\n\n                case value_t::number_integer:\n                    return lhs.m_value.number_integer == rhs.m_value.number_integer;\n\n                case value_t::number_unsigned:\n                    return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;\n\n                case value_t::number_float:\n                    return lhs.m_value.number_float == rhs.m_value.number_float;\n\n                case value_t::binary:\n                    return *lhs.m_value.binary == *rhs.m_value.binary;\n\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n\n        return false;\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs == basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) == rhs;\n    }\n\n    /*!\n    @brief comparison: not equal\n\n    Compares two JSON values for inequality by calculating `not (lhs == rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are not equal\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__notequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator!=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs == rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs != basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) != rhs;\n    }\n\n    /*!\n    @brief comparison: less than\n\n    Compares whether one JSON value @a lhs is less than another JSON value @a\n    rhs according to the following rules:\n    - If @a lhs and @a rhs have the same type, the values are compared using\n      the default `<` operator.\n    - Integer and floating-point numbers are automatically converted before\n      comparison\n    - In case @a lhs and @a rhs have different types, the values are ignored\n      and the order of the types is considered, see\n      @ref operator<(const value_t, const value_t).\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__less}\n\n    @since version 1.0.0\n    */\n    friend bool operator<(const_reference lhs, const_reference rhs) noexcept\n    {\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    // note parentheses are necessary, see\n                    // https://github.com/nlohmann/json/issues/1530\n                    return (*lhs.m_value.array) < (*rhs.m_value.array);\n\n                case value_t::object:\n                    return (*lhs.m_value.object) < (*rhs.m_value.object);\n\n                case value_t::null:\n                    return false;\n\n                case value_t::string:\n                    return (*lhs.m_value.string) < (*rhs.m_value.string);\n\n                case value_t::boolean:\n                    return (lhs.m_value.boolean) < (rhs.m_value.boolean);\n\n                case value_t::number_integer:\n                    return (lhs.m_value.number_integer) < (rhs.m_value.number_integer);\n\n                case value_t::number_unsigned:\n                    return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned);\n\n                case value_t::number_float:\n                    return (lhs.m_value.number_float) < (rhs.m_value.number_float);\n\n                case value_t::binary:\n                    return (*lhs.m_value.binary) < (*rhs.m_value.binary);\n\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;\n        }\n\n        // We only reach this line if we cannot compare values. In that case,\n        // we compare types. Note we have to call the operator explicitly,\n        // because MSVC has problems otherwise.\n        return operator<(lhs_type, rhs_type);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs < basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) < rhs;\n    }\n\n    /*!\n    @brief comparison: less than or equal\n\n    Compares whether one JSON value @a lhs is less than or equal to another\n    JSON value by calculating `not (rhs < lhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greater}\n\n    @since version 1.0.0\n    */\n    friend bool operator<=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(rhs < lhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs <= basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) <= rhs;\n    }\n\n    /*!\n    @brief comparison: greater than\n\n    Compares whether one JSON value @a lhs is greater than another\n    JSON value by calculating `not (lhs <= rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__lessequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs <= rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs > basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) > rhs;\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n\n    Compares whether one JSON value @a lhs is greater than or equal to another\n    JSON value by calculating `not (lhs < rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greaterequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs < rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs >= basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) >= rhs;\n    }\n\n    /// @}\n\n    ///////////////////\n    // serialization //\n    ///////////////////\n\n    /// @name serialization\n    /// @{\n\n    /*!\n    @brief serialize to stream\n\n    Serialize the given JSON value @a j to the output stream @a o. The JSON\n    value will be serialized using the @ref dump member function.\n\n    - The indentation of the output can be controlled with the member variable\n      `width` of the output stream @a o. For instance, using the manipulator\n      `std::setw(4)` on @a o sets the indentation level to `4` and the\n      serialization result is the same as calling `dump(4)`.\n\n    - The indentation character can be controlled with the member variable\n      `fill` of the output stream @a o. For instance, the manipulator\n      `std::setfill('\\\\t')` sets indentation to use a tab character rather than\n      the default space character.\n\n    @param[in,out] o  stream to serialize to\n    @param[in] j  JSON value to serialize\n\n    @return the stream @a o\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded\n\n    @complexity Linear.\n\n    @liveexample{The example below shows the serialization with different\n    parameters to `width` to adjust the indentation level.,operator_serialize}\n\n    @since version 1.0.0; indentation character added in version 3.0.0\n    */\n    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)\n    {\n        // read width member and use it as indentation parameter if nonzero\n        const bool pretty_print = o.width() > 0;\n        const auto indentation = pretty_print ? o.width() : 0;\n\n        // reset width to 0 for subsequent calls to this stream\n        o.width(0);\n\n        // do the actual serialization\n        serializer s(detail::output_adapter<char>(o), o.fill());\n        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));\n        return o;\n    }\n\n    /*!\n    @brief serialize to stream\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use\n                @ref operator<<(std::ostream&, const basic_json&)\n                instead; that is, replace calls like `j >> o;` with `o << j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))\n    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)\n    {\n        return o << j;\n    }\n\n    /// @}\n\n\n    /////////////////////\n    // deserialization //\n    /////////////////////\n\n    /// @name deserialization\n    /// @{\n\n    /*!\n    @brief deserialize from a compatible input\n\n    @tparam InputType A compatible input, for instance\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i  input to read from\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the parser callback function\n    @a cb or reading from the input @a i has a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from an array.,parse__array__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__string__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__istream__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from a contiguous container.,parse__contiguouscontainer__parser_callback_t}\n\n    @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to\n    ignore comments.\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(InputType&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    /*!\n    @brief deserialize from a pair of character iterators\n\n    The value_type of the iterator must be a integral type with size of 1, 2 or\n    4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32.\n\n    @param[in] first iterator to start of character range\n    @param[in] last  iterator to end of character range\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(IteratorType first,\n                            IteratorType last,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))\n    static basic_json parse(detail::span_input_adapter&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    /*!\n    @brief check if the input is valid JSON\n\n    Unlike the @ref parse(InputType&&, const parser_callback_t,const bool)\n    function, this function neither throws an exception in case of invalid JSON\n    input (i.e., a parse error) nor creates diagnostic information.\n\n    @tparam InputType A compatible input, for instance\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i input to read from\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return Whether the input read from @a i is valid JSON.\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `accept()` function reading\n    from a string.,accept__string}\n    */\n    template<typename InputType>\n    static bool accept(InputType&& i,\n                       const bool ignore_comments = false)\n    {\n        return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);\n    }\n\n    template<typename IteratorType>\n    static bool accept(IteratorType first, IteratorType last,\n                       const bool ignore_comments = false)\n    {\n        return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))\n    static bool accept(detail::span_input_adapter&& i,\n                       const bool ignore_comments = false)\n    {\n        return parser(i.get(), nullptr, false, ignore_comments).accept(true);\n    }\n\n    /*!\n    @brief generate SAX events\n\n    The SAX event lister must follow the interface of @ref json_sax.\n\n    This function reads from a compatible input. Examples are:\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i  input to read from\n    @param[in,out] sax  SAX event listener\n    @param[in] format  the format to parse (JSON, CBOR, MessagePack, or UBJSON)\n    @param[in] strict  whether the input has to be consumed completely\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default); only applies to the JSON file format.\n\n    @return return value of the last processed SAX event\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the SAX consumer @a sax has\n    a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `sax_parse()` function\n    reading from string and processing the events with a user-defined SAX\n    event consumer.,sax_parse}\n\n    @since version 3.2.0\n    */\n    template <typename InputType, typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(InputType&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n\n    template<class IteratorType, class SAX>\n    JSON_HEDLEY_NON_NULL(3)\n    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n\n    template <typename SAX>\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = i.get();\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n\n    /*!\n    @brief deserialize from stream\n    @deprecated This stream operator is deprecated and will be removed in\n                version 4.0.0 of the library. Please use\n                @ref operator>>(std::istream&, basic_json&)\n                instead; that is, replace calls like `j << i;` with `i >> j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))\n    friend std::istream& operator<<(basic_json& j, std::istream& i)\n    {\n        return operator>>(i, j);\n    }\n\n    /*!\n    @brief deserialize from stream\n\n    Deserializes an input stream to a JSON value.\n\n    @param[in,out] i  input stream to read a serialized JSON value from\n    @param[in,out] j  JSON value to write the deserialized input to\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below shows how a JSON value is constructed by\n    reading a serialization from a stream.,operator_deserialize}\n\n    @sa parse(std::istream&, const parser_callback_t) for a variant with a\n    parser callback function to filter values while parsing\n\n    @since version 1.0.0\n    */\n    friend std::istream& operator>>(std::istream& i, basic_json& j)\n    {\n        parser(detail::input_adapter(i)).parse(false, j);\n        return i;\n    }\n\n    /// @}\n\n    ///////////////////////////\n    // convenience functions //\n    ///////////////////////////\n\n    /*!\n    @brief return the type as string\n\n    Returns the type name as string to be used in error messages - usually to\n    indicate that a function was called on a wrong JSON type.\n\n    @return a string representation of a the @a m_type member:\n            Value type  | return value\n            ----------- | -------------\n            null        | `\"null\"`\n            boolean     | `\"boolean\"`\n            string      | `\"string\"`\n            number      | `\"number\"` (for all number types)\n            object      | `\"object\"`\n            array       | `\"array\"`\n            binary      | `\"binary\"`\n            discarded   | `\"discarded\"`\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Constant.\n\n    @liveexample{The following code exemplifies `type_name()` for all JSON\n    types.,type_name}\n\n    @sa @ref type() -- return the type of the JSON value\n    @sa @ref operator value_t() -- return the type of the JSON value (implicit)\n\n    @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`\n    since 3.0.0\n    */\n    JSON_HEDLEY_RETURNS_NON_NULL\n    const char* type_name() const noexcept\n    {\n        {\n            switch (m_type)\n            {\n                case value_t::null:\n                    return \"null\";\n                case value_t::object:\n                    return \"object\";\n                case value_t::array:\n                    return \"array\";\n                case value_t::string:\n                    return \"string\";\n                case value_t::boolean:\n                    return \"boolean\";\n                case value_t::binary:\n                    return \"binary\";\n                case value_t::discarded:\n                    return \"discarded\";\n                default:\n                    return \"number\";\n            }\n        }\n    }\n\n\n  private:\n    //////////////////////\n    // member variables //\n    //////////////////////\n\n    /// the type of the current element\n    value_t m_type = value_t::null;\n\n    /// the value of the current element\n    json_value m_value = {};\n\n    //////////////////////////////////////////\n    // binary serialization/deserialization //\n    //////////////////////////////////////////\n\n    /// @name binary serialization/deserialization support\n    /// @{\n\n  public:\n    /*!\n    @brief create a CBOR serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the CBOR (Concise\n    Binary Object Representation) serialization format. CBOR is a binary\n    serialization format which aims to be more compact than JSON itself, yet\n    more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    CBOR types according to the CBOR specification (RFC 7049):\n\n    JSON value type | value/range                                | CBOR type                          | first byte\n    --------------- | ------------------------------------------ | ---------------------------------- | ---------------\n    null            | `null`                                     | Null                               | 0xF6\n    boolean         | `true`                                     | True                               | 0xF5\n    boolean         | `false`                                    | False                              | 0xF4\n    number_integer  | -9223372036854775808..-2147483649          | Negative integer (8 bytes follow)  | 0x3B\n    number_integer  | -2147483648..-32769                        | Negative integer (4 bytes follow)  | 0x3A\n    number_integer  | -32768..-129                               | Negative integer (2 bytes follow)  | 0x39\n    number_integer  | -128..-25                                  | Negative integer (1 byte follow)   | 0x38\n    number_integer  | -24..-1                                    | Negative integer                   | 0x20..0x37\n    number_integer  | 0..23                                      | Integer                            | 0x00..0x17\n    number_integer  | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_integer  | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_integer  | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_integer  | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_unsigned | 0..23                                      | Integer                            | 0x00..0x17\n    number_unsigned | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_unsigned | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_unsigned | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_unsigned | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_float    | *any value representable by a float*       | Single-Precision Float             | 0xFA\n    number_float    | *any value NOT representable by a float*   | Double-Precision Float             | 0xFB\n    string          | *length*: 0..23                            | UTF-8 string                       | 0x60..0x77\n    string          | *length*: 23..255                          | UTF-8 string (1 byte follow)       | 0x78\n    string          | *length*: 256..65535                       | UTF-8 string (2 bytes follow)      | 0x79\n    string          | *length*: 65536..4294967295                | UTF-8 string (4 bytes follow)      | 0x7A\n    string          | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow)      | 0x7B\n    array           | *size*: 0..23                              | array                              | 0x80..0x97\n    array           | *size*: 23..255                            | array (1 byte follow)              | 0x98\n    array           | *size*: 256..65535                         | array (2 bytes follow)             | 0x99\n    array           | *size*: 65536..4294967295                  | array (4 bytes follow)             | 0x9A\n    array           | *size*: 4294967296..18446744073709551615   | array (8 bytes follow)             | 0x9B\n    object          | *size*: 0..23                              | map                                | 0xA0..0xB7\n    object          | *size*: 23..255                            | map (1 byte follow)                | 0xB8\n    object          | *size*: 256..65535                         | map (2 bytes follow)               | 0xB9\n    object          | *size*: 65536..4294967295                  | map (4 bytes follow)               | 0xBA\n    object          | *size*: 4294967296..18446744073709551615   | map (8 bytes follow)               | 0xBB\n    binary          | *size*: 0..23                              | byte string                        | 0x40..0x57\n    binary          | *size*: 23..255                            | byte string (1 byte follow)        | 0x58\n    binary          | *size*: 256..65535                         | byte string (2 bytes follow)       | 0x59\n    binary          | *size*: 65536..4294967295                  | byte string (4 bytes follow)       | 0x5A\n    binary          | *size*: 4294967296..18446744073709551615   | byte string (8 bytes follow)       | 0x5B\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a CBOR value.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The following CBOR types are not used in the conversion:\n          - UTF-8 strings terminated by \"break\" (0x7F)\n          - arrays terminated by \"break\" (0x9F)\n          - maps terminated by \"break\" (0xBF)\n          - byte strings terminated by \"break\" (0x5F)\n          - date/time (0xC0..0xC1)\n          - bignum (0xC2..0xC3)\n          - decimal fraction (0xC4)\n          - bigfloat (0xC5)\n          - expected conversions (0xD5..0xD7)\n          - simple values (0xE0..0xF3, 0xF8)\n          - undefined (0xF7)\n          - half-precision floats (0xF9)\n          - break (0xFF)\n\n    @param[in] j  JSON value to serialize\n    @return CBOR serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in CBOR format.,to_cbor}\n\n    @sa http://cbor.io\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the\n        analogous deserialization\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9; compact representation of floating-point numbers\n           since version 3.8.0\n    */\n    static std::vector<uint8_t> to_cbor(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_cbor(j, result);\n        return result;\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_cbor(j);\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_cbor(j);\n    }\n\n    /*!\n    @brief create a MessagePack serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the MessagePack\n    serialization format. MessagePack is a binary serialization format which\n    aims to be more compact than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    MessagePack types according to the MessagePack specification:\n\n    JSON value type | value/range                       | MessagePack type | first byte\n    --------------- | --------------------------------- | ---------------- | ----------\n    null            | `null`                            | nil              | 0xC0\n    boolean         | `true`                            | true             | 0xC3\n    boolean         | `false`                           | false            | 0xC2\n    number_integer  | -9223372036854775808..-2147483649 | int64            | 0xD3\n    number_integer  | -2147483648..-32769               | int32            | 0xD2\n    number_integer  | -32768..-129                      | int16            | 0xD1\n    number_integer  | -128..-33                         | int8             | 0xD0\n    number_integer  | -32..-1                           | negative fixint  | 0xE0..0xFF\n    number_integer  | 0..127                            | positive fixint  | 0x00..0x7F\n    number_integer  | 128..255                          | uint 8           | 0xCC\n    number_integer  | 256..65535                        | uint 16          | 0xCD\n    number_integer  | 65536..4294967295                 | uint 32          | 0xCE\n    number_integer  | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_unsigned | 0..127                            | positive fixint  | 0x00..0x7F\n    number_unsigned | 128..255                          | uint 8           | 0xCC\n    number_unsigned | 256..65535                        | uint 16          | 0xCD\n    number_unsigned | 65536..4294967295                 | uint 32          | 0xCE\n    number_unsigned | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_float    | *any value representable by a float*     | float 32 | 0xCA\n    number_float    | *any value NOT representable by a float* | float 64 | 0xCB\n    string          | *length*: 0..31                   | fixstr           | 0xA0..0xBF\n    string          | *length*: 32..255                 | str 8            | 0xD9\n    string          | *length*: 256..65535              | str 16           | 0xDA\n    string          | *length*: 65536..4294967295       | str 32           | 0xDB\n    array           | *size*: 0..15                     | fixarray         | 0x90..0x9F\n    array           | *size*: 16..65535                 | array 16         | 0xDC\n    array           | *size*: 65536..4294967295         | array 32         | 0xDD\n    object          | *size*: 0..15                     | fix map          | 0x80..0x8F\n    object          | *size*: 16..65535                 | map 16           | 0xDE\n    object          | *size*: 65536..4294967295         | map 32           | 0xDF\n    binary          | *size*: 0..255                    | bin 8            | 0xC4\n    binary          | *size*: 256..65535                | bin 16           | 0xC5\n    binary          | *size*: 65536..4294967295         | bin 32           | 0xC6\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a MessagePack value.\n\n    @note The following values can **not** be converted to a MessagePack value:\n          - strings with more than 4294967295 bytes\n          - byte strings with more than 4294967295 bytes\n          - arrays with more than 4294967295 elements\n          - objects with more than 4294967295 elements\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @param[in] j  JSON value to serialize\n    @return MessagePack serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in MessagePack format.,to_msgpack}\n\n    @sa http://msgpack.org\n    @sa @ref from_msgpack for the analogous deserialization\n    @sa @ref to_cbor(const basic_json& for the related CBOR format\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9\n    */\n    static std::vector<uint8_t> to_msgpack(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_msgpack(j, result);\n        return result;\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_msgpack(j);\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_msgpack(j);\n    }\n\n    /*!\n    @brief create a UBJSON serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the UBJSON\n    (Universal Binary JSON) serialization format. UBJSON aims to be more compact\n    than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    UBJSON types according to the UBJSON specification:\n\n    JSON value type | value/range                       | UBJSON type | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | `Z`\n    boolean         | `true`                            | true        | `T`\n    boolean         | `false`                           | false       | `F`\n    number_integer  | -9223372036854775808..-2147483649 | int64       | `L`\n    number_integer  | -2147483648..-32769               | int32       | `l`\n    number_integer  | -32768..-129                      | int16       | `I`\n    number_integer  | -128..127                         | int8        | `i`\n    number_integer  | 128..255                          | uint8       | `U`\n    number_integer  | 256..32767                        | int16       | `I`\n    number_integer  | 32768..2147483647                 | int32       | `l`\n    number_integer  | 2147483648..9223372036854775807   | int64       | `L`\n    number_unsigned | 0..127                            | int8        | `i`\n    number_unsigned | 128..255                          | uint8       | `U`\n    number_unsigned | 256..32767                        | int16       | `I`\n    number_unsigned | 32768..2147483647                 | int32       | `l`\n    number_unsigned | 2147483648..9223372036854775807   | int64       | `L`\n    number_unsigned | 2147483649..18446744073709551615  | high-precision | `H`\n    number_float    | *any value*                       | float64     | `D`\n    string          | *with shortest length indicator*  | string      | `S`\n    array           | *see notes on optimized format*   | array       | `[`\n    object          | *see notes on optimized format*   | map         | `{`\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a UBJSON value.\n\n    @note The following values can **not** be converted to a UBJSON value:\n          - strings with more than 9223372036854775807 bytes (theoretical)\n\n    @note The following markers are not used in the conversion:\n          - `Z`: no-op values are not created.\n          - `C`: single-byte strings are serialized with `S` markers.\n\n    @note Any UBJSON output created @ref to_ubjson can be successfully parsed\n          by @ref from_ubjson.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The optimized formats for containers are supported: Parameter\n          @a use_size adds size information to the beginning of a container and\n          removes the closing marker. Parameter @a use_type further checks\n          whether all elements of a container have the same type and adds the\n          type marker to the beginning of the container. The @a use_type\n          parameter must only be used together with @a use_size = true. Note\n          that @a use_size = true alone may result in larger representations -\n          the benefit of this parameter is that the receiving side is\n          immediately informed on the number of elements of the container.\n\n    @note If the JSON data contains the binary type, the value stored is a list\n          of integers, as suggested by the UBJSON documentation.  In particular,\n          this means that serialization and the deserialization of a JSON\n          containing binary values into UBJSON and back will result in a\n          different JSON object.\n\n    @param[in] j  JSON value to serialize\n    @param[in] use_size  whether to add size annotations to container types\n    @param[in] use_type  whether to add type annotations to container types\n                         (must be combined with @a use_size = true)\n    @return UBJSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in UBJSON format.,to_ubjson}\n\n    @sa http://ubjson.org\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        analogous deserialization\n    @sa @ref to_cbor(const basic_json& for the related CBOR format\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n\n    @since version 3.1.0\n    */\n    static std::vector<uint8_t> to_ubjson(const basic_json& j,\n                                          const bool use_size = false,\n                                          const bool use_type = false)\n    {\n        std::vector<uint8_t> result;\n        to_ubjson(j, result, use_size, use_type);\n        return result;\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<uint8_t> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<uint8_t>(o).write_ubjson(j, use_size, use_type);\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<char>(o).write_ubjson(j, use_size, use_type);\n    }\n\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and returns a vector\n           containing the corresponding BSON-representation.\n\n    BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are\n    stored as a single entity (a so-called document).\n\n    The library uses the following mapping from JSON values types to BSON types:\n\n    JSON value type | value/range                       | BSON type   | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | 0x0A\n    boolean         | `true`, `false`                   | boolean     | 0x08\n    number_integer  | -9223372036854775808..-2147483649 | int64       | 0x12\n    number_integer  | -2147483648..2147483647           | int32       | 0x10\n    number_integer  | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 0..2147483647                     | int32       | 0x10\n    number_unsigned | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 9223372036854775808..18446744073709551615| --   | --\n    number_float    | *any value*                       | double      | 0x01\n    string          | *any value*                       | string      | 0x02\n    array           | *any value*                       | document    | 0x04\n    object          | *any value*                       | document    | 0x03\n    binary          | *any value*                       | binary      | 0x05\n\n    @warning The mapping is **incomplete**, since only JSON-objects (and things\n    contained therein) can be serialized to BSON.\n    Also, integers larger than 9223372036854775807 cannot be serialized to BSON,\n    and the keys may not contain U+0000, since they are serialized a\n    zero-terminated c-strings.\n\n    @throw out_of_range.407  if `j.is_number_unsigned() && j.get<std::uint64_t>() > 9223372036854775807`\n    @throw out_of_range.409  if a key in `j` contains a NULL (U+0000)\n    @throw type_error.317    if `!j.is_object()`\n\n    @pre The input `j` is required to be an object: `j.is_object() == true`.\n\n    @note Any BSON output created via @ref to_bson can be successfully parsed\n          by @ref from_bson.\n\n    @param[in] j  JSON value to serialize\n    @return BSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in BSON format.,to_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa @ref from_bson(detail::input_adapter&&, const bool strict) for the\n        analogous deserialization\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n    @sa @ref to_cbor(const basic_json&) for the related CBOR format\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n    */\n    static std::vector<uint8_t> to_bson(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_bson(j, result);\n        return result;\n    }\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and forwards the\n           corresponding BSON-representation to the given output_adapter `o`.\n    @param j The JSON object to convert to BSON.\n    @param o The output adapter that receives the binary BSON representation.\n    @pre The input `j` shall be an object: `j.is_object() == true`\n    @sa @ref to_bson(const basic_json&)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_bson(j);\n    }\n\n    /*!\n    @copydoc to_bson(const basic_json&, detail::output_adapter<uint8_t>)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_bson(j);\n    }\n\n\n    /*!\n    @brief create a JSON value from an input in CBOR format\n\n    Deserializes a given input @a i to a JSON value using the CBOR (Concise\n    Binary Object Representation) serialization format.\n\n    The library maps CBOR types to JSON value types as follows:\n\n    CBOR type              | JSON value type | first byte\n    ---------------------- | --------------- | ----------\n    Integer                | number_unsigned | 0x00..0x17\n    Unsigned integer       | number_unsigned | 0x18\n    Unsigned integer       | number_unsigned | 0x19\n    Unsigned integer       | number_unsigned | 0x1A\n    Unsigned integer       | number_unsigned | 0x1B\n    Negative integer       | number_integer  | 0x20..0x37\n    Negative integer       | number_integer  | 0x38\n    Negative integer       | number_integer  | 0x39\n    Negative integer       | number_integer  | 0x3A\n    Negative integer       | number_integer  | 0x3B\n    Byte string            | binary          | 0x40..0x57\n    Byte string            | binary          | 0x58\n    Byte string            | binary          | 0x59\n    Byte string            | binary          | 0x5A\n    Byte string            | binary          | 0x5B\n    UTF-8 string           | string          | 0x60..0x77\n    UTF-8 string           | string          | 0x78\n    UTF-8 string           | string          | 0x79\n    UTF-8 string           | string          | 0x7A\n    UTF-8 string           | string          | 0x7B\n    UTF-8 string           | string          | 0x7F\n    array                  | array           | 0x80..0x97\n    array                  | array           | 0x98\n    array                  | array           | 0x99\n    array                  | array           | 0x9A\n    array                  | array           | 0x9B\n    array                  | array           | 0x9F\n    map                    | object          | 0xA0..0xB7\n    map                    | object          | 0xB8\n    map                    | object          | 0xB9\n    map                    | object          | 0xBA\n    map                    | object          | 0xBB\n    map                    | object          | 0xBF\n    False                  | `false`         | 0xF4\n    True                   | `true`          | 0xF5\n    Null                   | `null`          | 0xF6\n    Half-Precision Float   | number_float    | 0xF9\n    Single-Precision Float | number_float    | 0xFA\n    Double-Precision Float | number_float    | 0xFB\n\n    @warning The mapping is **incomplete** in the sense that not all CBOR\n             types can be converted to a JSON value. The following CBOR types\n             are not supported and will yield parse errors (parse_error.112):\n             - date/time (0xC0..0xC1)\n             - bignum (0xC2..0xC3)\n             - decimal fraction (0xC4)\n             - bigfloat (0xC5)\n             - expected conversions (0xD5..0xD7)\n             - simple values (0xE0..0xF3, 0xF8)\n             - undefined (0xF7)\n\n    @warning CBOR allows map keys of any type, whereas JSON only allows\n             strings as keys in object values. Therefore, CBOR maps with keys\n             other than UTF-8 strings are rejected (parse_error.113).\n\n    @note Any CBOR output created @ref to_cbor can be successfully parsed by\n          @ref from_cbor.\n\n    @param[in] i  an input in CBOR format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] tag_handler how to treat CBOR tags (optional, error by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from CBOR were\n    used in the given input @a v or if the input is not valid CBOR\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in CBOR\n    format to a JSON value.,from_cbor}\n\n    @sa http://cbor.io\n    @sa @ref to_cbor(const basic_json&) for the analogous serialization\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the\n        related MessagePack format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        related UBJSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0; added @a tag_handler parameter since 3.9.0.\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);\n    }\n\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @brief create a JSON value from an input in MessagePack format\n\n    Deserializes a given input @a i to a JSON value using the MessagePack\n    serialization format.\n\n    The library maps MessagePack types to JSON value types as follows:\n\n    MessagePack type | JSON value type | first byte\n    ---------------- | --------------- | ----------\n    positive fixint  | number_unsigned | 0x00..0x7F\n    fixmap           | object          | 0x80..0x8F\n    fixarray         | array           | 0x90..0x9F\n    fixstr           | string          | 0xA0..0xBF\n    nil              | `null`          | 0xC0\n    false            | `false`         | 0xC2\n    true             | `true`          | 0xC3\n    float 32         | number_float    | 0xCA\n    float 64         | number_float    | 0xCB\n    uint 8           | number_unsigned | 0xCC\n    uint 16          | number_unsigned | 0xCD\n    uint 32          | number_unsigned | 0xCE\n    uint 64          | number_unsigned | 0xCF\n    int 8            | number_integer  | 0xD0\n    int 16           | number_integer  | 0xD1\n    int 32           | number_integer  | 0xD2\n    int 64           | number_integer  | 0xD3\n    str 8            | string          | 0xD9\n    str 16           | string          | 0xDA\n    str 32           | string          | 0xDB\n    array 16         | array           | 0xDC\n    array 32         | array           | 0xDD\n    map 16           | object          | 0xDE\n    map 32           | object          | 0xDF\n    bin 8            | binary          | 0xC4\n    bin 16           | binary          | 0xC5\n    bin 32           | binary          | 0xC6\n    ext 8            | binary          | 0xC7\n    ext 16           | binary          | 0xC8\n    ext 32           | binary          | 0xC9\n    fixext 1         | binary          | 0xD4\n    fixext 2         | binary          | 0xD5\n    fixext 4         | binary          | 0xD6\n    fixext 8         | binary          | 0xD7\n    fixext 16        | binary          | 0xD8\n    negative fixint  | number_integer  | 0xE0-0xFF\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @param[in] i  an input in MessagePack format convertible to an input\n                  adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from MessagePack were\n    used in the given input @a i or if the input is not valid MessagePack\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    MessagePack format to a JSON value.,from_msgpack}\n\n    @sa http://msgpack.org\n    @sa @ref to_msgpack(const basic_json&) for the analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for\n        the related UBJSON format\n    @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(InputType&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(IteratorType first, IteratorType last,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(const T* ptr, std::size_t len,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        return from_msgpack(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(detail::span_input_adapter&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    /*!\n    @brief create a JSON value from an input in UBJSON format\n\n    Deserializes a given input @a i to a JSON value using the UBJSON (Universal\n    Binary JSON) serialization format.\n\n    The library maps UBJSON types to JSON value types as follows:\n\n    UBJSON type | JSON value type                         | marker\n    ----------- | --------------------------------------- | ------\n    no-op       | *no value, next value is read*          | `N`\n    null        | `null`                                  | `Z`\n    false       | `false`                                 | `F`\n    true        | `true`                                  | `T`\n    float32     | number_float                            | `d`\n    float64     | number_float                            | `D`\n    uint8       | number_unsigned                         | `U`\n    int8        | number_integer                          | `i`\n    int16       | number_integer                          | `I`\n    int32       | number_integer                          | `l`\n    int64       | number_integer                          | `L`\n    high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H'\n    string      | string                                  | `S`\n    char        | string                                  | `C`\n    array       | array (optimized values are supported)  | `[`\n    object      | object (optimized values are supported) | `{`\n\n    @note The mapping is **complete** in the sense that any UBJSON value can\n          be converted to a JSON value.\n\n    @param[in] i  an input in UBJSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if a parse error occurs\n    @throw parse_error.113 if a string could not be parsed successfully\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    UBJSON format to a JSON value.,from_ubjson}\n\n    @sa http://ubjson.org\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for\n        the related MessagePack format\n    @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(InputType&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(IteratorType first, IteratorType last,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(const T* ptr, std::size_t len,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        return from_ubjson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(detail::span_input_adapter&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    /*!\n    @brief Create a JSON value from an input in BSON format\n\n    Deserializes a given input @a i to a JSON value using the BSON (Binary JSON)\n    serialization format.\n\n    The library maps BSON record types to JSON value types as follows:\n\n    BSON type       | BSON marker byte | JSON value type\n    --------------- | ---------------- | ---------------------------\n    double          | 0x01             | number_float\n    string          | 0x02             | string\n    document        | 0x03             | object\n    array           | 0x04             | array\n    binary          | 0x05             | still unsupported\n    undefined       | 0x06             | still unsupported\n    ObjectId        | 0x07             | still unsupported\n    boolean         | 0x08             | boolean\n    UTC Date-Time   | 0x09             | still unsupported\n    null            | 0x0A             | null\n    Regular Expr.   | 0x0B             | still unsupported\n    DB Pointer      | 0x0C             | still unsupported\n    JavaScript Code | 0x0D             | still unsupported\n    Symbol          | 0x0E             | still unsupported\n    JavaScript Code | 0x0F             | still unsupported\n    int32           | 0x10             | number_integer\n    Timestamp       | 0x11             | still unsupported\n    128-bit decimal float | 0x13       | still unsupported\n    Max Key         | 0x7F             | still unsupported\n    Min Key         | 0xFF             | still unsupported\n\n    @warning The mapping is **incomplete**. The unsupported mappings\n             are indicated in the table above.\n\n    @param[in] i  an input in BSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.114 if an unsupported BSON record type is encountered\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    BSON format to a JSON value.,from_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa @ref to_bson(const basic_json&) for the analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for\n        the related MessagePack format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        related UBJSON format\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_bson(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        return from_bson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n    /// @}\n\n    //////////////////////////\n    // JSON Pointer support //\n    //////////////////////////\n\n    /// @name JSON Pointer functions\n    /// @{\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. Similar to @ref operator[](const typename\n    object_t::key_type&), `null` values are created in arrays and objects if\n    necessary.\n\n    In particular:\n    - If the JSON pointer points to an object key that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned.\n    - If the JSON pointer points to an array index that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned. All indices between the current maximum and the given\n      index are also filled with `null`.\n    - The special value `-` is treated as a synonym for the index past the\n      end.\n\n    @param[in] ptr  a JSON pointer\n\n    @return reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer}\n\n    @since version 2.0.0\n    */\n    reference operator[](const json_pointer& ptr)\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. The function does not change the JSON\n    value; no `null` values are created. In particular, the special value\n    `-` yields an exception.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return const reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}\n\n    @since version 2.0.0\n    */\n    const_reference operator[](const json_pointer& ptr) const\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a reference to the element at with specified JSON pointer @a ptr,\n    with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer}\n    */\n    reference at(const json_pointer& ptr)\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a const reference to the element at with specified JSON pointer @a\n    ptr, with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer_const}\n    */\n    const_reference at(const json_pointer& ptr) const\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief return flattened JSON value\n\n    The function creates a JSON object whose keys are JSON pointers (see [RFC\n    6901](https://tools.ietf.org/html/rfc6901)) and whose values are all\n    primitive. The original JSON value can be restored using the @ref\n    unflatten() function.\n\n    @return an object that maps JSON pointers to primitive values\n\n    @note Empty objects and arrays are flattened to `null` and will not be\n          reconstructed correctly by the @ref unflatten() function.\n\n    @complexity Linear in the size the JSON value.\n\n    @liveexample{The following code shows how a JSON object is flattened to an\n    object whose keys consist of JSON pointers.,flatten}\n\n    @sa @ref unflatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json flatten() const\n    {\n        basic_json result(value_t::object);\n        json_pointer::flatten(\"\", *this, result);\n        return result;\n    }\n\n    /*!\n    @brief unflatten a previously flattened JSON value\n\n    The function restores the arbitrary nesting of a JSON value that has been\n    flattened before using the @ref flatten() function. The JSON value must\n    meet certain constraints:\n    1. The value must be an object.\n    2. The keys must be JSON pointers (see\n       [RFC 6901](https://tools.ietf.org/html/rfc6901))\n    3. The mapped values must be primitive JSON types.\n\n    @return the original JSON from a flattened version\n\n    @note Empty objects and arrays are flattened by @ref flatten() to `null`\n          values and can not unflattened to their original type. Apart from\n          this example, for a JSON value `j`, the following is always true:\n          `j == j.flatten().unflatten()`.\n\n    @complexity Linear in the size the JSON value.\n\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n\n    @liveexample{The following code shows how a flattened JSON object is\n    unflattened into the original nested JSON object.,unflatten}\n\n    @sa @ref flatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json unflatten() const\n    {\n        return json_pointer::unflatten(*this);\n    }\n\n    /// @}\n\n    //////////////////////////\n    // JSON Patch functions //\n    //////////////////////////\n\n    /// @name JSON Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON patch\n\n    [JSON Patch](http://jsonpatch.com) defines a JSON document structure for\n    expressing a sequence of operations to apply to a JSON) document. With\n    this function, a JSON Patch is applied to the current JSON value by\n    executing all operations from the patch.\n\n    @param[in] json_patch  JSON patch document\n    @return patched document\n\n    @note The application of a patch is atomic: Either all operations succeed\n          and the patched document is returned or an exception is thrown. In\n          any case, the original value is not changed: the patch is applied\n          to a copy of the value.\n\n    @throw parse_error.104 if the JSON patch does not consist of an array of\n    objects\n\n    @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory\n    attributes are missing); example: `\"operation add must have member path\"`\n\n    @throw out_of_range.401 if an array index is out of range.\n\n    @throw out_of_range.403 if a JSON pointer inside the patch could not be\n    resolved successfully in the current JSON value; example: `\"key baz not\n    found\"`\n\n    @throw out_of_range.405 if JSON pointer has no parent (\"add\", \"remove\",\n    \"move\")\n\n    @throw other_error.501 if \"test\" operation was unsuccessful\n\n    @complexity Linear in the size of the JSON value and the length of the\n    JSON patch. As usually only a fraction of the JSON value is affected by\n    the patch, the complexity can usually be neglected.\n\n    @liveexample{The following code shows how a JSON patch is applied to a\n    value.,patch}\n\n    @sa @ref diff -- create a JSON patch by comparing two JSON values\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n    @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)\n\n    @since version 2.0.0\n    */\n    basic_json patch(const basic_json& json_patch) const\n    {\n        // make a working copy to apply the patch to\n        basic_json result = *this;\n\n        // the valid JSON Patch operations\n        enum class patch_operations {add, remove, replace, move, copy, test, invalid};\n\n        const auto get_op = [](const std::string & op)\n        {\n            if (op == \"add\")\n            {\n                return patch_operations::add;\n            }\n            if (op == \"remove\")\n            {\n                return patch_operations::remove;\n            }\n            if (op == \"replace\")\n            {\n                return patch_operations::replace;\n            }\n            if (op == \"move\")\n            {\n                return patch_operations::move;\n            }\n            if (op == \"copy\")\n            {\n                return patch_operations::copy;\n            }\n            if (op == \"test\")\n            {\n                return patch_operations::test;\n            }\n\n            return patch_operations::invalid;\n        };\n\n        // wrapper for \"add\" operation; add value at ptr\n        const auto operation_add = [&result](json_pointer & ptr, basic_json val)\n        {\n            // adding to the root of the target document means replacing it\n            if (ptr.empty())\n            {\n                result = val;\n                return;\n            }\n\n            // make sure the top element of the pointer exists\n            json_pointer top_pointer = ptr.top();\n            if (top_pointer != ptr)\n            {\n                result.at(top_pointer);\n            }\n\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result[ptr];\n\n            switch (parent.m_type)\n            {\n                case value_t::null:\n                case value_t::object:\n                {\n                    // use operator[] to add value\n                    parent[last_path] = val;\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    if (last_path == \"-\")\n                    {\n                        // special case: append to back\n                        parent.push_back(val);\n                    }\n                    else\n                    {\n                        const auto idx = json_pointer::array_index(last_path);\n                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))\n                        {\n                            // avoid undefined behavior\n                            JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n                        }\n\n                        // default case: insert add offset\n                        parent.insert(parent.begin() + static_cast<difference_type>(idx), val);\n                    }\n                    break;\n                }\n\n                // if there exists a parent it cannot be primitive\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false);  // LCOV_EXCL_LINE\n            }\n        };\n\n        // wrapper for \"remove\" operation; remove value at ptr\n        const auto operation_remove = [&result](json_pointer & ptr)\n        {\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result.at(ptr);\n\n            // remove child\n            if (parent.is_object())\n            {\n                // perform range check\n                auto it = parent.find(last_path);\n                if (JSON_HEDLEY_LIKELY(it != parent.end()))\n                {\n                    parent.erase(it);\n                }\n                else\n                {\n                    JSON_THROW(out_of_range::create(403, \"key '\" + last_path + \"' not found\"));\n                }\n            }\n            else if (parent.is_array())\n            {\n                // note erase performs range check\n                parent.erase(json_pointer::array_index(last_path));\n            }\n        };\n\n        // type check: top level value must be an array\n        if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))\n        {\n            JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\"));\n        }\n\n        // iterate and apply the operations\n        for (const auto& val : json_patch)\n        {\n            // wrapper to get a value for an operation\n            const auto get_value = [&val](const std::string & op,\n                                          const std::string & member,\n                                          bool string_type) -> basic_json &\n            {\n                // find value\n                auto it = val.m_value.object->find(member);\n\n                // context-sensitive error message\n                const auto error_msg = (op == \"op\") ? \"operation\" : \"operation '\" + op + \"'\";\n\n                // check if desired value is present\n                if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))\n                {\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have member '\" + member + \"'\"));\n                }\n\n                // check if result is of type string\n                if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))\n                {\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have string member '\" + member + \"'\"));\n                }\n\n                // no error: return value\n                return it->second;\n            };\n\n            // type check: every element of the array must be an object\n            if (JSON_HEDLEY_UNLIKELY(!val.is_object()))\n            {\n                JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\"));\n            }\n\n            // collect mandatory members\n            const auto op = get_value(\"op\", \"op\", true).template get<std::string>();\n            const auto path = get_value(op, \"path\", true).template get<std::string>();\n            json_pointer ptr(path);\n\n            switch (get_op(op))\n            {\n                case patch_operations::add:\n                {\n                    operation_add(ptr, get_value(\"add\", \"value\", false));\n                    break;\n                }\n\n                case patch_operations::remove:\n                {\n                    operation_remove(ptr);\n                    break;\n                }\n\n                case patch_operations::replace:\n                {\n                    // the \"path\" location must exist - use at()\n                    result.at(ptr) = get_value(\"replace\", \"value\", false);\n                    break;\n                }\n\n                case patch_operations::move:\n                {\n                    const auto from_path = get_value(\"move\", \"from\", true).template get<std::string>();\n                    json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The move operation is functionally identical to a\n                    // \"remove\" operation on the \"from\" location, followed\n                    // immediately by an \"add\" operation at the target\n                    // location with the value that was just removed.\n                    operation_remove(from_ptr);\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::copy:\n                {\n                    const auto from_path = get_value(\"copy\", \"from\", true).template get<std::string>();\n                    const json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The copy is functionally identical to an \"add\"\n                    // operation at the target location using the value\n                    // specified in the \"from\" member.\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::test:\n                {\n                    bool success = false;\n                    JSON_TRY\n                    {\n                        // check if \"value\" matches the one at \"path\"\n                        // the \"path\" location must exist - use at()\n                        success = (result.at(ptr) == get_value(\"test\", \"value\", false));\n                    }\n                    JSON_INTERNAL_CATCH (out_of_range&)\n                    {\n                        // ignore out of range errors: success remains false\n                    }\n\n                    // throw an exception if test fails\n                    if (JSON_HEDLEY_UNLIKELY(!success))\n                    {\n                        JSON_THROW(other_error::create(501, \"unsuccessful: \" + val.dump()));\n                    }\n\n                    break;\n                }\n\n                default:\n                {\n                    // op must be \"add\", \"remove\", \"replace\", \"move\", \"copy\", or\n                    // \"test\"\n                    JSON_THROW(parse_error::create(105, 0, \"operation value '\" + op + \"' is invalid\"));\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief creates a diff as a JSON patch\n\n    Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can\n    be changed into the value @a target by calling @ref patch function.\n\n    @invariant For two JSON values @a source and @a target, the following code\n    yields always `true`:\n    @code {.cpp}\n    source.patch(diff(source, target)) == target;\n    @endcode\n\n    @note Currently, only `remove`, `add`, and `replace` operations are\n          generated.\n\n    @param[in] source  JSON value to compare from\n    @param[in] target  JSON value to compare against\n    @param[in] path    helper value to create JSON pointers\n\n    @return a JSON patch to convert the @a source to @a target\n\n    @complexity Linear in the lengths of @a source and @a target.\n\n    @liveexample{The following code shows how a JSON patch is created as a\n    diff for two JSON values.,diff}\n\n    @sa @ref patch -- apply a JSON patch\n    @sa @ref merge_patch -- apply a JSON Merge Patch\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n\n    @since version 2.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json diff(const basic_json& source, const basic_json& target,\n                           const std::string& path = \"\")\n    {\n        // the patch\n        basic_json result(value_t::array);\n\n        // if the values are the same, return empty patch\n        if (source == target)\n        {\n            return result;\n        }\n\n        if (source.type() != target.type())\n        {\n            // different types: replace value\n            result.push_back(\n            {\n                {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n            });\n            return result;\n        }\n\n        switch (source.type())\n        {\n            case value_t::array:\n            {\n                // first pass: traverse common elements\n                std::size_t i = 0;\n                while (i < source.size() && i < target.size())\n                {\n                    // recursive call to compare array values at index i\n                    auto temp_diff = diff(source[i], target[i], path + \"/\" + std::to_string(i));\n                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    ++i;\n                }\n\n                // i now reached the end of at least one array\n                // in a second pass, traverse the remaining elements\n\n                // remove my remaining elements\n                const auto end_index = static_cast<difference_type>(result.size());\n                while (i < source.size())\n                {\n                    // add operations in reverse order to avoid invalid\n                    // indices\n                    result.insert(result.begin() + end_index, object(\n                    {\n                        {\"op\", \"remove\"},\n                        {\"path\", path + \"/\" + std::to_string(i)}\n                    }));\n                    ++i;\n                }\n\n                // add other remaining elements\n                while (i < target.size())\n                {\n                    result.push_back(\n                    {\n                        {\"op\", \"add\"},\n                        {\"path\", path + \"/-\"},\n                        {\"value\", target[i]}\n                    });\n                    ++i;\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // first pass: traverse this object's elements\n                for (auto it = source.cbegin(); it != source.cend(); ++it)\n                {\n                    // escape the key name to be used in a JSON patch\n                    const auto key = json_pointer::escape(it.key());\n\n                    if (target.find(it.key()) != target.end())\n                    {\n                        // recursive call to compare object values at key it\n                        auto temp_diff = diff(it.value(), target[it.key()], path + \"/\" + key);\n                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    }\n                    else\n                    {\n                        // found a key that is not in o -> remove it\n                        result.push_back(object(\n                        {\n                            {\"op\", \"remove\"}, {\"path\", path + \"/\" + key}\n                        }));\n                    }\n                }\n\n                // second pass: traverse other object's elements\n                for (auto it = target.cbegin(); it != target.cend(); ++it)\n                {\n                    if (source.find(it.key()) == source.end())\n                    {\n                        // found a key that is not in this -> add it\n                        const auto key = json_pointer::escape(it.key());\n                        result.push_back(\n                        {\n                            {\"op\", \"add\"}, {\"path\", path + \"/\" + key},\n                            {\"value\", it.value()}\n                        });\n                    }\n                }\n\n                break;\n            }\n\n            default:\n            {\n                // both primitive type: replace value\n                result.push_back(\n                {\n                    {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n                });\n                break;\n            }\n        }\n\n        return result;\n    }\n\n    /// @}\n\n    ////////////////////////////////\n    // JSON Merge Patch functions //\n    ////////////////////////////////\n\n    /// @name JSON Merge Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON Merge Patch\n\n    The merge patch format is primarily intended for use with the HTTP PATCH\n    method as a means of describing a set of modifications to a target\n    resource's content. This function applies a merge patch to the current\n    JSON value.\n\n    The function implements the following algorithm from Section 2 of\n    [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):\n\n    ```\n    define MergePatch(Target, Patch):\n      if Patch is an Object:\n        if Target is not an Object:\n          Target = {} // Ignore the contents and set it to an empty Object\n        for each Name/Value pair in Patch:\n          if Value is null:\n            if Name exists in Target:\n              remove the Name/Value pair from Target\n          else:\n            Target[Name] = MergePatch(Target[Name], Value)\n        return Target\n      else:\n        return Patch\n    ```\n\n    Thereby, `Target` is the current object; that is, the patch is applied to\n    the current value.\n\n    @param[in] apply_patch  the patch to apply\n\n    @complexity Linear in the lengths of @a patch.\n\n    @liveexample{The following code shows how a JSON Merge Patch is applied to\n    a JSON document.,merge_patch}\n\n    @sa @ref patch -- apply a JSON patch\n    @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)\n\n    @since version 3.0.0\n    */\n    void merge_patch(const basic_json& apply_patch)\n    {\n        if (apply_patch.is_object())\n        {\n            if (!is_object())\n            {\n                *this = object();\n            }\n            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)\n            {\n                if (it.value().is_null())\n                {\n                    erase(it.key());\n                }\n                else\n                {\n                    operator[](it.key()).merge_patch(it.value());\n                }\n            }\n        }\n        else\n        {\n            *this = apply_patch;\n        }\n    }\n\n    /// @}\n};\n\n/*!\n@brief user-defined to_string function for JSON values\n\nThis function implements a user-defined to_string  for JSON objects.\n\n@param[in] j  a JSON object\n@return a std::string object\n*/\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstd::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)\n{\n    return j.dump();\n}\n} // namespace nlohmann\n\n///////////////////////\n// nonmember support //\n///////////////////////\n\n// specialization of std::swap, and std::hash\nnamespace std\n{\n\n/// hash value for JSON objects\ntemplate<>\nstruct hash<nlohmann::json>\n{\n    /*!\n    @brief return a hash value for a JSON object\n\n    @since version 1.0.0\n    */\n    std::size_t operator()(const nlohmann::json& j) const\n    {\n        return nlohmann::detail::hash(j);\n    }\n};\n\n/// specialization for std::less<value_t>\n/// @note: do not remove the space after '<',\n///        see https://github.com/nlohmann/json/pull/679\ntemplate<>\nstruct less<::nlohmann::detail::value_t>\n{\n    /*!\n    @brief compare two value_t enum values\n    @since version 3.0.0\n    */\n    bool operator()(nlohmann::detail::value_t lhs,\n                    nlohmann::detail::value_t rhs) const noexcept\n    {\n        return nlohmann::detail::operator<(lhs, rhs);\n    }\n};\n\n// C++20 prohibit function specialization in the std namespace.\n#ifndef JSON_HAS_CPP_20\n\n/*!\n@brief exchanges the values of two JSON objects\n\n@since version 1.0.0\n*/\ntemplate<>\ninline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept(\n    is_nothrow_move_constructible<nlohmann::json>::value&&\n    is_nothrow_move_assignable<nlohmann::json>::value\n                              )\n{\n    j1.swap(j2);\n}\n\n#endif\n\n} // namespace std\n\n/*!\n@brief user-defined string literal for JSON values\n\nThis operator implements a user-defined string literal for JSON objects. It\ncan be used by adding `\"_json\"` to a string literal and returns a JSON object\nif no parse error occurred.\n\n@param[in] s  a string representation of a JSON object\n@param[in] n  the length of string @a s\n@return a JSON object\n\n@since version 1.0.0\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json operator \"\" _json(const char* s, std::size_t n)\n{\n    return nlohmann::json::parse(s, s + n);\n}\n\n/*!\n@brief user-defined string literal for JSON pointer\n\nThis operator implements a user-defined string literal for JSON Pointers. It\ncan be used by adding `\"_json_pointer\"` to a string literal and returns a JSON pointer\nobject if no parse error occurred.\n\n@param[in] s  a string representation of a JSON Pointer\n@param[in] n  the length of string @a s\n@return a JSON pointer object\n\n@since version 2.0.0\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json::json_pointer operator \"\" _json_pointer(const char* s, std::size_t n)\n{\n    return nlohmann::json::json_pointer(std::string(s, n));\n}\n\n// #include <nlohmann/detail/macro_unscope.hpp>\n\n\n// restore GCC/clang diagnostic settings\n#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)\n    #pragma GCC diagnostic pop\n#endif\n#if defined(__clang__)\n    #pragma GCC diagnostic pop\n#endif\n\n// clean up\n#undef JSON_ASSERT\n#undef JSON_INTERNAL_CATCH\n#undef JSON_CATCH\n#undef JSON_THROW\n#undef JSON_TRY\n#undef JSON_HAS_CPP_14\n#undef JSON_HAS_CPP_17\n#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION\n#undef NLOHMANN_BASIC_JSON_TPL\n#undef JSON_EXPLICIT\n\n// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>\n#undef JSON_HEDLEY_ALWAYS_INLINE\n#undef JSON_HEDLEY_ARM_VERSION\n#undef JSON_HEDLEY_ARM_VERSION_CHECK\n#undef JSON_HEDLEY_ARRAY_PARAM\n#undef JSON_HEDLEY_ASSUME\n#undef JSON_HEDLEY_BEGIN_C_DECLS\n#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#undef JSON_HEDLEY_CLANG_HAS_WARNING\n#undef JSON_HEDLEY_COMPCERT_VERSION\n#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#undef JSON_HEDLEY_CONCAT\n#undef JSON_HEDLEY_CONCAT3\n#undef JSON_HEDLEY_CONCAT3_EX\n#undef JSON_HEDLEY_CONCAT_EX\n#undef JSON_HEDLEY_CONST\n#undef JSON_HEDLEY_CONSTEXPR\n#undef JSON_HEDLEY_CONST_CAST\n#undef JSON_HEDLEY_CPP_CAST\n#undef JSON_HEDLEY_CRAY_VERSION\n#undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#undef JSON_HEDLEY_C_DECL\n#undef JSON_HEDLEY_DEPRECATED\n#undef JSON_HEDLEY_DEPRECATED_FOR\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#undef JSON_HEDLEY_DIAGNOSTIC_POP\n#undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#undef JSON_HEDLEY_DMC_VERSION\n#undef JSON_HEDLEY_DMC_VERSION_CHECK\n#undef JSON_HEDLEY_EMPTY_BASES\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#undef JSON_HEDLEY_END_C_DECLS\n#undef JSON_HEDLEY_FLAGS\n#undef JSON_HEDLEY_FLAGS_CAST\n#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#undef JSON_HEDLEY_GCC_HAS_FEATURE\n#undef JSON_HEDLEY_GCC_HAS_WARNING\n#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#undef JSON_HEDLEY_GCC_VERSION\n#undef JSON_HEDLEY_GCC_VERSION_CHECK\n#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#undef JSON_HEDLEY_GNUC_HAS_WARNING\n#undef JSON_HEDLEY_GNUC_VERSION\n#undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#undef JSON_HEDLEY_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_BUILTIN\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_EXTENSION\n#undef JSON_HEDLEY_HAS_FEATURE\n#undef JSON_HEDLEY_HAS_WARNING\n#undef JSON_HEDLEY_IAR_VERSION\n#undef JSON_HEDLEY_IAR_VERSION_CHECK\n#undef JSON_HEDLEY_IBM_VERSION\n#undef JSON_HEDLEY_IBM_VERSION_CHECK\n#undef JSON_HEDLEY_IMPORT\n#undef JSON_HEDLEY_INLINE\n#undef JSON_HEDLEY_INTEL_VERSION\n#undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#undef JSON_HEDLEY_IS_CONSTANT\n#undef JSON_HEDLEY_IS_CONSTEXPR_\n#undef JSON_HEDLEY_LIKELY\n#undef JSON_HEDLEY_MALLOC\n#undef JSON_HEDLEY_MESSAGE\n#undef JSON_HEDLEY_MSVC_VERSION\n#undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#undef JSON_HEDLEY_NEVER_INLINE\n#undef JSON_HEDLEY_NON_NULL\n#undef JSON_HEDLEY_NO_ESCAPE\n#undef JSON_HEDLEY_NO_RETURN\n#undef JSON_HEDLEY_NO_THROW\n#undef JSON_HEDLEY_NULL\n#undef JSON_HEDLEY_PELLES_VERSION\n#undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#undef JSON_HEDLEY_PGI_VERSION\n#undef JSON_HEDLEY_PGI_VERSION_CHECK\n#undef JSON_HEDLEY_PREDICT\n#undef JSON_HEDLEY_PRINTF_FORMAT\n#undef JSON_HEDLEY_PRIVATE\n#undef JSON_HEDLEY_PUBLIC\n#undef JSON_HEDLEY_PURE\n#undef JSON_HEDLEY_REINTERPRET_CAST\n#undef JSON_HEDLEY_REQUIRE\n#undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#undef JSON_HEDLEY_REQUIRE_MSG\n#undef JSON_HEDLEY_RESTRICT\n#undef JSON_HEDLEY_RETURNS_NON_NULL\n#undef JSON_HEDLEY_SENTINEL\n#undef JSON_HEDLEY_STATIC_ASSERT\n#undef JSON_HEDLEY_STATIC_CAST\n#undef JSON_HEDLEY_STRINGIFY\n#undef JSON_HEDLEY_STRINGIFY_EX\n#undef JSON_HEDLEY_SUNPRO_VERSION\n#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#undef JSON_HEDLEY_TINYC_VERSION\n#undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#undef JSON_HEDLEY_TI_ARMCL_VERSION\n#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL2000_VERSION\n#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL430_VERSION\n#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL6X_VERSION\n#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL7X_VERSION\n#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CLPRU_VERSION\n#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#undef JSON_HEDLEY_TI_VERSION\n#undef JSON_HEDLEY_TI_VERSION_CHECK\n#undef JSON_HEDLEY_UNAVAILABLE\n#undef JSON_HEDLEY_UNLIKELY\n#undef JSON_HEDLEY_UNPREDICTABLE\n#undef JSON_HEDLEY_UNREACHABLE\n#undef JSON_HEDLEY_UNREACHABLE_RETURN\n#undef JSON_HEDLEY_VERSION\n#undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#undef JSON_HEDLEY_VERSION_ENCODE\n#undef JSON_HEDLEY_WARNING\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#undef JSON_HEDLEY_FALL_THROUGH\n\n\n\n#endif  // INCLUDE_NLOHMANN_JSON_HPP_\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\n# Generated by CMake\ncc_library(\n    name = \"config\",\n    hdrs = [\"config.h\"],\n    strip_include_prefix = \".\",\n    visibility = [\"//visibility:public\"],\n)\n\nfilegroup(\n    name = \"genfiles\",\n    srcs = [\n        \"configure\",\n        \"install-sh\",\n        \"missing\",\n        \"config.guess\",\n        \"config.sub\",\n        \"Makefile.in\",\n        \"config.h.in\",\n        \"ltmain.sh\",\n        \"generated/zookeeper.jute.c\",\n        \"generated/zookeeper.jute.h\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/Makefile.in",
    "content": "# Makefile.in generated by automake 1.15 from Makefile.am.\n# @configure_input@\n\n# Copyright (C) 1994-2014 Free Software Foundation, Inc.\n\n# This Makefile.in is free software; the Free Software Foundation\n# gives unlimited permission to copy and/or distribute it,\n# with or without modifications, as long as this notice is preserved.\n\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY, to the extent permitted by law; without\n# even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n# PARTICULAR PURPOSE.\n\n@SET_MAKE@\n\n# Copyright (C) 2004 Oren Ben-Kiki\n# This file is distributed under the same terms as the Automake macro files.\n\n# Generate automatic documentation using Doxygen. Goals and variables values\n# are controlled by the various DX_COND_??? conditionals set by autoconf.\n#\n# The provided goals are:\n# doxygen-doc: Generate all doxygen documentation.\n# doxygen-run: Run doxygen, which will generate some of the documentation\n#              (HTML, CHM, CHI, MAN, RTF, XML) but will not do the post\n#              processing required for the rest of it (PS, PDF, and some MAN).\n# doxygen-man: Rename some doxygen generated man pages.\n# doxygen-ps: Generate doxygen PostScript documentation.\n# doxygen-pdf: Generate doxygen PDF documentation.\n#\n# Note that by default these are not integrated into the automake goals. If\n# doxygen is used to generate man pages, you can achieve this integration by\n# setting man3_MANS to the list of man pages generated and then adding the\n# dependency:\n#\n#   $(man3_MANS): doxygen-doc\n#\n# This will cause make to run doxygen and generate all the documentation.\n#\n# The following variable is intended for use in Makefile.am:\n#\n# DX_CLEANFILES = everything to clean.\n#\n# This is usually added to MOSTLYCLEANFILES.\n\n\n\nVPATH = @srcdir@\nam__is_gnu_make = { \\\n  if test -z '$(MAKELEVEL)'; then \\\n    false; \\\n  elif test -n '$(MAKE_HOST)'; then \\\n    true; \\\n  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \\\n    true; \\\n  else \\\n    false; \\\n  fi; \\\n}\nam__make_running_with_option = \\\n  case $${target_option-} in \\\n      ?) ;; \\\n      *) echo \"am__make_running_with_option: internal error: invalid\" \\\n              \"target option '$${target_option-}' specified\" >&2; \\\n         exit 1;; \\\n  esac; \\\n  has_opt=no; \\\n  sane_makeflags=$$MAKEFLAGS; \\\n  if $(am__is_gnu_make); then \\\n    sane_makeflags=$$MFLAGS; \\\n  else \\\n    case $$MAKEFLAGS in \\\n      *\\\\[\\ \\\t]*) \\\n        bs=\\\\; \\\n        sane_makeflags=`printf '%s\\n' \"$$MAKEFLAGS\" \\\n          | sed \"s/$$bs$$bs[$$bs $$bs\t]*//g\"`;; \\\n    esac; \\\n  fi; \\\n  skip_next=no; \\\n  strip_trailopt () \\\n  { \\\n    flg=`printf '%s\\n' \"$$flg\" | sed \"s/$$1.*$$//\"`; \\\n  }; \\\n  for flg in $$sane_makeflags; do \\\n    test $$skip_next = yes && { skip_next=no; continue; }; \\\n    case $$flg in \\\n      *=*|--*) continue;; \\\n        -*I) strip_trailopt 'I'; skip_next=yes;; \\\n      -*I?*) strip_trailopt 'I';; \\\n        -*O) strip_trailopt 'O'; skip_next=yes;; \\\n      -*O?*) strip_trailopt 'O';; \\\n        -*l) strip_trailopt 'l'; skip_next=yes;; \\\n      -*l?*) strip_trailopt 'l';; \\\n      -[dEDm]) skip_next=yes;; \\\n      -[JT]) skip_next=yes;; \\\n    esac; \\\n    case $$flg in \\\n      *$$target_option*) has_opt=yes; break;; \\\n    esac; \\\n  done; \\\n  test $$has_opt = yes\nam__make_dryrun = (target_option=n; $(am__make_running_with_option))\nam__make_keepgoing = (target_option=k; $(am__make_running_with_option))\npkgdatadir = $(datadir)/@PACKAGE@\npkgincludedir = $(includedir)/@PACKAGE@\npkglibdir = $(libdir)/@PACKAGE@\npkglibexecdir = $(libexecdir)/@PACKAGE@\nam__cd = CDPATH=\"$${ZSH_VERSION+.}$(PATH_SEPARATOR)\" && cd\ninstall_sh_DATA = $(install_sh) -c -m 644\ninstall_sh_PROGRAM = $(install_sh) -c\ninstall_sh_SCRIPT = $(install_sh) -c\nINSTALL_HEADER = $(INSTALL_DATA)\ntransform = $(program_transform_name)\nNORMAL_INSTALL = :\nPRE_INSTALL = :\nPOST_INSTALL = :\nNORMAL_UNINSTALL = :\nPRE_UNINSTALL = :\nPOST_UNINSTALL = :\nbuild_triplet = @build@\nhost_triplet = @host@\n\n# Additional flags for coverage testing (if enabled)\n@ENABLEGCOV_TRUE@am__append_1 = -fprofile-arcs -ftest-coverage\n@WANT_SYNCAPI_TRUE@am__append_2 = libzkmt.la\n@WANT_SYNCAPI_TRUE@am__append_3 = libzookeeper_mt.la\nbin_PROGRAMS = cli_st$(EXEEXT) $(am__EXEEXT_1)\n@WANT_SYNCAPI_TRUE@am__append_4 = cli_mt load_gen\ncheck_PROGRAMS = zktest-st$(EXEEXT) $(am__EXEEXT_2)\n@WANT_SYNCAPI_TRUE@am__append_5 = zktest-mt\nsubdir = .\nACLOCAL_M4 = $(top_srcdir)/aclocal.m4\nam__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \\\n\t$(top_srcdir)/configure.ac\nam__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \\\n\t$(ACLOCAL_M4)\nDIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \\\n\t$(am__configure_deps) $(pkginclude_HEADERS) $(am__DIST_COMMON)\nam__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \\\n configure.lineno config.status.lineno\nmkinstalldirs = $(install_sh) -d\nCONFIG_HEADER = config.h\nCONFIG_CLEAN_FILES =\nCONFIG_CLEAN_VPATH_FILES =\nam__vpath_adj_setup = srcdirstrip=`echo \"$(srcdir)\" | sed 's|.|.|g'`;\nam__vpath_adj = case $$p in \\\n    $(srcdir)/*) f=`echo \"$$p\" | sed \"s|^$$srcdirstrip/||\"`;; \\\n    *) f=$$p;; \\\n  esac;\nam__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;\nam__install_max = 40\nam__nobase_strip_setup = \\\n  srcdirstrip=`echo \"$(srcdir)\" | sed 's/[].[^$$\\\\*|]/\\\\\\\\&/g'`\nam__nobase_strip = \\\n  for p in $$list; do echo \"$$p\"; done | sed -e \"s|$$srcdirstrip/||\"\nam__nobase_list = $(am__nobase_strip_setup); \\\n  for p in $$list; do echo \"$$p $$p\"; done | \\\n  sed \"s| $$srcdirstrip/| |;\"' / .*\\//!s/ .*/ ./; s,\\( .*\\)/[^/]*$$,\\1,' | \\\n  $(AWK) 'BEGIN { files[\".\"] = \"\" } { files[$$2] = files[$$2] \" \" $$1; \\\n    if (++n[$$2] == $(am__install_max)) \\\n      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = \"\" } } \\\n    END { for (dir in files) print dir, files[dir] }'\nam__base_list = \\\n  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\\n/ /g' | \\\n  sed '$$!N;$$!N;$$!N;$$!N;s/\\n/ /g'\nam__uninstall_files_from_dir = { \\\n  test -z \"$$files\" \\\n    || { test ! -d \"$$dir\" && test ! -f \"$$dir\" && test ! -r \"$$dir\"; } \\\n    || { echo \" ( cd '$$dir' && rm -f\" $$files \")\"; \\\n         $(am__cd) \"$$dir\" && rm -f $$files; }; \\\n  }\nam__installdirs = \"$(DESTDIR)$(libdir)\" \"$(DESTDIR)$(bindir)\" \\\n\t\"$(DESTDIR)$(pkgincludedir)\"\nLTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)\nlibhashtable_la_LIBADD =\nam__objects_1 = hashtable_itr.lo hashtable.lo\nam_libhashtable_la_OBJECTS = $(am__objects_1)\nlibhashtable_la_OBJECTS = $(am_libhashtable_la_OBJECTS)\nAM_V_lt = $(am__v_lt_@AM_V@)\nam__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)\nam__v_lt_0 = --silent\nam__v_lt_1 = \nam__DEPENDENCIES_1 =\n@WANT_SYNCAPI_TRUE@libzkmt_la_DEPENDENCIES = $(am__DEPENDENCIES_1)\nam__libzkmt_la_SOURCES_DIST = src/zookeeper.c include/zookeeper.h \\\n\tinclude/zookeeper_version.h include/zookeeper_log.h \\\n\tsrc/recordio.c include/recordio.h include/proto.h \\\n\tsrc/zk_adaptor.h generated/zookeeper.jute.c src/zk_log.c \\\n\tsrc/zk_hashtable.h src/zk_hashtable.c src/addrvec.h \\\n\tsrc/addrvec.c src/zk_sasl.c src/mt_adaptor.c\n@WANT_SASL_TRUE@am__objects_2 = libzkmt_la-zk_sasl.lo\nam__objects_3 = libzkmt_la-zookeeper.lo libzkmt_la-recordio.lo \\\n\tlibzkmt_la-zookeeper.jute.lo libzkmt_la-zk_log.lo \\\n\tlibzkmt_la-zk_hashtable.lo libzkmt_la-addrvec.lo \\\n\t$(am__objects_2)\n@WANT_SYNCAPI_TRUE@am_libzkmt_la_OBJECTS = $(am__objects_3) \\\n@WANT_SYNCAPI_TRUE@\tlibzkmt_la-mt_adaptor.lo\nlibzkmt_la_OBJECTS = $(am_libzkmt_la_OBJECTS)\nlibzkmt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \\\n\t$(LIBTOOLFLAGS) --mode=link $(CCLD) $(libzkmt_la_CFLAGS) \\\n\t$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@\n@WANT_SYNCAPI_TRUE@am_libzkmt_la_rpath =\nlibzkst_la_DEPENDENCIES = $(am__DEPENDENCIES_1)\nam__libzkst_la_SOURCES_DIST = src/zookeeper.c include/zookeeper.h \\\n\tinclude/zookeeper_version.h include/zookeeper_log.h \\\n\tsrc/recordio.c include/recordio.h include/proto.h \\\n\tsrc/zk_adaptor.h generated/zookeeper.jute.c src/zk_log.c \\\n\tsrc/zk_hashtable.h src/zk_hashtable.c src/addrvec.h \\\n\tsrc/addrvec.c src/zk_sasl.c src/st_adaptor.c\n@WANT_SASL_TRUE@am__objects_4 = zk_sasl.lo\nam__objects_5 = zookeeper.lo recordio.lo zookeeper.jute.lo zk_log.lo \\\n\tzk_hashtable.lo addrvec.lo $(am__objects_4)\nam_libzkst_la_OBJECTS = $(am__objects_5) st_adaptor.lo\nlibzkst_la_OBJECTS = $(am_libzkst_la_OBJECTS)\nam_libzookeeper_mt_la_OBJECTS =\nlibzookeeper_mt_la_OBJECTS = $(am_libzookeeper_mt_la_OBJECTS)\nlibzookeeper_mt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \\\n\t$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \\\n\t$(AM_CFLAGS) $(CFLAGS) $(libzookeeper_mt_la_LDFLAGS) \\\n\t$(LDFLAGS) -o $@\n@WANT_SYNCAPI_TRUE@am_libzookeeper_mt_la_rpath = -rpath $(libdir)\nam_libzookeeper_st_la_OBJECTS =\nlibzookeeper_st_la_OBJECTS = $(am_libzookeeper_st_la_OBJECTS)\nlibzookeeper_st_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \\\n\t$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \\\n\t$(AM_CFLAGS) $(CFLAGS) $(libzookeeper_st_la_LDFLAGS) \\\n\t$(LDFLAGS) -o $@\n@WANT_SYNCAPI_TRUE@am__EXEEXT_1 = cli_mt$(EXEEXT) load_gen$(EXEEXT)\n@WANT_SYNCAPI_TRUE@am__EXEEXT_2 = zktest-mt$(EXEEXT)\nPROGRAMS = $(bin_PROGRAMS)\nam__cli_mt_SOURCES_DIST = src/cli.c\n@WANT_SYNCAPI_TRUE@am_cli_mt_OBJECTS = cli_mt-cli.$(OBJEXT)\ncli_mt_OBJECTS = $(am_cli_mt_OBJECTS)\n@WANT_SYNCAPI_TRUE@cli_mt_DEPENDENCIES = libzookeeper_mt.la \\\n@WANT_SYNCAPI_TRUE@\t$(am__DEPENDENCIES_1)\ncli_mt_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \\\n\t$(LIBTOOLFLAGS) --mode=link $(CCLD) $(cli_mt_CFLAGS) $(CFLAGS) \\\n\t$(AM_LDFLAGS) $(LDFLAGS) -o $@\nam_cli_st_OBJECTS = cli.$(OBJEXT)\ncli_st_OBJECTS = $(am_cli_st_OBJECTS)\ncli_st_DEPENDENCIES = libzookeeper_st.la $(am__DEPENDENCIES_1)\nam__load_gen_SOURCES_DIST = src/load_gen.c\n@WANT_SYNCAPI_TRUE@am_load_gen_OBJECTS = load_gen-load_gen.$(OBJEXT)\nload_gen_OBJECTS = $(am_load_gen_OBJECTS)\n@WANT_SYNCAPI_TRUE@load_gen_DEPENDENCIES = libzookeeper_mt.la\nload_gen_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \\\n\t$(LIBTOOLFLAGS) --mode=link $(CCLD) $(load_gen_CFLAGS) \\\n\t$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@\nam__objects_6 = zktest_mt-TestDriver.$(OBJEXT) \\\n\tzktest_mt-LibCMocks.$(OBJEXT) zktest_mt-LibCSymTable.$(OBJEXT) \\\n\tzktest_mt-MocksBase.$(OBJEXT) zktest_mt-ZKMocks.$(OBJEXT) \\\n\tzktest_mt-Util.$(OBJEXT) zktest_mt-ThreadingUtil.$(OBJEXT) \\\n\tzktest_mt-TestZookeeperInit.$(OBJEXT) \\\n\tzktest_mt-TestZookeeperClose.$(OBJEXT) \\\n\tzktest_mt-TestReconfig.$(OBJEXT) \\\n\tzktest_mt-TestReconfigServer.$(OBJEXT) \\\n\tzktest_mt-TestClientRetry.$(OBJEXT) \\\n\tzktest_mt-TestOperations.$(OBJEXT) \\\n\tzktest_mt-TestMulti.$(OBJEXT) zktest_mt-TestWatchers.$(OBJEXT) \\\n\tzktest_mt-TestClient.$(OBJEXT) \\\n\tzktest_mt-ZooKeeperQuorumServer.$(OBJEXT) \\\n\tzktest_mt-TestReadOnlyClient.$(OBJEXT) \\\n\tzktest_mt-TestLogClientEnv.$(OBJEXT) \\\n\tzktest_mt-TestSASLAuth.$(OBJEXT)\n@WANT_SYNCAPI_TRUE@nodist_zktest_mt_OBJECTS = $(am__objects_6) \\\n@WANT_SYNCAPI_TRUE@\tzktest_mt-PthreadMocks.$(OBJEXT)\nzktest_mt_OBJECTS = $(nodist_zktest_mt_OBJECTS)\n@WANT_SYNCAPI_TRUE@zktest_mt_DEPENDENCIES = libzkmt.la libhashtable.la \\\n@WANT_SYNCAPI_TRUE@\t$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \\\n@WANT_SYNCAPI_TRUE@\t$(am__DEPENDENCIES_1)\nzktest_mt_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \\\n\t$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(zktest_mt_CXXFLAGS) \\\n\t$(CXXFLAGS) $(zktest_mt_LDFLAGS) $(LDFLAGS) -o $@\nam__objects_7 = zktest_st-TestDriver.$(OBJEXT) \\\n\tzktest_st-LibCMocks.$(OBJEXT) zktest_st-LibCSymTable.$(OBJEXT) \\\n\tzktest_st-MocksBase.$(OBJEXT) zktest_st-ZKMocks.$(OBJEXT) \\\n\tzktest_st-Util.$(OBJEXT) zktest_st-ThreadingUtil.$(OBJEXT) \\\n\tzktest_st-TestZookeeperInit.$(OBJEXT) \\\n\tzktest_st-TestZookeeperClose.$(OBJEXT) \\\n\tzktest_st-TestReconfig.$(OBJEXT) \\\n\tzktest_st-TestReconfigServer.$(OBJEXT) \\\n\tzktest_st-TestClientRetry.$(OBJEXT) \\\n\tzktest_st-TestOperations.$(OBJEXT) \\\n\tzktest_st-TestMulti.$(OBJEXT) zktest_st-TestWatchers.$(OBJEXT) \\\n\tzktest_st-TestClient.$(OBJEXT) \\\n\tzktest_st-ZooKeeperQuorumServer.$(OBJEXT) \\\n\tzktest_st-TestReadOnlyClient.$(OBJEXT) \\\n\tzktest_st-TestLogClientEnv.$(OBJEXT) \\\n\tzktest_st-TestSASLAuth.$(OBJEXT)\nnodist_zktest_st_OBJECTS = $(am__objects_7)\nzktest_st_OBJECTS = $(nodist_zktest_st_OBJECTS)\nzktest_st_DEPENDENCIES = libzkst.la libhashtable.la \\\n\t$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \\\n\t$(am__DEPENDENCIES_1)\nzktest_st_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \\\n\t$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(zktest_st_CXXFLAGS) \\\n\t$(CXXFLAGS) $(zktest_st_LDFLAGS) $(LDFLAGS) -o $@\nAM_V_P = $(am__v_P_@AM_V@)\nam__v_P_ = $(am__v_P_@AM_DEFAULT_V@)\nam__v_P_0 = false\nam__v_P_1 = :\nAM_V_GEN = $(am__v_GEN_@AM_V@)\nam__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)\nam__v_GEN_0 = @echo \"  GEN     \" $@;\nam__v_GEN_1 = \nAM_V_at = $(am__v_at_@AM_V@)\nam__v_at_ = $(am__v_at_@AM_DEFAULT_V@)\nam__v_at_0 = @\nam__v_at_1 = \nDEFAULT_INCLUDES = -I.@am__isrc@\ndepcomp = $(SHELL) $(top_srcdir)/depcomp\nam__depfiles_maybe = depfiles\nam__mv = mv -f\nCOMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \\\n\t$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)\nLTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \\\n\t$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \\\n\t$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \\\n\t$(AM_CFLAGS) $(CFLAGS)\nAM_V_CC = $(am__v_CC_@AM_V@)\nam__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)\nam__v_CC_0 = @echo \"  CC      \" $@;\nam__v_CC_1 = \nCCLD = $(CC)\nLINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \\\n\t$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \\\n\t$(AM_LDFLAGS) $(LDFLAGS) -o $@\nAM_V_CCLD = $(am__v_CCLD_@AM_V@)\nam__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)\nam__v_CCLD_0 = @echo \"  CCLD    \" $@;\nam__v_CCLD_1 = \nCXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \\\n\t$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)\nLTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \\\n\t$(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \\\n\t$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \\\n\t$(AM_CXXFLAGS) $(CXXFLAGS)\nAM_V_CXX = $(am__v_CXX_@AM_V@)\nam__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)\nam__v_CXX_0 = @echo \"  CXX     \" $@;\nam__v_CXX_1 = \nCXXLD = $(CXX)\nCXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \\\n\t$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \\\n\t$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@\nAM_V_CXXLD = $(am__v_CXXLD_@AM_V@)\nam__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)\nam__v_CXXLD_0 = @echo \"  CXXLD   \" $@;\nam__v_CXXLD_1 = \nSOURCES = $(libhashtable_la_SOURCES) $(libzkmt_la_SOURCES) \\\n\t$(libzkst_la_SOURCES) $(libzookeeper_mt_la_SOURCES) \\\n\t$(libzookeeper_st_la_SOURCES) $(cli_mt_SOURCES) \\\n\t$(cli_st_SOURCES) $(load_gen_SOURCES) \\\n\t$(nodist_zktest_mt_SOURCES) $(nodist_zktest_st_SOURCES)\nDIST_SOURCES = $(libhashtable_la_SOURCES) \\\n\t$(am__libzkmt_la_SOURCES_DIST) $(am__libzkst_la_SOURCES_DIST) \\\n\t$(libzookeeper_mt_la_SOURCES) $(libzookeeper_st_la_SOURCES) \\\n\t$(am__cli_mt_SOURCES_DIST) $(cli_st_SOURCES) \\\n\t$(am__load_gen_SOURCES_DIST)\nam__can_run_installinfo = \\\n  case $$AM_UPDATE_INFO_DIR in \\\n    n|no|NO) false;; \\\n    *) (install-info --version) >/dev/null 2>&1;; \\\n  esac\nHEADERS = $(pkginclude_HEADERS)\nam__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \\\n\t$(LISP)config.h.in\n# Read a list of newline-separated strings from the standard input,\n# and print each of them once, without duplicates.  Input order is\n# *not* preserved.\nam__uniquify_input = $(AWK) '\\\n  BEGIN { nonempty = 0; } \\\n  { items[$$0] = 1; nonempty = 1; } \\\n  END { if (nonempty) { for (i in items) print i; }; } \\\n'\n# Make sure the list of sources is unique.  This is necessary because,\n# e.g., the same source file might be shared among _SOURCES variables\n# for different programs/libraries.\nam__define_uniq_tagged_files = \\\n  list='$(am__tagged_files)'; \\\n  unique=`for i in $$list; do \\\n    if test -f \"$$i\"; then echo $$i; else echo $(srcdir)/$$i; fi; \\\n  done | $(am__uniquify_input)`\nETAGS = etags\nCTAGS = ctags\nCSCOPE = cscope\nAM_RECURSIVE_TARGETS = cscope\nam__tty_colors_dummy = \\\n  mgn= red= grn= lgn= blu= brg= std=; \\\n  am__color_tests=no\nam__tty_colors = { \\\n  $(am__tty_colors_dummy); \\\n  if test \"X$(AM_COLOR_TESTS)\" = Xno; then \\\n    am__color_tests=no; \\\n  elif test \"X$(AM_COLOR_TESTS)\" = Xalways; then \\\n    am__color_tests=yes; \\\n  elif test \"X$$TERM\" != Xdumb && { test -t 1; } 2>/dev/null; then \\\n    am__color_tests=yes; \\\n  fi; \\\n  if test $$am__color_tests = yes; then \\\n    red='\u001b[0;31m'; \\\n    grn='\u001b[0;32m'; \\\n    lgn='\u001b[1;32m'; \\\n    blu='\u001b[1;34m'; \\\n    mgn='\u001b[0;35m'; \\\n    brg='\u001b[1m'; \\\n    std='\u001b[m'; \\\n  fi; \\\n}\nam__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \\\n\t$(top_srcdir)/aminclude.am ChangeLog INSTALL README compile \\\n\tconfig.guess config.sub depcomp install-sh ltmain.sh missing\nDISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)\ndistdir = $(PACKAGE)-$(VERSION)\ntop_distdir = $(distdir)\nam__remove_distdir = \\\n  if test -d \"$(distdir)\"; then \\\n    find \"$(distdir)\" -type d ! -perm -200 -exec chmod u+w {} ';' \\\n      && rm -rf \"$(distdir)\" \\\n      || { sleep 5 && rm -rf \"$(distdir)\"; }; \\\n  else :; fi\nam__post_remove_distdir = $(am__remove_distdir)\nDIST_ARCHIVES = $(distdir).tar.gz\nGZIP_ENV = --best\nDIST_TARGETS = dist-gzip\ndistuninstallcheck_listfiles = find . -type f -print\nam__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \\\n  | sed 's|^\\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'\ndistcleancheck_listfiles = find . -type f -print\nACLOCAL = @ACLOCAL@\nAMTAR = @AMTAR@\nAM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@\nAR = @AR@\nAUTOCONF = @AUTOCONF@\nAUTOHEADER = @AUTOHEADER@\nAUTOMAKE = @AUTOMAKE@\nAWK = @AWK@\nCC = @CC@\nCCDEPMODE = @CCDEPMODE@\nCFLAGS = @CFLAGS@\nCLOCK_GETTIME_LIBS = @CLOCK_GETTIME_LIBS@\nCPP = @CPP@\nCPPFLAGS = @CPPFLAGS@\nCPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@\nCPPUNIT_LIBS = @CPPUNIT_LIBS@\nCXX = @CXX@\nCXXCPP = @CXXCPP@\nCXXDEPMODE = @CXXDEPMODE@\nCXXFLAGS = @CXXFLAGS@\nCYGPATH_W = @CYGPATH_W@\nDEFS = @DEFS@\nDEPDIR = @DEPDIR@\nDLLTOOL = @DLLTOOL@\nDOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@\nDSYMUTIL = @DSYMUTIL@\nDUMPBIN = @DUMPBIN@\nDX_CONFIG = @DX_CONFIG@\nDX_DOCDIR = @DX_DOCDIR@\nDX_DOT = @DX_DOT@\nDX_DOXYGEN = @DX_DOXYGEN@\nDX_DVIPS = @DX_DVIPS@\nDX_EGREP = @DX_EGREP@\nDX_ENV = @DX_ENV@\nDX_FLAG_chi = @DX_FLAG_chi@\nDX_FLAG_chm = @DX_FLAG_chm@\nDX_FLAG_doc = @DX_FLAG_doc@\nDX_FLAG_dot = @DX_FLAG_dot@\nDX_FLAG_html = @DX_FLAG_html@\nDX_FLAG_man = @DX_FLAG_man@\nDX_FLAG_pdf = @DX_FLAG_pdf@\nDX_FLAG_ps = @DX_FLAG_ps@\nDX_FLAG_rtf = @DX_FLAG_rtf@\nDX_FLAG_xml = @DX_FLAG_xml@\nDX_HHC = @DX_HHC@\nDX_LATEX = @DX_LATEX@\nDX_MAKEINDEX = @DX_MAKEINDEX@\nDX_PDFLATEX = @DX_PDFLATEX@\nDX_PERL = @DX_PERL@\nDX_PROJECT = @DX_PROJECT@\nECHO_C = @ECHO_C@\nECHO_N = @ECHO_N@\nECHO_T = @ECHO_T@\nEGREP = @EGREP@\nEXEEXT = @EXEEXT@\nFGREP = @FGREP@\nGREP = @GREP@\nINSTALL = @INSTALL@\nINSTALL_DATA = @INSTALL_DATA@\nINSTALL_PROGRAM = @INSTALL_PROGRAM@\nINSTALL_SCRIPT = @INSTALL_SCRIPT@\nINSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@\nLD = @LD@\nLDFLAGS = @LDFLAGS@\nLIBOBJS = @LIBOBJS@\nLIBS = @LIBS@\nLIBTOOL = @LIBTOOL@\nLIPO = @LIPO@\nLN_S = @LN_S@\nLTLIBOBJS = @LTLIBOBJS@\nLT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@\nMAKEINFO = @MAKEINFO@\nMANIFEST_TOOL = @MANIFEST_TOOL@\nMKDIR_P = @MKDIR_P@\nNM = @NM@\nNMEDIT = @NMEDIT@\nOBJDUMP = @OBJDUMP@\nOBJEXT = @OBJEXT@\nOTOOL = @OTOOL@\nOTOOL64 = @OTOOL64@\nPACKAGE = @PACKAGE@\nPACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@\nPACKAGE_NAME = @PACKAGE_NAME@\nPACKAGE_STRING = @PACKAGE_STRING@\nPACKAGE_TARNAME = @PACKAGE_TARNAME@\nPACKAGE_URL = @PACKAGE_URL@\nPACKAGE_VERSION = @PACKAGE_VERSION@\nPATH_SEPARATOR = @PATH_SEPARATOR@\nPKG_CONFIG = @PKG_CONFIG@\nPKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@\nPKG_CONFIG_PATH = @PKG_CONFIG_PATH@\nRANLIB = @RANLIB@\nSED = @SED@\nSET_MAKE = @SET_MAKE@\nSHELL = @SHELL@\nSTRIP = @STRIP@\nUSEIPV6 = @USEIPV6@\nVERSION = @VERSION@\nabs_builddir = @abs_builddir@\nabs_srcdir = @abs_srcdir@\nabs_top_builddir = @abs_top_builddir@\nabs_top_srcdir = @abs_top_srcdir@\nac_ct_AR = @ac_ct_AR@\nac_ct_CC = @ac_ct_CC@\nac_ct_CXX = @ac_ct_CXX@\nac_ct_DUMPBIN = @ac_ct_DUMPBIN@\nam__include = @am__include@\nam__leading_dot = @am__leading_dot@\nam__quote = @am__quote@\nam__tar = @am__tar@\nam__untar = @am__untar@\nbindir = @bindir@\nbuild = @build@\nbuild_alias = @build_alias@\nbuild_cpu = @build_cpu@\nbuild_os = @build_os@\nbuild_vendor = @build_vendor@\nbuilddir = @builddir@\ndatadir = @datadir@\ndatarootdir = @datarootdir@\ndocdir = @docdir@\ndvidir = @dvidir@\nexec_prefix = @exec_prefix@\nhost = @host@\nhost_alias = @host_alias@\nhost_cpu = @host_cpu@\nhost_os = @host_os@\nhost_vendor = @host_vendor@\nhtmldir = @htmldir@\nincludedir = @includedir@\ninfodir = @infodir@\ninstall_sh = @install_sh@\nlibdir = @libdir@\nlibexecdir = @libexecdir@\nlocaledir = @localedir@\nlocalstatedir = @localstatedir@\nmandir = @mandir@\nmkdir_p = @mkdir_p@\noldincludedir = @oldincludedir@\npdfdir = @pdfdir@\nprefix = @prefix@\nprogram_transform_name = @program_transform_name@\npsdir = @psdir@\nrunstatedir = @runstatedir@\nsbindir = @sbindir@\nsharedstatedir = @sharedstatedir@\nsrcdir = @srcdir@\nsysconfdir = @sysconfdir@\ntarget_alias = @target_alias@\ntop_build_prefix = @top_build_prefix@\ntop_builddir = @top_builddir@\ntop_srcdir = @top_srcdir@\n@DX_COND_doc_TRUE@@DX_COND_html_TRUE@DX_CLEAN_HTML = @DX_DOCDIR@/html\n@DX_COND_chm_TRUE@@DX_COND_doc_TRUE@DX_CLEAN_CHM = @DX_DOCDIR@/chm\n@DX_COND_chi_TRUE@@DX_COND_chm_TRUE@@DX_COND_doc_TRUE@DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi\n@DX_COND_doc_TRUE@@DX_COND_man_TRUE@DX_CLEAN_MAN = @DX_DOCDIR@/man\n@DX_COND_doc_TRUE@@DX_COND_rtf_TRUE@DX_CLEAN_RTF = @DX_DOCDIR@/rtf\n@DX_COND_doc_TRUE@@DX_COND_xml_TRUE@DX_CLEAN_XML = @DX_DOCDIR@/xml\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@DX_PS_GOAL = doxygen-ps\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@DX_PDF_GOAL = doxygen-pdf\n@DX_COND_doc_TRUE@@DX_COND_latex_TRUE@DX_CLEAN_LATEX = @DX_DOCDIR@/latex\n@DX_COND_doc_TRUE@DX_CLEANFILES = \\\n@DX_COND_doc_TRUE@    @DX_DOCDIR@/@PACKAGE@.tag \\\n@DX_COND_doc_TRUE@    -r \\\n@DX_COND_doc_TRUE@    $(DX_CLEAN_HTML) \\\n@DX_COND_doc_TRUE@    $(DX_CLEAN_CHM) \\\n@DX_COND_doc_TRUE@    $(DX_CLEAN_CHI) \\\n@DX_COND_doc_TRUE@    $(DX_CLEAN_MAN) \\\n@DX_COND_doc_TRUE@    $(DX_CLEAN_RTF) \\\n@DX_COND_doc_TRUE@    $(DX_CLEAN_XML) \\\n@DX_COND_doc_TRUE@    $(DX_CLEAN_PS) \\\n@DX_COND_doc_TRUE@    $(DX_CLEAN_PDF) \\\n@DX_COND_doc_TRUE@    $(DX_CLEAN_LATEX)\n\n\n# need this for Doxygen integration\nAUTOMAKE_OPTIONS = serial-tests\n@SOLARIS_TRUE@SOLARIS_CPPFLAGS = -D_POSIX_PTHREAD_SEMANTICS\n@SOLARIS_TRUE@SOLARIS_LIB_LDFLAGS = -lnsl -lsocket\n@WANT_OPENSSL_TRUE@OPENSSL_CPPFLAGS = -DHAVE_OPENSSL_H\n@WANT_OPENSSL_TRUE@OPENSSL_LIB_LDFLAGS = -lssl -lcrypto\n@WANT_SASL_TRUE@SASL_CPPFLAGS = -DHAVE_CYRUS_SASL_H\n@WANT_SASL_TRUE@SASL_LIB_LDFLAGS = -lsasl2\n@WANT_SASL_TRUE@SASL_SRC = src/zk_sasl.c\nAM_CPPFLAGS = -I${srcdir}/include -I${srcdir}/tests -I${srcdir}/generated $(SOLARIS_CPPFLAGS) $(OPENSSL_CPPFLAGS) $(SASL_CPPFLAGS)\nAM_CFLAGS = -Wall -Werror -Wdeclaration-after-statement \\\n\t$(am__append_1)\nAM_CXXFLAGS = -Wall $(USEIPV6)\nLIB_LDFLAGS = -no-undefined -version-info 2 $(SOLARIS_LIB_LDFLAGS) $(OPENSSL_LIB_LDFLAGS) $(SASL_LIB_LDFLAGS)\n@ENABLEGCOV_TRUE@AM_LDFLAGS = -lgcov\npkginclude_HEADERS = include/zookeeper.h include/zookeeper_version.h include/zookeeper_log.h include/proto.h include/recordio.h generated/zookeeper.jute.h\n\n#########################################################################\n# build and run unit tests\nEXTRA_DIST = LICENSE $(wildcard ${srcdir}/tests/*.cc) $(wildcard \\\n\t${srcdir}/tests/*.h) ${srcdir}/tests/wrappers.opt \\\n\t${srcdir}/tests/wrappers-mt.opt\nHASHTABLE_SRC = src/hashtable/hashtable_itr.h src/hashtable/hashtable_itr.c \\\n    src/hashtable/hashtable_private.h src/hashtable/hashtable.h src/hashtable/hashtable.c\n\nnoinst_LTLIBRARIES = libhashtable.la libzkst.la $(am__append_2)\nlibhashtable_la_SOURCES = $(HASHTABLE_SRC)\nCOMMON_SRC = src/zookeeper.c include/zookeeper.h include/zookeeper_version.h include/zookeeper_log.h\\\n    src/recordio.c include/recordio.h include/proto.h \\\n    src/zk_adaptor.h generated/zookeeper.jute.c \\\n    src/zk_log.c src/zk_hashtable.h src/zk_hashtable.c \\\n    src/addrvec.h src/addrvec.c $(SASL_SRC)\n\n\n# These are the symbols (classes, mostly) we want to export from our library.\nEXPORT_SYMBOLS = '(zoo_|zookeeper_|zhandle|Z|format_log_message|log_message|logLevel|deallocate_|allocate_|zerror|is_unrecoverable)'\nlibzkst_la_SOURCES = $(COMMON_SRC) src/st_adaptor.c\nlibzkst_la_LIBADD = -lm $(CLOCK_GETTIME_LIBS)\nlib_LTLIBRARIES = libzookeeper_st.la $(am__append_3)\nlibzookeeper_st_la_SOURCES = \nlibzookeeper_st_la_LIBADD = libzkst.la libhashtable.la\nlibzookeeper_st_la_DEPENDENCIES = libzkst.la libhashtable.la\nlibzookeeper_st_la_LDFLAGS = $(LIB_LDFLAGS) -export-symbols-regex $(EXPORT_SYMBOLS)\n@WANT_SYNCAPI_TRUE@libzkmt_la_SOURCES = $(COMMON_SRC) src/mt_adaptor.c\n@WANT_SYNCAPI_TRUE@libzkmt_la_CFLAGS = $(AM_CFLAGS) -DTHREADED\n@WANT_SYNCAPI_TRUE@libzkmt_la_LIBADD = -lm $(CLOCK_GETTIME_LIBS)\n@WANT_SYNCAPI_TRUE@libzookeeper_mt_la_SOURCES = \n@WANT_SYNCAPI_TRUE@libzookeeper_mt_la_LIBADD = libzkmt.la libhashtable.la -lpthread\n@WANT_SYNCAPI_TRUE@libzookeeper_mt_la_DEPENDENCIES = libzkmt.la libhashtable.la\n@WANT_SYNCAPI_TRUE@libzookeeper_mt_la_LDFLAGS = $(LIB_LDFLAGS) -export-symbols-regex $(EXPORT_SYMBOLS)\ncli_st_SOURCES = src/cli.c\ncli_st_LDADD = libzookeeper_st.la $(SASL_LIB_LDFLAGS)\n@WANT_SYNCAPI_TRUE@cli_mt_SOURCES = src/cli.c\n@WANT_SYNCAPI_TRUE@cli_mt_LDADD = libzookeeper_mt.la $(SASL_LIB_LDFLAGS)\n@WANT_SYNCAPI_TRUE@cli_mt_CFLAGS = $(AM_CFLAGS) -DTHREADED\n@WANT_SYNCAPI_TRUE@load_gen_SOURCES = src/load_gen.c\n@WANT_SYNCAPI_TRUE@load_gen_LDADD = libzookeeper_mt.la\n@WANT_SYNCAPI_TRUE@load_gen_CFLAGS = $(AM_CFLAGS) -DTHREADED\n\n# These tests are ordered in a logical manner such that each builds upon basic\n# functionality tested in prior tests. e.g. the most basic functionality is\n# tested in TestZookeeperInit and TestZookeeperClose and as such should be tested\n# first as a foundation with more complex test suites to follow.\nTEST_SOURCES = \\\n\ttests/TestDriver.cc \\\n\ttests/LibCMocks.cc \\\n\ttests/LibCSymTable.cc \\\n\ttests/MocksBase.cc \\\n\ttests/ZKMocks.cc \\\n\ttests/Util.cc \\\n\ttests/ThreadingUtil.cc \\\n\ttests/TestZookeeperInit.cc \\\n\ttests/TestZookeeperClose.cc \\\n\ttests/TestReconfig.cc \\\n\ttests/TestReconfigServer.cc \\\n\ttests/TestClientRetry.cc \\\n\ttests/TestOperations.cc \\\n\ttests/TestMulti.cc \\\n\ttests/TestWatchers.cc \\\n\ttests/TestClient.cc \\\n\ttests/ZooKeeperQuorumServer.cc \\\n\ttests/ZooKeeperQuorumServer.h \\\n\ttests/TestReadOnlyClient.cc \\\n\ttests/TestLogClientEnv.cc \\\n        tests/TestSASLAuth.cc \\\n\t$(NULL)\n\n@SOLARIS_TRUE@SHELL_SYMBOL_WRAPPERS = cat ${srcdir}/tests/wrappers.opt\n@SOLARIS_FALSE@SYMBOL_WRAPPERS = $(shell cat ${srcdir}/tests/wrappers.opt)\n@SOLARIS_TRUE@SYMBOL_WRAPPERS = $(SHELL_SYMBOL_WRAPPERS:sh)\nTESTS_ENVIRONMENT = ZKROOT=${srcdir}/../.. \\\n                    CLASSPATH=$$CLASSPATH:$$CLOVER_HOME/lib/clover*.jar\n\nnodist_zktest_st_SOURCES = $(TEST_SOURCES)\nzktest_st_LDADD = libzkst.la libhashtable.la $(CPPUNIT_LIBS) $(OPENSSL_LIB_LDFLAGS) $(SASL_LIB_LDFLAGS) -ldl\nzktest_st_CXXFLAGS = $(AM_CXXFLAGS) -DUSE_STATIC_LIB $(CPPUNIT_CFLAGS) $(SOLARIS_CPPFLAGS)\nzktest_st_LDFLAGS = -shared $(SYMBOL_WRAPPERS) $(SOLARIS_LIB_LDFLAGS)\n@WANT_SYNCAPI_TRUE@nodist_zktest_mt_SOURCES = $(TEST_SOURCES) tests/PthreadMocks.cc\n@WANT_SYNCAPI_TRUE@zktest_mt_LDADD = libzkmt.la libhashtable.la -lpthread $(CPPUNIT_LIBS) $(OPENSSL_LIB_LDFLAGS) $(SASL_LIB_LDFLAGS) -ldl\n@WANT_SYNCAPI_TRUE@zktest_mt_CXXFLAGS = $(AM_CXXFLAGS) -DUSE_STATIC_LIB -DTHREADED $(CPPUNIT_CFLAGS) $(USEIPV6)\n@SOLARIS_TRUE@@WANT_SYNCAPI_TRUE@SHELL_SYMBOL_WRAPPERS_MT = cat ${srcdir}/tests/wrappers-mt.opt\n@SOLARIS_FALSE@@WANT_SYNCAPI_TRUE@SYMBOL_WRAPPERS_MT = $(SYMBOL_WRAPPERS) $(shell cat ${srcdir}/tests/wrappers-mt.opt)\n@SOLARIS_TRUE@@WANT_SYNCAPI_TRUE@SYMBOL_WRAPPERS_MT = $(SYMBOL_WRAPPERS) $(SHELL_SYMBOL_WRAPPERS_MT:sh)\n@WANT_SYNCAPI_TRUE@zktest_mt_LDFLAGS = -shared $(SYMBOL_WRAPPERS_MT) $(SOLARIS_LIB_LDFLAGS)\nTESTS = $(check_PROGRAMS)\nall: config.h\n\t$(MAKE) $(AM_MAKEFLAGS) all-am\n\n.SUFFIXES:\n.SUFFIXES: .c .cc .lo .o .obj\nam--refresh: Makefile\n\t@:\n$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am $(top_srcdir)/aminclude.am $(am__configure_deps)\n\t@for dep in $?; do \\\n\t  case '$(am__configure_deps)' in \\\n\t    *$$dep*) \\\n\t      echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \\\n\t      $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \\\n\t\t&& exit 0; \\\n\t      exit 1;; \\\n\t  esac; \\\n\tdone; \\\n\techo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \\\n\t$(am__cd) $(top_srcdir) && \\\n\t  $(AUTOMAKE) --foreign Makefile\nMakefile: $(srcdir)/Makefile.in $(top_builddir)/config.status\n\t@case '$?' in \\\n\t  *config.status*) \\\n\t    echo ' $(SHELL) ./config.status'; \\\n\t    $(SHELL) ./config.status;; \\\n\t  *) \\\n\t    echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \\\n\t    cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \\\n\tesac;\n$(top_srcdir)/aminclude.am $(am__empty):\n\n$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)\n\t$(SHELL) ./config.status --recheck\n\n$(top_srcdir)/configure:  $(am__configure_deps)\n\t$(am__cd) $(srcdir) && $(AUTOCONF)\n$(ACLOCAL_M4):  $(am__aclocal_m4_deps)\n\t$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)\n$(am__aclocal_m4_deps):\n\nconfig.h: stamp-h1\n\t@test -f $@ || rm -f stamp-h1\n\t@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1\n\nstamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status\n\t@rm -f stamp-h1\n\tcd $(top_builddir) && $(SHELL) ./config.status config.h\n$(srcdir)/config.h.in:  $(am__configure_deps) \n\t($(am__cd) $(top_srcdir) && $(AUTOHEADER))\n\trm -f stamp-h1\n\ttouch $@\n\ndistclean-hdr:\n\t-rm -f config.h stamp-h1\n\ninstall-libLTLIBRARIES: $(lib_LTLIBRARIES)\n\t@$(NORMAL_INSTALL)\n\t@list='$(lib_LTLIBRARIES)'; test -n \"$(libdir)\" || list=; \\\n\tlist2=; for p in $$list; do \\\n\t  if test -f $$p; then \\\n\t    list2=\"$$list2 $$p\"; \\\n\t  else :; fi; \\\n\tdone; \\\n\ttest -z \"$$list2\" || { \\\n\t  echo \" $(MKDIR_P) '$(DESTDIR)$(libdir)'\"; \\\n\t  $(MKDIR_P) \"$(DESTDIR)$(libdir)\" || exit 1; \\\n\t  echo \" $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'\"; \\\n\t  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 \"$(DESTDIR)$(libdir)\"; \\\n\t}\n\nuninstall-libLTLIBRARIES:\n\t@$(NORMAL_UNINSTALL)\n\t@list='$(lib_LTLIBRARIES)'; test -n \"$(libdir)\" || list=; \\\n\tfor p in $$list; do \\\n\t  $(am__strip_dir) \\\n\t  echo \" $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'\"; \\\n\t  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f \"$(DESTDIR)$(libdir)/$$f\"; \\\n\tdone\n\nclean-libLTLIBRARIES:\n\t-test -z \"$(lib_LTLIBRARIES)\" || rm -f $(lib_LTLIBRARIES)\n\t@list='$(lib_LTLIBRARIES)'; \\\n\tlocs=`for p in $$list; do echo $$p; done | \\\n\t      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \\\n\t      sort -u`; \\\n\ttest -z \"$$locs\" || { \\\n\t  echo rm -f $${locs}; \\\n\t  rm -f $${locs}; \\\n\t}\n\nclean-noinstLTLIBRARIES:\n\t-test -z \"$(noinst_LTLIBRARIES)\" || rm -f $(noinst_LTLIBRARIES)\n\t@list='$(noinst_LTLIBRARIES)'; \\\n\tlocs=`for p in $$list; do echo $$p; done | \\\n\t      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \\\n\t      sort -u`; \\\n\ttest -z \"$$locs\" || { \\\n\t  echo rm -f $${locs}; \\\n\t  rm -f $${locs}; \\\n\t}\n\nlibhashtable.la: $(libhashtable_la_OBJECTS) $(libhashtable_la_DEPENDENCIES) $(EXTRA_libhashtable_la_DEPENDENCIES) \n\t$(AM_V_CCLD)$(LINK)  $(libhashtable_la_OBJECTS) $(libhashtable_la_LIBADD) $(LIBS)\n\nlibzkmt.la: $(libzkmt_la_OBJECTS) $(libzkmt_la_DEPENDENCIES) $(EXTRA_libzkmt_la_DEPENDENCIES) \n\t$(AM_V_CCLD)$(libzkmt_la_LINK) $(am_libzkmt_la_rpath) $(libzkmt_la_OBJECTS) $(libzkmt_la_LIBADD) $(LIBS)\n\nlibzkst.la: $(libzkst_la_OBJECTS) $(libzkst_la_DEPENDENCIES) $(EXTRA_libzkst_la_DEPENDENCIES) \n\t$(AM_V_CCLD)$(LINK)  $(libzkst_la_OBJECTS) $(libzkst_la_LIBADD) $(LIBS)\n\nlibzookeeper_mt.la: $(libzookeeper_mt_la_OBJECTS) $(libzookeeper_mt_la_DEPENDENCIES) $(EXTRA_libzookeeper_mt_la_DEPENDENCIES) \n\t$(AM_V_CCLD)$(libzookeeper_mt_la_LINK) $(am_libzookeeper_mt_la_rpath) $(libzookeeper_mt_la_OBJECTS) $(libzookeeper_mt_la_LIBADD) $(LIBS)\n\nlibzookeeper_st.la: $(libzookeeper_st_la_OBJECTS) $(libzookeeper_st_la_DEPENDENCIES) $(EXTRA_libzookeeper_st_la_DEPENDENCIES) \n\t$(AM_V_CCLD)$(libzookeeper_st_la_LINK) -rpath $(libdir) $(libzookeeper_st_la_OBJECTS) $(libzookeeper_st_la_LIBADD) $(LIBS)\ninstall-binPROGRAMS: $(bin_PROGRAMS)\n\t@$(NORMAL_INSTALL)\n\t@list='$(bin_PROGRAMS)'; test -n \"$(bindir)\" || list=; \\\n\tif test -n \"$$list\"; then \\\n\t  echo \" $(MKDIR_P) '$(DESTDIR)$(bindir)'\"; \\\n\t  $(MKDIR_P) \"$(DESTDIR)$(bindir)\" || exit 1; \\\n\tfi; \\\n\tfor p in $$list; do echo \"$$p $$p\"; done | \\\n\tsed 's/$(EXEEXT)$$//' | \\\n\twhile read p p1; do if test -f $$p \\\n\t || test -f $$p1 \\\n\t  ; then echo \"$$p\"; echo \"$$p\"; else :; fi; \\\n\tdone | \\\n\tsed -e 'p;s,.*/,,;n;h' \\\n\t    -e 's|.*|.|' \\\n\t    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \\\n\tsed 'N;N;N;s,\\n, ,g' | \\\n\t$(AWK) 'BEGIN { files[\".\"] = \"\"; dirs[\".\"] = 1 } \\\n\t  { d=$$3; if (dirs[d] != 1) { print \"d\", d; dirs[d] = 1 } \\\n\t    if ($$2 == $$4) files[d] = files[d] \" \" $$1; \\\n\t    else { print \"f\", $$3 \"/\" $$4, $$1; } } \\\n\t  END { for (d in files) print \"f\", d, files[d] }' | \\\n\twhile read type dir files; do \\\n\t    if test \"$$dir\" = .; then dir=; else dir=/$$dir; fi; \\\n\t    test -z \"$$files\" || { \\\n\t    echo \" $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'\"; \\\n\t    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files \"$(DESTDIR)$(bindir)$$dir\" || exit $$?; \\\n\t    } \\\n\t; done\n\nuninstall-binPROGRAMS:\n\t@$(NORMAL_UNINSTALL)\n\t@list='$(bin_PROGRAMS)'; test -n \"$(bindir)\" || list=; \\\n\tfiles=`for p in $$list; do echo \"$$p\"; done | \\\n\t  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \\\n\t      -e 's/$$/$(EXEEXT)/' \\\n\t`; \\\n\ttest -n \"$$list\" || exit 0; \\\n\techo \" ( cd '$(DESTDIR)$(bindir)' && rm -f\" $$files \")\"; \\\n\tcd \"$(DESTDIR)$(bindir)\" && rm -f $$files\n\nclean-binPROGRAMS:\n\t@list='$(bin_PROGRAMS)'; test -n \"$$list\" || exit 0; \\\n\techo \" rm -f\" $$list; \\\n\trm -f $$list || exit $$?; \\\n\ttest -n \"$(EXEEXT)\" || exit 0; \\\n\tlist=`for p in $$list; do echo \"$$p\"; done | sed 's/$(EXEEXT)$$//'`; \\\n\techo \" rm -f\" $$list; \\\n\trm -f $$list\n\nclean-checkPROGRAMS:\n\t@list='$(check_PROGRAMS)'; test -n \"$$list\" || exit 0; \\\n\techo \" rm -f\" $$list; \\\n\trm -f $$list || exit $$?; \\\n\ttest -n \"$(EXEEXT)\" || exit 0; \\\n\tlist=`for p in $$list; do echo \"$$p\"; done | sed 's/$(EXEEXT)$$//'`; \\\n\techo \" rm -f\" $$list; \\\n\trm -f $$list\n\ncli_mt$(EXEEXT): $(cli_mt_OBJECTS) $(cli_mt_DEPENDENCIES) $(EXTRA_cli_mt_DEPENDENCIES) \n\t@rm -f cli_mt$(EXEEXT)\n\t$(AM_V_CCLD)$(cli_mt_LINK) $(cli_mt_OBJECTS) $(cli_mt_LDADD) $(LIBS)\n\ncli_st$(EXEEXT): $(cli_st_OBJECTS) $(cli_st_DEPENDENCIES) $(EXTRA_cli_st_DEPENDENCIES) \n\t@rm -f cli_st$(EXEEXT)\n\t$(AM_V_CCLD)$(LINK) $(cli_st_OBJECTS) $(cli_st_LDADD) $(LIBS)\n\nload_gen$(EXEEXT): $(load_gen_OBJECTS) $(load_gen_DEPENDENCIES) $(EXTRA_load_gen_DEPENDENCIES) \n\t@rm -f load_gen$(EXEEXT)\n\t$(AM_V_CCLD)$(load_gen_LINK) $(load_gen_OBJECTS) $(load_gen_LDADD) $(LIBS)\n\nzktest-mt$(EXEEXT): $(zktest_mt_OBJECTS) $(zktest_mt_DEPENDENCIES) $(EXTRA_zktest_mt_DEPENDENCIES) \n\t@rm -f zktest-mt$(EXEEXT)\n\t$(AM_V_CXXLD)$(zktest_mt_LINK) $(zktest_mt_OBJECTS) $(zktest_mt_LDADD) $(LIBS)\n\nzktest-st$(EXEEXT): $(zktest_st_OBJECTS) $(zktest_st_DEPENDENCIES) $(EXTRA_zktest_st_DEPENDENCIES) \n\t@rm -f zktest-st$(EXEEXT)\n\t$(AM_V_CXXLD)$(zktest_st_LINK) $(zktest_st_OBJECTS) $(zktest_st_LDADD) $(LIBS)\n\nmostlyclean-compile:\n\t-rm -f *.$(OBJEXT)\n\ndistclean-compile:\n\t-rm -f *.tab.c\n\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addrvec.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cli.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cli_mt-cli.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hashtable.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hashtable_itr.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzkmt_la-addrvec.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzkmt_la-mt_adaptor.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzkmt_la-recordio.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzkmt_la-zk_hashtable.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzkmt_la-zk_log.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzkmt_la-zk_sasl.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzkmt_la-zookeeper.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libzkmt_la-zookeeper.jute.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load_gen-load_gen.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/recordio.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/st_adaptor.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zk_hashtable.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zk_log.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zk_sasl.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-LibCMocks.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-LibCSymTable.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-MocksBase.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-PthreadMocks.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestClient.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestClientRetry.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestDriver.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestLogClientEnv.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestMulti.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestOperations.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestReadOnlyClient.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestReconfig.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestReconfigServer.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestSASLAuth.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestWatchers.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestZookeeperClose.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-TestZookeeperInit.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-ThreadingUtil.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-Util.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-ZKMocks.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_mt-ZooKeeperQuorumServer.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-LibCMocks.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-LibCSymTable.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-MocksBase.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestClient.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestClientRetry.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestDriver.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestLogClientEnv.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestMulti.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestOperations.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestReadOnlyClient.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestReconfig.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestReconfigServer.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestSASLAuth.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestWatchers.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestZookeeperClose.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-TestZookeeperInit.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-ThreadingUtil.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-Util.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-ZKMocks.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zktest_st-ZooKeeperQuorumServer.Po@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zookeeper.Plo@am__quote@\n@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zookeeper.jute.Plo@am__quote@\n\n.c.o:\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<\n\n.c.obj:\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`\n\n.c.lo:\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<\n\nhashtable_itr.lo: src/hashtable/hashtable_itr.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hashtable_itr.lo -MD -MP -MF $(DEPDIR)/hashtable_itr.Tpo -c -o hashtable_itr.lo `test -f 'src/hashtable/hashtable_itr.c' || echo '$(srcdir)/'`src/hashtable/hashtable_itr.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/hashtable_itr.Tpo $(DEPDIR)/hashtable_itr.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/hashtable/hashtable_itr.c' object='hashtable_itr.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hashtable_itr.lo `test -f 'src/hashtable/hashtable_itr.c' || echo '$(srcdir)/'`src/hashtable/hashtable_itr.c\n\nhashtable.lo: src/hashtable/hashtable.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hashtable.lo -MD -MP -MF $(DEPDIR)/hashtable.Tpo -c -o hashtable.lo `test -f 'src/hashtable/hashtable.c' || echo '$(srcdir)/'`src/hashtable/hashtable.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/hashtable.Tpo $(DEPDIR)/hashtable.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/hashtable/hashtable.c' object='hashtable.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hashtable.lo `test -f 'src/hashtable/hashtable.c' || echo '$(srcdir)/'`src/hashtable/hashtable.c\n\nlibzkmt_la-zookeeper.lo: src/zookeeper.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -MT libzkmt_la-zookeeper.lo -MD -MP -MF $(DEPDIR)/libzkmt_la-zookeeper.Tpo -c -o libzkmt_la-zookeeper.lo `test -f 'src/zookeeper.c' || echo '$(srcdir)/'`src/zookeeper.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/libzkmt_la-zookeeper.Tpo $(DEPDIR)/libzkmt_la-zookeeper.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/zookeeper.c' object='libzkmt_la-zookeeper.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -c -o libzkmt_la-zookeeper.lo `test -f 'src/zookeeper.c' || echo '$(srcdir)/'`src/zookeeper.c\n\nlibzkmt_la-recordio.lo: src/recordio.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -MT libzkmt_la-recordio.lo -MD -MP -MF $(DEPDIR)/libzkmt_la-recordio.Tpo -c -o libzkmt_la-recordio.lo `test -f 'src/recordio.c' || echo '$(srcdir)/'`src/recordio.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/libzkmt_la-recordio.Tpo $(DEPDIR)/libzkmt_la-recordio.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/recordio.c' object='libzkmt_la-recordio.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -c -o libzkmt_la-recordio.lo `test -f 'src/recordio.c' || echo '$(srcdir)/'`src/recordio.c\n\nlibzkmt_la-zookeeper.jute.lo: generated/zookeeper.jute.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -MT libzkmt_la-zookeeper.jute.lo -MD -MP -MF $(DEPDIR)/libzkmt_la-zookeeper.jute.Tpo -c -o libzkmt_la-zookeeper.jute.lo `test -f 'generated/zookeeper.jute.c' || echo '$(srcdir)/'`generated/zookeeper.jute.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/libzkmt_la-zookeeper.jute.Tpo $(DEPDIR)/libzkmt_la-zookeeper.jute.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='generated/zookeeper.jute.c' object='libzkmt_la-zookeeper.jute.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -c -o libzkmt_la-zookeeper.jute.lo `test -f 'generated/zookeeper.jute.c' || echo '$(srcdir)/'`generated/zookeeper.jute.c\n\nlibzkmt_la-zk_log.lo: src/zk_log.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -MT libzkmt_la-zk_log.lo -MD -MP -MF $(DEPDIR)/libzkmt_la-zk_log.Tpo -c -o libzkmt_la-zk_log.lo `test -f 'src/zk_log.c' || echo '$(srcdir)/'`src/zk_log.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/libzkmt_la-zk_log.Tpo $(DEPDIR)/libzkmt_la-zk_log.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/zk_log.c' object='libzkmt_la-zk_log.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -c -o libzkmt_la-zk_log.lo `test -f 'src/zk_log.c' || echo '$(srcdir)/'`src/zk_log.c\n\nlibzkmt_la-zk_hashtable.lo: src/zk_hashtable.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -MT libzkmt_la-zk_hashtable.lo -MD -MP -MF $(DEPDIR)/libzkmt_la-zk_hashtable.Tpo -c -o libzkmt_la-zk_hashtable.lo `test -f 'src/zk_hashtable.c' || echo '$(srcdir)/'`src/zk_hashtable.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/libzkmt_la-zk_hashtable.Tpo $(DEPDIR)/libzkmt_la-zk_hashtable.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/zk_hashtable.c' object='libzkmt_la-zk_hashtable.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -c -o libzkmt_la-zk_hashtable.lo `test -f 'src/zk_hashtable.c' || echo '$(srcdir)/'`src/zk_hashtable.c\n\nlibzkmt_la-addrvec.lo: src/addrvec.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -MT libzkmt_la-addrvec.lo -MD -MP -MF $(DEPDIR)/libzkmt_la-addrvec.Tpo -c -o libzkmt_la-addrvec.lo `test -f 'src/addrvec.c' || echo '$(srcdir)/'`src/addrvec.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/libzkmt_la-addrvec.Tpo $(DEPDIR)/libzkmt_la-addrvec.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/addrvec.c' object='libzkmt_la-addrvec.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -c -o libzkmt_la-addrvec.lo `test -f 'src/addrvec.c' || echo '$(srcdir)/'`src/addrvec.c\n\nlibzkmt_la-zk_sasl.lo: src/zk_sasl.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -MT libzkmt_la-zk_sasl.lo -MD -MP -MF $(DEPDIR)/libzkmt_la-zk_sasl.Tpo -c -o libzkmt_la-zk_sasl.lo `test -f 'src/zk_sasl.c' || echo '$(srcdir)/'`src/zk_sasl.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/libzkmt_la-zk_sasl.Tpo $(DEPDIR)/libzkmt_la-zk_sasl.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/zk_sasl.c' object='libzkmt_la-zk_sasl.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -c -o libzkmt_la-zk_sasl.lo `test -f 'src/zk_sasl.c' || echo '$(srcdir)/'`src/zk_sasl.c\n\nlibzkmt_la-mt_adaptor.lo: src/mt_adaptor.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -MT libzkmt_la-mt_adaptor.lo -MD -MP -MF $(DEPDIR)/libzkmt_la-mt_adaptor.Tpo -c -o libzkmt_la-mt_adaptor.lo `test -f 'src/mt_adaptor.c' || echo '$(srcdir)/'`src/mt_adaptor.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/libzkmt_la-mt_adaptor.Tpo $(DEPDIR)/libzkmt_la-mt_adaptor.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/mt_adaptor.c' object='libzkmt_la-mt_adaptor.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libzkmt_la_CFLAGS) $(CFLAGS) -c -o libzkmt_la-mt_adaptor.lo `test -f 'src/mt_adaptor.c' || echo '$(srcdir)/'`src/mt_adaptor.c\n\nzookeeper.lo: src/zookeeper.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zookeeper.lo -MD -MP -MF $(DEPDIR)/zookeeper.Tpo -c -o zookeeper.lo `test -f 'src/zookeeper.c' || echo '$(srcdir)/'`src/zookeeper.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zookeeper.Tpo $(DEPDIR)/zookeeper.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/zookeeper.c' object='zookeeper.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zookeeper.lo `test -f 'src/zookeeper.c' || echo '$(srcdir)/'`src/zookeeper.c\n\nrecordio.lo: src/recordio.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT recordio.lo -MD -MP -MF $(DEPDIR)/recordio.Tpo -c -o recordio.lo `test -f 'src/recordio.c' || echo '$(srcdir)/'`src/recordio.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/recordio.Tpo $(DEPDIR)/recordio.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/recordio.c' object='recordio.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o recordio.lo `test -f 'src/recordio.c' || echo '$(srcdir)/'`src/recordio.c\n\nzookeeper.jute.lo: generated/zookeeper.jute.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zookeeper.jute.lo -MD -MP -MF $(DEPDIR)/zookeeper.jute.Tpo -c -o zookeeper.jute.lo `test -f 'generated/zookeeper.jute.c' || echo '$(srcdir)/'`generated/zookeeper.jute.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zookeeper.jute.Tpo $(DEPDIR)/zookeeper.jute.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='generated/zookeeper.jute.c' object='zookeeper.jute.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zookeeper.jute.lo `test -f 'generated/zookeeper.jute.c' || echo '$(srcdir)/'`generated/zookeeper.jute.c\n\nzk_log.lo: src/zk_log.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zk_log.lo -MD -MP -MF $(DEPDIR)/zk_log.Tpo -c -o zk_log.lo `test -f 'src/zk_log.c' || echo '$(srcdir)/'`src/zk_log.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zk_log.Tpo $(DEPDIR)/zk_log.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/zk_log.c' object='zk_log.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zk_log.lo `test -f 'src/zk_log.c' || echo '$(srcdir)/'`src/zk_log.c\n\nzk_hashtable.lo: src/zk_hashtable.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zk_hashtable.lo -MD -MP -MF $(DEPDIR)/zk_hashtable.Tpo -c -o zk_hashtable.lo `test -f 'src/zk_hashtable.c' || echo '$(srcdir)/'`src/zk_hashtable.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zk_hashtable.Tpo $(DEPDIR)/zk_hashtable.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/zk_hashtable.c' object='zk_hashtable.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zk_hashtable.lo `test -f 'src/zk_hashtable.c' || echo '$(srcdir)/'`src/zk_hashtable.c\n\naddrvec.lo: src/addrvec.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT addrvec.lo -MD -MP -MF $(DEPDIR)/addrvec.Tpo -c -o addrvec.lo `test -f 'src/addrvec.c' || echo '$(srcdir)/'`src/addrvec.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/addrvec.Tpo $(DEPDIR)/addrvec.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/addrvec.c' object='addrvec.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o addrvec.lo `test -f 'src/addrvec.c' || echo '$(srcdir)/'`src/addrvec.c\n\nzk_sasl.lo: src/zk_sasl.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT zk_sasl.lo -MD -MP -MF $(DEPDIR)/zk_sasl.Tpo -c -o zk_sasl.lo `test -f 'src/zk_sasl.c' || echo '$(srcdir)/'`src/zk_sasl.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zk_sasl.Tpo $(DEPDIR)/zk_sasl.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/zk_sasl.c' object='zk_sasl.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o zk_sasl.lo `test -f 'src/zk_sasl.c' || echo '$(srcdir)/'`src/zk_sasl.c\n\nst_adaptor.lo: src/st_adaptor.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT st_adaptor.lo -MD -MP -MF $(DEPDIR)/st_adaptor.Tpo -c -o st_adaptor.lo `test -f 'src/st_adaptor.c' || echo '$(srcdir)/'`src/st_adaptor.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/st_adaptor.Tpo $(DEPDIR)/st_adaptor.Plo\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/st_adaptor.c' object='st_adaptor.lo' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o st_adaptor.lo `test -f 'src/st_adaptor.c' || echo '$(srcdir)/'`src/st_adaptor.c\n\ncli_mt-cli.o: src/cli.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cli_mt_CFLAGS) $(CFLAGS) -MT cli_mt-cli.o -MD -MP -MF $(DEPDIR)/cli_mt-cli.Tpo -c -o cli_mt-cli.o `test -f 'src/cli.c' || echo '$(srcdir)/'`src/cli.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/cli_mt-cli.Tpo $(DEPDIR)/cli_mt-cli.Po\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/cli.c' object='cli_mt-cli.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cli_mt_CFLAGS) $(CFLAGS) -c -o cli_mt-cli.o `test -f 'src/cli.c' || echo '$(srcdir)/'`src/cli.c\n\ncli_mt-cli.obj: src/cli.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cli_mt_CFLAGS) $(CFLAGS) -MT cli_mt-cli.obj -MD -MP -MF $(DEPDIR)/cli_mt-cli.Tpo -c -o cli_mt-cli.obj `if test -f 'src/cli.c'; then $(CYGPATH_W) 'src/cli.c'; else $(CYGPATH_W) '$(srcdir)/src/cli.c'; fi`\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/cli_mt-cli.Tpo $(DEPDIR)/cli_mt-cli.Po\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/cli.c' object='cli_mt-cli.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cli_mt_CFLAGS) $(CFLAGS) -c -o cli_mt-cli.obj `if test -f 'src/cli.c'; then $(CYGPATH_W) 'src/cli.c'; else $(CYGPATH_W) '$(srcdir)/src/cli.c'; fi`\n\ncli.o: src/cli.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cli.o -MD -MP -MF $(DEPDIR)/cli.Tpo -c -o cli.o `test -f 'src/cli.c' || echo '$(srcdir)/'`src/cli.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/cli.Tpo $(DEPDIR)/cli.Po\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/cli.c' object='cli.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cli.o `test -f 'src/cli.c' || echo '$(srcdir)/'`src/cli.c\n\ncli.obj: src/cli.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cli.obj -MD -MP -MF $(DEPDIR)/cli.Tpo -c -o cli.obj `if test -f 'src/cli.c'; then $(CYGPATH_W) 'src/cli.c'; else $(CYGPATH_W) '$(srcdir)/src/cli.c'; fi`\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/cli.Tpo $(DEPDIR)/cli.Po\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/cli.c' object='cli.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cli.obj `if test -f 'src/cli.c'; then $(CYGPATH_W) 'src/cli.c'; else $(CYGPATH_W) '$(srcdir)/src/cli.c'; fi`\n\nload_gen-load_gen.o: src/load_gen.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(load_gen_CFLAGS) $(CFLAGS) -MT load_gen-load_gen.o -MD -MP -MF $(DEPDIR)/load_gen-load_gen.Tpo -c -o load_gen-load_gen.o `test -f 'src/load_gen.c' || echo '$(srcdir)/'`src/load_gen.c\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/load_gen-load_gen.Tpo $(DEPDIR)/load_gen-load_gen.Po\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/load_gen.c' object='load_gen-load_gen.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(load_gen_CFLAGS) $(CFLAGS) -c -o load_gen-load_gen.o `test -f 'src/load_gen.c' || echo '$(srcdir)/'`src/load_gen.c\n\nload_gen-load_gen.obj: src/load_gen.c\n@am__fastdepCC_TRUE@\t$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(load_gen_CFLAGS) $(CFLAGS) -MT load_gen-load_gen.obj -MD -MP -MF $(DEPDIR)/load_gen-load_gen.Tpo -c -o load_gen-load_gen.obj `if test -f 'src/load_gen.c'; then $(CYGPATH_W) 'src/load_gen.c'; else $(CYGPATH_W) '$(srcdir)/src/load_gen.c'; fi`\n@am__fastdepCC_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/load_gen-load_gen.Tpo $(DEPDIR)/load_gen-load_gen.Po\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\t$(AM_V_CC)source='src/load_gen.c' object='load_gen-load_gen.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCC_FALSE@\tDEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCC_FALSE@\t$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(load_gen_CFLAGS) $(CFLAGS) -c -o load_gen-load_gen.obj `if test -f 'src/load_gen.c'; then $(CYGPATH_W) 'src/load_gen.c'; else $(CYGPATH_W) '$(srcdir)/src/load_gen.c'; fi`\n\n.cc.o:\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<\n\n.cc.obj:\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`\n\n.cc.lo:\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<\n\nzktest_mt-TestDriver.o: tests/TestDriver.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestDriver.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestDriver.Tpo -c -o zktest_mt-TestDriver.o `test -f 'tests/TestDriver.cc' || echo '$(srcdir)/'`tests/TestDriver.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestDriver.Tpo $(DEPDIR)/zktest_mt-TestDriver.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestDriver.cc' object='zktest_mt-TestDriver.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestDriver.o `test -f 'tests/TestDriver.cc' || echo '$(srcdir)/'`tests/TestDriver.cc\n\nzktest_mt-TestDriver.obj: tests/TestDriver.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestDriver.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestDriver.Tpo -c -o zktest_mt-TestDriver.obj `if test -f 'tests/TestDriver.cc'; then $(CYGPATH_W) 'tests/TestDriver.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestDriver.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestDriver.Tpo $(DEPDIR)/zktest_mt-TestDriver.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestDriver.cc' object='zktest_mt-TestDriver.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestDriver.obj `if test -f 'tests/TestDriver.cc'; then $(CYGPATH_W) 'tests/TestDriver.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestDriver.cc'; fi`\n\nzktest_mt-LibCMocks.o: tests/LibCMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-LibCMocks.o -MD -MP -MF $(DEPDIR)/zktest_mt-LibCMocks.Tpo -c -o zktest_mt-LibCMocks.o `test -f 'tests/LibCMocks.cc' || echo '$(srcdir)/'`tests/LibCMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-LibCMocks.Tpo $(DEPDIR)/zktest_mt-LibCMocks.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/LibCMocks.cc' object='zktest_mt-LibCMocks.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-LibCMocks.o `test -f 'tests/LibCMocks.cc' || echo '$(srcdir)/'`tests/LibCMocks.cc\n\nzktest_mt-LibCMocks.obj: tests/LibCMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-LibCMocks.obj -MD -MP -MF $(DEPDIR)/zktest_mt-LibCMocks.Tpo -c -o zktest_mt-LibCMocks.obj `if test -f 'tests/LibCMocks.cc'; then $(CYGPATH_W) 'tests/LibCMocks.cc'; else $(CYGPATH_W) '$(srcdir)/tests/LibCMocks.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-LibCMocks.Tpo $(DEPDIR)/zktest_mt-LibCMocks.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/LibCMocks.cc' object='zktest_mt-LibCMocks.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-LibCMocks.obj `if test -f 'tests/LibCMocks.cc'; then $(CYGPATH_W) 'tests/LibCMocks.cc'; else $(CYGPATH_W) '$(srcdir)/tests/LibCMocks.cc'; fi`\n\nzktest_mt-LibCSymTable.o: tests/LibCSymTable.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-LibCSymTable.o -MD -MP -MF $(DEPDIR)/zktest_mt-LibCSymTable.Tpo -c -o zktest_mt-LibCSymTable.o `test -f 'tests/LibCSymTable.cc' || echo '$(srcdir)/'`tests/LibCSymTable.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-LibCSymTable.Tpo $(DEPDIR)/zktest_mt-LibCSymTable.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/LibCSymTable.cc' object='zktest_mt-LibCSymTable.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-LibCSymTable.o `test -f 'tests/LibCSymTable.cc' || echo '$(srcdir)/'`tests/LibCSymTable.cc\n\nzktest_mt-LibCSymTable.obj: tests/LibCSymTable.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-LibCSymTable.obj -MD -MP -MF $(DEPDIR)/zktest_mt-LibCSymTable.Tpo -c -o zktest_mt-LibCSymTable.obj `if test -f 'tests/LibCSymTable.cc'; then $(CYGPATH_W) 'tests/LibCSymTable.cc'; else $(CYGPATH_W) '$(srcdir)/tests/LibCSymTable.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-LibCSymTable.Tpo $(DEPDIR)/zktest_mt-LibCSymTable.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/LibCSymTable.cc' object='zktest_mt-LibCSymTable.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-LibCSymTable.obj `if test -f 'tests/LibCSymTable.cc'; then $(CYGPATH_W) 'tests/LibCSymTable.cc'; else $(CYGPATH_W) '$(srcdir)/tests/LibCSymTable.cc'; fi`\n\nzktest_mt-MocksBase.o: tests/MocksBase.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-MocksBase.o -MD -MP -MF $(DEPDIR)/zktest_mt-MocksBase.Tpo -c -o zktest_mt-MocksBase.o `test -f 'tests/MocksBase.cc' || echo '$(srcdir)/'`tests/MocksBase.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-MocksBase.Tpo $(DEPDIR)/zktest_mt-MocksBase.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/MocksBase.cc' object='zktest_mt-MocksBase.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-MocksBase.o `test -f 'tests/MocksBase.cc' || echo '$(srcdir)/'`tests/MocksBase.cc\n\nzktest_mt-MocksBase.obj: tests/MocksBase.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-MocksBase.obj -MD -MP -MF $(DEPDIR)/zktest_mt-MocksBase.Tpo -c -o zktest_mt-MocksBase.obj `if test -f 'tests/MocksBase.cc'; then $(CYGPATH_W) 'tests/MocksBase.cc'; else $(CYGPATH_W) '$(srcdir)/tests/MocksBase.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-MocksBase.Tpo $(DEPDIR)/zktest_mt-MocksBase.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/MocksBase.cc' object='zktest_mt-MocksBase.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-MocksBase.obj `if test -f 'tests/MocksBase.cc'; then $(CYGPATH_W) 'tests/MocksBase.cc'; else $(CYGPATH_W) '$(srcdir)/tests/MocksBase.cc'; fi`\n\nzktest_mt-ZKMocks.o: tests/ZKMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-ZKMocks.o -MD -MP -MF $(DEPDIR)/zktest_mt-ZKMocks.Tpo -c -o zktest_mt-ZKMocks.o `test -f 'tests/ZKMocks.cc' || echo '$(srcdir)/'`tests/ZKMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-ZKMocks.Tpo $(DEPDIR)/zktest_mt-ZKMocks.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ZKMocks.cc' object='zktest_mt-ZKMocks.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-ZKMocks.o `test -f 'tests/ZKMocks.cc' || echo '$(srcdir)/'`tests/ZKMocks.cc\n\nzktest_mt-ZKMocks.obj: tests/ZKMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-ZKMocks.obj -MD -MP -MF $(DEPDIR)/zktest_mt-ZKMocks.Tpo -c -o zktest_mt-ZKMocks.obj `if test -f 'tests/ZKMocks.cc'; then $(CYGPATH_W) 'tests/ZKMocks.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ZKMocks.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-ZKMocks.Tpo $(DEPDIR)/zktest_mt-ZKMocks.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ZKMocks.cc' object='zktest_mt-ZKMocks.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-ZKMocks.obj `if test -f 'tests/ZKMocks.cc'; then $(CYGPATH_W) 'tests/ZKMocks.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ZKMocks.cc'; fi`\n\nzktest_mt-Util.o: tests/Util.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-Util.o -MD -MP -MF $(DEPDIR)/zktest_mt-Util.Tpo -c -o zktest_mt-Util.o `test -f 'tests/Util.cc' || echo '$(srcdir)/'`tests/Util.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-Util.Tpo $(DEPDIR)/zktest_mt-Util.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/Util.cc' object='zktest_mt-Util.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-Util.o `test -f 'tests/Util.cc' || echo '$(srcdir)/'`tests/Util.cc\n\nzktest_mt-Util.obj: tests/Util.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-Util.obj -MD -MP -MF $(DEPDIR)/zktest_mt-Util.Tpo -c -o zktest_mt-Util.obj `if test -f 'tests/Util.cc'; then $(CYGPATH_W) 'tests/Util.cc'; else $(CYGPATH_W) '$(srcdir)/tests/Util.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-Util.Tpo $(DEPDIR)/zktest_mt-Util.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/Util.cc' object='zktest_mt-Util.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-Util.obj `if test -f 'tests/Util.cc'; then $(CYGPATH_W) 'tests/Util.cc'; else $(CYGPATH_W) '$(srcdir)/tests/Util.cc'; fi`\n\nzktest_mt-ThreadingUtil.o: tests/ThreadingUtil.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-ThreadingUtil.o -MD -MP -MF $(DEPDIR)/zktest_mt-ThreadingUtil.Tpo -c -o zktest_mt-ThreadingUtil.o `test -f 'tests/ThreadingUtil.cc' || echo '$(srcdir)/'`tests/ThreadingUtil.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-ThreadingUtil.Tpo $(DEPDIR)/zktest_mt-ThreadingUtil.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ThreadingUtil.cc' object='zktest_mt-ThreadingUtil.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-ThreadingUtil.o `test -f 'tests/ThreadingUtil.cc' || echo '$(srcdir)/'`tests/ThreadingUtil.cc\n\nzktest_mt-ThreadingUtil.obj: tests/ThreadingUtil.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-ThreadingUtil.obj -MD -MP -MF $(DEPDIR)/zktest_mt-ThreadingUtil.Tpo -c -o zktest_mt-ThreadingUtil.obj `if test -f 'tests/ThreadingUtil.cc'; then $(CYGPATH_W) 'tests/ThreadingUtil.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ThreadingUtil.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-ThreadingUtil.Tpo $(DEPDIR)/zktest_mt-ThreadingUtil.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ThreadingUtil.cc' object='zktest_mt-ThreadingUtil.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-ThreadingUtil.obj `if test -f 'tests/ThreadingUtil.cc'; then $(CYGPATH_W) 'tests/ThreadingUtil.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ThreadingUtil.cc'; fi`\n\nzktest_mt-TestZookeeperInit.o: tests/TestZookeeperInit.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestZookeeperInit.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestZookeeperInit.Tpo -c -o zktest_mt-TestZookeeperInit.o `test -f 'tests/TestZookeeperInit.cc' || echo '$(srcdir)/'`tests/TestZookeeperInit.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestZookeeperInit.Tpo $(DEPDIR)/zktest_mt-TestZookeeperInit.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestZookeeperInit.cc' object='zktest_mt-TestZookeeperInit.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestZookeeperInit.o `test -f 'tests/TestZookeeperInit.cc' || echo '$(srcdir)/'`tests/TestZookeeperInit.cc\n\nzktest_mt-TestZookeeperInit.obj: tests/TestZookeeperInit.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestZookeeperInit.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestZookeeperInit.Tpo -c -o zktest_mt-TestZookeeperInit.obj `if test -f 'tests/TestZookeeperInit.cc'; then $(CYGPATH_W) 'tests/TestZookeeperInit.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestZookeeperInit.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestZookeeperInit.Tpo $(DEPDIR)/zktest_mt-TestZookeeperInit.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestZookeeperInit.cc' object='zktest_mt-TestZookeeperInit.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestZookeeperInit.obj `if test -f 'tests/TestZookeeperInit.cc'; then $(CYGPATH_W) 'tests/TestZookeeperInit.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestZookeeperInit.cc'; fi`\n\nzktest_mt-TestZookeeperClose.o: tests/TestZookeeperClose.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestZookeeperClose.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestZookeeperClose.Tpo -c -o zktest_mt-TestZookeeperClose.o `test -f 'tests/TestZookeeperClose.cc' || echo '$(srcdir)/'`tests/TestZookeeperClose.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestZookeeperClose.Tpo $(DEPDIR)/zktest_mt-TestZookeeperClose.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestZookeeperClose.cc' object='zktest_mt-TestZookeeperClose.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestZookeeperClose.o `test -f 'tests/TestZookeeperClose.cc' || echo '$(srcdir)/'`tests/TestZookeeperClose.cc\n\nzktest_mt-TestZookeeperClose.obj: tests/TestZookeeperClose.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestZookeeperClose.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestZookeeperClose.Tpo -c -o zktest_mt-TestZookeeperClose.obj `if test -f 'tests/TestZookeeperClose.cc'; then $(CYGPATH_W) 'tests/TestZookeeperClose.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestZookeeperClose.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestZookeeperClose.Tpo $(DEPDIR)/zktest_mt-TestZookeeperClose.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestZookeeperClose.cc' object='zktest_mt-TestZookeeperClose.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestZookeeperClose.obj `if test -f 'tests/TestZookeeperClose.cc'; then $(CYGPATH_W) 'tests/TestZookeeperClose.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestZookeeperClose.cc'; fi`\n\nzktest_mt-TestReconfig.o: tests/TestReconfig.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestReconfig.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestReconfig.Tpo -c -o zktest_mt-TestReconfig.o `test -f 'tests/TestReconfig.cc' || echo '$(srcdir)/'`tests/TestReconfig.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestReconfig.Tpo $(DEPDIR)/zktest_mt-TestReconfig.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReconfig.cc' object='zktest_mt-TestReconfig.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestReconfig.o `test -f 'tests/TestReconfig.cc' || echo '$(srcdir)/'`tests/TestReconfig.cc\n\nzktest_mt-TestReconfig.obj: tests/TestReconfig.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestReconfig.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestReconfig.Tpo -c -o zktest_mt-TestReconfig.obj `if test -f 'tests/TestReconfig.cc'; then $(CYGPATH_W) 'tests/TestReconfig.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReconfig.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestReconfig.Tpo $(DEPDIR)/zktest_mt-TestReconfig.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReconfig.cc' object='zktest_mt-TestReconfig.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestReconfig.obj `if test -f 'tests/TestReconfig.cc'; then $(CYGPATH_W) 'tests/TestReconfig.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReconfig.cc'; fi`\n\nzktest_mt-TestReconfigServer.o: tests/TestReconfigServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestReconfigServer.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestReconfigServer.Tpo -c -o zktest_mt-TestReconfigServer.o `test -f 'tests/TestReconfigServer.cc' || echo '$(srcdir)/'`tests/TestReconfigServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestReconfigServer.Tpo $(DEPDIR)/zktest_mt-TestReconfigServer.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReconfigServer.cc' object='zktest_mt-TestReconfigServer.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestReconfigServer.o `test -f 'tests/TestReconfigServer.cc' || echo '$(srcdir)/'`tests/TestReconfigServer.cc\n\nzktest_mt-TestReconfigServer.obj: tests/TestReconfigServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestReconfigServer.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestReconfigServer.Tpo -c -o zktest_mt-TestReconfigServer.obj `if test -f 'tests/TestReconfigServer.cc'; then $(CYGPATH_W) 'tests/TestReconfigServer.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReconfigServer.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestReconfigServer.Tpo $(DEPDIR)/zktest_mt-TestReconfigServer.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReconfigServer.cc' object='zktest_mt-TestReconfigServer.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestReconfigServer.obj `if test -f 'tests/TestReconfigServer.cc'; then $(CYGPATH_W) 'tests/TestReconfigServer.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReconfigServer.cc'; fi`\n\nzktest_mt-TestClientRetry.o: tests/TestClientRetry.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestClientRetry.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestClientRetry.Tpo -c -o zktest_mt-TestClientRetry.o `test -f 'tests/TestClientRetry.cc' || echo '$(srcdir)/'`tests/TestClientRetry.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestClientRetry.Tpo $(DEPDIR)/zktest_mt-TestClientRetry.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestClientRetry.cc' object='zktest_mt-TestClientRetry.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestClientRetry.o `test -f 'tests/TestClientRetry.cc' || echo '$(srcdir)/'`tests/TestClientRetry.cc\n\nzktest_mt-TestClientRetry.obj: tests/TestClientRetry.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestClientRetry.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestClientRetry.Tpo -c -o zktest_mt-TestClientRetry.obj `if test -f 'tests/TestClientRetry.cc'; then $(CYGPATH_W) 'tests/TestClientRetry.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestClientRetry.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestClientRetry.Tpo $(DEPDIR)/zktest_mt-TestClientRetry.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestClientRetry.cc' object='zktest_mt-TestClientRetry.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestClientRetry.obj `if test -f 'tests/TestClientRetry.cc'; then $(CYGPATH_W) 'tests/TestClientRetry.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestClientRetry.cc'; fi`\n\nzktest_mt-TestOperations.o: tests/TestOperations.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestOperations.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestOperations.Tpo -c -o zktest_mt-TestOperations.o `test -f 'tests/TestOperations.cc' || echo '$(srcdir)/'`tests/TestOperations.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestOperations.Tpo $(DEPDIR)/zktest_mt-TestOperations.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestOperations.cc' object='zktest_mt-TestOperations.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestOperations.o `test -f 'tests/TestOperations.cc' || echo '$(srcdir)/'`tests/TestOperations.cc\n\nzktest_mt-TestOperations.obj: tests/TestOperations.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestOperations.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestOperations.Tpo -c -o zktest_mt-TestOperations.obj `if test -f 'tests/TestOperations.cc'; then $(CYGPATH_W) 'tests/TestOperations.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestOperations.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestOperations.Tpo $(DEPDIR)/zktest_mt-TestOperations.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestOperations.cc' object='zktest_mt-TestOperations.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestOperations.obj `if test -f 'tests/TestOperations.cc'; then $(CYGPATH_W) 'tests/TestOperations.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestOperations.cc'; fi`\n\nzktest_mt-TestMulti.o: tests/TestMulti.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestMulti.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestMulti.Tpo -c -o zktest_mt-TestMulti.o `test -f 'tests/TestMulti.cc' || echo '$(srcdir)/'`tests/TestMulti.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestMulti.Tpo $(DEPDIR)/zktest_mt-TestMulti.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestMulti.cc' object='zktest_mt-TestMulti.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestMulti.o `test -f 'tests/TestMulti.cc' || echo '$(srcdir)/'`tests/TestMulti.cc\n\nzktest_mt-TestMulti.obj: tests/TestMulti.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestMulti.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestMulti.Tpo -c -o zktest_mt-TestMulti.obj `if test -f 'tests/TestMulti.cc'; then $(CYGPATH_W) 'tests/TestMulti.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestMulti.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestMulti.Tpo $(DEPDIR)/zktest_mt-TestMulti.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestMulti.cc' object='zktest_mt-TestMulti.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestMulti.obj `if test -f 'tests/TestMulti.cc'; then $(CYGPATH_W) 'tests/TestMulti.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestMulti.cc'; fi`\n\nzktest_mt-TestWatchers.o: tests/TestWatchers.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestWatchers.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestWatchers.Tpo -c -o zktest_mt-TestWatchers.o `test -f 'tests/TestWatchers.cc' || echo '$(srcdir)/'`tests/TestWatchers.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestWatchers.Tpo $(DEPDIR)/zktest_mt-TestWatchers.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestWatchers.cc' object='zktest_mt-TestWatchers.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestWatchers.o `test -f 'tests/TestWatchers.cc' || echo '$(srcdir)/'`tests/TestWatchers.cc\n\nzktest_mt-TestWatchers.obj: tests/TestWatchers.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestWatchers.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestWatchers.Tpo -c -o zktest_mt-TestWatchers.obj `if test -f 'tests/TestWatchers.cc'; then $(CYGPATH_W) 'tests/TestWatchers.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestWatchers.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestWatchers.Tpo $(DEPDIR)/zktest_mt-TestWatchers.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestWatchers.cc' object='zktest_mt-TestWatchers.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestWatchers.obj `if test -f 'tests/TestWatchers.cc'; then $(CYGPATH_W) 'tests/TestWatchers.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestWatchers.cc'; fi`\n\nzktest_mt-TestClient.o: tests/TestClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestClient.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestClient.Tpo -c -o zktest_mt-TestClient.o `test -f 'tests/TestClient.cc' || echo '$(srcdir)/'`tests/TestClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestClient.Tpo $(DEPDIR)/zktest_mt-TestClient.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestClient.cc' object='zktest_mt-TestClient.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestClient.o `test -f 'tests/TestClient.cc' || echo '$(srcdir)/'`tests/TestClient.cc\n\nzktest_mt-TestClient.obj: tests/TestClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestClient.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestClient.Tpo -c -o zktest_mt-TestClient.obj `if test -f 'tests/TestClient.cc'; then $(CYGPATH_W) 'tests/TestClient.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestClient.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestClient.Tpo $(DEPDIR)/zktest_mt-TestClient.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestClient.cc' object='zktest_mt-TestClient.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestClient.obj `if test -f 'tests/TestClient.cc'; then $(CYGPATH_W) 'tests/TestClient.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestClient.cc'; fi`\n\nzktest_mt-ZooKeeperQuorumServer.o: tests/ZooKeeperQuorumServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-ZooKeeperQuorumServer.o -MD -MP -MF $(DEPDIR)/zktest_mt-ZooKeeperQuorumServer.Tpo -c -o zktest_mt-ZooKeeperQuorumServer.o `test -f 'tests/ZooKeeperQuorumServer.cc' || echo '$(srcdir)/'`tests/ZooKeeperQuorumServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-ZooKeeperQuorumServer.Tpo $(DEPDIR)/zktest_mt-ZooKeeperQuorumServer.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ZooKeeperQuorumServer.cc' object='zktest_mt-ZooKeeperQuorumServer.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-ZooKeeperQuorumServer.o `test -f 'tests/ZooKeeperQuorumServer.cc' || echo '$(srcdir)/'`tests/ZooKeeperQuorumServer.cc\n\nzktest_mt-ZooKeeperQuorumServer.obj: tests/ZooKeeperQuorumServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-ZooKeeperQuorumServer.obj -MD -MP -MF $(DEPDIR)/zktest_mt-ZooKeeperQuorumServer.Tpo -c -o zktest_mt-ZooKeeperQuorumServer.obj `if test -f 'tests/ZooKeeperQuorumServer.cc'; then $(CYGPATH_W) 'tests/ZooKeeperQuorumServer.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ZooKeeperQuorumServer.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-ZooKeeperQuorumServer.Tpo $(DEPDIR)/zktest_mt-ZooKeeperQuorumServer.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ZooKeeperQuorumServer.cc' object='zktest_mt-ZooKeeperQuorumServer.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-ZooKeeperQuorumServer.obj `if test -f 'tests/ZooKeeperQuorumServer.cc'; then $(CYGPATH_W) 'tests/ZooKeeperQuorumServer.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ZooKeeperQuorumServer.cc'; fi`\n\nzktest_mt-TestReadOnlyClient.o: tests/TestReadOnlyClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestReadOnlyClient.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestReadOnlyClient.Tpo -c -o zktest_mt-TestReadOnlyClient.o `test -f 'tests/TestReadOnlyClient.cc' || echo '$(srcdir)/'`tests/TestReadOnlyClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestReadOnlyClient.Tpo $(DEPDIR)/zktest_mt-TestReadOnlyClient.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReadOnlyClient.cc' object='zktest_mt-TestReadOnlyClient.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestReadOnlyClient.o `test -f 'tests/TestReadOnlyClient.cc' || echo '$(srcdir)/'`tests/TestReadOnlyClient.cc\n\nzktest_mt-TestReadOnlyClient.obj: tests/TestReadOnlyClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestReadOnlyClient.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestReadOnlyClient.Tpo -c -o zktest_mt-TestReadOnlyClient.obj `if test -f 'tests/TestReadOnlyClient.cc'; then $(CYGPATH_W) 'tests/TestReadOnlyClient.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReadOnlyClient.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestReadOnlyClient.Tpo $(DEPDIR)/zktest_mt-TestReadOnlyClient.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReadOnlyClient.cc' object='zktest_mt-TestReadOnlyClient.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestReadOnlyClient.obj `if test -f 'tests/TestReadOnlyClient.cc'; then $(CYGPATH_W) 'tests/TestReadOnlyClient.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReadOnlyClient.cc'; fi`\n\nzktest_mt-TestLogClientEnv.o: tests/TestLogClientEnv.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestLogClientEnv.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestLogClientEnv.Tpo -c -o zktest_mt-TestLogClientEnv.o `test -f 'tests/TestLogClientEnv.cc' || echo '$(srcdir)/'`tests/TestLogClientEnv.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestLogClientEnv.Tpo $(DEPDIR)/zktest_mt-TestLogClientEnv.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestLogClientEnv.cc' object='zktest_mt-TestLogClientEnv.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestLogClientEnv.o `test -f 'tests/TestLogClientEnv.cc' || echo '$(srcdir)/'`tests/TestLogClientEnv.cc\n\nzktest_mt-TestLogClientEnv.obj: tests/TestLogClientEnv.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestLogClientEnv.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestLogClientEnv.Tpo -c -o zktest_mt-TestLogClientEnv.obj `if test -f 'tests/TestLogClientEnv.cc'; then $(CYGPATH_W) 'tests/TestLogClientEnv.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestLogClientEnv.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestLogClientEnv.Tpo $(DEPDIR)/zktest_mt-TestLogClientEnv.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestLogClientEnv.cc' object='zktest_mt-TestLogClientEnv.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestLogClientEnv.obj `if test -f 'tests/TestLogClientEnv.cc'; then $(CYGPATH_W) 'tests/TestLogClientEnv.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestLogClientEnv.cc'; fi`\n\nzktest_mt-TestSASLAuth.o: tests/TestSASLAuth.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestSASLAuth.o -MD -MP -MF $(DEPDIR)/zktest_mt-TestSASLAuth.Tpo -c -o zktest_mt-TestSASLAuth.o `test -f 'tests/TestSASLAuth.cc' || echo '$(srcdir)/'`tests/TestSASLAuth.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestSASLAuth.Tpo $(DEPDIR)/zktest_mt-TestSASLAuth.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestSASLAuth.cc' object='zktest_mt-TestSASLAuth.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestSASLAuth.o `test -f 'tests/TestSASLAuth.cc' || echo '$(srcdir)/'`tests/TestSASLAuth.cc\n\nzktest_mt-TestSASLAuth.obj: tests/TestSASLAuth.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-TestSASLAuth.obj -MD -MP -MF $(DEPDIR)/zktest_mt-TestSASLAuth.Tpo -c -o zktest_mt-TestSASLAuth.obj `if test -f 'tests/TestSASLAuth.cc'; then $(CYGPATH_W) 'tests/TestSASLAuth.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestSASLAuth.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-TestSASLAuth.Tpo $(DEPDIR)/zktest_mt-TestSASLAuth.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestSASLAuth.cc' object='zktest_mt-TestSASLAuth.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-TestSASLAuth.obj `if test -f 'tests/TestSASLAuth.cc'; then $(CYGPATH_W) 'tests/TestSASLAuth.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestSASLAuth.cc'; fi`\n\nzktest_mt-PthreadMocks.o: tests/PthreadMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-PthreadMocks.o -MD -MP -MF $(DEPDIR)/zktest_mt-PthreadMocks.Tpo -c -o zktest_mt-PthreadMocks.o `test -f 'tests/PthreadMocks.cc' || echo '$(srcdir)/'`tests/PthreadMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-PthreadMocks.Tpo $(DEPDIR)/zktest_mt-PthreadMocks.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/PthreadMocks.cc' object='zktest_mt-PthreadMocks.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-PthreadMocks.o `test -f 'tests/PthreadMocks.cc' || echo '$(srcdir)/'`tests/PthreadMocks.cc\n\nzktest_mt-PthreadMocks.obj: tests/PthreadMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -MT zktest_mt-PthreadMocks.obj -MD -MP -MF $(DEPDIR)/zktest_mt-PthreadMocks.Tpo -c -o zktest_mt-PthreadMocks.obj `if test -f 'tests/PthreadMocks.cc'; then $(CYGPATH_W) 'tests/PthreadMocks.cc'; else $(CYGPATH_W) '$(srcdir)/tests/PthreadMocks.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_mt-PthreadMocks.Tpo $(DEPDIR)/zktest_mt-PthreadMocks.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/PthreadMocks.cc' object='zktest_mt-PthreadMocks.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_mt_CXXFLAGS) $(CXXFLAGS) -c -o zktest_mt-PthreadMocks.obj `if test -f 'tests/PthreadMocks.cc'; then $(CYGPATH_W) 'tests/PthreadMocks.cc'; else $(CYGPATH_W) '$(srcdir)/tests/PthreadMocks.cc'; fi`\n\nzktest_st-TestDriver.o: tests/TestDriver.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestDriver.o -MD -MP -MF $(DEPDIR)/zktest_st-TestDriver.Tpo -c -o zktest_st-TestDriver.o `test -f 'tests/TestDriver.cc' || echo '$(srcdir)/'`tests/TestDriver.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestDriver.Tpo $(DEPDIR)/zktest_st-TestDriver.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestDriver.cc' object='zktest_st-TestDriver.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestDriver.o `test -f 'tests/TestDriver.cc' || echo '$(srcdir)/'`tests/TestDriver.cc\n\nzktest_st-TestDriver.obj: tests/TestDriver.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestDriver.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestDriver.Tpo -c -o zktest_st-TestDriver.obj `if test -f 'tests/TestDriver.cc'; then $(CYGPATH_W) 'tests/TestDriver.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestDriver.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestDriver.Tpo $(DEPDIR)/zktest_st-TestDriver.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestDriver.cc' object='zktest_st-TestDriver.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestDriver.obj `if test -f 'tests/TestDriver.cc'; then $(CYGPATH_W) 'tests/TestDriver.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestDriver.cc'; fi`\n\nzktest_st-LibCMocks.o: tests/LibCMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-LibCMocks.o -MD -MP -MF $(DEPDIR)/zktest_st-LibCMocks.Tpo -c -o zktest_st-LibCMocks.o `test -f 'tests/LibCMocks.cc' || echo '$(srcdir)/'`tests/LibCMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-LibCMocks.Tpo $(DEPDIR)/zktest_st-LibCMocks.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/LibCMocks.cc' object='zktest_st-LibCMocks.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-LibCMocks.o `test -f 'tests/LibCMocks.cc' || echo '$(srcdir)/'`tests/LibCMocks.cc\n\nzktest_st-LibCMocks.obj: tests/LibCMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-LibCMocks.obj -MD -MP -MF $(DEPDIR)/zktest_st-LibCMocks.Tpo -c -o zktest_st-LibCMocks.obj `if test -f 'tests/LibCMocks.cc'; then $(CYGPATH_W) 'tests/LibCMocks.cc'; else $(CYGPATH_W) '$(srcdir)/tests/LibCMocks.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-LibCMocks.Tpo $(DEPDIR)/zktest_st-LibCMocks.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/LibCMocks.cc' object='zktest_st-LibCMocks.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-LibCMocks.obj `if test -f 'tests/LibCMocks.cc'; then $(CYGPATH_W) 'tests/LibCMocks.cc'; else $(CYGPATH_W) '$(srcdir)/tests/LibCMocks.cc'; fi`\n\nzktest_st-LibCSymTable.o: tests/LibCSymTable.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-LibCSymTable.o -MD -MP -MF $(DEPDIR)/zktest_st-LibCSymTable.Tpo -c -o zktest_st-LibCSymTable.o `test -f 'tests/LibCSymTable.cc' || echo '$(srcdir)/'`tests/LibCSymTable.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-LibCSymTable.Tpo $(DEPDIR)/zktest_st-LibCSymTable.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/LibCSymTable.cc' object='zktest_st-LibCSymTable.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-LibCSymTable.o `test -f 'tests/LibCSymTable.cc' || echo '$(srcdir)/'`tests/LibCSymTable.cc\n\nzktest_st-LibCSymTable.obj: tests/LibCSymTable.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-LibCSymTable.obj -MD -MP -MF $(DEPDIR)/zktest_st-LibCSymTable.Tpo -c -o zktest_st-LibCSymTable.obj `if test -f 'tests/LibCSymTable.cc'; then $(CYGPATH_W) 'tests/LibCSymTable.cc'; else $(CYGPATH_W) '$(srcdir)/tests/LibCSymTable.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-LibCSymTable.Tpo $(DEPDIR)/zktest_st-LibCSymTable.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/LibCSymTable.cc' object='zktest_st-LibCSymTable.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-LibCSymTable.obj `if test -f 'tests/LibCSymTable.cc'; then $(CYGPATH_W) 'tests/LibCSymTable.cc'; else $(CYGPATH_W) '$(srcdir)/tests/LibCSymTable.cc'; fi`\n\nzktest_st-MocksBase.o: tests/MocksBase.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-MocksBase.o -MD -MP -MF $(DEPDIR)/zktest_st-MocksBase.Tpo -c -o zktest_st-MocksBase.o `test -f 'tests/MocksBase.cc' || echo '$(srcdir)/'`tests/MocksBase.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-MocksBase.Tpo $(DEPDIR)/zktest_st-MocksBase.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/MocksBase.cc' object='zktest_st-MocksBase.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-MocksBase.o `test -f 'tests/MocksBase.cc' || echo '$(srcdir)/'`tests/MocksBase.cc\n\nzktest_st-MocksBase.obj: tests/MocksBase.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-MocksBase.obj -MD -MP -MF $(DEPDIR)/zktest_st-MocksBase.Tpo -c -o zktest_st-MocksBase.obj `if test -f 'tests/MocksBase.cc'; then $(CYGPATH_W) 'tests/MocksBase.cc'; else $(CYGPATH_W) '$(srcdir)/tests/MocksBase.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-MocksBase.Tpo $(DEPDIR)/zktest_st-MocksBase.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/MocksBase.cc' object='zktest_st-MocksBase.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-MocksBase.obj `if test -f 'tests/MocksBase.cc'; then $(CYGPATH_W) 'tests/MocksBase.cc'; else $(CYGPATH_W) '$(srcdir)/tests/MocksBase.cc'; fi`\n\nzktest_st-ZKMocks.o: tests/ZKMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-ZKMocks.o -MD -MP -MF $(DEPDIR)/zktest_st-ZKMocks.Tpo -c -o zktest_st-ZKMocks.o `test -f 'tests/ZKMocks.cc' || echo '$(srcdir)/'`tests/ZKMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-ZKMocks.Tpo $(DEPDIR)/zktest_st-ZKMocks.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ZKMocks.cc' object='zktest_st-ZKMocks.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-ZKMocks.o `test -f 'tests/ZKMocks.cc' || echo '$(srcdir)/'`tests/ZKMocks.cc\n\nzktest_st-ZKMocks.obj: tests/ZKMocks.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-ZKMocks.obj -MD -MP -MF $(DEPDIR)/zktest_st-ZKMocks.Tpo -c -o zktest_st-ZKMocks.obj `if test -f 'tests/ZKMocks.cc'; then $(CYGPATH_W) 'tests/ZKMocks.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ZKMocks.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-ZKMocks.Tpo $(DEPDIR)/zktest_st-ZKMocks.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ZKMocks.cc' object='zktest_st-ZKMocks.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-ZKMocks.obj `if test -f 'tests/ZKMocks.cc'; then $(CYGPATH_W) 'tests/ZKMocks.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ZKMocks.cc'; fi`\n\nzktest_st-Util.o: tests/Util.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-Util.o -MD -MP -MF $(DEPDIR)/zktest_st-Util.Tpo -c -o zktest_st-Util.o `test -f 'tests/Util.cc' || echo '$(srcdir)/'`tests/Util.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-Util.Tpo $(DEPDIR)/zktest_st-Util.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/Util.cc' object='zktest_st-Util.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-Util.o `test -f 'tests/Util.cc' || echo '$(srcdir)/'`tests/Util.cc\n\nzktest_st-Util.obj: tests/Util.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-Util.obj -MD -MP -MF $(DEPDIR)/zktest_st-Util.Tpo -c -o zktest_st-Util.obj `if test -f 'tests/Util.cc'; then $(CYGPATH_W) 'tests/Util.cc'; else $(CYGPATH_W) '$(srcdir)/tests/Util.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-Util.Tpo $(DEPDIR)/zktest_st-Util.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/Util.cc' object='zktest_st-Util.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-Util.obj `if test -f 'tests/Util.cc'; then $(CYGPATH_W) 'tests/Util.cc'; else $(CYGPATH_W) '$(srcdir)/tests/Util.cc'; fi`\n\nzktest_st-ThreadingUtil.o: tests/ThreadingUtil.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-ThreadingUtil.o -MD -MP -MF $(DEPDIR)/zktest_st-ThreadingUtil.Tpo -c -o zktest_st-ThreadingUtil.o `test -f 'tests/ThreadingUtil.cc' || echo '$(srcdir)/'`tests/ThreadingUtil.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-ThreadingUtil.Tpo $(DEPDIR)/zktest_st-ThreadingUtil.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ThreadingUtil.cc' object='zktest_st-ThreadingUtil.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-ThreadingUtil.o `test -f 'tests/ThreadingUtil.cc' || echo '$(srcdir)/'`tests/ThreadingUtil.cc\n\nzktest_st-ThreadingUtil.obj: tests/ThreadingUtil.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-ThreadingUtil.obj -MD -MP -MF $(DEPDIR)/zktest_st-ThreadingUtil.Tpo -c -o zktest_st-ThreadingUtil.obj `if test -f 'tests/ThreadingUtil.cc'; then $(CYGPATH_W) 'tests/ThreadingUtil.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ThreadingUtil.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-ThreadingUtil.Tpo $(DEPDIR)/zktest_st-ThreadingUtil.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ThreadingUtil.cc' object='zktest_st-ThreadingUtil.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-ThreadingUtil.obj `if test -f 'tests/ThreadingUtil.cc'; then $(CYGPATH_W) 'tests/ThreadingUtil.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ThreadingUtil.cc'; fi`\n\nzktest_st-TestZookeeperInit.o: tests/TestZookeeperInit.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestZookeeperInit.o -MD -MP -MF $(DEPDIR)/zktest_st-TestZookeeperInit.Tpo -c -o zktest_st-TestZookeeperInit.o `test -f 'tests/TestZookeeperInit.cc' || echo '$(srcdir)/'`tests/TestZookeeperInit.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestZookeeperInit.Tpo $(DEPDIR)/zktest_st-TestZookeeperInit.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestZookeeperInit.cc' object='zktest_st-TestZookeeperInit.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestZookeeperInit.o `test -f 'tests/TestZookeeperInit.cc' || echo '$(srcdir)/'`tests/TestZookeeperInit.cc\n\nzktest_st-TestZookeeperInit.obj: tests/TestZookeeperInit.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestZookeeperInit.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestZookeeperInit.Tpo -c -o zktest_st-TestZookeeperInit.obj `if test -f 'tests/TestZookeeperInit.cc'; then $(CYGPATH_W) 'tests/TestZookeeperInit.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestZookeeperInit.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestZookeeperInit.Tpo $(DEPDIR)/zktest_st-TestZookeeperInit.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestZookeeperInit.cc' object='zktest_st-TestZookeeperInit.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestZookeeperInit.obj `if test -f 'tests/TestZookeeperInit.cc'; then $(CYGPATH_W) 'tests/TestZookeeperInit.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestZookeeperInit.cc'; fi`\n\nzktest_st-TestZookeeperClose.o: tests/TestZookeeperClose.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestZookeeperClose.o -MD -MP -MF $(DEPDIR)/zktest_st-TestZookeeperClose.Tpo -c -o zktest_st-TestZookeeperClose.o `test -f 'tests/TestZookeeperClose.cc' || echo '$(srcdir)/'`tests/TestZookeeperClose.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestZookeeperClose.Tpo $(DEPDIR)/zktest_st-TestZookeeperClose.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestZookeeperClose.cc' object='zktest_st-TestZookeeperClose.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestZookeeperClose.o `test -f 'tests/TestZookeeperClose.cc' || echo '$(srcdir)/'`tests/TestZookeeperClose.cc\n\nzktest_st-TestZookeeperClose.obj: tests/TestZookeeperClose.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestZookeeperClose.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestZookeeperClose.Tpo -c -o zktest_st-TestZookeeperClose.obj `if test -f 'tests/TestZookeeperClose.cc'; then $(CYGPATH_W) 'tests/TestZookeeperClose.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestZookeeperClose.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestZookeeperClose.Tpo $(DEPDIR)/zktest_st-TestZookeeperClose.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestZookeeperClose.cc' object='zktest_st-TestZookeeperClose.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestZookeeperClose.obj `if test -f 'tests/TestZookeeperClose.cc'; then $(CYGPATH_W) 'tests/TestZookeeperClose.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestZookeeperClose.cc'; fi`\n\nzktest_st-TestReconfig.o: tests/TestReconfig.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestReconfig.o -MD -MP -MF $(DEPDIR)/zktest_st-TestReconfig.Tpo -c -o zktest_st-TestReconfig.o `test -f 'tests/TestReconfig.cc' || echo '$(srcdir)/'`tests/TestReconfig.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestReconfig.Tpo $(DEPDIR)/zktest_st-TestReconfig.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReconfig.cc' object='zktest_st-TestReconfig.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestReconfig.o `test -f 'tests/TestReconfig.cc' || echo '$(srcdir)/'`tests/TestReconfig.cc\n\nzktest_st-TestReconfig.obj: tests/TestReconfig.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestReconfig.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestReconfig.Tpo -c -o zktest_st-TestReconfig.obj `if test -f 'tests/TestReconfig.cc'; then $(CYGPATH_W) 'tests/TestReconfig.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReconfig.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestReconfig.Tpo $(DEPDIR)/zktest_st-TestReconfig.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReconfig.cc' object='zktest_st-TestReconfig.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestReconfig.obj `if test -f 'tests/TestReconfig.cc'; then $(CYGPATH_W) 'tests/TestReconfig.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReconfig.cc'; fi`\n\nzktest_st-TestReconfigServer.o: tests/TestReconfigServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestReconfigServer.o -MD -MP -MF $(DEPDIR)/zktest_st-TestReconfigServer.Tpo -c -o zktest_st-TestReconfigServer.o `test -f 'tests/TestReconfigServer.cc' || echo '$(srcdir)/'`tests/TestReconfigServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestReconfigServer.Tpo $(DEPDIR)/zktest_st-TestReconfigServer.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReconfigServer.cc' object='zktest_st-TestReconfigServer.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestReconfigServer.o `test -f 'tests/TestReconfigServer.cc' || echo '$(srcdir)/'`tests/TestReconfigServer.cc\n\nzktest_st-TestReconfigServer.obj: tests/TestReconfigServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestReconfigServer.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestReconfigServer.Tpo -c -o zktest_st-TestReconfigServer.obj `if test -f 'tests/TestReconfigServer.cc'; then $(CYGPATH_W) 'tests/TestReconfigServer.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReconfigServer.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestReconfigServer.Tpo $(DEPDIR)/zktest_st-TestReconfigServer.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReconfigServer.cc' object='zktest_st-TestReconfigServer.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestReconfigServer.obj `if test -f 'tests/TestReconfigServer.cc'; then $(CYGPATH_W) 'tests/TestReconfigServer.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReconfigServer.cc'; fi`\n\nzktest_st-TestClientRetry.o: tests/TestClientRetry.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestClientRetry.o -MD -MP -MF $(DEPDIR)/zktest_st-TestClientRetry.Tpo -c -o zktest_st-TestClientRetry.o `test -f 'tests/TestClientRetry.cc' || echo '$(srcdir)/'`tests/TestClientRetry.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestClientRetry.Tpo $(DEPDIR)/zktest_st-TestClientRetry.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestClientRetry.cc' object='zktest_st-TestClientRetry.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestClientRetry.o `test -f 'tests/TestClientRetry.cc' || echo '$(srcdir)/'`tests/TestClientRetry.cc\n\nzktest_st-TestClientRetry.obj: tests/TestClientRetry.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestClientRetry.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestClientRetry.Tpo -c -o zktest_st-TestClientRetry.obj `if test -f 'tests/TestClientRetry.cc'; then $(CYGPATH_W) 'tests/TestClientRetry.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestClientRetry.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestClientRetry.Tpo $(DEPDIR)/zktest_st-TestClientRetry.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestClientRetry.cc' object='zktest_st-TestClientRetry.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestClientRetry.obj `if test -f 'tests/TestClientRetry.cc'; then $(CYGPATH_W) 'tests/TestClientRetry.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestClientRetry.cc'; fi`\n\nzktest_st-TestOperations.o: tests/TestOperations.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestOperations.o -MD -MP -MF $(DEPDIR)/zktest_st-TestOperations.Tpo -c -o zktest_st-TestOperations.o `test -f 'tests/TestOperations.cc' || echo '$(srcdir)/'`tests/TestOperations.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestOperations.Tpo $(DEPDIR)/zktest_st-TestOperations.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestOperations.cc' object='zktest_st-TestOperations.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestOperations.o `test -f 'tests/TestOperations.cc' || echo '$(srcdir)/'`tests/TestOperations.cc\n\nzktest_st-TestOperations.obj: tests/TestOperations.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestOperations.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestOperations.Tpo -c -o zktest_st-TestOperations.obj `if test -f 'tests/TestOperations.cc'; then $(CYGPATH_W) 'tests/TestOperations.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestOperations.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestOperations.Tpo $(DEPDIR)/zktest_st-TestOperations.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestOperations.cc' object='zktest_st-TestOperations.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestOperations.obj `if test -f 'tests/TestOperations.cc'; then $(CYGPATH_W) 'tests/TestOperations.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestOperations.cc'; fi`\n\nzktest_st-TestMulti.o: tests/TestMulti.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestMulti.o -MD -MP -MF $(DEPDIR)/zktest_st-TestMulti.Tpo -c -o zktest_st-TestMulti.o `test -f 'tests/TestMulti.cc' || echo '$(srcdir)/'`tests/TestMulti.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestMulti.Tpo $(DEPDIR)/zktest_st-TestMulti.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestMulti.cc' object='zktest_st-TestMulti.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestMulti.o `test -f 'tests/TestMulti.cc' || echo '$(srcdir)/'`tests/TestMulti.cc\n\nzktest_st-TestMulti.obj: tests/TestMulti.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestMulti.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestMulti.Tpo -c -o zktest_st-TestMulti.obj `if test -f 'tests/TestMulti.cc'; then $(CYGPATH_W) 'tests/TestMulti.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestMulti.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestMulti.Tpo $(DEPDIR)/zktest_st-TestMulti.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestMulti.cc' object='zktest_st-TestMulti.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestMulti.obj `if test -f 'tests/TestMulti.cc'; then $(CYGPATH_W) 'tests/TestMulti.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestMulti.cc'; fi`\n\nzktest_st-TestWatchers.o: tests/TestWatchers.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestWatchers.o -MD -MP -MF $(DEPDIR)/zktest_st-TestWatchers.Tpo -c -o zktest_st-TestWatchers.o `test -f 'tests/TestWatchers.cc' || echo '$(srcdir)/'`tests/TestWatchers.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestWatchers.Tpo $(DEPDIR)/zktest_st-TestWatchers.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestWatchers.cc' object='zktest_st-TestWatchers.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestWatchers.o `test -f 'tests/TestWatchers.cc' || echo '$(srcdir)/'`tests/TestWatchers.cc\n\nzktest_st-TestWatchers.obj: tests/TestWatchers.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestWatchers.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestWatchers.Tpo -c -o zktest_st-TestWatchers.obj `if test -f 'tests/TestWatchers.cc'; then $(CYGPATH_W) 'tests/TestWatchers.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestWatchers.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestWatchers.Tpo $(DEPDIR)/zktest_st-TestWatchers.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestWatchers.cc' object='zktest_st-TestWatchers.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestWatchers.obj `if test -f 'tests/TestWatchers.cc'; then $(CYGPATH_W) 'tests/TestWatchers.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestWatchers.cc'; fi`\n\nzktest_st-TestClient.o: tests/TestClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestClient.o -MD -MP -MF $(DEPDIR)/zktest_st-TestClient.Tpo -c -o zktest_st-TestClient.o `test -f 'tests/TestClient.cc' || echo '$(srcdir)/'`tests/TestClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestClient.Tpo $(DEPDIR)/zktest_st-TestClient.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestClient.cc' object='zktest_st-TestClient.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestClient.o `test -f 'tests/TestClient.cc' || echo '$(srcdir)/'`tests/TestClient.cc\n\nzktest_st-TestClient.obj: tests/TestClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestClient.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestClient.Tpo -c -o zktest_st-TestClient.obj `if test -f 'tests/TestClient.cc'; then $(CYGPATH_W) 'tests/TestClient.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestClient.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestClient.Tpo $(DEPDIR)/zktest_st-TestClient.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestClient.cc' object='zktest_st-TestClient.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestClient.obj `if test -f 'tests/TestClient.cc'; then $(CYGPATH_W) 'tests/TestClient.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestClient.cc'; fi`\n\nzktest_st-ZooKeeperQuorumServer.o: tests/ZooKeeperQuorumServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-ZooKeeperQuorumServer.o -MD -MP -MF $(DEPDIR)/zktest_st-ZooKeeperQuorumServer.Tpo -c -o zktest_st-ZooKeeperQuorumServer.o `test -f 'tests/ZooKeeperQuorumServer.cc' || echo '$(srcdir)/'`tests/ZooKeeperQuorumServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-ZooKeeperQuorumServer.Tpo $(DEPDIR)/zktest_st-ZooKeeperQuorumServer.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ZooKeeperQuorumServer.cc' object='zktest_st-ZooKeeperQuorumServer.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-ZooKeeperQuorumServer.o `test -f 'tests/ZooKeeperQuorumServer.cc' || echo '$(srcdir)/'`tests/ZooKeeperQuorumServer.cc\n\nzktest_st-ZooKeeperQuorumServer.obj: tests/ZooKeeperQuorumServer.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-ZooKeeperQuorumServer.obj -MD -MP -MF $(DEPDIR)/zktest_st-ZooKeeperQuorumServer.Tpo -c -o zktest_st-ZooKeeperQuorumServer.obj `if test -f 'tests/ZooKeeperQuorumServer.cc'; then $(CYGPATH_W) 'tests/ZooKeeperQuorumServer.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ZooKeeperQuorumServer.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-ZooKeeperQuorumServer.Tpo $(DEPDIR)/zktest_st-ZooKeeperQuorumServer.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/ZooKeeperQuorumServer.cc' object='zktest_st-ZooKeeperQuorumServer.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-ZooKeeperQuorumServer.obj `if test -f 'tests/ZooKeeperQuorumServer.cc'; then $(CYGPATH_W) 'tests/ZooKeeperQuorumServer.cc'; else $(CYGPATH_W) '$(srcdir)/tests/ZooKeeperQuorumServer.cc'; fi`\n\nzktest_st-TestReadOnlyClient.o: tests/TestReadOnlyClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestReadOnlyClient.o -MD -MP -MF $(DEPDIR)/zktest_st-TestReadOnlyClient.Tpo -c -o zktest_st-TestReadOnlyClient.o `test -f 'tests/TestReadOnlyClient.cc' || echo '$(srcdir)/'`tests/TestReadOnlyClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestReadOnlyClient.Tpo $(DEPDIR)/zktest_st-TestReadOnlyClient.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReadOnlyClient.cc' object='zktest_st-TestReadOnlyClient.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestReadOnlyClient.o `test -f 'tests/TestReadOnlyClient.cc' || echo '$(srcdir)/'`tests/TestReadOnlyClient.cc\n\nzktest_st-TestReadOnlyClient.obj: tests/TestReadOnlyClient.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestReadOnlyClient.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestReadOnlyClient.Tpo -c -o zktest_st-TestReadOnlyClient.obj `if test -f 'tests/TestReadOnlyClient.cc'; then $(CYGPATH_W) 'tests/TestReadOnlyClient.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReadOnlyClient.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestReadOnlyClient.Tpo $(DEPDIR)/zktest_st-TestReadOnlyClient.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestReadOnlyClient.cc' object='zktest_st-TestReadOnlyClient.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestReadOnlyClient.obj `if test -f 'tests/TestReadOnlyClient.cc'; then $(CYGPATH_W) 'tests/TestReadOnlyClient.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestReadOnlyClient.cc'; fi`\n\nzktest_st-TestLogClientEnv.o: tests/TestLogClientEnv.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestLogClientEnv.o -MD -MP -MF $(DEPDIR)/zktest_st-TestLogClientEnv.Tpo -c -o zktest_st-TestLogClientEnv.o `test -f 'tests/TestLogClientEnv.cc' || echo '$(srcdir)/'`tests/TestLogClientEnv.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestLogClientEnv.Tpo $(DEPDIR)/zktest_st-TestLogClientEnv.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestLogClientEnv.cc' object='zktest_st-TestLogClientEnv.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestLogClientEnv.o `test -f 'tests/TestLogClientEnv.cc' || echo '$(srcdir)/'`tests/TestLogClientEnv.cc\n\nzktest_st-TestLogClientEnv.obj: tests/TestLogClientEnv.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestLogClientEnv.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestLogClientEnv.Tpo -c -o zktest_st-TestLogClientEnv.obj `if test -f 'tests/TestLogClientEnv.cc'; then $(CYGPATH_W) 'tests/TestLogClientEnv.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestLogClientEnv.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestLogClientEnv.Tpo $(DEPDIR)/zktest_st-TestLogClientEnv.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestLogClientEnv.cc' object='zktest_st-TestLogClientEnv.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestLogClientEnv.obj `if test -f 'tests/TestLogClientEnv.cc'; then $(CYGPATH_W) 'tests/TestLogClientEnv.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestLogClientEnv.cc'; fi`\n\nzktest_st-TestSASLAuth.o: tests/TestSASLAuth.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestSASLAuth.o -MD -MP -MF $(DEPDIR)/zktest_st-TestSASLAuth.Tpo -c -o zktest_st-TestSASLAuth.o `test -f 'tests/TestSASLAuth.cc' || echo '$(srcdir)/'`tests/TestSASLAuth.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestSASLAuth.Tpo $(DEPDIR)/zktest_st-TestSASLAuth.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestSASLAuth.cc' object='zktest_st-TestSASLAuth.o' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestSASLAuth.o `test -f 'tests/TestSASLAuth.cc' || echo '$(srcdir)/'`tests/TestSASLAuth.cc\n\nzktest_st-TestSASLAuth.obj: tests/TestSASLAuth.cc\n@am__fastdepCXX_TRUE@\t$(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -MT zktest_st-TestSASLAuth.obj -MD -MP -MF $(DEPDIR)/zktest_st-TestSASLAuth.Tpo -c -o zktest_st-TestSASLAuth.obj `if test -f 'tests/TestSASLAuth.cc'; then $(CYGPATH_W) 'tests/TestSASLAuth.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestSASLAuth.cc'; fi`\n@am__fastdepCXX_TRUE@\t$(AM_V_at)$(am__mv) $(DEPDIR)/zktest_st-TestSASLAuth.Tpo $(DEPDIR)/zktest_st-TestSASLAuth.Po\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\t$(AM_V_CXX)source='tests/TestSASLAuth.cc' object='zktest_st-TestSASLAuth.obj' libtool=no @AMDEPBACKSLASH@\n@AMDEP_TRUE@@am__fastdepCXX_FALSE@\tDEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@\n@am__fastdepCXX_FALSE@\t$(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(zktest_st_CXXFLAGS) $(CXXFLAGS) -c -o zktest_st-TestSASLAuth.obj `if test -f 'tests/TestSASLAuth.cc'; then $(CYGPATH_W) 'tests/TestSASLAuth.cc'; else $(CYGPATH_W) '$(srcdir)/tests/TestSASLAuth.cc'; fi`\n\nmostlyclean-libtool:\n\t-rm -f *.lo\n\nclean-libtool:\n\t-rm -rf .libs _libs\n\ndistclean-libtool:\n\t-rm -f libtool config.lt\ninstall-pkgincludeHEADERS: $(pkginclude_HEADERS)\n\t@$(NORMAL_INSTALL)\n\t@list='$(pkginclude_HEADERS)'; test -n \"$(pkgincludedir)\" || list=; \\\n\tif test -n \"$$list\"; then \\\n\t  echo \" $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'\"; \\\n\t  $(MKDIR_P) \"$(DESTDIR)$(pkgincludedir)\" || exit 1; \\\n\tfi; \\\n\tfor p in $$list; do \\\n\t  if test -f \"$$p\"; then d=; else d=\"$(srcdir)/\"; fi; \\\n\t  echo \"$$d$$p\"; \\\n\tdone | $(am__base_list) | \\\n\twhile read files; do \\\n\t  echo \" $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'\"; \\\n\t  $(INSTALL_HEADER) $$files \"$(DESTDIR)$(pkgincludedir)\" || exit $$?; \\\n\tdone\n\nuninstall-pkgincludeHEADERS:\n\t@$(NORMAL_UNINSTALL)\n\t@list='$(pkginclude_HEADERS)'; test -n \"$(pkgincludedir)\" || list=; \\\n\tfiles=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \\\n\tdir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir)\n\nID: $(am__tagged_files)\n\t$(am__define_uniq_tagged_files); mkid -fID $$unique\ntags: tags-am\nTAGS: tags\n\ntags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)\n\tset x; \\\n\there=`pwd`; \\\n\t$(am__define_uniq_tagged_files); \\\n\tshift; \\\n\tif test -z \"$(ETAGS_ARGS)$$*$$unique\"; then :; else \\\n\t  test -n \"$$unique\" || unique=$$empty_fix; \\\n\t  if test $$# -gt 0; then \\\n\t    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \\\n\t      \"$$@\" $$unique; \\\n\t  else \\\n\t    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \\\n\t      $$unique; \\\n\t  fi; \\\n\tfi\nctags: ctags-am\n\nCTAGS: ctags\nctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)\n\t$(am__define_uniq_tagged_files); \\\n\ttest -z \"$(CTAGS_ARGS)$$unique\" \\\n\t  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \\\n\t     $$unique\n\nGTAGS:\n\there=`$(am__cd) $(top_builddir) && pwd` \\\n\t  && $(am__cd) $(top_srcdir) \\\n\t  && gtags -i $(GTAGS_ARGS) \"$$here\"\ncscope: cscope.files\n\ttest ! -s cscope.files \\\n\t  || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)\nclean-cscope:\n\t-rm -f cscope.files\ncscope.files: clean-cscope cscopelist\ncscopelist: cscopelist-am\n\ncscopelist-am: $(am__tagged_files)\n\tlist='$(am__tagged_files)'; \\\n\tcase \"$(srcdir)\" in \\\n\t  [\\\\/]* | ?:[\\\\/]*) sdir=\"$(srcdir)\" ;; \\\n\t  *) sdir=$(subdir)/$(srcdir) ;; \\\n\tesac; \\\n\tfor i in $$list; do \\\n\t  if test -f \"$$i\"; then \\\n\t    echo \"$(subdir)/$$i\"; \\\n\t  else \\\n\t    echo \"$$sdir/$$i\"; \\\n\t  fi; \\\n\tdone >> $(top_builddir)/cscope.files\n\ndistclean-tags:\n\t-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags\n\t-rm -f cscope.out cscope.in.out cscope.po.out cscope.files\n\ncheck-TESTS: $(TESTS)\n\t@failed=0; all=0; xfail=0; xpass=0; skip=0; \\\n\tsrcdir=$(srcdir); export srcdir; \\\n\tlist=' $(TESTS) '; \\\n\t$(am__tty_colors); \\\n\tif test -n \"$$list\"; then \\\n\t  for tst in $$list; do \\\n\t    if test -f ./$$tst; then dir=./; \\\n\t    elif test -f $$tst; then dir=; \\\n\t    else dir=\"$(srcdir)/\"; fi; \\\n\t    if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \\\n\t      all=`expr $$all + 1`; \\\n\t      case \" $(XFAIL_TESTS) \" in \\\n\t      *[\\ \\\t]$$tst[\\ \\\t]*) \\\n\t\txpass=`expr $$xpass + 1`; \\\n\t\tfailed=`expr $$failed + 1`; \\\n\t\tcol=$$red; res=XPASS; \\\n\t      ;; \\\n\t      *) \\\n\t\tcol=$$grn; res=PASS; \\\n\t      ;; \\\n\t      esac; \\\n\t    elif test $$? -ne 77; then \\\n\t      all=`expr $$all + 1`; \\\n\t      case \" $(XFAIL_TESTS) \" in \\\n\t      *[\\ \\\t]$$tst[\\ \\\t]*) \\\n\t\txfail=`expr $$xfail + 1`; \\\n\t\tcol=$$lgn; res=XFAIL; \\\n\t      ;; \\\n\t      *) \\\n\t\tfailed=`expr $$failed + 1`; \\\n\t\tcol=$$red; res=FAIL; \\\n\t      ;; \\\n\t      esac; \\\n\t    else \\\n\t      skip=`expr $$skip + 1`; \\\n\t      col=$$blu; res=SKIP; \\\n\t    fi; \\\n\t    echo \"$${col}$$res$${std}: $$tst\"; \\\n\t  done; \\\n\t  if test \"$$all\" -eq 1; then \\\n\t    tests=\"test\"; \\\n\t    All=\"\"; \\\n\t  else \\\n\t    tests=\"tests\"; \\\n\t    All=\"All \"; \\\n\t  fi; \\\n\t  if test \"$$failed\" -eq 0; then \\\n\t    if test \"$$xfail\" -eq 0; then \\\n\t      banner=\"$$All$$all $$tests passed\"; \\\n\t    else \\\n\t      if test \"$$xfail\" -eq 1; then failures=failure; else failures=failures; fi; \\\n\t      banner=\"$$All$$all $$tests behaved as expected ($$xfail expected $$failures)\"; \\\n\t    fi; \\\n\t  else \\\n\t    if test \"$$xpass\" -eq 0; then \\\n\t      banner=\"$$failed of $$all $$tests failed\"; \\\n\t    else \\\n\t      if test \"$$xpass\" -eq 1; then passes=pass; else passes=passes; fi; \\\n\t      banner=\"$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)\"; \\\n\t    fi; \\\n\t  fi; \\\n\t  dashes=\"$$banner\"; \\\n\t  skipped=\"\"; \\\n\t  if test \"$$skip\" -ne 0; then \\\n\t    if test \"$$skip\" -eq 1; then \\\n\t      skipped=\"($$skip test was not run)\"; \\\n\t    else \\\n\t      skipped=\"($$skip tests were not run)\"; \\\n\t    fi; \\\n\t    test `echo \"$$skipped\" | wc -c` -le `echo \"$$banner\" | wc -c` || \\\n\t      dashes=\"$$skipped\"; \\\n\t  fi; \\\n\t  report=\"\"; \\\n\t  if test \"$$failed\" -ne 0 && test -n \"$(PACKAGE_BUGREPORT)\"; then \\\n\t    report=\"Please report to $(PACKAGE_BUGREPORT)\"; \\\n\t    test `echo \"$$report\" | wc -c` -le `echo \"$$banner\" | wc -c` || \\\n\t      dashes=\"$$report\"; \\\n\t  fi; \\\n\t  dashes=`echo \"$$dashes\" | sed s/./=/g`; \\\n\t  if test \"$$failed\" -eq 0; then \\\n\t    col=\"$$grn\"; \\\n\t  else \\\n\t    col=\"$$red\"; \\\n\t  fi; \\\n\t  echo \"$${col}$$dashes$${std}\"; \\\n\t  echo \"$${col}$$banner$${std}\"; \\\n\t  test -z \"$$skipped\" || echo \"$${col}$$skipped$${std}\"; \\\n\t  test -z \"$$report\" || echo \"$${col}$$report$${std}\"; \\\n\t  echo \"$${col}$$dashes$${std}\"; \\\n\t  test \"$$failed\" -eq 0; \\\n\telse :; fi\n\ndistdir: $(DISTFILES)\n\t$(am__remove_distdir)\n\ttest -d \"$(distdir)\" || mkdir \"$(distdir)\"\n\t@srcdirstrip=`echo \"$(srcdir)\" | sed 's/[].[^$$\\\\*]/\\\\\\\\&/g'`; \\\n\ttopsrcdirstrip=`echo \"$(top_srcdir)\" | sed 's/[].[^$$\\\\*]/\\\\\\\\&/g'`; \\\n\tlist='$(DISTFILES)'; \\\n\t  dist_files=`for file in $$list; do echo $$file; done | \\\n\t  sed -e \"s|^$$srcdirstrip/||;t\" \\\n\t      -e \"s|^$$topsrcdirstrip/|$(top_builddir)/|;t\"`; \\\n\tcase $$dist_files in \\\n\t  */*) $(MKDIR_P) `echo \"$$dist_files\" | \\\n\t\t\t   sed '/\\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \\\n\t\t\t   sort -u` ;; \\\n\tesac; \\\n\tfor file in $$dist_files; do \\\n\t  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \\\n\t  if test -d $$d/$$file; then \\\n\t    dir=`echo \"/$$file\" | sed -e 's,/[^/]*$$,,'`; \\\n\t    if test -d \"$(distdir)/$$file\"; then \\\n\t      find \"$(distdir)/$$file\" -type d ! -perm -700 -exec chmod u+rwx {} \\;; \\\n\t    fi; \\\n\t    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \\\n\t      cp -fpR $(srcdir)/$$file \"$(distdir)$$dir\" || exit 1; \\\n\t      find \"$(distdir)/$$file\" -type d ! -perm -700 -exec chmod u+rwx {} \\;; \\\n\t    fi; \\\n\t    cp -fpR $$d/$$file \"$(distdir)$$dir\" || exit 1; \\\n\t  else \\\n\t    test -f \"$(distdir)/$$file\" \\\n\t    || cp -p $$d/$$file \"$(distdir)/$$file\" \\\n\t    || exit 1; \\\n\t  fi; \\\n\tdone\n\t-test -n \"$(am__skip_mode_fix)\" \\\n\t|| find \"$(distdir)\" -type d ! -perm -755 \\\n\t\t-exec chmod u+rwx,go+rx {} \\; -o \\\n\t  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \\; -o \\\n\t  ! -type d ! -perm -400 -exec chmod a+r {} \\; -o \\\n\t  ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \\; \\\n\t|| chmod -R a+r \"$(distdir)\"\ndist-gzip: distdir\n\ttardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz\n\t$(am__post_remove_distdir)\n\ndist-bzip2: distdir\n\ttardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2\n\t$(am__post_remove_distdir)\n\ndist-lzip: distdir\n\ttardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz\n\t$(am__post_remove_distdir)\n\ndist-xz: distdir\n\ttardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz\n\t$(am__post_remove_distdir)\n\ndist-tarZ: distdir\n\t@echo WARNING: \"Support for distribution archives compressed with\" \\\n\t\t       \"legacy program 'compress' is deprecated.\" >&2\n\t@echo WARNING: \"It will be removed altogether in Automake 2.0\" >&2\n\ttardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z\n\t$(am__post_remove_distdir)\n\ndist-shar: distdir\n\t@echo WARNING: \"Support for shar distribution archives is\" \\\n\t               \"deprecated.\" >&2\n\t@echo WARNING: \"It will be removed altogether in Automake 2.0\" >&2\n\tshar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz\n\t$(am__post_remove_distdir)\n\ndist-zip: distdir\n\t-rm -f $(distdir).zip\n\tzip -rq $(distdir).zip $(distdir)\n\t$(am__post_remove_distdir)\n\ndist dist-all:\n\t$(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'\n\t$(am__post_remove_distdir)\n\n# This target untars the dist file and tries a VPATH configuration.  Then\n# it guarantees that the distribution is self-contained by making another\n# tarfile.\ndistcheck: dist\n\tcase '$(DIST_ARCHIVES)' in \\\n\t*.tar.gz*) \\\n\t  GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\\\n\t*.tar.bz2*) \\\n\t  bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\\\n\t*.tar.lz*) \\\n\t  lzip -dc $(distdir).tar.lz | $(am__untar) ;;\\\n\t*.tar.xz*) \\\n\t  xz -dc $(distdir).tar.xz | $(am__untar) ;;\\\n\t*.tar.Z*) \\\n\t  uncompress -c $(distdir).tar.Z | $(am__untar) ;;\\\n\t*.shar.gz*) \\\n\t  GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\\\n\t*.zip*) \\\n\t  unzip $(distdir).zip ;;\\\n\tesac\n\tchmod -R a-w $(distdir)\n\tchmod u+w $(distdir)\n\tmkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst\n\tchmod a-w $(distdir)\n\ttest -d $(distdir)/_build || exit 0; \\\n\tdc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\\\/]:[\\\\/],/,'` \\\n\t  && dc_destdir=\"$${TMPDIR-/tmp}/am-dc-$$$$/\" \\\n\t  && am__cwd=`pwd` \\\n\t  && $(am__cd) $(distdir)/_build/sub \\\n\t  && ../../configure \\\n\t    $(AM_DISTCHECK_CONFIGURE_FLAGS) \\\n\t    $(DISTCHECK_CONFIGURE_FLAGS) \\\n\t    --srcdir=../.. --prefix=\"$$dc_install_base\" \\\n\t  && $(MAKE) $(AM_MAKEFLAGS) \\\n\t  && $(MAKE) $(AM_MAKEFLAGS) dvi \\\n\t  && $(MAKE) $(AM_MAKEFLAGS) check \\\n\t  && $(MAKE) $(AM_MAKEFLAGS) install \\\n\t  && $(MAKE) $(AM_MAKEFLAGS) installcheck \\\n\t  && $(MAKE) $(AM_MAKEFLAGS) uninstall \\\n\t  && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir=\"$$dc_install_base\" \\\n\t        distuninstallcheck \\\n\t  && chmod -R a-w \"$$dc_install_base\" \\\n\t  && ({ \\\n\t       (cd ../.. && umask 077 && mkdir \"$$dc_destdir\") \\\n\t       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR=\"$$dc_destdir\" install \\\n\t       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR=\"$$dc_destdir\" uninstall \\\n\t       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR=\"$$dc_destdir\" \\\n\t            distuninstallcheck_dir=\"$$dc_destdir\" distuninstallcheck; \\\n\t      } || { rm -rf \"$$dc_destdir\"; exit 1; }) \\\n\t  && rm -rf \"$$dc_destdir\" \\\n\t  && $(MAKE) $(AM_MAKEFLAGS) dist \\\n\t  && rm -rf $(DIST_ARCHIVES) \\\n\t  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \\\n\t  && cd \"$$am__cwd\" \\\n\t  || exit 1\n\t$(am__post_remove_distdir)\n\t@(echo \"$(distdir) archives ready for distribution: \"; \\\n\t  list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \\\n\t  sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'\ndistuninstallcheck:\n\t@test -n '$(distuninstallcheck_dir)' || { \\\n\t  echo 'ERROR: trying to run $@ with an empty' \\\n\t       '$$(distuninstallcheck_dir)' >&2; \\\n\t  exit 1; \\\n\t}; \\\n\t$(am__cd) '$(distuninstallcheck_dir)' || { \\\n\t  echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \\\n\t  exit 1; \\\n\t}; \\\n\ttest `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \\\n\t   || { echo \"ERROR: files left after uninstall:\" ; \\\n\t        if test -n \"$(DESTDIR)\"; then \\\n\t          echo \"  (check DESTDIR support)\"; \\\n\t        fi ; \\\n\t        $(distuninstallcheck_listfiles) ; \\\n\t        exit 1; } >&2\ndistcleancheck: distclean\n\t@if test '$(srcdir)' = . ; then \\\n\t  echo \"ERROR: distcleancheck can only run from a VPATH build\" ; \\\n\t  exit 1 ; \\\n\tfi\n\t@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \\\n\t  || { echo \"ERROR: files left in build directory after distclean:\" ; \\\n\t       $(distcleancheck_listfiles) ; \\\n\t       exit 1; } >&2\ncheck-am: all-am\n\t$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)\n\t$(MAKE) $(AM_MAKEFLAGS) check-TESTS\ncheck: check-am\nall-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) config.h\ninstall-binPROGRAMS: install-libLTLIBRARIES\n\ninstalldirs:\n\tfor dir in \"$(DESTDIR)$(libdir)\" \"$(DESTDIR)$(bindir)\" \"$(DESTDIR)$(pkgincludedir)\"; do \\\n\t  test -z \"$$dir\" || $(MKDIR_P) \"$$dir\"; \\\n\tdone\ninstall: install-am\ninstall-exec: install-exec-am\ninstall-data: install-data-am\nuninstall: uninstall-am\n\ninstall-am: all-am\n\t@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am\n\ninstallcheck: installcheck-am\ninstall-strip:\n\tif test -z '$(STRIP)'; then \\\n\t  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM=\"$(INSTALL_STRIP_PROGRAM)\" \\\n\t    install_sh_PROGRAM=\"$(INSTALL_STRIP_PROGRAM)\" INSTALL_STRIP_FLAG=-s \\\n\t      install; \\\n\telse \\\n\t  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM=\"$(INSTALL_STRIP_PROGRAM)\" \\\n\t    install_sh_PROGRAM=\"$(INSTALL_STRIP_PROGRAM)\" INSTALL_STRIP_FLAG=-s \\\n\t    \"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'\" install; \\\n\tfi\nmostlyclean-generic:\n\nclean-generic:\n\ndistclean-generic:\n\t-test -z \"$(CONFIG_CLEAN_FILES)\" || rm -f $(CONFIG_CLEAN_FILES)\n\t-test . = \"$(srcdir)\" || test -z \"$(CONFIG_CLEAN_VPATH_FILES)\" || rm -f $(CONFIG_CLEAN_VPATH_FILES)\n\nmaintainer-clean-generic:\n\t@echo \"This command is intended for maintainers to use\"\n\t@echo \"it deletes files that may require special tools to rebuild.\"\nclean: clean-am\n\nclean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \\\n\tclean-libLTLIBRARIES clean-libtool clean-local \\\n\tclean-noinstLTLIBRARIES mostlyclean-am\n\ndistclean: distclean-am\n\t-rm -f $(am__CONFIG_DISTCLEAN_FILES)\n\t-rm -rf ./$(DEPDIR)\n\t-rm -f Makefile\ndistclean-am: clean-am distclean-compile distclean-generic \\\n\tdistclean-hdr distclean-libtool distclean-tags\n\ndvi: dvi-am\n\ndvi-am:\n\nhtml: html-am\n\nhtml-am:\n\ninfo: info-am\n\ninfo-am:\n\ninstall-data-am: install-pkgincludeHEADERS\n\ninstall-dvi: install-dvi-am\n\ninstall-dvi-am:\n\ninstall-exec-am: install-binPROGRAMS install-libLTLIBRARIES\n\ninstall-html: install-html-am\n\ninstall-html-am:\n\ninstall-info: install-info-am\n\ninstall-info-am:\n\ninstall-man:\n\ninstall-pdf: install-pdf-am\n\ninstall-pdf-am:\n\ninstall-ps: install-ps-am\n\ninstall-ps-am:\n\ninstallcheck-am:\n\nmaintainer-clean: maintainer-clean-am\n\t-rm -f $(am__CONFIG_DISTCLEAN_FILES)\n\t-rm -rf $(top_srcdir)/autom4te.cache\n\t-rm -rf ./$(DEPDIR)\n\t-rm -f Makefile\nmaintainer-clean-am: distclean-am maintainer-clean-generic\n\nmostlyclean: mostlyclean-am\n\nmostlyclean-am: mostlyclean-compile mostlyclean-generic \\\n\tmostlyclean-libtool\n\npdf: pdf-am\n\npdf-am:\n\nps: ps-am\n\nps-am:\n\nuninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \\\n\tuninstall-pkgincludeHEADERS\n\n.MAKE: all check-am install-am install-strip\n\n.PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-TESTS \\\n\tcheck-am clean clean-binPROGRAMS clean-checkPROGRAMS \\\n\tclean-cscope clean-generic clean-libLTLIBRARIES clean-libtool \\\n\tclean-local clean-noinstLTLIBRARIES cscope cscopelist-am ctags \\\n\tctags-am dist dist-all dist-bzip2 dist-gzip dist-lzip \\\n\tdist-shar dist-tarZ dist-xz dist-zip distcheck distclean \\\n\tdistclean-compile distclean-generic distclean-hdr \\\n\tdistclean-libtool distclean-tags distcleancheck distdir \\\n\tdistuninstallcheck dvi dvi-am html html-am info info-am \\\n\tinstall install-am install-binPROGRAMS install-data \\\n\tinstall-data-am install-dvi install-dvi-am install-exec \\\n\tinstall-exec-am install-html install-html-am install-info \\\n\tinstall-info-am install-libLTLIBRARIES install-man install-pdf \\\n\tinstall-pdf-am install-pkgincludeHEADERS install-ps \\\n\tinstall-ps-am install-strip installcheck installcheck-am \\\n\tinstalldirs maintainer-clean maintainer-clean-generic \\\n\tmostlyclean mostlyclean-compile mostlyclean-generic \\\n\tmostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \\\n\tuninstall-am uninstall-binPROGRAMS uninstall-libLTLIBRARIES \\\n\tuninstall-pkgincludeHEADERS\n\n.PRECIOUS: Makefile\n\n\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps\n\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@@DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\tcd @DX_DOCDIR@/latex; \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\trm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\t$(DX_LATEX) refman.tex; \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\t$(MAKEINDEX_PATH) refman.idx; \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\t$(DX_LATEX) refman.tex; \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\tcountdown=5; \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\twhile $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\t                  refman.log > /dev/null 2>&1 \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\t   && test $$countdown -gt 0; do \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\t    $(DX_LATEX) refman.tex; \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\t    countdown=`expr $$countdown - 1`; \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\tdone; \\\n@DX_COND_doc_TRUE@@DX_COND_ps_TRUE@\t$(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi\n\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf\n\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@@DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\tcd @DX_DOCDIR@/latex; \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\trm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\t$(DX_PDFLATEX) refman.tex; \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\t$(DX_MAKEINDEX) refman.idx; \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\t$(DX_PDFLATEX) refman.tex; \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\tcountdown=5; \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\twhile $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\t                  refman.log > /dev/null 2>&1 \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\t   && test $$countdown -gt 0; do \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\t    $(DX_PDFLATEX) refman.tex; \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\t    countdown=`expr $$countdown - 1`; \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\tdone; \\\n@DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@\tmv refman.pdf ../@PACKAGE@.pdf\n\n@DX_COND_doc_TRUE@.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL)\n\n@DX_COND_doc_TRUE@.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)\n\n@DX_COND_doc_TRUE@doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag\n\n@DX_COND_doc_TRUE@doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)\n\n@DX_COND_doc_TRUE@@DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS)\n@DX_COND_doc_TRUE@\trm -rf @DX_DOCDIR@\n@DX_COND_doc_TRUE@\t$(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG)\n\nclean-local: clean-check\n\t$(RM) $(DX_CLEANFILES)\n\nclean-check:\n\t$(RM) $(nodist_zktest_st_OBJECTS) $(nodist_zktest_mt_OBJECTS)\n\n# Tell versions [3.59,3.63) of GNU make to not export all variables.\n# Otherwise a system limit (for SysV at least) may be exceeded.\n.NOEXPORT:\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/config.guess",
    "content": "#! /bin/sh\n# Attempt to guess a canonical system name.\n#   Copyright 1992-2016 Free Software Foundation, Inc.\n\ntimestamp='2016-10-02'\n\n# This file is free software; you can redistribute it and/or modify it\n# under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful, but\n# WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n# General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, see <http://www.gnu.org/licenses/>.\n#\n# As a special exception to the GNU General Public License, if you\n# distribute this file as part of a program that contains a\n# configuration script generated by Autoconf, you may include it under\n# the same distribution terms that you use for the rest of that\n# program.  This Exception is an additional permission under section 7\n# of the GNU General Public License, version 3 (\"GPLv3\").\n#\n# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.\n#\n# You can get the latest version of this script from:\n# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess\n#\n# Please send patches to <config-patches@gnu.org>.\n\n\nme=`echo \"$0\" | sed -e 's,.*/,,'`\n\nusage=\"\\\nUsage: $0 [OPTION]\n\nOutput the configuration name of the system \\`$me' is run on.\n\nOperation modes:\n  -h, --help         print this help, then exit\n  -t, --time-stamp   print date of last modification, then exit\n  -v, --version      print version number, then exit\n\nReport bugs and patches to <config-patches@gnu.org>.\"\n\nversion=\"\\\nGNU config.guess ($timestamp)\n\nOriginally written by Per Bothner.\nCopyright 1992-2016 Free Software Foundation, Inc.\n\nThis is free software; see the source for copying conditions.  There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\"\n\nhelp=\"\nTry \\`$me --help' for more information.\"\n\n# Parse command line\nwhile test $# -gt 0 ; do\n  case $1 in\n    --time-stamp | --time* | -t )\n       echo \"$timestamp\" ; exit ;;\n    --version | -v )\n       echo \"$version\" ; exit ;;\n    --help | --h* | -h )\n       echo \"$usage\"; exit ;;\n    -- )     # Stop option processing\n       shift; break ;;\n    - )\t# Use stdin as input.\n       break ;;\n    -* )\n       echo \"$me: invalid option $1$help\" >&2\n       exit 1 ;;\n    * )\n       break ;;\n  esac\ndone\n\nif test $# != 0; then\n  echo \"$me: too many arguments$help\" >&2\n  exit 1\nfi\n\ntrap 'exit 1' 1 2 15\n\n# CC_FOR_BUILD -- compiler used by this script. Note that the use of a\n# compiler to aid in system detection is discouraged as it requires\n# temporary files to be created and, as you can see below, it is a\n# headache to deal with in a portable fashion.\n\n# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still\n# use `HOST_CC' if defined, but it is deprecated.\n\n# Portable tmp directory creation inspired by the Autoconf team.\n\nset_cc_for_build='\ntrap \"exitcode=\\$?; (rm -f \\$tmpfiles 2>/dev/null; rmdir \\$tmp 2>/dev/null) && exit \\$exitcode\" 0 ;\ntrap \"rm -f \\$tmpfiles 2>/dev/null; rmdir \\$tmp 2>/dev/null; exit 1\" 1 2 13 15 ;\n: ${TMPDIR=/tmp} ;\n { tmp=`(umask 077 && mktemp -d \"$TMPDIR/cgXXXXXX\") 2>/dev/null` && test -n \"$tmp\" && test -d \"$tmp\" ; } ||\n { test -n \"$RANDOM\" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||\n { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo \"Warning: creating insecure temp directory\" >&2 ; } ||\n { echo \"$me: cannot create a temporary directory in $TMPDIR\" >&2 ; exit 1 ; } ;\ndummy=$tmp/dummy ;\ntmpfiles=\"$dummy.c $dummy.o $dummy.rel $dummy\" ;\ncase $CC_FOR_BUILD,$HOST_CC,$CC in\n ,,)    echo \"int x;\" > $dummy.c ;\n\tfor c in cc gcc c89 c99 ; do\n\t  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then\n\t     CC_FOR_BUILD=\"$c\"; break ;\n\t  fi ;\n\tdone ;\n\tif test x\"$CC_FOR_BUILD\" = x ; then\n\t  CC_FOR_BUILD=no_compiler_found ;\n\tfi\n\t;;\n ,,*)   CC_FOR_BUILD=$CC ;;\n ,*,*)  CC_FOR_BUILD=$HOST_CC ;;\nesac ; set_cc_for_build= ;'\n\n# This is needed to find uname on a Pyramid OSx when run in the BSD universe.\n# (ghazi@noc.rutgers.edu 1994-08-24)\nif (test -f /.attbin/uname) >/dev/null 2>&1 ; then\n\tPATH=$PATH:/.attbin ; export PATH\nfi\n\nUNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown\nUNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown\nUNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown\nUNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown\n\ncase \"${UNAME_SYSTEM}\" in\nLinux|GNU|GNU/*)\n\t# If the system lacks a compiler, then just pick glibc.\n\t# We could probably try harder.\n\tLIBC=gnu\n\n\teval $set_cc_for_build\n\tcat <<-EOF > $dummy.c\n\t#include <features.h>\n\t#if defined(__UCLIBC__)\n\tLIBC=uclibc\n\t#elif defined(__dietlibc__)\n\tLIBC=dietlibc\n\t#else\n\tLIBC=gnu\n\t#endif\n\tEOF\n\teval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`\n\t;;\nesac\n\n# Note: order is significant - the case branches are not exclusive.\n\ncase \"${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}\" in\n    *:NetBSD:*:*)\n\t# NetBSD (nbsd) targets should (where applicable) match one or\n\t# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,\n\t# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently\n\t# switched to ELF, *-*-netbsd* would select the old\n\t# object file format.  This provides both forward\n\t# compatibility and a consistent mechanism for selecting the\n\t# object file format.\n\t#\n\t# Note: NetBSD doesn't particularly care about the vendor\n\t# portion of the name.  We always set it to \"unknown\".\n\tsysctl=\"sysctl -n hw.machine_arch\"\n\tUNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \\\n\t    /sbin/$sysctl 2>/dev/null || \\\n\t    /usr/sbin/$sysctl 2>/dev/null || \\\n\t    echo unknown)`\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    armeb) machine=armeb-unknown ;;\n\t    arm*) machine=arm-unknown ;;\n\t    sh3el) machine=shl-unknown ;;\n\t    sh3eb) machine=sh-unknown ;;\n\t    sh5el) machine=sh5le-unknown ;;\n\t    earmv*)\n\t\tarch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\\(armv[0-9]\\).*$,\\1,'`\n\t\tendian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\\(eb\\)$,\\1,p'`\n\t\tmachine=${arch}${endian}-unknown\n\t\t;;\n\t    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;\n\tesac\n\t# The Operating System including object format, if it has switched\n\t# to ELF recently (or will in the future) and ABI.\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    earm*)\n\t\tos=netbsdelf\n\t\t;;\n\t    arm*|i386|m68k|ns32k|sh3*|sparc|vax)\n\t\teval $set_cc_for_build\n\t\tif echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \\\n\t\t\t| grep -q __ELF__\n\t\tthen\n\t\t    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).\n\t\t    # Return netbsd for either.  FIX?\n\t\t    os=netbsd\n\t\telse\n\t\t    os=netbsdelf\n\t\tfi\n\t\t;;\n\t    *)\n\t\tos=netbsd\n\t\t;;\n\tesac\n\t# Determine ABI tags.\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    earm*)\n\t\texpr='s/^earmv[0-9]/-eabi/;s/eb$//'\n\t\tabi=`echo ${UNAME_MACHINE_ARCH} | sed -e \"$expr\"`\n\t\t;;\n\tesac\n\t# The OS release\n\t# Debian GNU/NetBSD machines have a different userland, and\n\t# thus, need a distinct triplet. However, they do not need\n\t# kernel version information, so it can be replaced with a\n\t# suitable tag, in the style of linux-gnu.\n\tcase \"${UNAME_VERSION}\" in\n\t    Debian*)\n\t\trelease='-gnu'\n\t\t;;\n\t    *)\n\t\trelease=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2`\n\t\t;;\n\tesac\n\t# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:\n\t# contains redundant information, the shorter form:\n\t# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.\n\techo \"${machine}-${os}${release}${abi}\"\n\texit ;;\n    *:Bitrig:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}\n\texit ;;\n    *:OpenBSD:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}\n\texit ;;\n    *:LibertyBSD:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\\.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE}\n\texit ;;\n    *:ekkoBSD:*:*)\n\techo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}\n\texit ;;\n    *:SolidBSD:*:*)\n\techo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}\n\texit ;;\n    macppc:MirBSD:*:*)\n\techo powerpc-unknown-mirbsd${UNAME_RELEASE}\n\texit ;;\n    *:MirBSD:*:*)\n\techo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}\n\texit ;;\n    *:Sortix:*:*)\n\techo ${UNAME_MACHINE}-unknown-sortix\n\texit ;;\n    alpha:OSF1:*:*)\n\tcase $UNAME_RELEASE in\n\t*4.0)\n\t\tUNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`\n\t\t;;\n\t*5.*)\n\t\tUNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`\n\t\t;;\n\tesac\n\t# According to Compaq, /usr/sbin/psrinfo has been available on\n\t# OSF/1 and Tru64 systems produced since 1995.  I hope that\n\t# covers most systems running today.  This code pipes the CPU\n\t# types through head -n 1, so we only detect the type of CPU 0.\n\tALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \\(.*\\) processor.*$/\\1/p' | head -n 1`\n\tcase \"$ALPHA_CPU_TYPE\" in\n\t    \"EV4 (21064)\")\n\t\tUNAME_MACHINE=alpha ;;\n\t    \"EV4.5 (21064)\")\n\t\tUNAME_MACHINE=alpha ;;\n\t    \"LCA4 (21066/21068)\")\n\t\tUNAME_MACHINE=alpha ;;\n\t    \"EV5 (21164)\")\n\t\tUNAME_MACHINE=alphaev5 ;;\n\t    \"EV5.6 (21164A)\")\n\t\tUNAME_MACHINE=alphaev56 ;;\n\t    \"EV5.6 (21164PC)\")\n\t\tUNAME_MACHINE=alphapca56 ;;\n\t    \"EV5.7 (21164PC)\")\n\t\tUNAME_MACHINE=alphapca57 ;;\n\t    \"EV6 (21264)\")\n\t\tUNAME_MACHINE=alphaev6 ;;\n\t    \"EV6.7 (21264A)\")\n\t\tUNAME_MACHINE=alphaev67 ;;\n\t    \"EV6.8CB (21264C)\")\n\t\tUNAME_MACHINE=alphaev68 ;;\n\t    \"EV6.8AL (21264B)\")\n\t\tUNAME_MACHINE=alphaev68 ;;\n\t    \"EV6.8CX (21264D)\")\n\t\tUNAME_MACHINE=alphaev68 ;;\n\t    \"EV6.9A (21264/EV69A)\")\n\t\tUNAME_MACHINE=alphaev69 ;;\n\t    \"EV7 (21364)\")\n\t\tUNAME_MACHINE=alphaev7 ;;\n\t    \"EV7.9 (21364A)\")\n\t\tUNAME_MACHINE=alphaev79 ;;\n\tesac\n\t# A Pn.n version is a patched version.\n\t# A Vn.n version is a released version.\n\t# A Tn.n version is a released field test version.\n\t# A Xn.n version is an unreleased experimental baselevel.\n\t# 1.2 uses \"1.2\" for uname -r.\n\techo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`\n\t# Reset EXIT trap before exiting to avoid spurious non-zero exit code.\n\texitcode=$?\n\ttrap '' 0\n\texit $exitcode ;;\n    Alpha\\ *:Windows_NT*:*)\n\t# How do we know it's Interix rather than the generic POSIX subsystem?\n\t# Should we change UNAME_MACHINE based on the output of uname instead\n\t# of the specific Alpha model?\n\techo alpha-pc-interix\n\texit ;;\n    21064:Windows_NT:50:3)\n\techo alpha-dec-winnt3.5\n\texit ;;\n    Amiga*:UNIX_System_V:4.0:*)\n\techo m68k-unknown-sysv4\n\texit ;;\n    *:[Aa]miga[Oo][Ss]:*:*)\n\techo ${UNAME_MACHINE}-unknown-amigaos\n\texit ;;\n    *:[Mm]orph[Oo][Ss]:*:*)\n\techo ${UNAME_MACHINE}-unknown-morphos\n\texit ;;\n    *:OS/390:*:*)\n\techo i370-ibm-openedition\n\texit ;;\n    *:z/VM:*:*)\n\techo s390-ibm-zvmoe\n\texit ;;\n    *:OS400:*:*)\n\techo powerpc-ibm-os400\n\texit ;;\n    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)\n\techo arm-acorn-riscix${UNAME_RELEASE}\n\texit ;;\n    arm*:riscos:*:*|arm*:RISCOS:*:*)\n\techo arm-unknown-riscos\n\texit ;;\n    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)\n\techo hppa1.1-hitachi-hiuxmpp\n\texit ;;\n    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)\n\t# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.\n\tif test \"`(/bin/universe) 2>/dev/null`\" = att ; then\n\t\techo pyramid-pyramid-sysv3\n\telse\n\t\techo pyramid-pyramid-bsd\n\tfi\n\texit ;;\n    NILE*:*:*:dcosx)\n\techo pyramid-pyramid-svr4\n\texit ;;\n    DRS?6000:unix:4.0:6*)\n\techo sparc-icl-nx6\n\texit ;;\n    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)\n\tcase `/usr/bin/uname -p` in\n\t    sparc) echo sparc-icl-nx7; exit ;;\n\tesac ;;\n    s390x:SunOS:*:*)\n\techo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4H:SunOS:5.*:*)\n\techo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)\n\techo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)\n\techo i386-pc-auroraux${UNAME_RELEASE}\n\texit ;;\n    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)\n\teval $set_cc_for_build\n\tSUN_ARCH=i386\n\t# If there is a compiler, see if it is configured for 64-bit objects.\n\t# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.\n\t# This test works for both compilers.\n\tif [ \"$CC_FOR_BUILD\" != no_compiler_found ]; then\n\t    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \\\n\t\t(CCOPTS=\"\" $CC_FOR_BUILD -E - 2>/dev/null) | \\\n\t\tgrep IS_64BIT_ARCH >/dev/null\n\t    then\n\t\tSUN_ARCH=x86_64\n\t    fi\n\tfi\n\techo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4*:SunOS:6*:*)\n\t# According to config.sub, this is the proper way to canonicalize\n\t# SunOS6.  Hard to guess exactly what SunOS6 will be like, but\n\t# it's likely to be more like Solaris than SunOS4.\n\techo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4*:SunOS:*:*)\n\tcase \"`/usr/bin/arch -k`\" in\n\t    Series*|S4*)\n\t\tUNAME_RELEASE=`uname -v`\n\t\t;;\n\tesac\n\t# Japanese Language versions have a version number like `4.1.3-JL'.\n\techo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`\n\texit ;;\n    sun3*:SunOS:*:*)\n\techo m68k-sun-sunos${UNAME_RELEASE}\n\texit ;;\n    sun*:*:4.2BSD:*)\n\tUNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`\n\ttest \"x${UNAME_RELEASE}\" = x && UNAME_RELEASE=3\n\tcase \"`/bin/arch`\" in\n\t    sun3)\n\t\techo m68k-sun-sunos${UNAME_RELEASE}\n\t\t;;\n\t    sun4)\n\t\techo sparc-sun-sunos${UNAME_RELEASE}\n\t\t;;\n\tesac\n\texit ;;\n    aushp:SunOS:*:*)\n\techo sparc-auspex-sunos${UNAME_RELEASE}\n\texit ;;\n    # The situation for MiNT is a little confusing.  The machine name\n    # can be virtually everything (everything which is not\n    # \"atarist\" or \"atariste\" at least should have a processor\n    # > m68000).  The system name ranges from \"MiNT\" over \"FreeMiNT\"\n    # to the lowercase version \"mint\" (or \"freemint\").  Finally\n    # the system name \"TOS\" denotes a system which is actually not\n    # MiNT.  But MiNT is downward compatible to TOS, so this should\n    # be no problem.\n    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)\n\techo m68k-atari-mint${UNAME_RELEASE}\n\texit ;;\n    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)\n\techo m68k-atari-mint${UNAME_RELEASE}\n\texit ;;\n    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)\n\techo m68k-atari-mint${UNAME_RELEASE}\n\texit ;;\n    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)\n\techo m68k-milan-mint${UNAME_RELEASE}\n\texit ;;\n    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)\n\techo m68k-hades-mint${UNAME_RELEASE}\n\texit ;;\n    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)\n\techo m68k-unknown-mint${UNAME_RELEASE}\n\texit ;;\n    m68k:machten:*:*)\n\techo m68k-apple-machten${UNAME_RELEASE}\n\texit ;;\n    powerpc:machten:*:*)\n\techo powerpc-apple-machten${UNAME_RELEASE}\n\texit ;;\n    RISC*:Mach:*:*)\n\techo mips-dec-mach_bsd4.3\n\texit ;;\n    RISC*:ULTRIX:*:*)\n\techo mips-dec-ultrix${UNAME_RELEASE}\n\texit ;;\n    VAX*:ULTRIX*:*:*)\n\techo vax-dec-ultrix${UNAME_RELEASE}\n\texit ;;\n    2020:CLIX:*:* | 2430:CLIX:*:*)\n\techo clipper-intergraph-clix${UNAME_RELEASE}\n\texit ;;\n    mips:*:*:UMIPS | mips:*:*:RISCos)\n\teval $set_cc_for_build\n\tsed 's/^\t//' << EOF >$dummy.c\n#ifdef __cplusplus\n#include <stdio.h>  /* for printf() prototype */\n\tint main (int argc, char *argv[]) {\n#else\n\tint main (argc, argv) int argc; char *argv[]; {\n#endif\n\t#if defined (host_mips) && defined (MIPSEB)\n\t#if defined (SYSTYPE_SYSV)\n\t  printf (\"mips-mips-riscos%ssysv\\n\", argv[1]); exit (0);\n\t#endif\n\t#if defined (SYSTYPE_SVR4)\n\t  printf (\"mips-mips-riscos%ssvr4\\n\", argv[1]); exit (0);\n\t#endif\n\t#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)\n\t  printf (\"mips-mips-riscos%sbsd\\n\", argv[1]); exit (0);\n\t#endif\n\t#endif\n\t  exit (-1);\n\t}\nEOF\n\t$CC_FOR_BUILD -o $dummy $dummy.c &&\n\t  dummyarg=`echo \"${UNAME_RELEASE}\" | sed -n 's/\\([0-9]*\\).*/\\1/p'` &&\n\t  SYSTEM_NAME=`$dummy $dummyarg` &&\n\t    { echo \"$SYSTEM_NAME\"; exit; }\n\techo mips-mips-riscos${UNAME_RELEASE}\n\texit ;;\n    Motorola:PowerMAX_OS:*:*)\n\techo powerpc-motorola-powermax\n\texit ;;\n    Motorola:*:4.3:PL8-*)\n\techo powerpc-harris-powermax\n\texit ;;\n    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)\n\techo powerpc-harris-powermax\n\texit ;;\n    Night_Hawk:Power_UNIX:*:*)\n\techo powerpc-harris-powerunix\n\texit ;;\n    m88k:CX/UX:7*:*)\n\techo m88k-harris-cxux7\n\texit ;;\n    m88k:*:4*:R4*)\n\techo m88k-motorola-sysv4\n\texit ;;\n    m88k:*:3*:R3*)\n\techo m88k-motorola-sysv3\n\texit ;;\n    AViiON:dgux:*:*)\n\t# DG/UX returns AViiON for all architectures\n\tUNAME_PROCESSOR=`/usr/bin/uname -p`\n\tif [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]\n\tthen\n\t    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \\\n\t       [ ${TARGET_BINARY_INTERFACE}x = x ]\n\t    then\n\t\techo m88k-dg-dgux${UNAME_RELEASE}\n\t    else\n\t\techo m88k-dg-dguxbcs${UNAME_RELEASE}\n\t    fi\n\telse\n\t    echo i586-dg-dgux${UNAME_RELEASE}\n\tfi\n\texit ;;\n    M88*:DolphinOS:*:*)\t# DolphinOS (SVR3)\n\techo m88k-dolphin-sysv3\n\texit ;;\n    M88*:*:R3*:*)\n\t# Delta 88k system running SVR3\n\techo m88k-motorola-sysv3\n\texit ;;\n    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)\n\techo m88k-tektronix-sysv3\n\texit ;;\n    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)\n\techo m68k-tektronix-bsd\n\texit ;;\n    *:IRIX*:*:*)\n\techo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`\n\texit ;;\n    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.\n\techo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id\n\texit ;;               # Note that: echo \"'`uname -s`'\" gives 'AIX '\n    i*86:AIX:*:*)\n\techo i386-ibm-aix\n\texit ;;\n    ia64:AIX:*:*)\n\tif [ -x /usr/bin/oslevel ] ; then\n\t\tIBM_REV=`/usr/bin/oslevel`\n\telse\n\t\tIBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}\n\tfi\n\techo ${UNAME_MACHINE}-ibm-aix${IBM_REV}\n\texit ;;\n    *:AIX:2:3)\n\tif grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then\n\t\teval $set_cc_for_build\n\t\tsed 's/^\t\t//' << EOF >$dummy.c\n\t\t#include <sys/systemcfg.h>\n\n\t\tmain()\n\t\t\t{\n\t\t\tif (!__power_pc())\n\t\t\t\texit(1);\n\t\t\tputs(\"powerpc-ibm-aix3.2.5\");\n\t\t\texit(0);\n\t\t\t}\nEOF\n\t\tif $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`\n\t\tthen\n\t\t\techo \"$SYSTEM_NAME\"\n\t\telse\n\t\t\techo rs6000-ibm-aix3.2.5\n\t\tfi\n\telif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then\n\t\techo rs6000-ibm-aix3.2.4\n\telse\n\t\techo rs6000-ibm-aix3.2\n\tfi\n\texit ;;\n    *:AIX:*:[4567])\n\tIBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`\n\tif /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then\n\t\tIBM_ARCH=rs6000\n\telse\n\t\tIBM_ARCH=powerpc\n\tfi\n\tif [ -x /usr/bin/lslpp ] ; then\n\t\tIBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |\n\t\t\t   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`\n\telse\n\t\tIBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}\n\tfi\n\techo ${IBM_ARCH}-ibm-aix${IBM_REV}\n\texit ;;\n    *:AIX:*:*)\n\techo rs6000-ibm-aix\n\texit ;;\n    ibmrt:4.4BSD:*|romp-ibm:BSD:*)\n\techo romp-ibm-bsd4.4\n\texit ;;\n    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and\n\techo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to\n\texit ;;                             # report: romp-ibm BSD 4.3\n    *:BOSX:*:*)\n\techo rs6000-bull-bosx\n\texit ;;\n    DPX/2?00:B.O.S.:*:*)\n\techo m68k-bull-sysv3\n\texit ;;\n    9000/[34]??:4.3bsd:1.*:*)\n\techo m68k-hp-bsd\n\texit ;;\n    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)\n\techo m68k-hp-bsd4.4\n\texit ;;\n    9000/[34678]??:HP-UX:*:*)\n\tHPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`\n\tcase \"${UNAME_MACHINE}\" in\n\t    9000/31? )            HP_ARCH=m68000 ;;\n\t    9000/[34]?? )         HP_ARCH=m68k ;;\n\t    9000/[678][0-9][0-9])\n\t\tif [ -x /usr/bin/getconf ]; then\n\t\t    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`\n\t\t    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`\n\t\t    case \"${sc_cpu_version}\" in\n\t\t      523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0\n\t\t      528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1\n\t\t      532)                      # CPU_PA_RISC2_0\n\t\t\tcase \"${sc_kernel_bits}\" in\n\t\t\t  32) HP_ARCH=hppa2.0n ;;\n\t\t\t  64) HP_ARCH=hppa2.0w ;;\n\t\t\t  '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20\n\t\t\tesac ;;\n\t\t    esac\n\t\tfi\n\t\tif [ \"${HP_ARCH}\" = \"\" ]; then\n\t\t    eval $set_cc_for_build\n\t\t    sed 's/^\t\t//' << EOF >$dummy.c\n\n\t\t#define _HPUX_SOURCE\n\t\t#include <stdlib.h>\n\t\t#include <unistd.h>\n\n\t\tint main ()\n\t\t{\n\t\t#if defined(_SC_KERNEL_BITS)\n\t\t    long bits = sysconf(_SC_KERNEL_BITS);\n\t\t#endif\n\t\t    long cpu  = sysconf (_SC_CPU_VERSION);\n\n\t\t    switch (cpu)\n\t\t\t{\n\t\t\tcase CPU_PA_RISC1_0: puts (\"hppa1.0\"); break;\n\t\t\tcase CPU_PA_RISC1_1: puts (\"hppa1.1\"); break;\n\t\t\tcase CPU_PA_RISC2_0:\n\t\t#if defined(_SC_KERNEL_BITS)\n\t\t\t    switch (bits)\n\t\t\t\t{\n\t\t\t\tcase 64: puts (\"hppa2.0w\"); break;\n\t\t\t\tcase 32: puts (\"hppa2.0n\"); break;\n\t\t\t\tdefault: puts (\"hppa2.0\"); break;\n\t\t\t\t} break;\n\t\t#else  /* !defined(_SC_KERNEL_BITS) */\n\t\t\t    puts (\"hppa2.0\"); break;\n\t\t#endif\n\t\t\tdefault: puts (\"hppa1.0\"); break;\n\t\t\t}\n\t\t    exit (0);\n\t\t}\nEOF\n\t\t    (CCOPTS=\"\" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`\n\t\t    test -z \"$HP_ARCH\" && HP_ARCH=hppa\n\t\tfi ;;\n\tesac\n\tif [ ${HP_ARCH} = hppa2.0w ]\n\tthen\n\t    eval $set_cc_for_build\n\n\t    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating\n\t    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler\n\t    # generating 64-bit code.  GNU and HP use different nomenclature:\n\t    #\n\t    # $ CC_FOR_BUILD=cc ./config.guess\n\t    # => hppa2.0w-hp-hpux11.23\n\t    # $ CC_FOR_BUILD=\"cc +DA2.0w\" ./config.guess\n\t    # => hppa64-hp-hpux11.23\n\n\t    if echo __LP64__ | (CCOPTS=\"\" $CC_FOR_BUILD -E - 2>/dev/null) |\n\t\tgrep -q __LP64__\n\t    then\n\t\tHP_ARCH=hppa2.0w\n\t    else\n\t\tHP_ARCH=hppa64\n\t    fi\n\tfi\n\techo ${HP_ARCH}-hp-hpux${HPUX_REV}\n\texit ;;\n    ia64:HP-UX:*:*)\n\tHPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`\n\techo ia64-hp-hpux${HPUX_REV}\n\texit ;;\n    3050*:HI-UX:*:*)\n\teval $set_cc_for_build\n\tsed 's/^\t//' << EOF >$dummy.c\n\t#include <unistd.h>\n\tint\n\tmain ()\n\t{\n\t  long cpu = sysconf (_SC_CPU_VERSION);\n\t  /* The order matters, because CPU_IS_HP_MC68K erroneously returns\n\t     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct\n\t     results, however.  */\n\t  if (CPU_IS_PA_RISC (cpu))\n\t    {\n\t      switch (cpu)\n\t\t{\n\t\t  case CPU_PA_RISC1_0: puts (\"hppa1.0-hitachi-hiuxwe2\"); break;\n\t\t  case CPU_PA_RISC1_1: puts (\"hppa1.1-hitachi-hiuxwe2\"); break;\n\t\t  case CPU_PA_RISC2_0: puts (\"hppa2.0-hitachi-hiuxwe2\"); break;\n\t\t  default: puts (\"hppa-hitachi-hiuxwe2\"); break;\n\t\t}\n\t    }\n\t  else if (CPU_IS_HP_MC68K (cpu))\n\t    puts (\"m68k-hitachi-hiuxwe2\");\n\t  else puts (\"unknown-hitachi-hiuxwe2\");\n\t  exit (0);\n\t}\nEOF\n\t$CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&\n\t\t{ echo \"$SYSTEM_NAME\"; exit; }\n\techo unknown-hitachi-hiuxwe2\n\texit ;;\n    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )\n\techo hppa1.1-hp-bsd\n\texit ;;\n    9000/8??:4.3bsd:*:*)\n\techo hppa1.0-hp-bsd\n\texit ;;\n    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)\n\techo hppa1.0-hp-mpeix\n\texit ;;\n    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )\n\techo hppa1.1-hp-osf\n\texit ;;\n    hp8??:OSF1:*:*)\n\techo hppa1.0-hp-osf\n\texit ;;\n    i*86:OSF1:*:*)\n\tif [ -x /usr/sbin/sysversion ] ; then\n\t    echo ${UNAME_MACHINE}-unknown-osf1mk\n\telse\n\t    echo ${UNAME_MACHINE}-unknown-osf1\n\tfi\n\texit ;;\n    parisc*:Lites*:*:*)\n\techo hppa1.1-hp-lites\n\texit ;;\n    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)\n\techo c1-convex-bsd\n\texit ;;\n    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)\n\tif getsysinfo -f scalar_acc\n\tthen echo c32-convex-bsd\n\telse echo c2-convex-bsd\n\tfi\n\texit ;;\n    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)\n\techo c34-convex-bsd\n\texit ;;\n    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)\n\techo c38-convex-bsd\n\texit ;;\n    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)\n\techo c4-convex-bsd\n\texit ;;\n    CRAY*Y-MP:*:*:*)\n\techo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*[A-Z]90:*:*:*)\n\techo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \\\n\t| sed -e 's/CRAY.*\\([A-Z]90\\)/\\1/' \\\n\t      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \\\n\t      -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*TS:*:*:*)\n\techo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*T3E:*:*:*)\n\techo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*SV1:*:*:*)\n\techo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    *:UNICOS/mp:*:*)\n\techo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)\n\tFUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`\n\tFUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\\///'`\n\tFUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`\n\techo \"${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}\"\n\texit ;;\n    5000:UNIX_System_V:4.*:*)\n\tFUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\\///'`\n\tFUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`\n\techo \"sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}\"\n\texit ;;\n    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\\ Embedded/OS:*:*)\n\techo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}\n\texit ;;\n    sparc*:BSD/OS:*:*)\n\techo sparc-unknown-bsdi${UNAME_RELEASE}\n\texit ;;\n    *:BSD/OS:*:*)\n\techo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}\n\texit ;;\n    *:FreeBSD:*:*)\n\tUNAME_PROCESSOR=`/usr/bin/uname -p`\n\tcase ${UNAME_PROCESSOR} in\n\t    amd64)\n\t\techo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;\n\t    *)\n\t\techo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;\n\tesac\n\texit ;;\n    i*:CYGWIN*:*)\n\techo ${UNAME_MACHINE}-pc-cygwin\n\texit ;;\n    *:MINGW64*:*)\n\techo ${UNAME_MACHINE}-pc-mingw64\n\texit ;;\n    *:MINGW*:*)\n\techo ${UNAME_MACHINE}-pc-mingw32\n\texit ;;\n    *:MSYS*:*)\n\techo ${UNAME_MACHINE}-pc-msys\n\texit ;;\n    i*:windows32*:*)\n\t# uname -m includes \"-pc\" on this system.\n\techo ${UNAME_MACHINE}-mingw32\n\texit ;;\n    i*:PW*:*)\n\techo ${UNAME_MACHINE}-pc-pw32\n\texit ;;\n    *:Interix*:*)\n\tcase ${UNAME_MACHINE} in\n\t    x86)\n\t\techo i586-pc-interix${UNAME_RELEASE}\n\t\texit ;;\n\t    authenticamd | genuineintel | EM64T)\n\t\techo x86_64-unknown-interix${UNAME_RELEASE}\n\t\texit ;;\n\t    IA64)\n\t\techo ia64-unknown-interix${UNAME_RELEASE}\n\t\texit ;;\n\tesac ;;\n    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)\n\techo i${UNAME_MACHINE}-pc-mks\n\texit ;;\n    8664:Windows_NT:*)\n\techo x86_64-pc-mks\n\texit ;;\n    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)\n\t# How do we know it's Interix rather than the generic POSIX subsystem?\n\t# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we\n\t# UNAME_MACHINE based on the output of uname instead of i386?\n\techo i586-pc-interix\n\texit ;;\n    i*:UWIN*:*)\n\techo ${UNAME_MACHINE}-pc-uwin\n\texit ;;\n    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)\n\techo x86_64-unknown-cygwin\n\texit ;;\n    p*:CYGWIN*:*)\n\techo powerpcle-unknown-cygwin\n\texit ;;\n    prep*:SunOS:5.*:*)\n\techo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    *:GNU:*:*)\n\t# the GNU system\n\techo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`\n\texit ;;\n    *:GNU/*:*:*)\n\t# other systems with GNU libc and userland\n\techo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr \"[:upper:]\" \"[:lower:]\"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}\n\texit ;;\n    i*86:Minix:*:*)\n\techo ${UNAME_MACHINE}-pc-minix\n\texit ;;\n    aarch64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    aarch64_be:Linux:*:*)\n\tUNAME_MACHINE=aarch64_be\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    alpha:Linux:*:*)\n\tcase `sed -n '/^cpu model/s/^.*: \\(.*\\)/\\1/p' < /proc/cpuinfo` in\n\t  EV5)   UNAME_MACHINE=alphaev5 ;;\n\t  EV56)  UNAME_MACHINE=alphaev56 ;;\n\t  PCA56) UNAME_MACHINE=alphapca56 ;;\n\t  PCA57) UNAME_MACHINE=alphapca56 ;;\n\t  EV6)   UNAME_MACHINE=alphaev6 ;;\n\t  EV67)  UNAME_MACHINE=alphaev67 ;;\n\t  EV68*) UNAME_MACHINE=alphaev68 ;;\n\tesac\n\tobjdump --private-headers /bin/sh | grep -q ld.so.1\n\tif test \"$?\" = 0 ; then LIBC=gnulibc1 ; fi\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    arc:Linux:*:* | arceb:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    arm*:Linux:*:*)\n\teval $set_cc_for_build\n\tif echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \\\n\t    | grep -q __ARM_EABI__\n\tthen\n\t    echo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\telse\n\t    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \\\n\t\t| grep -q __ARM_PCS_VFP\n\t    then\n\t\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi\n\t    else\n\t\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf\n\t    fi\n\tfi\n\texit ;;\n    avr32*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    cris:Linux:*:*)\n\techo ${UNAME_MACHINE}-axis-linux-${LIBC}\n\texit ;;\n    crisv32:Linux:*:*)\n\techo ${UNAME_MACHINE}-axis-linux-${LIBC}\n\texit ;;\n    e2k:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    frv:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    hexagon:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    i*86:Linux:*:*)\n\techo ${UNAME_MACHINE}-pc-linux-${LIBC}\n\texit ;;\n    ia64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    k1om:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    m32r*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    m68*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    mips:Linux:*:* | mips64:Linux:*:*)\n\teval $set_cc_for_build\n\tsed 's/^\t//' << EOF >$dummy.c\n\t#undef CPU\n\t#undef ${UNAME_MACHINE}\n\t#undef ${UNAME_MACHINE}el\n\t#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)\n\tCPU=${UNAME_MACHINE}el\n\t#else\n\t#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)\n\tCPU=${UNAME_MACHINE}\n\t#else\n\tCPU=\n\t#endif\n\t#endif\nEOF\n\teval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`\n\ttest x\"${CPU}\" != x && { echo \"${CPU}-unknown-linux-${LIBC}\"; exit; }\n\t;;\n    mips64el:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    openrisc*:Linux:*:*)\n\techo or1k-unknown-linux-${LIBC}\n\texit ;;\n    or32:Linux:*:* | or1k*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    padre:Linux:*:*)\n\techo sparc-unknown-linux-${LIBC}\n\texit ;;\n    parisc64:Linux:*:* | hppa64:Linux:*:*)\n\techo hppa64-unknown-linux-${LIBC}\n\texit ;;\n    parisc:Linux:*:* | hppa:Linux:*:*)\n\t# Look for CPU level\n\tcase `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in\n\t  PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;\n\t  PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;\n\t  *)    echo hppa-unknown-linux-${LIBC} ;;\n\tesac\n\texit ;;\n    ppc64:Linux:*:*)\n\techo powerpc64-unknown-linux-${LIBC}\n\texit ;;\n    ppc:Linux:*:*)\n\techo powerpc-unknown-linux-${LIBC}\n\texit ;;\n    ppc64le:Linux:*:*)\n\techo powerpc64le-unknown-linux-${LIBC}\n\texit ;;\n    ppcle:Linux:*:*)\n\techo powerpcle-unknown-linux-${LIBC}\n\texit ;;\n    riscv32:Linux:*:* | riscv64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    s390:Linux:*:* | s390x:Linux:*:*)\n\techo ${UNAME_MACHINE}-ibm-linux-${LIBC}\n\texit ;;\n    sh64*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    sh*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    sparc:Linux:*:* | sparc64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    tile*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    vax:Linux:*:*)\n\techo ${UNAME_MACHINE}-dec-linux-${LIBC}\n\texit ;;\n    x86_64:Linux:*:*)\n\techo ${UNAME_MACHINE}-pc-linux-${LIBC}\n\texit ;;\n    xtensa*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    i*86:DYNIX/ptx:4*:*)\n\t# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.\n\t# earlier versions are messed up and put the nodename in both\n\t# sysname and nodename.\n\techo i386-sequent-sysv4\n\texit ;;\n    i*86:UNIX_SV:4.2MP:2.*)\n\t# Unixware is an offshoot of SVR4, but it has its own version\n\t# number series starting with 2...\n\t# I am not positive that other SVR4 systems won't match this,\n\t# I just have to hope.  -- rms.\n\t# Use sysv4.2uw... so that sysv4* matches it.\n\techo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}\n\texit ;;\n    i*86:OS/2:*:*)\n\t# If we were able to find `uname', then EMX Unix compatibility\n\t# is probably installed.\n\techo ${UNAME_MACHINE}-pc-os2-emx\n\texit ;;\n    i*86:XTS-300:*:STOP)\n\techo ${UNAME_MACHINE}-unknown-stop\n\texit ;;\n    i*86:atheos:*:*)\n\techo ${UNAME_MACHINE}-unknown-atheos\n\texit ;;\n    i*86:syllable:*:*)\n\techo ${UNAME_MACHINE}-pc-syllable\n\texit ;;\n    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)\n\techo i386-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    i*86:*DOS:*:*)\n\techo ${UNAME_MACHINE}-pc-msdosdjgpp\n\texit ;;\n    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)\n\tUNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\\/MP$//'`\n\tif grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then\n\t\techo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}\n\telse\n\t\techo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}\n\tfi\n\texit ;;\n    i*86:*:5:[678]*)\n\t# UnixWare 7.x, OpenUNIX and OpenServer 6.\n\tcase `/bin/uname -X | grep \"^Machine\"` in\n\t    *486*)\t     UNAME_MACHINE=i486 ;;\n\t    *Pentium)\t     UNAME_MACHINE=i586 ;;\n\t    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;\n\tesac\n\techo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}\n\texit ;;\n    i*86:*:3.2:*)\n\tif test -f /usr/options/cb.name; then\n\t\tUNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`\n\t\techo ${UNAME_MACHINE}-pc-isc$UNAME_REL\n\telif /bin/uname -X 2>/dev/null >/dev/null ; then\n\t\tUNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`\n\t\t(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486\n\t\t(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \\\n\t\t\t&& UNAME_MACHINE=i586\n\t\t(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \\\n\t\t\t&& UNAME_MACHINE=i686\n\t\t(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \\\n\t\t\t&& UNAME_MACHINE=i686\n\t\techo ${UNAME_MACHINE}-pc-sco$UNAME_REL\n\telse\n\t\techo ${UNAME_MACHINE}-pc-sysv32\n\tfi\n\texit ;;\n    pc:*:*:*)\n\t# Left here for compatibility:\n\t# uname -m prints for DJGPP always 'pc', but it prints nothing about\n\t# the processor, so we play safe by assuming i586.\n\t# Note: whatever this is, it MUST be the same as what config.sub\n\t# prints for the \"djgpp\" host, or else GDB configure will decide that\n\t# this is a cross-build.\n\techo i586-pc-msdosdjgpp\n\texit ;;\n    Intel:Mach:3*:*)\n\techo i386-pc-mach3\n\texit ;;\n    paragon:*:*:*)\n\techo i860-intel-osf1\n\texit ;;\n    i860:*:4.*:*) # i860-SVR4\n\tif grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then\n\t  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4\n\telse # Add other i860-SVR4 vendors below as they are discovered.\n\t  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4\n\tfi\n\texit ;;\n    mini*:CTIX:SYS*5:*)\n\t# \"miniframe\"\n\techo m68010-convergent-sysv\n\texit ;;\n    mc68k:UNIX:SYSTEM5:3.51m)\n\techo m68k-convergent-sysv\n\texit ;;\n    M680?0:D-NIX:5.3:*)\n\techo m68k-diab-dnix\n\texit ;;\n    M68*:*:R3V[5678]*:*)\n\ttest -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;\n    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)\n\tOS_REL=''\n\ttest -r /etc/.relid \\\n\t&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \\([0-9][0-9]\\).*/\\1/p' < /etc/.relid`\n\t/bin/uname -p 2>/dev/null | grep 86 >/dev/null \\\n\t  && { echo i486-ncr-sysv4.3${OS_REL}; exit; }\n\t/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \\\n\t  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;\n    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)\n\t/bin/uname -p 2>/dev/null | grep 86 >/dev/null \\\n\t  && { echo i486-ncr-sysv4; exit; } ;;\n    NCR*:*:4.2:* | MPRAS*:*:4.2:*)\n\tOS_REL='.3'\n\ttest -r /etc/.relid \\\n\t    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \\([0-9][0-9]\\).*/\\1/p' < /etc/.relid`\n\t/bin/uname -p 2>/dev/null | grep 86 >/dev/null \\\n\t    && { echo i486-ncr-sysv4.3${OS_REL}; exit; }\n\t/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \\\n\t    && { echo i586-ncr-sysv4.3${OS_REL}; exit; }\n\t/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \\\n\t    && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;\n    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)\n\techo m68k-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    mc68030:UNIX_System_V:4.*:*)\n\techo m68k-atari-sysv4\n\texit ;;\n    TSUNAMI:LynxOS:2.*:*)\n\techo sparc-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    rs6000:LynxOS:2.*:*)\n\techo rs6000-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)\n\techo powerpc-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    SM[BE]S:UNIX_SV:*:*)\n\techo mips-dde-sysv${UNAME_RELEASE}\n\texit ;;\n    RM*:ReliantUNIX-*:*:*)\n\techo mips-sni-sysv4\n\texit ;;\n    RM*:SINIX-*:*:*)\n\techo mips-sni-sysv4\n\texit ;;\n    *:SINIX-*:*:*)\n\tif uname -p 2>/dev/null >/dev/null ; then\n\t\tUNAME_MACHINE=`(uname -p) 2>/dev/null`\n\t\techo ${UNAME_MACHINE}-sni-sysv4\n\telse\n\t\techo ns32k-sni-sysv\n\tfi\n\texit ;;\n    PENTIUM:*:4.0*:*)\t# Unisys `ClearPath HMP IX 4000' SVR4/MP effort\n\t\t\t# says <Richard.M.Bartel@ccMail.Census.GOV>\n\techo i586-unisys-sysv4\n\texit ;;\n    *:UNIX_System_V:4*:FTX*)\n\t# From Gerald Hewes <hewes@openmarket.com>.\n\t# How about differentiating between stratus architectures? -djm\n\techo hppa1.1-stratus-sysv4\n\texit ;;\n    *:*:*:FTX*)\n\t# From seanf@swdc.stratus.com.\n\techo i860-stratus-sysv4\n\texit ;;\n    i*86:VOS:*:*)\n\t# From Paul.Green@stratus.com.\n\techo ${UNAME_MACHINE}-stratus-vos\n\texit ;;\n    *:VOS:*:*)\n\t# From Paul.Green@stratus.com.\n\techo hppa1.1-stratus-vos\n\texit ;;\n    mc68*:A/UX:*:*)\n\techo m68k-apple-aux${UNAME_RELEASE}\n\texit ;;\n    news*:NEWS-OS:6*:*)\n\techo mips-sony-newsos6\n\texit ;;\n    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)\n\tif [ -d /usr/nec ]; then\n\t\techo mips-nec-sysv${UNAME_RELEASE}\n\telse\n\t\techo mips-unknown-sysv${UNAME_RELEASE}\n\tfi\n\texit ;;\n    BeBox:BeOS:*:*)\t# BeOS running on hardware made by Be, PPC only.\n\techo powerpc-be-beos\n\texit ;;\n    BeMac:BeOS:*:*)\t# BeOS running on Mac or Mac clone, PPC only.\n\techo powerpc-apple-beos\n\texit ;;\n    BePC:BeOS:*:*)\t# BeOS running on Intel PC compatible.\n\techo i586-pc-beos\n\texit ;;\n    BePC:Haiku:*:*)\t# Haiku running on Intel PC compatible.\n\techo i586-pc-haiku\n\texit ;;\n    x86_64:Haiku:*:*)\n\techo x86_64-unknown-haiku\n\texit ;;\n    SX-4:SUPER-UX:*:*)\n\techo sx4-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-5:SUPER-UX:*:*)\n\techo sx5-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-6:SUPER-UX:*:*)\n\techo sx6-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-7:SUPER-UX:*:*)\n\techo sx7-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-8:SUPER-UX:*:*)\n\techo sx8-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-8R:SUPER-UX:*:*)\n\techo sx8r-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-ACE:SUPER-UX:*:*)\n\techo sxace-nec-superux${UNAME_RELEASE}\n\texit ;;\n    Power*:Rhapsody:*:*)\n\techo powerpc-apple-rhapsody${UNAME_RELEASE}\n\texit ;;\n    *:Rhapsody:*:*)\n\techo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}\n\texit ;;\n    *:Darwin:*:*)\n\tUNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown\n\teval $set_cc_for_build\n\tif test \"$UNAME_PROCESSOR\" = unknown ; then\n\t    UNAME_PROCESSOR=powerpc\n\tfi\n\tif test `echo \"$UNAME_RELEASE\" | sed -e 's/\\..*//'` -le 10 ; then\n\t    if [ \"$CC_FOR_BUILD\" != no_compiler_found ]; then\n\t\tif (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \\\n\t\t    (CCOPTS=\"\" $CC_FOR_BUILD -E - 2>/dev/null) | \\\n\t\t    grep IS_64BIT_ARCH >/dev/null\n\t\tthen\n\t\t    case $UNAME_PROCESSOR in\n\t\t\ti386) UNAME_PROCESSOR=x86_64 ;;\n\t\t\tpowerpc) UNAME_PROCESSOR=powerpc64 ;;\n\t\t    esac\n\t\tfi\n\t    fi\n\telif test \"$UNAME_PROCESSOR\" = i386 ; then\n\t    # Avoid executing cc on OS X 10.9, as it ships with a stub\n\t    # that puts up a graphical alert prompting to install\n\t    # developer tools.  Any system running Mac OS X 10.7 or\n\t    # later (Darwin 11 and later) is required to have a 64-bit\n\t    # processor. This is not true of the ARM version of Darwin\n\t    # that Apple uses in portable devices.\n\t    UNAME_PROCESSOR=x86_64\n\tfi\n\techo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}\n\texit ;;\n    *:procnto*:*:* | *:QNX:[0123456789]*:*)\n\tUNAME_PROCESSOR=`uname -p`\n\tif test \"$UNAME_PROCESSOR\" = x86; then\n\t\tUNAME_PROCESSOR=i386\n\t\tUNAME_MACHINE=pc\n\tfi\n\techo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}\n\texit ;;\n    *:QNX:*:4*)\n\techo i386-pc-qnx\n\texit ;;\n    NEO-?:NONSTOP_KERNEL:*:*)\n\techo neo-tandem-nsk${UNAME_RELEASE}\n\texit ;;\n    NSE-*:NONSTOP_KERNEL:*:*)\n\techo nse-tandem-nsk${UNAME_RELEASE}\n\texit ;;\n    NSR-?:NONSTOP_KERNEL:*:*)\n\techo nsr-tandem-nsk${UNAME_RELEASE}\n\texit ;;\n    *:NonStop-UX:*:*)\n\techo mips-compaq-nonstopux\n\texit ;;\n    BS2000:POSIX*:*:*)\n\techo bs2000-siemens-sysv\n\texit ;;\n    DS/*:UNIX_System_V:*:*)\n\techo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}\n\texit ;;\n    *:Plan9:*:*)\n\t# \"uname -m\" is not consistent, so use $cputype instead. 386\n\t# is converted to i386 for consistency with other x86\n\t# operating systems.\n\tif test \"$cputype\" = 386; then\n\t    UNAME_MACHINE=i386\n\telse\n\t    UNAME_MACHINE=\"$cputype\"\n\tfi\n\techo ${UNAME_MACHINE}-unknown-plan9\n\texit ;;\n    *:TOPS-10:*:*)\n\techo pdp10-unknown-tops10\n\texit ;;\n    *:TENEX:*:*)\n\techo pdp10-unknown-tenex\n\texit ;;\n    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)\n\techo pdp10-dec-tops20\n\texit ;;\n    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)\n\techo pdp10-xkl-tops20\n\texit ;;\n    *:TOPS-20:*:*)\n\techo pdp10-unknown-tops20\n\texit ;;\n    *:ITS:*:*)\n\techo pdp10-unknown-its\n\texit ;;\n    SEI:*:*:SEIUX)\n\techo mips-sei-seiux${UNAME_RELEASE}\n\texit ;;\n    *:DragonFly:*:*)\n\techo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`\n\texit ;;\n    *:*VMS:*:*)\n\tUNAME_MACHINE=`(uname -p) 2>/dev/null`\n\tcase \"${UNAME_MACHINE}\" in\n\t    A*) echo alpha-dec-vms ; exit ;;\n\t    I*) echo ia64-dec-vms ; exit ;;\n\t    V*) echo vax-dec-vms ; exit ;;\n\tesac ;;\n    *:XENIX:*:SysV)\n\techo i386-pc-xenix\n\texit ;;\n    i*86:skyos:*:*)\n\techo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'`\n\texit ;;\n    i*86:rdos:*:*)\n\techo ${UNAME_MACHINE}-pc-rdos\n\texit ;;\n    i*86:AROS:*:*)\n\techo ${UNAME_MACHINE}-pc-aros\n\texit ;;\n    x86_64:VMkernel:*:*)\n\techo ${UNAME_MACHINE}-unknown-esx\n\texit ;;\n    amd64:Isilon\\ OneFS:*:*)\n\techo x86_64-unknown-onefs\n\texit ;;\nesac\n\ncat >&2 <<EOF\n$0: unable to guess system type\n\nThis script (version $timestamp), has failed to recognize the\noperating system you are using. If your script is old, overwrite\nconfig.guess and config.sub with the latest versions from:\n\n  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess\nand\n  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub\n\nIf $0 has already been updated, send the following data and any\ninformation you think might be pertinent to config-patches@gnu.org to\nprovide the necessary information to handle your system.\n\nconfig.guess timestamp = $timestamp\n\nuname -m = `(uname -m) 2>/dev/null || echo unknown`\nuname -r = `(uname -r) 2>/dev/null || echo unknown`\nuname -s = `(uname -s) 2>/dev/null || echo unknown`\nuname -v = `(uname -v) 2>/dev/null || echo unknown`\n\n/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`\n/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`\n\nhostinfo               = `(hostinfo) 2>/dev/null`\n/bin/universe          = `(/bin/universe) 2>/dev/null`\n/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`\n/bin/arch              = `(/bin/arch) 2>/dev/null`\n/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`\n/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`\n\nUNAME_MACHINE = ${UNAME_MACHINE}\nUNAME_RELEASE = ${UNAME_RELEASE}\nUNAME_SYSTEM  = ${UNAME_SYSTEM}\nUNAME_VERSION = ${UNAME_VERSION}\nEOF\n\nexit 1\n\n# Local variables:\n# eval: (add-hook 'write-file-hooks 'time-stamp)\n# time-stamp-start: \"timestamp='\"\n# time-stamp-format: \"%:y-%02m-%02d\"\n# time-stamp-end: \"'\"\n# End:\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/config.h.in",
    "content": "/* config.h.in.  Generated from configure.ac by autoheader.  */\n\n/* Define to 1 if you have the <arpa/inet.h> header file. */\n#undef HAVE_ARPA_INET_H\n\n/* Define to 1 if you have the <dlfcn.h> header file. */\n#undef HAVE_DLFCN_H\n\n/* Define to 1 if you have the <fcntl.h> header file. */\n#undef HAVE_FCNTL_H\n\n/* Define to 1 if you have the `getcwd' function. */\n#undef HAVE_GETCWD\n\n/* Define to 1 if you have the `gethostbyname' function. */\n#undef HAVE_GETHOSTBYNAME\n\n/* Define to 1 if you have the `gethostname' function. */\n#undef HAVE_GETHOSTNAME\n\n/* Define to 1 if you have the `getlogin' function. */\n#undef HAVE_GETLOGIN\n\n/* Define to 1 if you have the `getpwuid_r' function. */\n#undef HAVE_GETPWUID_R\n\n/* Define to 1 if you have the `gettimeofday' function. */\n#undef HAVE_GETTIMEOFDAY\n\n/* Define to 1 if you have the `getuid' function. */\n#undef HAVE_GETUID\n\n/* Define to 1 if you have the <inttypes.h> header file. */\n#undef HAVE_INTTYPES_H\n\n/* Define to 1 if you have the `rt' library (-lrt). */\n#undef HAVE_LIBRT\n\n/* Define to 1 if you have the `memmove' function. */\n#undef HAVE_MEMMOVE\n\n/* Define to 1 if you have the <memory.h> header file. */\n#undef HAVE_MEMORY_H\n\n/* Define to 1 if you have the `memset' function. */\n#undef HAVE_MEMSET\n\n/* Define to 1 if you have the <netdb.h> header file. */\n#undef HAVE_NETDB_H\n\n/* Define to 1 if you have the <netinet/in.h> header file. */\n#undef HAVE_NETINET_IN_H\n\n/* Define to 1 if you have the `poll' function. */\n#undef HAVE_POLL\n\n/* Define to 1 if you have the `socket' function. */\n#undef HAVE_SOCKET\n\n/* Define to 1 if you have the <stdint.h> header file. */\n#undef HAVE_STDINT_H\n\n/* Define to 1 if you have the <stdlib.h> header file. */\n#undef HAVE_STDLIB_H\n\n/* Define to 1 if you have the `strchr' function. */\n#undef HAVE_STRCHR\n\n/* Define to 1 if you have the `strdup' function. */\n#undef HAVE_STRDUP\n\n/* Define to 1 if you have the `strerror' function. */\n#undef HAVE_STRERROR\n\n/* Define to 1 if you have the <strings.h> header file. */\n#undef HAVE_STRINGS_H\n\n/* Define to 1 if you have the <string.h> header file. */\n#undef HAVE_STRING_H\n\n/* Define to 1 if you have the `strtol' function. */\n#undef HAVE_STRTOL\n\n/* Define to 1 if you have the <sys/socket.h> header file. */\n#undef HAVE_SYS_SOCKET_H\n\n/* Define to 1 if you have the <sys/stat.h> header file. */\n#undef HAVE_SYS_STAT_H\n\n/* Define to 1 if you have the <sys/time.h> header file. */\n#undef HAVE_SYS_TIME_H\n\n/* Define to 1 if you have the <sys/types.h> header file. */\n#undef HAVE_SYS_TYPES_H\n\n/* Define to 1 if you have the <sys/utsname.h> header file. */\n#undef HAVE_SYS_UTSNAME_H\n\n/* Define to 1 if you have the <unistd.h> header file. */\n#undef HAVE_UNISTD_H\n\n/* Define to the sub-directory where libtool stores uninstalled libraries. */\n#undef LT_OBJDIR\n\n/* Name of package */\n#undef PACKAGE\n\n/* Define to the address where bug reports for this package should be sent. */\n#undef PACKAGE_BUGREPORT\n\n/* Define to the full name of this package. */\n#undef PACKAGE_NAME\n\n/* Define to the full name and version of this package. */\n#undef PACKAGE_STRING\n\n/* Define to the one symbol short name of this package. */\n#undef PACKAGE_TARNAME\n\n/* Define to the home page for this package. */\n#undef PACKAGE_URL\n\n/* Define to the version of this package. */\n#undef PACKAGE_VERSION\n\n/* poll() second argument type */\n#undef POLL_NFDS_TYPE\n\n/* Define to 1, if SOCK_CLOEXEC is defined and wanted */\n#undef SOCK_CLOEXEC_ENABLED\n\n/* Define to 1 if you have the ANSI C header files. */\n#undef STDC_HEADERS\n\n/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */\n#undef TIME_WITH_SYS_TIME\n\n/* Version number of package */\n#undef VERSION\n\n/* Define to empty if `const' does not conform to ANSI C. */\n#undef const\n\n/* Define to `__inline__' or `__inline' if that's what the C compiler\n   calls it, or to nothing if 'inline' is not supported under any name.  */\n#ifndef __cplusplus\n#undef inline\n#endif\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/config.sub",
    "content": "#! /bin/sh\n# Configuration validation subroutine script.\n#   Copyright 1992-2016 Free Software Foundation, Inc.\n\ntimestamp='2016-11-04'\n\n# This file is free software; you can redistribute it and/or modify it\n# under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful, but\n# WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n# General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, see <http://www.gnu.org/licenses/>.\n#\n# As a special exception to the GNU General Public License, if you\n# distribute this file as part of a program that contains a\n# configuration script generated by Autoconf, you may include it under\n# the same distribution terms that you use for the rest of that\n# program.  This Exception is an additional permission under section 7\n# of the GNU General Public License, version 3 (\"GPLv3\").\n\n\n# Please send patches to <config-patches@gnu.org>.\n#\n# Configuration subroutine to validate and canonicalize a configuration type.\n# Supply the specified configuration type as an argument.\n# If it is invalid, we print an error message on stderr and exit with code 1.\n# Otherwise, we print the canonical config type on stdout and succeed.\n\n# You can get the latest version of this script from:\n# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub\n\n# This file is supposed to be the same for all GNU packages\n# and recognize all the CPU types, system types and aliases\n# that are meaningful with *any* GNU software.\n# Each package is responsible for reporting which valid configurations\n# it does not support.  The user should be able to distinguish\n# a failure to support a valid configuration from a meaningless\n# configuration.\n\n# The goal of this file is to map all the various variations of a given\n# machine specification into a single specification in the form:\n#\tCPU_TYPE-MANUFACTURER-OPERATING_SYSTEM\n# or in some cases, the newer four-part form:\n#\tCPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM\n# It is wrong to echo any other type of specification.\n\nme=`echo \"$0\" | sed -e 's,.*/,,'`\n\nusage=\"\\\nUsage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS\n\nCanonicalize a configuration name.\n\nOperation modes:\n  -h, --help         print this help, then exit\n  -t, --time-stamp   print date of last modification, then exit\n  -v, --version      print version number, then exit\n\nReport bugs and patches to <config-patches@gnu.org>.\"\n\nversion=\"\\\nGNU config.sub ($timestamp)\n\nCopyright 1992-2016 Free Software Foundation, Inc.\n\nThis is free software; see the source for copying conditions.  There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\"\n\nhelp=\"\nTry \\`$me --help' for more information.\"\n\n# Parse command line\nwhile test $# -gt 0 ; do\n  case $1 in\n    --time-stamp | --time* | -t )\n       echo \"$timestamp\" ; exit ;;\n    --version | -v )\n       echo \"$version\" ; exit ;;\n    --help | --h* | -h )\n       echo \"$usage\"; exit ;;\n    -- )     # Stop option processing\n       shift; break ;;\n    - )\t# Use stdin as input.\n       break ;;\n    -* )\n       echo \"$me: invalid option $1$help\"\n       exit 1 ;;\n\n    *local*)\n       # First pass through any local machine types.\n       echo $1\n       exit ;;\n\n    * )\n       break ;;\n  esac\ndone\n\ncase $# in\n 0) echo \"$me: missing argument$help\" >&2\n    exit 1;;\n 1) ;;\n *) echo \"$me: too many arguments$help\" >&2\n    exit 1;;\nesac\n\n# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).\n# Here we must recognize all the valid KERNEL-OS combinations.\nmaybe_os=`echo $1 | sed 's/^\\(.*\\)-\\([^-]*-[^-]*\\)$/\\2/'`\ncase $maybe_os in\n  nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \\\n  linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \\\n  knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \\\n  kopensolaris*-gnu* | cloudabi*-eabi* | \\\n  storm-chaos* | os2-emx* | rtmk-nova*)\n    os=-$maybe_os\n    basic_machine=`echo $1 | sed 's/^\\(.*\\)-\\([^-]*-[^-]*\\)$/\\1/'`\n    ;;\n  android-linux)\n    os=-linux-android\n    basic_machine=`echo $1 | sed 's/^\\(.*\\)-\\([^-]*-[^-]*\\)$/\\1/'`-unknown\n    ;;\n  *)\n    basic_machine=`echo $1 | sed 's/-[^-]*$//'`\n    if [ $basic_machine != $1 ]\n    then os=`echo $1 | sed 's/.*-/-/'`\n    else os=; fi\n    ;;\nesac\n\n### Let's recognize common machines as not being operating systems so\n### that things like config.sub decstation-3100 work.  We also\n### recognize some manufacturers as not being operating systems, so we\n### can provide default operating systems below.\ncase $os in\n\t-sun*os*)\n\t\t# Prevent following clause from handling this invalid input.\n\t\t;;\n\t-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \\\n\t-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \\\n\t-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \\\n\t-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\\\n\t-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \\\n\t-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \\\n\t-apple | -axis | -knuth | -cray | -microblaze*)\n\t\tos=\n\t\tbasic_machine=$1\n\t\t;;\n\t-bluegene*)\n\t\tos=-cnk\n\t\t;;\n\t-sim | -cisco | -oki | -wec | -winbond)\n\t\tos=\n\t\tbasic_machine=$1\n\t\t;;\n\t-scout)\n\t\t;;\n\t-wrs)\n\t\tos=-vxworks\n\t\tbasic_machine=$1\n\t\t;;\n\t-chorusos*)\n\t\tos=-chorusos\n\t\tbasic_machine=$1\n\t\t;;\n\t-chorusrdb)\n\t\tos=-chorusrdb\n\t\tbasic_machine=$1\n\t\t;;\n\t-hiux*)\n\t\tos=-hiuxwe2\n\t\t;;\n\t-sco6)\n\t\tos=-sco5v6\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco5)\n\t\tos=-sco3.2v5\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco4)\n\t\tos=-sco3.2v4\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco3.2.[4-9]*)\n\t\tos=`echo $os | sed -e 's/sco3.2./sco3.2v/'`\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco3.2v[4-9]*)\n\t\t# Don't forget version if it is 3.2v4 or newer.\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco5v6*)\n\t\t# Don't forget version if it is 3.2v4 or newer.\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-sco*)\n\t\tos=-sco3.2v2\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-udk*)\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-isc)\n\t\tos=-isc2.2\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-clix*)\n\t\tbasic_machine=clipper-intergraph\n\t\t;;\n\t-isc*)\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`\n\t\t;;\n\t-lynx*178)\n\t\tos=-lynxos178\n\t\t;;\n\t-lynx*5)\n\t\tos=-lynxos5\n\t\t;;\n\t-lynx*)\n\t\tos=-lynxos\n\t\t;;\n\t-ptx*)\n\t\tbasic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`\n\t\t;;\n\t-windowsnt*)\n\t\tos=`echo $os | sed -e 's/windowsnt/winnt/'`\n\t\t;;\n\t-psos*)\n\t\tos=-psos\n\t\t;;\n\t-mint | -mint[0-9]*)\n\t\tbasic_machine=m68k-atari\n\t\tos=-mint\n\t\t;;\nesac\n\n# Decode aliases for certain CPU-COMPANY combinations.\ncase $basic_machine in\n\t# Recognize the basic CPU types without company name.\n\t# Some are omitted here because they have special meanings below.\n\t1750a | 580 \\\n\t| a29k \\\n\t| aarch64 | aarch64_be \\\n\t| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \\\n\t| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \\\n\t| am33_2.0 \\\n\t| arc | arceb \\\n\t| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \\\n\t| avr | avr32 \\\n\t| ba \\\n\t| be32 | be64 \\\n\t| bfin \\\n\t| c4x | c8051 | clipper \\\n\t| d10v | d30v | dlx | dsp16xx \\\n\t| e2k | epiphany \\\n\t| fido | fr30 | frv | ft32 \\\n\t| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \\\n\t| hexagon \\\n\t| i370 | i860 | i960 | ia64 \\\n\t| ip2k | iq2000 \\\n\t| k1om \\\n\t| le32 | le64 \\\n\t| lm32 \\\n\t| m32c | m32r | m32rle | m68000 | m68k | m88k \\\n\t| maxq | mb | microblaze | microblazeel | mcore | mep | metag \\\n\t| mips | mipsbe | mipseb | mipsel | mipsle \\\n\t| mips16 \\\n\t| mips64 | mips64el \\\n\t| mips64octeon | mips64octeonel \\\n\t| mips64orion | mips64orionel \\\n\t| mips64r5900 | mips64r5900el \\\n\t| mips64vr | mips64vrel \\\n\t| mips64vr4100 | mips64vr4100el \\\n\t| mips64vr4300 | mips64vr4300el \\\n\t| mips64vr5000 | mips64vr5000el \\\n\t| mips64vr5900 | mips64vr5900el \\\n\t| mipsisa32 | mipsisa32el \\\n\t| mipsisa32r2 | mipsisa32r2el \\\n\t| mipsisa32r6 | mipsisa32r6el \\\n\t| mipsisa64 | mipsisa64el \\\n\t| mipsisa64r2 | mipsisa64r2el \\\n\t| mipsisa64r6 | mipsisa64r6el \\\n\t| mipsisa64sb1 | mipsisa64sb1el \\\n\t| mipsisa64sr71k | mipsisa64sr71kel \\\n\t| mipsr5900 | mipsr5900el \\\n\t| mipstx39 | mipstx39el \\\n\t| mn10200 | mn10300 \\\n\t| moxie \\\n\t| mt \\\n\t| msp430 \\\n\t| nds32 | nds32le | nds32be \\\n\t| nios | nios2 | nios2eb | nios2el \\\n\t| ns16k | ns32k \\\n\t| open8 | or1k | or1knd | or32 \\\n\t| pdp10 | pdp11 | pj | pjl \\\n\t| powerpc | powerpc64 | powerpc64le | powerpcle \\\n\t| pru \\\n\t| pyramid \\\n\t| riscv32 | riscv64 \\\n\t| rl78 | rx \\\n\t| score \\\n\t| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \\\n\t| sh64 | sh64le \\\n\t| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \\\n\t| sparcv8 | sparcv9 | sparcv9b | sparcv9v \\\n\t| spu \\\n\t| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \\\n\t| ubicom32 \\\n\t| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \\\n\t| visium \\\n\t| we32k \\\n\t| x86 | xc16x | xstormy16 | xtensa \\\n\t| z8k | z80)\n\t\tbasic_machine=$basic_machine-unknown\n\t\t;;\n\tc54x)\n\t\tbasic_machine=tic54x-unknown\n\t\t;;\n\tc55x)\n\t\tbasic_machine=tic55x-unknown\n\t\t;;\n\tc6x)\n\t\tbasic_machine=tic6x-unknown\n\t\t;;\n\tleon|leon[3-9])\n\t\tbasic_machine=sparc-$basic_machine\n\t\t;;\n\tm6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)\n\t\tbasic_machine=$basic_machine-unknown\n\t\tos=-none\n\t\t;;\n\tm88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)\n\t\t;;\n\tms1)\n\t\tbasic_machine=mt-unknown\n\t\t;;\n\n\tstrongarm | thumb | xscale)\n\t\tbasic_machine=arm-unknown\n\t\t;;\n\txgate)\n\t\tbasic_machine=$basic_machine-unknown\n\t\tos=-none\n\t\t;;\n\txscaleeb)\n\t\tbasic_machine=armeb-unknown\n\t\t;;\n\n\txscaleel)\n\t\tbasic_machine=armel-unknown\n\t\t;;\n\n\t# We use `pc' rather than `unknown'\n\t# because (1) that's what they normally are, and\n\t# (2) the word \"unknown\" tends to confuse beginning users.\n\ti*86 | x86_64)\n\t  basic_machine=$basic_machine-pc\n\t  ;;\n\t# Object if more than one company name word.\n\t*-*-*)\n\t\techo Invalid configuration \\`$1\\': machine \\`$basic_machine\\' not recognized 1>&2\n\t\texit 1\n\t\t;;\n\t# Recognize the basic CPU types with company name.\n\t580-* \\\n\t| a29k-* \\\n\t| aarch64-* | aarch64_be-* \\\n\t| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \\\n\t| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \\\n\t| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \\\n\t| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \\\n\t| avr-* | avr32-* \\\n\t| ba-* \\\n\t| be32-* | be64-* \\\n\t| bfin-* | bs2000-* \\\n\t| c[123]* | c30-* | [cjt]90-* | c4x-* \\\n\t| c8051-* | clipper-* | craynv-* | cydra-* \\\n\t| d10v-* | d30v-* | dlx-* \\\n\t| e2k-* | elxsi-* \\\n\t| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \\\n\t| h8300-* | h8500-* \\\n\t| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \\\n\t| hexagon-* \\\n\t| i*86-* | i860-* | i960-* | ia64-* \\\n\t| ip2k-* | iq2000-* \\\n\t| k1om-* \\\n\t| le32-* | le64-* \\\n\t| lm32-* \\\n\t| m32c-* | m32r-* | m32rle-* \\\n\t| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \\\n\t| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \\\n\t| microblaze-* | microblazeel-* \\\n\t| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \\\n\t| mips16-* \\\n\t| mips64-* | mips64el-* \\\n\t| mips64octeon-* | mips64octeonel-* \\\n\t| mips64orion-* | mips64orionel-* \\\n\t| mips64r5900-* | mips64r5900el-* \\\n\t| mips64vr-* | mips64vrel-* \\\n\t| mips64vr4100-* | mips64vr4100el-* \\\n\t| mips64vr4300-* | mips64vr4300el-* \\\n\t| mips64vr5000-* | mips64vr5000el-* \\\n\t| mips64vr5900-* | mips64vr5900el-* \\\n\t| mipsisa32-* | mipsisa32el-* \\\n\t| mipsisa32r2-* | mipsisa32r2el-* \\\n\t| mipsisa32r6-* | mipsisa32r6el-* \\\n\t| mipsisa64-* | mipsisa64el-* \\\n\t| mipsisa64r2-* | mipsisa64r2el-* \\\n\t| mipsisa64r6-* | mipsisa64r6el-* \\\n\t| mipsisa64sb1-* | mipsisa64sb1el-* \\\n\t| mipsisa64sr71k-* | mipsisa64sr71kel-* \\\n\t| mipsr5900-* | mipsr5900el-* \\\n\t| mipstx39-* | mipstx39el-* \\\n\t| mmix-* \\\n\t| mt-* \\\n\t| msp430-* \\\n\t| nds32-* | nds32le-* | nds32be-* \\\n\t| nios-* | nios2-* | nios2eb-* | nios2el-* \\\n\t| none-* | np1-* | ns16k-* | ns32k-* \\\n\t| open8-* \\\n\t| or1k*-* \\\n\t| orion-* \\\n\t| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \\\n\t| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \\\n\t| pru-* \\\n\t| pyramid-* \\\n\t| riscv32-* | riscv64-* \\\n\t| rl78-* | romp-* | rs6000-* | rx-* \\\n\t| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \\\n\t| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \\\n\t| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \\\n\t| sparclite-* \\\n\t| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \\\n\t| tahoe-* \\\n\t| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \\\n\t| tile*-* \\\n\t| tron-* \\\n\t| ubicom32-* \\\n\t| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \\\n\t| vax-* \\\n\t| visium-* \\\n\t| we32k-* \\\n\t| x86-* | x86_64-* | xc16x-* | xps100-* \\\n\t| xstormy16-* | xtensa*-* \\\n\t| ymp-* \\\n\t| z8k-* | z80-*)\n\t\t;;\n\t# Recognize the basic CPU types without company name, with glob match.\n\txtensa*)\n\t\tbasic_machine=$basic_machine-unknown\n\t\t;;\n\t# Recognize the various machine names and aliases which stand\n\t# for a CPU type and a company and sometimes even an OS.\n\t386bsd)\n\t\tbasic_machine=i386-unknown\n\t\tos=-bsd\n\t\t;;\n\t3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)\n\t\tbasic_machine=m68000-att\n\t\t;;\n\t3b*)\n\t\tbasic_machine=we32k-att\n\t\t;;\n\ta29khif)\n\t\tbasic_machine=a29k-amd\n\t\tos=-udi\n\t\t;;\n\tabacus)\n\t\tbasic_machine=abacus-unknown\n\t\t;;\n\tadobe68k)\n\t\tbasic_machine=m68010-adobe\n\t\tos=-scout\n\t\t;;\n\talliant | fx80)\n\t\tbasic_machine=fx80-alliant\n\t\t;;\n\taltos | altos3068)\n\t\tbasic_machine=m68k-altos\n\t\t;;\n\tam29k)\n\t\tbasic_machine=a29k-none\n\t\tos=-bsd\n\t\t;;\n\tamd64)\n\t\tbasic_machine=x86_64-pc\n\t\t;;\n\tamd64-*)\n\t\tbasic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tamdahl)\n\t\tbasic_machine=580-amdahl\n\t\tos=-sysv\n\t\t;;\n\tamiga | amiga-*)\n\t\tbasic_machine=m68k-unknown\n\t\t;;\n\tamigaos | amigados)\n\t\tbasic_machine=m68k-unknown\n\t\tos=-amigaos\n\t\t;;\n\tamigaunix | amix)\n\t\tbasic_machine=m68k-unknown\n\t\tos=-sysv4\n\t\t;;\n\tapollo68)\n\t\tbasic_machine=m68k-apollo\n\t\tos=-sysv\n\t\t;;\n\tapollo68bsd)\n\t\tbasic_machine=m68k-apollo\n\t\tos=-bsd\n\t\t;;\n\taros)\n\t\tbasic_machine=i386-pc\n\t\tos=-aros\n\t\t;;\n\tasmjs)\n\t\tbasic_machine=asmjs-unknown\n\t\t;;\n\taux)\n\t\tbasic_machine=m68k-apple\n\t\tos=-aux\n\t\t;;\n\tbalance)\n\t\tbasic_machine=ns32k-sequent\n\t\tos=-dynix\n\t\t;;\n\tblackfin)\n\t\tbasic_machine=bfin-unknown\n\t\tos=-linux\n\t\t;;\n\tblackfin-*)\n\t\tbasic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\tos=-linux\n\t\t;;\n\tbluegene*)\n\t\tbasic_machine=powerpc-ibm\n\t\tos=-cnk\n\t\t;;\n\tc54x-*)\n\t\tbasic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tc55x-*)\n\t\tbasic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tc6x-*)\n\t\tbasic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tc90)\n\t\tbasic_machine=c90-cray\n\t\tos=-unicos\n\t\t;;\n\tcegcc)\n\t\tbasic_machine=arm-unknown\n\t\tos=-cegcc\n\t\t;;\n\tconvex-c1)\n\t\tbasic_machine=c1-convex\n\t\tos=-bsd\n\t\t;;\n\tconvex-c2)\n\t\tbasic_machine=c2-convex\n\t\tos=-bsd\n\t\t;;\n\tconvex-c32)\n\t\tbasic_machine=c32-convex\n\t\tos=-bsd\n\t\t;;\n\tconvex-c34)\n\t\tbasic_machine=c34-convex\n\t\tos=-bsd\n\t\t;;\n\tconvex-c38)\n\t\tbasic_machine=c38-convex\n\t\tos=-bsd\n\t\t;;\n\tcray | j90)\n\t\tbasic_machine=j90-cray\n\t\tos=-unicos\n\t\t;;\n\tcraynv)\n\t\tbasic_machine=craynv-cray\n\t\tos=-unicosmp\n\t\t;;\n\tcr16 | cr16-*)\n\t\tbasic_machine=cr16-unknown\n\t\tos=-elf\n\t\t;;\n\tcrds | unos)\n\t\tbasic_machine=m68k-crds\n\t\t;;\n\tcrisv32 | crisv32-* | etraxfs*)\n\t\tbasic_machine=crisv32-axis\n\t\t;;\n\tcris | cris-* | etrax*)\n\t\tbasic_machine=cris-axis\n\t\t;;\n\tcrx)\n\t\tbasic_machine=crx-unknown\n\t\tos=-elf\n\t\t;;\n\tda30 | da30-*)\n\t\tbasic_machine=m68k-da30\n\t\t;;\n\tdecstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)\n\t\tbasic_machine=mips-dec\n\t\t;;\n\tdecsystem10* | dec10*)\n\t\tbasic_machine=pdp10-dec\n\t\tos=-tops10\n\t\t;;\n\tdecsystem20* | dec20*)\n\t\tbasic_machine=pdp10-dec\n\t\tos=-tops20\n\t\t;;\n\tdelta | 3300 | motorola-3300 | motorola-delta \\\n\t      | 3300-motorola | delta-motorola)\n\t\tbasic_machine=m68k-motorola\n\t\t;;\n\tdelta88)\n\t\tbasic_machine=m88k-motorola\n\t\tos=-sysv3\n\t\t;;\n\tdicos)\n\t\tbasic_machine=i686-pc\n\t\tos=-dicos\n\t\t;;\n\tdjgpp)\n\t\tbasic_machine=i586-pc\n\t\tos=-msdosdjgpp\n\t\t;;\n\tdpx20 | dpx20-*)\n\t\tbasic_machine=rs6000-bull\n\t\tos=-bosx\n\t\t;;\n\tdpx2* | dpx2*-bull)\n\t\tbasic_machine=m68k-bull\n\t\tos=-sysv3\n\t\t;;\n\te500v[12])\n\t\tbasic_machine=powerpc-unknown\n\t\tos=$os\"spe\"\n\t\t;;\n\te500v[12]-*)\n\t\tbasic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\tos=$os\"spe\"\n\t\t;;\n\tebmon29k)\n\t\tbasic_machine=a29k-amd\n\t\tos=-ebmon\n\t\t;;\n\telxsi)\n\t\tbasic_machine=elxsi-elxsi\n\t\tos=-bsd\n\t\t;;\n\tencore | umax | mmax)\n\t\tbasic_machine=ns32k-encore\n\t\t;;\n\tes1800 | OSE68k | ose68k | ose | OSE)\n\t\tbasic_machine=m68k-ericsson\n\t\tos=-ose\n\t\t;;\n\tfx2800)\n\t\tbasic_machine=i860-alliant\n\t\t;;\n\tgenix)\n\t\tbasic_machine=ns32k-ns\n\t\t;;\n\tgmicro)\n\t\tbasic_machine=tron-gmicro\n\t\tos=-sysv\n\t\t;;\n\tgo32)\n\t\tbasic_machine=i386-pc\n\t\tos=-go32\n\t\t;;\n\th3050r* | hiux*)\n\t\tbasic_machine=hppa1.1-hitachi\n\t\tos=-hiuxwe2\n\t\t;;\n\th8300hms)\n\t\tbasic_machine=h8300-hitachi\n\t\tos=-hms\n\t\t;;\n\th8300xray)\n\t\tbasic_machine=h8300-hitachi\n\t\tos=-xray\n\t\t;;\n\th8500hms)\n\t\tbasic_machine=h8500-hitachi\n\t\tos=-hms\n\t\t;;\n\tharris)\n\t\tbasic_machine=m88k-harris\n\t\tos=-sysv3\n\t\t;;\n\thp300-*)\n\t\tbasic_machine=m68k-hp\n\t\t;;\n\thp300bsd)\n\t\tbasic_machine=m68k-hp\n\t\tos=-bsd\n\t\t;;\n\thp300hpux)\n\t\tbasic_machine=m68k-hp\n\t\tos=-hpux\n\t\t;;\n\thp3k9[0-9][0-9] | hp9[0-9][0-9])\n\t\tbasic_machine=hppa1.0-hp\n\t\t;;\n\thp9k2[0-9][0-9] | hp9k31[0-9])\n\t\tbasic_machine=m68000-hp\n\t\t;;\n\thp9k3[2-9][0-9])\n\t\tbasic_machine=m68k-hp\n\t\t;;\n\thp9k6[0-9][0-9] | hp6[0-9][0-9])\n\t\tbasic_machine=hppa1.0-hp\n\t\t;;\n\thp9k7[0-79][0-9] | hp7[0-79][0-9])\n\t\tbasic_machine=hppa1.1-hp\n\t\t;;\n\thp9k78[0-9] | hp78[0-9])\n\t\t# FIXME: really hppa2.0-hp\n\t\tbasic_machine=hppa1.1-hp\n\t\t;;\n\thp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)\n\t\t# FIXME: really hppa2.0-hp\n\t\tbasic_machine=hppa1.1-hp\n\t\t;;\n\thp9k8[0-9][13679] | hp8[0-9][13679])\n\t\tbasic_machine=hppa1.1-hp\n\t\t;;\n\thp9k8[0-9][0-9] | hp8[0-9][0-9])\n\t\tbasic_machine=hppa1.0-hp\n\t\t;;\n\thppa-next)\n\t\tos=-nextstep3\n\t\t;;\n\thppaosf)\n\t\tbasic_machine=hppa1.1-hp\n\t\tos=-osf\n\t\t;;\n\thppro)\n\t\tbasic_machine=hppa1.1-hp\n\t\tos=-proelf\n\t\t;;\n\ti370-ibm* | ibm*)\n\t\tbasic_machine=i370-ibm\n\t\t;;\n\ti*86v32)\n\t\tbasic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`\n\t\tos=-sysv32\n\t\t;;\n\ti*86v4*)\n\t\tbasic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`\n\t\tos=-sysv4\n\t\t;;\n\ti*86v)\n\t\tbasic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`\n\t\tos=-sysv\n\t\t;;\n\ti*86sol2)\n\t\tbasic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`\n\t\tos=-solaris2\n\t\t;;\n\ti386mach)\n\t\tbasic_machine=i386-mach\n\t\tos=-mach\n\t\t;;\n\ti386-vsta | vsta)\n\t\tbasic_machine=i386-unknown\n\t\tos=-vsta\n\t\t;;\n\tiris | iris4d)\n\t\tbasic_machine=mips-sgi\n\t\tcase $os in\n\t\t    -irix*)\n\t\t\t;;\n\t\t    *)\n\t\t\tos=-irix4\n\t\t\t;;\n\t\tesac\n\t\t;;\n\tisi68 | isi)\n\t\tbasic_machine=m68k-isi\n\t\tos=-sysv\n\t\t;;\n\tleon-*|leon[3-9]-*)\n\t\tbasic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`\n\t\t;;\n\tm68knommu)\n\t\tbasic_machine=m68k-unknown\n\t\tos=-linux\n\t\t;;\n\tm68knommu-*)\n\t\tbasic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\tos=-linux\n\t\t;;\n\tm88k-omron*)\n\t\tbasic_machine=m88k-omron\n\t\t;;\n\tmagnum | m3230)\n\t\tbasic_machine=mips-mips\n\t\tos=-sysv\n\t\t;;\n\tmerlin)\n\t\tbasic_machine=ns32k-utek\n\t\tos=-sysv\n\t\t;;\n\tmicroblaze*)\n\t\tbasic_machine=microblaze-xilinx\n\t\t;;\n\tmingw64)\n\t\tbasic_machine=x86_64-pc\n\t\tos=-mingw64\n\t\t;;\n\tmingw32)\n\t\tbasic_machine=i686-pc\n\t\tos=-mingw32\n\t\t;;\n\tmingw32ce)\n\t\tbasic_machine=arm-unknown\n\t\tos=-mingw32ce\n\t\t;;\n\tminiframe)\n\t\tbasic_machine=m68000-convergent\n\t\t;;\n\t*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)\n\t\tbasic_machine=m68k-atari\n\t\tos=-mint\n\t\t;;\n\tmips3*-*)\n\t\tbasic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`\n\t\t;;\n\tmips3*)\n\t\tbasic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown\n\t\t;;\n\tmonitor)\n\t\tbasic_machine=m68k-rom68k\n\t\tos=-coff\n\t\t;;\n\tmorphos)\n\t\tbasic_machine=powerpc-unknown\n\t\tos=-morphos\n\t\t;;\n\tmoxiebox)\n\t\tbasic_machine=moxie-unknown\n\t\tos=-moxiebox\n\t\t;;\n\tmsdos)\n\t\tbasic_machine=i386-pc\n\t\tos=-msdos\n\t\t;;\n\tms1-*)\n\t\tbasic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`\n\t\t;;\n\tmsys)\n\t\tbasic_machine=i686-pc\n\t\tos=-msys\n\t\t;;\n\tmvs)\n\t\tbasic_machine=i370-ibm\n\t\tos=-mvs\n\t\t;;\n\tnacl)\n\t\tbasic_machine=le32-unknown\n\t\tos=-nacl\n\t\t;;\n\tncr3000)\n\t\tbasic_machine=i486-ncr\n\t\tos=-sysv4\n\t\t;;\n\tnetbsd386)\n\t\tbasic_machine=i386-unknown\n\t\tos=-netbsd\n\t\t;;\n\tnetwinder)\n\t\tbasic_machine=armv4l-rebel\n\t\tos=-linux\n\t\t;;\n\tnews | news700 | news800 | news900)\n\t\tbasic_machine=m68k-sony\n\t\tos=-newsos\n\t\t;;\n\tnews1000)\n\t\tbasic_machine=m68030-sony\n\t\tos=-newsos\n\t\t;;\n\tnews-3600 | risc-news)\n\t\tbasic_machine=mips-sony\n\t\tos=-newsos\n\t\t;;\n\tnecv70)\n\t\tbasic_machine=v70-nec\n\t\tos=-sysv\n\t\t;;\n\tnext | m*-next )\n\t\tbasic_machine=m68k-next\n\t\tcase $os in\n\t\t    -nextstep* )\n\t\t\t;;\n\t\t    -ns2*)\n\t\t      os=-nextstep2\n\t\t\t;;\n\t\t    *)\n\t\t      os=-nextstep3\n\t\t\t;;\n\t\tesac\n\t\t;;\n\tnh3000)\n\t\tbasic_machine=m68k-harris\n\t\tos=-cxux\n\t\t;;\n\tnh[45]000)\n\t\tbasic_machine=m88k-harris\n\t\tos=-cxux\n\t\t;;\n\tnindy960)\n\t\tbasic_machine=i960-intel\n\t\tos=-nindy\n\t\t;;\n\tmon960)\n\t\tbasic_machine=i960-intel\n\t\tos=-mon960\n\t\t;;\n\tnonstopux)\n\t\tbasic_machine=mips-compaq\n\t\tos=-nonstopux\n\t\t;;\n\tnp1)\n\t\tbasic_machine=np1-gould\n\t\t;;\n\tneo-tandem)\n\t\tbasic_machine=neo-tandem\n\t\t;;\n\tnse-tandem)\n\t\tbasic_machine=nse-tandem\n\t\t;;\n\tnsr-tandem)\n\t\tbasic_machine=nsr-tandem\n\t\t;;\n\top50n-* | op60c-*)\n\t\tbasic_machine=hppa1.1-oki\n\t\tos=-proelf\n\t\t;;\n\topenrisc | openrisc-*)\n\t\tbasic_machine=or32-unknown\n\t\t;;\n\tos400)\n\t\tbasic_machine=powerpc-ibm\n\t\tos=-os400\n\t\t;;\n\tOSE68000 | ose68000)\n\t\tbasic_machine=m68000-ericsson\n\t\tos=-ose\n\t\t;;\n\tos68k)\n\t\tbasic_machine=m68k-none\n\t\tos=-os68k\n\t\t;;\n\tpa-hitachi)\n\t\tbasic_machine=hppa1.1-hitachi\n\t\tos=-hiuxwe2\n\t\t;;\n\tparagon)\n\t\tbasic_machine=i860-intel\n\t\tos=-osf\n\t\t;;\n\tparisc)\n\t\tbasic_machine=hppa-unknown\n\t\tos=-linux\n\t\t;;\n\tparisc-*)\n\t\tbasic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\tos=-linux\n\t\t;;\n\tpbd)\n\t\tbasic_machine=sparc-tti\n\t\t;;\n\tpbb)\n\t\tbasic_machine=m68k-tti\n\t\t;;\n\tpc532 | pc532-*)\n\t\tbasic_machine=ns32k-pc532\n\t\t;;\n\tpc98)\n\t\tbasic_machine=i386-pc\n\t\t;;\n\tpc98-*)\n\t\tbasic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tpentium | p5 | k5 | k6 | nexgen | viac3)\n\t\tbasic_machine=i586-pc\n\t\t;;\n\tpentiumpro | p6 | 6x86 | athlon | athlon_*)\n\t\tbasic_machine=i686-pc\n\t\t;;\n\tpentiumii | pentium2 | pentiumiii | pentium3)\n\t\tbasic_machine=i686-pc\n\t\t;;\n\tpentium4)\n\t\tbasic_machine=i786-pc\n\t\t;;\n\tpentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)\n\t\tbasic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tpentiumpro-* | p6-* | 6x86-* | athlon-*)\n\t\tbasic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tpentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)\n\t\tbasic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tpentium4-*)\n\t\tbasic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tpn)\n\t\tbasic_machine=pn-gould\n\t\t;;\n\tpower)\tbasic_machine=power-ibm\n\t\t;;\n\tppc | ppcbe)\tbasic_machine=powerpc-unknown\n\t\t;;\n\tppc-* | ppcbe-*)\n\t\tbasic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tppcle | powerpclittle)\n\t\tbasic_machine=powerpcle-unknown\n\t\t;;\n\tppcle-* | powerpclittle-*)\n\t\tbasic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tppc64)\tbasic_machine=powerpc64-unknown\n\t\t;;\n\tppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tppc64le | powerpc64little)\n\t\tbasic_machine=powerpc64le-unknown\n\t\t;;\n\tppc64le-* | powerpc64little-*)\n\t\tbasic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tps2)\n\t\tbasic_machine=i386-ibm\n\t\t;;\n\tpw32)\n\t\tbasic_machine=i586-unknown\n\t\tos=-pw32\n\t\t;;\n\trdos | rdos64)\n\t\tbasic_machine=x86_64-pc\n\t\tos=-rdos\n\t\t;;\n\trdos32)\n\t\tbasic_machine=i386-pc\n\t\tos=-rdos\n\t\t;;\n\trom68k)\n\t\tbasic_machine=m68k-rom68k\n\t\tos=-coff\n\t\t;;\n\trm[46]00)\n\t\tbasic_machine=mips-siemens\n\t\t;;\n\trtpc | rtpc-*)\n\t\tbasic_machine=romp-ibm\n\t\t;;\n\ts390 | s390-*)\n\t\tbasic_machine=s390-ibm\n\t\t;;\n\ts390x | s390x-*)\n\t\tbasic_machine=s390x-ibm\n\t\t;;\n\tsa29200)\n\t\tbasic_machine=a29k-amd\n\t\tos=-udi\n\t\t;;\n\tsb1)\n\t\tbasic_machine=mipsisa64sb1-unknown\n\t\t;;\n\tsb1el)\n\t\tbasic_machine=mipsisa64sb1el-unknown\n\t\t;;\n\tsde)\n\t\tbasic_machine=mipsisa32-sde\n\t\tos=-elf\n\t\t;;\n\tsei)\n\t\tbasic_machine=mips-sei\n\t\tos=-seiux\n\t\t;;\n\tsequent)\n\t\tbasic_machine=i386-sequent\n\t\t;;\n\tsh)\n\t\tbasic_machine=sh-hitachi\n\t\tos=-hms\n\t\t;;\n\tsh5el)\n\t\tbasic_machine=sh5le-unknown\n\t\t;;\n\tsh64)\n\t\tbasic_machine=sh64-unknown\n\t\t;;\n\tsparclite-wrs | simso-wrs)\n\t\tbasic_machine=sparclite-wrs\n\t\tos=-vxworks\n\t\t;;\n\tsps7)\n\t\tbasic_machine=m68k-bull\n\t\tos=-sysv2\n\t\t;;\n\tspur)\n\t\tbasic_machine=spur-unknown\n\t\t;;\n\tst2000)\n\t\tbasic_machine=m68k-tandem\n\t\t;;\n\tstratus)\n\t\tbasic_machine=i860-stratus\n\t\tos=-sysv4\n\t\t;;\n\tstrongarm-* | thumb-*)\n\t\tbasic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`\n\t\t;;\n\tsun2)\n\t\tbasic_machine=m68000-sun\n\t\t;;\n\tsun2os3)\n\t\tbasic_machine=m68000-sun\n\t\tos=-sunos3\n\t\t;;\n\tsun2os4)\n\t\tbasic_machine=m68000-sun\n\t\tos=-sunos4\n\t\t;;\n\tsun3os3)\n\t\tbasic_machine=m68k-sun\n\t\tos=-sunos3\n\t\t;;\n\tsun3os4)\n\t\tbasic_machine=m68k-sun\n\t\tos=-sunos4\n\t\t;;\n\tsun4os3)\n\t\tbasic_machine=sparc-sun\n\t\tos=-sunos3\n\t\t;;\n\tsun4os4)\n\t\tbasic_machine=sparc-sun\n\t\tos=-sunos4\n\t\t;;\n\tsun4sol2)\n\t\tbasic_machine=sparc-sun\n\t\tos=-solaris2\n\t\t;;\n\tsun3 | sun3-*)\n\t\tbasic_machine=m68k-sun\n\t\t;;\n\tsun4)\n\t\tbasic_machine=sparc-sun\n\t\t;;\n\tsun386 | sun386i | roadrunner)\n\t\tbasic_machine=i386-sun\n\t\t;;\n\tsv1)\n\t\tbasic_machine=sv1-cray\n\t\tos=-unicos\n\t\t;;\n\tsymmetry)\n\t\tbasic_machine=i386-sequent\n\t\tos=-dynix\n\t\t;;\n\tt3e)\n\t\tbasic_machine=alphaev5-cray\n\t\tos=-unicos\n\t\t;;\n\tt90)\n\t\tbasic_machine=t90-cray\n\t\tos=-unicos\n\t\t;;\n\ttile*)\n\t\tbasic_machine=$basic_machine-unknown\n\t\tos=-linux-gnu\n\t\t;;\n\ttx39)\n\t\tbasic_machine=mipstx39-unknown\n\t\t;;\n\ttx39el)\n\t\tbasic_machine=mipstx39el-unknown\n\t\t;;\n\ttoad1)\n\t\tbasic_machine=pdp10-xkl\n\t\tos=-tops20\n\t\t;;\n\ttower | tower-32)\n\t\tbasic_machine=m68k-ncr\n\t\t;;\n\ttpf)\n\t\tbasic_machine=s390x-ibm\n\t\tos=-tpf\n\t\t;;\n\tudi29k)\n\t\tbasic_machine=a29k-amd\n\t\tos=-udi\n\t\t;;\n\tultra3)\n\t\tbasic_machine=a29k-nyu\n\t\tos=-sym1\n\t\t;;\n\tv810 | necv810)\n\t\tbasic_machine=v810-nec\n\t\tos=-none\n\t\t;;\n\tvaxv)\n\t\tbasic_machine=vax-dec\n\t\tos=-sysv\n\t\t;;\n\tvms)\n\t\tbasic_machine=vax-dec\n\t\tos=-vms\n\t\t;;\n\tvpp*|vx|vx-*)\n\t\tbasic_machine=f301-fujitsu\n\t\t;;\n\tvxworks960)\n\t\tbasic_machine=i960-wrs\n\t\tos=-vxworks\n\t\t;;\n\tvxworks68)\n\t\tbasic_machine=m68k-wrs\n\t\tos=-vxworks\n\t\t;;\n\tvxworks29k)\n\t\tbasic_machine=a29k-wrs\n\t\tos=-vxworks\n\t\t;;\n\tw65*)\n\t\tbasic_machine=w65-wdc\n\t\tos=-none\n\t\t;;\n\tw89k-*)\n\t\tbasic_machine=hppa1.1-winbond\n\t\tos=-proelf\n\t\t;;\n\txbox)\n\t\tbasic_machine=i686-pc\n\t\tos=-mingw32\n\t\t;;\n\txps | xps100)\n\t\tbasic_machine=xps100-honeywell\n\t\t;;\n\txscale-* | xscalee[bl]-*)\n\t\tbasic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`\n\t\t;;\n\tymp)\n\t\tbasic_machine=ymp-cray\n\t\tos=-unicos\n\t\t;;\n\tz8k-*-coff)\n\t\tbasic_machine=z8k-unknown\n\t\tos=-sim\n\t\t;;\n\tz80-*-coff)\n\t\tbasic_machine=z80-unknown\n\t\tos=-sim\n\t\t;;\n\tnone)\n\t\tbasic_machine=none-none\n\t\tos=-none\n\t\t;;\n\n# Here we handle the default manufacturer of certain CPU types.  It is in\n# some cases the only manufacturer, in others, it is the most popular.\n\tw89k)\n\t\tbasic_machine=hppa1.1-winbond\n\t\t;;\n\top50n)\n\t\tbasic_machine=hppa1.1-oki\n\t\t;;\n\top60c)\n\t\tbasic_machine=hppa1.1-oki\n\t\t;;\n\tromp)\n\t\tbasic_machine=romp-ibm\n\t\t;;\n\tmmix)\n\t\tbasic_machine=mmix-knuth\n\t\t;;\n\trs6000)\n\t\tbasic_machine=rs6000-ibm\n\t\t;;\n\tvax)\n\t\tbasic_machine=vax-dec\n\t\t;;\n\tpdp10)\n\t\t# there are many clones, so DEC is not a safe bet\n\t\tbasic_machine=pdp10-unknown\n\t\t;;\n\tpdp11)\n\t\tbasic_machine=pdp11-dec\n\t\t;;\n\twe32k)\n\t\tbasic_machine=we32k-att\n\t\t;;\n\tsh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)\n\t\tbasic_machine=sh-unknown\n\t\t;;\n\tsparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)\n\t\tbasic_machine=sparc-sun\n\t\t;;\n\tcydra)\n\t\tbasic_machine=cydra-cydrome\n\t\t;;\n\torion)\n\t\tbasic_machine=orion-highlevel\n\t\t;;\n\torion105)\n\t\tbasic_machine=clipper-highlevel\n\t\t;;\n\tmac | mpw | mac-mpw)\n\t\tbasic_machine=m68k-apple\n\t\t;;\n\tpmac | pmac-mpw)\n\t\tbasic_machine=powerpc-apple\n\t\t;;\n\t*-unknown)\n\t\t# Make sure to match an already-canonicalized machine name.\n\t\t;;\n\t*)\n\t\techo Invalid configuration \\`$1\\': machine \\`$basic_machine\\' not recognized 1>&2\n\t\texit 1\n\t\t;;\nesac\n\n# Here we canonicalize certain aliases for manufacturers.\ncase $basic_machine in\n\t*-digital*)\n\t\tbasic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`\n\t\t;;\n\t*-commodore*)\n\t\tbasic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`\n\t\t;;\n\t*)\n\t\t;;\nesac\n\n# Decode manufacturer-specific aliases for certain operating systems.\n\nif [ x\"$os\" != x\"\" ]\nthen\ncase $os in\n\t# First match some system type aliases\n\t# that might get confused with valid system types.\n\t# -solaris* is a basic system type, with this one exception.\n\t-auroraux)\n\t\tos=-auroraux\n\t\t;;\n\t-solaris1 | -solaris1.*)\n\t\tos=`echo $os | sed -e 's|solaris1|sunos4|'`\n\t\t;;\n\t-solaris)\n\t\tos=-solaris2\n\t\t;;\n\t-svr4*)\n\t\tos=-sysv4\n\t\t;;\n\t-unixware*)\n\t\tos=-sysv4.2uw\n\t\t;;\n\t-gnu/linux*)\n\t\tos=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`\n\t\t;;\n\t# First accept the basic system types.\n\t# The portable systems comes first.\n\t# Each alternative MUST END IN A *, to match a version number.\n\t# -sysv* is not here because it comes later, after sysvr4.\n\t-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \\\n\t      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\\\n\t      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \\\n\t      | -sym* | -kopensolaris* | -plan9* \\\n\t      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \\\n\t      | -aos* | -aros* | -cloudabi* | -sortix* \\\n\t      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \\\n\t      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \\\n\t      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \\\n\t      | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \\\n\t      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \\\n\t      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \\\n\t      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \\\n\t      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \\\n\t      | -chorusos* | -chorusrdb* | -cegcc* \\\n\t      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \\\n\t      | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \\\n\t      | -linux-newlib* | -linux-musl* | -linux-uclibc* \\\n\t      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \\\n\t      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \\\n\t      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \\\n\t      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \\\n\t      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \\\n\t      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \\\n\t      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \\\n\t      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \\\n\t      | -onefs* | -tirtos* | -phoenix* | -fuchsia*)\n\t# Remember, each alternative MUST END IN *, to match a version number.\n\t\t;;\n\t-qnx*)\n\t\tcase $basic_machine in\n\t\t    x86-* | i*86-*)\n\t\t\t;;\n\t\t    *)\n\t\t\tos=-nto$os\n\t\t\t;;\n\t\tesac\n\t\t;;\n\t-nto-qnx*)\n\t\t;;\n\t-nto*)\n\t\tos=`echo $os | sed -e 's|nto|nto-qnx|'`\n\t\t;;\n\t-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \\\n\t      | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \\\n\t      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)\n\t\t;;\n\t-mac*)\n\t\tos=`echo $os | sed -e 's|mac|macos|'`\n\t\t;;\n\t-linux-dietlibc)\n\t\tos=-linux-dietlibc\n\t\t;;\n\t-linux*)\n\t\tos=`echo $os | sed -e 's|linux|linux-gnu|'`\n\t\t;;\n\t-sunos5*)\n\t\tos=`echo $os | sed -e 's|sunos5|solaris2|'`\n\t\t;;\n\t-sunos6*)\n\t\tos=`echo $os | sed -e 's|sunos6|solaris3|'`\n\t\t;;\n\t-opened*)\n\t\tos=-openedition\n\t\t;;\n\t-os400*)\n\t\tos=-os400\n\t\t;;\n\t-wince*)\n\t\tos=-wince\n\t\t;;\n\t-osfrose*)\n\t\tos=-osfrose\n\t\t;;\n\t-osf*)\n\t\tos=-osf\n\t\t;;\n\t-utek*)\n\t\tos=-bsd\n\t\t;;\n\t-dynix*)\n\t\tos=-bsd\n\t\t;;\n\t-acis*)\n\t\tos=-aos\n\t\t;;\n\t-atheos*)\n\t\tos=-atheos\n\t\t;;\n\t-syllable*)\n\t\tos=-syllable\n\t\t;;\n\t-386bsd)\n\t\tos=-bsd\n\t\t;;\n\t-ctix* | -uts*)\n\t\tos=-sysv\n\t\t;;\n\t-nova*)\n\t\tos=-rtmk-nova\n\t\t;;\n\t-ns2 )\n\t\tos=-nextstep2\n\t\t;;\n\t-nsk*)\n\t\tos=-nsk\n\t\t;;\n\t# Preserve the version number of sinix5.\n\t-sinix5.*)\n\t\tos=`echo $os | sed -e 's|sinix|sysv|'`\n\t\t;;\n\t-sinix*)\n\t\tos=-sysv4\n\t\t;;\n\t-tpf*)\n\t\tos=-tpf\n\t\t;;\n\t-triton*)\n\t\tos=-sysv3\n\t\t;;\n\t-oss*)\n\t\tos=-sysv3\n\t\t;;\n\t-svr4)\n\t\tos=-sysv4\n\t\t;;\n\t-svr3)\n\t\tos=-sysv3\n\t\t;;\n\t-sysvr4)\n\t\tos=-sysv4\n\t\t;;\n\t# This must come after -sysvr4.\n\t-sysv*)\n\t\t;;\n\t-ose*)\n\t\tos=-ose\n\t\t;;\n\t-es1800*)\n\t\tos=-ose\n\t\t;;\n\t-xenix)\n\t\tos=-xenix\n\t\t;;\n\t-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)\n\t\tos=-mint\n\t\t;;\n\t-aros*)\n\t\tos=-aros\n\t\t;;\n\t-zvmoe)\n\t\tos=-zvmoe\n\t\t;;\n\t-dicos*)\n\t\tos=-dicos\n\t\t;;\n\t-nacl*)\n\t\t;;\n\t-ios)\n\t\t;;\n\t-none)\n\t\t;;\n\t*)\n\t\t# Get rid of the `-' at the beginning of $os.\n\t\tos=`echo $os | sed 's/[^-]*-//'`\n\t\techo Invalid configuration \\`$1\\': system \\`$os\\' not recognized 1>&2\n\t\texit 1\n\t\t;;\nesac\nelse\n\n# Here we handle the default operating systems that come with various machines.\n# The value should be what the vendor currently ships out the door with their\n# machine or put another way, the most popular os provided with the machine.\n\n# Note that if you're going to try to match \"-MANUFACTURER\" here (say,\n# \"-sun\"), then you have to tell the case statement up towards the top\n# that MANUFACTURER isn't an operating system.  Otherwise, code above\n# will signal an error saying that MANUFACTURER isn't an operating\n# system, and we'll never get to this point.\n\ncase $basic_machine in\n\tscore-*)\n\t\tos=-elf\n\t\t;;\n\tspu-*)\n\t\tos=-elf\n\t\t;;\n\t*-acorn)\n\t\tos=-riscix1.2\n\t\t;;\n\tarm*-rebel)\n\t\tos=-linux\n\t\t;;\n\tarm*-semi)\n\t\tos=-aout\n\t\t;;\n\tc4x-* | tic4x-*)\n\t\tos=-coff\n\t\t;;\n\tc8051-*)\n\t\tos=-elf\n\t\t;;\n\thexagon-*)\n\t\tos=-elf\n\t\t;;\n\ttic54x-*)\n\t\tos=-coff\n\t\t;;\n\ttic55x-*)\n\t\tos=-coff\n\t\t;;\n\ttic6x-*)\n\t\tos=-coff\n\t\t;;\n\t# This must come before the *-dec entry.\n\tpdp10-*)\n\t\tos=-tops20\n\t\t;;\n\tpdp11-*)\n\t\tos=-none\n\t\t;;\n\t*-dec | vax-*)\n\t\tos=-ultrix4.2\n\t\t;;\n\tm68*-apollo)\n\t\tos=-domain\n\t\t;;\n\ti386-sun)\n\t\tos=-sunos4.0.2\n\t\t;;\n\tm68000-sun)\n\t\tos=-sunos3\n\t\t;;\n\tm68*-cisco)\n\t\tos=-aout\n\t\t;;\n\tmep-*)\n\t\tos=-elf\n\t\t;;\n\tmips*-cisco)\n\t\tos=-elf\n\t\t;;\n\tmips*-*)\n\t\tos=-elf\n\t\t;;\n\tor32-*)\n\t\tos=-coff\n\t\t;;\n\t*-tti)\t# must be before sparc entry or we get the wrong os.\n\t\tos=-sysv3\n\t\t;;\n\tsparc-* | *-sun)\n\t\tos=-sunos4.1.1\n\t\t;;\n\t*-be)\n\t\tos=-beos\n\t\t;;\n\t*-haiku)\n\t\tos=-haiku\n\t\t;;\n\t*-ibm)\n\t\tos=-aix\n\t\t;;\n\t*-knuth)\n\t\tos=-mmixware\n\t\t;;\n\t*-wec)\n\t\tos=-proelf\n\t\t;;\n\t*-winbond)\n\t\tos=-proelf\n\t\t;;\n\t*-oki)\n\t\tos=-proelf\n\t\t;;\n\t*-hp)\n\t\tos=-hpux\n\t\t;;\n\t*-hitachi)\n\t\tos=-hiux\n\t\t;;\n\ti860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)\n\t\tos=-sysv\n\t\t;;\n\t*-cbm)\n\t\tos=-amigaos\n\t\t;;\n\t*-dg)\n\t\tos=-dgux\n\t\t;;\n\t*-dolphin)\n\t\tos=-sysv3\n\t\t;;\n\tm68k-ccur)\n\t\tos=-rtu\n\t\t;;\n\tm88k-omron*)\n\t\tos=-luna\n\t\t;;\n\t*-next )\n\t\tos=-nextstep\n\t\t;;\n\t*-sequent)\n\t\tos=-ptx\n\t\t;;\n\t*-crds)\n\t\tos=-unos\n\t\t;;\n\t*-ns)\n\t\tos=-genix\n\t\t;;\n\ti370-*)\n\t\tos=-mvs\n\t\t;;\n\t*-next)\n\t\tos=-nextstep3\n\t\t;;\n\t*-gould)\n\t\tos=-sysv\n\t\t;;\n\t*-highlevel)\n\t\tos=-bsd\n\t\t;;\n\t*-encore)\n\t\tos=-bsd\n\t\t;;\n\t*-sgi)\n\t\tos=-irix\n\t\t;;\n\t*-siemens)\n\t\tos=-sysv4\n\t\t;;\n\t*-masscomp)\n\t\tos=-rtu\n\t\t;;\n\tf30[01]-fujitsu | f700-fujitsu)\n\t\tos=-uxpv\n\t\t;;\n\t*-rom68k)\n\t\tos=-coff\n\t\t;;\n\t*-*bug)\n\t\tos=-coff\n\t\t;;\n\t*-apple)\n\t\tos=-macos\n\t\t;;\n\t*-atari*)\n\t\tos=-mint\n\t\t;;\n\t*)\n\t\tos=-none\n\t\t;;\nesac\nfi\n\n# Here we handle the case where we know the os, and the CPU type, but not the\n# manufacturer.  We pick the logical manufacturer.\nvendor=unknown\ncase $basic_machine in\n\t*-unknown)\n\t\tcase $os in\n\t\t\t-riscix*)\n\t\t\t\tvendor=acorn\n\t\t\t\t;;\n\t\t\t-sunos*)\n\t\t\t\tvendor=sun\n\t\t\t\t;;\n\t\t\t-cnk*|-aix*)\n\t\t\t\tvendor=ibm\n\t\t\t\t;;\n\t\t\t-beos*)\n\t\t\t\tvendor=be\n\t\t\t\t;;\n\t\t\t-hpux*)\n\t\t\t\tvendor=hp\n\t\t\t\t;;\n\t\t\t-mpeix*)\n\t\t\t\tvendor=hp\n\t\t\t\t;;\n\t\t\t-hiux*)\n\t\t\t\tvendor=hitachi\n\t\t\t\t;;\n\t\t\t-unos*)\n\t\t\t\tvendor=crds\n\t\t\t\t;;\n\t\t\t-dgux*)\n\t\t\t\tvendor=dg\n\t\t\t\t;;\n\t\t\t-luna*)\n\t\t\t\tvendor=omron\n\t\t\t\t;;\n\t\t\t-genix*)\n\t\t\t\tvendor=ns\n\t\t\t\t;;\n\t\t\t-mvs* | -opened*)\n\t\t\t\tvendor=ibm\n\t\t\t\t;;\n\t\t\t-os400*)\n\t\t\t\tvendor=ibm\n\t\t\t\t;;\n\t\t\t-ptx*)\n\t\t\t\tvendor=sequent\n\t\t\t\t;;\n\t\t\t-tpf*)\n\t\t\t\tvendor=ibm\n\t\t\t\t;;\n\t\t\t-vxsim* | -vxworks* | -windiss*)\n\t\t\t\tvendor=wrs\n\t\t\t\t;;\n\t\t\t-aux*)\n\t\t\t\tvendor=apple\n\t\t\t\t;;\n\t\t\t-hms*)\n\t\t\t\tvendor=hitachi\n\t\t\t\t;;\n\t\t\t-mpw* | -macos*)\n\t\t\t\tvendor=apple\n\t\t\t\t;;\n\t\t\t-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)\n\t\t\t\tvendor=atari\n\t\t\t\t;;\n\t\t\t-vos*)\n\t\t\t\tvendor=stratus\n\t\t\t\t;;\n\t\tesac\n\t\tbasic_machine=`echo $basic_machine | sed \"s/unknown/$vendor/\"`\n\t\t;;\nesac\n\necho $basic_machine$os\nexit\n\n# Local variables:\n# eval: (add-hook 'write-file-hooks 'time-stamp)\n# time-stamp-start: \"timestamp='\"\n# time-stamp-format: \"%:y-%02m-%02d\"\n# time-stamp-end: \"'\"\n# End:\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/configure",
    "content": "#! /bin/sh\n# Guess values for system-dependent variables and create Makefiles.\n# Generated by GNU Autoconf 2.69 for zookeeper C client 3.7.0.\n#\n# Report bugs to <user@zookeeper.apache.org>.\n#\n#\n# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.\n#\n#\n# This configure script is free software; the Free Software Foundation\n# gives unlimited permission to copy, distribute and modify it.\n## -------------------- ##\n## M4sh Initialization. ##\n## -------------------- ##\n\n# Be more Bourne compatible\nDUALCASE=1; export DUALCASE # for MKS sh\nif test -n \"${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :\n  emulate sh\n  NULLCMD=:\n  # Pre-4.2 versions of Zsh do word splitting on ${1+\"$@\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '${1+\"$@\"}'='\"$@\"'\n  setopt NO_GLOB_SUBST\nelse\n  case `(set -o) 2>/dev/null` in #(\n  *posix*) :\n    set -o posix ;; #(\n  *) :\n     ;;\nesac\nfi\n\n\nas_nl='\n'\nexport as_nl\n# Printing a long string crashes Solaris 7 /usr/bin/printf.\nas_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo\n# Prefer a ksh shell builtin over an external printf program on Solaris,\n# but without wasting forks for bash or zsh.\nif test -z \"$BASH_VERSION$ZSH_VERSION\" \\\n    && (test \"X`print -r -- $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='print -r --'\n  as_echo_n='print -rn --'\nelif (test \"X`printf %s $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='printf %s\\n'\n  as_echo_n='printf %s'\nelse\n  if test \"X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`\" = \"X-n $as_echo\"; then\n    as_echo_body='eval /usr/ucb/echo -n \"$1$as_nl\"'\n    as_echo_n='/usr/ucb/echo -n'\n  else\n    as_echo_body='eval expr \"X$1\" : \"X\\\\(.*\\\\)\"'\n    as_echo_n_body='eval\n      arg=$1;\n      case $arg in #(\n      *\"$as_nl\"*)\n\texpr \"X$arg\" : \"X\\\\(.*\\\\)$as_nl\";\n\targ=`expr \"X$arg\" : \".*$as_nl\\\\(.*\\\\)\"`;;\n      esac;\n      expr \"X$arg\" : \"X\\\\(.*\\\\)\" | tr -d \"$as_nl\"\n    '\n    export as_echo_n_body\n    as_echo_n='sh -c $as_echo_n_body as_echo'\n  fi\n  export as_echo_body\n  as_echo='sh -c $as_echo_body as_echo'\nfi\n\n# The user is always right.\nif test \"${PATH_SEPARATOR+set}\" != set; then\n  PATH_SEPARATOR=:\n  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {\n    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||\n      PATH_SEPARATOR=';'\n  }\nfi\n\n\n# IFS\n# We need space, tab and new line, in precisely that order.  Quoting is\n# there to prevent editors from complaining about space-tab.\n# (If _AS_PATH_WALK were called with IFS unset, it would disable word\n# splitting by setting IFS to empty value.)\nIFS=\" \"\"\t$as_nl\"\n\n# Find who we are.  Look in the path if we contain no directory separator.\nas_myself=\ncase $0 in #((\n  *[\\\\/]* ) as_myself=$0 ;;\n  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    test -r \"$as_dir/$0\" && as_myself=$as_dir/$0 && break\n  done\nIFS=$as_save_IFS\n\n     ;;\nesac\n# We did not find ourselves, most probably we were run as `sh COMMAND'\n# in which case we are not to be found in the path.\nif test \"x$as_myself\" = x; then\n  as_myself=$0\nfi\nif test ! -f \"$as_myself\"; then\n  $as_echo \"$as_myself: error: cannot find myself; rerun with an absolute file name\" >&2\n  exit 1\nfi\n\n# Unset variables that we do not need and which cause bugs (e.g. in\n# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the \"|| exit 1\"\n# suppresses any \"Segmentation fault\" message there.  '((' could\n# trigger a bug in pdksh 5.2.14.\nfor as_var in BASH_ENV ENV MAIL MAILPATH\ndo eval test x\\${$as_var+set} = xset \\\n  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :\ndone\nPS1='$ '\nPS2='> '\nPS4='+ '\n\n# NLS nuisances.\nLC_ALL=C\nexport LC_ALL\nLANGUAGE=C\nexport LANGUAGE\n\n# CDPATH.\n(unset CDPATH) >/dev/null 2>&1 && unset CDPATH\n\n# Use a proper internal environment variable to ensure we don't fall\n  # into an infinite loop, continuously re-executing ourselves.\n  if test x\"${_as_can_reexec}\" != xno && test \"x$CONFIG_SHELL\" != x; then\n    _as_can_reexec=no; export _as_can_reexec;\n    # We cannot yet assume a decent shell, so we have to provide a\n# neutralization value for shells without unset; and this also\n# works around shells that cannot unset nonexistent variables.\n# Preserve -v and -x to the replacement shell.\nBASH_ENV=/dev/null\nENV=/dev/null\n(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV\ncase $- in # ((((\n  *v*x* | *x*v* ) as_opts=-vx ;;\n  *v* ) as_opts=-v ;;\n  *x* ) as_opts=-x ;;\n  * ) as_opts= ;;\nesac\nexec $CONFIG_SHELL $as_opts \"$as_myself\" ${1+\"$@\"}\n# Admittedly, this is quite paranoid, since all the known shells bail\n# out after a failed `exec'.\n$as_echo \"$0: could not re-execute with $CONFIG_SHELL\" >&2\nas_fn_exit 255\n  fi\n  # We don't want this to propagate to other subprocesses.\n          { _as_can_reexec=; unset _as_can_reexec;}\nif test \"x$CONFIG_SHELL\" = x; then\n  as_bourne_compatible=\"if test -n \\\"\\${ZSH_VERSION+set}\\\" && (emulate sh) >/dev/null 2>&1; then :\n  emulate sh\n  NULLCMD=:\n  # Pre-4.2 versions of Zsh do word splitting on \\${1+\\\"\\$@\\\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '\\${1+\\\"\\$@\\\"}'='\\\"\\$@\\\"'\n  setopt NO_GLOB_SUBST\nelse\n  case \\`(set -o) 2>/dev/null\\` in #(\n  *posix*) :\n    set -o posix ;; #(\n  *) :\n     ;;\nesac\nfi\n\"\n  as_required=\"as_fn_return () { (exit \\$1); }\nas_fn_success () { as_fn_return 0; }\nas_fn_failure () { as_fn_return 1; }\nas_fn_ret_success () { return 0; }\nas_fn_ret_failure () { return 1; }\n\nexitcode=0\nas_fn_success || { exitcode=1; echo as_fn_success failed.; }\nas_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }\nas_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }\nas_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }\nif ( set x; as_fn_ret_success y && test x = \\\"\\$1\\\" ); then :\n\nelse\n  exitcode=1; echo positional parameters were not saved.\nfi\ntest x\\$exitcode = x0 || exit 1\ntest -x / || exit 1\"\n  as_suggested=\"  as_lineno_1=\";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested\" as_lineno_1a=\\$LINENO\n  as_lineno_2=\";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested\" as_lineno_2a=\\$LINENO\n  eval 'test \\\"x\\$as_lineno_1'\\$as_run'\\\" != \\\"x\\$as_lineno_2'\\$as_run'\\\" &&\n  test \\\"x\\`expr \\$as_lineno_1'\\$as_run' + 1\\`\\\" = \\\"x\\$as_lineno_2'\\$as_run'\\\"' || exit 1\ntest \\$(( 1 + 1 )) = 2 || exit 1\n\n  test -n \\\"\\${ZSH_VERSION+set}\\${BASH_VERSION+set}\\\" || (\n    ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'\n    ECHO=\\$ECHO\\$ECHO\\$ECHO\\$ECHO\\$ECHO\n    ECHO=\\$ECHO\\$ECHO\\$ECHO\\$ECHO\\$ECHO\\$ECHO\n    PATH=/empty FPATH=/empty; export PATH FPATH\n    test \\\"X\\`printf %s \\$ECHO\\`\\\" = \\\"X\\$ECHO\\\" \\\\\n      || test \\\"X\\`print -r -- \\$ECHO\\`\\\" = \\\"X\\$ECHO\\\" ) || exit 1\"\n  if (eval \"$as_required\") 2>/dev/null; then :\n  as_have_required=yes\nelse\n  as_have_required=no\nfi\n  if test x$as_have_required = xyes && (eval \"$as_suggested\") 2>/dev/null; then :\n\nelse\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nas_found=false\nfor as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n  as_found=:\n  case $as_dir in #(\n\t /*)\n\t   for as_base in sh bash ksh sh5; do\n\t     # Try only shells that exist, to save several forks.\n\t     as_shell=$as_dir/$as_base\n\t     if { test -f \"$as_shell\" || test -f \"$as_shell.exe\"; } &&\n\t\t    { $as_echo \"$as_bourne_compatible\"\"$as_required\" | as_run=a \"$as_shell\"; } 2>/dev/null; then :\n  CONFIG_SHELL=$as_shell as_have_required=yes\n\t\t   if { $as_echo \"$as_bourne_compatible\"\"$as_suggested\" | as_run=a \"$as_shell\"; } 2>/dev/null; then :\n  break 2\nfi\nfi\n\t   done;;\n       esac\n  as_found=false\ndone\n$as_found || { if { test -f \"$SHELL\" || test -f \"$SHELL.exe\"; } &&\n\t      { $as_echo \"$as_bourne_compatible\"\"$as_required\" | as_run=a \"$SHELL\"; } 2>/dev/null; then :\n  CONFIG_SHELL=$SHELL as_have_required=yes\nfi; }\nIFS=$as_save_IFS\n\n\n      if test \"x$CONFIG_SHELL\" != x; then :\n  export CONFIG_SHELL\n             # We cannot yet assume a decent shell, so we have to provide a\n# neutralization value for shells without unset; and this also\n# works around shells that cannot unset nonexistent variables.\n# Preserve -v and -x to the replacement shell.\nBASH_ENV=/dev/null\nENV=/dev/null\n(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV\ncase $- in # ((((\n  *v*x* | *x*v* ) as_opts=-vx ;;\n  *v* ) as_opts=-v ;;\n  *x* ) as_opts=-x ;;\n  * ) as_opts= ;;\nesac\nexec $CONFIG_SHELL $as_opts \"$as_myself\" ${1+\"$@\"}\n# Admittedly, this is quite paranoid, since all the known shells bail\n# out after a failed `exec'.\n$as_echo \"$0: could not re-execute with $CONFIG_SHELL\" >&2\nexit 255\nfi\n\n    if test x$as_have_required = xno; then :\n  $as_echo \"$0: This script requires a shell more modern than all\"\n  $as_echo \"$0: the shells that I found on your system.\"\n  if test x${ZSH_VERSION+set} = xset ; then\n    $as_echo \"$0: In particular, zsh $ZSH_VERSION has bugs and should\"\n    $as_echo \"$0: be upgraded to zsh 4.3.4 or later.\"\n  else\n    $as_echo \"$0: Please tell bug-autoconf@gnu.org and\n$0: user@zookeeper.apache.org about your system, including\n$0: any error possibly output before this message. Then\n$0: install a modern shell, or manually run the script\n$0: under such a shell if you do have one.\"\n  fi\n  exit 1\nfi\nfi\nfi\nSHELL=${CONFIG_SHELL-/bin/sh}\nexport SHELL\n# Unset more variables known to interfere with behavior of common tools.\nCLICOLOR_FORCE= GREP_OPTIONS=\nunset CLICOLOR_FORCE GREP_OPTIONS\n\n## --------------------- ##\n## M4sh Shell Functions. ##\n## --------------------- ##\n# as_fn_unset VAR\n# ---------------\n# Portably unset VAR.\nas_fn_unset ()\n{\n  { eval $1=; unset $1;}\n}\nas_unset=as_fn_unset\n\n# as_fn_set_status STATUS\n# -----------------------\n# Set $? to STATUS, without forking.\nas_fn_set_status ()\n{\n  return $1\n} # as_fn_set_status\n\n# as_fn_exit STATUS\n# -----------------\n# Exit the shell with STATUS, even in a \"trap 0\" or \"set -e\" context.\nas_fn_exit ()\n{\n  set +e\n  as_fn_set_status $1\n  exit $1\n} # as_fn_exit\n\n# as_fn_mkdir_p\n# -------------\n# Create \"$as_dir\" as a directory, including parents if necessary.\nas_fn_mkdir_p ()\n{\n\n  case $as_dir in #(\n  -*) as_dir=./$as_dir;;\n  esac\n  test -d \"$as_dir\" || eval $as_mkdir_p || {\n    as_dirs=\n    while :; do\n      case $as_dir in #(\n      *\\'*) as_qdir=`$as_echo \"$as_dir\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;; #'(\n      *) as_qdir=$as_dir;;\n      esac\n      as_dirs=\"'$as_qdir' $as_dirs\"\n      as_dir=`$as_dirname -- \"$as_dir\" ||\n$as_expr X\"$as_dir\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)$' \\| \\\n\t X\"$as_dir\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$as_dir\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n      test -d \"$as_dir\" && break\n    done\n    test -z \"$as_dirs\" || eval \"mkdir $as_dirs\"\n  } || test -d \"$as_dir\" || as_fn_error $? \"cannot create directory $as_dir\"\n\n\n} # as_fn_mkdir_p\n\n# as_fn_executable_p FILE\n# -----------------------\n# Test if FILE is an executable regular file.\nas_fn_executable_p ()\n{\n  test -f \"$1\" && test -x \"$1\"\n} # as_fn_executable_p\n# as_fn_append VAR VALUE\n# ----------------------\n# Append the text in VALUE to the end of the definition contained in VAR. Take\n# advantage of any shell optimizations that allow amortized linear growth over\n# repeated appends, instead of the typical quadratic growth present in naive\n# implementations.\nif (eval \"as_var=1; as_var+=2; test x\\$as_var = x12\") 2>/dev/null; then :\n  eval 'as_fn_append ()\n  {\n    eval $1+=\\$2\n  }'\nelse\n  as_fn_append ()\n  {\n    eval $1=\\$$1\\$2\n  }\nfi # as_fn_append\n\n# as_fn_arith ARG...\n# ------------------\n# Perform arithmetic evaluation on the ARGs, and store the result in the\n# global $as_val. Take advantage of shells that can avoid forks. The arguments\n# must be portable across $(()) and expr.\nif (eval \"test \\$(( 1 + 1 )) = 2\") 2>/dev/null; then :\n  eval 'as_fn_arith ()\n  {\n    as_val=$(( $* ))\n  }'\nelse\n  as_fn_arith ()\n  {\n    as_val=`expr \"$@\" || test $? -eq 1`\n  }\nfi # as_fn_arith\n\n\n# as_fn_error STATUS ERROR [LINENO LOG_FD]\n# ----------------------------------------\n# Output \"`basename $0`: error: ERROR\" to stderr. If LINENO and LOG_FD are\n# provided, also output the error to LOG_FD, referencing LINENO. Then exit the\n# script with STATUS, using 1 if that was 0.\nas_fn_error ()\n{\n  as_status=$1; test $as_status -eq 0 && as_status=1\n  if test \"$4\"; then\n    as_lineno=${as_lineno-\"$3\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n    $as_echo \"$as_me:${as_lineno-$LINENO}: error: $2\" >&$4\n  fi\n  $as_echo \"$as_me: error: $2\" >&2\n  as_fn_exit $as_status\n} # as_fn_error\n\nif expr a : '\\(a\\)' >/dev/null 2>&1 &&\n   test \"X`expr 00001 : '.*\\(...\\)'`\" = X001; then\n  as_expr=expr\nelse\n  as_expr=false\nfi\n\nif (basename -- /) >/dev/null 2>&1 && test \"X`basename -- / 2>&1`\" = \"X/\"; then\n  as_basename=basename\nelse\n  as_basename=false\nfi\n\nif (as_dir=`dirname -- /` && test \"X$as_dir\" = X/) >/dev/null 2>&1; then\n  as_dirname=dirname\nelse\n  as_dirname=false\nfi\n\nas_me=`$as_basename -- \"$0\" ||\n$as_expr X/\"$0\" : '.*/\\([^/][^/]*\\)/*$' \\| \\\n\t X\"$0\" : 'X\\(//\\)$' \\| \\\n\t X\"$0\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X/\"$0\" |\n    sed '/^.*\\/\\([^/][^/]*\\)\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n\n# Avoid depending upon Character Ranges.\nas_cr_letters='abcdefghijklmnopqrstuvwxyz'\nas_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'\nas_cr_Letters=$as_cr_letters$as_cr_LETTERS\nas_cr_digits='0123456789'\nas_cr_alnum=$as_cr_Letters$as_cr_digits\n\n\n  as_lineno_1=$LINENO as_lineno_1a=$LINENO\n  as_lineno_2=$LINENO as_lineno_2a=$LINENO\n  eval 'test \"x$as_lineno_1'$as_run'\" != \"x$as_lineno_2'$as_run'\" &&\n  test \"x`expr $as_lineno_1'$as_run' + 1`\" = \"x$as_lineno_2'$as_run'\"' || {\n  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)\n  sed -n '\n    p\n    /[$]LINENO/=\n  ' <$as_myself |\n    sed '\n      s/[$]LINENO.*/&-/\n      t lineno\n      b\n      :lineno\n      N\n      :loop\n      s/[$]LINENO\\([^'$as_cr_alnum'_].*\\n\\)\\(.*\\)/\\2\\1\\2/\n      t loop\n      s/-\\n.*//\n    ' >$as_me.lineno &&\n  chmod +x \"$as_me.lineno\" ||\n    { $as_echo \"$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell\" >&2; as_fn_exit 1; }\n\n  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have\n  # already done that, so ensure we don't try to do so again and fall\n  # in an infinite loop.  This has already happened in practice.\n  _as_can_reexec=no; export _as_can_reexec\n  # Don't try to exec as it changes $[0], causing all sort of problems\n  # (the dirname of $[0] is not the place where we might find the\n  # original and so on.  Autoconf is especially sensitive to this).\n  . \"./$as_me.lineno\"\n  # Exit status is that of the last command.\n  exit\n}\n\nECHO_C= ECHO_N= ECHO_T=\ncase `echo -n x` in #(((((\n-n*)\n  case `echo 'xy\\c'` in\n  *c*) ECHO_T='\t';;\t# ECHO_T is single tab character.\n  xy)  ECHO_C='\\c';;\n  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null\n       ECHO_T='\t';;\n  esac;;\n*)\n  ECHO_N='-n';;\nesac\n\nrm -f conf$$ conf$$.exe conf$$.file\nif test -d conf$$.dir; then\n  rm -f conf$$.dir/conf$$.file\nelse\n  rm -f conf$$.dir\n  mkdir conf$$.dir 2>/dev/null\nfi\nif (echo >conf$$.file) 2>/dev/null; then\n  if ln -s conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s='ln -s'\n    # ... but there are two gotchas:\n    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.\n    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.\n    # In both cases, we have to default to `cp -pR'.\n    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||\n      as_ln_s='cp -pR'\n  elif ln conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s=ln\n  else\n    as_ln_s='cp -pR'\n  fi\nelse\n  as_ln_s='cp -pR'\nfi\nrm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file\nrmdir conf$$.dir 2>/dev/null\n\nif mkdir -p . 2>/dev/null; then\n  as_mkdir_p='mkdir -p \"$as_dir\"'\nelse\n  test -d ./-p && rmdir ./-p\n  as_mkdir_p=false\nfi\n\nas_test_x='test -x'\nas_executable_p=as_fn_executable_p\n\n# Sed expression to map a string onto a valid CPP name.\nas_tr_cpp=\"eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'\"\n\n# Sed expression to map a string onto a valid variable name.\nas_tr_sh=\"eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'\"\n\nSHELL=${CONFIG_SHELL-/bin/sh}\n\n\ntest -n \"$DJDIR\" || exec 7<&0 </dev/null\nexec 6>&1\n\n# Name of the host.\n# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,\n# so uname gets run too.\nac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`\n\n#\n# Initializations.\n#\nac_default_prefix=/usr/local\nac_clean_files=\nac_config_libobj_dir=.\nLIBOBJS=\ncross_compiling=no\nsubdirs=\nMFLAGS=\nMAKEFLAGS=\n\n# Identity of this package.\nPACKAGE_NAME='zookeeper C client'\nPACKAGE_TARNAME='zookeeper'\nPACKAGE_VERSION='3.7.0'\nPACKAGE_STRING='zookeeper C client 3.7.0'\nPACKAGE_BUGREPORT='user@zookeeper.apache.org'\nPACKAGE_URL=''\n\nac_unique_file=\"src/zookeeper.c\"\n# Factoring default headers for most tests.\nac_includes_default=\"\\\n#include <stdio.h>\n#ifdef HAVE_SYS_TYPES_H\n# include <sys/types.h>\n#endif\n#ifdef HAVE_SYS_STAT_H\n# include <sys/stat.h>\n#endif\n#ifdef STDC_HEADERS\n# include <stdlib.h>\n# include <stddef.h>\n#else\n# ifdef HAVE_STDLIB_H\n#  include <stdlib.h>\n# endif\n#endif\n#ifdef HAVE_STRING_H\n# if !defined STDC_HEADERS && defined HAVE_MEMORY_H\n#  include <memory.h>\n# endif\n# include <string.h>\n#endif\n#ifdef HAVE_STRINGS_H\n# include <strings.h>\n#endif\n#ifdef HAVE_INTTYPES_H\n# include <inttypes.h>\n#endif\n#ifdef HAVE_STDINT_H\n# include <stdint.h>\n#endif\n#ifdef HAVE_UNISTD_H\n# include <unistd.h>\n#endif\"\n\nac_subst_vars='am__EXEEXT_FALSE\nam__EXEEXT_TRUE\nLTLIBOBJS\nLIBOBJS\nSOLARIS_FALSE\nSOLARIS_TRUE\nCLOCK_GETTIME_LIBS\nSOCK_CLOEXEC_ENABLED_FALSE\nSOCK_CLOEXEC_ENABLED_TRUE\nUSEIPV6\nWANT_SASL_FALSE\nWANT_SASL_TRUE\nWANT_SYNCAPI_FALSE\nWANT_SYNCAPI_TRUE\nENABLEGCOV_FALSE\nENABLEGCOV_TRUE\nCXXCPP\nLT_SYS_LIBRARY_PATH\nOTOOL64\nOTOOL\nLIPO\nNMEDIT\nDSYMUTIL\nMANIFEST_TOOL\nRANLIB\nac_ct_AR\nAR\nDLLTOOL\nOBJDUMP\nNM\nac_ct_DUMPBIN\nDUMPBIN\nLD\nFGREP\nSED\nhost_os\nhost_vendor\nhost_cpu\nhost\nbuild_os\nbuild_vendor\nbuild_cpu\nbuild\nLIBTOOL\nLN_S\nam__fastdepCXX_FALSE\nam__fastdepCXX_TRUE\nCXXDEPMODE\nac_ct_CXX\nCXXFLAGS\nCXX\nWANT_OPENSSL_FALSE\nWANT_OPENSSL_TRUE\nEGREP\nGREP\nCPP\nam__fastdepCC_FALSE\nam__fastdepCC_TRUE\nCCDEPMODE\nam__nodep\nAMDEPBACKSLASH\nAMDEP_FALSE\nAMDEP_TRUE\nam__quote\nam__include\nDEPDIR\nOBJEXT\nEXEEXT\nac_ct_CC\nCPPFLAGS\nLDFLAGS\nCFLAGS\nCC\nCPPUNIT_LIBS\nCPPUNIT_CFLAGS\nPKG_CONFIG_LIBDIR\nPKG_CONFIG_PATH\nPKG_CONFIG\nAM_BACKSLASH\nAM_DEFAULT_VERBOSITY\nAM_DEFAULT_V\nAM_V\nam__untar\nam__tar\nAMTAR\nam__leading_dot\nSET_MAKE\nAWK\nmkdir_p\nMKDIR_P\nINSTALL_STRIP_PROGRAM\nSTRIP\ninstall_sh\nMAKEINFO\nAUTOHEADER\nAUTOMAKE\nAUTOCONF\nACLOCAL\nVERSION\nPACKAGE\nCYGPATH_W\nam__isrc\nINSTALL_DATA\nINSTALL_SCRIPT\nINSTALL_PROGRAM\nDOXYGEN_PAPER_SIZE\nDX_COND_latex_FALSE\nDX_COND_latex_TRUE\nDX_COND_pdf_FALSE\nDX_COND_pdf_TRUE\nDX_PDFLATEX\nDX_FLAG_pdf\nDX_COND_ps_FALSE\nDX_COND_ps_TRUE\nDX_EGREP\nDX_DVIPS\nDX_MAKEINDEX\nDX_LATEX\nDX_FLAG_ps\nDX_COND_html_FALSE\nDX_COND_html_TRUE\nDX_FLAG_html\nDX_COND_chi_FALSE\nDX_COND_chi_TRUE\nDX_FLAG_chi\nDX_COND_chm_FALSE\nDX_COND_chm_TRUE\nDX_HHC\nDX_FLAG_chm\nDX_COND_xml_FALSE\nDX_COND_xml_TRUE\nDX_FLAG_xml\nDX_COND_rtf_FALSE\nDX_COND_rtf_TRUE\nDX_FLAG_rtf\nDX_COND_man_FALSE\nDX_COND_man_TRUE\nDX_FLAG_man\nDX_COND_dot_FALSE\nDX_COND_dot_TRUE\nDX_DOT\nDX_FLAG_dot\nDX_COND_doc_FALSE\nDX_COND_doc_TRUE\nDX_PERL\nDX_DOXYGEN\nDX_FLAG_doc\nDX_ENV\nDX_DOCDIR\nDX_CONFIG\nDX_PROJECT\ntarget_alias\nhost_alias\nbuild_alias\nLIBS\nECHO_T\nECHO_N\nECHO_C\nDEFS\nmandir\nlocaledir\nlibdir\npsdir\npdfdir\ndvidir\nhtmldir\ninfodir\ndocdir\noldincludedir\nincludedir\nrunstatedir\nlocalstatedir\nsharedstatedir\nsysconfdir\ndatadir\ndatarootdir\nlibexecdir\nsbindir\nbindir\nprogram_transform_name\nprefix\nexec_prefix\nPACKAGE_URL\nPACKAGE_BUGREPORT\nPACKAGE_STRING\nPACKAGE_VERSION\nPACKAGE_TARNAME\nPACKAGE_NAME\nPATH_SEPARATOR\nSHELL'\nac_subst_files=''\nac_user_opts='\nenable_option_checking\nenable_doxygen_doc\nenable_doxygen_dot\nenable_doxygen_man\nenable_doxygen_rtf\nenable_doxygen_xml\nenable_doxygen_chm\nenable_doxygen_chi\nenable_doxygen_html\nenable_doxygen_ps\nenable_doxygen_pdf\nenable_silent_rules\nwith_cppunit\nwith_openssl\nenable_dependency_tracking\nenable_shared\nenable_static\nwith_pic\nenable_fast_install\nwith_aix_soname\nwith_gnu_ld\nwith_sysroot\nenable_libtool_lock\nenable_debug\nenable_gcov\nwith_syncapi\nwith_sasl\nwith_sock_cloexec\n'\n      ac_precious_vars='build_alias\nhost_alias\ntarget_alias\nDOXYGEN_PAPER_SIZE\nPKG_CONFIG\nPKG_CONFIG_PATH\nPKG_CONFIG_LIBDIR\nCPPUNIT_CFLAGS\nCPPUNIT_LIBS\nCC\nCFLAGS\nLDFLAGS\nLIBS\nCPPFLAGS\nCPP\nCXX\nCXXFLAGS\nCCC\nLT_SYS_LIBRARY_PATH\nCXXCPP'\n\n\n# Initialize some variables set by options.\nac_init_help=\nac_init_version=false\nac_unrecognized_opts=\nac_unrecognized_sep=\n# The variables have the same names as the options, with\n# dashes changed to underlines.\ncache_file=/dev/null\nexec_prefix=NONE\nno_create=\nno_recursion=\nprefix=NONE\nprogram_prefix=NONE\nprogram_suffix=NONE\nprogram_transform_name=s,x,x,\nsilent=\nsite=\nsrcdir=\nverbose=\nx_includes=NONE\nx_libraries=NONE\n\n# Installation directory options.\n# These are left unexpanded so users can \"make install exec_prefix=/foo\"\n# and all the variables that are supposed to be based on exec_prefix\n# by default will actually change.\n# Use braces instead of parens because sh, perl, etc. also accept them.\n# (The list follows the same order as the GNU Coding Standards.)\nbindir='${exec_prefix}/bin'\nsbindir='${exec_prefix}/sbin'\nlibexecdir='${exec_prefix}/libexec'\ndatarootdir='${prefix}/share'\ndatadir='${datarootdir}'\nsysconfdir='${prefix}/etc'\nsharedstatedir='${prefix}/com'\nlocalstatedir='${prefix}/var'\nrunstatedir='${localstatedir}/run'\nincludedir='${prefix}/include'\noldincludedir='/usr/include'\ndocdir='${datarootdir}/doc/${PACKAGE_TARNAME}'\ninfodir='${datarootdir}/info'\nhtmldir='${docdir}'\ndvidir='${docdir}'\npdfdir='${docdir}'\npsdir='${docdir}'\nlibdir='${exec_prefix}/lib'\nlocaledir='${datarootdir}/locale'\nmandir='${datarootdir}/man'\n\nac_prev=\nac_dashdash=\nfor ac_option\ndo\n  # If the previous option needs an argument, assign it.\n  if test -n \"$ac_prev\"; then\n    eval $ac_prev=\\$ac_option\n    ac_prev=\n    continue\n  fi\n\n  case $ac_option in\n  *=?*) ac_optarg=`expr \"X$ac_option\" : '[^=]*=\\(.*\\)'` ;;\n  *=)   ac_optarg= ;;\n  *)    ac_optarg=yes ;;\n  esac\n\n  # Accept the important Cygnus configure options, so we can diagnose typos.\n\n  case $ac_dashdash$ac_option in\n  --)\n    ac_dashdash=yes ;;\n\n  -bindir | --bindir | --bindi | --bind | --bin | --bi)\n    ac_prev=bindir ;;\n  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)\n    bindir=$ac_optarg ;;\n\n  -build | --build | --buil | --bui | --bu)\n    ac_prev=build_alias ;;\n  -build=* | --build=* | --buil=* | --bui=* | --bu=*)\n    build_alias=$ac_optarg ;;\n\n  -cache-file | --cache-file | --cache-fil | --cache-fi \\\n  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)\n    ac_prev=cache_file ;;\n  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \\\n  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)\n    cache_file=$ac_optarg ;;\n\n  --config-cache | -C)\n    cache_file=config.cache ;;\n\n  -datadir | --datadir | --datadi | --datad)\n    ac_prev=datadir ;;\n  -datadir=* | --datadir=* | --datadi=* | --datad=*)\n    datadir=$ac_optarg ;;\n\n  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \\\n  | --dataroo | --dataro | --datar)\n    ac_prev=datarootdir ;;\n  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \\\n  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)\n    datarootdir=$ac_optarg ;;\n\n  -disable-* | --disable-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*disable-\\(.*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid feature name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"enable_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval enable_$ac_useropt=no ;;\n\n  -docdir | --docdir | --docdi | --doc | --do)\n    ac_prev=docdir ;;\n  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)\n    docdir=$ac_optarg ;;\n\n  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)\n    ac_prev=dvidir ;;\n  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)\n    dvidir=$ac_optarg ;;\n\n  -enable-* | --enable-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*enable-\\([^=]*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid feature name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"enable_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval enable_$ac_useropt=\\$ac_optarg ;;\n\n  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \\\n  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \\\n  | --exec | --exe | --ex)\n    ac_prev=exec_prefix ;;\n  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \\\n  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \\\n  | --exec=* | --exe=* | --ex=*)\n    exec_prefix=$ac_optarg ;;\n\n  -gas | --gas | --ga | --g)\n    # Obsolete; use --with-gas.\n    with_gas=yes ;;\n\n  -help | --help | --hel | --he | -h)\n    ac_init_help=long ;;\n  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)\n    ac_init_help=recursive ;;\n  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)\n    ac_init_help=short ;;\n\n  -host | --host | --hos | --ho)\n    ac_prev=host_alias ;;\n  -host=* | --host=* | --hos=* | --ho=*)\n    host_alias=$ac_optarg ;;\n\n  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)\n    ac_prev=htmldir ;;\n  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \\\n  | --ht=*)\n    htmldir=$ac_optarg ;;\n\n  -includedir | --includedir | --includedi | --included | --include \\\n  | --includ | --inclu | --incl | --inc)\n    ac_prev=includedir ;;\n  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \\\n  | --includ=* | --inclu=* | --incl=* | --inc=*)\n    includedir=$ac_optarg ;;\n\n  -infodir | --infodir | --infodi | --infod | --info | --inf)\n    ac_prev=infodir ;;\n  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)\n    infodir=$ac_optarg ;;\n\n  -libdir | --libdir | --libdi | --libd)\n    ac_prev=libdir ;;\n  -libdir=* | --libdir=* | --libdi=* | --libd=*)\n    libdir=$ac_optarg ;;\n\n  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \\\n  | --libexe | --libex | --libe)\n    ac_prev=libexecdir ;;\n  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \\\n  | --libexe=* | --libex=* | --libe=*)\n    libexecdir=$ac_optarg ;;\n\n  -localedir | --localedir | --localedi | --localed | --locale)\n    ac_prev=localedir ;;\n  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)\n    localedir=$ac_optarg ;;\n\n  -localstatedir | --localstatedir | --localstatedi | --localstated \\\n  | --localstate | --localstat | --localsta | --localst | --locals)\n    ac_prev=localstatedir ;;\n  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \\\n  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)\n    localstatedir=$ac_optarg ;;\n\n  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)\n    ac_prev=mandir ;;\n  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)\n    mandir=$ac_optarg ;;\n\n  -nfp | --nfp | --nf)\n    # Obsolete; use --without-fp.\n    with_fp=no ;;\n\n  -no-create | --no-create | --no-creat | --no-crea | --no-cre \\\n  | --no-cr | --no-c | -n)\n    no_create=yes ;;\n\n  -no-recursion | --no-recursion | --no-recursio | --no-recursi \\\n  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)\n    no_recursion=yes ;;\n\n  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \\\n  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \\\n  | --oldin | --oldi | --old | --ol | --o)\n    ac_prev=oldincludedir ;;\n  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \\\n  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \\\n  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)\n    oldincludedir=$ac_optarg ;;\n\n  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)\n    ac_prev=prefix ;;\n  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)\n    prefix=$ac_optarg ;;\n\n  -program-prefix | --program-prefix | --program-prefi | --program-pref \\\n  | --program-pre | --program-pr | --program-p)\n    ac_prev=program_prefix ;;\n  -program-prefix=* | --program-prefix=* | --program-prefi=* \\\n  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)\n    program_prefix=$ac_optarg ;;\n\n  -program-suffix | --program-suffix | --program-suffi | --program-suff \\\n  | --program-suf | --program-su | --program-s)\n    ac_prev=program_suffix ;;\n  -program-suffix=* | --program-suffix=* | --program-suffi=* \\\n  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)\n    program_suffix=$ac_optarg ;;\n\n  -program-transform-name | --program-transform-name \\\n  | --program-transform-nam | --program-transform-na \\\n  | --program-transform-n | --program-transform- \\\n  | --program-transform | --program-transfor \\\n  | --program-transfo | --program-transf \\\n  | --program-trans | --program-tran \\\n  | --progr-tra | --program-tr | --program-t)\n    ac_prev=program_transform_name ;;\n  -program-transform-name=* | --program-transform-name=* \\\n  | --program-transform-nam=* | --program-transform-na=* \\\n  | --program-transform-n=* | --program-transform-=* \\\n  | --program-transform=* | --program-transfor=* \\\n  | --program-transfo=* | --program-transf=* \\\n  | --program-trans=* | --program-tran=* \\\n  | --progr-tra=* | --program-tr=* | --program-t=*)\n    program_transform_name=$ac_optarg ;;\n\n  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)\n    ac_prev=pdfdir ;;\n  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)\n    pdfdir=$ac_optarg ;;\n\n  -psdir | --psdir | --psdi | --psd | --ps)\n    ac_prev=psdir ;;\n  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)\n    psdir=$ac_optarg ;;\n\n  -q | -quiet | --quiet | --quie | --qui | --qu | --q \\\n  | -silent | --silent | --silen | --sile | --sil)\n    silent=yes ;;\n\n  -runstatedir | --runstatedir | --runstatedi | --runstated \\\n  | --runstate | --runstat | --runsta | --runst | --runs \\\n  | --run | --ru | --r)\n    ac_prev=runstatedir ;;\n  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \\\n  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \\\n  | --run=* | --ru=* | --r=*)\n    runstatedir=$ac_optarg ;;\n\n  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)\n    ac_prev=sbindir ;;\n  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \\\n  | --sbi=* | --sb=*)\n    sbindir=$ac_optarg ;;\n\n  -sharedstatedir | --sharedstatedir | --sharedstatedi \\\n  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \\\n  | --sharedst | --shareds | --shared | --share | --shar \\\n  | --sha | --sh)\n    ac_prev=sharedstatedir ;;\n  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \\\n  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \\\n  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \\\n  | --sha=* | --sh=*)\n    sharedstatedir=$ac_optarg ;;\n\n  -site | --site | --sit)\n    ac_prev=site ;;\n  -site=* | --site=* | --sit=*)\n    site=$ac_optarg ;;\n\n  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)\n    ac_prev=srcdir ;;\n  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)\n    srcdir=$ac_optarg ;;\n\n  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \\\n  | --syscon | --sysco | --sysc | --sys | --sy)\n    ac_prev=sysconfdir ;;\n  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \\\n  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)\n    sysconfdir=$ac_optarg ;;\n\n  -target | --target | --targe | --targ | --tar | --ta | --t)\n    ac_prev=target_alias ;;\n  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)\n    target_alias=$ac_optarg ;;\n\n  -v | -verbose | --verbose | --verbos | --verbo | --verb)\n    verbose=yes ;;\n\n  -version | --version | --versio | --versi | --vers | -V)\n    ac_init_version=: ;;\n\n  -with-* | --with-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*with-\\([^=]*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid package name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"with_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval with_$ac_useropt=\\$ac_optarg ;;\n\n  -without-* | --without-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*without-\\(.*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid package name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"with_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval with_$ac_useropt=no ;;\n\n  --x)\n    # Obsolete; use --with-x.\n    with_x=yes ;;\n\n  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \\\n  | --x-incl | --x-inc | --x-in | --x-i)\n    ac_prev=x_includes ;;\n  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \\\n  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)\n    x_includes=$ac_optarg ;;\n\n  -x-libraries | --x-libraries | --x-librarie | --x-librari \\\n  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)\n    ac_prev=x_libraries ;;\n  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \\\n  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)\n    x_libraries=$ac_optarg ;;\n\n  -*) as_fn_error $? \"unrecognized option: \\`$ac_option'\nTry \\`$0 --help' for more information\"\n    ;;\n\n  *=*)\n    ac_envvar=`expr \"x$ac_option\" : 'x\\([^=]*\\)='`\n    # Reject names that are not valid shell variable names.\n    case $ac_envvar in #(\n      '' | [0-9]* | *[!_$as_cr_alnum]* )\n      as_fn_error $? \"invalid variable name: \\`$ac_envvar'\" ;;\n    esac\n    eval $ac_envvar=\\$ac_optarg\n    export $ac_envvar ;;\n\n  *)\n    # FIXME: should be removed in autoconf 3.0.\n    $as_echo \"$as_me: WARNING: you should use --build, --host, --target\" >&2\n    expr \"x$ac_option\" : \".*[^-._$as_cr_alnum]\" >/dev/null &&\n      $as_echo \"$as_me: WARNING: invalid host type: $ac_option\" >&2\n    : \"${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}\"\n    ;;\n\n  esac\ndone\n\nif test -n \"$ac_prev\"; then\n  ac_option=--`echo $ac_prev | sed 's/_/-/g'`\n  as_fn_error $? \"missing argument to $ac_option\"\nfi\n\nif test -n \"$ac_unrecognized_opts\"; then\n  case $enable_option_checking in\n    no) ;;\n    fatal) as_fn_error $? \"unrecognized options: $ac_unrecognized_opts\" ;;\n    *)     $as_echo \"$as_me: WARNING: unrecognized options: $ac_unrecognized_opts\" >&2 ;;\n  esac\nfi\n\n# Check all directory arguments for consistency.\nfor ac_var in\texec_prefix prefix bindir sbindir libexecdir datarootdir \\\n\t\tdatadir sysconfdir sharedstatedir localstatedir includedir \\\n\t\toldincludedir docdir infodir htmldir dvidir pdfdir psdir \\\n\t\tlibdir localedir mandir runstatedir\ndo\n  eval ac_val=\\$$ac_var\n  # Remove trailing slashes.\n  case $ac_val in\n    */ )\n      ac_val=`expr \"X$ac_val\" : 'X\\(.*[^/]\\)' \\| \"X$ac_val\" : 'X\\(.*\\)'`\n      eval $ac_var=\\$ac_val;;\n  esac\n  # Be sure to have absolute directory names.\n  case $ac_val in\n    [\\\\/$]* | ?:[\\\\/]* )  continue;;\n    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;\n  esac\n  as_fn_error $? \"expected an absolute directory name for --$ac_var: $ac_val\"\ndone\n\n# There might be people who depend on the old broken behavior: `$host'\n# used to hold the argument of --host etc.\n# FIXME: To remove some day.\nbuild=$build_alias\nhost=$host_alias\ntarget=$target_alias\n\n# FIXME: To remove some day.\nif test \"x$host_alias\" != x; then\n  if test \"x$build_alias\" = x; then\n    cross_compiling=maybe\n  elif test \"x$build_alias\" != \"x$host_alias\"; then\n    cross_compiling=yes\n  fi\nfi\n\nac_tool_prefix=\ntest -n \"$host_alias\" && ac_tool_prefix=$host_alias-\n\ntest \"$silent\" = yes && exec 6>/dev/null\n\n\nac_pwd=`pwd` && test -n \"$ac_pwd\" &&\nac_ls_di=`ls -di .` &&\nac_pwd_ls_di=`cd \"$ac_pwd\" && ls -di .` ||\n  as_fn_error $? \"working directory cannot be determined\"\ntest \"X$ac_ls_di\" = \"X$ac_pwd_ls_di\" ||\n  as_fn_error $? \"pwd does not report name of working directory\"\n\n\n# Find the source files, if location was not specified.\nif test -z \"$srcdir\"; then\n  ac_srcdir_defaulted=yes\n  # Try the directory containing this script, then the parent directory.\n  ac_confdir=`$as_dirname -- \"$as_myself\" ||\n$as_expr X\"$as_myself\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$as_myself\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$as_myself\" : 'X\\(//\\)$' \\| \\\n\t X\"$as_myself\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$as_myself\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n  srcdir=$ac_confdir\n  if test ! -r \"$srcdir/$ac_unique_file\"; then\n    srcdir=..\n  fi\nelse\n  ac_srcdir_defaulted=no\nfi\nif test ! -r \"$srcdir/$ac_unique_file\"; then\n  test \"$ac_srcdir_defaulted\" = yes && srcdir=\"$ac_confdir or ..\"\n  as_fn_error $? \"cannot find sources ($ac_unique_file) in $srcdir\"\nfi\nac_msg=\"sources are in $srcdir, but \\`cd $srcdir' does not work\"\nac_abs_confdir=`(\n\tcd \"$srcdir\" && test -r \"./$ac_unique_file\" || as_fn_error $? \"$ac_msg\"\n\tpwd)`\n# When building in place, set srcdir=.\nif test \"$ac_abs_confdir\" = \"$ac_pwd\"; then\n  srcdir=.\nfi\n# Remove unnecessary trailing slashes from srcdir.\n# Double slashes in file names in object file debugging info\n# mess up M-x gdb in Emacs.\ncase $srcdir in\n*/) srcdir=`expr \"X$srcdir\" : 'X\\(.*[^/]\\)' \\| \"X$srcdir\" : 'X\\(.*\\)'`;;\nesac\nfor ac_var in $ac_precious_vars; do\n  eval ac_env_${ac_var}_set=\\${${ac_var}+set}\n  eval ac_env_${ac_var}_value=\\$${ac_var}\n  eval ac_cv_env_${ac_var}_set=\\${${ac_var}+set}\n  eval ac_cv_env_${ac_var}_value=\\$${ac_var}\ndone\n\n#\n# Report the --help message.\n#\nif test \"$ac_init_help\" = \"long\"; then\n  # Omit some internal or obsolete options to make the list less imposing.\n  # This message is too long to be a string in the A/UX 3.1 sh.\n  cat <<_ACEOF\n\\`configure' configures zookeeper C client 3.7.0 to adapt to many kinds of systems.\n\nUsage: $0 [OPTION]... [VAR=VALUE]...\n\nTo assign environment variables (e.g., CC, CFLAGS...), specify them as\nVAR=VALUE.  See below for descriptions of some of the useful variables.\n\nDefaults for the options are specified in brackets.\n\nConfiguration:\n  -h, --help              display this help and exit\n      --help=short        display options specific to this package\n      --help=recursive    display the short help of all the included packages\n  -V, --version           display version information and exit\n  -q, --quiet, --silent   do not print \\`checking ...' messages\n      --cache-file=FILE   cache test results in FILE [disabled]\n  -C, --config-cache      alias for \\`--cache-file=config.cache'\n  -n, --no-create         do not create output files\n      --srcdir=DIR        find the sources in DIR [configure dir or \\`..']\n\nInstallation directories:\n  --prefix=PREFIX         install architecture-independent files in PREFIX\n                          [$ac_default_prefix]\n  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX\n                          [PREFIX]\n\nBy default, \\`make install' will install all the files in\n\\`$ac_default_prefix/bin', \\`$ac_default_prefix/lib' etc.  You can specify\nan installation prefix other than \\`$ac_default_prefix' using \\`--prefix',\nfor instance \\`--prefix=\\$HOME'.\n\nFor better control, use the options below.\n\nFine tuning of the installation directories:\n  --bindir=DIR            user executables [EPREFIX/bin]\n  --sbindir=DIR           system admin executables [EPREFIX/sbin]\n  --libexecdir=DIR        program executables [EPREFIX/libexec]\n  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]\n  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]\n  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]\n  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]\n  --libdir=DIR            object code libraries [EPREFIX/lib]\n  --includedir=DIR        C header files [PREFIX/include]\n  --oldincludedir=DIR     C header files for non-gcc [/usr/include]\n  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]\n  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]\n  --infodir=DIR           info documentation [DATAROOTDIR/info]\n  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]\n  --mandir=DIR            man documentation [DATAROOTDIR/man]\n  --docdir=DIR            documentation root [DATAROOTDIR/doc/zookeeper]\n  --htmldir=DIR           html documentation [DOCDIR]\n  --dvidir=DIR            dvi documentation [DOCDIR]\n  --pdfdir=DIR            pdf documentation [DOCDIR]\n  --psdir=DIR             ps documentation [DOCDIR]\n_ACEOF\n\n  cat <<\\_ACEOF\n\nProgram names:\n  --program-prefix=PREFIX            prepend PREFIX to installed program names\n  --program-suffix=SUFFIX            append SUFFIX to installed program names\n  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names\n\nSystem types:\n  --build=BUILD     configure for building on BUILD [guessed]\n  --host=HOST       cross-compile to build programs to run on HOST [BUILD]\n_ACEOF\nfi\n\nif test -n \"$ac_init_help\"; then\n  case $ac_init_help in\n     short | recursive ) echo \"Configuration of zookeeper C client 3.7.0:\";;\n   esac\n  cat <<\\_ACEOF\n\nOptional Features:\n  --disable-option-checking  ignore unrecognized --enable/--with options\n  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)\n  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]\n  --disable-doxygen-doc   don't generate any doxygen documentation\n  --disable-doxygen-dot   don't generate graphics for doxygen documentation\n  --enable-doxygen-man    generate doxygen manual pages\n  --enable-doxygen-rtf    generate doxygen RTF documentation\n  --enable-doxygen-xml    generate doxygen XML documentation\n  --enable-doxygen-chm    generate doxygen compressed HTML help documentation\n  --enable-doxygen-chi    generate doxygen seperate compressed HTML help index\n                          file\n  --disable-doxygen-html  don't generate doxygen plain HTML documentation\n  --enable-doxygen-ps     generate doxygen PostScript documentation\n  --enable-doxygen-pdf    generate doxygen PDF documentation\n  --enable-silent-rules   less verbose build output (undo: \"make V=1\")\n  --disable-silent-rules  verbose build output (undo: \"make V=0\")\n  --enable-dependency-tracking\n                          do not reject slow dependency extractors\n  --disable-dependency-tracking\n                          speeds up one-time build\n  --enable-shared[=PKGS]  build shared libraries [default=yes]\n  --enable-static[=PKGS]  build static libraries [default=yes]\n  --enable-fast-install[=PKGS]\n                          optimize for fast installation [default=yes]\n  --disable-libtool-lock  avoid locking (might break parallel builds)\n  --enable-debug          enable debug build [default=no]\n  --enable-gcov           enable coverage test\n\nOptional Packages:\n  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]\n  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)\n  --without-cppunit       do not use CPPUNIT\n  --with-openssl=DIR      build with openssl (autodetect openssl library by\n                          default) )\n  --with-pic[=PKGS]       try to use only PIC/non-PIC objects [default=use\n                          both]\n  --with-aix-soname=aix|svr4|both\n                          shared library versioning (aka \"SONAME\") variant to\n                          provide on AIX, [default=aix].\n  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]\n  --with-sysroot[=DIR]    Search for dependent libraries within DIR (or the\n                          compiler's sysroot if not specified).\n  --with-syncapi          build with support for SyncAPI [default=yes]\n  --with-sasl=DIR         build with SASL support via Cyrus SASL 2.x\n                          (default=auto)\n  --with-sock-cloexec     build with SOCK_CLOEXEC flag set on the connections\n\nSome influential environment variables:\n  DOXYGEN_PAPER_SIZE\n              a4wide (default), a4, letter, legal or executive\n  PKG_CONFIG  path to pkg-config utility\n  PKG_CONFIG_PATH\n              directories to add to pkg-config's search path\n  PKG_CONFIG_LIBDIR\n              path overriding pkg-config's built-in search path\n  CPPUNIT_CFLAGS\n              C compiler flags for CPPUNIT, overriding pkg-config\n  CPPUNIT_LIBS\n              linker flags for CPPUNIT, overriding pkg-config\n  CC          C compiler command\n  CFLAGS      C compiler flags\n  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a\n              nonstandard directory <lib dir>\n  LIBS        libraries to pass to the linker, e.g. -l<library>\n  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if\n              you have headers in a nonstandard directory <include dir>\n  CPP         C preprocessor\n  CXX         C++ compiler command\n  CXXFLAGS    C++ compiler flags\n  LT_SYS_LIBRARY_PATH\n              User-defined run-time library search path.\n  CXXCPP      C++ preprocessor\n\nUse these variables to override the choices made by `configure' or to help\nit to find libraries and programs with nonstandard names/locations.\n\nReport bugs to <user@zookeeper.apache.org>.\n_ACEOF\nac_status=$?\nfi\n\nif test \"$ac_init_help\" = \"recursive\"; then\n  # If there are subdirs, report their specific --help.\n  for ac_dir in : $ac_subdirs_all; do test \"x$ac_dir\" = x: && continue\n    test -d \"$ac_dir\" ||\n      { cd \"$srcdir\" && ac_pwd=`pwd` && srcdir=. && test -d \"$ac_dir\"; } ||\n      continue\n    ac_builddir=.\n\ncase \"$ac_dir\" in\n.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;\n*)\n  ac_dir_suffix=/`$as_echo \"$ac_dir\" | sed 's|^\\.[\\\\/]||'`\n  # A \"..\" for each directory in $ac_dir_suffix.\n  ac_top_builddir_sub=`$as_echo \"$ac_dir_suffix\" | sed 's|/[^\\\\/]*|/..|g;s|/||'`\n  case $ac_top_builddir_sub in\n  \"\") ac_top_builddir_sub=. ac_top_build_prefix= ;;\n  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;\n  esac ;;\nesac\nac_abs_top_builddir=$ac_pwd\nac_abs_builddir=$ac_pwd$ac_dir_suffix\n# for backward compatibility:\nac_top_builddir=$ac_top_build_prefix\n\ncase $srcdir in\n  .)  # We are building in place.\n    ac_srcdir=.\n    ac_top_srcdir=$ac_top_builddir_sub\n    ac_abs_top_srcdir=$ac_pwd ;;\n  [\\\\/]* | ?:[\\\\/]* )  # Absolute name.\n    ac_srcdir=$srcdir$ac_dir_suffix;\n    ac_top_srcdir=$srcdir\n    ac_abs_top_srcdir=$srcdir ;;\n  *) # Relative name.\n    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix\n    ac_top_srcdir=$ac_top_build_prefix$srcdir\n    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;\nesac\nac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix\n\n    cd \"$ac_dir\" || { ac_status=$?; continue; }\n    # Check for guested configure.\n    if test -f \"$ac_srcdir/configure.gnu\"; then\n      echo &&\n      $SHELL \"$ac_srcdir/configure.gnu\" --help=recursive\n    elif test -f \"$ac_srcdir/configure\"; then\n      echo &&\n      $SHELL \"$ac_srcdir/configure\" --help=recursive\n    else\n      $as_echo \"$as_me: WARNING: no configuration information is in $ac_dir\" >&2\n    fi || ac_status=$?\n    cd \"$ac_pwd\" || { ac_status=$?; break; }\n  done\nfi\n\ntest -n \"$ac_init_help\" && exit $ac_status\nif $ac_init_version; then\n  cat <<\\_ACEOF\nzookeeper C client configure 3.7.0\ngenerated by GNU Autoconf 2.69\n\nCopyright (C) 2012 Free Software Foundation, Inc.\nThis configure script is free software; the Free Software Foundation\ngives unlimited permission to copy, distribute and modify it.\n_ACEOF\n  exit\nfi\n\n## ------------------------ ##\n## Autoconf initialization. ##\n## ------------------------ ##\n\n# ac_fn_c_try_compile LINENO\n# --------------------------\n# Try to compile conftest.$ac_ext, and return whether this succeeded.\nac_fn_c_try_compile ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  rm -f conftest.$ac_objext\n  if { { ac_try=\"$ac_compile\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compile\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && {\n\t test -z \"$ac_c_werror_flag\" ||\n\t test ! -s conftest.err\n       } && test -s conftest.$ac_objext; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n\tac_retval=1\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_compile\n\n# ac_fn_c_try_cpp LINENO\n# ----------------------\n# Try to preprocess conftest.$ac_ext, and return whether this succeeded.\nac_fn_c_try_cpp ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if { { ac_try=\"$ac_cpp conftest.$ac_ext\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_cpp conftest.$ac_ext\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } > conftest.i && {\n\t test -z \"$ac_c_preproc_warn_flag$ac_c_werror_flag\" ||\n\t test ! -s conftest.err\n       }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n    ac_retval=1\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_cpp\n\n# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES\n# -------------------------------------------------------\n# Tests whether HEADER exists, giving a warning if it cannot be compiled using\n# the include files in INCLUDES and setting the cache variable VAR\n# accordingly.\nac_fn_c_check_header_mongrel ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if eval \\${$3+:} false; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\nelse\n  # Is the header compilable?\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking $2 usability\" >&5\n$as_echo_n \"checking $2 usability... \" >&6; }\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\n#include <$2>\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_header_compiler=yes\nelse\n  ac_header_compiler=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler\" >&5\n$as_echo \"$ac_header_compiler\" >&6; }\n\n# Is the header present?\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking $2 presence\" >&5\n$as_echo_n \"checking $2 presence... \" >&6; }\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <$2>\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n  ac_header_preproc=yes\nelse\n  ac_header_preproc=no\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc\" >&5\n$as_echo \"$ac_header_preproc\" >&6; }\n\n# So?  What about this header?\ncase $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((\n  yes:no: )\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!\" >&5\n$as_echo \"$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result\" >&5\n$as_echo \"$as_me: WARNING: $2: proceeding with the compiler's result\" >&2;}\n    ;;\n  no:yes:* )\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled\" >&5\n$as_echo \"$as_me: WARNING: $2: present but cannot be compiled\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?\" >&5\n$as_echo \"$as_me: WARNING: $2:     check for missing prerequisite headers?\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation\" >&5\n$as_echo \"$as_me: WARNING: $2: see the Autoconf documentation\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \\\"Present But Cannot Be Compiled\\\"\" >&5\n$as_echo \"$as_me: WARNING: $2:     section \\\"Present But Cannot Be Compiled\\\"\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result\" >&5\n$as_echo \"$as_me: WARNING: $2: proceeding with the compiler's result\" >&2;}\n( $as_echo \"## ---------------------------------------- ##\n## Report this to user@zookeeper.apache.org ##\n## ---------------------------------------- ##\"\n     ) | sed \"s/^/$as_me: WARNING:     /\" >&2\n    ;;\nesac\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  eval \"$3=\\$ac_header_compiler\"\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n\n} # ac_fn_c_check_header_mongrel\n\n# ac_fn_c_try_run LINENO\n# ----------------------\n# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes\n# that executables *can* be run.\nac_fn_c_try_run ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'\n  { { case \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_try\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: program exited with status $ac_status\" >&5\n       $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n       ac_retval=$ac_status\nfi\n  rm -rf conftest.dSYM conftest_ipa8_conftest.oo\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_run\n\n# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES\n# -------------------------------------------------------\n# Tests whether HEADER exists and can be compiled using the include files in\n# INCLUDES, setting the cache variable VAR accordingly.\nac_fn_c_check_header_compile ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\n#include <$2>\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  eval \"$3=yes\"\nelse\n  eval \"$3=no\"\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n\n} # ac_fn_c_check_header_compile\n\n# ac_fn_c_try_link LINENO\n# -----------------------\n# Try to link conftest.$ac_ext, and return whether this succeeded.\nac_fn_c_try_link ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  rm -f conftest.$ac_objext conftest$ac_exeext\n  if { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && {\n\t test -z \"$ac_c_werror_flag\" ||\n\t test ! -s conftest.err\n       } && test -s conftest$ac_exeext && {\n\t test \"$cross_compiling\" = yes ||\n\t test -x conftest$ac_exeext\n       }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n\tac_retval=1\nfi\n  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information\n  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would\n  # interfere with the next link command; also delete a directory that is\n  # left behind by Apple's compiler.  We do this before executing the actions.\n  rm -rf conftest.dSYM conftest_ipa8_conftest.oo\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_link\n\n# ac_fn_cxx_try_compile LINENO\n# ----------------------------\n# Try to compile conftest.$ac_ext, and return whether this succeeded.\nac_fn_cxx_try_compile ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  rm -f conftest.$ac_objext\n  if { { ac_try=\"$ac_compile\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compile\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && {\n\t test -z \"$ac_cxx_werror_flag\" ||\n\t test ! -s conftest.err\n       } && test -s conftest.$ac_objext; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n\tac_retval=1\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_cxx_try_compile\n\n# ac_fn_c_check_func LINENO FUNC VAR\n# ----------------------------------\n# Tests whether FUNC exists, setting the cache variable VAR accordingly\nac_fn_c_check_func ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n/* Define $2 to an innocuous variant, in case <limits.h> declares $2.\n   For example, HP-UX 11i <limits.h> declares gettimeofday.  */\n#define $2 innocuous_$2\n\n/* System header to define __stub macros and hopefully few prototypes,\n    which can conflict with char $2 (); below.\n    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since\n    <limits.h> exists even on freestanding compilers.  */\n\n#ifdef __STDC__\n# include <limits.h>\n#else\n# include <assert.h>\n#endif\n\n#undef $2\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar $2 ();\n/* The GNU C library defines this for functions which it implements\n    to always fail with ENOSYS.  Some functions are actually named\n    something starting with __ and the normal name is an alias.  */\n#if defined __stub_$2 || defined __stub___$2\nchoke me\n#endif\n\nint\nmain ()\n{\nreturn $2 ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  eval \"$3=yes\"\nelse\n  eval \"$3=no\"\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n\n} # ac_fn_c_check_func\n\n# ac_fn_cxx_try_cpp LINENO\n# ------------------------\n# Try to preprocess conftest.$ac_ext, and return whether this succeeded.\nac_fn_cxx_try_cpp ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if { { ac_try=\"$ac_cpp conftest.$ac_ext\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_cpp conftest.$ac_ext\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } > conftest.i && {\n\t test -z \"$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag\" ||\n\t test ! -s conftest.err\n       }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n    ac_retval=1\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_cxx_try_cpp\n\n# ac_fn_cxx_try_link LINENO\n# -------------------------\n# Try to link conftest.$ac_ext, and return whether this succeeded.\nac_fn_cxx_try_link ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  rm -f conftest.$ac_objext conftest$ac_exeext\n  if { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && {\n\t test -z \"$ac_cxx_werror_flag\" ||\n\t test ! -s conftest.err\n       } && test -s conftest$ac_exeext && {\n\t test \"$cross_compiling\" = yes ||\n\t test -x conftest$ac_exeext\n       }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n\tac_retval=1\nfi\n  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information\n  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would\n  # interfere with the next link command; also delete a directory that is\n  # left behind by Apple's compiler.  We do this before executing the actions.\n  rm -rf conftest.dSYM conftest_ipa8_conftest.oo\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_cxx_try_link\n\n# ac_fn_c_check_type LINENO TYPE VAR INCLUDES\n# -------------------------------------------\n# Tests whether TYPE exists after having included INCLUDES, setting cache\n# variable VAR accordingly.\nac_fn_c_check_type ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  eval \"$3=no\"\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nif (sizeof ($2))\n\t return 0;\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nif (sizeof (($2)))\n\t    return 0;\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n\nelse\n  eval \"$3=yes\"\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n\n} # ac_fn_c_check_type\ncat >config.log <<_ACEOF\nThis file contains any messages produced by compilers while\nrunning configure, to aid debugging if configure makes a mistake.\n\nIt was created by zookeeper C client $as_me 3.7.0, which was\ngenerated by GNU Autoconf 2.69.  Invocation command line was\n\n  $ $0 $@\n\n_ACEOF\nexec 5>>config.log\n{\ncat <<_ASUNAME\n## --------- ##\n## Platform. ##\n## --------- ##\n\nhostname = `(hostname || uname -n) 2>/dev/null | sed 1q`\nuname -m = `(uname -m) 2>/dev/null || echo unknown`\nuname -r = `(uname -r) 2>/dev/null || echo unknown`\nuname -s = `(uname -s) 2>/dev/null || echo unknown`\nuname -v = `(uname -v) 2>/dev/null || echo unknown`\n\n/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`\n/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`\n\n/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`\n/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`\n/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`\n/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`\n/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`\n/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`\n/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`\n\n_ASUNAME\n\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    $as_echo \"PATH: $as_dir\"\n  done\nIFS=$as_save_IFS\n\n} >&5\n\ncat >&5 <<_ACEOF\n\n\n## ----------- ##\n## Core tests. ##\n## ----------- ##\n\n_ACEOF\n\n\n# Keep a trace of the command line.\n# Strip out --no-create and --no-recursion so they do not pile up.\n# Strip out --silent because we don't want to record it for future runs.\n# Also quote any args containing shell meta-characters.\n# Make two passes to allow for proper duplicate-argument suppression.\nac_configure_args=\nac_configure_args0=\nac_configure_args1=\nac_must_keep_next=false\nfor ac_pass in 1 2\ndo\n  for ac_arg\n  do\n    case $ac_arg in\n    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;\n    -q | -quiet | --quiet | --quie | --qui | --qu | --q \\\n    | -silent | --silent | --silen | --sile | --sil)\n      continue ;;\n    *\\'*)\n      ac_arg=`$as_echo \"$ac_arg\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    esac\n    case $ac_pass in\n    1) as_fn_append ac_configure_args0 \" '$ac_arg'\" ;;\n    2)\n      as_fn_append ac_configure_args1 \" '$ac_arg'\"\n      if test $ac_must_keep_next = true; then\n\tac_must_keep_next=false # Got value, back to normal.\n      else\n\tcase $ac_arg in\n\t  *=* | --config-cache | -C | -disable-* | --disable-* \\\n\t  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \\\n\t  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \\\n\t  | -with-* | --with-* | -without-* | --without-* | --x)\n\t    case \"$ac_configure_args0 \" in\n\t      \"$ac_configure_args1\"*\" '$ac_arg' \"* ) continue ;;\n\t    esac\n\t    ;;\n\t  -* ) ac_must_keep_next=true ;;\n\tesac\n      fi\n      as_fn_append ac_configure_args \" '$ac_arg'\"\n      ;;\n    esac\n  done\ndone\n{ ac_configure_args0=; unset ac_configure_args0;}\n{ ac_configure_args1=; unset ac_configure_args1;}\n\n# When interrupted or exit'd, cleanup temporary files, and complete\n# config.log.  We remove comments because anyway the quotes in there\n# would cause problems or look ugly.\n# WARNING: Use '\\'' to represent an apostrophe within the trap.\n# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.\ntrap 'exit_status=$?\n  # Save into config.log some information that might help in debugging.\n  {\n    echo\n\n    $as_echo \"## ---------------- ##\n## Cache variables. ##\n## ---------------- ##\"\n    echo\n    # The following way of writing the cache mishandles newlines in values,\n(\n  for ac_var in `(set) 2>&1 | sed -n '\\''s/^\\([a-zA-Z_][a-zA-Z0-9_]*\\)=.*/\\1/p'\\''`; do\n    eval ac_val=\\$$ac_var\n    case $ac_val in #(\n    *${as_nl}*)\n      case $ac_var in #(\n      *_cv_*) { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline\" >&5\n$as_echo \"$as_me: WARNING: cache variable $ac_var contains a newline\" >&2;} ;;\n      esac\n      case $ac_var in #(\n      _ | IFS | as_nl) ;; #(\n      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(\n      *) { eval $ac_var=; unset $ac_var;} ;;\n      esac ;;\n    esac\n  done\n  (set) 2>&1 |\n    case $as_nl`(ac_space='\\'' '\\''; set) 2>&1` in #(\n    *${as_nl}ac_space=\\ *)\n      sed -n \\\n\t\"s/'\\''/'\\''\\\\\\\\'\\'''\\''/g;\n\t  s/^\\\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\\\)=\\\\(.*\\\\)/\\\\1='\\''\\\\2'\\''/p\"\n      ;; #(\n    *)\n      sed -n \"/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p\"\n      ;;\n    esac |\n    sort\n)\n    echo\n\n    $as_echo \"## ----------------- ##\n## Output variables. ##\n## ----------------- ##\"\n    echo\n    for ac_var in $ac_subst_vars\n    do\n      eval ac_val=\\$$ac_var\n      case $ac_val in\n      *\\'\\''*) ac_val=`$as_echo \"$ac_val\" | sed \"s/'\\''/'\\''\\\\\\\\\\\\\\\\'\\'''\\''/g\"`;;\n      esac\n      $as_echo \"$ac_var='\\''$ac_val'\\''\"\n    done | sort\n    echo\n\n    if test -n \"$ac_subst_files\"; then\n      $as_echo \"## ------------------- ##\n## File substitutions. ##\n## ------------------- ##\"\n      echo\n      for ac_var in $ac_subst_files\n      do\n\teval ac_val=\\$$ac_var\n\tcase $ac_val in\n\t*\\'\\''*) ac_val=`$as_echo \"$ac_val\" | sed \"s/'\\''/'\\''\\\\\\\\\\\\\\\\'\\'''\\''/g\"`;;\n\tesac\n\t$as_echo \"$ac_var='\\''$ac_val'\\''\"\n      done | sort\n      echo\n    fi\n\n    if test -s confdefs.h; then\n      $as_echo \"## ----------- ##\n## confdefs.h. ##\n## ----------- ##\"\n      echo\n      cat confdefs.h\n      echo\n    fi\n    test \"$ac_signal\" != 0 &&\n      $as_echo \"$as_me: caught signal $ac_signal\"\n    $as_echo \"$as_me: exit $exit_status\"\n  } >&5\n  rm -f core *.core core.conftest.* &&\n    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&\n    exit $exit_status\n' 0\nfor ac_signal in 1 2 13 15; do\n  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal\ndone\nac_signal=0\n\n# confdefs.h avoids OS command line length limits that DEFS can exceed.\nrm -f -r conftest* confdefs.h\n\n$as_echo \"/* confdefs.h */\" > confdefs.h\n\n# Predefined preprocessor variables.\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_NAME \"$PACKAGE_NAME\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_VERSION \"$PACKAGE_VERSION\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_STRING \"$PACKAGE_STRING\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_URL \"$PACKAGE_URL\"\n_ACEOF\n\n\n# Let the site file select an alternate cache file if it wants to.\n# Prefer an explicitly selected file to automatically selected ones.\nac_site_file1=NONE\nac_site_file2=NONE\nif test -n \"$CONFIG_SITE\"; then\n  # We do not want a PATH search for config.site.\n  case $CONFIG_SITE in #((\n    -*)  ac_site_file1=./$CONFIG_SITE;;\n    */*) ac_site_file1=$CONFIG_SITE;;\n    *)   ac_site_file1=./$CONFIG_SITE;;\n  esac\nelif test \"x$prefix\" != xNONE; then\n  ac_site_file1=$prefix/share/config.site\n  ac_site_file2=$prefix/etc/config.site\nelse\n  ac_site_file1=$ac_default_prefix/share/config.site\n  ac_site_file2=$ac_default_prefix/etc/config.site\nfi\nfor ac_site_file in \"$ac_site_file1\" \"$ac_site_file2\"\ndo\n  test \"x$ac_site_file\" = xNONE && continue\n  if test /dev/null != \"$ac_site_file\" && test -r \"$ac_site_file\"; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file\" >&5\n$as_echo \"$as_me: loading site script $ac_site_file\" >&6;}\n    sed 's/^/| /' \"$ac_site_file\" >&5\n    . \"$ac_site_file\" \\\n      || { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"failed to load site script $ac_site_file\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n  fi\ndone\n\nif test -r \"$cache_file\"; then\n  # Some versions of bash will fail to source /dev/null (special files\n  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.\n  if test /dev/null != \"$cache_file\" && test -f \"$cache_file\"; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: loading cache $cache_file\" >&5\n$as_echo \"$as_me: loading cache $cache_file\" >&6;}\n    case $cache_file in\n      [\\\\/]* | ?:[\\\\/]* ) . \"$cache_file\";;\n      *)                      . \"./$cache_file\";;\n    esac\n  fi\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: creating cache $cache_file\" >&5\n$as_echo \"$as_me: creating cache $cache_file\" >&6;}\n  >$cache_file\nfi\n\n# Check that the precious variables saved in the cache have kept the same\n# value.\nac_cache_corrupted=false\nfor ac_var in $ac_precious_vars; do\n  eval ac_old_set=\\$ac_cv_env_${ac_var}_set\n  eval ac_new_set=\\$ac_env_${ac_var}_set\n  eval ac_old_val=\\$ac_cv_env_${ac_var}_value\n  eval ac_new_val=\\$ac_env_${ac_var}_value\n  case $ac_old_set,$ac_new_set in\n    set,)\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: error: \\`$ac_var' was set to \\`$ac_old_val' in the previous run\" >&5\n$as_echo \"$as_me: error: \\`$ac_var' was set to \\`$ac_old_val' in the previous run\" >&2;}\n      ac_cache_corrupted=: ;;\n    ,set)\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: error: \\`$ac_var' was not set in the previous run\" >&5\n$as_echo \"$as_me: error: \\`$ac_var' was not set in the previous run\" >&2;}\n      ac_cache_corrupted=: ;;\n    ,);;\n    *)\n      if test \"x$ac_old_val\" != \"x$ac_new_val\"; then\n\t# differences in whitespace do not lead to failure.\n\tac_old_val_w=`echo x $ac_old_val`\n\tac_new_val_w=`echo x $ac_new_val`\n\tif test \"$ac_old_val_w\" != \"$ac_new_val_w\"; then\n\t  { $as_echo \"$as_me:${as_lineno-$LINENO}: error: \\`$ac_var' has changed since the previous run:\" >&5\n$as_echo \"$as_me: error: \\`$ac_var' has changed since the previous run:\" >&2;}\n\t  ac_cache_corrupted=:\n\telse\n\t  { $as_echo \"$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \\`$ac_var' since the previous run:\" >&5\n$as_echo \"$as_me: warning: ignoring whitespace changes in \\`$ac_var' since the previous run:\" >&2;}\n\t  eval $ac_var=\\$ac_old_val\n\tfi\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}:   former value:  \\`$ac_old_val'\" >&5\n$as_echo \"$as_me:   former value:  \\`$ac_old_val'\" >&2;}\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}:   current value: \\`$ac_new_val'\" >&5\n$as_echo \"$as_me:   current value: \\`$ac_new_val'\" >&2;}\n      fi;;\n  esac\n  # Pass precious variables to config.status.\n  if test \"$ac_new_set\" = set; then\n    case $ac_new_val in\n    *\\'*) ac_arg=$ac_var=`$as_echo \"$ac_new_val\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    *) ac_arg=$ac_var=$ac_new_val ;;\n    esac\n    case \" $ac_configure_args \" in\n      *\" '$ac_arg' \"*) ;; # Avoid dups.  Use of quotes ensures accuracy.\n      *) as_fn_append ac_configure_args \" '$ac_arg'\" ;;\n    esac\n  fi\ndone\nif $ac_cache_corrupted; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build\" >&5\n$as_echo \"$as_me: error: changes in the environment can compromise the build\" >&2;}\n  as_fn_error $? \"run \\`make distclean' and/or \\`rm $cache_file' and start over\" \"$LINENO\" 5\nfi\n## -------------------- ##\n## Main body of script. ##\n## -------------------- ##\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n\n\n\n# Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX\ninit_cflags=\"$CFLAGS\"\ninit_cxxflags=\"$CXXFLAGS\"\n\n# initialize Doxygen support\n\n\n\n\n\n\n\n\n\n\n# Files:\nDX_PROJECT=zookeeper\n\nDX_CONFIG=c-doc.Doxyfile\n\nDX_DOCDIR=docs\n\n\n# Environment variables used inside doxygen.cfg:\nDX_ENV=\"$DX_ENV SRCDIR='$srcdir'\"\n\nDX_ENV=\"$DX_ENV PROJECT='$DX_PROJECT'\"\n\nDX_ENV=\"$DX_ENV DOCDIR='$DX_DOCDIR'\"\n\nDX_ENV=\"$DX_ENV VERSION='$PACKAGE_VERSION'\"\n\n\n# Doxygen itself:\n\n\n\n    # Check whether --enable-doxygen-doc was given.\nif test \"${enable_doxygen_doc+set}\" = set; then :\n  enableval=$enable_doxygen_doc;\ncase \"$enableval\" in\n#(\ny|Y|yes|Yes|YES)\n    DX_FLAG_doc=1\n\n\n;; #(\nn|N|no|No|NO)\n    DX_FLAG_doc=0\n\n;; #(\n*)\n    as_fn_error $? \"invalid value '$enableval' given to doxygen-doc\" \"$LINENO\" 5\n;;\nesac\n\nelse\n\nDX_FLAG_doc=1\n\n\n\nfi\n\nif test \"$DX_FLAG_doc\" = 1; then\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}doxygen\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}doxygen; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_DOXYGEN+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_DOXYGEN in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_DOXYGEN=\"$DX_DOXYGEN\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_DOXYGEN=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_DOXYGEN=$ac_cv_path_DX_DOXYGEN\nif test -n \"$DX_DOXYGEN\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_DOXYGEN\" >&5\n$as_echo \"$DX_DOXYGEN\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_DOXYGEN\"; then\n  ac_pt_DX_DOXYGEN=$DX_DOXYGEN\n  # Extract the first word of \"doxygen\", so it can be a program name with args.\nset dummy doxygen; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_DOXYGEN+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_DOXYGEN in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_DOXYGEN=\"$ac_pt_DX_DOXYGEN\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_DOXYGEN=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_DOXYGEN=$ac_cv_path_ac_pt_DX_DOXYGEN\nif test -n \"$ac_pt_DX_DOXYGEN\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_DOXYGEN\" >&5\n$as_echo \"$ac_pt_DX_DOXYGEN\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_DOXYGEN\" = x; then\n    DX_DOXYGEN=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_DOXYGEN=$ac_pt_DX_DOXYGEN\n  fi\nelse\n  DX_DOXYGEN=\"$ac_cv_path_DX_DOXYGEN\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_DOXYGEN\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: doxygen not found - will not generate any doxygen documentation\" >&5\n$as_echo \"$as_me: WARNING: doxygen not found - will not generate any doxygen documentation\" >&2;}\n    DX_FLAG_doc=0\n\nfi\n\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}perl\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}perl; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_PERL+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_PERL in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_PERL=\"$DX_PERL\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_PERL=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_PERL=$ac_cv_path_DX_PERL\nif test -n \"$DX_PERL\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_PERL\" >&5\n$as_echo \"$DX_PERL\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_PERL\"; then\n  ac_pt_DX_PERL=$DX_PERL\n  # Extract the first word of \"perl\", so it can be a program name with args.\nset dummy perl; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_PERL+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_PERL in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_PERL=\"$ac_pt_DX_PERL\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_PERL=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_PERL=$ac_cv_path_ac_pt_DX_PERL\nif test -n \"$ac_pt_DX_PERL\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_PERL\" >&5\n$as_echo \"$ac_pt_DX_PERL\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_PERL\" = x; then\n    DX_PERL=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_PERL=$ac_pt_DX_PERL\n  fi\nelse\n  DX_PERL=\"$ac_cv_path_DX_PERL\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_PERL\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: perl not found - will not generate any doxygen documentation\" >&5\n$as_echo \"$as_me: WARNING: perl not found - will not generate any doxygen documentation\" >&2;}\n    DX_FLAG_doc=0\n\nfi\n\n    :\nfi\nif test \"$DX_FLAG_doc\" = 1; then\n     if :; then\n  DX_COND_doc_TRUE=\n  DX_COND_doc_FALSE='#'\nelse\n  DX_COND_doc_TRUE='#'\n  DX_COND_doc_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV PERL_PATH='$DX_PERL'\"\n\n    :\nelse\n     if false; then\n  DX_COND_doc_TRUE=\n  DX_COND_doc_FALSE='#'\nelse\n  DX_COND_doc_TRUE='#'\n  DX_COND_doc_FALSE=\nfi\n\n\n    :\nfi\n\n\n# Dot for graphics:\n\n\n\n    # Check whether --enable-doxygen-dot was given.\nif test \"${enable_doxygen_dot+set}\" = set; then :\n  enableval=$enable_doxygen_dot;\ncase \"$enableval\" in\n#(\ny|Y|yes|Yes|YES)\n    DX_FLAG_dot=1\n\n\ntest \"$DX_FLAG_doc\" = \"1\" \\\n|| as_fn_error $? \"doxygen-dot requires doxygen-dot\" \"$LINENO\" 5\n\n;; #(\nn|N|no|No|NO)\n    DX_FLAG_dot=0\n\n;; #(\n*)\n    as_fn_error $? \"invalid value '$enableval' given to doxygen-dot\" \"$LINENO\" 5\n;;\nesac\n\nelse\n\nDX_FLAG_dot=1\n\n\ntest \"$DX_FLAG_doc\" = \"1\" || DX_FLAG_dot=0\n\n\n\nfi\n\nif test \"$DX_FLAG_dot\" = 1; then\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}dot\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}dot; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_DOT+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_DOT in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_DOT=\"$DX_DOT\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_DOT=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_DOT=$ac_cv_path_DX_DOT\nif test -n \"$DX_DOT\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_DOT\" >&5\n$as_echo \"$DX_DOT\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_DOT\"; then\n  ac_pt_DX_DOT=$DX_DOT\n  # Extract the first word of \"dot\", so it can be a program name with args.\nset dummy dot; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_DOT+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_DOT in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_DOT=\"$ac_pt_DX_DOT\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_DOT=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_DOT=$ac_cv_path_ac_pt_DX_DOT\nif test -n \"$ac_pt_DX_DOT\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_DOT\" >&5\n$as_echo \"$ac_pt_DX_DOT\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_DOT\" = x; then\n    DX_DOT=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_DOT=$ac_pt_DX_DOT\n  fi\nelse\n  DX_DOT=\"$ac_cv_path_DX_DOT\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_DOT\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: dot not found - will not generate graphics for doxygen documentation\" >&5\n$as_echo \"$as_me: WARNING: dot not found - will not generate graphics for doxygen documentation\" >&2;}\n    DX_FLAG_dot=0\n\nfi\n\n    :\nfi\nif test \"$DX_FLAG_dot\" = 1; then\n     if :; then\n  DX_COND_dot_TRUE=\n  DX_COND_dot_FALSE='#'\nelse\n  DX_COND_dot_TRUE='#'\n  DX_COND_dot_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV HAVE_DOT='YES'\"\n\n             DX_ENV=\"$DX_ENV DOT_PATH='`expr \".$DX_DOT\" : '\\(\\.\\)[^/]*$' \\| \"x$DX_DOT\" : 'x\\(.*\\)/[^/]*$'`'\"\n\n    :\nelse\n     if false; then\n  DX_COND_dot_TRUE=\n  DX_COND_dot_FALSE='#'\nelse\n  DX_COND_dot_TRUE='#'\n  DX_COND_dot_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV HAVE_DOT='NO'\"\n\n    :\nfi\n\n\n# Man pages generation:\n\n\n\n    # Check whether --enable-doxygen-man was given.\nif test \"${enable_doxygen_man+set}\" = set; then :\n  enableval=$enable_doxygen_man;\ncase \"$enableval\" in\n#(\ny|Y|yes|Yes|YES)\n    DX_FLAG_man=1\n\n\ntest \"$DX_FLAG_doc\" = \"1\" \\\n|| as_fn_error $? \"doxygen-man requires doxygen-man\" \"$LINENO\" 5\n\n;; #(\nn|N|no|No|NO)\n    DX_FLAG_man=0\n\n;; #(\n*)\n    as_fn_error $? \"invalid value '$enableval' given to doxygen-man\" \"$LINENO\" 5\n;;\nesac\n\nelse\n\nDX_FLAG_man=0\n\n\ntest \"$DX_FLAG_doc\" = \"1\" || DX_FLAG_man=0\n\n\n\nfi\n\nif test \"$DX_FLAG_man\" = 1; then\n\n    :\nfi\nif test \"$DX_FLAG_man\" = 1; then\n     if :; then\n  DX_COND_man_TRUE=\n  DX_COND_man_FALSE='#'\nelse\n  DX_COND_man_TRUE='#'\n  DX_COND_man_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_MAN='YES'\"\n\n    :\nelse\n     if false; then\n  DX_COND_man_TRUE=\n  DX_COND_man_FALSE='#'\nelse\n  DX_COND_man_TRUE='#'\n  DX_COND_man_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_MAN='NO'\"\n\n    :\nfi\n\n\n# RTF file generation:\n\n\n\n    # Check whether --enable-doxygen-rtf was given.\nif test \"${enable_doxygen_rtf+set}\" = set; then :\n  enableval=$enable_doxygen_rtf;\ncase \"$enableval\" in\n#(\ny|Y|yes|Yes|YES)\n    DX_FLAG_rtf=1\n\n\ntest \"$DX_FLAG_doc\" = \"1\" \\\n|| as_fn_error $? \"doxygen-rtf requires doxygen-rtf\" \"$LINENO\" 5\n\n;; #(\nn|N|no|No|NO)\n    DX_FLAG_rtf=0\n\n;; #(\n*)\n    as_fn_error $? \"invalid value '$enableval' given to doxygen-rtf\" \"$LINENO\" 5\n;;\nesac\n\nelse\n\nDX_FLAG_rtf=0\n\n\ntest \"$DX_FLAG_doc\" = \"1\" || DX_FLAG_rtf=0\n\n\n\nfi\n\nif test \"$DX_FLAG_rtf\" = 1; then\n\n    :\nfi\nif test \"$DX_FLAG_rtf\" = 1; then\n     if :; then\n  DX_COND_rtf_TRUE=\n  DX_COND_rtf_FALSE='#'\nelse\n  DX_COND_rtf_TRUE='#'\n  DX_COND_rtf_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_RTF='YES'\"\n\n    :\nelse\n     if false; then\n  DX_COND_rtf_TRUE=\n  DX_COND_rtf_FALSE='#'\nelse\n  DX_COND_rtf_TRUE='#'\n  DX_COND_rtf_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_RTF='NO'\"\n\n    :\nfi\n\n\n# XML file generation:\n\n\n\n    # Check whether --enable-doxygen-xml was given.\nif test \"${enable_doxygen_xml+set}\" = set; then :\n  enableval=$enable_doxygen_xml;\ncase \"$enableval\" in\n#(\ny|Y|yes|Yes|YES)\n    DX_FLAG_xml=1\n\n\ntest \"$DX_FLAG_doc\" = \"1\" \\\n|| as_fn_error $? \"doxygen-xml requires doxygen-xml\" \"$LINENO\" 5\n\n;; #(\nn|N|no|No|NO)\n    DX_FLAG_xml=0\n\n;; #(\n*)\n    as_fn_error $? \"invalid value '$enableval' given to doxygen-xml\" \"$LINENO\" 5\n;;\nesac\n\nelse\n\nDX_FLAG_xml=0\n\n\ntest \"$DX_FLAG_doc\" = \"1\" || DX_FLAG_xml=0\n\n\n\nfi\n\nif test \"$DX_FLAG_xml\" = 1; then\n\n    :\nfi\nif test \"$DX_FLAG_xml\" = 1; then\n     if :; then\n  DX_COND_xml_TRUE=\n  DX_COND_xml_FALSE='#'\nelse\n  DX_COND_xml_TRUE='#'\n  DX_COND_xml_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_XML='YES'\"\n\n    :\nelse\n     if false; then\n  DX_COND_xml_TRUE=\n  DX_COND_xml_FALSE='#'\nelse\n  DX_COND_xml_TRUE='#'\n  DX_COND_xml_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_XML='NO'\"\n\n    :\nfi\n\n\n# (Compressed) HTML help generation:\n\n\n\n    # Check whether --enable-doxygen-chm was given.\nif test \"${enable_doxygen_chm+set}\" = set; then :\n  enableval=$enable_doxygen_chm;\ncase \"$enableval\" in\n#(\ny|Y|yes|Yes|YES)\n    DX_FLAG_chm=1\n\n\ntest \"$DX_FLAG_doc\" = \"1\" \\\n|| as_fn_error $? \"doxygen-chm requires doxygen-chm\" \"$LINENO\" 5\n\n;; #(\nn|N|no|No|NO)\n    DX_FLAG_chm=0\n\n;; #(\n*)\n    as_fn_error $? \"invalid value '$enableval' given to doxygen-chm\" \"$LINENO\" 5\n;;\nesac\n\nelse\n\nDX_FLAG_chm=0\n\n\ntest \"$DX_FLAG_doc\" = \"1\" || DX_FLAG_chm=0\n\n\n\nfi\n\nif test \"$DX_FLAG_chm\" = 1; then\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}hhc\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}hhc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_HHC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_HHC in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_HHC=\"$DX_HHC\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_HHC=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_HHC=$ac_cv_path_DX_HHC\nif test -n \"$DX_HHC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_HHC\" >&5\n$as_echo \"$DX_HHC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_HHC\"; then\n  ac_pt_DX_HHC=$DX_HHC\n  # Extract the first word of \"hhc\", so it can be a program name with args.\nset dummy hhc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_HHC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_HHC in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_HHC=\"$ac_pt_DX_HHC\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_HHC=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_HHC=$ac_cv_path_ac_pt_DX_HHC\nif test -n \"$ac_pt_DX_HHC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_HHC\" >&5\n$as_echo \"$ac_pt_DX_HHC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_HHC\" = x; then\n    DX_HHC=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_HHC=$ac_pt_DX_HHC\n  fi\nelse\n  DX_HHC=\"$ac_cv_path_DX_HHC\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_HHC\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: hhc not found - will not generate doxygen compressed HTML help documentation\" >&5\n$as_echo \"$as_me: WARNING: hhc not found - will not generate doxygen compressed HTML help documentation\" >&2;}\n    DX_FLAG_chm=0\n\nfi\n\n    :\nfi\nif test \"$DX_FLAG_chm\" = 1; then\n     if :; then\n  DX_COND_chm_TRUE=\n  DX_COND_chm_FALSE='#'\nelse\n  DX_COND_chm_TRUE='#'\n  DX_COND_chm_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV HHC_PATH='$DX_HHC'\"\n\n             DX_ENV=\"$DX_ENV GENERATE_HTML='YES'\"\n\n             DX_ENV=\"$DX_ENV GENERATE_HTMLHELP='YES'\"\n\n    :\nelse\n     if false; then\n  DX_COND_chm_TRUE=\n  DX_COND_chm_FALSE='#'\nelse\n  DX_COND_chm_TRUE='#'\n  DX_COND_chm_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_HTMLHELP='NO'\"\n\n    :\nfi\n\n\n# Seperate CHI file generation.\n\n\n\n    # Check whether --enable-doxygen-chi was given.\nif test \"${enable_doxygen_chi+set}\" = set; then :\n  enableval=$enable_doxygen_chi;\ncase \"$enableval\" in\n#(\ny|Y|yes|Yes|YES)\n    DX_FLAG_chi=1\n\n\ntest \"$DX_FLAG_chm\" = \"1\" \\\n|| as_fn_error $? \"doxygen-chi requires doxygen-chi\" \"$LINENO\" 5\n\n;; #(\nn|N|no|No|NO)\n    DX_FLAG_chi=0\n\n;; #(\n*)\n    as_fn_error $? \"invalid value '$enableval' given to doxygen-chi\" \"$LINENO\" 5\n;;\nesac\n\nelse\n\nDX_FLAG_chi=0\n\n\ntest \"$DX_FLAG_chm\" = \"1\" || DX_FLAG_chi=0\n\n\n\nfi\n\nif test \"$DX_FLAG_chi\" = 1; then\n\n    :\nfi\nif test \"$DX_FLAG_chi\" = 1; then\n     if :; then\n  DX_COND_chi_TRUE=\n  DX_COND_chi_FALSE='#'\nelse\n  DX_COND_chi_TRUE='#'\n  DX_COND_chi_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_CHI='YES'\"\n\n    :\nelse\n     if false; then\n  DX_COND_chi_TRUE=\n  DX_COND_chi_FALSE='#'\nelse\n  DX_COND_chi_TRUE='#'\n  DX_COND_chi_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_CHI='NO'\"\n\n    :\nfi\n\n\n# Plain HTML pages generation:\n\n\n\n    # Check whether --enable-doxygen-html was given.\nif test \"${enable_doxygen_html+set}\" = set; then :\n  enableval=$enable_doxygen_html;\ncase \"$enableval\" in\n#(\ny|Y|yes|Yes|YES)\n    DX_FLAG_html=1\n\n\ntest \"$DX_FLAG_doc\" = \"1\" \\\n|| as_fn_error $? \"doxygen-html requires doxygen-html\" \"$LINENO\" 5\n\ntest \"$DX_FLAG_chm\" = \"0\" \\\n|| as_fn_error $? \"doxygen-html contradicts doxygen-html\" \"$LINENO\" 5\n\n;; #(\nn|N|no|No|NO)\n    DX_FLAG_html=0\n\n;; #(\n*)\n    as_fn_error $? \"invalid value '$enableval' given to doxygen-html\" \"$LINENO\" 5\n;;\nesac\n\nelse\n\nDX_FLAG_html=1\n\n\ntest \"$DX_FLAG_doc\" = \"1\" || DX_FLAG_html=0\n\n\ntest \"$DX_FLAG_chm\" = \"0\" || DX_FLAG_html=0\n\n\n\nfi\n\nif test \"$DX_FLAG_html\" = 1; then\n\n    :\nfi\nif test \"$DX_FLAG_html\" = 1; then\n     if :; then\n  DX_COND_html_TRUE=\n  DX_COND_html_FALSE='#'\nelse\n  DX_COND_html_TRUE='#'\n  DX_COND_html_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_HTML='YES'\"\n\n    :\nelse\n     if false; then\n  DX_COND_html_TRUE=\n  DX_COND_html_FALSE='#'\nelse\n  DX_COND_html_TRUE='#'\n  DX_COND_html_FALSE=\nfi\n\n    test \"$DX_FLAG_chm\" = 1 || DX_ENV=\"$DX_ENV GENERATE_HTML='NO'\"\n\n    :\nfi\n\n\n# PostScript file generation:\n\n\n\n    # Check whether --enable-doxygen-ps was given.\nif test \"${enable_doxygen_ps+set}\" = set; then :\n  enableval=$enable_doxygen_ps;\ncase \"$enableval\" in\n#(\ny|Y|yes|Yes|YES)\n    DX_FLAG_ps=1\n\n\ntest \"$DX_FLAG_doc\" = \"1\" \\\n|| as_fn_error $? \"doxygen-ps requires doxygen-ps\" \"$LINENO\" 5\n\n;; #(\nn|N|no|No|NO)\n    DX_FLAG_ps=0\n\n;; #(\n*)\n    as_fn_error $? \"invalid value '$enableval' given to doxygen-ps\" \"$LINENO\" 5\n;;\nesac\n\nelse\n\nDX_FLAG_ps=0\n\n\ntest \"$DX_FLAG_doc\" = \"1\" || DX_FLAG_ps=0\n\n\n\nfi\n\nif test \"$DX_FLAG_ps\" = 1; then\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}latex\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}latex; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_LATEX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_LATEX in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_LATEX=\"$DX_LATEX\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_LATEX=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_LATEX=$ac_cv_path_DX_LATEX\nif test -n \"$DX_LATEX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_LATEX\" >&5\n$as_echo \"$DX_LATEX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_LATEX\"; then\n  ac_pt_DX_LATEX=$DX_LATEX\n  # Extract the first word of \"latex\", so it can be a program name with args.\nset dummy latex; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_LATEX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_LATEX in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_LATEX=\"$ac_pt_DX_LATEX\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_LATEX=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_LATEX=$ac_cv_path_ac_pt_DX_LATEX\nif test -n \"$ac_pt_DX_LATEX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_LATEX\" >&5\n$as_echo \"$ac_pt_DX_LATEX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_LATEX\" = x; then\n    DX_LATEX=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_LATEX=$ac_pt_DX_LATEX\n  fi\nelse\n  DX_LATEX=\"$ac_cv_path_DX_LATEX\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_LATEX\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: latex not found - will not generate doxygen PostScript documentation\" >&5\n$as_echo \"$as_me: WARNING: latex not found - will not generate doxygen PostScript documentation\" >&2;}\n    DX_FLAG_ps=0\n\nfi\n\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}makeindex\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}makeindex; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_MAKEINDEX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_MAKEINDEX in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_MAKEINDEX=\"$DX_MAKEINDEX\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_MAKEINDEX=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_MAKEINDEX=$ac_cv_path_DX_MAKEINDEX\nif test -n \"$DX_MAKEINDEX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_MAKEINDEX\" >&5\n$as_echo \"$DX_MAKEINDEX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_MAKEINDEX\"; then\n  ac_pt_DX_MAKEINDEX=$DX_MAKEINDEX\n  # Extract the first word of \"makeindex\", so it can be a program name with args.\nset dummy makeindex; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_MAKEINDEX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_MAKEINDEX in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_MAKEINDEX=\"$ac_pt_DX_MAKEINDEX\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_MAKEINDEX=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_MAKEINDEX=$ac_cv_path_ac_pt_DX_MAKEINDEX\nif test -n \"$ac_pt_DX_MAKEINDEX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_MAKEINDEX\" >&5\n$as_echo \"$ac_pt_DX_MAKEINDEX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_MAKEINDEX\" = x; then\n    DX_MAKEINDEX=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_MAKEINDEX=$ac_pt_DX_MAKEINDEX\n  fi\nelse\n  DX_MAKEINDEX=\"$ac_cv_path_DX_MAKEINDEX\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_MAKEINDEX\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: makeindex not found - will not generate doxygen PostScript documentation\" >&5\n$as_echo \"$as_me: WARNING: makeindex not found - will not generate doxygen PostScript documentation\" >&2;}\n    DX_FLAG_ps=0\n\nfi\n\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}dvips\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}dvips; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_DVIPS+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_DVIPS in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_DVIPS=\"$DX_DVIPS\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_DVIPS=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_DVIPS=$ac_cv_path_DX_DVIPS\nif test -n \"$DX_DVIPS\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_DVIPS\" >&5\n$as_echo \"$DX_DVIPS\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_DVIPS\"; then\n  ac_pt_DX_DVIPS=$DX_DVIPS\n  # Extract the first word of \"dvips\", so it can be a program name with args.\nset dummy dvips; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_DVIPS+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_DVIPS in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_DVIPS=\"$ac_pt_DX_DVIPS\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_DVIPS=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_DVIPS=$ac_cv_path_ac_pt_DX_DVIPS\nif test -n \"$ac_pt_DX_DVIPS\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_DVIPS\" >&5\n$as_echo \"$ac_pt_DX_DVIPS\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_DVIPS\" = x; then\n    DX_DVIPS=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_DVIPS=$ac_pt_DX_DVIPS\n  fi\nelse\n  DX_DVIPS=\"$ac_cv_path_DX_DVIPS\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_DVIPS\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: dvips not found - will not generate doxygen PostScript documentation\" >&5\n$as_echo \"$as_me: WARNING: dvips not found - will not generate doxygen PostScript documentation\" >&2;}\n    DX_FLAG_ps=0\n\nfi\n\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}egrep\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}egrep; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_EGREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_EGREP in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_EGREP=\"$DX_EGREP\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_EGREP=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_EGREP=$ac_cv_path_DX_EGREP\nif test -n \"$DX_EGREP\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_EGREP\" >&5\n$as_echo \"$DX_EGREP\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_EGREP\"; then\n  ac_pt_DX_EGREP=$DX_EGREP\n  # Extract the first word of \"egrep\", so it can be a program name with args.\nset dummy egrep; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_EGREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_EGREP in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_EGREP=\"$ac_pt_DX_EGREP\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_EGREP=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_EGREP=$ac_cv_path_ac_pt_DX_EGREP\nif test -n \"$ac_pt_DX_EGREP\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_EGREP\" >&5\n$as_echo \"$ac_pt_DX_EGREP\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_EGREP\" = x; then\n    DX_EGREP=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_EGREP=$ac_pt_DX_EGREP\n  fi\nelse\n  DX_EGREP=\"$ac_cv_path_DX_EGREP\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_EGREP\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: egrep not found - will not generate doxygen PostScript documentation\" >&5\n$as_echo \"$as_me: WARNING: egrep not found - will not generate doxygen PostScript documentation\" >&2;}\n    DX_FLAG_ps=0\n\nfi\n\n    :\nfi\nif test \"$DX_FLAG_ps\" = 1; then\n     if :; then\n  DX_COND_ps_TRUE=\n  DX_COND_ps_FALSE='#'\nelse\n  DX_COND_ps_TRUE='#'\n  DX_COND_ps_FALSE=\nfi\n\n\n    :\nelse\n     if false; then\n  DX_COND_ps_TRUE=\n  DX_COND_ps_FALSE='#'\nelse\n  DX_COND_ps_TRUE='#'\n  DX_COND_ps_FALSE=\nfi\n\n\n    :\nfi\n\n\n# PDF file generation:\n\n\n\n    # Check whether --enable-doxygen-pdf was given.\nif test \"${enable_doxygen_pdf+set}\" = set; then :\n  enableval=$enable_doxygen_pdf;\ncase \"$enableval\" in\n#(\ny|Y|yes|Yes|YES)\n    DX_FLAG_pdf=1\n\n\ntest \"$DX_FLAG_doc\" = \"1\" \\\n|| as_fn_error $? \"doxygen-pdf requires doxygen-pdf\" \"$LINENO\" 5\n\n;; #(\nn|N|no|No|NO)\n    DX_FLAG_pdf=0\n\n;; #(\n*)\n    as_fn_error $? \"invalid value '$enableval' given to doxygen-pdf\" \"$LINENO\" 5\n;;\nesac\n\nelse\n\nDX_FLAG_pdf=0\n\n\ntest \"$DX_FLAG_doc\" = \"1\" || DX_FLAG_pdf=0\n\n\n\nfi\n\nif test \"$DX_FLAG_pdf\" = 1; then\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}pdflatex\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}pdflatex; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_PDFLATEX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_PDFLATEX in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_PDFLATEX=\"$DX_PDFLATEX\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_PDFLATEX=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_PDFLATEX=$ac_cv_path_DX_PDFLATEX\nif test -n \"$DX_PDFLATEX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_PDFLATEX\" >&5\n$as_echo \"$DX_PDFLATEX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_PDFLATEX\"; then\n  ac_pt_DX_PDFLATEX=$DX_PDFLATEX\n  # Extract the first word of \"pdflatex\", so it can be a program name with args.\nset dummy pdflatex; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_PDFLATEX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_PDFLATEX in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_PDFLATEX=\"$ac_pt_DX_PDFLATEX\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_PDFLATEX=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_PDFLATEX=$ac_cv_path_ac_pt_DX_PDFLATEX\nif test -n \"$ac_pt_DX_PDFLATEX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_PDFLATEX\" >&5\n$as_echo \"$ac_pt_DX_PDFLATEX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_PDFLATEX\" = x; then\n    DX_PDFLATEX=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_PDFLATEX=$ac_pt_DX_PDFLATEX\n  fi\nelse\n  DX_PDFLATEX=\"$ac_cv_path_DX_PDFLATEX\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_PDFLATEX\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: pdflatex not found - will not generate doxygen PDF documentation\" >&5\n$as_echo \"$as_me: WARNING: pdflatex not found - will not generate doxygen PDF documentation\" >&2;}\n    DX_FLAG_pdf=0\n\nfi\n\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}makeindex\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}makeindex; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_MAKEINDEX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_MAKEINDEX in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_MAKEINDEX=\"$DX_MAKEINDEX\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_MAKEINDEX=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_MAKEINDEX=$ac_cv_path_DX_MAKEINDEX\nif test -n \"$DX_MAKEINDEX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_MAKEINDEX\" >&5\n$as_echo \"$DX_MAKEINDEX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_MAKEINDEX\"; then\n  ac_pt_DX_MAKEINDEX=$DX_MAKEINDEX\n  # Extract the first word of \"makeindex\", so it can be a program name with args.\nset dummy makeindex; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_MAKEINDEX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_MAKEINDEX in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_MAKEINDEX=\"$ac_pt_DX_MAKEINDEX\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_MAKEINDEX=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_MAKEINDEX=$ac_cv_path_ac_pt_DX_MAKEINDEX\nif test -n \"$ac_pt_DX_MAKEINDEX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_MAKEINDEX\" >&5\n$as_echo \"$ac_pt_DX_MAKEINDEX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_MAKEINDEX\" = x; then\n    DX_MAKEINDEX=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_MAKEINDEX=$ac_pt_DX_MAKEINDEX\n  fi\nelse\n  DX_MAKEINDEX=\"$ac_cv_path_DX_MAKEINDEX\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_MAKEINDEX\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: makeindex not found - will not generate doxygen PDF documentation\" >&5\n$as_echo \"$as_me: WARNING: makeindex not found - will not generate doxygen PDF documentation\" >&2;}\n    DX_FLAG_pdf=0\n\nfi\n\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}egrep\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}egrep; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_DX_EGREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $DX_EGREP in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_DX_EGREP=\"$DX_EGREP\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_DX_EGREP=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nDX_EGREP=$ac_cv_path_DX_EGREP\nif test -n \"$DX_EGREP\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DX_EGREP\" >&5\n$as_echo \"$DX_EGREP\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_DX_EGREP\"; then\n  ac_pt_DX_EGREP=$DX_EGREP\n  # Extract the first word of \"egrep\", so it can be a program name with args.\nset dummy egrep; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_DX_EGREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_DX_EGREP in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_DX_EGREP=\"$ac_pt_DX_EGREP\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_DX_EGREP=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_DX_EGREP=$ac_cv_path_ac_pt_DX_EGREP\nif test -n \"$ac_pt_DX_EGREP\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_EGREP\" >&5\n$as_echo \"$ac_pt_DX_EGREP\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_DX_EGREP\" = x; then\n    DX_EGREP=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DX_EGREP=$ac_pt_DX_EGREP\n  fi\nelse\n  DX_EGREP=\"$ac_cv_path_DX_EGREP\"\nfi\n\nif test \"$DX_FLAG_$DX_CURRENT_FEATURE$DX_EGREP\" = 1; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: egrep not found - will not generate doxygen PDF documentation\" >&5\n$as_echo \"$as_me: WARNING: egrep not found - will not generate doxygen PDF documentation\" >&2;}\n    DX_FLAG_pdf=0\n\nfi\n\n    :\nfi\nif test \"$DX_FLAG_pdf\" = 1; then\n     if :; then\n  DX_COND_pdf_TRUE=\n  DX_COND_pdf_FALSE='#'\nelse\n  DX_COND_pdf_TRUE='#'\n  DX_COND_pdf_FALSE=\nfi\n\n\n    :\nelse\n     if false; then\n  DX_COND_pdf_TRUE=\n  DX_COND_pdf_FALSE='#'\nelse\n  DX_COND_pdf_TRUE='#'\n  DX_COND_pdf_FALSE=\nfi\n\n\n    :\nfi\n\n\n# LaTeX generation for PS and/or PDF:\nif test \"$DX_FLAG_ps\" = 1 || test \"$DX_FLAG_pdf\" = 1; then\n     if :; then\n  DX_COND_latex_TRUE=\n  DX_COND_latex_FALSE='#'\nelse\n  DX_COND_latex_TRUE='#'\n  DX_COND_latex_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_LATEX='YES'\"\n\nelse\n     if false; then\n  DX_COND_latex_TRUE=\n  DX_COND_latex_FALSE='#'\nelse\n  DX_COND_latex_TRUE='#'\n  DX_COND_latex_FALSE=\nfi\n\n    DX_ENV=\"$DX_ENV GENERATE_LATEX='NO'\"\n\nfi\n\n# Paper size for PS and/or PDF:\n\ncase \"$DOXYGEN_PAPER_SIZE\" in\n#(\n\"\")\n    DOXYGEN_PAPER_SIZE=\"\"\n\n;; #(\na4wide|a4|letter|legal|executive)\n    DX_ENV=\"$DX_ENV PAPER_SIZE='$DOXYGEN_PAPER_SIZE'\"\n\n;; #(\n*)\n    as_fn_error $? \"unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE'\" \"$LINENO\" 5\n;;\nesac\n\n#For debugging:\n#echo DX_FLAG_doc=$DX_FLAG_doc\n#echo DX_FLAG_dot=$DX_FLAG_dot\n#echo DX_FLAG_man=$DX_FLAG_man\n#echo DX_FLAG_html=$DX_FLAG_html\n#echo DX_FLAG_chm=$DX_FLAG_chm\n#echo DX_FLAG_chi=$DX_FLAG_chi\n#echo DX_FLAG_rtf=$DX_FLAG_rtf\n#echo DX_FLAG_xml=$DX_FLAG_xml\n#echo DX_FLAG_pdf=$DX_FLAG_pdf\n#echo DX_FLAG_ps=$DX_FLAG_ps\n#echo DX_ENV=$DX_ENV\n\n\n# initialize automake\nam__api_version='1.15'\n\nac_aux_dir=\nfor ac_dir in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"; do\n  if test -f \"$ac_dir/install-sh\"; then\n    ac_aux_dir=$ac_dir\n    ac_install_sh=\"$ac_aux_dir/install-sh -c\"\n    break\n  elif test -f \"$ac_dir/install.sh\"; then\n    ac_aux_dir=$ac_dir\n    ac_install_sh=\"$ac_aux_dir/install.sh -c\"\n    break\n  elif test -f \"$ac_dir/shtool\"; then\n    ac_aux_dir=$ac_dir\n    ac_install_sh=\"$ac_aux_dir/shtool install -c\"\n    break\n  fi\ndone\nif test -z \"$ac_aux_dir\"; then\n  as_fn_error $? \"cannot find install-sh, install.sh, or shtool in \\\"$srcdir\\\" \\\"$srcdir/..\\\" \\\"$srcdir/../..\\\"\" \"$LINENO\" 5\nfi\n\n# These three variables are undocumented and unsupported,\n# and are intended to be withdrawn in a future Autoconf release.\n# They can cause serious problems if a builder's source tree is in a directory\n# whose full name contains unusual characters.\nac_config_guess=\"$SHELL $ac_aux_dir/config.guess\"  # Please don't use this var.\nac_config_sub=\"$SHELL $ac_aux_dir/config.sub\"  # Please don't use this var.\nac_configure=\"$SHELL $ac_aux_dir/configure\"  # Please don't use this var.\n\n\n# Find a good install program.  We prefer a C program (faster),\n# so one script is as good as another.  But avoid the broken or\n# incompatible versions:\n# SysV /etc/install, /usr/sbin/install\n# SunOS /usr/etc/install\n# IRIX /sbin/install\n# AIX /bin/install\n# AmigaOS /C/install, which installs bootblocks on floppy discs\n# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag\n# AFS /usr/afsws/bin/install, which mishandles nonexistent args\n# SVR4 /usr/ucb/install, which tries to use the nonexistent group \"staff\"\n# OS/2's system install, which has a completely different semantic\n# ./install, which can be erroneously created by make from ./install.sh.\n# Reject install programs that cannot install multiple files.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install\" >&5\n$as_echo_n \"checking for a BSD-compatible install... \" >&6; }\nif test -z \"$INSTALL\"; then\nif ${ac_cv_path_install+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    # Account for people who put trailing slashes in PATH elements.\ncase $as_dir/ in #((\n  ./ | .// | /[cC]/* | \\\n  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \\\n  ?:[\\\\/]os2[\\\\/]install[\\\\/]* | ?:[\\\\/]OS2[\\\\/]INSTALL[\\\\/]* | \\\n  /usr/ucb/* ) ;;\n  *)\n    # OSF1 and SCO ODT 3.0 have their own names for install.\n    # Don't use installbsd from OSF since it installs stuff as root\n    # by default.\n    for ac_prog in ginstall scoinst install; do\n      for ac_exec_ext in '' $ac_executable_extensions; do\n\tif as_fn_executable_p \"$as_dir/$ac_prog$ac_exec_ext\"; then\n\t  if test $ac_prog = install &&\n\t    grep dspmsg \"$as_dir/$ac_prog$ac_exec_ext\" >/dev/null 2>&1; then\n\t    # AIX install.  It has an incompatible calling convention.\n\t    :\n\t  elif test $ac_prog = install &&\n\t    grep pwplus \"$as_dir/$ac_prog$ac_exec_ext\" >/dev/null 2>&1; then\n\t    # program-specific install script used by HP pwplus--don't use.\n\t    :\n\t  else\n\t    rm -rf conftest.one conftest.two conftest.dir\n\t    echo one > conftest.one\n\t    echo two > conftest.two\n\t    mkdir conftest.dir\n\t    if \"$as_dir/$ac_prog$ac_exec_ext\" -c conftest.one conftest.two \"`pwd`/conftest.dir\" &&\n\t      test -s conftest.one && test -s conftest.two &&\n\t      test -s conftest.dir/conftest.one &&\n\t      test -s conftest.dir/conftest.two\n\t    then\n\t      ac_cv_path_install=\"$as_dir/$ac_prog$ac_exec_ext -c\"\n\t      break 3\n\t    fi\n\t  fi\n\tfi\n      done\n    done\n    ;;\nesac\n\n  done\nIFS=$as_save_IFS\n\nrm -rf conftest.one conftest.two conftest.dir\n\nfi\n  if test \"${ac_cv_path_install+set}\" = set; then\n    INSTALL=$ac_cv_path_install\n  else\n    # As a last resort, use the slow shell script.  Don't cache a\n    # value for INSTALL within a source directory, because that will\n    # break other packages using the cache if that directory is\n    # removed, or if the value is a relative name.\n    INSTALL=$ac_install_sh\n  fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $INSTALL\" >&5\n$as_echo \"$INSTALL\" >&6; }\n\n# Use test -z because SunOS4 sh mishandles braces in ${var-val}.\n# It thinks the first close brace ends the variable substitution.\ntest -z \"$INSTALL_PROGRAM\" && INSTALL_PROGRAM='${INSTALL}'\n\ntest -z \"$INSTALL_SCRIPT\" && INSTALL_SCRIPT='${INSTALL}'\n\ntest -z \"$INSTALL_DATA\" && INSTALL_DATA='${INSTALL} -m 644'\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether build environment is sane\" >&5\n$as_echo_n \"checking whether build environment is sane... \" >&6; }\n# Reject unsafe characters in $srcdir or the absolute working directory\n# name.  Accept space and tab only in the latter.\nam_lf='\n'\ncase `pwd` in\n  *[\\\\\\\"\\#\\$\\&\\'\\`$am_lf]*)\n    as_fn_error $? \"unsafe absolute working directory name\" \"$LINENO\" 5;;\nesac\ncase $srcdir in\n  *[\\\\\\\"\\#\\$\\&\\'\\`$am_lf\\ \\\t]*)\n    as_fn_error $? \"unsafe srcdir value: '$srcdir'\" \"$LINENO\" 5;;\nesac\n\n# Do 'set' in a subshell so we don't clobber the current shell's\n# arguments.  Must try -L first in case configure is actually a\n# symlink; some systems play weird games with the mod time of symlinks\n# (eg FreeBSD returns the mod time of the symlink's containing\n# directory).\nif (\n   am_has_slept=no\n   for am_try in 1 2; do\n     echo \"timestamp, slept: $am_has_slept\" > conftest.file\n     set X `ls -Lt \"$srcdir/configure\" conftest.file 2> /dev/null`\n     if test \"$*\" = \"X\"; then\n\t# -L didn't work.\n\tset X `ls -t \"$srcdir/configure\" conftest.file`\n     fi\n     if test \"$*\" != \"X $srcdir/configure conftest.file\" \\\n\t&& test \"$*\" != \"X conftest.file $srcdir/configure\"; then\n\n\t# If neither matched, then we have a broken ls.  This can happen\n\t# if, for instance, CONFIG_SHELL is bash and it inherits a\n\t# broken ls alias from the environment.  This has actually\n\t# happened.  Such a system could not be considered \"sane\".\n\tas_fn_error $? \"ls -t appears to fail.  Make sure there is not a broken\n  alias in your environment\" \"$LINENO\" 5\n     fi\n     if test \"$2\" = conftest.file || test $am_try -eq 2; then\n       break\n     fi\n     # Just in case.\n     sleep 1\n     am_has_slept=yes\n   done\n   test \"$2\" = conftest.file\n   )\nthen\n   # Ok.\n   :\nelse\n   as_fn_error $? \"newly created file is older than distributed files!\nCheck your system clock\" \"$LINENO\" 5\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\n# If we didn't sleep, we still need to ensure time stamps of config.status and\n# generated files are strictly newer.\nam_sleep_pid=\nif grep 'slept: no' conftest.file >/dev/null 2>&1; then\n  ( sleep 1 ) &\n  am_sleep_pid=$!\nfi\n\nrm -f conftest.file\n\ntest \"$program_prefix\" != NONE &&\n  program_transform_name=\"s&^&$program_prefix&;$program_transform_name\"\n# Use a double $ so make ignores it.\ntest \"$program_suffix\" != NONE &&\n  program_transform_name=\"s&\\$&$program_suffix&;$program_transform_name\"\n# Double any \\ or $.\n# By default was `s,x,x', remove it if useless.\nac_script='s/[\\\\$]/&&/g;s/;s,x,x,$//'\nprogram_transform_name=`$as_echo \"$program_transform_name\" | sed \"$ac_script\"`\n\n# Expand $ac_aux_dir to an absolute path.\nam_aux_dir=`cd \"$ac_aux_dir\" && pwd`\n\nif test x\"${MISSING+set}\" != xset; then\n  case $am_aux_dir in\n  *\\ * | *\\\t*)\n    MISSING=\"\\${SHELL} \\\"$am_aux_dir/missing\\\"\" ;;\n  *)\n    MISSING=\"\\${SHELL} $am_aux_dir/missing\" ;;\n  esac\nfi\n# Use eval to expand $SHELL\nif eval \"$MISSING --is-lightweight\"; then\n  am_missing_run=\"$MISSING \"\nelse\n  am_missing_run=\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing\" >&5\n$as_echo \"$as_me: WARNING: 'missing' script is too old or missing\" >&2;}\nfi\n\nif test x\"${install_sh+set}\" != xset; then\n  case $am_aux_dir in\n  *\\ * | *\\\t*)\n    install_sh=\"\\${SHELL} '$am_aux_dir/install-sh'\" ;;\n  *)\n    install_sh=\"\\${SHELL} $am_aux_dir/install-sh\"\n  esac\nfi\n\n# Installed binaries are usually stripped using 'strip' when the user\n# run \"make install-strip\".  However 'strip' might not be the right\n# tool to use in cross-compilation environments, therefore Automake\n# will honor the 'STRIP' environment variable to overrule this program.\nif test \"$cross_compiling\" != no; then\n  if test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}strip\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}strip; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_STRIP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$STRIP\"; then\n  ac_cv_prog_STRIP=\"$STRIP\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_STRIP=\"${ac_tool_prefix}strip\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nSTRIP=$ac_cv_prog_STRIP\nif test -n \"$STRIP\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $STRIP\" >&5\n$as_echo \"$STRIP\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_STRIP\"; then\n  ac_ct_STRIP=$STRIP\n  # Extract the first word of \"strip\", so it can be a program name with args.\nset dummy strip; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_STRIP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_STRIP\"; then\n  ac_cv_prog_ac_ct_STRIP=\"$ac_ct_STRIP\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_STRIP=\"strip\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP\nif test -n \"$ac_ct_STRIP\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP\" >&5\n$as_echo \"$ac_ct_STRIP\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_STRIP\" = x; then\n    STRIP=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    STRIP=$ac_ct_STRIP\n  fi\nelse\n  STRIP=\"$ac_cv_prog_STRIP\"\nfi\n\nfi\nINSTALL_STRIP_PROGRAM=\"\\$(install_sh) -c -s\"\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p\" >&5\n$as_echo_n \"checking for a thread-safe mkdir -p... \" >&6; }\nif test -z \"$MKDIR_P\"; then\n  if ${ac_cv_path_mkdir+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in mkdir gmkdir; do\n\t for ac_exec_ext in '' $ac_executable_extensions; do\n\t   as_fn_executable_p \"$as_dir/$ac_prog$ac_exec_ext\" || continue\n\t   case `\"$as_dir/$ac_prog$ac_exec_ext\" --version 2>&1` in #(\n\t     'mkdir (GNU coreutils) '* | \\\n\t     'mkdir (coreutils) '* | \\\n\t     'mkdir (fileutils) '4.1*)\n\t       ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext\n\t       break 3;;\n\t   esac\n\t done\n       done\n  done\nIFS=$as_save_IFS\n\nfi\n\n  test -d ./--version && rmdir ./--version\n  if test \"${ac_cv_path_mkdir+set}\" = set; then\n    MKDIR_P=\"$ac_cv_path_mkdir -p\"\n  else\n    # As a last resort, use the slow shell script.  Don't cache a\n    # value for MKDIR_P within a source directory, because that will\n    # break other packages using the cache if that directory is\n    # removed, or if the value is a relative name.\n    MKDIR_P=\"$ac_install_sh -d\"\n  fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $MKDIR_P\" >&5\n$as_echo \"$MKDIR_P\" >&6; }\n\nfor ac_prog in gawk mawk nawk awk\ndo\n  # Extract the first word of \"$ac_prog\", so it can be a program name with args.\nset dummy $ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_AWK+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$AWK\"; then\n  ac_cv_prog_AWK=\"$AWK\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_AWK=\"$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nAWK=$ac_cv_prog_AWK\nif test -n \"$AWK\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $AWK\" >&5\n$as_echo \"$AWK\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  test -n \"$AWK\" && break\ndone\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \\$(MAKE)\" >&5\n$as_echo_n \"checking whether ${MAKE-make} sets \\$(MAKE)... \" >&6; }\nset x ${MAKE-make}\nac_make=`$as_echo \"$2\" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`\nif eval \\${ac_cv_prog_make_${ac_make}_set+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat >conftest.make <<\\_ACEOF\nSHELL = /bin/sh\nall:\n\t@echo '@@@%%%=$(MAKE)=@@@%%%'\n_ACEOF\n# GNU make sometimes prints \"make[1]: Entering ...\", which would confuse us.\ncase `${MAKE-make} -f conftest.make 2>/dev/null` in\n  *@@@%%%=?*=@@@%%%*)\n    eval ac_cv_prog_make_${ac_make}_set=yes;;\n  *)\n    eval ac_cv_prog_make_${ac_make}_set=no;;\nesac\nrm -f conftest.make\nfi\nif eval test \\$ac_cv_prog_make_${ac_make}_set = yes; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\n  SET_MAKE=\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n  SET_MAKE=\"MAKE=${MAKE-make}\"\nfi\n\nrm -rf .tst 2>/dev/null\nmkdir .tst 2>/dev/null\nif test -d .tst; then\n  am__leading_dot=.\nelse\n  am__leading_dot=_\nfi\nrmdir .tst 2>/dev/null\n\n# Check whether --enable-silent-rules was given.\nif test \"${enable_silent_rules+set}\" = set; then :\n  enableval=$enable_silent_rules;\nfi\n\ncase $enable_silent_rules in # (((\n  yes) AM_DEFAULT_VERBOSITY=0;;\n   no) AM_DEFAULT_VERBOSITY=1;;\n    *) AM_DEFAULT_VERBOSITY=1;;\nesac\nam_make=${MAKE-make}\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables\" >&5\n$as_echo_n \"checking whether $am_make supports nested variables... \" >&6; }\nif ${am_cv_make_support_nested_variables+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if $as_echo 'TRUE=$(BAR$(V))\nBAR0=false\nBAR1=true\nV=1\nam__doit:\n\t@$(TRUE)\n.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then\n  am_cv_make_support_nested_variables=yes\nelse\n  am_cv_make_support_nested_variables=no\nfi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables\" >&5\n$as_echo \"$am_cv_make_support_nested_variables\" >&6; }\nif test $am_cv_make_support_nested_variables = yes; then\n    AM_V='$(V)'\n  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'\nelse\n  AM_V=$AM_DEFAULT_VERBOSITY\n  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY\nfi\nAM_BACKSLASH='\\'\n\nif test \"`cd $srcdir && pwd`\" != \"`pwd`\"; then\n  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output\n  # is not polluted with repeated \"-I.\"\n  am__isrc=' -I$(srcdir)'\n  # test to see if srcdir already configured\n  if test -f $srcdir/config.status; then\n    as_fn_error $? \"source directory already configured; run \\\"make distclean\\\" there first\" \"$LINENO\" 5\n  fi\nfi\n\n# test whether we have cygpath\nif test -z \"$CYGPATH_W\"; then\n  if (cygpath --version) >/dev/null 2>/dev/null; then\n    CYGPATH_W='cygpath -w'\n  else\n    CYGPATH_W=echo\n  fi\nfi\n\n\n# Define the identity of the package.\n PACKAGE='zookeeper'\n VERSION='3.7.0'\n\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE \"$PACKAGE\"\n_ACEOF\n\n\ncat >>confdefs.h <<_ACEOF\n#define VERSION \"$VERSION\"\n_ACEOF\n\n# Some tools Automake needs.\n\nACLOCAL=${ACLOCAL-\"${am_missing_run}aclocal-${am__api_version}\"}\n\n\nAUTOCONF=${AUTOCONF-\"${am_missing_run}autoconf\"}\n\n\nAUTOMAKE=${AUTOMAKE-\"${am_missing_run}automake-${am__api_version}\"}\n\n\nAUTOHEADER=${AUTOHEADER-\"${am_missing_run}autoheader\"}\n\n\nMAKEINFO=${MAKEINFO-\"${am_missing_run}makeinfo\"}\n\n# For better backward compatibility.  To be removed once Automake 1.9.x\n# dies out for good.  For more background, see:\n# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>\n# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>\nmkdir_p='$(MKDIR_P)'\n\n# We need awk for the \"check\" target (and possibly the TAP driver).  The\n# system \"awk\" is bad on some platforms.\n# Always define AMTAR for backward compatibility.  Yes, it's still used\n# in the wild :-(  We should find a proper way to deprecate it ...\nAMTAR='$${TAR-tar}'\n\n\n# We'll loop over all known methods to create a tar archive until one works.\n_am_tools='gnutar  pax cpio none'\n\nam__tar='$${TAR-tar} chof - \"$$tardir\"' am__untar='$${TAR-tar} xf -'\n\n\n\n\n\n\n# POSIX will say in a future version that running \"rm -f\" with no argument\n# is OK; and we want to be able to make that assumption in our Makefile\n# recipes.  So use an aggressive probe to check that the usage we want is\n# actually supported \"in the wild\" to an acceptable degree.\n# See automake bug#10828.\n# To make any issue more visible, cause the running configure to be aborted\n# by default if the 'rm' program in use doesn't match our expectations; the\n# user can still override this though.\nif rm -f && rm -fr && rm -rf; then : OK; else\n  cat >&2 <<'END'\nOops!\n\nYour 'rm' program seems unable to run without file operands specified\non the command line, even when the '-f' option is present.  This is contrary\nto the behaviour of most rm programs out there, and not conforming with\nthe upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>\n\nPlease tell bug-automake@gnu.org about your system, including the value\nof your $PATH and any error possibly output before this message.  This\ncan help us improve future automake versions.\n\nEND\n  if test x\"$ACCEPT_INFERIOR_RM_PROGRAM\" = x\"yes\"; then\n    echo 'Configuration will proceed anyway, since you have set the' >&2\n    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to \"yes\"' >&2\n    echo >&2\n  else\n    cat >&2 <<'END'\nAborting the configuration process, to ensure you take notice of the issue.\n\nYou can download and install GNU coreutils to get an 'rm' implementation\nthat behaves properly: <http://www.gnu.org/software/coreutils/>.\n\nIf you want to complete the configuration process using your problematic\n'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM\nto \"yes\", and re-run configure.\n\nEND\n    as_fn_error $? \"Your 'rm' program is bad, sorry.\" \"$LINENO\" 5\n  fi\nfi\n\n\nac_config_headers=\"$ac_config_headers config.h\"\n\n\n# Checks for programs.\n\n# Check whether --with-cppunit was given.\nif test \"${with_cppunit+set}\" = set; then :\n  withval=$with_cppunit;\nfi\n\n\nif test \"$with_cppunit\" = \"no\" ; then\n   CPPUNIT_PATH=\"No_CPPUNIT\"\n   CPPUNIT_INCLUDE=\n   CPPUNIT_LIBS=\nelse\n\n\n\n\n\n\n\nif test \"x$ac_cv_env_PKG_CONFIG_set\" != \"xset\"; then\n\tif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}pkg-config\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}pkg-config; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_PKG_CONFIG+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $PKG_CONFIG in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_PKG_CONFIG=\"$PKG_CONFIG\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_PKG_CONFIG=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nPKG_CONFIG=$ac_cv_path_PKG_CONFIG\nif test -n \"$PKG_CONFIG\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG\" >&5\n$as_echo \"$PKG_CONFIG\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_path_PKG_CONFIG\"; then\n  ac_pt_PKG_CONFIG=$PKG_CONFIG\n  # Extract the first word of \"pkg-config\", so it can be a program name with args.\nset dummy pkg-config; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $ac_pt_PKG_CONFIG in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_ac_pt_PKG_CONFIG=\"$ac_pt_PKG_CONFIG\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_ac_pt_PKG_CONFIG=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG\nif test -n \"$ac_pt_PKG_CONFIG\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG\" >&5\n$as_echo \"$ac_pt_PKG_CONFIG\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_pt_PKG_CONFIG\" = x; then\n    PKG_CONFIG=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    PKG_CONFIG=$ac_pt_PKG_CONFIG\n  fi\nelse\n  PKG_CONFIG=\"$ac_cv_path_PKG_CONFIG\"\nfi\n\nfi\nif test -n \"$PKG_CONFIG\"; then\n\t_pkg_min_version=0.9.0\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version\" >&5\n$as_echo_n \"checking pkg-config is at least version $_pkg_min_version... \" >&6; }\n\tif $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then\n\t\t{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\n\telse\n\t\t{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n\t\tPKG_CONFIG=\"\"\n\tfi\nfi\n\n\npkg_failed=no\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for CPPUNIT\" >&5\n$as_echo_n \"checking for CPPUNIT... \" >&6; }\n\nif test -n \"$CPPUNIT_CFLAGS\"; then\n    pkg_cv_CPPUNIT_CFLAGS=\"$CPPUNIT_CFLAGS\"\n elif test -n \"$PKG_CONFIG\"; then\n    if test -n \"$PKG_CONFIG\" && \\\n    { { $as_echo \"$as_me:${as_lineno-$LINENO}: \\$PKG_CONFIG --exists --print-errors \\\"cppunit >= 1.10.2\\\"\"; } >&5\n  ($PKG_CONFIG --exists --print-errors \"cppunit >= 1.10.2\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then\n  pkg_cv_CPPUNIT_CFLAGS=`$PKG_CONFIG --cflags \"cppunit >= 1.10.2\" 2>/dev/null`\n\t\t      test \"x$?\" != \"x0\" && pkg_failed=yes\nelse\n  pkg_failed=yes\nfi\n else\n    pkg_failed=untried\nfi\nif test -n \"$CPPUNIT_LIBS\"; then\n    pkg_cv_CPPUNIT_LIBS=\"$CPPUNIT_LIBS\"\n elif test -n \"$PKG_CONFIG\"; then\n    if test -n \"$PKG_CONFIG\" && \\\n    { { $as_echo \"$as_me:${as_lineno-$LINENO}: \\$PKG_CONFIG --exists --print-errors \\\"cppunit >= 1.10.2\\\"\"; } >&5\n  ($PKG_CONFIG --exists --print-errors \"cppunit >= 1.10.2\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then\n  pkg_cv_CPPUNIT_LIBS=`$PKG_CONFIG --libs \"cppunit >= 1.10.2\" 2>/dev/null`\n\t\t      test \"x$?\" != \"x0\" && pkg_failed=yes\nelse\n  pkg_failed=yes\nfi\n else\n    pkg_failed=untried\nfi\n\n\n\nif test $pkg_failed = yes; then\n   \t{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n\nif $PKG_CONFIG --atleast-pkgconfig-version 0.20; then\n        _pkg_short_errors_supported=yes\nelse\n        _pkg_short_errors_supported=no\nfi\n        if test $_pkg_short_errors_supported = yes; then\n\t        CPPUNIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs \"cppunit >= 1.10.2\" 2>&1`\n        else\n\t        CPPUNIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs \"cppunit >= 1.10.2\" 2>&1`\n        fi\n\t# Put the nasty error message in config.log where it belongs\n\techo \"$CPPUNIT_PKG_ERRORS\" >&5\n\n\tas_fn_error $? \"Package requirements (cppunit >= 1.10.2) were not met:\n\n$CPPUNIT_PKG_ERRORS\n\nConsider adjusting the PKG_CONFIG_PATH environment variable if you\ninstalled software in a non-standard prefix.\n\nAlternatively, you may set the environment variables CPPUNIT_CFLAGS\nand CPPUNIT_LIBS to avoid the need to call pkg-config.\nSee the pkg-config man page for more details.\" \"$LINENO\" 5\nelif test $pkg_failed = untried; then\n     \t{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n\t{ { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"The pkg-config script could not be found or is too old.  Make sure it\nis in your PATH or set the PKG_CONFIG environment variable to the full\npath to pkg-config.\n\nAlternatively, you may set the environment variables CPPUNIT_CFLAGS\nand CPPUNIT_LIBS to avoid the need to call pkg-config.\nSee the pkg-config man page for more details.\n\nTo get pkg-config, see <http://pkg-config.freedesktop.org/>.\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nelse\n\tCPPUNIT_CFLAGS=$pkg_cv_CPPUNIT_CFLAGS\n\tCPPUNIT_LIBS=$pkg_cv_CPPUNIT_LIBS\n        { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\n\nfi\n\n\nfi\n\n\n# Check whether --with-openssl was given.\nif test \"${with_openssl+set}\" = set; then :\n  withval=$with_openssl;\nelse\n  with_openssl=yes\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: configuring SSL using --with-openssl=$with_openssl\" >&5\n$as_echo \"$as_me: configuring SSL using --with-openssl=$with_openssl\" >&6;}\nsaved_CPPFLAGS=\"$CPPFLAGS\"\nsaved_LDFLAGS=\"$LDFLAGS\"\nif test \"x$with_openssl\" != \"xno\" && test \"x$with_openssl\" != \"xyes\" ; then\n        CPPFLAGS=\"$CPPFLAGS -I$with_openssl/include\"\n        LDFLAGS=\"$LDFLAGS -L$with_openssl/lib\"\nfi\nhave_openssl=no\nDEPDIR=\"${am__leading_dot}deps\"\n\nac_config_commands=\"$ac_config_commands depfiles\"\n\n\nam_make=${MAKE-make}\ncat > confinc << 'END'\nam__doit:\n\t@echo this is the am__doit target\n.PHONY: am__doit\nEND\n# If we don't find an include directive, just comment out the code.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make\" >&5\n$as_echo_n \"checking for style of include used by $am_make... \" >&6; }\nam__include=\"#\"\nam__quote=\n_am_result=none\n# First try GNU make style include.\necho \"include confinc\" > confmf\n# Ignore all kinds of additional output from 'make'.\ncase `$am_make -s -f confmf 2> /dev/null` in #(\n*the\\ am__doit\\ target*)\n  am__include=include\n  am__quote=\n  _am_result=GNU\n  ;;\nesac\n# Now try BSD make style include.\nif test \"$am__include\" = \"#\"; then\n   echo '.include \"confinc\"' > confmf\n   case `$am_make -s -f confmf 2> /dev/null` in #(\n   *the\\ am__doit\\ target*)\n     am__include=.include\n     am__quote=\"\\\"\"\n     _am_result=BSD\n     ;;\n   esac\nfi\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $_am_result\" >&5\n$as_echo \"$_am_result\" >&6; }\nrm -f confinc confmf\n\n# Check whether --enable-dependency-tracking was given.\nif test \"${enable_dependency_tracking+set}\" = set; then :\n  enableval=$enable_dependency_tracking;\nfi\n\nif test \"x$enable_dependency_tracking\" != xno; then\n  am_depcomp=\"$ac_aux_dir/depcomp\"\n  AMDEPBACKSLASH='\\'\n  am__nodep='_no'\nfi\n if test \"x$enable_dependency_tracking\" != xno; then\n  AMDEP_TRUE=\n  AMDEP_FALSE='#'\nelse\n  AMDEP_TRUE='#'\n  AMDEP_FALSE=\nfi\n\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}gcc\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}gcc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_CC=\"${ac_tool_prefix}gcc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_CC\"; then\n  ac_ct_CC=$CC\n  # Extract the first word of \"gcc\", so it can be a program name with args.\nset dummy gcc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_CC\"; then\n  ac_cv_prog_ac_ct_CC=\"$ac_ct_CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_CC=\"gcc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_CC=$ac_cv_prog_ac_ct_CC\nif test -n \"$ac_ct_CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC\" >&5\n$as_echo \"$ac_ct_CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_CC\" = x; then\n    CC=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    CC=$ac_ct_CC\n  fi\nelse\n  CC=\"$ac_cv_prog_CC\"\nfi\n\nif test -z \"$CC\"; then\n          if test -n \"$ac_tool_prefix\"; then\n    # Extract the first word of \"${ac_tool_prefix}cc\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}cc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_CC=\"${ac_tool_prefix}cc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  fi\nfi\nif test -z \"$CC\"; then\n  # Extract the first word of \"cc\", so it can be a program name with args.\nset dummy cc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\n  ac_prog_rejected=no\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    if test \"$as_dir/$ac_word$ac_exec_ext\" = \"/usr/ucb/cc\"; then\n       ac_prog_rejected=yes\n       continue\n     fi\n    ac_cv_prog_CC=\"cc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nif test $ac_prog_rejected = yes; then\n  # We found a bogon in the path, so make sure we never use it.\n  set dummy $ac_cv_prog_CC\n  shift\n  if test $# != 0; then\n    # We chose a different compiler from the bogus one.\n    # However, it has the same basename, so the bogon will be chosen\n    # first if we set CC to just the basename; use the full file name.\n    shift\n    ac_cv_prog_CC=\"$as_dir/$ac_word${1+' '}$@\"\n  fi\nfi\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$CC\"; then\n  if test -n \"$ac_tool_prefix\"; then\n  for ac_prog in cl.exe\n  do\n    # Extract the first word of \"$ac_tool_prefix$ac_prog\", so it can be a program name with args.\nset dummy $ac_tool_prefix$ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_CC=\"$ac_tool_prefix$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n    test -n \"$CC\" && break\n  done\nfi\nif test -z \"$CC\"; then\n  ac_ct_CC=$CC\n  for ac_prog in cl.exe\ndo\n  # Extract the first word of \"$ac_prog\", so it can be a program name with args.\nset dummy $ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_CC\"; then\n  ac_cv_prog_ac_ct_CC=\"$ac_ct_CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_CC=\"$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_CC=$ac_cv_prog_ac_ct_CC\nif test -n \"$ac_ct_CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC\" >&5\n$as_echo \"$ac_ct_CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  test -n \"$ac_ct_CC\" && break\ndone\n\n  if test \"x$ac_ct_CC\" = x; then\n    CC=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    CC=$ac_ct_CC\n  fi\nfi\n\nfi\n\n\ntest -z \"$CC\" && { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"no acceptable C compiler found in \\$PATH\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n\n# Provide some information about the compiler.\n$as_echo \"$as_me:${as_lineno-$LINENO}: checking for C compiler version\" >&5\nset X $ac_compile\nac_compiler=$2\nfor ac_option in --version -v -V -qversion; do\n  { { ac_try=\"$ac_compiler $ac_option >&5\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compiler $ac_option >&5\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    sed '10a\\\n... rest of stderr output deleted ...\n         10q' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n  fi\n  rm -f conftest.er1 conftest.err\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\ndone\n\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nac_clean_files_save=$ac_clean_files\nac_clean_files=\"$ac_clean_files a.out a.out.dSYM a.exe b.out\"\n# Try to create an executable without -o first, disregard a.out.\n# It will help us diagnose broken compilers, and finding out an intuition\n# of exeext.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether the C compiler works\" >&5\n$as_echo_n \"checking whether the C compiler works... \" >&6; }\nac_link_default=`$as_echo \"$ac_link\" | sed 's/ -o *conftest[^ ]*//'`\n\n# The possible output files:\nac_files=\"a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*\"\n\nac_rmfiles=\nfor ac_file in $ac_files\ndo\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;\n    * ) ac_rmfiles=\"$ac_rmfiles $ac_file\";;\n  esac\ndone\nrm -f $ac_rmfiles\n\nif { { ac_try=\"$ac_link_default\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link_default\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then :\n  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.\n# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'\n# in a Makefile.  We should not override ac_cv_exeext if it was cached,\n# so that the user can short-circuit this test for compilers unknown to\n# Autoconf.\nfor ac_file in $ac_files ''\ndo\n  test -f \"$ac_file\" || continue\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )\n\t;;\n    [ab].out )\n\t# We found the default executable, but exeext='' is most\n\t# certainly right.\n\tbreak;;\n    *.* )\n\tif test \"${ac_cv_exeext+set}\" = set && test \"$ac_cv_exeext\" != no;\n\tthen :; else\n\t   ac_cv_exeext=`expr \"$ac_file\" : '[^.]*\\(\\..*\\)'`\n\tfi\n\t# We set ac_cv_exeext here because the later test for it is not\n\t# safe: cross compilers may not add the suffix if given an `-o'\n\t# argument, so we may need to know it at that point already.\n\t# Even if this section looks crufty: it has the advantage of\n\t# actually working.\n\tbreak;;\n    * )\n\tbreak;;\n  esac\ndone\ntest \"$ac_cv_exeext\" = no && ac_cv_exeext=\n\nelse\n  ac_file=''\nfi\nif test -z \"$ac_file\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n$as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n{ { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error 77 \"C compiler cannot create executables\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name\" >&5\n$as_echo_n \"checking for C compiler default output file name... \" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_file\" >&5\n$as_echo \"$ac_file\" >&6; }\nac_exeext=$ac_cv_exeext\n\nrm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out\nac_clean_files=$ac_clean_files_save\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for suffix of executables\" >&5\n$as_echo_n \"checking for suffix of executables... \" >&6; }\nif { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then :\n  # If both `conftest.exe' and `conftest' are `present' (well, observable)\n# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will\n# work properly (i.e., refer to `conftest.exe'), while it won't with\n# `rm'.\nfor ac_file in conftest.exe conftest conftest.*; do\n  test -f \"$ac_file\" || continue\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;\n    *.* ) ac_cv_exeext=`expr \"$ac_file\" : '[^.]*\\(\\..*\\)'`\n\t  break;;\n    * ) break;;\n  esac\ndone\nelse\n  { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"cannot compute suffix of executables: cannot compile and link\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nfi\nrm -f conftest conftest$ac_cv_exeext\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext\" >&5\n$as_echo \"$ac_cv_exeext\" >&6; }\n\nrm -f conftest.$ac_ext\nEXEEXT=$ac_cv_exeext\nac_exeext=$EXEEXT\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdio.h>\nint\nmain ()\n{\nFILE *f = fopen (\"conftest.out\", \"w\");\n return ferror (f) || fclose (f) != 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nac_clean_files=\"$ac_clean_files conftest.out\"\n# Check that the compiler produces executables we can run.  If not, either\n# the compiler is broken, or we cross compile.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling\" >&5\n$as_echo_n \"checking whether we are cross compiling... \" >&6; }\nif test \"$cross_compiling\" != yes; then\n  { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\n  if { ac_try='./conftest$ac_cv_exeext'\n  { { case \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_try\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; }; then\n    cross_compiling=no\n  else\n    if test \"$cross_compiling\" = maybe; then\n\tcross_compiling=yes\n    else\n\t{ { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"cannot run C compiled programs.\nIf you meant to cross compile, use \\`--host'.\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n    fi\n  fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $cross_compiling\" >&5\n$as_echo \"$cross_compiling\" >&6; }\n\nrm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out\nac_clean_files=$ac_clean_files_save\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for suffix of object files\" >&5\n$as_echo_n \"checking for suffix of object files... \" >&6; }\nif ${ac_cv_objext+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nrm -f conftest.o conftest.obj\nif { { ac_try=\"$ac_compile\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compile\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then :\n  for ac_file in conftest.o conftest.obj conftest.*; do\n  test -f \"$ac_file\" || continue;\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;\n    *) ac_cv_objext=`expr \"$ac_file\" : '.*\\.\\(.*\\)'`\n       break;;\n  esac\ndone\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n{ { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"cannot compute suffix of object files: cannot compile\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nfi\nrm -f conftest.$ac_cv_objext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext\" >&5\n$as_echo \"$ac_cv_objext\" >&6; }\nOBJEXT=$ac_cv_objext\nac_objext=$OBJEXT\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler\" >&5\n$as_echo_n \"checking whether we are using the GNU C compiler... \" >&6; }\nif ${ac_cv_c_compiler_gnu+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n#ifndef __GNUC__\n       choke me\n#endif\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_compiler_gnu=yes\nelse\n  ac_compiler_gnu=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_cv_c_compiler_gnu=$ac_compiler_gnu\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu\" >&5\n$as_echo \"$ac_cv_c_compiler_gnu\" >&6; }\nif test $ac_compiler_gnu = yes; then\n  GCC=yes\nelse\n  GCC=\nfi\nac_test_CFLAGS=${CFLAGS+set}\nac_save_CFLAGS=$CFLAGS\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g\" >&5\n$as_echo_n \"checking whether $CC accepts -g... \" >&6; }\nif ${ac_cv_prog_cc_g+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_save_c_werror_flag=$ac_c_werror_flag\n   ac_c_werror_flag=yes\n   ac_cv_prog_cc_g=no\n   CFLAGS=\"-g\"\n   cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_g=yes\nelse\n  CFLAGS=\"\"\n      cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n\nelse\n  ac_c_werror_flag=$ac_save_c_werror_flag\n\t CFLAGS=\"-g\"\n\t cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_g=yes\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n   ac_c_werror_flag=$ac_save_c_werror_flag\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g\" >&5\n$as_echo \"$ac_cv_prog_cc_g\" >&6; }\nif test \"$ac_test_CFLAGS\" = set; then\n  CFLAGS=$ac_save_CFLAGS\nelif test $ac_cv_prog_cc_g = yes; then\n  if test \"$GCC\" = yes; then\n    CFLAGS=\"-g -O2\"\n  else\n    CFLAGS=\"-g\"\n  fi\nelse\n  if test \"$GCC\" = yes; then\n    CFLAGS=\"-O2\"\n  else\n    CFLAGS=\n  fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89\" >&5\n$as_echo_n \"checking for $CC option to accept ISO C89... \" >&6; }\nif ${ac_cv_prog_cc_c89+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_cv_prog_cc_c89=no\nac_save_CC=$CC\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdarg.h>\n#include <stdio.h>\nstruct stat;\n/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */\nstruct buf { int x; };\nFILE * (*rcsopen) (struct buf *, struct stat *, int);\nstatic char *e (p, i)\n     char **p;\n     int i;\n{\n  return p[i];\n}\nstatic char *f (char * (*g) (char **, int), char **p, ...)\n{\n  char *s;\n  va_list v;\n  va_start (v,p);\n  s = g (p, va_arg (v,int));\n  va_end (v);\n  return s;\n}\n\n/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has\n   function prototypes and stuff, but not '\\xHH' hex character constants.\n   These don't provoke an error unfortunately, instead are silently treated\n   as 'x'.  The following induces an error, until -std is added to get\n   proper ANSI mode.  Curiously '\\x00'!='x' always comes out true, for an\n   array size at least.  It's necessary to write '\\x00'==0 to get something\n   that's true only with -std.  */\nint osf4_cc_array ['\\x00' == 0 ? 1 : -1];\n\n/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters\n   inside strings and character constants.  */\n#define FOO(x) 'x'\nint xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];\n\nint test (int i, double x);\nstruct s1 {int (*f) (int a);};\nstruct s2 {int (*f) (double a);};\nint pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);\nint argc;\nchar **argv;\nint\nmain ()\n{\nreturn f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];\n  ;\n  return 0;\n}\n_ACEOF\nfor ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \\\n\t-Ae \"-Aa -D_HPUX_SOURCE\" \"-Xc -D__EXTENSIONS__\"\ndo\n  CC=\"$ac_save_CC $ac_arg\"\n  if ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_c89=$ac_arg\nfi\nrm -f core conftest.err conftest.$ac_objext\n  test \"x$ac_cv_prog_cc_c89\" != \"xno\" && break\ndone\nrm -f conftest.$ac_ext\nCC=$ac_save_CC\n\nfi\n# AC_CACHE_VAL\ncase \"x$ac_cv_prog_cc_c89\" in\n  x)\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: none needed\" >&5\n$as_echo \"none needed\" >&6; } ;;\n  xno)\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: unsupported\" >&5\n$as_echo \"unsupported\" >&6; } ;;\n  *)\n    CC=\"$CC $ac_cv_prog_cc_c89\"\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89\" >&5\n$as_echo \"$ac_cv_prog_cc_c89\" >&6; } ;;\nesac\nif test \"x$ac_cv_prog_cc_c89\" != xno; then :\n\nfi\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together\" >&5\n$as_echo_n \"checking whether $CC understands -c and -o together... \" >&6; }\nif ${am_cv_prog_cc_c_o+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\n  # Make sure it works both with $CC and with simple cc.\n  # Following AC_PROG_CC_C_O, we do the test twice because some\n  # compilers refuse to overwrite an existing .o file with -o,\n  # though they will create one.\n  am_cv_prog_cc_c_o=yes\n  for am_i in 1 2; do\n    if { echo \"$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext\" >&5\n   ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5\n   ac_status=$?\n   echo \"$as_me:$LINENO: \\$? = $ac_status\" >&5\n   (exit $ac_status); } \\\n         && test -f conftest2.$ac_objext; then\n      : OK\n    else\n      am_cv_prog_cc_c_o=no\n      break\n    fi\n  done\n  rm -f core conftest*\n  unset am_i\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o\" >&5\n$as_echo \"$am_cv_prog_cc_c_o\" >&6; }\nif test \"$am_cv_prog_cc_c_o\" != yes; then\n   # Losing compiler, so override with the script.\n   # FIXME: It is wrong to rewrite CC.\n   # But if we don't then we get into trouble of one sort or another.\n   # A longer-term fix would be to have automake use am__CC in this case,\n   # and then we could set am__CC=\"\\$(top_srcdir)/compile \\$(CC)\"\n   CC=\"$am_aux_dir/compile $CC\"\nfi\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n\ndepcc=\"$CC\"   am_compiler_list=\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc\" >&5\n$as_echo_n \"checking dependency style of $depcc... \" >&6; }\nif ${am_cv_CC_dependencies_compiler_type+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -z \"$AMDEP_TRUE\" && test -f \"$am_depcomp\"; then\n  # We make a subdir and do the tests there.  Otherwise we can end up\n  # making bogus files that we don't know about and never remove.  For\n  # instance it was reported that on HP-UX the gcc test will end up\n  # making a dummy file named 'D' -- because '-MD' means \"put the output\n  # in D\".\n  rm -rf conftest.dir\n  mkdir conftest.dir\n  # Copy depcomp to subdir because otherwise we won't find it if we're\n  # using a relative directory.\n  cp \"$am_depcomp\" conftest.dir\n  cd conftest.dir\n  # We will build objects and dependencies in a subdirectory because\n  # it helps to detect inapplicable dependency modes.  For instance\n  # both Tru64's cc and ICC support -MD to output dependencies as a\n  # side effect of compilation, but ICC will put the dependencies in\n  # the current directory while Tru64 will put them in the object\n  # directory.\n  mkdir sub\n\n  am_cv_CC_dependencies_compiler_type=none\n  if test \"$am_compiler_list\" = \"\"; then\n     am_compiler_list=`sed -n 's/^#*\\([a-zA-Z0-9]*\\))$/\\1/p' < ./depcomp`\n  fi\n  am__universal=false\n  case \" $depcc \" in #(\n     *\\ -arch\\ *\\ -arch\\ *) am__universal=true ;;\n     esac\n\n  for depmode in $am_compiler_list; do\n    # Setup a source with many dependencies, because some compilers\n    # like to wrap large dependency lists on column 80 (with \\), and\n    # we should not choose a depcomp mode which is confused by this.\n    #\n    # We need to recreate these files for each test, as the compiler may\n    # overwrite some of them when testing with obscure command lines.\n    # This happens at least with the AIX C compiler.\n    : > sub/conftest.c\n    for i in 1 2 3 4 5 6; do\n      echo '#include \"conftst'$i'.h\"' >> sub/conftest.c\n      # Using \": > sub/conftst$i.h\" creates only sub/conftst1.h with\n      # Solaris 10 /bin/sh.\n      echo '/* dummy */' > sub/conftst$i.h\n    done\n    echo \"${am__include} ${am__quote}sub/conftest.Po${am__quote}\" > confmf\n\n    # We check with '-c' and '-o' for the sake of the \"dashmstdout\"\n    # mode.  It turns out that the SunPro C++ compiler does not properly\n    # handle '-M -o', and we need to detect this.  Also, some Intel\n    # versions had trouble with output in subdirs.\n    am__obj=sub/conftest.${OBJEXT-o}\n    am__minus_obj=\"-o $am__obj\"\n    case $depmode in\n    gcc)\n      # This depmode causes a compiler race in universal mode.\n      test \"$am__universal\" = false || continue\n      ;;\n    nosideeffect)\n      # After this tag, mechanisms are not by side-effect, so they'll\n      # only be used when explicitly requested.\n      if test \"x$enable_dependency_tracking\" = xyes; then\n\tcontinue\n      else\n\tbreak\n      fi\n      ;;\n    msvc7 | msvc7msys | msvisualcpp | msvcmsys)\n      # This compiler won't grok '-c -o', but also, the minuso test has\n      # not run yet.  These depmodes are late enough in the game, and\n      # so weak that their functioning should not be impacted.\n      am__obj=conftest.${OBJEXT-o}\n      am__minus_obj=\n      ;;\n    none) break ;;\n    esac\n    if depmode=$depmode \\\n       source=sub/conftest.c object=$am__obj \\\n       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \\\n       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \\\n         >/dev/null 2>conftest.err &&\n       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&\n       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&\n       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&\n       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then\n      # icc doesn't choke on unknown options, it will just issue warnings\n      # or remarks (even with -Werror).  So we grep stderr for any message\n      # that says an option was ignored or not supported.\n      # When given -MP, icc 7.0 and 7.1 complain thusly:\n      #   icc: Command line warning: ignoring option '-M'; no argument required\n      # The diagnosis changed in icc 8.0:\n      #   icc: Command line remark: option '-MP' not supported\n      if (grep 'ignoring option' conftest.err ||\n          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else\n        am_cv_CC_dependencies_compiler_type=$depmode\n        break\n      fi\n    fi\n  done\n\n  cd ..\n  rm -rf conftest.dir\nelse\n  am_cv_CC_dependencies_compiler_type=none\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type\" >&5\n$as_echo \"$am_cv_CC_dependencies_compiler_type\" >&6; }\nCCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type\n\n if\n  test \"x$enable_dependency_tracking\" != xno \\\n  && test \"$am_cv_CC_dependencies_compiler_type\" = gcc3; then\n  am__fastdepCC_TRUE=\n  am__fastdepCC_FALSE='#'\nelse\n  am__fastdepCC_TRUE='#'\n  am__fastdepCC_FALSE=\nfi\n\n\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor\" >&5\n$as_echo_n \"checking how to run the C preprocessor... \" >&6; }\n# On Suns, sometimes $CPP names a directory.\nif test -n \"$CPP\" && test -d \"$CPP\"; then\n  CPP=\nfi\nif test -z \"$CPP\"; then\n  if ${ac_cv_prog_CPP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n      # Double quotes because CPP needs to be expanded\n    for CPP in \"$CC -E\" \"$CC -E -traditional-cpp\" \"/lib/cpp\"\n    do\n      ac_preproc_ok=false\nfor ac_c_preproc_warn_flag in '' yes\ndo\n  # Use a header file that comes with gcc, so configuring glibc\n  # with a fresh cross-compiler works.\n  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since\n  # <limits.h> exists even on freestanding compilers.\n  # On the NeXT, cc -E runs the code through the compiler's parser,\n  # not just through cpp. \"Syntax error\" is here to catch this case.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#ifdef __STDC__\n# include <limits.h>\n#else\n# include <assert.h>\n#endif\n\t\t     Syntax error\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n\nelse\n  # Broken: fails on valid input.\ncontinue\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\n  # OK, works on sane cases.  Now check whether nonexistent headers\n  # can be detected and how.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ac_nonexistent.h>\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n  # Broken: success on invalid input.\ncontinue\nelse\n  # Passes both tests.\nac_preproc_ok=:\nbreak\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\ndone\n# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.\nrm -f conftest.i conftest.err conftest.$ac_ext\nif $ac_preproc_ok; then :\n  break\nfi\n\n    done\n    ac_cv_prog_CPP=$CPP\n\nfi\n  CPP=$ac_cv_prog_CPP\nelse\n  ac_cv_prog_CPP=$CPP\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CPP\" >&5\n$as_echo \"$CPP\" >&6; }\nac_preproc_ok=false\nfor ac_c_preproc_warn_flag in '' yes\ndo\n  # Use a header file that comes with gcc, so configuring glibc\n  # with a fresh cross-compiler works.\n  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since\n  # <limits.h> exists even on freestanding compilers.\n  # On the NeXT, cc -E runs the code through the compiler's parser,\n  # not just through cpp. \"Syntax error\" is here to catch this case.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#ifdef __STDC__\n# include <limits.h>\n#else\n# include <assert.h>\n#endif\n\t\t     Syntax error\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n\nelse\n  # Broken: fails on valid input.\ncontinue\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\n  # OK, works on sane cases.  Now check whether nonexistent headers\n  # can be detected and how.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ac_nonexistent.h>\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n  # Broken: success on invalid input.\ncontinue\nelse\n  # Passes both tests.\nac_preproc_ok=:\nbreak\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\ndone\n# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.\nrm -f conftest.i conftest.err conftest.$ac_ext\nif $ac_preproc_ok; then :\n\nelse\n  { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"C preprocessor \\\"$CPP\\\" fails sanity check\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nfi\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e\" >&5\n$as_echo_n \"checking for grep that handles long lines and -e... \" >&6; }\nif ${ac_cv_path_GREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -z \"$GREP\"; then\n  ac_path_GREP_found=false\n  # Loop through the user's path and test for each of PROGNAME-LIST\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in grep ggrep; do\n    for ac_exec_ext in '' $ac_executable_extensions; do\n      ac_path_GREP=\"$as_dir/$ac_prog$ac_exec_ext\"\n      as_fn_executable_p \"$ac_path_GREP\" || continue\n# Check for GNU ac_path_GREP and select it if it is found.\n  # Check for GNU $ac_path_GREP\ncase `\"$ac_path_GREP\" --version 2>&1` in\n*GNU*)\n  ac_cv_path_GREP=\"$ac_path_GREP\" ac_path_GREP_found=:;;\n*)\n  ac_count=0\n  $as_echo_n 0123456789 >\"conftest.in\"\n  while :\n  do\n    cat \"conftest.in\" \"conftest.in\" >\"conftest.tmp\"\n    mv \"conftest.tmp\" \"conftest.in\"\n    cp \"conftest.in\" \"conftest.nl\"\n    $as_echo 'GREP' >> \"conftest.nl\"\n    \"$ac_path_GREP\" -e 'GREP$' -e '-(cannot match)-' < \"conftest.nl\" >\"conftest.out\" 2>/dev/null || break\n    diff \"conftest.out\" \"conftest.nl\" >/dev/null 2>&1 || break\n    as_fn_arith $ac_count + 1 && ac_count=$as_val\n    if test $ac_count -gt ${ac_path_GREP_max-0}; then\n      # Best one so far, save it but keep looking for a better one\n      ac_cv_path_GREP=\"$ac_path_GREP\"\n      ac_path_GREP_max=$ac_count\n    fi\n    # 10*(2^10) chars as input seems more than enough\n    test $ac_count -gt 10 && break\n  done\n  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;\nesac\n\n      $ac_path_GREP_found && break 3\n    done\n  done\n  done\nIFS=$as_save_IFS\n  if test -z \"$ac_cv_path_GREP\"; then\n    as_fn_error $? \"no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin\" \"$LINENO\" 5\n  fi\nelse\n  ac_cv_path_GREP=$GREP\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP\" >&5\n$as_echo \"$ac_cv_path_GREP\" >&6; }\n GREP=\"$ac_cv_path_GREP\"\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for egrep\" >&5\n$as_echo_n \"checking for egrep... \" >&6; }\nif ${ac_cv_path_EGREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1\n   then ac_cv_path_EGREP=\"$GREP -E\"\n   else\n     if test -z \"$EGREP\"; then\n  ac_path_EGREP_found=false\n  # Loop through the user's path and test for each of PROGNAME-LIST\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in egrep; do\n    for ac_exec_ext in '' $ac_executable_extensions; do\n      ac_path_EGREP=\"$as_dir/$ac_prog$ac_exec_ext\"\n      as_fn_executable_p \"$ac_path_EGREP\" || continue\n# Check for GNU ac_path_EGREP and select it if it is found.\n  # Check for GNU $ac_path_EGREP\ncase `\"$ac_path_EGREP\" --version 2>&1` in\n*GNU*)\n  ac_cv_path_EGREP=\"$ac_path_EGREP\" ac_path_EGREP_found=:;;\n*)\n  ac_count=0\n  $as_echo_n 0123456789 >\"conftest.in\"\n  while :\n  do\n    cat \"conftest.in\" \"conftest.in\" >\"conftest.tmp\"\n    mv \"conftest.tmp\" \"conftest.in\"\n    cp \"conftest.in\" \"conftest.nl\"\n    $as_echo 'EGREP' >> \"conftest.nl\"\n    \"$ac_path_EGREP\" 'EGREP$' < \"conftest.nl\" >\"conftest.out\" 2>/dev/null || break\n    diff \"conftest.out\" \"conftest.nl\" >/dev/null 2>&1 || break\n    as_fn_arith $ac_count + 1 && ac_count=$as_val\n    if test $ac_count -gt ${ac_path_EGREP_max-0}; then\n      # Best one so far, save it but keep looking for a better one\n      ac_cv_path_EGREP=\"$ac_path_EGREP\"\n      ac_path_EGREP_max=$ac_count\n    fi\n    # 10*(2^10) chars as input seems more than enough\n    test $ac_count -gt 10 && break\n  done\n  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;\nesac\n\n      $ac_path_EGREP_found && break 3\n    done\n  done\n  done\nIFS=$as_save_IFS\n  if test -z \"$ac_cv_path_EGREP\"; then\n    as_fn_error $? \"no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin\" \"$LINENO\" 5\n  fi\nelse\n  ac_cv_path_EGREP=$EGREP\nfi\n\n   fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP\" >&5\n$as_echo \"$ac_cv_path_EGREP\" >&6; }\n EGREP=\"$ac_cv_path_EGREP\"\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for ANSI C header files\" >&5\n$as_echo_n \"checking for ANSI C header files... \" >&6; }\nif ${ac_cv_header_stdc+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdlib.h>\n#include <stdarg.h>\n#include <string.h>\n#include <float.h>\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_header_stdc=yes\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\nif test $ac_cv_header_stdc = yes; then\n  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <string.h>\n\n_ACEOF\nif (eval \"$ac_cpp conftest.$ac_ext\") 2>&5 |\n  $EGREP \"memchr\" >/dev/null 2>&1; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f conftest*\n\nfi\n\nif test $ac_cv_header_stdc = yes; then\n  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdlib.h>\n\n_ACEOF\nif (eval \"$ac_cpp conftest.$ac_ext\") 2>&5 |\n  $EGREP \"free\" >/dev/null 2>&1; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f conftest*\n\nfi\n\nif test $ac_cv_header_stdc = yes; then\n  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.\n  if test \"$cross_compiling\" = yes; then :\n  :\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ctype.h>\n#include <stdlib.h>\n#if ((' ' & 0x0FF) == 0x020)\n# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')\n# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))\n#else\n# define ISLOWER(c) \\\n\t\t   (('a' <= (c) && (c) <= 'i') \\\n\t\t     || ('j' <= (c) && (c) <= 'r') \\\n\t\t     || ('s' <= (c) && (c) <= 'z'))\n# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))\n#endif\n\n#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))\nint\nmain ()\n{\n  int i;\n  for (i = 0; i < 256; i++)\n    if (XOR (islower (i), ISLOWER (i))\n\t|| toupper (i) != TOUPPER (i))\n      return 2;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_run \"$LINENO\"; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \\\n  conftest.$ac_objext conftest.beam conftest.$ac_ext\nfi\n\nfi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc\" >&5\n$as_echo \"$ac_cv_header_stdc\" >&6; }\nif test $ac_cv_header_stdc = yes; then\n\n$as_echo \"#define STDC_HEADERS 1\" >>confdefs.h\n\nfi\n\n# On IRIX 5.3, sys/types and inttypes.h are conflicting.\nfor ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \\\n\t\t  inttypes.h stdint.h unistd.h\ndo :\n  as_ac_Header=`$as_echo \"ac_cv_header_$ac_header\" | $as_tr_sh`\nac_fn_c_check_header_compile \"$LINENO\" \"$ac_header\" \"$as_ac_Header\" \"$ac_includes_default\n\"\nif eval test \\\"x\\$\"$as_ac_Header\"\\\" = x\"yes\"; then :\n  cat >>confdefs.h <<_ACEOF\n#define `$as_echo \"HAVE_$ac_header\" | $as_tr_cpp` 1\n_ACEOF\n\nfi\n\ndone\n\n\nac_fn_c_check_header_mongrel \"$LINENO\" \"openssl/ssl.h\" \"ac_cv_header_openssl_ssl_h\" \"$ac_includes_default\"\nif test \"x$ac_cv_header_openssl_ssl_h\" = xyes; then :\n   { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_new in -lssl\" >&5\n$as_echo_n \"checking for SSL_CTX_new in -lssl... \" >&6; }\nif ${ac_cv_lib_ssl_SSL_CTX_new+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-lssl  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar SSL_CTX_new ();\nint\nmain ()\n{\nreturn SSL_CTX_new ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_ssl_SSL_CTX_new=yes\nelse\n  ac_cv_lib_ssl_SSL_CTX_new=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_CTX_new\" >&5\n$as_echo \"$ac_cv_lib_ssl_SSL_CTX_new\" >&6; }\nif test \"x$ac_cv_lib_ssl_SSL_CTX_new\" = xyes; then :\n  have_openssl=yes\nfi\n\nfi\n\n\nif test \"x$with_openssl\" != \"xno\" && test \"x$with_openssl\" != \"xyes\" && test \"x$have_openssl\" != \"xyes\"; then\n    CPPFLAGS=\"$saved_CPPFLAGS\"\n    LDFLAGS=\"$saved_LDFLAGS\"\nfi\nif test \"x$with_openssl\" != xno && test \"x$have_openssl\" = xno; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: cannot build SSL support -- openssl not found\" >&5\n$as_echo \"$as_me: WARNING: cannot build SSL support -- openssl not found\" >&2;}\n    with_openssl=no\nfi\nif test \"x$with_openssl\" != xno; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: building with SSL support\" >&5\n$as_echo \"$as_me: building with SSL support\" >&6;}\nelse\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: building without SSL support\" >&5\n$as_echo \"$as_me: building without SSL support\" >&6;}\nfi\n if test \"x$with_openssl\" != xno; then\n  WANT_OPENSSL_TRUE=\n  WANT_OPENSSL_FALSE='#'\nelse\n  WANT_OPENSSL_TRUE='#'\n  WANT_OPENSSL_FALSE=\nfi\n\n\nif test \"$CALLER\" = \"ANT\" ; then\nCPPUNIT_CFLAGS=\"$CPPUNIT_CFLAGS -DZKSERVER_CMD=\\\"\\\\\\\"${base_dir}/zookeeper-client/zookeeper-client-c/tests/zkServer.sh\\\\\\\"\\\"\"\nelse\nCPPUNIT_CFLAGS=\"$CPPUNIT_CFLAGS -DZKSERVER_CMD=\\\"\\\\\\\"./tests/zkServer.sh\\\\\\\"\\\"\"\nas_ac_File=`$as_echo \"ac_cv_file_$srcdir/generated/zookeeper.jute.c\" | $as_tr_sh`\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $srcdir/generated/zookeeper.jute.c\" >&5\n$as_echo_n \"checking for $srcdir/generated/zookeeper.jute.c... \" >&6; }\nif eval \\${$as_ac_File+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  test \"$cross_compiling\" = yes &&\n  as_fn_error $? \"cannot check for file existence when cross compiling\" \"$LINENO\" 5\nif test -r \"$srcdir/generated/zookeeper.jute.c\"; then\n  eval \"$as_ac_File=yes\"\nelse\n  eval \"$as_ac_File=no\"\nfi\nfi\neval ac_res=\\$$as_ac_File\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\nif eval test \\\"x\\$\"$as_ac_File\"\\\" = x\"yes\"; then :\n\ncat >>confdefs.h <<_ACEOF\n#define `$as_echo \"HAVE_$srcdir/generated/zookeeper.jute.c\" | $as_tr_cpp` 1\n_ACEOF\n\nelse\n  as_fn_error $? \"jute files are missing! Please run \\\"ant compile_jute\\\" while in the zookeeper top level directory.\" \"$LINENO\" 5\n\nfi\nas_ac_File=`$as_echo \"ac_cv_file_$srcdir/generated/zookeeper.jute.h\" | $as_tr_sh`\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $srcdir/generated/zookeeper.jute.h\" >&5\n$as_echo_n \"checking for $srcdir/generated/zookeeper.jute.h... \" >&6; }\nif eval \\${$as_ac_File+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  test \"$cross_compiling\" = yes &&\n  as_fn_error $? \"cannot check for file existence when cross compiling\" \"$LINENO\" 5\nif test -r \"$srcdir/generated/zookeeper.jute.h\"; then\n  eval \"$as_ac_File=yes\"\nelse\n  eval \"$as_ac_File=no\"\nfi\nfi\neval ac_res=\\$$as_ac_File\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\nif eval test \\\"x\\$\"$as_ac_File\"\\\" = x\"yes\"; then :\n\ncat >>confdefs.h <<_ACEOF\n#define `$as_echo \"HAVE_$srcdir/generated/zookeeper.jute.h\" | $as_tr_cpp` 1\n_ACEOF\n\nelse\n  as_fn_error $? \"jute files are missing! Please run \\\"ant compile_jute\\\" while in the zookeeper top level directory.\" \"$LINENO\" 5\n\nfi\n\nfi\n\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}gcc\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}gcc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_CC=\"${ac_tool_prefix}gcc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_CC\"; then\n  ac_ct_CC=$CC\n  # Extract the first word of \"gcc\", so it can be a program name with args.\nset dummy gcc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_CC\"; then\n  ac_cv_prog_ac_ct_CC=\"$ac_ct_CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_CC=\"gcc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_CC=$ac_cv_prog_ac_ct_CC\nif test -n \"$ac_ct_CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC\" >&5\n$as_echo \"$ac_ct_CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_CC\" = x; then\n    CC=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    CC=$ac_ct_CC\n  fi\nelse\n  CC=\"$ac_cv_prog_CC\"\nfi\n\nif test -z \"$CC\"; then\n          if test -n \"$ac_tool_prefix\"; then\n    # Extract the first word of \"${ac_tool_prefix}cc\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}cc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_CC=\"${ac_tool_prefix}cc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  fi\nfi\nif test -z \"$CC\"; then\n  # Extract the first word of \"cc\", so it can be a program name with args.\nset dummy cc; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\n  ac_prog_rejected=no\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    if test \"$as_dir/$ac_word$ac_exec_ext\" = \"/usr/ucb/cc\"; then\n       ac_prog_rejected=yes\n       continue\n     fi\n    ac_cv_prog_CC=\"cc\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nif test $ac_prog_rejected = yes; then\n  # We found a bogon in the path, so make sure we never use it.\n  set dummy $ac_cv_prog_CC\n  shift\n  if test $# != 0; then\n    # We chose a different compiler from the bogus one.\n    # However, it has the same basename, so the bogon will be chosen\n    # first if we set CC to just the basename; use the full file name.\n    shift\n    ac_cv_prog_CC=\"$as_dir/$ac_word${1+' '}$@\"\n  fi\nfi\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$CC\"; then\n  if test -n \"$ac_tool_prefix\"; then\n  for ac_prog in cl.exe\n  do\n    # Extract the first word of \"$ac_tool_prefix$ac_prog\", so it can be a program name with args.\nset dummy $ac_tool_prefix$ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_CC=\"$ac_tool_prefix$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n    test -n \"$CC\" && break\n  done\nfi\nif test -z \"$CC\"; then\n  ac_ct_CC=$CC\n  for ac_prog in cl.exe\ndo\n  # Extract the first word of \"$ac_prog\", so it can be a program name with args.\nset dummy $ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_CC\"; then\n  ac_cv_prog_ac_ct_CC=\"$ac_ct_CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_CC=\"$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_CC=$ac_cv_prog_ac_ct_CC\nif test -n \"$ac_ct_CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC\" >&5\n$as_echo \"$ac_ct_CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  test -n \"$ac_ct_CC\" && break\ndone\n\n  if test \"x$ac_ct_CC\" = x; then\n    CC=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    CC=$ac_ct_CC\n  fi\nfi\n\nfi\n\n\ntest -z \"$CC\" && { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"no acceptable C compiler found in \\$PATH\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n\n# Provide some information about the compiler.\n$as_echo \"$as_me:${as_lineno-$LINENO}: checking for C compiler version\" >&5\nset X $ac_compile\nac_compiler=$2\nfor ac_option in --version -v -V -qversion; do\n  { { ac_try=\"$ac_compiler $ac_option >&5\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compiler $ac_option >&5\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    sed '10a\\\n... rest of stderr output deleted ...\n         10q' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n  fi\n  rm -f conftest.er1 conftest.err\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\ndone\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler\" >&5\n$as_echo_n \"checking whether we are using the GNU C compiler... \" >&6; }\nif ${ac_cv_c_compiler_gnu+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n#ifndef __GNUC__\n       choke me\n#endif\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_compiler_gnu=yes\nelse\n  ac_compiler_gnu=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_cv_c_compiler_gnu=$ac_compiler_gnu\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu\" >&5\n$as_echo \"$ac_cv_c_compiler_gnu\" >&6; }\nif test $ac_compiler_gnu = yes; then\n  GCC=yes\nelse\n  GCC=\nfi\nac_test_CFLAGS=${CFLAGS+set}\nac_save_CFLAGS=$CFLAGS\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g\" >&5\n$as_echo_n \"checking whether $CC accepts -g... \" >&6; }\nif ${ac_cv_prog_cc_g+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_save_c_werror_flag=$ac_c_werror_flag\n   ac_c_werror_flag=yes\n   ac_cv_prog_cc_g=no\n   CFLAGS=\"-g\"\n   cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_g=yes\nelse\n  CFLAGS=\"\"\n      cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n\nelse\n  ac_c_werror_flag=$ac_save_c_werror_flag\n\t CFLAGS=\"-g\"\n\t cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_g=yes\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n   ac_c_werror_flag=$ac_save_c_werror_flag\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g\" >&5\n$as_echo \"$ac_cv_prog_cc_g\" >&6; }\nif test \"$ac_test_CFLAGS\" = set; then\n  CFLAGS=$ac_save_CFLAGS\nelif test $ac_cv_prog_cc_g = yes; then\n  if test \"$GCC\" = yes; then\n    CFLAGS=\"-g -O2\"\n  else\n    CFLAGS=\"-g\"\n  fi\nelse\n  if test \"$GCC\" = yes; then\n    CFLAGS=\"-O2\"\n  else\n    CFLAGS=\n  fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89\" >&5\n$as_echo_n \"checking for $CC option to accept ISO C89... \" >&6; }\nif ${ac_cv_prog_cc_c89+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_cv_prog_cc_c89=no\nac_save_CC=$CC\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdarg.h>\n#include <stdio.h>\nstruct stat;\n/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */\nstruct buf { int x; };\nFILE * (*rcsopen) (struct buf *, struct stat *, int);\nstatic char *e (p, i)\n     char **p;\n     int i;\n{\n  return p[i];\n}\nstatic char *f (char * (*g) (char **, int), char **p, ...)\n{\n  char *s;\n  va_list v;\n  va_start (v,p);\n  s = g (p, va_arg (v,int));\n  va_end (v);\n  return s;\n}\n\n/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has\n   function prototypes and stuff, but not '\\xHH' hex character constants.\n   These don't provoke an error unfortunately, instead are silently treated\n   as 'x'.  The following induces an error, until -std is added to get\n   proper ANSI mode.  Curiously '\\x00'!='x' always comes out true, for an\n   array size at least.  It's necessary to write '\\x00'==0 to get something\n   that's true only with -std.  */\nint osf4_cc_array ['\\x00' == 0 ? 1 : -1];\n\n/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters\n   inside strings and character constants.  */\n#define FOO(x) 'x'\nint xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];\n\nint test (int i, double x);\nstruct s1 {int (*f) (int a);};\nstruct s2 {int (*f) (double a);};\nint pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);\nint argc;\nchar **argv;\nint\nmain ()\n{\nreturn f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];\n  ;\n  return 0;\n}\n_ACEOF\nfor ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \\\n\t-Ae \"-Aa -D_HPUX_SOURCE\" \"-Xc -D__EXTENSIONS__\"\ndo\n  CC=\"$ac_save_CC $ac_arg\"\n  if ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_c89=$ac_arg\nfi\nrm -f core conftest.err conftest.$ac_objext\n  test \"x$ac_cv_prog_cc_c89\" != \"xno\" && break\ndone\nrm -f conftest.$ac_ext\nCC=$ac_save_CC\n\nfi\n# AC_CACHE_VAL\ncase \"x$ac_cv_prog_cc_c89\" in\n  x)\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: none needed\" >&5\n$as_echo \"none needed\" >&6; } ;;\n  xno)\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: unsupported\" >&5\n$as_echo \"unsupported\" >&6; } ;;\n  *)\n    CC=\"$CC $ac_cv_prog_cc_c89\"\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89\" >&5\n$as_echo \"$ac_cv_prog_cc_c89\" >&6; } ;;\nesac\nif test \"x$ac_cv_prog_cc_c89\" != xno; then :\n\nfi\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together\" >&5\n$as_echo_n \"checking whether $CC understands -c and -o together... \" >&6; }\nif ${am_cv_prog_cc_c_o+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\n  # Make sure it works both with $CC and with simple cc.\n  # Following AC_PROG_CC_C_O, we do the test twice because some\n  # compilers refuse to overwrite an existing .o file with -o,\n  # though they will create one.\n  am_cv_prog_cc_c_o=yes\n  for am_i in 1 2; do\n    if { echo \"$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext\" >&5\n   ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5\n   ac_status=$?\n   echo \"$as_me:$LINENO: \\$? = $ac_status\" >&5\n   (exit $ac_status); } \\\n         && test -f conftest2.$ac_objext; then\n      : OK\n    else\n      am_cv_prog_cc_c_o=no\n      break\n    fi\n  done\n  rm -f core conftest*\n  unset am_i\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o\" >&5\n$as_echo \"$am_cv_prog_cc_c_o\" >&6; }\nif test \"$am_cv_prog_cc_c_o\" != yes; then\n   # Losing compiler, so override with the script.\n   # FIXME: It is wrong to rewrite CC.\n   # But if we don't then we get into trouble of one sort or another.\n   # A longer-term fix would be to have automake use am__CC in this case,\n   # and then we could set am__CC=\"\\$(top_srcdir)/compile \\$(CC)\"\n   CC=\"$am_aux_dir/compile $CC\"\nfi\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n\ndepcc=\"$CC\"   am_compiler_list=\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc\" >&5\n$as_echo_n \"checking dependency style of $depcc... \" >&6; }\nif ${am_cv_CC_dependencies_compiler_type+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -z \"$AMDEP_TRUE\" && test -f \"$am_depcomp\"; then\n  # We make a subdir and do the tests there.  Otherwise we can end up\n  # making bogus files that we don't know about and never remove.  For\n  # instance it was reported that on HP-UX the gcc test will end up\n  # making a dummy file named 'D' -- because '-MD' means \"put the output\n  # in D\".\n  rm -rf conftest.dir\n  mkdir conftest.dir\n  # Copy depcomp to subdir because otherwise we won't find it if we're\n  # using a relative directory.\n  cp \"$am_depcomp\" conftest.dir\n  cd conftest.dir\n  # We will build objects and dependencies in a subdirectory because\n  # it helps to detect inapplicable dependency modes.  For instance\n  # both Tru64's cc and ICC support -MD to output dependencies as a\n  # side effect of compilation, but ICC will put the dependencies in\n  # the current directory while Tru64 will put them in the object\n  # directory.\n  mkdir sub\n\n  am_cv_CC_dependencies_compiler_type=none\n  if test \"$am_compiler_list\" = \"\"; then\n     am_compiler_list=`sed -n 's/^#*\\([a-zA-Z0-9]*\\))$/\\1/p' < ./depcomp`\n  fi\n  am__universal=false\n  case \" $depcc \" in #(\n     *\\ -arch\\ *\\ -arch\\ *) am__universal=true ;;\n     esac\n\n  for depmode in $am_compiler_list; do\n    # Setup a source with many dependencies, because some compilers\n    # like to wrap large dependency lists on column 80 (with \\), and\n    # we should not choose a depcomp mode which is confused by this.\n    #\n    # We need to recreate these files for each test, as the compiler may\n    # overwrite some of them when testing with obscure command lines.\n    # This happens at least with the AIX C compiler.\n    : > sub/conftest.c\n    for i in 1 2 3 4 5 6; do\n      echo '#include \"conftst'$i'.h\"' >> sub/conftest.c\n      # Using \": > sub/conftst$i.h\" creates only sub/conftst1.h with\n      # Solaris 10 /bin/sh.\n      echo '/* dummy */' > sub/conftst$i.h\n    done\n    echo \"${am__include} ${am__quote}sub/conftest.Po${am__quote}\" > confmf\n\n    # We check with '-c' and '-o' for the sake of the \"dashmstdout\"\n    # mode.  It turns out that the SunPro C++ compiler does not properly\n    # handle '-M -o', and we need to detect this.  Also, some Intel\n    # versions had trouble with output in subdirs.\n    am__obj=sub/conftest.${OBJEXT-o}\n    am__minus_obj=\"-o $am__obj\"\n    case $depmode in\n    gcc)\n      # This depmode causes a compiler race in universal mode.\n      test \"$am__universal\" = false || continue\n      ;;\n    nosideeffect)\n      # After this tag, mechanisms are not by side-effect, so they'll\n      # only be used when explicitly requested.\n      if test \"x$enable_dependency_tracking\" = xyes; then\n\tcontinue\n      else\n\tbreak\n      fi\n      ;;\n    msvc7 | msvc7msys | msvisualcpp | msvcmsys)\n      # This compiler won't grok '-c -o', but also, the minuso test has\n      # not run yet.  These depmodes are late enough in the game, and\n      # so weak that their functioning should not be impacted.\n      am__obj=conftest.${OBJEXT-o}\n      am__minus_obj=\n      ;;\n    none) break ;;\n    esac\n    if depmode=$depmode \\\n       source=sub/conftest.c object=$am__obj \\\n       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \\\n       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \\\n         >/dev/null 2>conftest.err &&\n       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&\n       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&\n       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&\n       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then\n      # icc doesn't choke on unknown options, it will just issue warnings\n      # or remarks (even with -Werror).  So we grep stderr for any message\n      # that says an option was ignored or not supported.\n      # When given -MP, icc 7.0 and 7.1 complain thusly:\n      #   icc: Command line warning: ignoring option '-M'; no argument required\n      # The diagnosis changed in icc 8.0:\n      #   icc: Command line remark: option '-MP' not supported\n      if (grep 'ignoring option' conftest.err ||\n          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else\n        am_cv_CC_dependencies_compiler_type=$depmode\n        break\n      fi\n    fi\n  done\n\n  cd ..\n  rm -rf conftest.dir\nelse\n  am_cv_CC_dependencies_compiler_type=none\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type\" >&5\n$as_echo \"$am_cv_CC_dependencies_compiler_type\" >&6; }\nCCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type\n\n if\n  test \"x$enable_dependency_tracking\" != xno \\\n  && test \"$am_cv_CC_dependencies_compiler_type\" = gcc3; then\n  am__fastdepCC_TRUE=\n  am__fastdepCC_FALSE='#'\nelse\n  am__fastdepCC_TRUE='#'\n  am__fastdepCC_FALSE=\nfi\n\n\n\nac_ext=cpp\nac_cpp='$CXXCPP $CPPFLAGS'\nac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_cxx_compiler_gnu\nif test -z \"$CXX\"; then\n  if test -n \"$CCC\"; then\n    CXX=$CCC\n  else\n    if test -n \"$ac_tool_prefix\"; then\n  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC\n  do\n    # Extract the first word of \"$ac_tool_prefix$ac_prog\", so it can be a program name with args.\nset dummy $ac_tool_prefix$ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CXX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CXX\"; then\n  ac_cv_prog_CXX=\"$CXX\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_CXX=\"$ac_tool_prefix$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCXX=$ac_cv_prog_CXX\nif test -n \"$CXX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CXX\" >&5\n$as_echo \"$CXX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n    test -n \"$CXX\" && break\n  done\nfi\nif test -z \"$CXX\"; then\n  ac_ct_CXX=$CXX\n  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC\ndo\n  # Extract the first word of \"$ac_prog\", so it can be a program name with args.\nset dummy $ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_CXX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_CXX\"; then\n  ac_cv_prog_ac_ct_CXX=\"$ac_ct_CXX\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_CXX=\"$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_CXX=$ac_cv_prog_ac_ct_CXX\nif test -n \"$ac_ct_CXX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX\" >&5\n$as_echo \"$ac_ct_CXX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  test -n \"$ac_ct_CXX\" && break\ndone\n\n  if test \"x$ac_ct_CXX\" = x; then\n    CXX=\"g++\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    CXX=$ac_ct_CXX\n  fi\nfi\n\n  fi\nfi\n# Provide some information about the compiler.\n$as_echo \"$as_me:${as_lineno-$LINENO}: checking for C++ compiler version\" >&5\nset X $ac_compile\nac_compiler=$2\nfor ac_option in --version -v -V -qversion; do\n  { { ac_try=\"$ac_compiler $ac_option >&5\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compiler $ac_option >&5\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    sed '10a\\\n... rest of stderr output deleted ...\n         10q' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n  fi\n  rm -f conftest.er1 conftest.err\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\ndone\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler\" >&5\n$as_echo_n \"checking whether we are using the GNU C++ compiler... \" >&6; }\nif ${ac_cv_cxx_compiler_gnu+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n#ifndef __GNUC__\n       choke me\n#endif\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_cxx_try_compile \"$LINENO\"; then :\n  ac_compiler_gnu=yes\nelse\n  ac_compiler_gnu=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_cv_cxx_compiler_gnu=$ac_compiler_gnu\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu\" >&5\n$as_echo \"$ac_cv_cxx_compiler_gnu\" >&6; }\nif test $ac_compiler_gnu = yes; then\n  GXX=yes\nelse\n  GXX=\nfi\nac_test_CXXFLAGS=${CXXFLAGS+set}\nac_save_CXXFLAGS=$CXXFLAGS\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g\" >&5\n$as_echo_n \"checking whether $CXX accepts -g... \" >&6; }\nif ${ac_cv_prog_cxx_g+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_save_cxx_werror_flag=$ac_cxx_werror_flag\n   ac_cxx_werror_flag=yes\n   ac_cv_prog_cxx_g=no\n   CXXFLAGS=\"-g\"\n   cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_cxx_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cxx_g=yes\nelse\n  CXXFLAGS=\"\"\n      cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_cxx_try_compile \"$LINENO\"; then :\n\nelse\n  ac_cxx_werror_flag=$ac_save_cxx_werror_flag\n\t CXXFLAGS=\"-g\"\n\t cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_cxx_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cxx_g=yes\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n   ac_cxx_werror_flag=$ac_save_cxx_werror_flag\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g\" >&5\n$as_echo \"$ac_cv_prog_cxx_g\" >&6; }\nif test \"$ac_test_CXXFLAGS\" = set; then\n  CXXFLAGS=$ac_save_CXXFLAGS\nelif test $ac_cv_prog_cxx_g = yes; then\n  if test \"$GXX\" = yes; then\n    CXXFLAGS=\"-g -O2\"\n  else\n    CXXFLAGS=\"-g\"\n  fi\nelse\n  if test \"$GXX\" = yes; then\n    CXXFLAGS=\"-O2\"\n  else\n    CXXFLAGS=\n  fi\nfi\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\ndepcc=\"$CXX\"  am_compiler_list=\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc\" >&5\n$as_echo_n \"checking dependency style of $depcc... \" >&6; }\nif ${am_cv_CXX_dependencies_compiler_type+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -z \"$AMDEP_TRUE\" && test -f \"$am_depcomp\"; then\n  # We make a subdir and do the tests there.  Otherwise we can end up\n  # making bogus files that we don't know about and never remove.  For\n  # instance it was reported that on HP-UX the gcc test will end up\n  # making a dummy file named 'D' -- because '-MD' means \"put the output\n  # in D\".\n  rm -rf conftest.dir\n  mkdir conftest.dir\n  # Copy depcomp to subdir because otherwise we won't find it if we're\n  # using a relative directory.\n  cp \"$am_depcomp\" conftest.dir\n  cd conftest.dir\n  # We will build objects and dependencies in a subdirectory because\n  # it helps to detect inapplicable dependency modes.  For instance\n  # both Tru64's cc and ICC support -MD to output dependencies as a\n  # side effect of compilation, but ICC will put the dependencies in\n  # the current directory while Tru64 will put them in the object\n  # directory.\n  mkdir sub\n\n  am_cv_CXX_dependencies_compiler_type=none\n  if test \"$am_compiler_list\" = \"\"; then\n     am_compiler_list=`sed -n 's/^#*\\([a-zA-Z0-9]*\\))$/\\1/p' < ./depcomp`\n  fi\n  am__universal=false\n  case \" $depcc \" in #(\n     *\\ -arch\\ *\\ -arch\\ *) am__universal=true ;;\n     esac\n\n  for depmode in $am_compiler_list; do\n    # Setup a source with many dependencies, because some compilers\n    # like to wrap large dependency lists on column 80 (with \\), and\n    # we should not choose a depcomp mode which is confused by this.\n    #\n    # We need to recreate these files for each test, as the compiler may\n    # overwrite some of them when testing with obscure command lines.\n    # This happens at least with the AIX C compiler.\n    : > sub/conftest.c\n    for i in 1 2 3 4 5 6; do\n      echo '#include \"conftst'$i'.h\"' >> sub/conftest.c\n      # Using \": > sub/conftst$i.h\" creates only sub/conftst1.h with\n      # Solaris 10 /bin/sh.\n      echo '/* dummy */' > sub/conftst$i.h\n    done\n    echo \"${am__include} ${am__quote}sub/conftest.Po${am__quote}\" > confmf\n\n    # We check with '-c' and '-o' for the sake of the \"dashmstdout\"\n    # mode.  It turns out that the SunPro C++ compiler does not properly\n    # handle '-M -o', and we need to detect this.  Also, some Intel\n    # versions had trouble with output in subdirs.\n    am__obj=sub/conftest.${OBJEXT-o}\n    am__minus_obj=\"-o $am__obj\"\n    case $depmode in\n    gcc)\n      # This depmode causes a compiler race in universal mode.\n      test \"$am__universal\" = false || continue\n      ;;\n    nosideeffect)\n      # After this tag, mechanisms are not by side-effect, so they'll\n      # only be used when explicitly requested.\n      if test \"x$enable_dependency_tracking\" = xyes; then\n\tcontinue\n      else\n\tbreak\n      fi\n      ;;\n    msvc7 | msvc7msys | msvisualcpp | msvcmsys)\n      # This compiler won't grok '-c -o', but also, the minuso test has\n      # not run yet.  These depmodes are late enough in the game, and\n      # so weak that their functioning should not be impacted.\n      am__obj=conftest.${OBJEXT-o}\n      am__minus_obj=\n      ;;\n    none) break ;;\n    esac\n    if depmode=$depmode \\\n       source=sub/conftest.c object=$am__obj \\\n       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \\\n       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \\\n         >/dev/null 2>conftest.err &&\n       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&\n       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&\n       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&\n       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then\n      # icc doesn't choke on unknown options, it will just issue warnings\n      # or remarks (even with -Werror).  So we grep stderr for any message\n      # that says an option was ignored or not supported.\n      # When given -MP, icc 7.0 and 7.1 complain thusly:\n      #   icc: Command line warning: ignoring option '-M'; no argument required\n      # The diagnosis changed in icc 8.0:\n      #   icc: Command line remark: option '-MP' not supported\n      if (grep 'ignoring option' conftest.err ||\n          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else\n        am_cv_CXX_dependencies_compiler_type=$depmode\n        break\n      fi\n    fi\n  done\n\n  cd ..\n  rm -rf conftest.dir\nelse\n  am_cv_CXX_dependencies_compiler_type=none\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type\" >&5\n$as_echo \"$am_cv_CXX_dependencies_compiler_type\" >&6; }\nCXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type\n\n if\n  test \"x$enable_dependency_tracking\" != xno \\\n  && test \"$am_cv_CXX_dependencies_compiler_type\" = gcc3; then\n  am__fastdepCXX_TRUE=\n  am__fastdepCXX_FALSE='#'\nelse\n  am__fastdepCXX_TRUE='#'\n  am__fastdepCXX_FALSE=\nfi\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether ln -s works\" >&5\n$as_echo_n \"checking whether ln -s works... \" >&6; }\nLN_S=$as_ln_s\nif test \"$LN_S\" = \"ln -s\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no, using $LN_S\" >&5\n$as_echo \"no, using $LN_S\" >&6; }\nfi\n\n\n# AC_DISABLE_SHARED\ncase `pwd` in\n  *\\ * | *\\\t*)\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \\`pwd\\`\" >&5\n$as_echo \"$as_me: WARNING: Libtool does not cope well with whitespace in \\`pwd\\`\" >&2;} ;;\nesac\n\n\n\nmacro_version='2.4.6'\nmacro_revision='2.4.6'\n\n\n\n\n\n\n\n\n\n\n\n\n\nltmain=$ac_aux_dir/ltmain.sh\n\n# Make sure we can run config.sub.\n$SHELL \"$ac_aux_dir/config.sub\" sun4 >/dev/null 2>&1 ||\n  as_fn_error $? \"cannot run $SHELL $ac_aux_dir/config.sub\" \"$LINENO\" 5\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking build system type\" >&5\n$as_echo_n \"checking build system type... \" >&6; }\nif ${ac_cv_build+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_build_alias=$build_alias\ntest \"x$ac_build_alias\" = x &&\n  ac_build_alias=`$SHELL \"$ac_aux_dir/config.guess\"`\ntest \"x$ac_build_alias\" = x &&\n  as_fn_error $? \"cannot guess build type; you must specify one\" \"$LINENO\" 5\nac_cv_build=`$SHELL \"$ac_aux_dir/config.sub\" $ac_build_alias` ||\n  as_fn_error $? \"$SHELL $ac_aux_dir/config.sub $ac_build_alias failed\" \"$LINENO\" 5\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_build\" >&5\n$as_echo \"$ac_cv_build\" >&6; }\ncase $ac_cv_build in\n*-*-*) ;;\n*) as_fn_error $? \"invalid value of canonical build\" \"$LINENO\" 5;;\nesac\nbuild=$ac_cv_build\nac_save_IFS=$IFS; IFS='-'\nset x $ac_cv_build\nshift\nbuild_cpu=$1\nbuild_vendor=$2\nshift; shift\n# Remember, the first character of IFS is used to create $*,\n# except with old shells:\nbuild_os=$*\nIFS=$ac_save_IFS\ncase $build_os in *\\ *) build_os=`echo \"$build_os\" | sed 's/ /-/g'`;; esac\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking host system type\" >&5\n$as_echo_n \"checking host system type... \" >&6; }\nif ${ac_cv_host+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test \"x$host_alias\" = x; then\n  ac_cv_host=$ac_cv_build\nelse\n  ac_cv_host=`$SHELL \"$ac_aux_dir/config.sub\" $host_alias` ||\n    as_fn_error $? \"$SHELL $ac_aux_dir/config.sub $host_alias failed\" \"$LINENO\" 5\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_host\" >&5\n$as_echo \"$ac_cv_host\" >&6; }\ncase $ac_cv_host in\n*-*-*) ;;\n*) as_fn_error $? \"invalid value of canonical host\" \"$LINENO\" 5;;\nesac\nhost=$ac_cv_host\nac_save_IFS=$IFS; IFS='-'\nset x $ac_cv_host\nshift\nhost_cpu=$1\nhost_vendor=$2\nshift; shift\n# Remember, the first character of IFS is used to create $*,\n# except with old shells:\nhost_os=$*\nIFS=$ac_save_IFS\ncase $host_os in *\\ *) host_os=`echo \"$host_os\" | sed 's/ /-/g'`;; esac\n\n\n# Backslashify metacharacters that are still active within\n# double-quoted strings.\nsed_quote_subst='s/\\([\"`$\\\\]\\)/\\\\\\1/g'\n\n# Same as above, but do not quote variable references.\ndouble_quote_subst='s/\\([\"`\\\\]\\)/\\\\\\1/g'\n\n# Sed substitution to delay expansion of an escaped shell variable in a\n# double_quote_subst'ed string.\ndelay_variable_subst='s/\\\\\\\\\\\\\\\\\\\\\\$/\\\\\\\\\\\\$/g'\n\n# Sed substitution to delay expansion of an escaped single quote.\ndelay_single_quote_subst='s/'\\''/'\\'\\\\\\\\\\\\\\'\\''/g'\n\n# Sed substitution to avoid accidental globbing in evaled expressions\nno_glob_subst='s/\\*/\\\\\\*/g'\n\nECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'\nECHO=$ECHO$ECHO$ECHO$ECHO$ECHO\nECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to print strings\" >&5\n$as_echo_n \"checking how to print strings... \" >&6; }\n# Test print first, because it will be a builtin if present.\nif test \"X`( print -r -- -n ) 2>/dev/null`\" = X-n && \\\n   test \"X`print -r -- $ECHO 2>/dev/null`\" = \"X$ECHO\"; then\n  ECHO='print -r --'\nelif test \"X`printf %s $ECHO 2>/dev/null`\" = \"X$ECHO\"; then\n  ECHO='printf %s\\n'\nelse\n  # Use this function as a fallback that always works.\n  func_fallback_echo ()\n  {\n    eval 'cat <<_LTECHO_EOF\n$1\n_LTECHO_EOF'\n  }\n  ECHO='func_fallback_echo'\nfi\n\n# func_echo_all arg...\n# Invoke $ECHO with all args, space-separated.\nfunc_echo_all ()\n{\n    $ECHO \"\"\n}\n\ncase $ECHO in\n  printf*) { $as_echo \"$as_me:${as_lineno-$LINENO}: result: printf\" >&5\n$as_echo \"printf\" >&6; } ;;\n  print*) { $as_echo \"$as_me:${as_lineno-$LINENO}: result: print -r\" >&5\n$as_echo \"print -r\" >&6; } ;;\n  *) { $as_echo \"$as_me:${as_lineno-$LINENO}: result: cat\" >&5\n$as_echo \"cat\" >&6; } ;;\nesac\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output\" >&5\n$as_echo_n \"checking for a sed that does not truncate output... \" >&6; }\nif ${ac_cv_path_SED+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n            ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/\n     for ac_i in 1 2 3 4 5 6 7; do\n       ac_script=\"$ac_script$as_nl$ac_script\"\n     done\n     echo \"$ac_script\" 2>/dev/null | sed 99q >conftest.sed\n     { ac_script=; unset ac_script;}\n     if test -z \"$SED\"; then\n  ac_path_SED_found=false\n  # Loop through the user's path and test for each of PROGNAME-LIST\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in sed gsed; do\n    for ac_exec_ext in '' $ac_executable_extensions; do\n      ac_path_SED=\"$as_dir/$ac_prog$ac_exec_ext\"\n      as_fn_executable_p \"$ac_path_SED\" || continue\n# Check for GNU ac_path_SED and select it if it is found.\n  # Check for GNU $ac_path_SED\ncase `\"$ac_path_SED\" --version 2>&1` in\n*GNU*)\n  ac_cv_path_SED=\"$ac_path_SED\" ac_path_SED_found=:;;\n*)\n  ac_count=0\n  $as_echo_n 0123456789 >\"conftest.in\"\n  while :\n  do\n    cat \"conftest.in\" \"conftest.in\" >\"conftest.tmp\"\n    mv \"conftest.tmp\" \"conftest.in\"\n    cp \"conftest.in\" \"conftest.nl\"\n    $as_echo '' >> \"conftest.nl\"\n    \"$ac_path_SED\" -f conftest.sed < \"conftest.nl\" >\"conftest.out\" 2>/dev/null || break\n    diff \"conftest.out\" \"conftest.nl\" >/dev/null 2>&1 || break\n    as_fn_arith $ac_count + 1 && ac_count=$as_val\n    if test $ac_count -gt ${ac_path_SED_max-0}; then\n      # Best one so far, save it but keep looking for a better one\n      ac_cv_path_SED=\"$ac_path_SED\"\n      ac_path_SED_max=$ac_count\n    fi\n    # 10*(2^10) chars as input seems more than enough\n    test $ac_count -gt 10 && break\n  done\n  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;\nesac\n\n      $ac_path_SED_found && break 3\n    done\n  done\n  done\nIFS=$as_save_IFS\n  if test -z \"$ac_cv_path_SED\"; then\n    as_fn_error $? \"no acceptable sed could be found in \\$PATH\" \"$LINENO\" 5\n  fi\nelse\n  ac_cv_path_SED=$SED\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED\" >&5\n$as_echo \"$ac_cv_path_SED\" >&6; }\n SED=\"$ac_cv_path_SED\"\n  rm -f conftest.sed\n\ntest -z \"$SED\" && SED=sed\nXsed=\"$SED -e 1s/^X//\"\n\n\n\n\n\n\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for fgrep\" >&5\n$as_echo_n \"checking for fgrep... \" >&6; }\nif ${ac_cv_path_FGREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1\n   then ac_cv_path_FGREP=\"$GREP -F\"\n   else\n     if test -z \"$FGREP\"; then\n  ac_path_FGREP_found=false\n  # Loop through the user's path and test for each of PROGNAME-LIST\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in fgrep; do\n    for ac_exec_ext in '' $ac_executable_extensions; do\n      ac_path_FGREP=\"$as_dir/$ac_prog$ac_exec_ext\"\n      as_fn_executable_p \"$ac_path_FGREP\" || continue\n# Check for GNU ac_path_FGREP and select it if it is found.\n  # Check for GNU $ac_path_FGREP\ncase `\"$ac_path_FGREP\" --version 2>&1` in\n*GNU*)\n  ac_cv_path_FGREP=\"$ac_path_FGREP\" ac_path_FGREP_found=:;;\n*)\n  ac_count=0\n  $as_echo_n 0123456789 >\"conftest.in\"\n  while :\n  do\n    cat \"conftest.in\" \"conftest.in\" >\"conftest.tmp\"\n    mv \"conftest.tmp\" \"conftest.in\"\n    cp \"conftest.in\" \"conftest.nl\"\n    $as_echo 'FGREP' >> \"conftest.nl\"\n    \"$ac_path_FGREP\" FGREP < \"conftest.nl\" >\"conftest.out\" 2>/dev/null || break\n    diff \"conftest.out\" \"conftest.nl\" >/dev/null 2>&1 || break\n    as_fn_arith $ac_count + 1 && ac_count=$as_val\n    if test $ac_count -gt ${ac_path_FGREP_max-0}; then\n      # Best one so far, save it but keep looking for a better one\n      ac_cv_path_FGREP=\"$ac_path_FGREP\"\n      ac_path_FGREP_max=$ac_count\n    fi\n    # 10*(2^10) chars as input seems more than enough\n    test $ac_count -gt 10 && break\n  done\n  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;\nesac\n\n      $ac_path_FGREP_found && break 3\n    done\n  done\n  done\nIFS=$as_save_IFS\n  if test -z \"$ac_cv_path_FGREP\"; then\n    as_fn_error $? \"no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin\" \"$LINENO\" 5\n  fi\nelse\n  ac_cv_path_FGREP=$FGREP\nfi\n\n   fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP\" >&5\n$as_echo \"$ac_cv_path_FGREP\" >&6; }\n FGREP=\"$ac_cv_path_FGREP\"\n\n\ntest -z \"$GREP\" && GREP=grep\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# Check whether --with-gnu-ld was given.\nif test \"${with_gnu_ld+set}\" = set; then :\n  withval=$with_gnu_ld; test no = \"$withval\" || with_gnu_ld=yes\nelse\n  with_gnu_ld=no\nfi\n\nac_prog=ld\nif test yes = \"$GCC\"; then\n  # Check if gcc -print-prog-name=ld gives a path.\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for ld used by $CC\" >&5\n$as_echo_n \"checking for ld used by $CC... \" >&6; }\n  case $host in\n  *-*-mingw*)\n    # gcc leaves a trailing carriage return, which upsets mingw\n    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\\015'` ;;\n  *)\n    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;\n  esac\n  case $ac_prog in\n    # Accept absolute paths.\n    [\\\\/]* | ?:[\\\\/]*)\n      re_direlt='/[^/][^/]*/\\.\\./'\n      # Canonicalize the pathname of ld\n      ac_prog=`$ECHO \"$ac_prog\"| $SED 's%\\\\\\\\%/%g'`\n      while $ECHO \"$ac_prog\" | $GREP \"$re_direlt\" > /dev/null 2>&1; do\n\tac_prog=`$ECHO $ac_prog| $SED \"s%$re_direlt%/%\"`\n      done\n      test -z \"$LD\" && LD=$ac_prog\n      ;;\n  \"\")\n    # If it fails, then pretend we aren't using GCC.\n    ac_prog=ld\n    ;;\n  *)\n    # If it is relative, then search for the first ld in PATH.\n    with_gnu_ld=unknown\n    ;;\n  esac\nelif test yes = \"$with_gnu_ld\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for GNU ld\" >&5\n$as_echo_n \"checking for GNU ld... \" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for non-GNU ld\" >&5\n$as_echo_n \"checking for non-GNU ld... \" >&6; }\nfi\nif ${lt_cv_path_LD+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -z \"$LD\"; then\n  lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR\n  for ac_dir in $PATH; do\n    IFS=$lt_save_ifs\n    test -z \"$ac_dir\" && ac_dir=.\n    if test -f \"$ac_dir/$ac_prog\" || test -f \"$ac_dir/$ac_prog$ac_exeext\"; then\n      lt_cv_path_LD=$ac_dir/$ac_prog\n      # Check to see if the program is GNU ld.  I'd rather use --version,\n      # but apparently some variants of GNU ld only accept -v.\n      # Break only if it was the GNU/non-GNU ld that we prefer.\n      case `\"$lt_cv_path_LD\" -v 2>&1 </dev/null` in\n      *GNU* | *'with BFD'*)\n\ttest no != \"$with_gnu_ld\" && break\n\t;;\n      *)\n\ttest yes != \"$with_gnu_ld\" && break\n\t;;\n      esac\n    fi\n  done\n  IFS=$lt_save_ifs\nelse\n  lt_cv_path_LD=$LD # Let the user override the test with a path.\nfi\nfi\n\nLD=$lt_cv_path_LD\nif test -n \"$LD\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $LD\" >&5\n$as_echo \"$LD\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\ntest -z \"$LD\" && as_fn_error $? \"no acceptable ld found in \\$PATH\" \"$LINENO\" 5\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld\" >&5\n$as_echo_n \"checking if the linker ($LD) is GNU ld... \" >&6; }\nif ${lt_cv_prog_gnu_ld+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  # I'd rather use --version here, but apparently some GNU lds only accept -v.\ncase `$LD -v 2>&1 </dev/null` in\n*GNU* | *'with BFD'*)\n  lt_cv_prog_gnu_ld=yes\n  ;;\n*)\n  lt_cv_prog_gnu_ld=no\n  ;;\nesac\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld\" >&5\n$as_echo \"$lt_cv_prog_gnu_ld\" >&6; }\nwith_gnu_ld=$lt_cv_prog_gnu_ld\n\n\n\n\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)\" >&5\n$as_echo_n \"checking for BSD- or MS-compatible name lister (nm)... \" >&6; }\nif ${lt_cv_path_NM+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$NM\"; then\n  # Let the user override the test.\n  lt_cv_path_NM=$NM\nelse\n  lt_nm_to_check=${ac_tool_prefix}nm\n  if test -n \"$ac_tool_prefix\" && test \"$build\" = \"$host\"; then\n    lt_nm_to_check=\"$lt_nm_to_check nm\"\n  fi\n  for lt_tmp_nm in $lt_nm_to_check; do\n    lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR\n    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do\n      IFS=$lt_save_ifs\n      test -z \"$ac_dir\" && ac_dir=.\n      tmp_nm=$ac_dir/$lt_tmp_nm\n      if test -f \"$tmp_nm\" || test -f \"$tmp_nm$ac_exeext\"; then\n\t# Check to see if the nm accepts a BSD-compat flag.\n\t# Adding the 'sed 1q' prevents false positives on HP-UX, which says:\n\t#   nm: unknown option \"B\" ignored\n\t# Tru64's nm complains that /dev/null is an invalid object file\n\t# MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty\n\tcase $build_os in\n\tmingw*) lt_bad_file=conftest.nm/nofile ;;\n\t*) lt_bad_file=/dev/null ;;\n\tesac\n\tcase `\"$tmp_nm\" -B $lt_bad_file 2>&1 | sed '1q'` in\n\t*$lt_bad_file* | *'Invalid file or object type'*)\n\t  lt_cv_path_NM=\"$tmp_nm -B\"\n\t  break 2\n\t  ;;\n\t*)\n\t  case `\"$tmp_nm\" -p /dev/null 2>&1 | sed '1q'` in\n\t  */dev/null*)\n\t    lt_cv_path_NM=\"$tmp_nm -p\"\n\t    break 2\n\t    ;;\n\t  *)\n\t    lt_cv_path_NM=${lt_cv_path_NM=\"$tmp_nm\"} # keep the first match, but\n\t    continue # so that we can try to find one that supports BSD flags\n\t    ;;\n\t  esac\n\t  ;;\n\tesac\n      fi\n    done\n    IFS=$lt_save_ifs\n  done\n  : ${lt_cv_path_NM=no}\nfi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM\" >&5\n$as_echo \"$lt_cv_path_NM\" >&6; }\nif test no != \"$lt_cv_path_NM\"; then\n  NM=$lt_cv_path_NM\nelse\n  # Didn't find any BSD compatible name lister, look for dumpbin.\n  if test -n \"$DUMPBIN\"; then :\n    # Let the user override the test.\n  else\n    if test -n \"$ac_tool_prefix\"; then\n  for ac_prog in dumpbin \"link -dump\"\n  do\n    # Extract the first word of \"$ac_tool_prefix$ac_prog\", so it can be a program name with args.\nset dummy $ac_tool_prefix$ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_DUMPBIN+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$DUMPBIN\"; then\n  ac_cv_prog_DUMPBIN=\"$DUMPBIN\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_DUMPBIN=\"$ac_tool_prefix$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nDUMPBIN=$ac_cv_prog_DUMPBIN\nif test -n \"$DUMPBIN\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DUMPBIN\" >&5\n$as_echo \"$DUMPBIN\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n    test -n \"$DUMPBIN\" && break\n  done\nfi\nif test -z \"$DUMPBIN\"; then\n  ac_ct_DUMPBIN=$DUMPBIN\n  for ac_prog in dumpbin \"link -dump\"\ndo\n  # Extract the first word of \"$ac_prog\", so it can be a program name with args.\nset dummy $ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_DUMPBIN\"; then\n  ac_cv_prog_ac_ct_DUMPBIN=\"$ac_ct_DUMPBIN\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_DUMPBIN=\"$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN\nif test -n \"$ac_ct_DUMPBIN\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN\" >&5\n$as_echo \"$ac_ct_DUMPBIN\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  test -n \"$ac_ct_DUMPBIN\" && break\ndone\n\n  if test \"x$ac_ct_DUMPBIN\" = x; then\n    DUMPBIN=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DUMPBIN=$ac_ct_DUMPBIN\n  fi\nfi\n\n    case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in\n    *COFF*)\n      DUMPBIN=\"$DUMPBIN -symbols -headers\"\n      ;;\n    *)\n      DUMPBIN=:\n      ;;\n    esac\n  fi\n\n  if test : != \"$DUMPBIN\"; then\n    NM=$DUMPBIN\n  fi\nfi\ntest -z \"$NM\" && NM=nm\n\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface\" >&5\n$as_echo_n \"checking the name lister ($NM) interface... \" >&6; }\nif ${lt_cv_nm_interface+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_nm_interface=\"BSD nm\"\n  echo \"int some_variable = 0;\" > conftest.$ac_ext\n  (eval echo \"\\\"\\$as_me:$LINENO: $ac_compile\\\"\" >&5)\n  (eval \"$ac_compile\" 2>conftest.err)\n  cat conftest.err >&5\n  (eval echo \"\\\"\\$as_me:$LINENO: $NM \\\\\\\"conftest.$ac_objext\\\\\\\"\\\"\" >&5)\n  (eval \"$NM \\\"conftest.$ac_objext\\\"\" 2>conftest.err > conftest.out)\n  cat conftest.err >&5\n  (eval echo \"\\\"\\$as_me:$LINENO: output\\\"\" >&5)\n  cat conftest.out >&5\n  if $GREP 'External.*some_variable' conftest.out > /dev/null; then\n    lt_cv_nm_interface=\"MS dumpbin\"\n  fi\n  rm -f conftest*\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface\" >&5\n$as_echo \"$lt_cv_nm_interface\" >&6; }\n\n# find the maximum length of command line arguments\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments\" >&5\n$as_echo_n \"checking the maximum length of command line arguments... \" >&6; }\nif ${lt_cv_sys_max_cmd_len+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n    i=0\n  teststring=ABCD\n\n  case $build_os in\n  msdosdjgpp*)\n    # On DJGPP, this test can blow up pretty badly due to problems in libc\n    # (any single argument exceeding 2000 bytes causes a buffer overrun\n    # during glob expansion).  Even if it were fixed, the result of this\n    # check would be larger than it should be.\n    lt_cv_sys_max_cmd_len=12288;    # 12K is about right\n    ;;\n\n  gnu*)\n    # Under GNU Hurd, this test is not required because there is\n    # no limit to the length of command line arguments.\n    # Libtool will interpret -1 as no limit whatsoever\n    lt_cv_sys_max_cmd_len=-1;\n    ;;\n\n  cygwin* | mingw* | cegcc*)\n    # On Win9x/ME, this test blows up -- it succeeds, but takes\n    # about 5 minutes as the teststring grows exponentially.\n    # Worse, since 9x/ME are not pre-emptively multitasking,\n    # you end up with a \"frozen\" computer, even though with patience\n    # the test eventually succeeds (with a max line length of 256k).\n    # Instead, let's just punt: use the minimum linelength reported by\n    # all of the supported platforms: 8192 (on NT/2K/XP).\n    lt_cv_sys_max_cmd_len=8192;\n    ;;\n\n  mint*)\n    # On MiNT this can take a long time and run out of memory.\n    lt_cv_sys_max_cmd_len=8192;\n    ;;\n\n  amigaos*)\n    # On AmigaOS with pdksh, this test takes hours, literally.\n    # So we just punt and use a minimum line length of 8192.\n    lt_cv_sys_max_cmd_len=8192;\n    ;;\n\n  bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)\n    # This has been around since 386BSD, at least.  Likely further.\n    if test -x /sbin/sysctl; then\n      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`\n    elif test -x /usr/sbin/sysctl; then\n      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`\n    else\n      lt_cv_sys_max_cmd_len=65536\t# usable default for all BSDs\n    fi\n    # And add a safety zone\n    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \\/ 4`\n    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \\* 3`\n    ;;\n\n  interix*)\n    # We know the value 262144 and hardcode it with a safety zone (like BSD)\n    lt_cv_sys_max_cmd_len=196608\n    ;;\n\n  os2*)\n    # The test takes a long time on OS/2.\n    lt_cv_sys_max_cmd_len=8192\n    ;;\n\n  osf*)\n    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure\n    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not\n    # nice to cause kernel panics so lets avoid the loop below.\n    # First set a reasonable default.\n    lt_cv_sys_max_cmd_len=16384\n    #\n    if test -x /sbin/sysconfig; then\n      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in\n        *1*) lt_cv_sys_max_cmd_len=-1 ;;\n      esac\n    fi\n    ;;\n  sco3.2v5*)\n    lt_cv_sys_max_cmd_len=102400\n    ;;\n  sysv5* | sco5v6* | sysv4.2uw2*)\n    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`\n    if test -n \"$kargmax\"; then\n      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[\t ]//'`\n    else\n      lt_cv_sys_max_cmd_len=32768\n    fi\n    ;;\n  *)\n    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`\n    if test -n \"$lt_cv_sys_max_cmd_len\" && \\\n       test undefined != \"$lt_cv_sys_max_cmd_len\"; then\n      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \\/ 4`\n      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \\* 3`\n    else\n      # Make teststring a little bigger before we do anything with it.\n      # a 1K string should be a reasonable start.\n      for i in 1 2 3 4 5 6 7 8; do\n        teststring=$teststring$teststring\n      done\n      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}\n      # If test is not a shell built-in, we'll probably end up computing a\n      # maximum length that is only half of the actual maximum length, but\n      # we can't tell.\n      while { test X`env echo \"$teststring$teststring\" 2>/dev/null` \\\n\t         = \"X$teststring$teststring\"; } >/dev/null 2>&1 &&\n\t      test 17 != \"$i\" # 1/2 MB should be enough\n      do\n        i=`expr $i + 1`\n        teststring=$teststring$teststring\n      done\n      # Only check the string length outside the loop.\n      lt_cv_sys_max_cmd_len=`expr \"X$teststring\" : \".*\" 2>&1`\n      teststring=\n      # Add a significant safety factor because C++ compilers can tack on\n      # massive amounts of additional arguments before passing them to the\n      # linker.  It appears as though 1/2 is a usable value.\n      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \\/ 2`\n    fi\n    ;;\n  esac\n\nfi\n\nif test -n \"$lt_cv_sys_max_cmd_len\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len\" >&5\n$as_echo \"$lt_cv_sys_max_cmd_len\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: none\" >&5\n$as_echo \"none\" >&6; }\nfi\nmax_cmd_len=$lt_cv_sys_max_cmd_len\n\n\n\n\n\n\n: ${CP=\"cp -f\"}\n: ${MV=\"mv -f\"}\n: ${RM=\"rm -f\"}\n\nif ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then\n  lt_unset=unset\nelse\n  lt_unset=false\nfi\n\n\n\n\n\n# test EBCDIC or ASCII\ncase `echo X|tr X '\\101'` in\n A) # ASCII based system\n    # \\n is not interpreted correctly by Solaris 8 /usr/ucb/tr\n  lt_SP2NL='tr \\040 \\012'\n  lt_NL2SP='tr \\015\\012 \\040\\040'\n  ;;\n *) # EBCDIC based system\n  lt_SP2NL='tr \\100 \\n'\n  lt_NL2SP='tr \\r\\n \\100\\100'\n  ;;\nesac\n\n\n\n\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format\" >&5\n$as_echo_n \"checking how to convert $build file names to $host format... \" >&6; }\nif ${lt_cv_to_host_file_cmd+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $host in\n  *-*-mingw* )\n    case $build in\n      *-*-mingw* ) # actually msys\n        lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32\n        ;;\n      *-*-cygwin* )\n        lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32\n        ;;\n      * ) # otherwise, assume *nix\n        lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32\n        ;;\n    esac\n    ;;\n  *-*-cygwin* )\n    case $build in\n      *-*-mingw* ) # actually msys\n        lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin\n        ;;\n      *-*-cygwin* )\n        lt_cv_to_host_file_cmd=func_convert_file_noop\n        ;;\n      * ) # otherwise, assume *nix\n        lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin\n        ;;\n    esac\n    ;;\n  * ) # unhandled hosts (and \"normal\" native builds)\n    lt_cv_to_host_file_cmd=func_convert_file_noop\n    ;;\nesac\n\nfi\n\nto_host_file_cmd=$lt_cv_to_host_file_cmd\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd\" >&5\n$as_echo \"$lt_cv_to_host_file_cmd\" >&6; }\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format\" >&5\n$as_echo_n \"checking how to convert $build file names to toolchain format... \" >&6; }\nif ${lt_cv_to_tool_file_cmd+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  #assume ordinary cross tools, or native build.\nlt_cv_to_tool_file_cmd=func_convert_file_noop\ncase $host in\n  *-*-mingw* )\n    case $build in\n      *-*-mingw* ) # actually msys\n        lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32\n        ;;\n    esac\n    ;;\nesac\n\nfi\n\nto_tool_file_cmd=$lt_cv_to_tool_file_cmd\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd\" >&5\n$as_echo \"$lt_cv_to_tool_file_cmd\" >&6; }\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files\" >&5\n$as_echo_n \"checking for $LD option to reload object files... \" >&6; }\nif ${lt_cv_ld_reload_flag+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_ld_reload_flag='-r'\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag\" >&5\n$as_echo \"$lt_cv_ld_reload_flag\" >&6; }\nreload_flag=$lt_cv_ld_reload_flag\ncase $reload_flag in\n\"\" | \" \"*) ;;\n*) reload_flag=\" $reload_flag\" ;;\nesac\nreload_cmds='$LD$reload_flag -o $output$reload_objs'\ncase $host_os in\n  cygwin* | mingw* | pw32* | cegcc*)\n    if test yes != \"$GCC\"; then\n      reload_cmds=false\n    fi\n    ;;\n  darwin*)\n    if test yes = \"$GCC\"; then\n      reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'\n    else\n      reload_cmds='$LD$reload_flag -o $output$reload_objs'\n    fi\n    ;;\nesac\n\n\n\n\n\n\n\n\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}objdump\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}objdump; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_OBJDUMP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$OBJDUMP\"; then\n  ac_cv_prog_OBJDUMP=\"$OBJDUMP\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_OBJDUMP=\"${ac_tool_prefix}objdump\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nOBJDUMP=$ac_cv_prog_OBJDUMP\nif test -n \"$OBJDUMP\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $OBJDUMP\" >&5\n$as_echo \"$OBJDUMP\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_OBJDUMP\"; then\n  ac_ct_OBJDUMP=$OBJDUMP\n  # Extract the first word of \"objdump\", so it can be a program name with args.\nset dummy objdump; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_OBJDUMP\"; then\n  ac_cv_prog_ac_ct_OBJDUMP=\"$ac_ct_OBJDUMP\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_OBJDUMP=\"objdump\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP\nif test -n \"$ac_ct_OBJDUMP\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP\" >&5\n$as_echo \"$ac_ct_OBJDUMP\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_OBJDUMP\" = x; then\n    OBJDUMP=\"false\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    OBJDUMP=$ac_ct_OBJDUMP\n  fi\nelse\n  OBJDUMP=\"$ac_cv_prog_OBJDUMP\"\nfi\n\ntest -z \"$OBJDUMP\" && OBJDUMP=objdump\n\n\n\n\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries\" >&5\n$as_echo_n \"checking how to recognize dependent libraries... \" >&6; }\nif ${lt_cv_deplibs_check_method+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_file_magic_cmd='$MAGIC_CMD'\nlt_cv_file_magic_test_file=\nlt_cv_deplibs_check_method='unknown'\n# Need to set the preceding variable on all platforms that support\n# interlibrary dependencies.\n# 'none' -- dependencies not supported.\n# 'unknown' -- same as none, but documents that we really don't know.\n# 'pass_all' -- all dependencies passed with no checks.\n# 'test_compile' -- check by making test program.\n# 'file_magic [[regex]]' -- check by looking for files in library path\n# that responds to the $file_magic_cmd with a given extended regex.\n# If you have 'file' or equivalent on your system and you're not sure\n# whether 'pass_all' will *always* work, you probably want this one.\n\ncase $host_os in\naix[4-9]*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\nbeos*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\nbsdi[45]*)\n  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'\n  lt_cv_file_magic_cmd='/usr/bin/file -L'\n  lt_cv_file_magic_test_file=/shlib/libc.so\n  ;;\n\ncygwin*)\n  # func_win32_libid is a shell function defined in ltmain.sh\n  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'\n  lt_cv_file_magic_cmd='func_win32_libid'\n  ;;\n\nmingw* | pw32*)\n  # Base MSYS/MinGW do not provide the 'file' command needed by\n  # func_win32_libid shell function, so use a weaker test based on 'objdump',\n  # unless we find 'file', for example because we are cross-compiling.\n  if ( file / ) >/dev/null 2>&1; then\n    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'\n    lt_cv_file_magic_cmd='func_win32_libid'\n  else\n    # Keep this pattern in sync with the one in func_win32_libid.\n    lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'\n    lt_cv_file_magic_cmd='$OBJDUMP -f'\n  fi\n  ;;\n\ncegcc*)\n  # use the weaker test based on 'objdump'. See mingw*.\n  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'\n  lt_cv_file_magic_cmd='$OBJDUMP -f'\n  ;;\n\ndarwin* | rhapsody*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\nfreebsd* | dragonfly*)\n  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then\n    case $host_cpu in\n    i*86 )\n      # Not sure whether the presence of OpenBSD here was a mistake.\n      # Let's accept both of them until this is cleared up.\n      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'\n      lt_cv_file_magic_cmd=/usr/bin/file\n      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`\n      ;;\n    esac\n  else\n    lt_cv_deplibs_check_method=pass_all\n  fi\n  ;;\n\nhaiku*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\nhpux10.20* | hpux11*)\n  lt_cv_file_magic_cmd=/usr/bin/file\n  case $host_cpu in\n  ia64*)\n    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'\n    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so\n    ;;\n  hppa*64*)\n    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\\.[0-9]'\n    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl\n    ;;\n  *)\n    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\\.[0-9]) shared library'\n    lt_cv_file_magic_test_file=/usr/lib/libc.sl\n    ;;\n  esac\n  ;;\n\ninterix[3-9]*)\n  # PIC code is broken on Interix 3.x, that's why |\\.a not |_pic\\.a here\n  lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\\.so|\\.a)$'\n  ;;\n\nirix5* | irix6* | nonstopux*)\n  case $LD in\n  *-32|*\"-32 \") libmagic=32-bit;;\n  *-n32|*\"-n32 \") libmagic=N32;;\n  *-64|*\"-64 \") libmagic=64-bit;;\n  *) libmagic=never-match;;\n  esac\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\n# This must be glibc/ELF.\nlinux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\nnetbsd* | netbsdelf*-gnu)\n  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then\n    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\\.so\\.[0-9]+\\.[0-9]+|_pic\\.a)$'\n  else\n    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\\.so|_pic\\.a)$'\n  fi\n  ;;\n\nnewos6*)\n  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'\n  lt_cv_file_magic_cmd=/usr/bin/file\n  lt_cv_file_magic_test_file=/usr/lib/libnls.so\n  ;;\n\n*nto* | *qnx*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\nopenbsd* | bitrig*)\n  if test -z \"`echo __ELF__ | $CC -E - | $GREP __ELF__`\"; then\n    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\\.so\\.[0-9]+\\.[0-9]+|\\.so|_pic\\.a)$'\n  else\n    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\\.so\\.[0-9]+\\.[0-9]+|_pic\\.a)$'\n  fi\n  ;;\n\nosf3* | osf4* | osf5*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\nrdos*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\nsolaris*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\nsysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\n\nsysv4 | sysv4.3*)\n  case $host_vendor in\n  motorola)\n    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'\n    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`\n    ;;\n  ncr)\n    lt_cv_deplibs_check_method=pass_all\n    ;;\n  sequent)\n    lt_cv_file_magic_cmd='/bin/file'\n    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'\n    ;;\n  sni)\n    lt_cv_file_magic_cmd='/bin/file'\n    lt_cv_deplibs_check_method=\"file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib\"\n    lt_cv_file_magic_test_file=/lib/libc.so\n    ;;\n  siemens)\n    lt_cv_deplibs_check_method=pass_all\n    ;;\n  pc)\n    lt_cv_deplibs_check_method=pass_all\n    ;;\n  esac\n  ;;\n\ntpf*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\nos2*)\n  lt_cv_deplibs_check_method=pass_all\n  ;;\nesac\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method\" >&5\n$as_echo \"$lt_cv_deplibs_check_method\" >&6; }\n\nfile_magic_glob=\nwant_nocaseglob=no\nif test \"$build\" = \"$host\"; then\n  case $host_os in\n  mingw* | pw32*)\n    if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then\n      want_nocaseglob=yes\n    else\n      file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e \"s/\\(..\\)/s\\/[\\1]\\/[\\1]\\/g;/g\"`\n    fi\n    ;;\n  esac\nfi\n\nfile_magic_cmd=$lt_cv_file_magic_cmd\ndeplibs_check_method=$lt_cv_deplibs_check_method\ntest -z \"$deplibs_check_method\" && deplibs_check_method=unknown\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}dlltool\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}dlltool; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_DLLTOOL+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$DLLTOOL\"; then\n  ac_cv_prog_DLLTOOL=\"$DLLTOOL\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_DLLTOOL=\"${ac_tool_prefix}dlltool\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nDLLTOOL=$ac_cv_prog_DLLTOOL\nif test -n \"$DLLTOOL\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DLLTOOL\" >&5\n$as_echo \"$DLLTOOL\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_DLLTOOL\"; then\n  ac_ct_DLLTOOL=$DLLTOOL\n  # Extract the first word of \"dlltool\", so it can be a program name with args.\nset dummy dlltool; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_DLLTOOL\"; then\n  ac_cv_prog_ac_ct_DLLTOOL=\"$ac_ct_DLLTOOL\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_DLLTOOL=\"dlltool\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL\nif test -n \"$ac_ct_DLLTOOL\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL\" >&5\n$as_echo \"$ac_ct_DLLTOOL\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_DLLTOOL\" = x; then\n    DLLTOOL=\"false\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DLLTOOL=$ac_ct_DLLTOOL\n  fi\nelse\n  DLLTOOL=\"$ac_cv_prog_DLLTOOL\"\nfi\n\ntest -z \"$DLLTOOL\" && DLLTOOL=dlltool\n\n\n\n\n\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries\" >&5\n$as_echo_n \"checking how to associate runtime and link libraries... \" >&6; }\nif ${lt_cv_sharedlib_from_linklib_cmd+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_sharedlib_from_linklib_cmd='unknown'\n\ncase $host_os in\ncygwin* | mingw* | pw32* | cegcc*)\n  # two different shell functions defined in ltmain.sh;\n  # decide which one to use based on capabilities of $DLLTOOL\n  case `$DLLTOOL --help 2>&1` in\n  *--identify-strict*)\n    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib\n    ;;\n  *)\n    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback\n    ;;\n  esac\n  ;;\n*)\n  # fallback: assume linklib IS sharedlib\n  lt_cv_sharedlib_from_linklib_cmd=$ECHO\n  ;;\nesac\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd\" >&5\n$as_echo \"$lt_cv_sharedlib_from_linklib_cmd\" >&6; }\nsharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd\ntest -z \"$sharedlib_from_linklib_cmd\" && sharedlib_from_linklib_cmd=$ECHO\n\n\n\n\n\n\n\nif test -n \"$ac_tool_prefix\"; then\n  for ac_prog in ar\n  do\n    # Extract the first word of \"$ac_tool_prefix$ac_prog\", so it can be a program name with args.\nset dummy $ac_tool_prefix$ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_AR+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$AR\"; then\n  ac_cv_prog_AR=\"$AR\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_AR=\"$ac_tool_prefix$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nAR=$ac_cv_prog_AR\nif test -n \"$AR\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $AR\" >&5\n$as_echo \"$AR\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n    test -n \"$AR\" && break\n  done\nfi\nif test -z \"$AR\"; then\n  ac_ct_AR=$AR\n  for ac_prog in ar\ndo\n  # Extract the first word of \"$ac_prog\", so it can be a program name with args.\nset dummy $ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_AR+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_AR\"; then\n  ac_cv_prog_ac_ct_AR=\"$ac_ct_AR\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_AR=\"$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_AR=$ac_cv_prog_ac_ct_AR\nif test -n \"$ac_ct_AR\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR\" >&5\n$as_echo \"$ac_ct_AR\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  test -n \"$ac_ct_AR\" && break\ndone\n\n  if test \"x$ac_ct_AR\" = x; then\n    AR=\"false\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    AR=$ac_ct_AR\n  fi\nfi\n\n: ${AR=ar}\n: ${AR_FLAGS=cru}\n\n\n\n\n\n\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support\" >&5\n$as_echo_n \"checking for archiver @FILE support... \" >&6; }\nif ${lt_cv_ar_at_file+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_ar_at_file=no\n   cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  echo conftest.$ac_objext > conftest.lst\n      lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5'\n      { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$lt_ar_try\\\"\"; } >&5\n  (eval $lt_ar_try) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\n      if test 0 -eq \"$ac_status\"; then\n\t# Ensure the archiver fails upon bogus file names.\n\trm -f conftest.$ac_objext libconftest.a\n\t{ { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$lt_ar_try\\\"\"; } >&5\n  (eval $lt_ar_try) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\n\tif test 0 -ne \"$ac_status\"; then\n          lt_cv_ar_at_file=@\n        fi\n      fi\n      rm -f conftest.* libconftest.a\n\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file\" >&5\n$as_echo \"$lt_cv_ar_at_file\" >&6; }\n\nif test no = \"$lt_cv_ar_at_file\"; then\n  archiver_list_spec=\nelse\n  archiver_list_spec=$lt_cv_ar_at_file\nfi\n\n\n\n\n\n\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}strip\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}strip; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_STRIP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$STRIP\"; then\n  ac_cv_prog_STRIP=\"$STRIP\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_STRIP=\"${ac_tool_prefix}strip\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nSTRIP=$ac_cv_prog_STRIP\nif test -n \"$STRIP\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $STRIP\" >&5\n$as_echo \"$STRIP\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_STRIP\"; then\n  ac_ct_STRIP=$STRIP\n  # Extract the first word of \"strip\", so it can be a program name with args.\nset dummy strip; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_STRIP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_STRIP\"; then\n  ac_cv_prog_ac_ct_STRIP=\"$ac_ct_STRIP\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_STRIP=\"strip\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP\nif test -n \"$ac_ct_STRIP\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP\" >&5\n$as_echo \"$ac_ct_STRIP\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_STRIP\" = x; then\n    STRIP=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    STRIP=$ac_ct_STRIP\n  fi\nelse\n  STRIP=\"$ac_cv_prog_STRIP\"\nfi\n\ntest -z \"$STRIP\" && STRIP=:\n\n\n\n\n\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}ranlib\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}ranlib; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_RANLIB+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$RANLIB\"; then\n  ac_cv_prog_RANLIB=\"$RANLIB\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_RANLIB=\"${ac_tool_prefix}ranlib\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nRANLIB=$ac_cv_prog_RANLIB\nif test -n \"$RANLIB\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $RANLIB\" >&5\n$as_echo \"$RANLIB\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_RANLIB\"; then\n  ac_ct_RANLIB=$RANLIB\n  # Extract the first word of \"ranlib\", so it can be a program name with args.\nset dummy ranlib; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_RANLIB+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_RANLIB\"; then\n  ac_cv_prog_ac_ct_RANLIB=\"$ac_ct_RANLIB\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_RANLIB=\"ranlib\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB\nif test -n \"$ac_ct_RANLIB\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB\" >&5\n$as_echo \"$ac_ct_RANLIB\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_RANLIB\" = x; then\n    RANLIB=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    RANLIB=$ac_ct_RANLIB\n  fi\nelse\n  RANLIB=\"$ac_cv_prog_RANLIB\"\nfi\n\ntest -z \"$RANLIB\" && RANLIB=:\n\n\n\n\n\n\n# Determine commands to create old-style static archives.\nold_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'\nold_postinstall_cmds='chmod 644 $oldlib'\nold_postuninstall_cmds=\n\nif test -n \"$RANLIB\"; then\n  case $host_os in\n  bitrig* | openbsd*)\n    old_postinstall_cmds=\"$old_postinstall_cmds~\\$RANLIB -t \\$tool_oldlib\"\n    ;;\n  *)\n    old_postinstall_cmds=\"$old_postinstall_cmds~\\$RANLIB \\$tool_oldlib\"\n    ;;\n  esac\n  old_archive_cmds=\"$old_archive_cmds~\\$RANLIB \\$tool_oldlib\"\nfi\n\ncase $host_os in\n  darwin*)\n    lock_old_archive_extraction=yes ;;\n  *)\n    lock_old_archive_extraction=no ;;\nesac\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\n\n\n\n\n\n\n\n\n\n\n\n\n# If no C compiler was specified, use CC.\nLTCC=${LTCC-\"$CC\"}\n\n# If no C compiler flags were specified, use CFLAGS.\nLTCFLAGS=${LTCFLAGS-\"$CFLAGS\"}\n\n# Allow CC to be a program name with arguments.\ncompiler=$CC\n\n\n# Check for command to grab the raw symbol name followed by C symbol from nm.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object\" >&5\n$as_echo_n \"checking command to parse $NM output from $compiler object... \" >&6; }\nif ${lt_cv_sys_global_symbol_pipe+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n\n# These are sane defaults that work on at least a few old systems.\n# [They come from Ultrix.  What could be older than Ultrix?!! ;)]\n\n# Character class describing NM global symbol codes.\nsymcode='[BCDEGRST]'\n\n# Regexp to match symbols that can be accessed directly from C.\nsympat='\\([_A-Za-z][_A-Za-z0-9]*\\)'\n\n# Define system-specific variables.\ncase $host_os in\naix*)\n  symcode='[BCDT]'\n  ;;\ncygwin* | mingw* | pw32* | cegcc*)\n  symcode='[ABCDGISTW]'\n  ;;\nhpux*)\n  if test ia64 = \"$host_cpu\"; then\n    symcode='[ABCDEGRST]'\n  fi\n  ;;\nirix* | nonstopux*)\n  symcode='[BCDEGRST]'\n  ;;\nosf*)\n  symcode='[BCDEGQRST]'\n  ;;\nsolaris*)\n  symcode='[BDRT]'\n  ;;\nsco3.2v5*)\n  symcode='[DT]'\n  ;;\nsysv4.2uw2*)\n  symcode='[DT]'\n  ;;\nsysv5* | sco5v6* | unixware* | OpenUNIX*)\n  symcode='[ABDT]'\n  ;;\nsysv4)\n  symcode='[DFNSTU]'\n  ;;\nesac\n\n# If we're using GNU nm, then use its standard symbol codes.\ncase `$NM -V 2>&1` in\n*GNU* | *'with BFD'*)\n  symcode='[ABCDGIRSTW]' ;;\nesac\n\nif test \"$lt_cv_nm_interface\" = \"MS dumpbin\"; then\n  # Gets list of data symbols to import.\n  lt_cv_sys_global_symbol_to_import=\"sed -n -e 's/^I .* \\(.*\\)$/\\1/p'\"\n  # Adjust the below global symbol transforms to fixup imported variables.\n  lt_cdecl_hook=\" -e 's/^I .* \\(.*\\)$/extern __declspec(dllimport) char \\1;/p'\"\n  lt_c_name_hook=\" -e 's/^I .* \\(.*\\)$/  {\\\"\\1\\\", (void *) 0},/p'\"\n  lt_c_name_lib_hook=\"\\\n  -e 's/^I .* \\(lib.*\\)$/  {\\\"\\1\\\", (void *) 0},/p'\\\n  -e 's/^I .* \\(.*\\)$/  {\\\"lib\\1\\\", (void *) 0},/p'\"\nelse\n  # Disable hooks by default.\n  lt_cv_sys_global_symbol_to_import=\n  lt_cdecl_hook=\n  lt_c_name_hook=\n  lt_c_name_lib_hook=\nfi\n\n# Transform an extracted symbol line into a proper C declaration.\n# Some systems (esp. on ia64) link data and code symbols differently,\n# so use this general approach.\nlt_cv_sys_global_symbol_to_cdecl=\"sed -n\"\\\n$lt_cdecl_hook\\\n\" -e 's/^T .* \\(.*\\)$/extern int \\1();/p'\"\\\n\" -e 's/^$symcode$symcode* .* \\(.*\\)$/extern char \\1;/p'\"\n\n# Transform an extracted symbol line into symbol name and symbol address\nlt_cv_sys_global_symbol_to_c_name_address=\"sed -n\"\\\n$lt_c_name_hook\\\n\" -e 's/^: \\(.*\\) .*$/  {\\\"\\1\\\", (void *) 0},/p'\"\\\n\" -e 's/^$symcode$symcode* .* \\(.*\\)$/  {\\\"\\1\\\", (void *) \\&\\1},/p'\"\n\n# Transform an extracted symbol line into symbol name with lib prefix and\n# symbol address.\nlt_cv_sys_global_symbol_to_c_name_address_lib_prefix=\"sed -n\"\\\n$lt_c_name_lib_hook\\\n\" -e 's/^: \\(.*\\) .*$/  {\\\"\\1\\\", (void *) 0},/p'\"\\\n\" -e 's/^$symcode$symcode* .* \\(lib.*\\)$/  {\\\"\\1\\\", (void *) \\&\\1},/p'\"\\\n\" -e 's/^$symcode$symcode* .* \\(.*\\)$/  {\\\"lib\\1\\\", (void *) \\&\\1},/p'\"\n\n# Handle CRLF in mingw tool chain\nopt_cr=\ncase $build_os in\nmingw*)\n  opt_cr=`$ECHO 'x\\{0,1\\}' | tr x '\\015'` # option cr in regexp\n  ;;\nesac\n\n# Try without a prefix underscore, then with it.\nfor ac_symprfx in \"\" \"_\"; do\n\n  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.\n  symxfrm=\"\\\\1 $ac_symprfx\\\\2 \\\\2\"\n\n  # Write the raw and C identifiers.\n  if test \"$lt_cv_nm_interface\" = \"MS dumpbin\"; then\n    # Fake it for dumpbin and say T for any non-static function,\n    # D for any global variable and I for any imported variable.\n    # Also find C++ and __fastcall symbols from MSVC++,\n    # which start with @ or ?.\n    lt_cv_sys_global_symbol_pipe=\"$AWK '\"\\\n\"     {last_section=section; section=\\$ 3};\"\\\n\"     /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};\"\\\n\"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};\"\\\n\"     /^ *Symbol name *: /{split(\\$ 0,sn,\\\":\\\"); si=substr(sn[2],2)};\"\\\n\"     /^ *Type *: code/{print \\\"T\\\",si,substr(si,length(prfx))};\"\\\n\"     /^ *Type *: data/{print \\\"I\\\",si,substr(si,length(prfx))};\"\\\n\"     \\$ 0!~/External *\\|/{next};\"\\\n\"     / 0+ UNDEF /{next}; / UNDEF \\([^|]\\)*()/{next};\"\\\n\"     {if(hide[section]) next};\"\\\n\"     {f=\\\"D\\\"}; \\$ 0~/\\(\\).*\\|/{f=\\\"T\\\"};\"\\\n\"     {split(\\$ 0,a,/\\||\\r/); split(a[2],s)};\"\\\n\"     s[1]~/^[@?]/{print f,s[1],s[1]; next};\"\\\n\"     s[1]~prfx {split(s[1],t,\\\"@\\\"); print f,t[1],substr(t[1],length(prfx))}\"\\\n\"     ' prfx=^$ac_symprfx\"\n  else\n    lt_cv_sys_global_symbol_pipe=\"sed -n -e 's/^.*[\t ]\\($symcode$symcode*\\)[\t ][\t ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'\"\n  fi\n  lt_cv_sys_global_symbol_pipe=\"$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'\"\n\n  # Check to see that the pipe works correctly.\n  pipe_works=no\n\n  rm -f conftest*\n  cat > conftest.$ac_ext <<_LT_EOF\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nchar nm_test_var;\nvoid nm_test_func(void);\nvoid nm_test_func(void){}\n#ifdef __cplusplus\n}\n#endif\nint main(){nm_test_var='a';nm_test_func();return(0);}\n_LT_EOF\n\n  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_compile\\\"\"; } >&5\n  (eval $ac_compile) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then\n    # Now try to grab the symbols.\n    nlist=conftest.nm\n    if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$NM conftest.$ac_objext \\| \"$lt_cv_sys_global_symbol_pipe\" \\> $nlist\\\"\"; } >&5\n  (eval $NM conftest.$ac_objext \\| \"$lt_cv_sys_global_symbol_pipe\" \\> $nlist) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && test -s \"$nlist\"; then\n      # Try sorting and uniquifying the output.\n      if sort \"$nlist\" | uniq > \"$nlist\"T; then\n\tmv -f \"$nlist\"T \"$nlist\"\n      else\n\trm -f \"$nlist\"T\n      fi\n\n      # Make sure that we snagged all the symbols we need.\n      if $GREP ' nm_test_var$' \"$nlist\" >/dev/null; then\n\tif $GREP ' nm_test_func$' \"$nlist\" >/dev/null; then\n\t  cat <<_LT_EOF > conftest.$ac_ext\n/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */\n#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE\n/* DATA imports from DLLs on WIN32 can't be const, because runtime\n   relocations are performed -- see ld's documentation on pseudo-relocs.  */\n# define LT_DLSYM_CONST\n#elif defined __osf__\n/* This system does not cope well with relocations in const data.  */\n# define LT_DLSYM_CONST\n#else\n# define LT_DLSYM_CONST const\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n_LT_EOF\n\t  # Now generate the symbol file.\n\t  eval \"$lt_cv_sys_global_symbol_to_cdecl\"' < \"$nlist\" | $GREP -v main >> conftest.$ac_ext'\n\n\t  cat <<_LT_EOF >> conftest.$ac_ext\n\n/* The mapping between symbol names and symbols.  */\nLT_DLSYM_CONST struct {\n  const char *name;\n  void       *address;\n}\nlt__PROGRAM__LTX_preloaded_symbols[] =\n{\n  { \"@PROGRAM@\", (void *) 0 },\n_LT_EOF\n\t  $SED \"s/^$symcode$symcode* .* \\(.*\\)$/  {\\\"\\1\\\", (void *) \\&\\1},/\" < \"$nlist\" | $GREP -v main >> conftest.$ac_ext\n\t  cat <<\\_LT_EOF >> conftest.$ac_ext\n  {0, (void *) 0}\n};\n\n/* This works around a problem in FreeBSD linker */\n#ifdef FREEBSD_WORKAROUND\nstatic const void *lt_preloaded_setup() {\n  return lt__PROGRAM__LTX_preloaded_symbols;\n}\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n_LT_EOF\n\t  # Now try linking the two files.\n\t  mv conftest.$ac_objext conftstm.$ac_objext\n\t  lt_globsym_save_LIBS=$LIBS\n\t  lt_globsym_save_CFLAGS=$CFLAGS\n\t  LIBS=conftstm.$ac_objext\n\t  CFLAGS=\"$CFLAGS$lt_prog_compiler_no_builtin_flag\"\n\t  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_link\\\"\"; } >&5\n  (eval $ac_link) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && test -s conftest$ac_exeext; then\n\t    pipe_works=yes\n\t  fi\n\t  LIBS=$lt_globsym_save_LIBS\n\t  CFLAGS=$lt_globsym_save_CFLAGS\n\telse\n\t  echo \"cannot find nm_test_func in $nlist\" >&5\n\tfi\n      else\n\techo \"cannot find nm_test_var in $nlist\" >&5\n      fi\n    else\n      echo \"cannot run $lt_cv_sys_global_symbol_pipe\" >&5\n    fi\n  else\n    echo \"$progname: failed program was:\" >&5\n    cat conftest.$ac_ext >&5\n  fi\n  rm -rf conftest* conftst*\n\n  # Do not use the global_symbol_pipe unless it works.\n  if test yes = \"$pipe_works\"; then\n    break\n  else\n    lt_cv_sys_global_symbol_pipe=\n  fi\ndone\n\nfi\n\nif test -z \"$lt_cv_sys_global_symbol_pipe\"; then\n  lt_cv_sys_global_symbol_to_cdecl=\nfi\nif test -z \"$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: failed\" >&5\n$as_echo \"failed\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: ok\" >&5\n$as_echo \"ok\" >&6; }\nfi\n\n# Response file support.\nif test \"$lt_cv_nm_interface\" = \"MS dumpbin\"; then\n  nm_file_list_spec='@'\nelif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then\n  nm_file_list_spec='@'\nfi\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\n\n\n\n\n\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for sysroot\" >&5\n$as_echo_n \"checking for sysroot... \" >&6; }\n\n# Check whether --with-sysroot was given.\nif test \"${with_sysroot+set}\" = set; then :\n  withval=$with_sysroot;\nelse\n  with_sysroot=no\nfi\n\n\nlt_sysroot=\ncase $with_sysroot in #(\n yes)\n   if test yes = \"$GCC\"; then\n     lt_sysroot=`$CC --print-sysroot 2>/dev/null`\n   fi\n   ;; #(\n /*)\n   lt_sysroot=`echo \"$with_sysroot\" | sed -e \"$sed_quote_subst\"`\n   ;; #(\n no|'')\n   ;; #(\n *)\n   { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $with_sysroot\" >&5\n$as_echo \"$with_sysroot\" >&6; }\n   as_fn_error $? \"The sysroot must be an absolute path.\" \"$LINENO\" 5\n   ;;\nesac\n\n { $as_echo \"$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}\" >&5\n$as_echo \"${lt_sysroot:-no}\" >&6; }\n\n\n\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for a working dd\" >&5\n$as_echo_n \"checking for a working dd... \" >&6; }\nif ${ac_cv_path_lt_DD+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  printf 0123456789abcdef0123456789abcdef >conftest.i\ncat conftest.i conftest.i >conftest2.i\n: ${lt_DD:=$DD}\nif test -z \"$lt_DD\"; then\n  ac_path_lt_DD_found=false\n  # Loop through the user's path and test for each of PROGNAME-LIST\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in dd; do\n    for ac_exec_ext in '' $ac_executable_extensions; do\n      ac_path_lt_DD=\"$as_dir/$ac_prog$ac_exec_ext\"\n      as_fn_executable_p \"$ac_path_lt_DD\" || continue\nif \"$ac_path_lt_DD\" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then\n  cmp -s conftest.i conftest.out \\\n  && ac_cv_path_lt_DD=\"$ac_path_lt_DD\" ac_path_lt_DD_found=:\nfi\n      $ac_path_lt_DD_found && break 3\n    done\n  done\n  done\nIFS=$as_save_IFS\n  if test -z \"$ac_cv_path_lt_DD\"; then\n    :\n  fi\nelse\n  ac_cv_path_lt_DD=$lt_DD\nfi\n\nrm -f conftest.i conftest2.i conftest.out\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD\" >&5\n$as_echo \"$ac_cv_path_lt_DD\" >&6; }\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes\" >&5\n$as_echo_n \"checking how to truncate binary pipes... \" >&6; }\nif ${lt_cv_truncate_bin+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  printf 0123456789abcdef0123456789abcdef >conftest.i\ncat conftest.i conftest.i >conftest2.i\nlt_cv_truncate_bin=\nif \"$ac_cv_path_lt_DD\" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then\n  cmp -s conftest.i conftest.out \\\n  && lt_cv_truncate_bin=\"$ac_cv_path_lt_DD bs=4096 count=1\"\nfi\nrm -f conftest.i conftest2.i conftest.out\ntest -z \"$lt_cv_truncate_bin\" && lt_cv_truncate_bin=\"$SED -e 4q\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin\" >&5\n$as_echo \"$lt_cv_truncate_bin\" >&6; }\n\n\n\n\n\n\n\n# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.\nfunc_cc_basename ()\n{\n    for cc_temp in $*\"\"; do\n      case $cc_temp in\n        compile | *[\\\\/]compile | ccache | *[\\\\/]ccache ) ;;\n        distcc | *[\\\\/]distcc | purify | *[\\\\/]purify ) ;;\n        \\-*) ;;\n        *) break;;\n      esac\n    done\n    func_cc_basename_result=`$ECHO \"$cc_temp\" | $SED \"s%.*/%%; s%^$host_alias-%%\"`\n}\n\n# Check whether --enable-libtool-lock was given.\nif test \"${enable_libtool_lock+set}\" = set; then :\n  enableval=$enable_libtool_lock;\nfi\n\ntest no = \"$enable_libtool_lock\" || enable_libtool_lock=yes\n\n# Some flags need to be propagated to the compiler or linker for good\n# libtool support.\ncase $host in\nia64-*-hpux*)\n  # Find out what ABI is being produced by ac_compile, and set mode\n  # options accordingly.\n  echo 'int i;' > conftest.$ac_ext\n  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_compile\\\"\"; } >&5\n  (eval $ac_compile) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then\n    case `/usr/bin/file conftest.$ac_objext` in\n      *ELF-32*)\n\tHPUX_IA64_MODE=32\n\t;;\n      *ELF-64*)\n\tHPUX_IA64_MODE=64\n\t;;\n    esac\n  fi\n  rm -rf conftest*\n  ;;\n*-*-irix6*)\n  # Find out what ABI is being produced by ac_compile, and set linker\n  # options accordingly.\n  echo '#line '$LINENO' \"configure\"' > conftest.$ac_ext\n  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_compile\\\"\"; } >&5\n  (eval $ac_compile) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then\n    if test yes = \"$lt_cv_prog_gnu_ld\"; then\n      case `/usr/bin/file conftest.$ac_objext` in\n\t*32-bit*)\n\t  LD=\"${LD-ld} -melf32bsmip\"\n\t  ;;\n\t*N32*)\n\t  LD=\"${LD-ld} -melf32bmipn32\"\n\t  ;;\n\t*64-bit*)\n\t  LD=\"${LD-ld} -melf64bmip\"\n\t;;\n      esac\n    else\n      case `/usr/bin/file conftest.$ac_objext` in\n\t*32-bit*)\n\t  LD=\"${LD-ld} -32\"\n\t  ;;\n\t*N32*)\n\t  LD=\"${LD-ld} -n32\"\n\t  ;;\n\t*64-bit*)\n\t  LD=\"${LD-ld} -64\"\n\t  ;;\n      esac\n    fi\n  fi\n  rm -rf conftest*\n  ;;\n\nmips64*-*linux*)\n  # Find out what ABI is being produced by ac_compile, and set linker\n  # options accordingly.\n  echo '#line '$LINENO' \"configure\"' > conftest.$ac_ext\n  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_compile\\\"\"; } >&5\n  (eval $ac_compile) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then\n    emul=elf\n    case `/usr/bin/file conftest.$ac_objext` in\n      *32-bit*)\n\temul=\"${emul}32\"\n\t;;\n      *64-bit*)\n\temul=\"${emul}64\"\n\t;;\n    esac\n    case `/usr/bin/file conftest.$ac_objext` in\n      *MSB*)\n\temul=\"${emul}btsmip\"\n\t;;\n      *LSB*)\n\temul=\"${emul}ltsmip\"\n\t;;\n    esac\n    case `/usr/bin/file conftest.$ac_objext` in\n      *N32*)\n\temul=\"${emul}n32\"\n\t;;\n    esac\n    LD=\"${LD-ld} -m $emul\"\n  fi\n  rm -rf conftest*\n  ;;\n\nx86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \\\ns390*-*linux*|s390*-*tpf*|sparc*-*linux*)\n  # Find out what ABI is being produced by ac_compile, and set linker\n  # options accordingly.  Note that the listed cases only cover the\n  # situations where additional linker options are needed (such as when\n  # doing 32-bit compilation for a host where ld defaults to 64-bit, or\n  # vice versa); the common cases where no linker options are needed do\n  # not appear in the list.\n  echo 'int i;' > conftest.$ac_ext\n  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_compile\\\"\"; } >&5\n  (eval $ac_compile) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then\n    case `/usr/bin/file conftest.o` in\n      *32-bit*)\n\tcase $host in\n\t  x86_64-*kfreebsd*-gnu)\n\t    LD=\"${LD-ld} -m elf_i386_fbsd\"\n\t    ;;\n\t  x86_64-*linux*)\n\t    case `/usr/bin/file conftest.o` in\n\t      *x86-64*)\n\t\tLD=\"${LD-ld} -m elf32_x86_64\"\n\t\t;;\n\t      *)\n\t\tLD=\"${LD-ld} -m elf_i386\"\n\t\t;;\n\t    esac\n\t    ;;\n\t  powerpc64le-*linux*)\n\t    LD=\"${LD-ld} -m elf32lppclinux\"\n\t    ;;\n\t  powerpc64-*linux*)\n\t    LD=\"${LD-ld} -m elf32ppclinux\"\n\t    ;;\n\t  s390x-*linux*)\n\t    LD=\"${LD-ld} -m elf_s390\"\n\t    ;;\n\t  sparc64-*linux*)\n\t    LD=\"${LD-ld} -m elf32_sparc\"\n\t    ;;\n\tesac\n\t;;\n      *64-bit*)\n\tcase $host in\n\t  x86_64-*kfreebsd*-gnu)\n\t    LD=\"${LD-ld} -m elf_x86_64_fbsd\"\n\t    ;;\n\t  x86_64-*linux*)\n\t    LD=\"${LD-ld} -m elf_x86_64\"\n\t    ;;\n\t  powerpcle-*linux*)\n\t    LD=\"${LD-ld} -m elf64lppc\"\n\t    ;;\n\t  powerpc-*linux*)\n\t    LD=\"${LD-ld} -m elf64ppc\"\n\t    ;;\n\t  s390*-*linux*|s390*-*tpf*)\n\t    LD=\"${LD-ld} -m elf64_s390\"\n\t    ;;\n\t  sparc*-*linux*)\n\t    LD=\"${LD-ld} -m elf64_sparc\"\n\t    ;;\n\tesac\n\t;;\n    esac\n  fi\n  rm -rf conftest*\n  ;;\n\n*-*-sco3.2v5*)\n  # On SCO OpenServer 5, we need -belf to get full-featured binaries.\n  SAVE_CFLAGS=$CFLAGS\n  CFLAGS=\"$CFLAGS -belf\"\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf\" >&5\n$as_echo_n \"checking whether the C compiler needs -belf... \" >&6; }\nif ${lt_cv_cc_needs_belf+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n     cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  lt_cv_cc_needs_belf=yes\nelse\n  lt_cv_cc_needs_belf=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\n     ac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf\" >&5\n$as_echo \"$lt_cv_cc_needs_belf\" >&6; }\n  if test yes != \"$lt_cv_cc_needs_belf\"; then\n    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf\n    CFLAGS=$SAVE_CFLAGS\n  fi\n  ;;\n*-*solaris*)\n  # Find out what ABI is being produced by ac_compile, and set linker\n  # options accordingly.\n  echo 'int i;' > conftest.$ac_ext\n  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_compile\\\"\"; } >&5\n  (eval $ac_compile) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then\n    case `/usr/bin/file conftest.o` in\n    *64-bit*)\n      case $lt_cv_prog_gnu_ld in\n      yes*)\n        case $host in\n        i?86-*-solaris*|x86_64-*-solaris*)\n          LD=\"${LD-ld} -m elf_x86_64\"\n          ;;\n        sparc*-*-solaris*)\n          LD=\"${LD-ld} -m elf64_sparc\"\n          ;;\n        esac\n        # GNU ld 2.21 introduced _sol2 emulations.  Use them if available.\n        if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then\n          LD=${LD-ld}_sol2\n        fi\n        ;;\n      *)\n\tif ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then\n\t  LD=\"${LD-ld} -64\"\n\tfi\n\t;;\n      esac\n      ;;\n    esac\n  fi\n  rm -rf conftest*\n  ;;\nesac\n\nneed_locks=$enable_libtool_lock\n\nif test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}mt\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}mt; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_MANIFEST_TOOL+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$MANIFEST_TOOL\"; then\n  ac_cv_prog_MANIFEST_TOOL=\"$MANIFEST_TOOL\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_MANIFEST_TOOL=\"${ac_tool_prefix}mt\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nMANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL\nif test -n \"$MANIFEST_TOOL\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL\" >&5\n$as_echo \"$MANIFEST_TOOL\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_MANIFEST_TOOL\"; then\n  ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL\n  # Extract the first word of \"mt\", so it can be a program name with args.\nset dummy mt; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_MANIFEST_TOOL\"; then\n  ac_cv_prog_ac_ct_MANIFEST_TOOL=\"$ac_ct_MANIFEST_TOOL\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_MANIFEST_TOOL=\"mt\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL\nif test -n \"$ac_ct_MANIFEST_TOOL\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL\" >&5\n$as_echo \"$ac_ct_MANIFEST_TOOL\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_MANIFEST_TOOL\" = x; then\n    MANIFEST_TOOL=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL\n  fi\nelse\n  MANIFEST_TOOL=\"$ac_cv_prog_MANIFEST_TOOL\"\nfi\n\ntest -z \"$MANIFEST_TOOL\" && MANIFEST_TOOL=mt\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool\" >&5\n$as_echo_n \"checking if $MANIFEST_TOOL is a manifest tool... \" >&6; }\nif ${lt_cv_path_mainfest_tool+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_path_mainfest_tool=no\n  echo \"$as_me:$LINENO: $MANIFEST_TOOL '-?'\" >&5\n  $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out\n  cat conftest.err >&5\n  if $GREP 'Manifest Tool' conftest.out > /dev/null; then\n    lt_cv_path_mainfest_tool=yes\n  fi\n  rm -f conftest*\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool\" >&5\n$as_echo \"$lt_cv_path_mainfest_tool\" >&6; }\nif test yes != \"$lt_cv_path_mainfest_tool\"; then\n  MANIFEST_TOOL=:\nfi\n\n\n\n\n\n\n  case $host_os in\n    rhapsody* | darwin*)\n    if test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}dsymutil\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}dsymutil; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_DSYMUTIL+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$DSYMUTIL\"; then\n  ac_cv_prog_DSYMUTIL=\"$DSYMUTIL\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_DSYMUTIL=\"${ac_tool_prefix}dsymutil\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nDSYMUTIL=$ac_cv_prog_DSYMUTIL\nif test -n \"$DSYMUTIL\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL\" >&5\n$as_echo \"$DSYMUTIL\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_DSYMUTIL\"; then\n  ac_ct_DSYMUTIL=$DSYMUTIL\n  # Extract the first word of \"dsymutil\", so it can be a program name with args.\nset dummy dsymutil; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_DSYMUTIL\"; then\n  ac_cv_prog_ac_ct_DSYMUTIL=\"$ac_ct_DSYMUTIL\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_DSYMUTIL=\"dsymutil\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL\nif test -n \"$ac_ct_DSYMUTIL\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL\" >&5\n$as_echo \"$ac_ct_DSYMUTIL\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_DSYMUTIL\" = x; then\n    DSYMUTIL=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    DSYMUTIL=$ac_ct_DSYMUTIL\n  fi\nelse\n  DSYMUTIL=\"$ac_cv_prog_DSYMUTIL\"\nfi\n\n    if test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}nmedit\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}nmedit; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_NMEDIT+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$NMEDIT\"; then\n  ac_cv_prog_NMEDIT=\"$NMEDIT\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_NMEDIT=\"${ac_tool_prefix}nmedit\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nNMEDIT=$ac_cv_prog_NMEDIT\nif test -n \"$NMEDIT\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $NMEDIT\" >&5\n$as_echo \"$NMEDIT\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_NMEDIT\"; then\n  ac_ct_NMEDIT=$NMEDIT\n  # Extract the first word of \"nmedit\", so it can be a program name with args.\nset dummy nmedit; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_NMEDIT\"; then\n  ac_cv_prog_ac_ct_NMEDIT=\"$ac_ct_NMEDIT\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_NMEDIT=\"nmedit\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT\nif test -n \"$ac_ct_NMEDIT\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT\" >&5\n$as_echo \"$ac_ct_NMEDIT\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_NMEDIT\" = x; then\n    NMEDIT=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    NMEDIT=$ac_ct_NMEDIT\n  fi\nelse\n  NMEDIT=\"$ac_cv_prog_NMEDIT\"\nfi\n\n    if test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}lipo\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}lipo; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_LIPO+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$LIPO\"; then\n  ac_cv_prog_LIPO=\"$LIPO\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_LIPO=\"${ac_tool_prefix}lipo\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nLIPO=$ac_cv_prog_LIPO\nif test -n \"$LIPO\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $LIPO\" >&5\n$as_echo \"$LIPO\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_LIPO\"; then\n  ac_ct_LIPO=$LIPO\n  # Extract the first word of \"lipo\", so it can be a program name with args.\nset dummy lipo; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_LIPO+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_LIPO\"; then\n  ac_cv_prog_ac_ct_LIPO=\"$ac_ct_LIPO\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_LIPO=\"lipo\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO\nif test -n \"$ac_ct_LIPO\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO\" >&5\n$as_echo \"$ac_ct_LIPO\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_LIPO\" = x; then\n    LIPO=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    LIPO=$ac_ct_LIPO\n  fi\nelse\n  LIPO=\"$ac_cv_prog_LIPO\"\nfi\n\n    if test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}otool\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}otool; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_OTOOL+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$OTOOL\"; then\n  ac_cv_prog_OTOOL=\"$OTOOL\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_OTOOL=\"${ac_tool_prefix}otool\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nOTOOL=$ac_cv_prog_OTOOL\nif test -n \"$OTOOL\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $OTOOL\" >&5\n$as_echo \"$OTOOL\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_OTOOL\"; then\n  ac_ct_OTOOL=$OTOOL\n  # Extract the first word of \"otool\", so it can be a program name with args.\nset dummy otool; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_OTOOL+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_OTOOL\"; then\n  ac_cv_prog_ac_ct_OTOOL=\"$ac_ct_OTOOL\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_OTOOL=\"otool\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL\nif test -n \"$ac_ct_OTOOL\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL\" >&5\n$as_echo \"$ac_ct_OTOOL\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_OTOOL\" = x; then\n    OTOOL=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    OTOOL=$ac_ct_OTOOL\n  fi\nelse\n  OTOOL=\"$ac_cv_prog_OTOOL\"\nfi\n\n    if test -n \"$ac_tool_prefix\"; then\n  # Extract the first word of \"${ac_tool_prefix}otool64\", so it can be a program name with args.\nset dummy ${ac_tool_prefix}otool64; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_OTOOL64+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$OTOOL64\"; then\n  ac_cv_prog_OTOOL64=\"$OTOOL64\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_OTOOL64=\"${ac_tool_prefix}otool64\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nOTOOL64=$ac_cv_prog_OTOOL64\nif test -n \"$OTOOL64\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $OTOOL64\" >&5\n$as_echo \"$OTOOL64\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\nif test -z \"$ac_cv_prog_OTOOL64\"; then\n  ac_ct_OTOOL64=$OTOOL64\n  # Extract the first word of \"otool64\", so it can be a program name with args.\nset dummy otool64; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_OTOOL64\"; then\n  ac_cv_prog_ac_ct_OTOOL64=\"$ac_ct_OTOOL64\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_OTOOL64=\"otool64\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64\nif test -n \"$ac_ct_OTOOL64\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64\" >&5\n$as_echo \"$ac_ct_OTOOL64\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n  if test \"x$ac_ct_OTOOL64\" = x; then\n    OTOOL64=\":\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    OTOOL64=$ac_ct_OTOOL64\n  fi\nelse\n  OTOOL64=\"$ac_cv_prog_OTOOL64\"\nfi\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\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag\" >&5\n$as_echo_n \"checking for -single_module linker flag... \" >&6; }\nif ${lt_cv_apple_cc_single_mod+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_apple_cc_single_mod=no\n      if test -z \"$LT_MULTI_MODULE\"; then\n\t# By default we will add the -single_module flag. You can override\n\t# by either setting the environment variable LT_MULTI_MODULE\n\t# non-empty at configure time, or by adding -multi_module to the\n\t# link flags.\n\trm -rf libconftest.dylib*\n\techo \"int foo(void){return 1;}\" > conftest.c\n\techo \"$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \\\n-dynamiclib -Wl,-single_module conftest.c\" >&5\n\t$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \\\n\t  -dynamiclib -Wl,-single_module conftest.c 2>conftest.err\n        _lt_result=$?\n\t# If there is a non-empty error log, and \"single_module\"\n\t# appears in it, assume the flag caused a linker warning\n        if test -s conftest.err && $GREP single_module conftest.err; then\n\t  cat conftest.err >&5\n\t# Otherwise, if the output was created with a 0 exit code from\n\t# the compiler, it worked.\n\telif test -f libconftest.dylib && test 0 = \"$_lt_result\"; then\n\t  lt_cv_apple_cc_single_mod=yes\n\telse\n\t  cat conftest.err >&5\n\tfi\n\trm -rf libconftest.dylib*\n\trm -f conftest.*\n      fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod\" >&5\n$as_echo \"$lt_cv_apple_cc_single_mod\" >&6; }\n\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag\" >&5\n$as_echo_n \"checking for -exported_symbols_list linker flag... \" >&6; }\nif ${lt_cv_ld_exported_symbols_list+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_ld_exported_symbols_list=no\n      save_LDFLAGS=$LDFLAGS\n      echo \"_main\" > conftest.sym\n      LDFLAGS=\"$LDFLAGS -Wl,-exported_symbols_list,conftest.sym\"\n      cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  lt_cv_ld_exported_symbols_list=yes\nelse\n  lt_cv_ld_exported_symbols_list=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\n\tLDFLAGS=$save_LDFLAGS\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list\" >&5\n$as_echo \"$lt_cv_ld_exported_symbols_list\" >&6; }\n\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag\" >&5\n$as_echo_n \"checking for -force_load linker flag... \" >&6; }\nif ${lt_cv_ld_force_load+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_ld_force_load=no\n      cat > conftest.c << _LT_EOF\nint forced_loaded() { return 2;}\n_LT_EOF\n      echo \"$LTCC $LTCFLAGS -c -o conftest.o conftest.c\" >&5\n      $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5\n      echo \"$AR cru libconftest.a conftest.o\" >&5\n      $AR cru libconftest.a conftest.o 2>&5\n      echo \"$RANLIB libconftest.a\" >&5\n      $RANLIB libconftest.a 2>&5\n      cat > conftest.c << _LT_EOF\nint main() { return 0;}\n_LT_EOF\n      echo \"$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a\" >&5\n      $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err\n      _lt_result=$?\n      if test -s conftest.err && $GREP force_load conftest.err; then\n\tcat conftest.err >&5\n      elif test -f conftest && test 0 = \"$_lt_result\" && $GREP forced_load conftest >/dev/null 2>&1; then\n\tlt_cv_ld_force_load=yes\n      else\n\tcat conftest.err >&5\n      fi\n        rm -f conftest.err libconftest.a conftest conftest.c\n        rm -rf conftest.dSYM\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load\" >&5\n$as_echo \"$lt_cv_ld_force_load\" >&6; }\n    case $host_os in\n    rhapsody* | darwin1.[012])\n      _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;\n    darwin1.*)\n      _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;\n    darwin*) # darwin 5.x on\n      # if running on 10.5 or later, the deployment target defaults\n      # to the OS version, if on x86, and 10.4, the deployment\n      # target defaults to 10.4. Don't you love it?\n      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in\n\t10.0,*86*-darwin8*|10.0,*-darwin[91]*)\n\t  _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;\n\t10.[012][,.]*)\n\t  _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;\n\t10.*)\n\t  _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;\n      esac\n    ;;\n  esac\n    if test yes = \"$lt_cv_apple_cc_single_mod\"; then\n      _lt_dar_single_mod='$single_module'\n    fi\n    if test yes = \"$lt_cv_ld_exported_symbols_list\"; then\n      _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'\n    else\n      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'\n    fi\n    if test : != \"$DSYMUTIL\" && test no = \"$lt_cv_ld_force_load\"; then\n      _lt_dsymutil='~$DSYMUTIL $lib || :'\n    else\n      _lt_dsymutil=\n    fi\n    ;;\n  esac\n\n# func_munge_path_list VARIABLE PATH\n# -----------------------------------\n# VARIABLE is name of variable containing _space_ separated list of\n# directories to be munged by the contents of PATH, which is string\n# having a format:\n# \"DIR[:DIR]:\"\n#       string \"DIR[ DIR]\" will be prepended to VARIABLE\n# \":DIR[:DIR]\"\n#       string \"DIR[ DIR]\" will be appended to VARIABLE\n# \"DIRP[:DIRP]::[DIRA:]DIRA\"\n#       string \"DIRP[ DIRP]\" will be prepended to VARIABLE and string\n#       \"DIRA[ DIRA]\" will be appended to VARIABLE\n# \"DIR[:DIR]\"\n#       VARIABLE will be replaced by \"DIR[ DIR]\"\nfunc_munge_path_list ()\n{\n    case x$2 in\n    x)\n        ;;\n    *:)\n        eval $1=\\\"`$ECHO $2 | $SED 's/:/ /g'` \\$$1\\\"\n        ;;\n    x:*)\n        eval $1=\\\"\\$$1 `$ECHO $2 | $SED 's/:/ /g'`\\\"\n        ;;\n    *::*)\n        eval $1=\\\"\\$$1\\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\\\"\n        eval $1=\\\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\\ \\$$1\\\"\n        ;;\n    *)\n        eval $1=\\\"`$ECHO $2 | $SED 's/:/ /g'`\\\"\n        ;;\n    esac\n}\n\nfor ac_header in dlfcn.h\ndo :\n  ac_fn_c_check_header_compile \"$LINENO\" \"dlfcn.h\" \"ac_cv_header_dlfcn_h\" \"$ac_includes_default\n\"\nif test \"x$ac_cv_header_dlfcn_h\" = xyes; then :\n  cat >>confdefs.h <<_ACEOF\n#define HAVE_DLFCN_H 1\n_ACEOF\n\nfi\n\ndone\n\n\n\n\nfunc_stripname_cnf ()\n{\n  case $2 in\n  .*) func_stripname_result=`$ECHO \"$3\" | $SED \"s%^$1%%; s%\\\\\\\\$2\\$%%\"`;;\n  *)  func_stripname_result=`$ECHO \"$3\" | $SED \"s%^$1%%; s%$2\\$%%\"`;;\n  esac\n} # func_stripname_cnf\n\n\n\n\n\n# Set options\n\n\n\n        enable_dlopen=no\n\n\n  enable_win32_dll=no\n\n\n            # Check whether --enable-shared was given.\nif test \"${enable_shared+set}\" = set; then :\n  enableval=$enable_shared; p=${PACKAGE-default}\n    case $enableval in\n    yes) enable_shared=yes ;;\n    no) enable_shared=no ;;\n    *)\n      enable_shared=no\n      # Look at the argument we got.  We use all the common list separators.\n      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,\n      for pkg in $enableval; do\n\tIFS=$lt_save_ifs\n\tif test \"X$pkg\" = \"X$p\"; then\n\t  enable_shared=yes\n\tfi\n      done\n      IFS=$lt_save_ifs\n      ;;\n    esac\nelse\n  enable_shared=yes\nfi\n\n\n\n\n\n\n\n\n\n  # Check whether --enable-static was given.\nif test \"${enable_static+set}\" = set; then :\n  enableval=$enable_static; p=${PACKAGE-default}\n    case $enableval in\n    yes) enable_static=yes ;;\n    no) enable_static=no ;;\n    *)\n     enable_static=no\n      # Look at the argument we got.  We use all the common list separators.\n      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,\n      for pkg in $enableval; do\n\tIFS=$lt_save_ifs\n\tif test \"X$pkg\" = \"X$p\"; then\n\t  enable_static=yes\n\tfi\n      done\n      IFS=$lt_save_ifs\n      ;;\n    esac\nelse\n  enable_static=yes\nfi\n\n\n\n\n\n\n\n\n\n\n# Check whether --with-pic was given.\nif test \"${with_pic+set}\" = set; then :\n  withval=$with_pic; lt_p=${PACKAGE-default}\n    case $withval in\n    yes|no) pic_mode=$withval ;;\n    *)\n      pic_mode=default\n      # Look at the argument we got.  We use all the common list separators.\n      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,\n      for lt_pkg in $withval; do\n\tIFS=$lt_save_ifs\n\tif test \"X$lt_pkg\" = \"X$lt_p\"; then\n\t  pic_mode=yes\n\tfi\n      done\n      IFS=$lt_save_ifs\n      ;;\n    esac\nelse\n  pic_mode=default\nfi\n\n\n\n\n\n\n\n\n  # Check whether --enable-fast-install was given.\nif test \"${enable_fast_install+set}\" = set; then :\n  enableval=$enable_fast_install; p=${PACKAGE-default}\n    case $enableval in\n    yes) enable_fast_install=yes ;;\n    no) enable_fast_install=no ;;\n    *)\n      enable_fast_install=no\n      # Look at the argument we got.  We use all the common list separators.\n      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,\n      for pkg in $enableval; do\n\tIFS=$lt_save_ifs\n\tif test \"X$pkg\" = \"X$p\"; then\n\t  enable_fast_install=yes\n\tfi\n      done\n      IFS=$lt_save_ifs\n      ;;\n    esac\nelse\n  enable_fast_install=yes\nfi\n\n\n\n\n\n\n\n\n  shared_archive_member_spec=\ncase $host,$enable_shared in\npower*-*-aix[5-9]*,yes)\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide\" >&5\n$as_echo_n \"checking which variant of shared library versioning to provide... \" >&6; }\n\n# Check whether --with-aix-soname was given.\nif test \"${with_aix_soname+set}\" = set; then :\n  withval=$with_aix_soname; case $withval in\n    aix|svr4|both)\n      ;;\n    *)\n      as_fn_error $? \"Unknown argument to --with-aix-soname\" \"$LINENO\" 5\n      ;;\n    esac\n    lt_cv_with_aix_soname=$with_aix_soname\nelse\n  if ${lt_cv_with_aix_soname+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_with_aix_soname=aix\nfi\n\n    with_aix_soname=$lt_cv_with_aix_soname\nfi\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $with_aix_soname\" >&5\n$as_echo \"$with_aix_soname\" >&6; }\n  if test aix != \"$with_aix_soname\"; then\n    # For the AIX way of multilib, we name the shared archive member\n    # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',\n    # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.\n    # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,\n    # the AIX toolchain works better with OBJECT_MODE set (default 32).\n    if test 64 = \"${OBJECT_MODE-32}\"; then\n      shared_archive_member_spec=shr_64\n    else\n      shared_archive_member_spec=shr\n    fi\n  fi\n  ;;\n*)\n  with_aix_soname=aix\n  ;;\nesac\n\n\n\n\n\n\n\n\n\n\n# This can be used to rebuild libtool when needed\nLIBTOOL_DEPS=$ltmain\n\n# Always use our own libtool.\nLIBTOOL='$(SHELL) $(top_builddir)/libtool'\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\n\n\n\ntest -z \"$LN_S\" && LN_S=\"ln -s\"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nif test -n \"${ZSH_VERSION+set}\"; then\n   setopt NO_GLOB_SUBST\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for objdir\" >&5\n$as_echo_n \"checking for objdir... \" >&6; }\nif ${lt_cv_objdir+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  rm -f .libs 2>/dev/null\nmkdir .libs 2>/dev/null\nif test -d .libs; then\n  lt_cv_objdir=.libs\nelse\n  # MS-DOS does not allow filenames that begin with a dot.\n  lt_cv_objdir=_libs\nfi\nrmdir .libs 2>/dev/null\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir\" >&5\n$as_echo \"$lt_cv_objdir\" >&6; }\nobjdir=$lt_cv_objdir\n\n\n\n\n\ncat >>confdefs.h <<_ACEOF\n#define LT_OBJDIR \"$lt_cv_objdir/\"\n_ACEOF\n\n\n\n\ncase $host_os in\naix3*)\n  # AIX sometimes has problems with the GCC collect2 program.  For some\n  # reason, if we set the COLLECT_NAMES environment variable, the problems\n  # vanish in a puff of smoke.\n  if test set != \"${COLLECT_NAMES+set}\"; then\n    COLLECT_NAMES=\n    export COLLECT_NAMES\n  fi\n  ;;\nesac\n\n# Global variables:\nofile=libtool\ncan_build_shared=yes\n\n# All known linkers require a '.a' archive for static linking (except MSVC,\n# which needs '.lib').\nlibext=a\n\nwith_gnu_ld=$lt_cv_prog_gnu_ld\n\nold_CC=$CC\nold_CFLAGS=$CFLAGS\n\n# Set sane defaults for various variables\ntest -z \"$CC\" && CC=cc\ntest -z \"$LTCC\" && LTCC=$CC\ntest -z \"$LTCFLAGS\" && LTCFLAGS=$CFLAGS\ntest -z \"$LD\" && LD=ld\ntest -z \"$ac_objext\" && ac_objext=o\n\nfunc_cc_basename $compiler\ncc_basename=$func_cc_basename_result\n\n\n# Only perform the check for file, if the check method requires it\ntest -z \"$MAGIC_CMD\" && MAGIC_CMD=file\ncase $deplibs_check_method in\nfile_magic*)\n  if test \"$file_magic_cmd\" = '$MAGIC_CMD'; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file\" >&5\n$as_echo_n \"checking for ${ac_tool_prefix}file... \" >&6; }\nif ${lt_cv_path_MAGIC_CMD+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $MAGIC_CMD in\n[\\\\/*] |  ?:[\\\\/]*)\n  lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.\n  ;;\n*)\n  lt_save_MAGIC_CMD=$MAGIC_CMD\n  lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR\n  ac_dummy=\"/usr/bin$PATH_SEPARATOR$PATH\"\n  for ac_dir in $ac_dummy; do\n    IFS=$lt_save_ifs\n    test -z \"$ac_dir\" && ac_dir=.\n    if test -f \"$ac_dir/${ac_tool_prefix}file\"; then\n      lt_cv_path_MAGIC_CMD=$ac_dir/\"${ac_tool_prefix}file\"\n      if test -n \"$file_magic_test_file\"; then\n\tcase $deplibs_check_method in\n\t\"file_magic \"*)\n\t  file_magic_regex=`expr \"$deplibs_check_method\" : \"file_magic \\(.*\\)\"`\n\t  MAGIC_CMD=$lt_cv_path_MAGIC_CMD\n\t  if eval $file_magic_cmd \\$file_magic_test_file 2> /dev/null |\n\t    $EGREP \"$file_magic_regex\" > /dev/null; then\n\t    :\n\t  else\n\t    cat <<_LT_EOF 1>&2\n\n*** Warning: the command libtool uses to detect shared libraries,\n*** $file_magic_cmd, produces output that libtool cannot recognize.\n*** The result is that libtool may fail to recognize shared libraries\n*** as such.  This will affect the creation of libtool libraries that\n*** depend on shared libraries, but programs linked with such libtool\n*** libraries will work regardless of this problem.  Nevertheless, you\n*** may want to report the problem to your system manager and/or to\n*** bug-libtool@gnu.org\n\n_LT_EOF\n\t  fi ;;\n\tesac\n      fi\n      break\n    fi\n  done\n  IFS=$lt_save_ifs\n  MAGIC_CMD=$lt_save_MAGIC_CMD\n  ;;\nesac\nfi\n\nMAGIC_CMD=$lt_cv_path_MAGIC_CMD\nif test -n \"$MAGIC_CMD\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD\" >&5\n$as_echo \"$MAGIC_CMD\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n\n\n\nif test -z \"$lt_cv_path_MAGIC_CMD\"; then\n  if test -n \"$ac_tool_prefix\"; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for file\" >&5\n$as_echo_n \"checking for file... \" >&6; }\nif ${lt_cv_path_MAGIC_CMD+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $MAGIC_CMD in\n[\\\\/*] |  ?:[\\\\/]*)\n  lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.\n  ;;\n*)\n  lt_save_MAGIC_CMD=$MAGIC_CMD\n  lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR\n  ac_dummy=\"/usr/bin$PATH_SEPARATOR$PATH\"\n  for ac_dir in $ac_dummy; do\n    IFS=$lt_save_ifs\n    test -z \"$ac_dir\" && ac_dir=.\n    if test -f \"$ac_dir/file\"; then\n      lt_cv_path_MAGIC_CMD=$ac_dir/\"file\"\n      if test -n \"$file_magic_test_file\"; then\n\tcase $deplibs_check_method in\n\t\"file_magic \"*)\n\t  file_magic_regex=`expr \"$deplibs_check_method\" : \"file_magic \\(.*\\)\"`\n\t  MAGIC_CMD=$lt_cv_path_MAGIC_CMD\n\t  if eval $file_magic_cmd \\$file_magic_test_file 2> /dev/null |\n\t    $EGREP \"$file_magic_regex\" > /dev/null; then\n\t    :\n\t  else\n\t    cat <<_LT_EOF 1>&2\n\n*** Warning: the command libtool uses to detect shared libraries,\n*** $file_magic_cmd, produces output that libtool cannot recognize.\n*** The result is that libtool may fail to recognize shared libraries\n*** as such.  This will affect the creation of libtool libraries that\n*** depend on shared libraries, but programs linked with such libtool\n*** libraries will work regardless of this problem.  Nevertheless, you\n*** may want to report the problem to your system manager and/or to\n*** bug-libtool@gnu.org\n\n_LT_EOF\n\t  fi ;;\n\tesac\n      fi\n      break\n    fi\n  done\n  IFS=$lt_save_ifs\n  MAGIC_CMD=$lt_save_MAGIC_CMD\n  ;;\nesac\nfi\n\nMAGIC_CMD=$lt_cv_path_MAGIC_CMD\nif test -n \"$MAGIC_CMD\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD\" >&5\n$as_echo \"$MAGIC_CMD\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  else\n    MAGIC_CMD=:\n  fi\nfi\n\n  fi\n  ;;\nesac\n\n# Use C for the default configuration in the libtool script\n\nlt_save_CC=$CC\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n\n# Source file extension for C test sources.\nac_ext=c\n\n# Object file extension for compiled C test sources.\nobjext=o\nobjext=$objext\n\n# Code to be used in simple compile tests\nlt_simple_compile_test_code=\"int some_variable = 0;\"\n\n# Code to be used in simple link tests\nlt_simple_link_test_code='int main(){return(0);}'\n\n\n\n\n\n\n\n# If no C compiler was specified, use CC.\nLTCC=${LTCC-\"$CC\"}\n\n# If no C compiler flags were specified, use CFLAGS.\nLTCFLAGS=${LTCFLAGS-\"$CFLAGS\"}\n\n# Allow CC to be a program name with arguments.\ncompiler=$CC\n\n# Save the default compiler, since it gets overwritten when the other\n# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.\ncompiler_DEFAULT=$CC\n\n# save warnings/boilerplate of simple test code\nac_outfile=conftest.$ac_objext\necho \"$lt_simple_compile_test_code\" >conftest.$ac_ext\neval \"$ac_compile\" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err\n_lt_compiler_boilerplate=`cat conftest.err`\n$RM conftest*\n\nac_outfile=conftest.$ac_objext\necho \"$lt_simple_link_test_code\" >conftest.$ac_ext\neval \"$ac_link\" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err\n_lt_linker_boilerplate=`cat conftest.err`\n$RM -r conftest*\n\n\nif test -n \"$compiler\"; then\n\nlt_prog_compiler_no_builtin_flag=\n\nif test yes = \"$GCC\"; then\n  case $cc_basename in\n  nvcc*)\n    lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;\n  *)\n    lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;;\n  esac\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions\" >&5\n$as_echo_n \"checking if $compiler supports -fno-rtti -fno-exceptions... \" >&6; }\nif ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_rtti_exceptions=no\n   ac_outfile=conftest.$ac_objext\n   echo \"$lt_simple_compile_test_code\" > conftest.$ac_ext\n   lt_compiler_flag=\"-fno-rtti -fno-exceptions\"  ## exclude from sc_useless_quotes_in_assignment\n   # Insert the option either (1) after the last *FLAGS variable, or\n   # (2) before a word containing \"conftest.\", or (3) at the end.\n   # Note that $ac_compile itself does not contain backslashes and begins\n   # with a dollar sign (not a hyphen), so the echo should work correctly.\n   # The option is referenced via a variable to avoid confusing sed.\n   lt_compile=`echo \"$ac_compile\" | $SED \\\n   -e 's:.*FLAGS}\\{0,1\\} :&$lt_compiler_flag :; t' \\\n   -e 's: [^ ]*conftest\\.: $lt_compiler_flag&:; t' \\\n   -e 's:$: $lt_compiler_flag:'`\n   (eval echo \"\\\"\\$as_me:$LINENO: $lt_compile\\\"\" >&5)\n   (eval \"$lt_compile\" 2>conftest.err)\n   ac_status=$?\n   cat conftest.err >&5\n   echo \"$as_me:$LINENO: \\$? = $ac_status\" >&5\n   if (exit $ac_status) && test -s \"$ac_outfile\"; then\n     # The compiler can only warn and ignore the option if not recognized\n     # So say no if there are warnings other than the usual output.\n     $ECHO \"$_lt_compiler_boilerplate\" | $SED '/^$/d' >conftest.exp\n     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2\n     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then\n       lt_cv_prog_compiler_rtti_exceptions=yes\n     fi\n   fi\n   $RM conftest*\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions\" >&5\n$as_echo \"$lt_cv_prog_compiler_rtti_exceptions\" >&6; }\n\nif test yes = \"$lt_cv_prog_compiler_rtti_exceptions\"; then\n    lt_prog_compiler_no_builtin_flag=\"$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions\"\nelse\n    :\nfi\n\nfi\n\n\n\n\n\n\n  lt_prog_compiler_wl=\nlt_prog_compiler_pic=\nlt_prog_compiler_static=\n\n\n  if test yes = \"$GCC\"; then\n    lt_prog_compiler_wl='-Wl,'\n    lt_prog_compiler_static='-static'\n\n    case $host_os in\n      aix*)\n      # All AIX code is PIC.\n      if test ia64 = \"$host_cpu\"; then\n\t# AIX 5 now supports IA64 processor\n\tlt_prog_compiler_static='-Bstatic'\n      fi\n      lt_prog_compiler_pic='-fPIC'\n      ;;\n\n    amigaos*)\n      case $host_cpu in\n      powerpc)\n            # see comment about AmigaOS4 .so support\n            lt_prog_compiler_pic='-fPIC'\n        ;;\n      m68k)\n            # FIXME: we need at least 68020 code to build shared libraries, but\n            # adding the '-m68020' flag to GCC prevents building anything better,\n            # like '-m68040'.\n            lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'\n        ;;\n      esac\n      ;;\n\n    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)\n      # PIC is the default for these OSes.\n      ;;\n\n    mingw* | cygwin* | pw32* | os2* | cegcc*)\n      # This hack is so that the source file can tell whether it is being\n      # built for inclusion in a dll (and should export symbols for example).\n      # Although the cygwin gcc ignores -fPIC, still need this for old-style\n      # (--disable-auto-import) libraries\n      lt_prog_compiler_pic='-DDLL_EXPORT'\n      case $host_os in\n      os2*)\n\tlt_prog_compiler_static='$wl-static'\n\t;;\n      esac\n      ;;\n\n    darwin* | rhapsody*)\n      # PIC is the default on this platform\n      # Common symbols not allowed in MH_DYLIB files\n      lt_prog_compiler_pic='-fno-common'\n      ;;\n\n    haiku*)\n      # PIC is the default for Haiku.\n      # The \"-static\" flag exists, but is broken.\n      lt_prog_compiler_static=\n      ;;\n\n    hpux*)\n      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit\n      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag\n      # sets the default TLS model and affects inlining.\n      case $host_cpu in\n      hppa*64*)\n\t# +Z the default\n\t;;\n      *)\n\tlt_prog_compiler_pic='-fPIC'\n\t;;\n      esac\n      ;;\n\n    interix[3-9]*)\n      # Interix 3.x gcc -fpic/-fPIC options generate broken code.\n      # Instead, we relocate shared libraries at runtime.\n      ;;\n\n    msdosdjgpp*)\n      # Just because we use GCC doesn't mean we suddenly get shared libraries\n      # on systems that don't support them.\n      lt_prog_compiler_can_build_shared=no\n      enable_shared=no\n      ;;\n\n    *nto* | *qnx*)\n      # QNX uses GNU C++, but need to define -shared option too, otherwise\n      # it will coredump.\n      lt_prog_compiler_pic='-fPIC -shared'\n      ;;\n\n    sysv4*MP*)\n      if test -d /usr/nec; then\n\tlt_prog_compiler_pic=-Kconform_pic\n      fi\n      ;;\n\n    *)\n      lt_prog_compiler_pic='-fPIC'\n      ;;\n    esac\n\n    case $cc_basename in\n    nvcc*) # Cuda Compiler Driver 2.2\n      lt_prog_compiler_wl='-Xlinker '\n      if test -n \"$lt_prog_compiler_pic\"; then\n        lt_prog_compiler_pic=\"-Xcompiler $lt_prog_compiler_pic\"\n      fi\n      ;;\n    esac\n  else\n    # PORTME Check for flag to pass linker flags through the system compiler.\n    case $host_os in\n    aix*)\n      lt_prog_compiler_wl='-Wl,'\n      if test ia64 = \"$host_cpu\"; then\n\t# AIX 5 now supports IA64 processor\n\tlt_prog_compiler_static='-Bstatic'\n      else\n\tlt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'\n      fi\n      ;;\n\n    darwin* | rhapsody*)\n      # PIC is the default on this platform\n      # Common symbols not allowed in MH_DYLIB files\n      lt_prog_compiler_pic='-fno-common'\n      case $cc_basename in\n      nagfor*)\n        # NAG Fortran compiler\n        lt_prog_compiler_wl='-Wl,-Wl,,'\n        lt_prog_compiler_pic='-PIC'\n        lt_prog_compiler_static='-Bstatic'\n        ;;\n      esac\n      ;;\n\n    mingw* | cygwin* | pw32* | os2* | cegcc*)\n      # This hack is so that the source file can tell whether it is being\n      # built for inclusion in a dll (and should export symbols for example).\n      lt_prog_compiler_pic='-DDLL_EXPORT'\n      case $host_os in\n      os2*)\n\tlt_prog_compiler_static='$wl-static'\n\t;;\n      esac\n      ;;\n\n    hpux9* | hpux10* | hpux11*)\n      lt_prog_compiler_wl='-Wl,'\n      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but\n      # not for PA HP-UX.\n      case $host_cpu in\n      hppa*64*|ia64*)\n\t# +Z the default\n\t;;\n      *)\n\tlt_prog_compiler_pic='+Z'\n\t;;\n      esac\n      # Is there a better lt_prog_compiler_static that works with the bundled CC?\n      lt_prog_compiler_static='$wl-a ${wl}archive'\n      ;;\n\n    irix5* | irix6* | nonstopux*)\n      lt_prog_compiler_wl='-Wl,'\n      # PIC (with -KPIC) is the default.\n      lt_prog_compiler_static='-non_shared'\n      ;;\n\n    linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)\n      case $cc_basename in\n      # old Intel for x86_64, which still supported -KPIC.\n      ecc*)\n\tlt_prog_compiler_wl='-Wl,'\n\tlt_prog_compiler_pic='-KPIC'\n\tlt_prog_compiler_static='-static'\n        ;;\n      # icc used to be incompatible with GCC.\n      # ICC 10 doesn't accept -KPIC any more.\n      icc* | ifort*)\n\tlt_prog_compiler_wl='-Wl,'\n\tlt_prog_compiler_pic='-fPIC'\n\tlt_prog_compiler_static='-static'\n        ;;\n      # Lahey Fortran 8.1.\n      lf95*)\n\tlt_prog_compiler_wl='-Wl,'\n\tlt_prog_compiler_pic='--shared'\n\tlt_prog_compiler_static='--static'\n\t;;\n      nagfor*)\n\t# NAG Fortran compiler\n\tlt_prog_compiler_wl='-Wl,-Wl,,'\n\tlt_prog_compiler_pic='-PIC'\n\tlt_prog_compiler_static='-Bstatic'\n\t;;\n      tcc*)\n\t# Fabrice Bellard et al's Tiny C Compiler\n\tlt_prog_compiler_wl='-Wl,'\n\tlt_prog_compiler_pic='-fPIC'\n\tlt_prog_compiler_static='-static'\n\t;;\n      pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)\n        # Portland Group compilers (*not* the Pentium gcc compiler,\n\t# which looks to be a dead project)\n\tlt_prog_compiler_wl='-Wl,'\n\tlt_prog_compiler_pic='-fpic'\n\tlt_prog_compiler_static='-Bstatic'\n        ;;\n      ccc*)\n        lt_prog_compiler_wl='-Wl,'\n        # All Alpha code is PIC.\n        lt_prog_compiler_static='-non_shared'\n        ;;\n      xl* | bgxl* | bgf* | mpixl*)\n\t# IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene\n\tlt_prog_compiler_wl='-Wl,'\n\tlt_prog_compiler_pic='-qpic'\n\tlt_prog_compiler_static='-qstaticlink'\n\t;;\n      *)\n\tcase `$CC -V 2>&1 | sed 5q` in\n\t*Sun\\ Ceres\\ Fortran* | *Sun*Fortran*\\ [1-7].* | *Sun*Fortran*\\ 8.[0-3]*)\n\t  # Sun Fortran 8.3 passes all unrecognized flags to the linker\n\t  lt_prog_compiler_pic='-KPIC'\n\t  lt_prog_compiler_static='-Bstatic'\n\t  lt_prog_compiler_wl=''\n\t  ;;\n\t*Sun\\ F* | *Sun*Fortran*)\n\t  lt_prog_compiler_pic='-KPIC'\n\t  lt_prog_compiler_static='-Bstatic'\n\t  lt_prog_compiler_wl='-Qoption ld '\n\t  ;;\n\t*Sun\\ C*)\n\t  # Sun C 5.9\n\t  lt_prog_compiler_pic='-KPIC'\n\t  lt_prog_compiler_static='-Bstatic'\n\t  lt_prog_compiler_wl='-Wl,'\n\t  ;;\n        *Intel*\\ [CF]*Compiler*)\n\t  lt_prog_compiler_wl='-Wl,'\n\t  lt_prog_compiler_pic='-fPIC'\n\t  lt_prog_compiler_static='-static'\n\t  ;;\n\t*Portland\\ Group*)\n\t  lt_prog_compiler_wl='-Wl,'\n\t  lt_prog_compiler_pic='-fpic'\n\t  lt_prog_compiler_static='-Bstatic'\n\t  ;;\n\tesac\n\t;;\n      esac\n      ;;\n\n    newsos6)\n      lt_prog_compiler_pic='-KPIC'\n      lt_prog_compiler_static='-Bstatic'\n      ;;\n\n    *nto* | *qnx*)\n      # QNX uses GNU C++, but need to define -shared option too, otherwise\n      # it will coredump.\n      lt_prog_compiler_pic='-fPIC -shared'\n      ;;\n\n    osf3* | osf4* | osf5*)\n      lt_prog_compiler_wl='-Wl,'\n      # All OSF/1 code is PIC.\n      lt_prog_compiler_static='-non_shared'\n      ;;\n\n    rdos*)\n      lt_prog_compiler_static='-non_shared'\n      ;;\n\n    solaris*)\n      lt_prog_compiler_pic='-KPIC'\n      lt_prog_compiler_static='-Bstatic'\n      case $cc_basename in\n      f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)\n\tlt_prog_compiler_wl='-Qoption ld ';;\n      *)\n\tlt_prog_compiler_wl='-Wl,';;\n      esac\n      ;;\n\n    sunos4*)\n      lt_prog_compiler_wl='-Qoption ld '\n      lt_prog_compiler_pic='-PIC'\n      lt_prog_compiler_static='-Bstatic'\n      ;;\n\n    sysv4 | sysv4.2uw2* | sysv4.3*)\n      lt_prog_compiler_wl='-Wl,'\n      lt_prog_compiler_pic='-KPIC'\n      lt_prog_compiler_static='-Bstatic'\n      ;;\n\n    sysv4*MP*)\n      if test -d /usr/nec; then\n\tlt_prog_compiler_pic='-Kconform_pic'\n\tlt_prog_compiler_static='-Bstatic'\n      fi\n      ;;\n\n    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)\n      lt_prog_compiler_wl='-Wl,'\n      lt_prog_compiler_pic='-KPIC'\n      lt_prog_compiler_static='-Bstatic'\n      ;;\n\n    unicos*)\n      lt_prog_compiler_wl='-Wl,'\n      lt_prog_compiler_can_build_shared=no\n      ;;\n\n    uts4*)\n      lt_prog_compiler_pic='-pic'\n      lt_prog_compiler_static='-Bstatic'\n      ;;\n\n    *)\n      lt_prog_compiler_can_build_shared=no\n      ;;\n    esac\n  fi\n\ncase $host_os in\n  # For platforms that do not support PIC, -DPIC is meaningless:\n  *djgpp*)\n    lt_prog_compiler_pic=\n    ;;\n  *)\n    lt_prog_compiler_pic=\"$lt_prog_compiler_pic -DPIC\"\n    ;;\nesac\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC\" >&5\n$as_echo_n \"checking for $compiler option to produce PIC... \" >&6; }\nif ${lt_cv_prog_compiler_pic+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_pic=$lt_prog_compiler_pic\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic\" >&5\n$as_echo \"$lt_cv_prog_compiler_pic\" >&6; }\nlt_prog_compiler_pic=$lt_cv_prog_compiler_pic\n\n#\n# Check to make sure the PIC flag actually works.\n#\nif test -n \"$lt_prog_compiler_pic\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works\" >&5\n$as_echo_n \"checking if $compiler PIC flag $lt_prog_compiler_pic works... \" >&6; }\nif ${lt_cv_prog_compiler_pic_works+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_pic_works=no\n   ac_outfile=conftest.$ac_objext\n   echo \"$lt_simple_compile_test_code\" > conftest.$ac_ext\n   lt_compiler_flag=\"$lt_prog_compiler_pic -DPIC\"  ## exclude from sc_useless_quotes_in_assignment\n   # Insert the option either (1) after the last *FLAGS variable, or\n   # (2) before a word containing \"conftest.\", or (3) at the end.\n   # Note that $ac_compile itself does not contain backslashes and begins\n   # with a dollar sign (not a hyphen), so the echo should work correctly.\n   # The option is referenced via a variable to avoid confusing sed.\n   lt_compile=`echo \"$ac_compile\" | $SED \\\n   -e 's:.*FLAGS}\\{0,1\\} :&$lt_compiler_flag :; t' \\\n   -e 's: [^ ]*conftest\\.: $lt_compiler_flag&:; t' \\\n   -e 's:$: $lt_compiler_flag:'`\n   (eval echo \"\\\"\\$as_me:$LINENO: $lt_compile\\\"\" >&5)\n   (eval \"$lt_compile\" 2>conftest.err)\n   ac_status=$?\n   cat conftest.err >&5\n   echo \"$as_me:$LINENO: \\$? = $ac_status\" >&5\n   if (exit $ac_status) && test -s \"$ac_outfile\"; then\n     # The compiler can only warn and ignore the option if not recognized\n     # So say no if there are warnings other than the usual output.\n     $ECHO \"$_lt_compiler_boilerplate\" | $SED '/^$/d' >conftest.exp\n     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2\n     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then\n       lt_cv_prog_compiler_pic_works=yes\n     fi\n   fi\n   $RM conftest*\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works\" >&5\n$as_echo \"$lt_cv_prog_compiler_pic_works\" >&6; }\n\nif test yes = \"$lt_cv_prog_compiler_pic_works\"; then\n    case $lt_prog_compiler_pic in\n     \"\" | \" \"*) ;;\n     *) lt_prog_compiler_pic=\" $lt_prog_compiler_pic\" ;;\n     esac\nelse\n    lt_prog_compiler_pic=\n     lt_prog_compiler_can_build_shared=no\nfi\n\nfi\n\n\n\n\n\n\n\n\n\n\n\n#\n# Check to make sure the static flag actually works.\n#\nwl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\\\"$lt_prog_compiler_static\\\"\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works\" >&5\n$as_echo_n \"checking if $compiler static flag $lt_tmp_static_flag works... \" >&6; }\nif ${lt_cv_prog_compiler_static_works+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_static_works=no\n   save_LDFLAGS=$LDFLAGS\n   LDFLAGS=\"$LDFLAGS $lt_tmp_static_flag\"\n   echo \"$lt_simple_link_test_code\" > conftest.$ac_ext\n   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then\n     # The linker can only warn and ignore the option if not recognized\n     # So say no if there are warnings\n     if test -s conftest.err; then\n       # Append any errors to the config.log.\n       cat conftest.err 1>&5\n       $ECHO \"$_lt_linker_boilerplate\" | $SED '/^$/d' > conftest.exp\n       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2\n       if diff conftest.exp conftest.er2 >/dev/null; then\n         lt_cv_prog_compiler_static_works=yes\n       fi\n     else\n       lt_cv_prog_compiler_static_works=yes\n     fi\n   fi\n   $RM -r conftest*\n   LDFLAGS=$save_LDFLAGS\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works\" >&5\n$as_echo \"$lt_cv_prog_compiler_static_works\" >&6; }\n\nif test yes = \"$lt_cv_prog_compiler_static_works\"; then\n    :\nelse\n    lt_prog_compiler_static=\nfi\n\n\n\n\n\n\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext\" >&5\n$as_echo_n \"checking if $compiler supports -c -o file.$ac_objext... \" >&6; }\nif ${lt_cv_prog_compiler_c_o+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_c_o=no\n   $RM -r conftest 2>/dev/null\n   mkdir conftest\n   cd conftest\n   mkdir out\n   echo \"$lt_simple_compile_test_code\" > conftest.$ac_ext\n\n   lt_compiler_flag=\"-o out/conftest2.$ac_objext\"\n   # Insert the option either (1) after the last *FLAGS variable, or\n   # (2) before a word containing \"conftest.\", or (3) at the end.\n   # Note that $ac_compile itself does not contain backslashes and begins\n   # with a dollar sign (not a hyphen), so the echo should work correctly.\n   lt_compile=`echo \"$ac_compile\" | $SED \\\n   -e 's:.*FLAGS}\\{0,1\\} :&$lt_compiler_flag :; t' \\\n   -e 's: [^ ]*conftest\\.: $lt_compiler_flag&:; t' \\\n   -e 's:$: $lt_compiler_flag:'`\n   (eval echo \"\\\"\\$as_me:$LINENO: $lt_compile\\\"\" >&5)\n   (eval \"$lt_compile\" 2>out/conftest.err)\n   ac_status=$?\n   cat out/conftest.err >&5\n   echo \"$as_me:$LINENO: \\$? = $ac_status\" >&5\n   if (exit $ac_status) && test -s out/conftest2.$ac_objext\n   then\n     # The compiler can only warn and ignore the option if not recognized\n     # So say no if there are warnings\n     $ECHO \"$_lt_compiler_boilerplate\" | $SED '/^$/d' > out/conftest.exp\n     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2\n     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then\n       lt_cv_prog_compiler_c_o=yes\n     fi\n   fi\n   chmod u+w . 2>&5\n   $RM conftest*\n   # SGI C++ compiler will create directory out/ii_files/ for\n   # template instantiation\n   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files\n   $RM out/* && rmdir out\n   cd ..\n   $RM -r conftest\n   $RM conftest*\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o\" >&5\n$as_echo \"$lt_cv_prog_compiler_c_o\" >&6; }\n\n\n\n\n\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext\" >&5\n$as_echo_n \"checking if $compiler supports -c -o file.$ac_objext... \" >&6; }\nif ${lt_cv_prog_compiler_c_o+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_c_o=no\n   $RM -r conftest 2>/dev/null\n   mkdir conftest\n   cd conftest\n   mkdir out\n   echo \"$lt_simple_compile_test_code\" > conftest.$ac_ext\n\n   lt_compiler_flag=\"-o out/conftest2.$ac_objext\"\n   # Insert the option either (1) after the last *FLAGS variable, or\n   # (2) before a word containing \"conftest.\", or (3) at the end.\n   # Note that $ac_compile itself does not contain backslashes and begins\n   # with a dollar sign (not a hyphen), so the echo should work correctly.\n   lt_compile=`echo \"$ac_compile\" | $SED \\\n   -e 's:.*FLAGS}\\{0,1\\} :&$lt_compiler_flag :; t' \\\n   -e 's: [^ ]*conftest\\.: $lt_compiler_flag&:; t' \\\n   -e 's:$: $lt_compiler_flag:'`\n   (eval echo \"\\\"\\$as_me:$LINENO: $lt_compile\\\"\" >&5)\n   (eval \"$lt_compile\" 2>out/conftest.err)\n   ac_status=$?\n   cat out/conftest.err >&5\n   echo \"$as_me:$LINENO: \\$? = $ac_status\" >&5\n   if (exit $ac_status) && test -s out/conftest2.$ac_objext\n   then\n     # The compiler can only warn and ignore the option if not recognized\n     # So say no if there are warnings\n     $ECHO \"$_lt_compiler_boilerplate\" | $SED '/^$/d' > out/conftest.exp\n     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2\n     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then\n       lt_cv_prog_compiler_c_o=yes\n     fi\n   fi\n   chmod u+w . 2>&5\n   $RM conftest*\n   # SGI C++ compiler will create directory out/ii_files/ for\n   # template instantiation\n   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files\n   $RM out/* && rmdir out\n   cd ..\n   $RM -r conftest\n   $RM conftest*\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o\" >&5\n$as_echo \"$lt_cv_prog_compiler_c_o\" >&6; }\n\n\n\n\nhard_links=nottested\nif test no = \"$lt_cv_prog_compiler_c_o\" && test no != \"$need_locks\"; then\n  # do not overwrite the value of need_locks provided by the user\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links\" >&5\n$as_echo_n \"checking if we can lock with hard links... \" >&6; }\n  hard_links=yes\n  $RM conftest*\n  ln conftest.a conftest.b 2>/dev/null && hard_links=no\n  touch conftest.a\n  ln conftest.a conftest.b 2>&5 || hard_links=no\n  ln conftest.a conftest.b 2>/dev/null && hard_links=no\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $hard_links\" >&5\n$as_echo \"$hard_links\" >&6; }\n  if test no = \"$hard_links\"; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe\" >&5\n$as_echo \"$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe\" >&2;}\n    need_locks=warn\n  fi\nelse\n  need_locks=no\nfi\n\n\n\n\n\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries\" >&5\n$as_echo_n \"checking whether the $compiler linker ($LD) supports shared libraries... \" >&6; }\n\n  runpath_var=\n  allow_undefined_flag=\n  always_export_symbols=no\n  archive_cmds=\n  archive_expsym_cmds=\n  compiler_needs_object=no\n  enable_shared_with_static_runtimes=no\n  export_dynamic_flag_spec=\n  export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\\''s/.* //'\\'' | sort | uniq > $export_symbols'\n  hardcode_automatic=no\n  hardcode_direct=no\n  hardcode_direct_absolute=no\n  hardcode_libdir_flag_spec=\n  hardcode_libdir_separator=\n  hardcode_minus_L=no\n  hardcode_shlibpath_var=unsupported\n  inherit_rpath=no\n  link_all_deplibs=unknown\n  module_cmds=\n  module_expsym_cmds=\n  old_archive_from_new_cmds=\n  old_archive_from_expsyms_cmds=\n  thread_safe_flag_spec=\n  whole_archive_flag_spec=\n  # include_expsyms should be a list of space-separated symbols to be *always*\n  # included in the symbol list\n  include_expsyms=\n  # exclude_expsyms can be an extended regexp of symbols to exclude\n  # it will be wrapped by ' (' and ')$', so one must not match beginning or\n  # end of line.  Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',\n  # as well as any symbol that contains 'd'.\n  exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'\n  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out\n  # platforms (ab)use it in PIC code, but their linkers get confused if\n  # the symbol is explicitly referenced.  Since portable code cannot\n  # rely on this symbol name, it's probably fine to never include it in\n  # preloaded symbol tables.\n  # Exclude shared library initialization/finalization symbols.\n  extract_expsyms_cmds=\n\n  case $host_os in\n  cygwin* | mingw* | pw32* | cegcc*)\n    # FIXME: the MSVC++ port hasn't been tested in a loooong time\n    # When not using gcc, we currently assume that we are using\n    # Microsoft Visual C++.\n    if test yes != \"$GCC\"; then\n      with_gnu_ld=no\n    fi\n    ;;\n  interix*)\n    # we just hope/assume this is gcc and not c89 (= MSVC++)\n    with_gnu_ld=yes\n    ;;\n  openbsd* | bitrig*)\n    with_gnu_ld=no\n    ;;\n  linux* | k*bsd*-gnu | gnu*)\n    link_all_deplibs=no\n    ;;\n  esac\n\n  ld_shlibs=yes\n\n  # On some targets, GNU ld is compatible enough with the native linker\n  # that we're better off using the native interface for both.\n  lt_use_gnu_ld_interface=no\n  if test yes = \"$with_gnu_ld\"; then\n    case $host_os in\n      aix*)\n\t# The AIX port of GNU ld has always aspired to compatibility\n\t# with the native linker.  However, as the warning in the GNU ld\n\t# block says, versions before 2.19.5* couldn't really create working\n\t# shared libraries, regardless of the interface used.\n\tcase `$LD -v 2>&1` in\n\t  *\\ \\(GNU\\ Binutils\\)\\ 2.19.5*) ;;\n\t  *\\ \\(GNU\\ Binutils\\)\\ 2.[2-9]*) ;;\n\t  *\\ \\(GNU\\ Binutils\\)\\ [3-9]*) ;;\n\t  *)\n\t    lt_use_gnu_ld_interface=yes\n\t    ;;\n\tesac\n\t;;\n      *)\n\tlt_use_gnu_ld_interface=yes\n\t;;\n    esac\n  fi\n\n  if test yes = \"$lt_use_gnu_ld_interface\"; then\n    # If archive_cmds runs LD, not CC, wlarc should be empty\n    wlarc='$wl'\n\n    # Set some defaults for GNU ld with shared library support. These\n    # are reset later if shared libraries are not supported. Putting them\n    # here allows them to be overridden if necessary.\n    runpath_var=LD_RUN_PATH\n    hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'\n    export_dynamic_flag_spec='$wl--export-dynamic'\n    # ancient GNU ld didn't support --whole-archive et. al.\n    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then\n      whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'\n    else\n      whole_archive_flag_spec=\n    fi\n    supports_anon_versioning=no\n    case `$LD -v | $SED -e 's/(^)\\+)\\s\\+//' 2>&1` in\n      *GNU\\ gold*) supports_anon_versioning=yes ;;\n      *\\ [01].* | *\\ 2.[0-9].* | *\\ 2.10.*) ;; # catch versions < 2.11\n      *\\ 2.11.93.0.2\\ *) supports_anon_versioning=yes ;; # RH7.3 ...\n      *\\ 2.11.92.0.12\\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...\n      *\\ 2.11.*) ;; # other 2.11 versions\n      *) supports_anon_versioning=yes ;;\n    esac\n\n    # See if GNU ld supports shared libraries.\n    case $host_os in\n    aix[3-9]*)\n      # On AIX/PPC, the GNU linker is very broken\n      if test ia64 != \"$host_cpu\"; then\n\tld_shlibs=no\n\tcat <<_LT_EOF 1>&2\n\n*** Warning: the GNU linker, at least up to release 2.19, is reported\n*** to be unable to reliably create shared libraries on AIX.\n*** Therefore, libtool is disabling shared libraries support.  If you\n*** really care for shared libraries, you may want to install binutils\n*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.\n*** You will then need to restart the configuration process.\n\n_LT_EOF\n      fi\n      ;;\n\n    amigaos*)\n      case $host_cpu in\n      powerpc)\n            # see comment about AmigaOS4 .so support\n            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n            archive_expsym_cmds=''\n        ;;\n      m68k)\n            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO \"#define NAME $libname\" > $output_objdir/a2ixlibrary.data~$ECHO \"#define LIBRARY_ID 1\" >> $output_objdir/a2ixlibrary.data~$ECHO \"#define VERSION $major\" >> $output_objdir/a2ixlibrary.data~$ECHO \"#define REVISION $revision\" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'\n            hardcode_libdir_flag_spec='-L$libdir'\n            hardcode_minus_L=yes\n        ;;\n      esac\n      ;;\n\n    beos*)\n      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then\n\tallow_undefined_flag=unsupported\n\t# Joseph Beckenbach <jrb3@best.com> says some releases of gcc\n\t# support --undefined.  This deserves some investigation.  FIXME\n\tarchive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n      else\n\tld_shlibs=no\n      fi\n      ;;\n\n    cygwin* | mingw* | pw32* | cegcc*)\n      # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,\n      # as there is no search path for DLLs.\n      hardcode_libdir_flag_spec='-L$libdir'\n      export_dynamic_flag_spec='$wl--export-all-symbols'\n      allow_undefined_flag=unsupported\n      always_export_symbols=no\n      enable_shared_with_static_runtimes=yes\n      export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\\''/^[BCDGRS][ ]/s/.*[ ]\\([^ ]*\\)/\\1 DATA/;s/^.*[ ]__nm__\\([^ ]*\\)[ ][^ ]*/\\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\\'' | sort | uniq > $export_symbols'\n      exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'\n\n      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then\n        archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'\n\t# If the export-symbols file already is a .def file, use it as\n\t# is; otherwise, prepend EXPORTS...\n\tarchive_expsym_cmds='if   test DEF = \"`$SED -n     -e '\\''s/^[\t ]*//'\\''     -e '\\''/^\\(;.*\\)*$/d'\\''     -e '\\''s/^\\(EXPORTS\\|LIBRARY\\)\\([\t ].*\\)*$/DEF/p'\\''     -e q     $export_symbols`\" ; then\n          cp $export_symbols $output_objdir/$soname.def;\n        else\n          echo EXPORTS > $output_objdir/$soname.def;\n          cat $export_symbols >> $output_objdir/$soname.def;\n        fi~\n        $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'\n      else\n\tld_shlibs=no\n      fi\n      ;;\n\n    haiku*)\n      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n      link_all_deplibs=yes\n      ;;\n\n    os2*)\n      hardcode_libdir_flag_spec='-L$libdir'\n      hardcode_minus_L=yes\n      allow_undefined_flag=unsupported\n      shrext_cmds=.dll\n      archive_cmds='$ECHO \"LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE\" > $output_objdir/$libname.def~\n\t$ECHO \"DESCRIPTION \\\"$libname\\\"\" >> $output_objdir/$libname.def~\n\t$ECHO \"DATA MULTIPLE NONSHARED\" >> $output_objdir/$libname.def~\n\t$ECHO EXPORTS >> $output_objdir/$libname.def~\n\temxexp $libobjs | $SED /\"_DLL_InitTerm\"/d >> $output_objdir/$libname.def~\n\t$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~\n\temximp -o $lib $output_objdir/$libname.def'\n      archive_expsym_cmds='$ECHO \"LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE\" > $output_objdir/$libname.def~\n\t$ECHO \"DESCRIPTION \\\"$libname\\\"\" >> $output_objdir/$libname.def~\n\t$ECHO \"DATA MULTIPLE NONSHARED\" >> $output_objdir/$libname.def~\n\t$ECHO EXPORTS >> $output_objdir/$libname.def~\n\tprefix_cmds=\"$SED\"~\n\tif test EXPORTS = \"`$SED 1q $export_symbols`\"; then\n\t  prefix_cmds=\"$prefix_cmds -e 1d\";\n\tfi~\n\tprefix_cmds=\"$prefix_cmds -e \\\"s/^\\(.*\\)$/_\\1/g\\\"\"~\n\tcat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~\n\t$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~\n\temximp -o $lib $output_objdir/$libname.def'\n      old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'\n      enable_shared_with_static_runtimes=yes\n      ;;\n\n    interix[3-9]*)\n      hardcode_direct=no\n      hardcode_shlibpath_var=no\n      hardcode_libdir_flag_spec='$wl-rpath,$libdir'\n      export_dynamic_flag_spec='$wl-E'\n      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.\n      # Instead, shared libraries are loaded at an image base (0x10000000 by\n      # default) and relocated if they conflict, which is a slow very memory\n      # consuming and fragmenting process.  To avoid this, we pick a random,\n      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link\n      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.\n      archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \\* 262144 + 1342177280` -o $lib'\n      archive_expsym_cmds='sed \"s|^|_|\" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \\* 262144 + 1342177280` -o $lib'\n      ;;\n\n    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)\n      tmp_diet=no\n      if test linux-dietlibc = \"$host_os\"; then\n\tcase $cc_basename in\n\t  diet\\ *) tmp_diet=yes;;\t# linux-dietlibc with static linking (!diet-dyn)\n\tesac\n      fi\n      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \\\n\t && test no = \"$tmp_diet\"\n      then\n\ttmp_addflag=' $pic_flag'\n\ttmp_sharedflag='-shared'\n\tcase $cc_basename,$host_cpu in\n        pgcc*)\t\t\t\t# Portland Group C compiler\n\t  whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\\\"\\\"; do test  -n \\\"$conv\\\" && new_convenience=\\\"$new_convenience,$conv\\\"; done; func_echo_all \\\"$new_convenience\\\"` $wl--no-whole-archive'\n\t  tmp_addflag=' $pic_flag'\n\t  ;;\n\tpgf77* | pgf90* | pgf95* | pgfortran*)\n\t\t\t\t\t# Portland Group f77 and f90 compilers\n\t  whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\\\"\\\"; do test  -n \\\"$conv\\\" && new_convenience=\\\"$new_convenience,$conv\\\"; done; func_echo_all \\\"$new_convenience\\\"` $wl--no-whole-archive'\n\t  tmp_addflag=' $pic_flag -Mnomain' ;;\n\tecc*,ia64* | icc*,ia64*)\t# Intel C compiler on ia64\n\t  tmp_addflag=' -i_dynamic' ;;\n\tefc*,ia64* | ifort*,ia64*)\t# Intel Fortran compiler on ia64\n\t  tmp_addflag=' -i_dynamic -nofor_main' ;;\n\tifc* | ifort*)\t\t\t# Intel Fortran compiler\n\t  tmp_addflag=' -nofor_main' ;;\n\tlf95*)\t\t\t\t# Lahey Fortran 8.1\n\t  whole_archive_flag_spec=\n\t  tmp_sharedflag='--shared' ;;\n        nagfor*)                        # NAGFOR 5.3\n          tmp_sharedflag='-Wl,-shared' ;;\n\txl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)\n\t  tmp_sharedflag='-qmkshrobj'\n\t  tmp_addflag= ;;\n\tnvcc*)\t# Cuda Compiler Driver 2.2\n\t  whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\\\"\\\"; do test  -n \\\"$conv\\\" && new_convenience=\\\"$new_convenience,$conv\\\"; done; func_echo_all \\\"$new_convenience\\\"` $wl--no-whole-archive'\n\t  compiler_needs_object=yes\n\t  ;;\n\tesac\n\tcase `$CC -V 2>&1 | sed 5q` in\n\t*Sun\\ C*)\t\t\t# Sun C 5.9\n\t  whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\\\"\\\"; do test -z \\\"$conv\\\" || new_convenience=\\\"$new_convenience,$conv\\\"; done; func_echo_all \\\"$new_convenience\\\"` $wl--no-whole-archive'\n\t  compiler_needs_object=yes\n\t  tmp_sharedflag='-G' ;;\n\t*Sun\\ F*)\t\t\t# Sun Fortran 8.3\n\t  tmp_sharedflag='-G' ;;\n\tesac\n\tarchive_cmds='$CC '\"$tmp_sharedflag\"\"$tmp_addflag\"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n\n        if test yes = \"$supports_anon_versioning\"; then\n          archive_expsym_cmds='echo \"{ global:\" > $output_objdir/$libname.ver~\n            cat $export_symbols | sed -e \"s/\\(.*\\)/\\1;/\" >> $output_objdir/$libname.ver~\n            echo \"local: *; };\" >> $output_objdir/$libname.ver~\n            $CC '\"$tmp_sharedflag\"\"$tmp_addflag\"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'\n        fi\n\n\tcase $cc_basename in\n\ttcc*)\n\t  export_dynamic_flag_spec='-rdynamic'\n\t  ;;\n\txlf* | bgf* | bgxlf* | mpixlf*)\n\t  # IBM XL Fortran 10.1 on PPC cannot create shared libs itself\n\t  whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'\n\t  hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'\n\t  archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'\n\t  if test yes = \"$supports_anon_versioning\"; then\n\t    archive_expsym_cmds='echo \"{ global:\" > $output_objdir/$libname.ver~\n              cat $export_symbols | sed -e \"s/\\(.*\\)/\\1;/\" >> $output_objdir/$libname.ver~\n              echo \"local: *; };\" >> $output_objdir/$libname.ver~\n              $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'\n\t  fi\n\t  ;;\n\tesac\n      else\n        ld_shlibs=no\n      fi\n      ;;\n\n    netbsd* | netbsdelf*-gnu)\n      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then\n\tarchive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'\n\twlarc=\n      else\n\tarchive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n\tarchive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'\n      fi\n      ;;\n\n    solaris*)\n      if $LD -v 2>&1 | $GREP 'BFD 2\\.8' > /dev/null; then\n\tld_shlibs=no\n\tcat <<_LT_EOF 1>&2\n\n*** Warning: The releases 2.8.* of the GNU linker cannot reliably\n*** create shared libraries on Solaris systems.  Therefore, libtool\n*** is disabling shared libraries support.  We urge you to upgrade GNU\n*** binutils to release 2.9.1 or newer.  Another option is to modify\n*** your PATH or compiler configuration so that the native linker is\n*** used, and then restart.\n\n_LT_EOF\n      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then\n\tarchive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n\tarchive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'\n      else\n\tld_shlibs=no\n      fi\n      ;;\n\n    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)\n      case `$LD -v 2>&1` in\n        *\\ [01].* | *\\ 2.[0-9].* | *\\ 2.1[0-5].*)\n\tld_shlibs=no\n\tcat <<_LT_EOF 1>&2\n\n*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot\n*** reliably create shared libraries on SCO systems.  Therefore, libtool\n*** is disabling shared libraries support.  We urge you to upgrade GNU\n*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify\n*** your PATH or compiler configuration so that the native linker is\n*** used, and then restart.\n\n_LT_EOF\n\t;;\n\t*)\n\t  # For security reasons, it is highly recommended that you always\n\t  # use absolute paths for naming shared libraries, and exclude the\n\t  # DT_RUNPATH tag from executables and libraries.  But doing so\n\t  # requires that you compile everything twice, which is a pain.\n\t  if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then\n\t    hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'\n\t    archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n\t    archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'\n\t  else\n\t    ld_shlibs=no\n\t  fi\n\t;;\n      esac\n      ;;\n\n    sunos4*)\n      archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'\n      wlarc=\n      hardcode_direct=yes\n      hardcode_shlibpath_var=no\n      ;;\n\n    *)\n      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then\n\tarchive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n\tarchive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'\n      else\n\tld_shlibs=no\n      fi\n      ;;\n    esac\n\n    if test no = \"$ld_shlibs\"; then\n      runpath_var=\n      hardcode_libdir_flag_spec=\n      export_dynamic_flag_spec=\n      whole_archive_flag_spec=\n    fi\n  else\n    # PORTME fill in a description of your system's linker (not GNU ld)\n    case $host_os in\n    aix3*)\n      allow_undefined_flag=unsupported\n      always_export_symbols=yes\n      archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'\n      # Note: this linker hardcodes the directories in LIBPATH if there\n      # are no directories specified by -L.\n      hardcode_minus_L=yes\n      if test yes = \"$GCC\" && test -z \"$lt_prog_compiler_static\"; then\n\t# Neither direct hardcoding nor static linking is supported with a\n\t# broken collect2.\n\thardcode_direct=unsupported\n      fi\n      ;;\n\n    aix[4-9]*)\n      if test ia64 = \"$host_cpu\"; then\n\t# On IA64, the linker does run time linking by default, so we don't\n\t# have to do anything special.\n\taix_use_runtimelinking=no\n\texp_sym_flag='-Bexport'\n\tno_entry_flag=\n      else\n\t# If we're using GNU nm, then we don't want the \"-C\" option.\n\t# -C means demangle to GNU nm, but means don't demangle to AIX nm.\n\t# Without the \"-l\" option, or with the \"-B\" option, AIX nm treats\n\t# weak defined symbols like other global defined symbols, whereas\n\t# GNU nm marks them as \"W\".\n\t# While the 'weak' keyword is ignored in the Export File, we need\n\t# it in the Import File for the 'aix-soname' feature, so we have\n\t# to replace the \"-B\" option with \"-P\" for AIX nm.\n\tif $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then\n\t  export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\\''{ if (((\\$ 2 == \"T\") || (\\$ 2 == \"D\") || (\\$ 2 == \"B\") || (\\$ 2 == \"W\")) && (substr(\\$ 3,1,1) != \".\")) { if (\\$ 2 == \"W\") { print \\$ 3 \" weak\" } else { print \\$ 3 } } }'\\'' | sort -u > $export_symbols'\n\telse\n\t  export_symbols_cmds='`func_echo_all $NM | $SED -e '\\''s/B\\([^B]*\\)$/P\\1/'\\''` -PCpgl $libobjs $convenience | awk '\\''{ if (((\\$ 2 == \"T\") || (\\$ 2 == \"D\") || (\\$ 2 == \"B\") || (\\$ 2 == \"W\") || (\\$ 2 == \"V\") || (\\$ 2 == \"Z\")) && (substr(\\$ 1,1,1) != \".\")) { if ((\\$ 2 == \"W\") || (\\$ 2 == \"V\") || (\\$ 2 == \"Z\")) { print \\$ 1 \" weak\" } else { print \\$ 1 } } }'\\'' | sort -u > $export_symbols'\n\tfi\n\taix_use_runtimelinking=no\n\n\t# Test if we are trying to use run time linking or normal\n\t# AIX style linking. If -brtl is somewhere in LDFLAGS, we\n\t# have runtime linking enabled, and use it for executables.\n\t# For shared libraries, we enable/disable runtime linking\n\t# depending on the kind of the shared library created -\n\t# when \"with_aix_soname,aix_use_runtimelinking\" is:\n\t# \"aix,no\"   lib.a(lib.so.V) shared, rtl:no,  for executables\n\t# \"aix,yes\"  lib.so          shared, rtl:yes, for executables\n\t#            lib.a           static archive\n\t# \"both,no\"  lib.so.V(shr.o) shared, rtl:yes\n\t#            lib.a(lib.so.V) shared, rtl:no,  for executables\n\t# \"both,yes\" lib.so.V(shr.o) shared, rtl:yes, for executables\n\t#            lib.a(lib.so.V) shared, rtl:no\n\t# \"svr4,*\"   lib.so.V(shr.o) shared, rtl:yes, for executables\n\t#            lib.a           static archive\n\tcase $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)\n\t  for ld_flag in $LDFLAGS; do\n\t  if (test x-brtl = \"x$ld_flag\" || test x-Wl,-brtl = \"x$ld_flag\"); then\n\t    aix_use_runtimelinking=yes\n\t    break\n\t  fi\n\t  done\n\t  if test svr4,no = \"$with_aix_soname,$aix_use_runtimelinking\"; then\n\t    # With aix-soname=svr4, we create the lib.so.V shared archives only,\n\t    # so we don't have lib.a shared libs to link our executables.\n\t    # We have to force runtime linking in this case.\n\t    aix_use_runtimelinking=yes\n\t    LDFLAGS=\"$LDFLAGS -Wl,-brtl\"\n\t  fi\n\t  ;;\n\tesac\n\n\texp_sym_flag='-bexport'\n\tno_entry_flag='-bnoentry'\n      fi\n\n      # When large executables or shared objects are built, AIX ld can\n      # have problems creating the table of contents.  If linking a library\n      # or program results in \"error TOC overflow\" add -mminimal-toc to\n      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not\n      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.\n\n      archive_cmds=''\n      hardcode_direct=yes\n      hardcode_direct_absolute=yes\n      hardcode_libdir_separator=':'\n      link_all_deplibs=yes\n      file_list_spec='$wl-f,'\n      case $with_aix_soname,$aix_use_runtimelinking in\n      aix,*) ;; # traditional, no import file\n      svr4,* | *,yes) # use import file\n\t# The Import File defines what to hardcode.\n\thardcode_direct=no\n\thardcode_direct_absolute=no\n\t;;\n      esac\n\n      if test yes = \"$GCC\"; then\n\tcase $host_os in aix4.[012]|aix4.[012].*)\n\t# We only want to do this on AIX 4.2 and lower, the check\n\t# below for broken collect2 doesn't work under 4.3+\n\t  collect2name=`$CC -print-prog-name=collect2`\n\t  if test -f \"$collect2name\" &&\n\t   strings \"$collect2name\" | $GREP resolve_lib_name >/dev/null\n\t  then\n\t  # We have reworked collect2\n\t  :\n\t  else\n\t  # We have old collect2\n\t  hardcode_direct=unsupported\n\t  # It fails to find uninstalled libraries when the uninstalled\n\t  # path is not listed in the libpath.  Setting hardcode_minus_L\n\t  # to unsupported forces relinking\n\t  hardcode_minus_L=yes\n\t  hardcode_libdir_flag_spec='-L$libdir'\n\t  hardcode_libdir_separator=\n\t  fi\n\t  ;;\n\tesac\n\tshared_flag='-shared'\n\tif test yes = \"$aix_use_runtimelinking\"; then\n\t  shared_flag=\"$shared_flag \"'$wl-G'\n\tfi\n\t# Need to ensure runtime linking is disabled for the traditional\n\t# shared library, or the linker may eventually find shared libraries\n\t# /with/ Import File - we do not want to mix them.\n\tshared_flag_aix='-shared'\n\tshared_flag_svr4='-shared $wl-G'\n      else\n\t# not using gcc\n\tif test ia64 = \"$host_cpu\"; then\n\t# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release\n\t# chokes on -Wl,-G. The following line is correct:\n\t  shared_flag='-G'\n\telse\n\t  if test yes = \"$aix_use_runtimelinking\"; then\n\t    shared_flag='$wl-G'\n\t  else\n\t    shared_flag='$wl-bM:SRE'\n\t  fi\n\t  shared_flag_aix='$wl-bM:SRE'\n\t  shared_flag_svr4='$wl-G'\n\tfi\n      fi\n\n      export_dynamic_flag_spec='$wl-bexpall'\n      # It seems that -bexpall does not export symbols beginning with\n      # underscore (_), so it is better to generate a list of symbols to export.\n      always_export_symbols=yes\n      if test aix,yes = \"$with_aix_soname,$aix_use_runtimelinking\"; then\n\t# Warning - without using the other runtime loading flags (-brtl),\n\t# -berok will link without error, but may produce a broken library.\n\tallow_undefined_flag='-berok'\n        # Determine the default libpath from the value encoded in an\n        # empty executable.\n        if test set = \"${lt_cv_aix_libpath+set}\"; then\n  aix_libpath=$lt_cv_aix_libpath\nelse\n  if ${lt_cv_aix_libpath_+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n\n  lt_aix_libpath_sed='\n      /Import File Strings/,/^$/ {\n\t  /^0/ {\n\t      s/^0  *\\([^ ]*\\) *$/\\1/\n\t      p\n\t  }\n      }'\n  lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e \"$lt_aix_libpath_sed\"`\n  # Check for a 64-bit object if we didn't find anything.\n  if test -z \"$lt_cv_aix_libpath_\"; then\n    lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e \"$lt_aix_libpath_sed\"`\n  fi\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\n  if test -z \"$lt_cv_aix_libpath_\"; then\n    lt_cv_aix_libpath_=/usr/lib:/lib\n  fi\n\nfi\n\n  aix_libpath=$lt_cv_aix_libpath_\nfi\n\n        hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'\"$aix_libpath\"\n        archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n \"$allow_undefined_flag\"; then func_echo_all \"$wl$allow_undefined_flag\"; else :; fi` $wl'$exp_sym_flag:\\$export_symbols' '$shared_flag\n      else\n\tif test ia64 = \"$host_cpu\"; then\n\t  hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib'\n\t  allow_undefined_flag=\"-z nodefs\"\n\t  archive_expsym_cmds=\"\\$CC $shared_flag\"' -o $output_objdir/$soname $libobjs $deplibs '\"\\$wl$no_entry_flag\"' $compiler_flags $wl$allow_undefined_flag '\"\\$wl$exp_sym_flag:\\$export_symbols\"\n\telse\n\t # Determine the default libpath from the value encoded in an\n\t # empty executable.\n\t if test set = \"${lt_cv_aix_libpath+set}\"; then\n  aix_libpath=$lt_cv_aix_libpath\nelse\n  if ${lt_cv_aix_libpath_+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n\n  lt_aix_libpath_sed='\n      /Import File Strings/,/^$/ {\n\t  /^0/ {\n\t      s/^0  *\\([^ ]*\\) *$/\\1/\n\t      p\n\t  }\n      }'\n  lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e \"$lt_aix_libpath_sed\"`\n  # Check for a 64-bit object if we didn't find anything.\n  if test -z \"$lt_cv_aix_libpath_\"; then\n    lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e \"$lt_aix_libpath_sed\"`\n  fi\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\n  if test -z \"$lt_cv_aix_libpath_\"; then\n    lt_cv_aix_libpath_=/usr/lib:/lib\n  fi\n\nfi\n\n  aix_libpath=$lt_cv_aix_libpath_\nfi\n\n\t hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'\"$aix_libpath\"\n\t  # Warning - without using the other run time loading flags,\n\t  # -berok will link without error, but may produce a broken library.\n\t  no_undefined_flag=' $wl-bernotok'\n\t  allow_undefined_flag=' $wl-berok'\n\t  if test yes = \"$with_gnu_ld\"; then\n\t    # We only use this code for GNU lds that support --whole-archive.\n\t    whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive'\n\t  else\n\t    # Exported symbols can be pulled into shared objects from archives\n\t    whole_archive_flag_spec='$convenience'\n\t  fi\n\t  archive_cmds_need_lc=yes\n\t  archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'\n\t  # -brtl affects multiple linker settings, -berok does not and is overridden later\n\t  compiler_flags_filtered='`func_echo_all \"$compiler_flags \" | $SED -e \"s%-brtl\\\\([, ]\\\\)%-berok\\\\1%g\"`'\n\t  if test svr4 != \"$with_aix_soname\"; then\n\t    # This is similar to how AIX traditionally builds its shared libraries.\n\t    archive_expsym_cmds=\"$archive_expsym_cmds\"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'\n\t  fi\n\t  if test aix != \"$with_aix_soname\"; then\n\t    archive_expsym_cmds=\"$archive_expsym_cmds\"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all \"#! $soname($shared_archive_member_spec.o)\"; if test shr_64 = \"$shared_archive_member_spec\"; then func_echo_all \"# 64\"; else func_echo_all \"# 32\"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'\n\t  else\n\t    # used by -dlpreopen to get the symbols\n\t    archive_expsym_cmds=\"$archive_expsym_cmds\"'~$MV  $output_objdir/$realname.d/$soname $output_objdir'\n\t  fi\n\t  archive_expsym_cmds=\"$archive_expsym_cmds\"'~$RM -r $output_objdir/$realname.d'\n\tfi\n      fi\n      ;;\n\n    amigaos*)\n      case $host_cpu in\n      powerpc)\n            # see comment about AmigaOS4 .so support\n            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n            archive_expsym_cmds=''\n        ;;\n      m68k)\n            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO \"#define NAME $libname\" > $output_objdir/a2ixlibrary.data~$ECHO \"#define LIBRARY_ID 1\" >> $output_objdir/a2ixlibrary.data~$ECHO \"#define VERSION $major\" >> $output_objdir/a2ixlibrary.data~$ECHO \"#define REVISION $revision\" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'\n            hardcode_libdir_flag_spec='-L$libdir'\n            hardcode_minus_L=yes\n        ;;\n      esac\n      ;;\n\n    bsdi[45]*)\n      export_dynamic_flag_spec=-rdynamic\n      ;;\n\n    cygwin* | mingw* | pw32* | cegcc*)\n      # When not using gcc, we currently assume that we are using\n      # Microsoft Visual C++.\n      # hardcode_libdir_flag_spec is actually meaningless, as there is\n      # no search path for DLLs.\n      case $cc_basename in\n      cl*)\n\t# Native MSVC\n\thardcode_libdir_flag_spec=' '\n\tallow_undefined_flag=unsupported\n\talways_export_symbols=yes\n\tfile_list_spec='@'\n\t# Tell ltmain to make .lib files, not .a files.\n\tlibext=lib\n\t# Tell ltmain to make .dll files, not .so files.\n\tshrext_cmds=.dll\n\t# FIXME: Setting linknames here is a bad hack.\n\tarchive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:\"$tool_output_objdir$libname.dll.lib\"~linknames='\n\tarchive_expsym_cmds='if   test DEF = \"`$SED -n     -e '\\''s/^[\t ]*//'\\''     -e '\\''/^\\(;.*\\)*$/d'\\''     -e '\\''s/^\\(EXPORTS\\|LIBRARY\\)\\([\t ].*\\)*$/DEF/p'\\''     -e q     $export_symbols`\" ; then\n            cp \"$export_symbols\" \"$output_objdir/$soname.def\";\n            echo \"$tool_output_objdir$soname.def\" > \"$output_objdir/$soname.exp\";\n          else\n            $SED -e '\\''s/^/-link -EXPORT:/'\\'' < $export_symbols > $output_objdir/$soname.exp;\n          fi~\n          $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs \"@$tool_output_objdir$soname.exp\" -Wl,-DLL,-IMPLIB:\"$tool_output_objdir$libname.dll.lib\"~\n          linknames='\n\t# The linker will not automatically build a static lib if we build a DLL.\n\t# _LT_TAGVAR(old_archive_from_new_cmds, )='true'\n\tenable_shared_with_static_runtimes=yes\n\texclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'\n\texport_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\\''/^[BCDGRS][ ]/s/.*[ ]\\([^ ]*\\)/\\1,DATA/'\\'' | $SED -e '\\''/^[AITW][ ]/s/.*[ ]//'\\'' | sort | uniq > $export_symbols'\n\t# Don't use ranlib\n\told_postinstall_cmds='chmod 644 $oldlib'\n\tpostlink_cmds='lt_outputfile=\"@OUTPUT@\"~\n          lt_tool_outputfile=\"@TOOL_OUTPUT@\"~\n          case $lt_outputfile in\n            *.exe|*.EXE) ;;\n            *)\n              lt_outputfile=$lt_outputfile.exe\n              lt_tool_outputfile=$lt_tool_outputfile.exe\n              ;;\n          esac~\n          if test : != \"$MANIFEST_TOOL\" && test -f \"$lt_outputfile.manifest\"; then\n            $MANIFEST_TOOL -manifest \"$lt_tool_outputfile.manifest\" -outputresource:\"$lt_tool_outputfile\" || exit 1;\n            $RM \"$lt_outputfile.manifest\";\n          fi'\n\t;;\n      *)\n\t# Assume MSVC wrapper\n\thardcode_libdir_flag_spec=' '\n\tallow_undefined_flag=unsupported\n\t# Tell ltmain to make .lib files, not .a files.\n\tlibext=lib\n\t# Tell ltmain to make .dll files, not .so files.\n\tshrext_cmds=.dll\n\t# FIXME: Setting linknames here is a bad hack.\n\tarchive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all \"$deplibs\" | $SED '\\''s/ -lc$//'\\''` -link -dll~linknames='\n\t# The linker will automatically build a .lib file if we build a DLL.\n\told_archive_from_new_cmds='true'\n\t# FIXME: Should let the user specify the lib program.\n\told_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'\n\tenable_shared_with_static_runtimes=yes\n\t;;\n      esac\n      ;;\n\n    darwin* | rhapsody*)\n\n\n  archive_cmds_need_lc=no\n  hardcode_direct=no\n  hardcode_automatic=yes\n  hardcode_shlibpath_var=unsupported\n  if test yes = \"$lt_cv_ld_force_load\"; then\n    whole_archive_flag_spec='`for conv in $convenience\\\"\\\"; do test  -n \\\"$conv\\\" && new_convenience=\\\"$new_convenience $wl-force_load,$conv\\\"; done; func_echo_all \\\"$new_convenience\\\"`'\n\n  else\n    whole_archive_flag_spec=''\n  fi\n  link_all_deplibs=yes\n  allow_undefined_flag=$_lt_dar_allow_undefined\n  case $cc_basename in\n     ifort*|nagfor*) _lt_dar_can_shared=yes ;;\n     *) _lt_dar_can_shared=$GCC ;;\n  esac\n  if test yes = \"$_lt_dar_can_shared\"; then\n    output_verbose_link_cmd=func_echo_all\n    archive_cmds=\"\\$CC -dynamiclib \\$allow_undefined_flag -o \\$lib \\$libobjs \\$deplibs \\$compiler_flags -install_name \\$rpath/\\$soname \\$verstring $_lt_dar_single_mod$_lt_dsymutil\"\n    module_cmds=\"\\$CC \\$allow_undefined_flag -o \\$lib -bundle \\$libobjs \\$deplibs \\$compiler_flags$_lt_dsymutil\"\n    archive_expsym_cmds=\"sed 's|^|_|' < \\$export_symbols > \\$output_objdir/\\$libname-symbols.expsym~\\$CC -dynamiclib \\$allow_undefined_flag -o \\$lib \\$libobjs \\$deplibs \\$compiler_flags -install_name \\$rpath/\\$soname \\$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil\"\n    module_expsym_cmds=\"sed -e 's|^|_|' < \\$export_symbols > \\$output_objdir/\\$libname-symbols.expsym~\\$CC \\$allow_undefined_flag -o \\$lib -bundle \\$libobjs \\$deplibs \\$compiler_flags$_lt_dar_export_syms$_lt_dsymutil\"\n\n  else\n  ld_shlibs=no\n  fi\n\n      ;;\n\n    dgux*)\n      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'\n      hardcode_libdir_flag_spec='-L$libdir'\n      hardcode_shlibpath_var=no\n      ;;\n\n    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor\n    # support.  Future versions do this automatically, but an explicit c++rt0.o\n    # does not break anything, and helps significantly (at the cost of a little\n    # extra space).\n    freebsd2.2*)\n      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'\n      hardcode_libdir_flag_spec='-R$libdir'\n      hardcode_direct=yes\n      hardcode_shlibpath_var=no\n      ;;\n\n    # Unfortunately, older versions of FreeBSD 2 do not have this feature.\n    freebsd2.*)\n      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'\n      hardcode_direct=yes\n      hardcode_minus_L=yes\n      hardcode_shlibpath_var=no\n      ;;\n\n    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.\n    freebsd* | dragonfly*)\n      archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'\n      hardcode_libdir_flag_spec='-R$libdir'\n      hardcode_direct=yes\n      hardcode_shlibpath_var=no\n      ;;\n\n    hpux9*)\n      if test yes = \"$GCC\"; then\n\tarchive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test \"x$output_objdir/$soname\" = \"x$lib\" || mv $output_objdir/$soname $lib'\n      else\n\tarchive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test \"x$output_objdir/$soname\" = \"x$lib\" || mv $output_objdir/$soname $lib'\n      fi\n      hardcode_libdir_flag_spec='$wl+b $wl$libdir'\n      hardcode_libdir_separator=:\n      hardcode_direct=yes\n\n      # hardcode_minus_L: Not really in the search PATH,\n      # but as the default location of the library.\n      hardcode_minus_L=yes\n      export_dynamic_flag_spec='$wl-E'\n      ;;\n\n    hpux10*)\n      if test yes,no = \"$GCC,$with_gnu_ld\"; then\n\tarchive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'\n      else\n\tarchive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'\n      fi\n      if test no = \"$with_gnu_ld\"; then\n\thardcode_libdir_flag_spec='$wl+b $wl$libdir'\n\thardcode_libdir_separator=:\n\thardcode_direct=yes\n\thardcode_direct_absolute=yes\n\texport_dynamic_flag_spec='$wl-E'\n\t# hardcode_minus_L: Not really in the search PATH,\n\t# but as the default location of the library.\n\thardcode_minus_L=yes\n      fi\n      ;;\n\n    hpux11*)\n      if test yes,no = \"$GCC,$with_gnu_ld\"; then\n\tcase $host_cpu in\n\thppa*64*)\n\t  archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'\n\t  ;;\n\tia64*)\n\t  archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'\n\t  ;;\n\t*)\n\t  archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'\n\t  ;;\n\tesac\n      else\n\tcase $host_cpu in\n\thppa*64*)\n\t  archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'\n\t  ;;\n\tia64*)\n\t  archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'\n\t  ;;\n\t*)\n\n\t  # Older versions of the 11.00 compiler do not understand -b yet\n\t  # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)\n\t  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $CC understands -b\" >&5\n$as_echo_n \"checking if $CC understands -b... \" >&6; }\nif ${lt_cv_prog_compiler__b+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler__b=no\n   save_LDFLAGS=$LDFLAGS\n   LDFLAGS=\"$LDFLAGS -b\"\n   echo \"$lt_simple_link_test_code\" > conftest.$ac_ext\n   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then\n     # The linker can only warn and ignore the option if not recognized\n     # So say no if there are warnings\n     if test -s conftest.err; then\n       # Append any errors to the config.log.\n       cat conftest.err 1>&5\n       $ECHO \"$_lt_linker_boilerplate\" | $SED '/^$/d' > conftest.exp\n       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2\n       if diff conftest.exp conftest.er2 >/dev/null; then\n         lt_cv_prog_compiler__b=yes\n       fi\n     else\n       lt_cv_prog_compiler__b=yes\n     fi\n   fi\n   $RM -r conftest*\n   LDFLAGS=$save_LDFLAGS\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b\" >&5\n$as_echo \"$lt_cv_prog_compiler__b\" >&6; }\n\nif test yes = \"$lt_cv_prog_compiler__b\"; then\n    archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'\nelse\n    archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'\nfi\n\n\t  ;;\n\tesac\n      fi\n      if test no = \"$with_gnu_ld\"; then\n\thardcode_libdir_flag_spec='$wl+b $wl$libdir'\n\thardcode_libdir_separator=:\n\n\tcase $host_cpu in\n\thppa*64*|ia64*)\n\t  hardcode_direct=no\n\t  hardcode_shlibpath_var=no\n\t  ;;\n\t*)\n\t  hardcode_direct=yes\n\t  hardcode_direct_absolute=yes\n\t  export_dynamic_flag_spec='$wl-E'\n\n\t  # hardcode_minus_L: Not really in the search PATH,\n\t  # but as the default location of the library.\n\t  hardcode_minus_L=yes\n\t  ;;\n\tesac\n      fi\n      ;;\n\n    irix5* | irix6* | nonstopux*)\n      if test yes = \"$GCC\"; then\n\tarchive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n \"$verstring\" && func_echo_all \"$wl-set_version $wl$verstring\"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'\n\t# Try to use the -exported_symbol ld option, if it does not\n\t# work, assume that -exports_file does not work either and\n\t# implicitly export all symbols.\n\t# This should be the same for all languages, so no per-tag cache variable.\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol\" >&5\n$as_echo_n \"checking whether the $host_os linker accepts -exported_symbol... \" >&6; }\nif ${lt_cv_irix_exported_symbol+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  save_LDFLAGS=$LDFLAGS\n\t   LDFLAGS=\"$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null\"\n\t   cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\nint foo (void) { return 0; }\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  lt_cv_irix_exported_symbol=yes\nelse\n  lt_cv_irix_exported_symbol=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\n           LDFLAGS=$save_LDFLAGS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol\" >&5\n$as_echo \"$lt_cv_irix_exported_symbol\" >&6; }\n\tif test yes = \"$lt_cv_irix_exported_symbol\"; then\n          archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n \"$verstring\" && func_echo_all \"$wl-set_version $wl$verstring\"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'\n\tfi\n\tlink_all_deplibs=no\n      else\n\tarchive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n \"$verstring\" && func_echo_all \"-set_version $verstring\"` -update_registry $output_objdir/so_locations -o $lib'\n\tarchive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n \"$verstring\" && func_echo_all \"-set_version $verstring\"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'\n      fi\n      archive_cmds_need_lc='no'\n      hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'\n      hardcode_libdir_separator=:\n      inherit_rpath=yes\n      link_all_deplibs=yes\n      ;;\n\n    linux*)\n      case $cc_basename in\n      tcc*)\n\t# Fabrice Bellard et al's Tiny C Compiler\n\tld_shlibs=yes\n\tarchive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'\n\t;;\n      esac\n      ;;\n\n    netbsd* | netbsdelf*-gnu)\n      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then\n\tarchive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out\n      else\n\tarchive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF\n      fi\n      hardcode_libdir_flag_spec='-R$libdir'\n      hardcode_direct=yes\n      hardcode_shlibpath_var=no\n      ;;\n\n    newsos6)\n      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'\n      hardcode_direct=yes\n      hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'\n      hardcode_libdir_separator=:\n      hardcode_shlibpath_var=no\n      ;;\n\n    *nto* | *qnx*)\n      ;;\n\n    openbsd* | bitrig*)\n      if test -f /usr/libexec/ld.so; then\n\thardcode_direct=yes\n\thardcode_shlibpath_var=no\n\thardcode_direct_absolute=yes\n\tif test -z \"`echo __ELF__ | $CC -E - | $GREP __ELF__`\"; then\n\t  archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'\n\t  archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'\n\t  hardcode_libdir_flag_spec='$wl-rpath,$libdir'\n\t  export_dynamic_flag_spec='$wl-E'\n\telse\n\t  archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'\n\t  hardcode_libdir_flag_spec='$wl-rpath,$libdir'\n\tfi\n      else\n\tld_shlibs=no\n      fi\n      ;;\n\n    os2*)\n      hardcode_libdir_flag_spec='-L$libdir'\n      hardcode_minus_L=yes\n      allow_undefined_flag=unsupported\n      shrext_cmds=.dll\n      archive_cmds='$ECHO \"LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE\" > $output_objdir/$libname.def~\n\t$ECHO \"DESCRIPTION \\\"$libname\\\"\" >> $output_objdir/$libname.def~\n\t$ECHO \"DATA MULTIPLE NONSHARED\" >> $output_objdir/$libname.def~\n\t$ECHO EXPORTS >> $output_objdir/$libname.def~\n\temxexp $libobjs | $SED /\"_DLL_InitTerm\"/d >> $output_objdir/$libname.def~\n\t$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~\n\temximp -o $lib $output_objdir/$libname.def'\n      archive_expsym_cmds='$ECHO \"LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE\" > $output_objdir/$libname.def~\n\t$ECHO \"DESCRIPTION \\\"$libname\\\"\" >> $output_objdir/$libname.def~\n\t$ECHO \"DATA MULTIPLE NONSHARED\" >> $output_objdir/$libname.def~\n\t$ECHO EXPORTS >> $output_objdir/$libname.def~\n\tprefix_cmds=\"$SED\"~\n\tif test EXPORTS = \"`$SED 1q $export_symbols`\"; then\n\t  prefix_cmds=\"$prefix_cmds -e 1d\";\n\tfi~\n\tprefix_cmds=\"$prefix_cmds -e \\\"s/^\\(.*\\)$/_\\1/g\\\"\"~\n\tcat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~\n\t$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~\n\temximp -o $lib $output_objdir/$libname.def'\n      old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'\n      enable_shared_with_static_runtimes=yes\n      ;;\n\n    osf3*)\n      if test yes = \"$GCC\"; then\n\tallow_undefined_flag=' $wl-expect_unresolved $wl\\*'\n\tarchive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n \"$verstring\" && func_echo_all \"$wl-set_version $wl$verstring\"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'\n      else\n\tallow_undefined_flag=' -expect_unresolved \\*'\n\tarchive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n \"$verstring\" && func_echo_all \"-set_version $verstring\"` -update_registry $output_objdir/so_locations -o $lib'\n      fi\n      archive_cmds_need_lc='no'\n      hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'\n      hardcode_libdir_separator=:\n      ;;\n\n    osf4* | osf5*)\t# as osf3* with the addition of -msym flag\n      if test yes = \"$GCC\"; then\n\tallow_undefined_flag=' $wl-expect_unresolved $wl\\*'\n\tarchive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n \"$verstring\" && func_echo_all \"$wl-set_version $wl$verstring\"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'\n\thardcode_libdir_flag_spec='$wl-rpath $wl$libdir'\n      else\n\tallow_undefined_flag=' -expect_unresolved \\*'\n\tarchive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n \"$verstring\" && func_echo_all \"-set_version $verstring\"` -update_registry $output_objdir/so_locations -o $lib'\n\tarchive_expsym_cmds='for i in `cat $export_symbols`; do printf \"%s %s\\\\n\" -exported_symbol \"\\$i\" >> $lib.exp; done; printf \"%s\\\\n\" \"-hidden\">> $lib.exp~\n          $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n \"$verstring\" && $ECHO \"-set_version $verstring\"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'\n\n\t# Both c and cxx compiler support -rpath directly\n\thardcode_libdir_flag_spec='-rpath $libdir'\n      fi\n      archive_cmds_need_lc='no'\n      hardcode_libdir_separator=:\n      ;;\n\n    solaris*)\n      no_undefined_flag=' -z defs'\n      if test yes = \"$GCC\"; then\n\twlarc='$wl'\n\tarchive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'\n\tarchive_expsym_cmds='echo \"{ global:\" > $lib.exp~cat $export_symbols | $SED -e \"s/\\(.*\\)/\\1;/\" >> $lib.exp~echo \"local: *; };\" >> $lib.exp~\n          $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'\n      else\n\tcase `$CC -V 2>&1` in\n\t*\"Compilers 5.0\"*)\n\t  wlarc=''\n\t  archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'\n\t  archive_expsym_cmds='echo \"{ global:\" > $lib.exp~cat $export_symbols | $SED -e \"s/\\(.*\\)/\\1;/\" >> $lib.exp~echo \"local: *; };\" >> $lib.exp~\n            $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'\n\t  ;;\n\t*)\n\t  wlarc='$wl'\n\t  archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'\n\t  archive_expsym_cmds='echo \"{ global:\" > $lib.exp~cat $export_symbols | $SED -e \"s/\\(.*\\)/\\1;/\" >> $lib.exp~echo \"local: *; };\" >> $lib.exp~\n            $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'\n\t  ;;\n\tesac\n      fi\n      hardcode_libdir_flag_spec='-R$libdir'\n      hardcode_shlibpath_var=no\n      case $host_os in\n      solaris2.[0-5] | solaris2.[0-5].*) ;;\n      *)\n\t# The compiler driver will combine and reorder linker options,\n\t# but understands '-z linker_flag'.  GCC discards it without '$wl',\n\t# but is careful enough not to reorder.\n\t# Supported since Solaris 2.6 (maybe 2.5.1?)\n\tif test yes = \"$GCC\"; then\n\t  whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'\n\telse\n\t  whole_archive_flag_spec='-z allextract$convenience -z defaultextract'\n\tfi\n\t;;\n      esac\n      link_all_deplibs=yes\n      ;;\n\n    sunos4*)\n      if test sequent = \"$host_vendor\"; then\n\t# Use $CC to link under sequent, because it throws in some extra .o\n\t# files that make .init and .fini sections work.\n\tarchive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'\n      else\n\tarchive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'\n      fi\n      hardcode_libdir_flag_spec='-L$libdir'\n      hardcode_direct=yes\n      hardcode_minus_L=yes\n      hardcode_shlibpath_var=no\n      ;;\n\n    sysv4)\n      case $host_vendor in\n\tsni)\n\t  archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'\n\t  hardcode_direct=yes # is this really true???\n\t;;\n\tsiemens)\n\t  ## LD is ld it makes a PLAMLIB\n\t  ## CC just makes a GrossModule.\n\t  archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'\n\t  reload_cmds='$CC -r -o $output$reload_objs'\n\t  hardcode_direct=no\n        ;;\n\tmotorola)\n\t  archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'\n\t  hardcode_direct=no #Motorola manual says yes, but my tests say they lie\n\t;;\n      esac\n      runpath_var='LD_RUN_PATH'\n      hardcode_shlibpath_var=no\n      ;;\n\n    sysv4.3*)\n      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'\n      hardcode_shlibpath_var=no\n      export_dynamic_flag_spec='-Bexport'\n      ;;\n\n    sysv4*MP*)\n      if test -d /usr/nec; then\n\tarchive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'\n\thardcode_shlibpath_var=no\n\trunpath_var=LD_RUN_PATH\n\thardcode_runpath_var=yes\n\tld_shlibs=yes\n      fi\n      ;;\n\n    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)\n      no_undefined_flag='$wl-z,text'\n      archive_cmds_need_lc=no\n      hardcode_shlibpath_var=no\n      runpath_var='LD_RUN_PATH'\n\n      if test yes = \"$GCC\"; then\n\tarchive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\tarchive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n      else\n\tarchive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\tarchive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n      fi\n      ;;\n\n    sysv5* | sco3.2v5* | sco5v6*)\n      # Note: We CANNOT use -z defs as we might desire, because we do not\n      # link with -lc, and that would cause any symbols used from libc to\n      # always be unresolved, which means just about no library would\n      # ever link correctly.  If we're not using GNU ld we use -z text\n      # though, which does catch some bad symbols but isn't as heavy-handed\n      # as -z defs.\n      no_undefined_flag='$wl-z,text'\n      allow_undefined_flag='$wl-z,nodefs'\n      archive_cmds_need_lc=no\n      hardcode_shlibpath_var=no\n      hardcode_libdir_flag_spec='$wl-R,$libdir'\n      hardcode_libdir_separator=':'\n      link_all_deplibs=yes\n      export_dynamic_flag_spec='$wl-Bexport'\n      runpath_var='LD_RUN_PATH'\n\n      if test yes = \"$GCC\"; then\n\tarchive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\tarchive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n      else\n\tarchive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\tarchive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n      fi\n      ;;\n\n    uts4*)\n      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'\n      hardcode_libdir_flag_spec='-L$libdir'\n      hardcode_shlibpath_var=no\n      ;;\n\n    *)\n      ld_shlibs=no\n      ;;\n    esac\n\n    if test sni = \"$host_vendor\"; then\n      case $host in\n      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)\n\texport_dynamic_flag_spec='$wl-Blargedynsym'\n\t;;\n      esac\n    fi\n  fi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ld_shlibs\" >&5\n$as_echo \"$ld_shlibs\" >&6; }\ntest no = \"$ld_shlibs\" && can_build_shared=no\n\nwith_gnu_ld=$with_gnu_ld\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n#\n# Do we need to explicitly link libc?\n#\ncase \"x$archive_cmds_need_lc\" in\nx|xyes)\n  # Assume -lc should be added\n  archive_cmds_need_lc=yes\n\n  if test yes,yes = \"$GCC,$enable_shared\"; then\n    case $archive_cmds in\n    *'~'*)\n      # FIXME: we may have to deal with multi-command sequences.\n      ;;\n    '$CC '*)\n      # Test whether the compiler implicitly links with -lc since on some\n      # systems, -lgcc has to come before -lc. If gcc already passes -lc\n      # to ld, don't add -lc before -lgcc.\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in\" >&5\n$as_echo_n \"checking whether -lc should be explicitly linked in... \" >&6; }\nif ${lt_cv_archive_cmds_need_lc+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  $RM conftest*\n\techo \"$lt_simple_compile_test_code\" > conftest.$ac_ext\n\n\tif { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_compile\\\"\"; } >&5\n  (eval $ac_compile) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } 2>conftest.err; then\n\t  soname=conftest\n\t  lib=conftest\n\t  libobjs=conftest.$ac_objext\n\t  deplibs=\n\t  wl=$lt_prog_compiler_wl\n\t  pic_flag=$lt_prog_compiler_pic\n\t  compiler_flags=-v\n\t  linker_flags=-v\n\t  verstring=\n\t  output_objdir=.\n\t  libname=conftest\n\t  lt_save_allow_undefined_flag=$allow_undefined_flag\n\t  allow_undefined_flag=\n\t  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$archive_cmds 2\\>\\&1 \\| $GREP \\\" -lc \\\" \\>/dev/null 2\\>\\&1\\\"\"; } >&5\n  (eval $archive_cmds 2\\>\\&1 \\| $GREP \\\" -lc \\\" \\>/dev/null 2\\>\\&1) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\n\t  then\n\t    lt_cv_archive_cmds_need_lc=no\n\t  else\n\t    lt_cv_archive_cmds_need_lc=yes\n\t  fi\n\t  allow_undefined_flag=$lt_save_allow_undefined_flag\n\telse\n\t  cat conftest.err 1>&5\n\tfi\n\t$RM conftest*\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc\" >&5\n$as_echo \"$lt_cv_archive_cmds_need_lc\" >&6; }\n      archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc\n      ;;\n    esac\n  fi\n  ;;\nesac\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\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\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\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\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\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics\" >&5\n$as_echo_n \"checking dynamic linker characteristics... \" >&6; }\n\nif test yes = \"$GCC\"; then\n  case $host_os in\n    darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;\n    *) lt_awk_arg='/^libraries:/' ;;\n  esac\n  case $host_os in\n    mingw* | cegcc*) lt_sed_strip_eq='s|=\\([A-Za-z]:\\)|\\1|g' ;;\n    *) lt_sed_strip_eq='s|=/|/|g' ;;\n  esac\n  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e \"s/^libraries://\" -e $lt_sed_strip_eq`\n  case $lt_search_path_spec in\n  *\\;*)\n    # if the path contains \";\" then we assume it to be the separator\n    # otherwise default to the standard path separator (i.e. \":\") - it is\n    # assumed that no part of a normal pathname contains \";\" but that should\n    # okay in the real world where \";\" in dirpaths is itself problematic.\n    lt_search_path_spec=`$ECHO \"$lt_search_path_spec\" | $SED 's/;/ /g'`\n    ;;\n  *)\n    lt_search_path_spec=`$ECHO \"$lt_search_path_spec\" | $SED \"s/$PATH_SEPARATOR/ /g\"`\n    ;;\n  esac\n  # Ok, now we have the path, separated by spaces, we can step through it\n  # and add multilib dir if necessary...\n  lt_tmp_lt_search_path_spec=\n  lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`\n  # ...but if some path component already ends with the multilib dir we assume\n  # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).\n  case \"$lt_multi_os_dir; $lt_search_path_spec \" in\n  \"/; \"* | \"/.; \"* | \"/./; \"* | *\"$lt_multi_os_dir \"* | *\"$lt_multi_os_dir/ \"*)\n    lt_multi_os_dir=\n    ;;\n  esac\n  for lt_sys_path in $lt_search_path_spec; do\n    if test -d \"$lt_sys_path$lt_multi_os_dir\"; then\n      lt_tmp_lt_search_path_spec=\"$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir\"\n    elif test -n \"$lt_multi_os_dir\"; then\n      test -d \"$lt_sys_path\" && \\\n\tlt_tmp_lt_search_path_spec=\"$lt_tmp_lt_search_path_spec $lt_sys_path\"\n    fi\n  done\n  lt_search_path_spec=`$ECHO \"$lt_tmp_lt_search_path_spec\" | awk '\nBEGIN {RS = \" \"; FS = \"/|\\n\";} {\n  lt_foo = \"\";\n  lt_count = 0;\n  for (lt_i = NF; lt_i > 0; lt_i--) {\n    if ($lt_i != \"\" && $lt_i != \".\") {\n      if ($lt_i == \"..\") {\n        lt_count++;\n      } else {\n        if (lt_count == 0) {\n          lt_foo = \"/\" $lt_i lt_foo;\n        } else {\n          lt_count--;\n        }\n      }\n    }\n  }\n  if (lt_foo != \"\") { lt_freq[lt_foo]++; }\n  if (lt_freq[lt_foo] == 1) { print lt_foo; }\n}'`\n  # AWK program above erroneously prepends '/' to C:/dos/paths\n  # for these hosts.\n  case $host_os in\n    mingw* | cegcc*) lt_search_path_spec=`$ECHO \"$lt_search_path_spec\" |\\\n      $SED 's|/\\([A-Za-z]:\\)|\\1|g'` ;;\n  esac\n  sys_lib_search_path_spec=`$ECHO \"$lt_search_path_spec\" | $lt_NL2SP`\nelse\n  sys_lib_search_path_spec=\"/lib /usr/lib /usr/local/lib\"\nfi\nlibrary_names_spec=\nlibname_spec='lib$name'\nsoname_spec=\nshrext_cmds=.so\npostinstall_cmds=\npostuninstall_cmds=\nfinish_cmds=\nfinish_eval=\nshlibpath_var=\nshlibpath_overrides_runpath=unknown\nversion_type=none\ndynamic_linker=\"$host_os ld.so\"\nsys_lib_dlsearch_path_spec=\"/lib /usr/lib\"\nneed_lib_prefix=unknown\nhardcode_into_libs=no\n\n# when you set need_version to no, make sure it does not cause -set_version\n# flags to be left without arguments\nneed_version=unknown\n\n\n\ncase $host_os in\naix3*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  library_names_spec='$libname$release$shared_ext$versuffix $libname.a'\n  shlibpath_var=LIBPATH\n\n  # AIX 3 has no versioning support, so we append a major version to the name.\n  soname_spec='$libname$release$shared_ext$major'\n  ;;\n\naix[4-9]*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  hardcode_into_libs=yes\n  if test ia64 = \"$host_cpu\"; then\n    # AIX 5 supports IA64\n    library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'\n    shlibpath_var=LD_LIBRARY_PATH\n  else\n    # With GCC up to 2.95.x, collect2 would create an import file\n    # for dependence libraries.  The import file would start with\n    # the line '#! .'.  This would cause the generated library to\n    # depend on '.', always an invalid library.  This was fixed in\n    # development snapshots of GCC prior to 3.0.\n    case $host_os in\n      aix4 | aix4.[01] | aix4.[01].*)\n      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'\n\t   echo ' yes '\n\t   echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then\n\t:\n      else\n\tcan_build_shared=no\n      fi\n      ;;\n    esac\n    # Using Import Files as archive members, it is possible to support\n    # filename-based versioning of shared library archives on AIX. While\n    # this would work for both with and without runtime linking, it will\n    # prevent static linking of such archives. So we do filename-based\n    # shared library versioning with .so extension only, which is used\n    # when both runtime linking and shared linking is enabled.\n    # Unfortunately, runtime linking may impact performance, so we do\n    # not want this to be the default eventually. Also, we use the\n    # versioned .so libs for executables only if there is the -brtl\n    # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.\n    # To allow for filename-based versioning support, we need to create\n    # libNAME.so.V as an archive file, containing:\n    # *) an Import File, referring to the versioned filename of the\n    #    archive as well as the shared archive member, telling the\n    #    bitwidth (32 or 64) of that shared object, and providing the\n    #    list of exported symbols of that shared object, eventually\n    #    decorated with the 'weak' keyword\n    # *) the shared object with the F_LOADONLY flag set, to really avoid\n    #    it being seen by the linker.\n    # At run time we better use the real file rather than another symlink,\n    # but for link time we create the symlink libNAME.so -> libNAME.so.V\n\n    case $with_aix_soname,$aix_use_runtimelinking in\n    # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct\n    # soname into executable. Probably we can add versioning support to\n    # collect2, so additional links can be useful in future.\n    aix,yes) # traditional libtool\n      dynamic_linker='AIX unversionable lib.so'\n      # If using run time linking (on AIX 4.2 or later) use lib<name>.so\n      # instead of lib<name>.a to let people know that these are not\n      # typical AIX shared libraries.\n      library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n      ;;\n    aix,no) # traditional AIX only\n      dynamic_linker='AIX lib.a(lib.so.V)'\n      # We preserve .a as extension for shared libraries through AIX4.2\n      # and later when we are not doing run time linking.\n      library_names_spec='$libname$release.a $libname.a'\n      soname_spec='$libname$release$shared_ext$major'\n      ;;\n    svr4,*) # full svr4 only\n      dynamic_linker=\"AIX lib.so.V($shared_archive_member_spec.o)\"\n      library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'\n      # We do not specify a path in Import Files, so LIBPATH fires.\n      shlibpath_overrides_runpath=yes\n      ;;\n    *,yes) # both, prefer svr4\n      dynamic_linker=\"AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)\"\n      library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'\n      # unpreferred sharedlib libNAME.a needs extra handling\n      postinstall_cmds='test -n \"$linkname\" || linkname=\"$realname\"~func_stripname \"\" \".so\" \"$linkname\"~$install_shared_prog \"$dir/$func_stripname_result.$libext\" \"$destdir/$func_stripname_result.$libext\"~test -z \"$tstripme\" || test -z \"$striplib\" || $striplib \"$destdir/$func_stripname_result.$libext\"'\n      postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname \"\" \".so\" \"$n\"~test \"$func_stripname_result\" = \"$n\" || func_append rmfiles \" $odir/$func_stripname_result.$libext\"'\n      # We do not specify a path in Import Files, so LIBPATH fires.\n      shlibpath_overrides_runpath=yes\n      ;;\n    *,no) # both, prefer aix\n      dynamic_linker=\"AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)\"\n      library_names_spec='$libname$release.a $libname.a'\n      soname_spec='$libname$release$shared_ext$major'\n      # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling\n      postinstall_cmds='test -z \"$dlname\" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z \"$tstripme\" || test -z \"$striplib\" || $striplib $destdir/$dlname~test -n \"$linkname\" || linkname=$realname~func_stripname \"\" \".a\" \"$linkname\"~(cd \"$destdir\" && $LN_S -f $dlname $func_stripname_result.so)'\n      postuninstall_cmds='test -z \"$dlname\" || func_append rmfiles \" $odir/$dlname\"~for n in $old_library $library_names; do :; done~func_stripname \"\" \".a\" \"$n\"~func_append rmfiles \" $odir/$func_stripname_result.so\"'\n      ;;\n    esac\n    shlibpath_var=LIBPATH\n  fi\n  ;;\n\namigaos*)\n  case $host_cpu in\n  powerpc)\n    # Since July 2007 AmigaOS4 officially supports .so libraries.\n    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n    ;;\n  m68k)\n    library_names_spec='$libname.ixlibrary $libname.a'\n    # Create ${libname}_ixlibrary.a entries in /sys/libs.\n    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all \"$lib\" | $SED '\\''s%^.*/\\([^/]*\\)\\.ixlibrary$%\\1%'\\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show \"cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a\"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'\n    ;;\n  esac\n  ;;\n\nbeos*)\n  library_names_spec='$libname$shared_ext'\n  dynamic_linker=\"$host_os ld.so\"\n  shlibpath_var=LIBRARY_PATH\n  ;;\n\nbsdi[45]*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  finish_cmds='PATH=\"\\$PATH:/sbin\" ldconfig $libdir'\n  shlibpath_var=LD_LIBRARY_PATH\n  sys_lib_search_path_spec=\"/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib\"\n  sys_lib_dlsearch_path_spec=\"/shlib /usr/lib /usr/local/lib\"\n  # the default ld.so.conf also contains /usr/contrib/lib and\n  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow\n  # libtool to hard-code these into programs\n  ;;\n\ncygwin* | mingw* | pw32* | cegcc*)\n  version_type=windows\n  shrext_cmds=.dll\n  need_version=no\n  need_lib_prefix=no\n\n  case $GCC,$cc_basename in\n  yes,*)\n    # gcc\n    library_names_spec='$libname.dll.a'\n    # DLL is installed to $(libdir)/../bin by postinstall_cmds\n    postinstall_cmds='base_file=`basename \\$file`~\n      dlpath=`$SHELL 2>&1 -c '\\''. $dir/'\\''\\$base_file'\\''i; echo \\$dlname'\\''`~\n      dldir=$destdir/`dirname \\$dlpath`~\n      test -d \\$dldir || mkdir -p \\$dldir~\n      $install_prog $dir/$dlname \\$dldir/$dlname~\n      chmod a+x \\$dldir/$dlname~\n      if test -n '\\''$stripme'\\'' && test -n '\\''$striplib'\\''; then\n        eval '\\''$striplib \\$dldir/$dlname'\\'' || exit \\$?;\n      fi'\n    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\\''. $file; echo \\$dlname'\\''`~\n      dlpath=$dir/\\$dldll~\n       $RM \\$dlpath'\n    shlibpath_overrides_runpath=yes\n\n    case $host_os in\n    cygwin*)\n      # Cygwin DLLs use 'cyg' prefix rather than 'lib'\n      soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'\n\n      sys_lib_search_path_spec=\"$sys_lib_search_path_spec /usr/lib/w32api\"\n      ;;\n    mingw* | cegcc*)\n      # MinGW DLLs use traditional 'lib' prefix\n      soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'\n      ;;\n    pw32*)\n      # pw32 DLLs use 'pw' prefix rather than 'lib'\n      library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'\n      ;;\n    esac\n    dynamic_linker='Win32 ld.exe'\n    ;;\n\n  *,cl*)\n    # Native MSVC\n    libname_spec='$name'\n    soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'\n    library_names_spec='$libname.dll.lib'\n\n    case $build_os in\n    mingw*)\n      sys_lib_search_path_spec=\n      lt_save_ifs=$IFS\n      IFS=';'\n      for lt_path in $LIB\n      do\n        IFS=$lt_save_ifs\n        # Let DOS variable expansion print the short 8.3 style file name.\n        lt_path=`cd \"$lt_path\" 2>/dev/null && cmd //C \"for %i in (\".\") do @echo %~si\"`\n        sys_lib_search_path_spec=\"$sys_lib_search_path_spec $lt_path\"\n      done\n      IFS=$lt_save_ifs\n      # Convert to MSYS style.\n      sys_lib_search_path_spec=`$ECHO \"$sys_lib_search_path_spec\" | sed -e 's|\\\\\\\\|/|g' -e 's| \\\\([a-zA-Z]\\\\):| /\\\\1|g' -e 's|^ ||'`\n      ;;\n    cygwin*)\n      # Convert to unix form, then to dos form, then back to unix form\n      # but this time dos style (no spaces!) so that the unix form looks\n      # like /cygdrive/c/PROGRA~1:/cygdr...\n      sys_lib_search_path_spec=`cygpath --path --unix \"$LIB\"`\n      sys_lib_search_path_spec=`cygpath --path --dos \"$sys_lib_search_path_spec\" 2>/dev/null`\n      sys_lib_search_path_spec=`cygpath --path --unix \"$sys_lib_search_path_spec\" | $SED -e \"s/$PATH_SEPARATOR/ /g\"`\n      ;;\n    *)\n      sys_lib_search_path_spec=$LIB\n      if $ECHO \"$sys_lib_search_path_spec\" | $GREP ';[c-zC-Z]:/' >/dev/null; then\n        # It is most probably a Windows format PATH.\n        sys_lib_search_path_spec=`$ECHO \"$sys_lib_search_path_spec\" | $SED -e 's/;/ /g'`\n      else\n        sys_lib_search_path_spec=`$ECHO \"$sys_lib_search_path_spec\" | $SED -e \"s/$PATH_SEPARATOR/ /g\"`\n      fi\n      # FIXME: find the short name or the path components, as spaces are\n      # common. (e.g. \"Program Files\" -> \"PROGRA~1\")\n      ;;\n    esac\n\n    # DLL is installed to $(libdir)/../bin by postinstall_cmds\n    postinstall_cmds='base_file=`basename \\$file`~\n      dlpath=`$SHELL 2>&1 -c '\\''. $dir/'\\''\\$base_file'\\''i; echo \\$dlname'\\''`~\n      dldir=$destdir/`dirname \\$dlpath`~\n      test -d \\$dldir || mkdir -p \\$dldir~\n      $install_prog $dir/$dlname \\$dldir/$dlname'\n    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\\''. $file; echo \\$dlname'\\''`~\n      dlpath=$dir/\\$dldll~\n       $RM \\$dlpath'\n    shlibpath_overrides_runpath=yes\n    dynamic_linker='Win32 link.exe'\n    ;;\n\n  *)\n    # Assume MSVC wrapper\n    library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'\n    dynamic_linker='Win32 ld.exe'\n    ;;\n  esac\n  # FIXME: first we should search . and the directory the executable is in\n  shlibpath_var=PATH\n  ;;\n\ndarwin* | rhapsody*)\n  dynamic_linker=\"$host_os dyld\"\n  version_type=darwin\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'\n  soname_spec='$libname$release$major$shared_ext'\n  shlibpath_overrides_runpath=yes\n  shlibpath_var=DYLD_LIBRARY_PATH\n  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'\n\n  sys_lib_search_path_spec=\"$sys_lib_search_path_spec /usr/local/lib\"\n  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'\n  ;;\n\ndgux*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  ;;\n\nfreebsd* | dragonfly*)\n  # DragonFly does not have aout.  When/if they implement a new\n  # versioning mechanism, adjust this.\n  if test -x /usr/bin/objformat; then\n    objformat=`/usr/bin/objformat`\n  else\n    case $host_os in\n    freebsd[23].*) objformat=aout ;;\n    *) objformat=elf ;;\n    esac\n  fi\n  version_type=freebsd-$objformat\n  case $version_type in\n    freebsd-elf*)\n      library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n      soname_spec='$libname$release$shared_ext$major'\n      need_version=no\n      need_lib_prefix=no\n      ;;\n    freebsd-*)\n      library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'\n      need_version=yes\n      ;;\n  esac\n  shlibpath_var=LD_LIBRARY_PATH\n  case $host_os in\n  freebsd2.*)\n    shlibpath_overrides_runpath=yes\n    ;;\n  freebsd3.[01]* | freebsdelf3.[01]*)\n    shlibpath_overrides_runpath=yes\n    hardcode_into_libs=yes\n    ;;\n  freebsd3.[2-9]* | freebsdelf3.[2-9]* | \\\n  freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)\n    shlibpath_overrides_runpath=no\n    hardcode_into_libs=yes\n    ;;\n  *) # from 4.6 on, and DragonFly\n    shlibpath_overrides_runpath=yes\n    hardcode_into_libs=yes\n    ;;\n  esac\n  ;;\n\nhaiku*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  dynamic_linker=\"$host_os runtime_loader\"\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'\n  hardcode_into_libs=yes\n  ;;\n\nhpux9* | hpux10* | hpux11*)\n  # Give a soname corresponding to the major version so that dld.sl refuses to\n  # link against other versions.\n  version_type=sunos\n  need_lib_prefix=no\n  need_version=no\n  case $host_cpu in\n  ia64*)\n    shrext_cmds='.so'\n    hardcode_into_libs=yes\n    dynamic_linker=\"$host_os dld.so\"\n    shlibpath_var=LD_LIBRARY_PATH\n    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n    soname_spec='$libname$release$shared_ext$major'\n    if test 32 = \"$HPUX_IA64_MODE\"; then\n      sys_lib_search_path_spec=\"/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib\"\n      sys_lib_dlsearch_path_spec=/usr/lib/hpux32\n    else\n      sys_lib_search_path_spec=\"/usr/lib/hpux64 /usr/local/lib/hpux64\"\n      sys_lib_dlsearch_path_spec=/usr/lib/hpux64\n    fi\n    ;;\n  hppa*64*)\n    shrext_cmds='.sl'\n    hardcode_into_libs=yes\n    dynamic_linker=\"$host_os dld.sl\"\n    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH\n    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n    soname_spec='$libname$release$shared_ext$major'\n    sys_lib_search_path_spec=\"/usr/lib/pa20_64 /usr/ccs/lib/pa20_64\"\n    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec\n    ;;\n  *)\n    shrext_cmds='.sl'\n    dynamic_linker=\"$host_os dld.sl\"\n    shlibpath_var=SHLIB_PATH\n    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n    soname_spec='$libname$release$shared_ext$major'\n    ;;\n  esac\n  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...\n  postinstall_cmds='chmod 555 $lib'\n  # or fails outright, so override atomically:\n  install_override_mode=555\n  ;;\n\ninterix[3-9]*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n  hardcode_into_libs=yes\n  ;;\n\nirix5* | irix6* | nonstopux*)\n  case $host_os in\n    nonstopux*) version_type=nonstopux ;;\n    *)\n\tif test yes = \"$lt_cv_prog_gnu_ld\"; then\n\t\tversion_type=linux # correct to gnu/linux during the next big refactor\n\telse\n\t\tversion_type=irix\n\tfi ;;\n  esac\n  need_lib_prefix=no\n  need_version=no\n  soname_spec='$libname$release$shared_ext$major'\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'\n  case $host_os in\n  irix5* | nonstopux*)\n    libsuff= shlibsuff=\n    ;;\n  *)\n    case $LD in # libtool.m4 will add one of these switches to LD\n    *-32|*\"-32 \"|*-melf32bsmip|*\"-melf32bsmip \")\n      libsuff= shlibsuff= libmagic=32-bit;;\n    *-n32|*\"-n32 \"|*-melf32bmipn32|*\"-melf32bmipn32 \")\n      libsuff=32 shlibsuff=N32 libmagic=N32;;\n    *-64|*\"-64 \"|*-melf64bmip|*\"-melf64bmip \")\n      libsuff=64 shlibsuff=64 libmagic=64-bit;;\n    *) libsuff= shlibsuff= libmagic=never-match;;\n    esac\n    ;;\n  esac\n  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH\n  shlibpath_overrides_runpath=no\n  sys_lib_search_path_spec=\"/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff\"\n  sys_lib_dlsearch_path_spec=\"/usr/lib$libsuff /lib$libsuff\"\n  hardcode_into_libs=yes\n  ;;\n\n# No shared lib support for Linux oldld, aout, or coff.\nlinux*oldld* | linux*aout* | linux*coff*)\n  dynamic_linker=no\n  ;;\n\nlinux*android*)\n  version_type=none # Android doesn't support versioned libraries.\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext'\n  soname_spec='$libname$release$shared_ext'\n  finish_cmds=\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n\n  # This implies no fast_install, which is unacceptable.\n  # Some rework will be needed to allow for fast_install\n  # before this can be enabled.\n  hardcode_into_libs=yes\n\n  dynamic_linker='Android linker'\n  # Don't embed -rpath directories since the linker doesn't support them.\n  hardcode_libdir_flag_spec='-L$libdir'\n  ;;\n\n# This must be glibc/ELF.\nlinux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  finish_cmds='PATH=\"\\$PATH:/sbin\" ldconfig -n $libdir'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n\n  # Some binutils ld are patched to set DT_RUNPATH\n  if ${lt_cv_shlibpath_overrides_runpath+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_shlibpath_overrides_runpath=no\n    save_LDFLAGS=$LDFLAGS\n    save_libdir=$libdir\n    eval \"libdir=/foo; wl=\\\"$lt_prog_compiler_wl\\\"; \\\n\t LDFLAGS=\\\"\\$LDFLAGS $hardcode_libdir_flag_spec\\\"\"\n    cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  if  ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep \"RUNPATH.*$libdir\" >/dev/null; then :\n  lt_cv_shlibpath_overrides_runpath=yes\nfi\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\n    LDFLAGS=$save_LDFLAGS\n    libdir=$save_libdir\n\nfi\n\n  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath\n\n  # This implies no fast_install, which is unacceptable.\n  # Some rework will be needed to allow for fast_install\n  # before this can be enabled.\n  hardcode_into_libs=yes\n\n  # Ideally, we could use ldconfig to report *all* directores which are\n  # searched for libraries, however this is still not possible.  Aside from not\n  # being certain /sbin/ldconfig is available, command\n  # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,\n  # even though it is searched at run-time.  Try to do the best guess by\n  # appending ld.so.conf contents (and includes) to the search path.\n  if test -f /etc/ld.so.conf; then\n    lt_ld_extra=`awk '/^include / { system(sprintf(\"cd /etc; cat %s 2>/dev/null\", \\$2)); skip = 1; } { if (!skip) print \\$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[\t ]*hwcap[\t ]/d;s/[:,\t]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/\"//g;/^$/d' | tr '\\n' ' '`\n    sys_lib_dlsearch_path_spec=\"/lib /usr/lib $lt_ld_extra\"\n  fi\n\n  # We used to test for /lib/ld.so.1 and disable shared libraries on\n  # powerpc, because MkLinux only supported shared libraries with the\n  # GNU dynamic linker.  Since this was broken with cross compilers,\n  # most powerpc-linux boxes support dynamic linking these days and\n  # people can always --disable-shared, the test was removed, and we\n  # assume the GNU/Linux dynamic linker is in use.\n  dynamic_linker='GNU/Linux ld.so'\n  ;;\n\nnetbsdelf*-gnu)\n  version_type=linux\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'\n  soname_spec='${libname}${release}${shared_ext}$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n  hardcode_into_libs=yes\n  dynamic_linker='NetBSD ld.elf_so'\n  ;;\n\nnetbsd*)\n  version_type=sunos\n  need_lib_prefix=no\n  need_version=no\n  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'\n    finish_cmds='PATH=\"\\$PATH:/sbin\" ldconfig -m $libdir'\n    dynamic_linker='NetBSD (a.out) ld.so'\n  else\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n    soname_spec='$libname$release$shared_ext$major'\n    dynamic_linker='NetBSD ld.elf_so'\n  fi\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  hardcode_into_libs=yes\n  ;;\n\nnewsos6)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  ;;\n\n*nto* | *qnx*)\n  version_type=qnx\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n  hardcode_into_libs=yes\n  dynamic_linker='ldqnx.so'\n  ;;\n\nopenbsd* | bitrig*)\n  version_type=sunos\n  sys_lib_dlsearch_path_spec=/usr/lib\n  need_lib_prefix=no\n  if test -z \"`echo __ELF__ | $CC -E - | $GREP __ELF__`\"; then\n    need_version=no\n  else\n    need_version=yes\n  fi\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'\n  finish_cmds='PATH=\"\\$PATH:/sbin\" ldconfig -m $libdir'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  ;;\n\nos2*)\n  libname_spec='$name'\n  version_type=windows\n  shrext_cmds=.dll\n  need_version=no\n  need_lib_prefix=no\n  # OS/2 can only load a DLL with a base name of 8 characters or less.\n  soname_spec='`test -n \"$os2dllname\" && libname=\"$os2dllname\";\n    v=$($ECHO $release$versuffix | tr -d .-);\n    n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);\n    $ECHO $n$v`$shared_ext'\n  library_names_spec='${libname}_dll.$libext'\n  dynamic_linker='OS/2 ld.exe'\n  shlibpath_var=BEGINLIBPATH\n  sys_lib_search_path_spec=\"/lib /usr/lib /usr/local/lib\"\n  sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec\n  postinstall_cmds='base_file=`basename \\$file`~\n    dlpath=`$SHELL 2>&1 -c '\\''. $dir/'\\''\\$base_file'\\''i; $ECHO \\$dlname'\\''`~\n    dldir=$destdir/`dirname \\$dlpath`~\n    test -d \\$dldir || mkdir -p \\$dldir~\n    $install_prog $dir/$dlname \\$dldir/$dlname~\n    chmod a+x \\$dldir/$dlname~\n    if test -n '\\''$stripme'\\'' && test -n '\\''$striplib'\\''; then\n      eval '\\''$striplib \\$dldir/$dlname'\\'' || exit \\$?;\n    fi'\n  postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\\''. $file; $ECHO \\$dlname'\\''`~\n    dlpath=$dir/\\$dldll~\n    $RM \\$dlpath'\n  ;;\n\nosf3* | osf4* | osf5*)\n  version_type=osf\n  need_lib_prefix=no\n  need_version=no\n  soname_spec='$libname$release$shared_ext$major'\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  shlibpath_var=LD_LIBRARY_PATH\n  sys_lib_search_path_spec=\"/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib\"\n  sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec\n  ;;\n\nrdos*)\n  dynamic_linker=no\n  ;;\n\nsolaris*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  hardcode_into_libs=yes\n  # ldd complains unless libraries are executable\n  postinstall_cmds='chmod +x $lib'\n  ;;\n\nsunos4*)\n  version_type=sunos\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'\n  finish_cmds='PATH=\"\\$PATH:/usr/etc\" ldconfig $libdir'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  if test yes = \"$with_gnu_ld\"; then\n    need_lib_prefix=no\n  fi\n  need_version=yes\n  ;;\n\nsysv4 | sysv4.3*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  case $host_vendor in\n    sni)\n      shlibpath_overrides_runpath=no\n      need_lib_prefix=no\n      runpath_var=LD_RUN_PATH\n      ;;\n    siemens)\n      need_lib_prefix=no\n      ;;\n    motorola)\n      need_lib_prefix=no\n      need_version=no\n      shlibpath_overrides_runpath=no\n      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'\n      ;;\n  esac\n  ;;\n\nsysv4*MP*)\n  if test -d /usr/nec; then\n    version_type=linux # correct to gnu/linux during the next big refactor\n    library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'\n    soname_spec='$libname$shared_ext.$major'\n    shlibpath_var=LD_LIBRARY_PATH\n  fi\n  ;;\n\nsysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)\n  version_type=sco\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  hardcode_into_libs=yes\n  if test yes = \"$with_gnu_ld\"; then\n    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'\n  else\n    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'\n    case $host_os in\n      sco3.2v5*)\n        sys_lib_search_path_spec=\"$sys_lib_search_path_spec /lib\"\n\t;;\n    esac\n  fi\n  sys_lib_dlsearch_path_spec='/usr/lib'\n  ;;\n\ntpf*)\n  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n  hardcode_into_libs=yes\n  ;;\n\nuts4*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  ;;\n\n*)\n  dynamic_linker=no\n  ;;\nesac\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $dynamic_linker\" >&5\n$as_echo \"$dynamic_linker\" >&6; }\ntest no = \"$dynamic_linker\" && can_build_shared=no\n\nvariables_saved_for_relink=\"PATH $shlibpath_var $runpath_var\"\nif test yes = \"$GCC\"; then\n  variables_saved_for_relink=\"$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH\"\nfi\n\nif test set = \"${lt_cv_sys_lib_search_path_spec+set}\"; then\n  sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec\nfi\n\nif test set = \"${lt_cv_sys_lib_dlsearch_path_spec+set}\"; then\n  sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec\nfi\n\n# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...\nconfigure_time_dlsearch_path=$sys_lib_dlsearch_path_spec\n\n# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code\nfunc_munge_path_list sys_lib_dlsearch_path_spec \"$LT_SYS_LIBRARY_PATH\"\n\n# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool\nconfigure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH\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\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\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\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs\" >&5\n$as_echo_n \"checking how to hardcode library paths into programs... \" >&6; }\nhardcode_action=\nif test -n \"$hardcode_libdir_flag_spec\" ||\n   test -n \"$runpath_var\" ||\n   test yes = \"$hardcode_automatic\"; then\n\n  # We can hardcode non-existent directories.\n  if test no != \"$hardcode_direct\" &&\n     # If the only mechanism to avoid hardcoding is shlibpath_var, we\n     # have to relink, otherwise we might link with an installed library\n     # when we should be linking with a yet-to-be-installed one\n     ## test no != \"$_LT_TAGVAR(hardcode_shlibpath_var, )\" &&\n     test no != \"$hardcode_minus_L\"; then\n    # Linking always hardcodes the temporary library directory.\n    hardcode_action=relink\n  else\n    # We can link without hardcoding, and we can hardcode nonexisting dirs.\n    hardcode_action=immediate\n  fi\nelse\n  # We cannot hardcode anything, or else we can only hardcode existing\n  # directories.\n  hardcode_action=unsupported\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $hardcode_action\" >&5\n$as_echo \"$hardcode_action\" >&6; }\n\nif test relink = \"$hardcode_action\" ||\n   test yes = \"$inherit_rpath\"; then\n  # Fast installation is not supported\n  enable_fast_install=no\nelif test yes = \"$shlibpath_overrides_runpath\" ||\n     test no = \"$enable_shared\"; then\n  # Fast installation is not necessary\n  enable_fast_install=needless\nfi\n\n\n\n\n\n\n  if test yes != \"$enable_dlopen\"; then\n  enable_dlopen=unknown\n  enable_dlopen_self=unknown\n  enable_dlopen_self_static=unknown\nelse\n  lt_cv_dlopen=no\n  lt_cv_dlopen_libs=\n\n  case $host_os in\n  beos*)\n    lt_cv_dlopen=load_add_on\n    lt_cv_dlopen_libs=\n    lt_cv_dlopen_self=yes\n    ;;\n\n  mingw* | pw32* | cegcc*)\n    lt_cv_dlopen=LoadLibrary\n    lt_cv_dlopen_libs=\n    ;;\n\n  cygwin*)\n    lt_cv_dlopen=dlopen\n    lt_cv_dlopen_libs=\n    ;;\n\n  darwin*)\n    # if libdl is installed we need to link against it\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl\" >&5\n$as_echo_n \"checking for dlopen in -ldl... \" >&6; }\nif ${ac_cv_lib_dl_dlopen+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-ldl  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar dlopen ();\nint\nmain ()\n{\nreturn dlopen ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_dl_dlopen=yes\nelse\n  ac_cv_lib_dl_dlopen=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen\" >&5\n$as_echo \"$ac_cv_lib_dl_dlopen\" >&6; }\nif test \"x$ac_cv_lib_dl_dlopen\" = xyes; then :\n  lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl\nelse\n\n    lt_cv_dlopen=dyld\n    lt_cv_dlopen_libs=\n    lt_cv_dlopen_self=yes\n\nfi\n\n    ;;\n\n  tpf*)\n    # Don't try to run any link tests for TPF.  We know it's impossible\n    # because TPF is a cross-compiler, and we know how we open DSOs.\n    lt_cv_dlopen=dlopen\n    lt_cv_dlopen_libs=\n    lt_cv_dlopen_self=no\n    ;;\n\n  *)\n    ac_fn_c_check_func \"$LINENO\" \"shl_load\" \"ac_cv_func_shl_load\"\nif test \"x$ac_cv_func_shl_load\" = xyes; then :\n  lt_cv_dlopen=shl_load\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld\" >&5\n$as_echo_n \"checking for shl_load in -ldld... \" >&6; }\nif ${ac_cv_lib_dld_shl_load+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-ldld  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar shl_load ();\nint\nmain ()\n{\nreturn shl_load ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_dld_shl_load=yes\nelse\n  ac_cv_lib_dld_shl_load=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load\" >&5\n$as_echo \"$ac_cv_lib_dld_shl_load\" >&6; }\nif test \"x$ac_cv_lib_dld_shl_load\" = xyes; then :\n  lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld\nelse\n  ac_fn_c_check_func \"$LINENO\" \"dlopen\" \"ac_cv_func_dlopen\"\nif test \"x$ac_cv_func_dlopen\" = xyes; then :\n  lt_cv_dlopen=dlopen\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl\" >&5\n$as_echo_n \"checking for dlopen in -ldl... \" >&6; }\nif ${ac_cv_lib_dl_dlopen+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-ldl  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar dlopen ();\nint\nmain ()\n{\nreturn dlopen ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_dl_dlopen=yes\nelse\n  ac_cv_lib_dl_dlopen=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen\" >&5\n$as_echo \"$ac_cv_lib_dl_dlopen\" >&6; }\nif test \"x$ac_cv_lib_dl_dlopen\" = xyes; then :\n  lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld\" >&5\n$as_echo_n \"checking for dlopen in -lsvld... \" >&6; }\nif ${ac_cv_lib_svld_dlopen+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-lsvld  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar dlopen ();\nint\nmain ()\n{\nreturn dlopen ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_svld_dlopen=yes\nelse\n  ac_cv_lib_svld_dlopen=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen\" >&5\n$as_echo \"$ac_cv_lib_svld_dlopen\" >&6; }\nif test \"x$ac_cv_lib_svld_dlopen\" = xyes; then :\n  lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld\" >&5\n$as_echo_n \"checking for dld_link in -ldld... \" >&6; }\nif ${ac_cv_lib_dld_dld_link+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-ldld  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar dld_link ();\nint\nmain ()\n{\nreturn dld_link ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_dld_dld_link=yes\nelse\n  ac_cv_lib_dld_dld_link=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link\" >&5\n$as_echo \"$ac_cv_lib_dld_dld_link\" >&6; }\nif test \"x$ac_cv_lib_dld_dld_link\" = xyes; then :\n  lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld\nfi\n\n\nfi\n\n\nfi\n\n\nfi\n\n\nfi\n\n\nfi\n\n    ;;\n  esac\n\n  if test no = \"$lt_cv_dlopen\"; then\n    enable_dlopen=no\n  else\n    enable_dlopen=yes\n  fi\n\n  case $lt_cv_dlopen in\n  dlopen)\n    save_CPPFLAGS=$CPPFLAGS\n    test yes = \"$ac_cv_header_dlfcn_h\" && CPPFLAGS=\"$CPPFLAGS -DHAVE_DLFCN_H\"\n\n    save_LDFLAGS=$LDFLAGS\n    wl=$lt_prog_compiler_wl eval LDFLAGS=\\\"\\$LDFLAGS $export_dynamic_flag_spec\\\"\n\n    save_LIBS=$LIBS\n    LIBS=\"$lt_cv_dlopen_libs $LIBS\"\n\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself\" >&5\n$as_echo_n \"checking whether a program can dlopen itself... \" >&6; }\nif ${lt_cv_dlopen_self+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  \t  if test yes = \"$cross_compiling\"; then :\n  lt_cv_dlopen_self=cross\nelse\n  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2\n  lt_status=$lt_dlunknown\n  cat > conftest.$ac_ext <<_LT_EOF\n#line $LINENO \"configure\"\n#include \"confdefs.h\"\n\n#if HAVE_DLFCN_H\n#include <dlfcn.h>\n#endif\n\n#include <stdio.h>\n\n#ifdef RTLD_GLOBAL\n#  define LT_DLGLOBAL\t\tRTLD_GLOBAL\n#else\n#  ifdef DL_GLOBAL\n#    define LT_DLGLOBAL\t\tDL_GLOBAL\n#  else\n#    define LT_DLGLOBAL\t\t0\n#  endif\n#endif\n\n/* We may have to define LT_DLLAZY_OR_NOW in the command line if we\n   find out it does not work in some platform. */\n#ifndef LT_DLLAZY_OR_NOW\n#  ifdef RTLD_LAZY\n#    define LT_DLLAZY_OR_NOW\t\tRTLD_LAZY\n#  else\n#    ifdef DL_LAZY\n#      define LT_DLLAZY_OR_NOW\t\tDL_LAZY\n#    else\n#      ifdef RTLD_NOW\n#        define LT_DLLAZY_OR_NOW\tRTLD_NOW\n#      else\n#        ifdef DL_NOW\n#          define LT_DLLAZY_OR_NOW\tDL_NOW\n#        else\n#          define LT_DLLAZY_OR_NOW\t0\n#        endif\n#      endif\n#    endif\n#  endif\n#endif\n\n/* When -fvisibility=hidden is used, assume the code has been annotated\n   correspondingly for the symbols needed.  */\n#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))\nint fnord () __attribute__((visibility(\"default\")));\n#endif\n\nint fnord () { return 42; }\nint main ()\n{\n  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);\n  int status = $lt_dlunknown;\n\n  if (self)\n    {\n      if (dlsym (self,\"fnord\"))       status = $lt_dlno_uscore;\n      else\n        {\n\t  if (dlsym( self,\"_fnord\"))  status = $lt_dlneed_uscore;\n          else puts (dlerror ());\n\t}\n      /* dlclose (self); */\n    }\n  else\n    puts (dlerror ());\n\n  return status;\n}\n_LT_EOF\n  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_link\\\"\"; } >&5\n  (eval $ac_link) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && test -s \"conftest$ac_exeext\" 2>/dev/null; then\n    (./conftest; exit; ) >&5 2>/dev/null\n    lt_status=$?\n    case x$lt_status in\n      x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;\n      x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;\n      x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;\n    esac\n  else :\n    # compilation failed\n    lt_cv_dlopen_self=no\n  fi\nfi\nrm -fr conftest*\n\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self\" >&5\n$as_echo \"$lt_cv_dlopen_self\" >&6; }\n\n    if test yes = \"$lt_cv_dlopen_self\"; then\n      wl=$lt_prog_compiler_wl eval LDFLAGS=\\\"\\$LDFLAGS $lt_prog_compiler_static\\\"\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself\" >&5\n$as_echo_n \"checking whether a statically linked program can dlopen itself... \" >&6; }\nif ${lt_cv_dlopen_self_static+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  \t  if test yes = \"$cross_compiling\"; then :\n  lt_cv_dlopen_self_static=cross\nelse\n  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2\n  lt_status=$lt_dlunknown\n  cat > conftest.$ac_ext <<_LT_EOF\n#line $LINENO \"configure\"\n#include \"confdefs.h\"\n\n#if HAVE_DLFCN_H\n#include <dlfcn.h>\n#endif\n\n#include <stdio.h>\n\n#ifdef RTLD_GLOBAL\n#  define LT_DLGLOBAL\t\tRTLD_GLOBAL\n#else\n#  ifdef DL_GLOBAL\n#    define LT_DLGLOBAL\t\tDL_GLOBAL\n#  else\n#    define LT_DLGLOBAL\t\t0\n#  endif\n#endif\n\n/* We may have to define LT_DLLAZY_OR_NOW in the command line if we\n   find out it does not work in some platform. */\n#ifndef LT_DLLAZY_OR_NOW\n#  ifdef RTLD_LAZY\n#    define LT_DLLAZY_OR_NOW\t\tRTLD_LAZY\n#  else\n#    ifdef DL_LAZY\n#      define LT_DLLAZY_OR_NOW\t\tDL_LAZY\n#    else\n#      ifdef RTLD_NOW\n#        define LT_DLLAZY_OR_NOW\tRTLD_NOW\n#      else\n#        ifdef DL_NOW\n#          define LT_DLLAZY_OR_NOW\tDL_NOW\n#        else\n#          define LT_DLLAZY_OR_NOW\t0\n#        endif\n#      endif\n#    endif\n#  endif\n#endif\n\n/* When -fvisibility=hidden is used, assume the code has been annotated\n   correspondingly for the symbols needed.  */\n#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))\nint fnord () __attribute__((visibility(\"default\")));\n#endif\n\nint fnord () { return 42; }\nint main ()\n{\n  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);\n  int status = $lt_dlunknown;\n\n  if (self)\n    {\n      if (dlsym (self,\"fnord\"))       status = $lt_dlno_uscore;\n      else\n        {\n\t  if (dlsym( self,\"_fnord\"))  status = $lt_dlneed_uscore;\n          else puts (dlerror ());\n\t}\n      /* dlclose (self); */\n    }\n  else\n    puts (dlerror ());\n\n  return status;\n}\n_LT_EOF\n  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_link\\\"\"; } >&5\n  (eval $ac_link) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && test -s \"conftest$ac_exeext\" 2>/dev/null; then\n    (./conftest; exit; ) >&5 2>/dev/null\n    lt_status=$?\n    case x$lt_status in\n      x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;\n      x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;\n      x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;\n    esac\n  else :\n    # compilation failed\n    lt_cv_dlopen_self_static=no\n  fi\nfi\nrm -fr conftest*\n\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static\" >&5\n$as_echo \"$lt_cv_dlopen_self_static\" >&6; }\n    fi\n\n    CPPFLAGS=$save_CPPFLAGS\n    LDFLAGS=$save_LDFLAGS\n    LIBS=$save_LIBS\n    ;;\n  esac\n\n  case $lt_cv_dlopen_self in\n  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;\n  *) enable_dlopen_self=unknown ;;\n  esac\n\n  case $lt_cv_dlopen_self_static in\n  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;\n  *) enable_dlopen_self_static=unknown ;;\n  esac\nfi\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nstriplib=\nold_striplib=\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible\" >&5\n$as_echo_n \"checking whether stripping libraries is possible... \" >&6; }\nif test -n \"$STRIP\" && $STRIP -V 2>&1 | $GREP \"GNU strip\" >/dev/null; then\n  test -z \"$old_striplib\" && old_striplib=\"$STRIP --strip-debug\"\n  test -z \"$striplib\" && striplib=\"$STRIP --strip-unneeded\"\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n# FIXME - insert some real tests, host_os isn't really good enough\n  case $host_os in\n  darwin*)\n    if test -n \"$STRIP\"; then\n      striplib=\"$STRIP -x\"\n      old_striplib=\"$STRIP -S\"\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\n    else\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n    fi\n    ;;\n  *)\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n    ;;\n  esac\nfi\n\n\n\n\n\n\n\n\n\n\n\n\n  # Report what library types will actually be built\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries\" >&5\n$as_echo_n \"checking if libtool supports shared libraries... \" >&6; }\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $can_build_shared\" >&5\n$as_echo \"$can_build_shared\" >&6; }\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries\" >&5\n$as_echo_n \"checking whether to build shared libraries... \" >&6; }\n  test no = \"$can_build_shared\" && enable_shared=no\n\n  # On AIX, shared libraries and static libraries use the same namespace, and\n  # are all built from PIC.\n  case $host_os in\n  aix3*)\n    test yes = \"$enable_shared\" && enable_static=no\n    if test -n \"$RANLIB\"; then\n      archive_cmds=\"$archive_cmds~\\$RANLIB \\$lib\"\n      postinstall_cmds='$RANLIB $lib'\n    fi\n    ;;\n\n  aix[4-9]*)\n    if test ia64 != \"$host_cpu\"; then\n      case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in\n      yes,aix,yes) ;;\t\t\t# shared object as lib.so file only\n      yes,svr4,*) ;;\t\t\t# shared object as lib.so archive member only\n      yes,*) enable_static=no ;;\t# shared object in lib.a archive as well\n      esac\n    fi\n    ;;\n  esac\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $enable_shared\" >&5\n$as_echo \"$enable_shared\" >&6; }\n\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether to build static libraries\" >&5\n$as_echo_n \"checking whether to build static libraries... \" >&6; }\n  # Make sure either enable_shared or enable_static is yes.\n  test yes = \"$enable_shared\" || enable_static=yes\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $enable_static\" >&5\n$as_echo \"$enable_static\" >&6; }\n\n\n\n\nfi\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\nCC=$lt_save_CC\n\n      if test -n \"$CXX\" && ( test no != \"$CXX\" &&\n    ( (test g++ = \"$CXX\" && `g++ -v >/dev/null 2>&1` ) ||\n    (test g++ != \"$CXX\"))); then\n  ac_ext=cpp\nac_cpp='$CXXCPP $CPPFLAGS'\nac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_cxx_compiler_gnu\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor\" >&5\n$as_echo_n \"checking how to run the C++ preprocessor... \" >&6; }\nif test -z \"$CXXCPP\"; then\n  if ${ac_cv_prog_CXXCPP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n      # Double quotes because CXXCPP needs to be expanded\n    for CXXCPP in \"$CXX -E\" \"/lib/cpp\"\n    do\n      ac_preproc_ok=false\nfor ac_cxx_preproc_warn_flag in '' yes\ndo\n  # Use a header file that comes with gcc, so configuring glibc\n  # with a fresh cross-compiler works.\n  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since\n  # <limits.h> exists even on freestanding compilers.\n  # On the NeXT, cc -E runs the code through the compiler's parser,\n  # not just through cpp. \"Syntax error\" is here to catch this case.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#ifdef __STDC__\n# include <limits.h>\n#else\n# include <assert.h>\n#endif\n\t\t     Syntax error\n_ACEOF\nif ac_fn_cxx_try_cpp \"$LINENO\"; then :\n\nelse\n  # Broken: fails on valid input.\ncontinue\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\n  # OK, works on sane cases.  Now check whether nonexistent headers\n  # can be detected and how.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ac_nonexistent.h>\n_ACEOF\nif ac_fn_cxx_try_cpp \"$LINENO\"; then :\n  # Broken: success on invalid input.\ncontinue\nelse\n  # Passes both tests.\nac_preproc_ok=:\nbreak\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\ndone\n# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.\nrm -f conftest.i conftest.err conftest.$ac_ext\nif $ac_preproc_ok; then :\n  break\nfi\n\n    done\n    ac_cv_prog_CXXCPP=$CXXCPP\n\nfi\n  CXXCPP=$ac_cv_prog_CXXCPP\nelse\n  ac_cv_prog_CXXCPP=$CXXCPP\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CXXCPP\" >&5\n$as_echo \"$CXXCPP\" >&6; }\nac_preproc_ok=false\nfor ac_cxx_preproc_warn_flag in '' yes\ndo\n  # Use a header file that comes with gcc, so configuring glibc\n  # with a fresh cross-compiler works.\n  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since\n  # <limits.h> exists even on freestanding compilers.\n  # On the NeXT, cc -E runs the code through the compiler's parser,\n  # not just through cpp. \"Syntax error\" is here to catch this case.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#ifdef __STDC__\n# include <limits.h>\n#else\n# include <assert.h>\n#endif\n\t\t     Syntax error\n_ACEOF\nif ac_fn_cxx_try_cpp \"$LINENO\"; then :\n\nelse\n  # Broken: fails on valid input.\ncontinue\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\n  # OK, works on sane cases.  Now check whether nonexistent headers\n  # can be detected and how.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ac_nonexistent.h>\n_ACEOF\nif ac_fn_cxx_try_cpp \"$LINENO\"; then :\n  # Broken: success on invalid input.\ncontinue\nelse\n  # Passes both tests.\nac_preproc_ok=:\nbreak\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\ndone\n# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.\nrm -f conftest.i conftest.err conftest.$ac_ext\nif $ac_preproc_ok; then :\n\nelse\n  { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"C++ preprocessor \\\"$CXXCPP\\\" fails sanity check\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nfi\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\nelse\n  _lt_caught_CXX_error=yes\nfi\n\nac_ext=cpp\nac_cpp='$CXXCPP $CPPFLAGS'\nac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_cxx_compiler_gnu\n\narchive_cmds_need_lc_CXX=no\nallow_undefined_flag_CXX=\nalways_export_symbols_CXX=no\narchive_expsym_cmds_CXX=\ncompiler_needs_object_CXX=no\nexport_dynamic_flag_spec_CXX=\nhardcode_direct_CXX=no\nhardcode_direct_absolute_CXX=no\nhardcode_libdir_flag_spec_CXX=\nhardcode_libdir_separator_CXX=\nhardcode_minus_L_CXX=no\nhardcode_shlibpath_var_CXX=unsupported\nhardcode_automatic_CXX=no\ninherit_rpath_CXX=no\nmodule_cmds_CXX=\nmodule_expsym_cmds_CXX=\nlink_all_deplibs_CXX=unknown\nold_archive_cmds_CXX=$old_archive_cmds\nreload_flag_CXX=$reload_flag\nreload_cmds_CXX=$reload_cmds\nno_undefined_flag_CXX=\nwhole_archive_flag_spec_CXX=\nenable_shared_with_static_runtimes_CXX=no\n\n# Source file extension for C++ test sources.\nac_ext=cpp\n\n# Object file extension for compiled C++ test sources.\nobjext=o\nobjext_CXX=$objext\n\n# No sense in running all these tests if we already determined that\n# the CXX compiler isn't working.  Some variables (like enable_shared)\n# are currently assumed to apply to all compilers on this platform,\n# and will be corrupted by setting them based on a non-working compiler.\nif test yes != \"$_lt_caught_CXX_error\"; then\n  # Code to be used in simple compile tests\n  lt_simple_compile_test_code=\"int some_variable = 0;\"\n\n  # Code to be used in simple link tests\n  lt_simple_link_test_code='int main(int, char *[]) { return(0); }'\n\n  # ltmain only uses $CC for tagged configurations so make sure $CC is set.\n\n\n\n\n\n\n# If no C compiler was specified, use CC.\nLTCC=${LTCC-\"$CC\"}\n\n# If no C compiler flags were specified, use CFLAGS.\nLTCFLAGS=${LTCFLAGS-\"$CFLAGS\"}\n\n# Allow CC to be a program name with arguments.\ncompiler=$CC\n\n\n  # save warnings/boilerplate of simple test code\n  ac_outfile=conftest.$ac_objext\necho \"$lt_simple_compile_test_code\" >conftest.$ac_ext\neval \"$ac_compile\" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err\n_lt_compiler_boilerplate=`cat conftest.err`\n$RM conftest*\n\n  ac_outfile=conftest.$ac_objext\necho \"$lt_simple_link_test_code\" >conftest.$ac_ext\neval \"$ac_link\" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err\n_lt_linker_boilerplate=`cat conftest.err`\n$RM -r conftest*\n\n\n  # Allow CC to be a program name with arguments.\n  lt_save_CC=$CC\n  lt_save_CFLAGS=$CFLAGS\n  lt_save_LD=$LD\n  lt_save_GCC=$GCC\n  GCC=$GXX\n  lt_save_with_gnu_ld=$with_gnu_ld\n  lt_save_path_LD=$lt_cv_path_LD\n  if test -n \"${lt_cv_prog_gnu_ldcxx+set}\"; then\n    lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx\n  else\n    $as_unset lt_cv_prog_gnu_ld\n  fi\n  if test -n \"${lt_cv_path_LDCXX+set}\"; then\n    lt_cv_path_LD=$lt_cv_path_LDCXX\n  else\n    $as_unset lt_cv_path_LD\n  fi\n  test -z \"${LDCXX+set}\" || LD=$LDCXX\n  CC=${CXX-\"c++\"}\n  CFLAGS=$CXXFLAGS\n  compiler=$CC\n  compiler_CXX=$CC\n  func_cc_basename $compiler\ncc_basename=$func_cc_basename_result\n\n\n  if test -n \"$compiler\"; then\n    # We don't want -fno-exception when compiling C++ code, so set the\n    # no_builtin_flag separately\n    if test yes = \"$GXX\"; then\n      lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin'\n    else\n      lt_prog_compiler_no_builtin_flag_CXX=\n    fi\n\n    if test yes = \"$GXX\"; then\n      # Set up default GNU C++ configuration\n\n\n\n# Check whether --with-gnu-ld was given.\nif test \"${with_gnu_ld+set}\" = set; then :\n  withval=$with_gnu_ld; test no = \"$withval\" || with_gnu_ld=yes\nelse\n  with_gnu_ld=no\nfi\n\nac_prog=ld\nif test yes = \"$GCC\"; then\n  # Check if gcc -print-prog-name=ld gives a path.\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for ld used by $CC\" >&5\n$as_echo_n \"checking for ld used by $CC... \" >&6; }\n  case $host in\n  *-*-mingw*)\n    # gcc leaves a trailing carriage return, which upsets mingw\n    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\\015'` ;;\n  *)\n    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;\n  esac\n  case $ac_prog in\n    # Accept absolute paths.\n    [\\\\/]* | ?:[\\\\/]*)\n      re_direlt='/[^/][^/]*/\\.\\./'\n      # Canonicalize the pathname of ld\n      ac_prog=`$ECHO \"$ac_prog\"| $SED 's%\\\\\\\\%/%g'`\n      while $ECHO \"$ac_prog\" | $GREP \"$re_direlt\" > /dev/null 2>&1; do\n\tac_prog=`$ECHO $ac_prog| $SED \"s%$re_direlt%/%\"`\n      done\n      test -z \"$LD\" && LD=$ac_prog\n      ;;\n  \"\")\n    # If it fails, then pretend we aren't using GCC.\n    ac_prog=ld\n    ;;\n  *)\n    # If it is relative, then search for the first ld in PATH.\n    with_gnu_ld=unknown\n    ;;\n  esac\nelif test yes = \"$with_gnu_ld\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for GNU ld\" >&5\n$as_echo_n \"checking for GNU ld... \" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for non-GNU ld\" >&5\n$as_echo_n \"checking for non-GNU ld... \" >&6; }\nfi\nif ${lt_cv_path_LD+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -z \"$LD\"; then\n  lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR\n  for ac_dir in $PATH; do\n    IFS=$lt_save_ifs\n    test -z \"$ac_dir\" && ac_dir=.\n    if test -f \"$ac_dir/$ac_prog\" || test -f \"$ac_dir/$ac_prog$ac_exeext\"; then\n      lt_cv_path_LD=$ac_dir/$ac_prog\n      # Check to see if the program is GNU ld.  I'd rather use --version,\n      # but apparently some variants of GNU ld only accept -v.\n      # Break only if it was the GNU/non-GNU ld that we prefer.\n      case `\"$lt_cv_path_LD\" -v 2>&1 </dev/null` in\n      *GNU* | *'with BFD'*)\n\ttest no != \"$with_gnu_ld\" && break\n\t;;\n      *)\n\ttest yes != \"$with_gnu_ld\" && break\n\t;;\n      esac\n    fi\n  done\n  IFS=$lt_save_ifs\nelse\n  lt_cv_path_LD=$LD # Let the user override the test with a path.\nfi\nfi\n\nLD=$lt_cv_path_LD\nif test -n \"$LD\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $LD\" >&5\n$as_echo \"$LD\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\ntest -z \"$LD\" && as_fn_error $? \"no acceptable ld found in \\$PATH\" \"$LINENO\" 5\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld\" >&5\n$as_echo_n \"checking if the linker ($LD) is GNU ld... \" >&6; }\nif ${lt_cv_prog_gnu_ld+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  # I'd rather use --version here, but apparently some GNU lds only accept -v.\ncase `$LD -v 2>&1 </dev/null` in\n*GNU* | *'with BFD'*)\n  lt_cv_prog_gnu_ld=yes\n  ;;\n*)\n  lt_cv_prog_gnu_ld=no\n  ;;\nesac\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld\" >&5\n$as_echo \"$lt_cv_prog_gnu_ld\" >&6; }\nwith_gnu_ld=$lt_cv_prog_gnu_ld\n\n\n\n\n\n\n\n      # Check if GNU C++ uses GNU ld as the underlying linker, since the\n      # archiving commands below assume that GNU ld is being used.\n      if test yes = \"$with_gnu_ld\"; then\n        archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'\n        archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'\n\n        hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'\n        export_dynamic_flag_spec_CXX='$wl--export-dynamic'\n\n        # If archive_cmds runs LD, not CC, wlarc should be empty\n        # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to\n        #     investigate it a little bit more. (MM)\n        wlarc='$wl'\n\n        # ancient GNU ld didn't support --whole-archive et. al.\n        if eval \"`$CC -print-prog-name=ld` --help 2>&1\" |\n\t  $GREP 'no-whole-archive' > /dev/null; then\n          whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'\n        else\n          whole_archive_flag_spec_CXX=\n        fi\n      else\n        with_gnu_ld=no\n        wlarc=\n\n        # A generic and very simple default shared library creation\n        # command for GNU C++ for the case where it uses the native\n        # linker, instead of GNU ld.  If possible, this setting should\n        # overridden to take advantage of the native linker features on\n        # the platform it is being used on.\n        archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'\n      fi\n\n      # Commands to make compiler produce verbose output that lists\n      # what \"hidden\" libraries, object files and flags are used when\n      # linking a shared library.\n      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v \"^Configured with:\" | $GREP \"\\-L\"'\n\n    else\n      GXX=no\n      with_gnu_ld=no\n      wlarc=\n    fi\n\n    # PORTME: fill in a description of your system's C++ link characteristics\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries\" >&5\n$as_echo_n \"checking whether the $compiler linker ($LD) supports shared libraries... \" >&6; }\n    ld_shlibs_CXX=yes\n    case $host_os in\n      aix3*)\n        # FIXME: insert proper C++ library support\n        ld_shlibs_CXX=no\n        ;;\n      aix[4-9]*)\n        if test ia64 = \"$host_cpu\"; then\n          # On IA64, the linker does run time linking by default, so we don't\n          # have to do anything special.\n          aix_use_runtimelinking=no\n          exp_sym_flag='-Bexport'\n          no_entry_flag=\n        else\n          aix_use_runtimelinking=no\n\n          # Test if we are trying to use run time linking or normal\n          # AIX style linking. If -brtl is somewhere in LDFLAGS, we\n          # have runtime linking enabled, and use it for executables.\n          # For shared libraries, we enable/disable runtime linking\n          # depending on the kind of the shared library created -\n          # when \"with_aix_soname,aix_use_runtimelinking\" is:\n          # \"aix,no\"   lib.a(lib.so.V) shared, rtl:no,  for executables\n          # \"aix,yes\"  lib.so          shared, rtl:yes, for executables\n          #            lib.a           static archive\n          # \"both,no\"  lib.so.V(shr.o) shared, rtl:yes\n          #            lib.a(lib.so.V) shared, rtl:no,  for executables\n          # \"both,yes\" lib.so.V(shr.o) shared, rtl:yes, for executables\n          #            lib.a(lib.so.V) shared, rtl:no\n          # \"svr4,*\"   lib.so.V(shr.o) shared, rtl:yes, for executables\n          #            lib.a           static archive\n          case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)\n\t    for ld_flag in $LDFLAGS; do\n\t      case $ld_flag in\n\t      *-brtl*)\n\t        aix_use_runtimelinking=yes\n\t        break\n\t        ;;\n\t      esac\n\t    done\n\t    if test svr4,no = \"$with_aix_soname,$aix_use_runtimelinking\"; then\n\t      # With aix-soname=svr4, we create the lib.so.V shared archives only,\n\t      # so we don't have lib.a shared libs to link our executables.\n\t      # We have to force runtime linking in this case.\n\t      aix_use_runtimelinking=yes\n\t      LDFLAGS=\"$LDFLAGS -Wl,-brtl\"\n\t    fi\n\t    ;;\n          esac\n\n          exp_sym_flag='-bexport'\n          no_entry_flag='-bnoentry'\n        fi\n\n        # When large executables or shared objects are built, AIX ld can\n        # have problems creating the table of contents.  If linking a library\n        # or program results in \"error TOC overflow\" add -mminimal-toc to\n        # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not\n        # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.\n\n        archive_cmds_CXX=''\n        hardcode_direct_CXX=yes\n        hardcode_direct_absolute_CXX=yes\n        hardcode_libdir_separator_CXX=':'\n        link_all_deplibs_CXX=yes\n        file_list_spec_CXX='$wl-f,'\n        case $with_aix_soname,$aix_use_runtimelinking in\n        aix,*) ;;\t# no import file\n        svr4,* | *,yes) # use import file\n          # The Import File defines what to hardcode.\n          hardcode_direct_CXX=no\n          hardcode_direct_absolute_CXX=no\n          ;;\n        esac\n\n        if test yes = \"$GXX\"; then\n          case $host_os in aix4.[012]|aix4.[012].*)\n          # We only want to do this on AIX 4.2 and lower, the check\n          # below for broken collect2 doesn't work under 4.3+\n\t  collect2name=`$CC -print-prog-name=collect2`\n\t  if test -f \"$collect2name\" &&\n\t     strings \"$collect2name\" | $GREP resolve_lib_name >/dev/null\n\t  then\n\t    # We have reworked collect2\n\t    :\n\t  else\n\t    # We have old collect2\n\t    hardcode_direct_CXX=unsupported\n\t    # It fails to find uninstalled libraries when the uninstalled\n\t    # path is not listed in the libpath.  Setting hardcode_minus_L\n\t    # to unsupported forces relinking\n\t    hardcode_minus_L_CXX=yes\n\t    hardcode_libdir_flag_spec_CXX='-L$libdir'\n\t    hardcode_libdir_separator_CXX=\n\t  fi\n          esac\n          shared_flag='-shared'\n\t  if test yes = \"$aix_use_runtimelinking\"; then\n\t    shared_flag=$shared_flag' $wl-G'\n\t  fi\n\t  # Need to ensure runtime linking is disabled for the traditional\n\t  # shared library, or the linker may eventually find shared libraries\n\t  # /with/ Import File - we do not want to mix them.\n\t  shared_flag_aix='-shared'\n\t  shared_flag_svr4='-shared $wl-G'\n        else\n          # not using gcc\n          if test ia64 = \"$host_cpu\"; then\n\t  # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release\n\t  # chokes on -Wl,-G. The following line is correct:\n\t  shared_flag='-G'\n          else\n\t    if test yes = \"$aix_use_runtimelinking\"; then\n\t      shared_flag='$wl-G'\n\t    else\n\t      shared_flag='$wl-bM:SRE'\n\t    fi\n\t    shared_flag_aix='$wl-bM:SRE'\n\t    shared_flag_svr4='$wl-G'\n          fi\n        fi\n\n        export_dynamic_flag_spec_CXX='$wl-bexpall'\n        # It seems that -bexpall does not export symbols beginning with\n        # underscore (_), so it is better to generate a list of symbols to\n\t# export.\n        always_export_symbols_CXX=yes\n\tif test aix,yes = \"$with_aix_soname,$aix_use_runtimelinking\"; then\n          # Warning - without using the other runtime loading flags (-brtl),\n          # -berok will link without error, but may produce a broken library.\n          # The \"-G\" linker flag allows undefined symbols.\n          no_undefined_flag_CXX='-bernotok'\n          # Determine the default libpath from the value encoded in an empty\n          # executable.\n          if test set = \"${lt_cv_aix_libpath+set}\"; then\n  aix_libpath=$lt_cv_aix_libpath\nelse\n  if ${lt_cv_aix_libpath__CXX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_cxx_try_link \"$LINENO\"; then :\n\n  lt_aix_libpath_sed='\n      /Import File Strings/,/^$/ {\n\t  /^0/ {\n\t      s/^0  *\\([^ ]*\\) *$/\\1/\n\t      p\n\t  }\n      }'\n  lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e \"$lt_aix_libpath_sed\"`\n  # Check for a 64-bit object if we didn't find anything.\n  if test -z \"$lt_cv_aix_libpath__CXX\"; then\n    lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e \"$lt_aix_libpath_sed\"`\n  fi\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\n  if test -z \"$lt_cv_aix_libpath__CXX\"; then\n    lt_cv_aix_libpath__CXX=/usr/lib:/lib\n  fi\n\nfi\n\n  aix_libpath=$lt_cv_aix_libpath__CXX\nfi\n\n          hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'\"$aix_libpath\"\n\n          archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n \"$allow_undefined_flag\"; then func_echo_all \"$wl$allow_undefined_flag\"; else :; fi` $wl'$exp_sym_flag:\\$export_symbols' '$shared_flag\n        else\n          if test ia64 = \"$host_cpu\"; then\n\t    hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib'\n\t    allow_undefined_flag_CXX=\"-z nodefs\"\n\t    archive_expsym_cmds_CXX=\"\\$CC $shared_flag\"' -o $output_objdir/$soname $libobjs $deplibs '\"\\$wl$no_entry_flag\"' $compiler_flags $wl$allow_undefined_flag '\"\\$wl$exp_sym_flag:\\$export_symbols\"\n          else\n\t    # Determine the default libpath from the value encoded in an\n\t    # empty executable.\n\t    if test set = \"${lt_cv_aix_libpath+set}\"; then\n  aix_libpath=$lt_cv_aix_libpath\nelse\n  if ${lt_cv_aix_libpath__CXX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_cxx_try_link \"$LINENO\"; then :\n\n  lt_aix_libpath_sed='\n      /Import File Strings/,/^$/ {\n\t  /^0/ {\n\t      s/^0  *\\([^ ]*\\) *$/\\1/\n\t      p\n\t  }\n      }'\n  lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e \"$lt_aix_libpath_sed\"`\n  # Check for a 64-bit object if we didn't find anything.\n  if test -z \"$lt_cv_aix_libpath__CXX\"; then\n    lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e \"$lt_aix_libpath_sed\"`\n  fi\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\n  if test -z \"$lt_cv_aix_libpath__CXX\"; then\n    lt_cv_aix_libpath__CXX=/usr/lib:/lib\n  fi\n\nfi\n\n  aix_libpath=$lt_cv_aix_libpath__CXX\nfi\n\n\t    hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'\"$aix_libpath\"\n\t    # Warning - without using the other run time loading flags,\n\t    # -berok will link without error, but may produce a broken library.\n\t    no_undefined_flag_CXX=' $wl-bernotok'\n\t    allow_undefined_flag_CXX=' $wl-berok'\n\t    if test yes = \"$with_gnu_ld\"; then\n\t      # We only use this code for GNU lds that support --whole-archive.\n\t      whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive'\n\t    else\n\t      # Exported symbols can be pulled into shared objects from archives\n\t      whole_archive_flag_spec_CXX='$convenience'\n\t    fi\n\t    archive_cmds_need_lc_CXX=yes\n\t    archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'\n\t    # -brtl affects multiple linker settings, -berok does not and is overridden later\n\t    compiler_flags_filtered='`func_echo_all \"$compiler_flags \" | $SED -e \"s%-brtl\\\\([, ]\\\\)%-berok\\\\1%g\"`'\n\t    if test svr4 != \"$with_aix_soname\"; then\n\t      # This is similar to how AIX traditionally builds its shared\n\t      # libraries. Need -bnortl late, we may have -brtl in LDFLAGS.\n\t      archive_expsym_cmds_CXX=\"$archive_expsym_cmds_CXX\"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'\n\t    fi\n\t    if test aix != \"$with_aix_soname\"; then\n\t      archive_expsym_cmds_CXX=\"$archive_expsym_cmds_CXX\"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all \"#! $soname($shared_archive_member_spec.o)\"; if test shr_64 = \"$shared_archive_member_spec\"; then func_echo_all \"# 64\"; else func_echo_all \"# 32\"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'\n\t    else\n\t      # used by -dlpreopen to get the symbols\n\t      archive_expsym_cmds_CXX=\"$archive_expsym_cmds_CXX\"'~$MV  $output_objdir/$realname.d/$soname $output_objdir'\n\t    fi\n\t    archive_expsym_cmds_CXX=\"$archive_expsym_cmds_CXX\"'~$RM -r $output_objdir/$realname.d'\n          fi\n        fi\n        ;;\n\n      beos*)\n\tif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then\n\t  allow_undefined_flag_CXX=unsupported\n\t  # Joseph Beckenbach <jrb3@best.com> says some releases of gcc\n\t  # support --undefined.  This deserves some investigation.  FIXME\n\t  archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n\telse\n\t  ld_shlibs_CXX=no\n\tfi\n\t;;\n\n      chorus*)\n        case $cc_basename in\n          *)\n\t  # FIXME: insert proper C++ library support\n\t  ld_shlibs_CXX=no\n\t  ;;\n        esac\n        ;;\n\n      cygwin* | mingw* | pw32* | cegcc*)\n\tcase $GXX,$cc_basename in\n\t,cl* | no,cl*)\n\t  # Native MSVC\n\t  # hardcode_libdir_flag_spec is actually meaningless, as there is\n\t  # no search path for DLLs.\n\t  hardcode_libdir_flag_spec_CXX=' '\n\t  allow_undefined_flag_CXX=unsupported\n\t  always_export_symbols_CXX=yes\n\t  file_list_spec_CXX='@'\n\t  # Tell ltmain to make .lib files, not .a files.\n\t  libext=lib\n\t  # Tell ltmain to make .dll files, not .so files.\n\t  shrext_cmds=.dll\n\t  # FIXME: Setting linknames here is a bad hack.\n\t  archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:\"$tool_output_objdir$libname.dll.lib\"~linknames='\n\t  archive_expsym_cmds_CXX='if   test DEF = \"`$SED -n     -e '\\''s/^[\t ]*//'\\''     -e '\\''/^\\(;.*\\)*$/d'\\''     -e '\\''s/^\\(EXPORTS\\|LIBRARY\\)\\([\t ].*\\)*$/DEF/p'\\''     -e q     $export_symbols`\" ; then\n              cp \"$export_symbols\" \"$output_objdir/$soname.def\";\n              echo \"$tool_output_objdir$soname.def\" > \"$output_objdir/$soname.exp\";\n            else\n              $SED -e '\\''s/^/-link -EXPORT:/'\\'' < $export_symbols > $output_objdir/$soname.exp;\n            fi~\n            $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs \"@$tool_output_objdir$soname.exp\" -Wl,-DLL,-IMPLIB:\"$tool_output_objdir$libname.dll.lib\"~\n            linknames='\n\t  # The linker will not automatically build a static lib if we build a DLL.\n\t  # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true'\n\t  enable_shared_with_static_runtimes_CXX=yes\n\t  # Don't use ranlib\n\t  old_postinstall_cmds_CXX='chmod 644 $oldlib'\n\t  postlink_cmds_CXX='lt_outputfile=\"@OUTPUT@\"~\n            lt_tool_outputfile=\"@TOOL_OUTPUT@\"~\n            case $lt_outputfile in\n              *.exe|*.EXE) ;;\n              *)\n                lt_outputfile=$lt_outputfile.exe\n                lt_tool_outputfile=$lt_tool_outputfile.exe\n                ;;\n            esac~\n            func_to_tool_file \"$lt_outputfile\"~\n            if test : != \"$MANIFEST_TOOL\" && test -f \"$lt_outputfile.manifest\"; then\n              $MANIFEST_TOOL -manifest \"$lt_tool_outputfile.manifest\" -outputresource:\"$lt_tool_outputfile\" || exit 1;\n              $RM \"$lt_outputfile.manifest\";\n            fi'\n\t  ;;\n\t*)\n\t  # g++\n\t  # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless,\n\t  # as there is no search path for DLLs.\n\t  hardcode_libdir_flag_spec_CXX='-L$libdir'\n\t  export_dynamic_flag_spec_CXX='$wl--export-all-symbols'\n\t  allow_undefined_flag_CXX=unsupported\n\t  always_export_symbols_CXX=no\n\t  enable_shared_with_static_runtimes_CXX=yes\n\n\t  if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then\n\t    archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'\n\t    # If the export-symbols file already is a .def file, use it as\n\t    # is; otherwise, prepend EXPORTS...\n\t    archive_expsym_cmds_CXX='if   test DEF = \"`$SED -n     -e '\\''s/^[\t ]*//'\\''     -e '\\''/^\\(;.*\\)*$/d'\\''     -e '\\''s/^\\(EXPORTS\\|LIBRARY\\)\\([\t ].*\\)*$/DEF/p'\\''     -e q     $export_symbols`\" ; then\n              cp $export_symbols $output_objdir/$soname.def;\n            else\n              echo EXPORTS > $output_objdir/$soname.def;\n              cat $export_symbols >> $output_objdir/$soname.def;\n            fi~\n            $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'\n\t  else\n\t    ld_shlibs_CXX=no\n\t  fi\n\t  ;;\n\tesac\n\t;;\n      darwin* | rhapsody*)\n\n\n  archive_cmds_need_lc_CXX=no\n  hardcode_direct_CXX=no\n  hardcode_automatic_CXX=yes\n  hardcode_shlibpath_var_CXX=unsupported\n  if test yes = \"$lt_cv_ld_force_load\"; then\n    whole_archive_flag_spec_CXX='`for conv in $convenience\\\"\\\"; do test  -n \\\"$conv\\\" && new_convenience=\\\"$new_convenience $wl-force_load,$conv\\\"; done; func_echo_all \\\"$new_convenience\\\"`'\n\n  else\n    whole_archive_flag_spec_CXX=''\n  fi\n  link_all_deplibs_CXX=yes\n  allow_undefined_flag_CXX=$_lt_dar_allow_undefined\n  case $cc_basename in\n     ifort*|nagfor*) _lt_dar_can_shared=yes ;;\n     *) _lt_dar_can_shared=$GCC ;;\n  esac\n  if test yes = \"$_lt_dar_can_shared\"; then\n    output_verbose_link_cmd=func_echo_all\n    archive_cmds_CXX=\"\\$CC -dynamiclib \\$allow_undefined_flag -o \\$lib \\$libobjs \\$deplibs \\$compiler_flags -install_name \\$rpath/\\$soname \\$verstring $_lt_dar_single_mod$_lt_dsymutil\"\n    module_cmds_CXX=\"\\$CC \\$allow_undefined_flag -o \\$lib -bundle \\$libobjs \\$deplibs \\$compiler_flags$_lt_dsymutil\"\n    archive_expsym_cmds_CXX=\"sed 's|^|_|' < \\$export_symbols > \\$output_objdir/\\$libname-symbols.expsym~\\$CC -dynamiclib \\$allow_undefined_flag -o \\$lib \\$libobjs \\$deplibs \\$compiler_flags -install_name \\$rpath/\\$soname \\$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil\"\n    module_expsym_cmds_CXX=\"sed -e 's|^|_|' < \\$export_symbols > \\$output_objdir/\\$libname-symbols.expsym~\\$CC \\$allow_undefined_flag -o \\$lib -bundle \\$libobjs \\$deplibs \\$compiler_flags$_lt_dar_export_syms$_lt_dsymutil\"\n       if test yes != \"$lt_cv_apple_cc_single_mod\"; then\n      archive_cmds_CXX=\"\\$CC -r -keep_private_externs -nostdlib -o \\$lib-master.o \\$libobjs~\\$CC -dynamiclib \\$allow_undefined_flag -o \\$lib \\$lib-master.o \\$deplibs \\$compiler_flags -install_name \\$rpath/\\$soname \\$verstring$_lt_dsymutil\"\n      archive_expsym_cmds_CXX=\"sed 's|^|_|' < \\$export_symbols > \\$output_objdir/\\$libname-symbols.expsym~\\$CC -r -keep_private_externs -nostdlib -o \\$lib-master.o \\$libobjs~\\$CC -dynamiclib \\$allow_undefined_flag -o \\$lib \\$lib-master.o \\$deplibs \\$compiler_flags -install_name \\$rpath/\\$soname \\$verstring$_lt_dar_export_syms$_lt_dsymutil\"\n    fi\n\n  else\n  ld_shlibs_CXX=no\n  fi\n\n\t;;\n\n      os2*)\n\thardcode_libdir_flag_spec_CXX='-L$libdir'\n\thardcode_minus_L_CXX=yes\n\tallow_undefined_flag_CXX=unsupported\n\tshrext_cmds=.dll\n\tarchive_cmds_CXX='$ECHO \"LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE\" > $output_objdir/$libname.def~\n\t  $ECHO \"DESCRIPTION \\\"$libname\\\"\" >> $output_objdir/$libname.def~\n\t  $ECHO \"DATA MULTIPLE NONSHARED\" >> $output_objdir/$libname.def~\n\t  $ECHO EXPORTS >> $output_objdir/$libname.def~\n\t  emxexp $libobjs | $SED /\"_DLL_InitTerm\"/d >> $output_objdir/$libname.def~\n\t  $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~\n\t  emximp -o $lib $output_objdir/$libname.def'\n\tarchive_expsym_cmds_CXX='$ECHO \"LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE\" > $output_objdir/$libname.def~\n\t  $ECHO \"DESCRIPTION \\\"$libname\\\"\" >> $output_objdir/$libname.def~\n\t  $ECHO \"DATA MULTIPLE NONSHARED\" >> $output_objdir/$libname.def~\n\t  $ECHO EXPORTS >> $output_objdir/$libname.def~\n\t  prefix_cmds=\"$SED\"~\n\t  if test EXPORTS = \"`$SED 1q $export_symbols`\"; then\n\t    prefix_cmds=\"$prefix_cmds -e 1d\";\n\t  fi~\n\t  prefix_cmds=\"$prefix_cmds -e \\\"s/^\\(.*\\)$/_\\1/g\\\"\"~\n\t  cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~\n\t  $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~\n\t  emximp -o $lib $output_objdir/$libname.def'\n\told_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'\n\tenable_shared_with_static_runtimes_CXX=yes\n\t;;\n\n      dgux*)\n        case $cc_basename in\n          ec++*)\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n          ghcx*)\n\t    # Green Hills C++ Compiler\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n          *)\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n        esac\n        ;;\n\n      freebsd2.*)\n        # C++ shared libraries reported to be fairly broken before\n\t# switch to ELF\n        ld_shlibs_CXX=no\n        ;;\n\n      freebsd-elf*)\n        archive_cmds_need_lc_CXX=no\n        ;;\n\n      freebsd* | dragonfly*)\n        # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF\n        # conventions\n        ld_shlibs_CXX=yes\n        ;;\n\n      haiku*)\n        archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n        link_all_deplibs_CXX=yes\n        ;;\n\n      hpux9*)\n        hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir'\n        hardcode_libdir_separator_CXX=:\n        export_dynamic_flag_spec_CXX='$wl-E'\n        hardcode_direct_CXX=yes\n        hardcode_minus_L_CXX=yes # Not in the search PATH,\n\t\t\t\t             # but as the default\n\t\t\t\t             # location of the library.\n\n        case $cc_basename in\n          CC*)\n            # FIXME: insert proper C++ library support\n            ld_shlibs_CXX=no\n            ;;\n          aCC*)\n            archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test \"x$output_objdir/$soname\" = \"x$lib\" || mv $output_objdir/$soname $lib'\n            # Commands to make compiler produce verbose output that lists\n            # what \"hidden\" libraries, object files and flags are used when\n            # linking a shared library.\n            #\n            # There doesn't appear to be a way to prevent this compiler from\n            # explicitly linking system object files so we need to strip them\n            # from the output so that they don't get included in the library\n            # dependencies.\n            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP \"\\-L\"`; list= ; for z in $templist; do case $z in conftest.$objext) list=\"$list $z\";; *.$objext);; *) list=\"$list $z\";;esac; done; func_echo_all \"$list\"'\n            ;;\n          *)\n            if test yes = \"$GXX\"; then\n              archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test \"x$output_objdir/$soname\" = \"x$lib\" || mv $output_objdir/$soname $lib'\n            else\n              # FIXME: insert proper C++ library support\n              ld_shlibs_CXX=no\n            fi\n            ;;\n        esac\n        ;;\n\n      hpux10*|hpux11*)\n        if test no = \"$with_gnu_ld\"; then\n\t  hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir'\n\t  hardcode_libdir_separator_CXX=:\n\n          case $host_cpu in\n            hppa*64*|ia64*)\n              ;;\n            *)\n\t      export_dynamic_flag_spec_CXX='$wl-E'\n              ;;\n          esac\n        fi\n        case $host_cpu in\n          hppa*64*|ia64*)\n            hardcode_direct_CXX=no\n            hardcode_shlibpath_var_CXX=no\n            ;;\n          *)\n            hardcode_direct_CXX=yes\n            hardcode_direct_absolute_CXX=yes\n            hardcode_minus_L_CXX=yes # Not in the search PATH,\n\t\t\t\t\t         # but as the default\n\t\t\t\t\t         # location of the library.\n            ;;\n        esac\n\n        case $cc_basename in\n          CC*)\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n          aCC*)\n\t    case $host_cpu in\n\t      hppa*64*)\n\t        archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'\n\t        ;;\n\t      ia64*)\n\t        archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'\n\t        ;;\n\t      *)\n\t        archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'\n\t        ;;\n\t    esac\n\t    # Commands to make compiler produce verbose output that lists\n\t    # what \"hidden\" libraries, object files and flags are used when\n\t    # linking a shared library.\n\t    #\n\t    # There doesn't appear to be a way to prevent this compiler from\n\t    # explicitly linking system object files so we need to strip them\n\t    # from the output so that they don't get included in the library\n\t    # dependencies.\n\t    output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP \"\\-L\"`; list= ; for z in $templist; do case $z in conftest.$objext) list=\"$list $z\";; *.$objext);; *) list=\"$list $z\";;esac; done; func_echo_all \"$list\"'\n\t    ;;\n          *)\n\t    if test yes = \"$GXX\"; then\n\t      if test no = \"$with_gnu_ld\"; then\n\t        case $host_cpu in\n\t          hppa*64*)\n\t            archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'\n\t            ;;\n\t          ia64*)\n\t            archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'\n\t            ;;\n\t          *)\n\t            archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'\n\t            ;;\n\t        esac\n\t      fi\n\t    else\n\t      # FIXME: insert proper C++ library support\n\t      ld_shlibs_CXX=no\n\t    fi\n\t    ;;\n        esac\n        ;;\n\n      interix[3-9]*)\n\thardcode_direct_CXX=no\n\thardcode_shlibpath_var_CXX=no\n\thardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'\n\texport_dynamic_flag_spec_CXX='$wl-E'\n\t# Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.\n\t# Instead, shared libraries are loaded at an image base (0x10000000 by\n\t# default) and relocated if they conflict, which is a slow very memory\n\t# consuming and fragmenting process.  To avoid this, we pick a random,\n\t# 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link\n\t# time.  Moving up from 0x10000000 also allows more sbrk(2) space.\n\tarchive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \\* 262144 + 1342177280` -o $lib'\n\tarchive_expsym_cmds_CXX='sed \"s|^|_|\" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \\* 262144 + 1342177280` -o $lib'\n\t;;\n      irix5* | irix6*)\n        case $cc_basename in\n          CC*)\n\t    # SGI C++\n\t    archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n \"$verstring\" && func_echo_all \"-set_version $verstring\"` -update_registry $output_objdir/so_locations -o $lib'\n\n\t    # Archives containing C++ object files must be created using\n\t    # \"CC -ar\", where \"CC\" is the IRIX C++ compiler.  This is\n\t    # necessary to make sure instantiated templates are included\n\t    # in the archive.\n\t    old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs'\n\t    ;;\n          *)\n\t    if test yes = \"$GXX\"; then\n\t      if test no = \"$with_gnu_ld\"; then\n\t        archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n \"$verstring\" && func_echo_all \"$wl-set_version $wl$verstring\"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'\n\t      else\n\t        archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n \"$verstring\" && func_echo_all \"$wl-set_version $wl$verstring\"` -o $lib'\n\t      fi\n\t    fi\n\t    link_all_deplibs_CXX=yes\n\t    ;;\n        esac\n        hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'\n        hardcode_libdir_separator_CXX=:\n        inherit_rpath_CXX=yes\n        ;;\n\n      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)\n        case $cc_basename in\n          KCC*)\n\t    # Kuck and Associates, Inc. (KAI) C++ Compiler\n\n\t    # KCC will only create a shared library if the output file\n\t    # ends with \".so\" (or \".sl\" for HP-UX), so rename the library\n\t    # to its proper name (with version) after linking.\n\t    archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\\''s/\\([^()0-9A-Za-z{}]\\)/\\\\\\\\\\1/g'\\''`; templib=`echo $lib | $SED -e \"s/\\$tempext\\..*/.so/\"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \\$templib; mv \\$templib $lib'\n\t    archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\\''s/\\([^()0-9A-Za-z{}]\\)/\\\\\\\\\\1/g'\\''`; templib=`echo $lib | $SED -e \"s/\\$tempext\\..*/.so/\"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \\$templib $wl-retain-symbols-file,$export_symbols; mv \\$templib $lib'\n\t    # Commands to make compiler produce verbose output that lists\n\t    # what \"hidden\" libraries, object files and flags are used when\n\t    # linking a shared library.\n\t    #\n\t    # There doesn't appear to be a way to prevent this compiler from\n\t    # explicitly linking system object files so we need to strip them\n\t    # from the output so that they don't get included in the library\n\t    # dependencies.\n\t    output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP \"ld\"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list=\"$list $z\";; *.$objext);; *) list=\"$list $z\";;esac; done; func_echo_all \"$list\"'\n\n\t    hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'\n\t    export_dynamic_flag_spec_CXX='$wl--export-dynamic'\n\n\t    # Archives containing C++ object files must be created using\n\t    # \"CC -Bstatic\", where \"CC\" is the KAI C++ compiler.\n\t    old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs'\n\t    ;;\n\t  icpc* | ecpc* )\n\t    # Intel C++\n\t    with_gnu_ld=yes\n\t    # version 8.0 and above of icpc choke on multiply defined symbols\n\t    # if we add $predep_objects and $postdep_objects, however 7.1 and\n\t    # earlier do not add the objects themselves.\n\t    case `$CC -V 2>&1` in\n\t      *\"Version 7.\"*)\n\t        archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'\n\t\tarchive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'\n\t\t;;\n\t      *)  # Version 8.0 or newer\n\t        tmp_idyn=\n\t        case $host_cpu in\n\t\t  ia64*) tmp_idyn=' -i_dynamic';;\n\t\tesac\n\t        archive_cmds_CXX='$CC -shared'\"$tmp_idyn\"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n\t\tarchive_expsym_cmds_CXX='$CC -shared'\"$tmp_idyn\"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'\n\t\t;;\n\t    esac\n\t    archive_cmds_need_lc_CXX=no\n\t    hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'\n\t    export_dynamic_flag_spec_CXX='$wl--export-dynamic'\n\t    whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive'\n\t    ;;\n          pgCC* | pgcpp*)\n            # Portland Group C++ compiler\n\t    case `$CC -V` in\n\t    *pgCC\\ [1-5].* | *pgcpp\\ [1-5].*)\n\t      prelink_cmds_CXX='tpldir=Template.dir~\n               rm -rf $tpldir~\n               $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~\n               compile_command=\"$compile_command `find $tpldir -name \\*.o | sort | $NL2SP`\"'\n\t      old_archive_cmds_CXX='tpldir=Template.dir~\n                rm -rf $tpldir~\n                $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~\n                $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \\*.o | sort | $NL2SP`~\n                $RANLIB $oldlib'\n\t      archive_cmds_CXX='tpldir=Template.dir~\n                rm -rf $tpldir~\n                $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~\n                $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \\*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'\n\t      archive_expsym_cmds_CXX='tpldir=Template.dir~\n                rm -rf $tpldir~\n                $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~\n                $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \\*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'\n\t      ;;\n\t    *) # Version 6 and above use weak symbols\n\t      archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'\n\t      archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'\n\t      ;;\n\t    esac\n\n\t    hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir'\n\t    export_dynamic_flag_spec_CXX='$wl--export-dynamic'\n\t    whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\\\"\\\"; do test  -n \\\"$conv\\\" && new_convenience=\\\"$new_convenience,$conv\\\"; done; func_echo_all \\\"$new_convenience\\\"` $wl--no-whole-archive'\n            ;;\n\t  cxx*)\n\t    # Compaq C++\n\t    archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'\n\t    archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname  -o $lib $wl-retain-symbols-file $wl$export_symbols'\n\n\t    runpath_var=LD_RUN_PATH\n\t    hardcode_libdir_flag_spec_CXX='-rpath $libdir'\n\t    hardcode_libdir_separator_CXX=:\n\n\t    # Commands to make compiler produce verbose output that lists\n\t    # what \"hidden\" libraries, object files and flags are used when\n\t    # linking a shared library.\n\t    #\n\t    # There doesn't appear to be a way to prevent this compiler from\n\t    # explicitly linking system object files so we need to strip them\n\t    # from the output so that they don't get included in the library\n\t    # dependencies.\n\t    output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP \"ld\"`; templist=`func_echo_all \"$templist\" | $SED \"s/\\(^.*ld.*\\)\\( .*ld .*$\\)/\\1/\"`; list= ; for z in $templist; do case $z in conftest.$objext) list=\"$list $z\";; *.$objext);; *) list=\"$list $z\";;esac; done; func_echo_all \"X$list\" | $Xsed'\n\t    ;;\n\t  xl* | mpixl* | bgxl*)\n\t    # IBM XL 8.0 on PPC, with GNU ld\n\t    hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'\n\t    export_dynamic_flag_spec_CXX='$wl--export-dynamic'\n\t    archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'\n\t    if test yes = \"$supports_anon_versioning\"; then\n\t      archive_expsym_cmds_CXX='echo \"{ global:\" > $output_objdir/$libname.ver~\n                cat $export_symbols | sed -e \"s/\\(.*\\)/\\1;/\" >> $output_objdir/$libname.ver~\n                echo \"local: *; };\" >> $output_objdir/$libname.ver~\n                $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'\n\t    fi\n\t    ;;\n\t  *)\n\t    case `$CC -V 2>&1 | sed 5q` in\n\t    *Sun\\ C*)\n\t      # Sun C++ 5.9\n\t      no_undefined_flag_CXX=' -zdefs'\n\t      archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'\n\t      archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols'\n\t      hardcode_libdir_flag_spec_CXX='-R$libdir'\n\t      whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\\\"\\\"; do test -z \\\"$conv\\\" || new_convenience=\\\"$new_convenience,$conv\\\"; done; func_echo_all \\\"$new_convenience\\\"` $wl--no-whole-archive'\n\t      compiler_needs_object_CXX=yes\n\n\t      # Not sure whether something based on\n\t      # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1\n\t      # would be better.\n\t      output_verbose_link_cmd='func_echo_all'\n\n\t      # Archives containing C++ object files must be created using\n\t      # \"CC -xar\", where \"CC\" is the Sun C++ compiler.  This is\n\t      # necessary to make sure instantiated templates are included\n\t      # in the archive.\n\t      old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs'\n\t      ;;\n\t    esac\n\t    ;;\n\tesac\n\t;;\n\n      lynxos*)\n        # FIXME: insert proper C++ library support\n\tld_shlibs_CXX=no\n\t;;\n\n      m88k*)\n        # FIXME: insert proper C++ library support\n        ld_shlibs_CXX=no\n\t;;\n\n      mvs*)\n        case $cc_basename in\n          cxx*)\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n\t  *)\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n\tesac\n\t;;\n\n      netbsd*)\n        if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then\n\t  archive_cmds_CXX='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'\n\t  wlarc=\n\t  hardcode_libdir_flag_spec_CXX='-R$libdir'\n\t  hardcode_direct_CXX=yes\n\t  hardcode_shlibpath_var_CXX=no\n\tfi\n\t# Workaround some broken pre-1.5 toolchains\n\toutput_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e \"s:-lgcc -lc -lgcc::\"'\n\t;;\n\n      *nto* | *qnx*)\n        ld_shlibs_CXX=yes\n\t;;\n\n      openbsd* | bitrig*)\n\tif test -f /usr/libexec/ld.so; then\n\t  hardcode_direct_CXX=yes\n\t  hardcode_shlibpath_var_CXX=no\n\t  hardcode_direct_absolute_CXX=yes\n\t  archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'\n\t  hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'\n\t  if test -z \"`echo __ELF__ | $CC -E - | grep __ELF__`\"; then\n\t    archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib'\n\t    export_dynamic_flag_spec_CXX='$wl-E'\n\t    whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'\n\t  fi\n\t  output_verbose_link_cmd=func_echo_all\n\telse\n\t  ld_shlibs_CXX=no\n\tfi\n\t;;\n\n      osf3* | osf4* | osf5*)\n        case $cc_basename in\n          KCC*)\n\t    # Kuck and Associates, Inc. (KAI) C++ Compiler\n\n\t    # KCC will only create a shared library if the output file\n\t    # ends with \".so\" (or \".sl\" for HP-UX), so rename the library\n\t    # to its proper name (with version) after linking.\n\t    archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\\''s/\\([^()0-9A-Za-z{}]\\)/\\\\\\\\\\1/g'\\''`; templib=`echo \"$lib\" | $SED -e \"s/\\$tempext\\..*/.so/\"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \\$templib; mv \\$templib $lib'\n\n\t    hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'\n\t    hardcode_libdir_separator_CXX=:\n\n\t    # Archives containing C++ object files must be created using\n\t    # the KAI C++ compiler.\n\t    case $host in\n\t      osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;;\n\t      *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;;\n\t    esac\n\t    ;;\n          RCC*)\n\t    # Rational C++ 2.4.1\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n          cxx*)\n\t    case $host in\n\t      osf3*)\n\t        allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\\*'\n\t        archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n \"$verstring\" && func_echo_all \"$wl-set_version $verstring\"` -update_registry $output_objdir/so_locations -o $lib'\n\t        hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'\n\t\t;;\n\t      *)\n\t        allow_undefined_flag_CXX=' -expect_unresolved \\*'\n\t        archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n \"$verstring\" && func_echo_all \"-set_version $verstring\"` -update_registry $output_objdir/so_locations -o $lib'\n\t        archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf \"%s %s\\\\n\" -exported_symbol \"\\$i\" >> $lib.exp; done~\n                  echo \"-hidden\">> $lib.exp~\n                  $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp  `test -n \"$verstring\" && $ECHO \"-set_version $verstring\"` -update_registry $output_objdir/so_locations -o $lib~\n                  $RM $lib.exp'\n\t        hardcode_libdir_flag_spec_CXX='-rpath $libdir'\n\t\t;;\n\t    esac\n\n\t    hardcode_libdir_separator_CXX=:\n\n\t    # Commands to make compiler produce verbose output that lists\n\t    # what \"hidden\" libraries, object files and flags are used when\n\t    # linking a shared library.\n\t    #\n\t    # There doesn't appear to be a way to prevent this compiler from\n\t    # explicitly linking system object files so we need to strip them\n\t    # from the output so that they don't get included in the library\n\t    # dependencies.\n\t    output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP \"ld\" | $GREP -v \"ld:\"`; templist=`func_echo_all \"$templist\" | $SED \"s/\\(^.*ld.*\\)\\( .*ld.*$\\)/\\1/\"`; list= ; for z in $templist; do case $z in conftest.$objext) list=\"$list $z\";; *.$objext);; *) list=\"$list $z\";;esac; done; func_echo_all \"$list\"'\n\t    ;;\n\t  *)\n\t    if test yes,no = \"$GXX,$with_gnu_ld\"; then\n\t      allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\\*'\n\t      case $host in\n\t        osf3*)\n\t          archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n \"$verstring\" && func_echo_all \"$wl-set_version $wl$verstring\"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'\n\t\t  ;;\n\t        *)\n\t          archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n \"$verstring\" && func_echo_all \"$wl-set_version $wl$verstring\"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'\n\t\t  ;;\n\t      esac\n\n\t      hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'\n\t      hardcode_libdir_separator_CXX=:\n\n\t      # Commands to make compiler produce verbose output that lists\n\t      # what \"hidden\" libraries, object files and flags are used when\n\t      # linking a shared library.\n\t      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v \"^Configured with:\" | $GREP \"\\-L\"'\n\n\t    else\n\t      # FIXME: insert proper C++ library support\n\t      ld_shlibs_CXX=no\n\t    fi\n\t    ;;\n        esac\n        ;;\n\n      psos*)\n        # FIXME: insert proper C++ library support\n        ld_shlibs_CXX=no\n        ;;\n\n      sunos4*)\n        case $cc_basename in\n          CC*)\n\t    # Sun C++ 4.x\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n          lcc*)\n\t    # Lucid\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n          *)\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n        esac\n        ;;\n\n      solaris*)\n        case $cc_basename in\n          CC* | sunCC*)\n\t    # Sun C++ 4.2, 5.x and Centerline C++\n            archive_cmds_need_lc_CXX=yes\n\t    no_undefined_flag_CXX=' -zdefs'\n\t    archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'\n\t    archive_expsym_cmds_CXX='echo \"{ global:\" > $lib.exp~cat $export_symbols | $SED -e \"s/\\(.*\\)/\\1;/\" >> $lib.exp~echo \"local: *; };\" >> $lib.exp~\n              $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'\n\n\t    hardcode_libdir_flag_spec_CXX='-R$libdir'\n\t    hardcode_shlibpath_var_CXX=no\n\t    case $host_os in\n\t      solaris2.[0-5] | solaris2.[0-5].*) ;;\n\t      *)\n\t\t# The compiler driver will combine and reorder linker options,\n\t\t# but understands '-z linker_flag'.\n\t        # Supported since Solaris 2.6 (maybe 2.5.1?)\n\t\twhole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract'\n\t        ;;\n\t    esac\n\t    link_all_deplibs_CXX=yes\n\n\t    output_verbose_link_cmd='func_echo_all'\n\n\t    # Archives containing C++ object files must be created using\n\t    # \"CC -xar\", where \"CC\" is the Sun C++ compiler.  This is\n\t    # necessary to make sure instantiated templates are included\n\t    # in the archive.\n\t    old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs'\n\t    ;;\n          gcx*)\n\t    # Green Hills C++ Compiler\n\t    archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'\n\n\t    # The C++ compiler must be used to create the archive.\n\t    old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs'\n\t    ;;\n          *)\n\t    # GNU C++ compiler with Solaris linker\n\t    if test yes,no = \"$GXX,$with_gnu_ld\"; then\n\t      no_undefined_flag_CXX=' $wl-z ${wl}defs'\n\t      if $CC --version | $GREP -v '^2\\.7' > /dev/null; then\n\t        archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'\n\t        archive_expsym_cmds_CXX='echo \"{ global:\" > $lib.exp~cat $export_symbols | $SED -e \"s/\\(.*\\)/\\1;/\" >> $lib.exp~echo \"local: *; };\" >> $lib.exp~\n                  $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'\n\n\t        # Commands to make compiler produce verbose output that lists\n\t        # what \"hidden\" libraries, object files and flags are used when\n\t        # linking a shared library.\n\t        output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v \"^Configured with:\" | $GREP \"\\-L\"'\n\t      else\n\t        # g++ 2.7 appears to require '-G' NOT '-shared' on this\n\t        # platform.\n\t        archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'\n\t        archive_expsym_cmds_CXX='echo \"{ global:\" > $lib.exp~cat $export_symbols | $SED -e \"s/\\(.*\\)/\\1;/\" >> $lib.exp~echo \"local: *; };\" >> $lib.exp~\n                  $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'\n\n\t        # Commands to make compiler produce verbose output that lists\n\t        # what \"hidden\" libraries, object files and flags are used when\n\t        # linking a shared library.\n\t        output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v \"^Configured with:\" | $GREP \"\\-L\"'\n\t      fi\n\n\t      hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir'\n\t      case $host_os in\n\t\tsolaris2.[0-5] | solaris2.[0-5].*) ;;\n\t\t*)\n\t\t  whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'\n\t\t  ;;\n\t      esac\n\t    fi\n\t    ;;\n        esac\n        ;;\n\n    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)\n      no_undefined_flag_CXX='$wl-z,text'\n      archive_cmds_need_lc_CXX=no\n      hardcode_shlibpath_var_CXX=no\n      runpath_var='LD_RUN_PATH'\n\n      case $cc_basename in\n        CC*)\n\t  archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\t  archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\t  ;;\n\t*)\n\t  archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\t  archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\t  ;;\n      esac\n      ;;\n\n      sysv5* | sco3.2v5* | sco5v6*)\n\t# Note: We CANNOT use -z defs as we might desire, because we do not\n\t# link with -lc, and that would cause any symbols used from libc to\n\t# always be unresolved, which means just about no library would\n\t# ever link correctly.  If we're not using GNU ld we use -z text\n\t# though, which does catch some bad symbols but isn't as heavy-handed\n\t# as -z defs.\n\tno_undefined_flag_CXX='$wl-z,text'\n\tallow_undefined_flag_CXX='$wl-z,nodefs'\n\tarchive_cmds_need_lc_CXX=no\n\thardcode_shlibpath_var_CXX=no\n\thardcode_libdir_flag_spec_CXX='$wl-R,$libdir'\n\thardcode_libdir_separator_CXX=':'\n\tlink_all_deplibs_CXX=yes\n\texport_dynamic_flag_spec_CXX='$wl-Bexport'\n\trunpath_var='LD_RUN_PATH'\n\n\tcase $cc_basename in\n          CC*)\n\t    archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\t    archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\t    old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~\n              '\"$old_archive_cmds_CXX\"\n\t    reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~\n              '\"$reload_cmds_CXX\"\n\t    ;;\n\t  *)\n\t    archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\t    archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'\n\t    ;;\n\tesac\n      ;;\n\n      tandem*)\n        case $cc_basename in\n          NCC*)\n\t    # NonStop-UX NCC 3.20\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n          *)\n\t    # FIXME: insert proper C++ library support\n\t    ld_shlibs_CXX=no\n\t    ;;\n        esac\n        ;;\n\n      vxworks*)\n        # FIXME: insert proper C++ library support\n        ld_shlibs_CXX=no\n        ;;\n\n      *)\n        # FIXME: insert proper C++ library support\n        ld_shlibs_CXX=no\n        ;;\n    esac\n\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX\" >&5\n$as_echo \"$ld_shlibs_CXX\" >&6; }\n    test no = \"$ld_shlibs_CXX\" && can_build_shared=no\n\n    GCC_CXX=$GXX\n    LD_CXX=$LD\n\n    ## CAVEAT EMPTOR:\n    ## There is no encapsulation within the following macros, do not change\n    ## the running order or otherwise move them around unless you know exactly\n    ## what you are doing...\n    # Dependencies to place before and after the object being linked:\npredep_objects_CXX=\npostdep_objects_CXX=\npredeps_CXX=\npostdeps_CXX=\ncompiler_lib_search_path_CXX=\n\ncat > conftest.$ac_ext <<_LT_EOF\nclass Foo\n{\npublic:\n  Foo (void) { a = 0; }\nprivate:\n  int a;\n};\n_LT_EOF\n\n\n_lt_libdeps_save_CFLAGS=$CFLAGS\ncase \"$CC $CFLAGS \" in #(\n*\\ -flto*\\ *) CFLAGS=\"$CFLAGS -fno-lto\" ;;\n*\\ -fwhopr*\\ *) CFLAGS=\"$CFLAGS -fno-whopr\" ;;\n*\\ -fuse-linker-plugin*\\ *) CFLAGS=\"$CFLAGS -fno-use-linker-plugin\" ;;\nesac\n\nif { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_compile\\\"\"; } >&5\n  (eval $ac_compile) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then\n  # Parse the compiler output and extract the necessary\n  # objects, libraries and library flags.\n\n  # Sentinel used to keep track of whether or not we are before\n  # the conftest object file.\n  pre_test_object_deps_done=no\n\n  for p in `eval \"$output_verbose_link_cmd\"`; do\n    case $prev$p in\n\n    -L* | -R* | -l*)\n       # Some compilers place space between \"-{L,R}\" and the path.\n       # Remove the space.\n       if test x-L = \"$p\" ||\n          test x-R = \"$p\"; then\n\t prev=$p\n\t continue\n       fi\n\n       # Expand the sysroot to ease extracting the directories later.\n       if test -z \"$prev\"; then\n         case $p in\n         -L*) func_stripname_cnf '-L' '' \"$p\"; prev=-L; p=$func_stripname_result ;;\n         -R*) func_stripname_cnf '-R' '' \"$p\"; prev=-R; p=$func_stripname_result ;;\n         -l*) func_stripname_cnf '-l' '' \"$p\"; prev=-l; p=$func_stripname_result ;;\n         esac\n       fi\n       case $p in\n       =*) func_stripname_cnf '=' '' \"$p\"; p=$lt_sysroot$func_stripname_result ;;\n       esac\n       if test no = \"$pre_test_object_deps_done\"; then\n\t case $prev in\n\t -L | -R)\n\t   # Internal compiler library paths should come after those\n\t   # provided the user.  The postdeps already come after the\n\t   # user supplied libs so there is no need to process them.\n\t   if test -z \"$compiler_lib_search_path_CXX\"; then\n\t     compiler_lib_search_path_CXX=$prev$p\n\t   else\n\t     compiler_lib_search_path_CXX=\"${compiler_lib_search_path_CXX} $prev$p\"\n\t   fi\n\t   ;;\n\t # The \"-l\" case would never come before the object being\n\t # linked, so don't bother handling this case.\n\t esac\n       else\n\t if test -z \"$postdeps_CXX\"; then\n\t   postdeps_CXX=$prev$p\n\t else\n\t   postdeps_CXX=\"${postdeps_CXX} $prev$p\"\n\t fi\n       fi\n       prev=\n       ;;\n\n    *.lto.$objext) ;; # Ignore GCC LTO objects\n    *.$objext)\n       # This assumes that the test object file only shows up\n       # once in the compiler output.\n       if test \"$p\" = \"conftest.$objext\"; then\n\t pre_test_object_deps_done=yes\n\t continue\n       fi\n\n       if test no = \"$pre_test_object_deps_done\"; then\n\t if test -z \"$predep_objects_CXX\"; then\n\t   predep_objects_CXX=$p\n\t else\n\t   predep_objects_CXX=\"$predep_objects_CXX $p\"\n\t fi\n       else\n\t if test -z \"$postdep_objects_CXX\"; then\n\t   postdep_objects_CXX=$p\n\t else\n\t   postdep_objects_CXX=\"$postdep_objects_CXX $p\"\n\t fi\n       fi\n       ;;\n\n    *) ;; # Ignore the rest.\n\n    esac\n  done\n\n  # Clean up.\n  rm -f a.out a.exe\nelse\n  echo \"libtool.m4: error: problem compiling CXX test program\"\nfi\n\n$RM -f confest.$objext\nCFLAGS=$_lt_libdeps_save_CFLAGS\n\n# PORTME: override above test on systems where it is broken\ncase $host_os in\ninterix[3-9]*)\n  # Interix 3.5 installs completely hosed .la files for C++, so rather than\n  # hack all around it, let's just trust \"g++\" to DTRT.\n  predep_objects_CXX=\n  postdep_objects_CXX=\n  postdeps_CXX=\n  ;;\nesac\n\n\ncase \" $postdeps_CXX \" in\n*\" -lc \"*) archive_cmds_need_lc_CXX=no ;;\nesac\n compiler_lib_search_dirs_CXX=\nif test -n \"${compiler_lib_search_path_CXX}\"; then\n compiler_lib_search_dirs_CXX=`echo \" ${compiler_lib_search_path_CXX}\" | $SED -e 's! -L! !g' -e 's!^ !!'`\nfi\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\n\n\n\n\n    lt_prog_compiler_wl_CXX=\nlt_prog_compiler_pic_CXX=\nlt_prog_compiler_static_CXX=\n\n\n  # C++ specific cases for pic, static, wl, etc.\n  if test yes = \"$GXX\"; then\n    lt_prog_compiler_wl_CXX='-Wl,'\n    lt_prog_compiler_static_CXX='-static'\n\n    case $host_os in\n    aix*)\n      # All AIX code is PIC.\n      if test ia64 = \"$host_cpu\"; then\n\t# AIX 5 now supports IA64 processor\n\tlt_prog_compiler_static_CXX='-Bstatic'\n      fi\n      lt_prog_compiler_pic_CXX='-fPIC'\n      ;;\n\n    amigaos*)\n      case $host_cpu in\n      powerpc)\n            # see comment about AmigaOS4 .so support\n            lt_prog_compiler_pic_CXX='-fPIC'\n        ;;\n      m68k)\n            # FIXME: we need at least 68020 code to build shared libraries, but\n            # adding the '-m68020' flag to GCC prevents building anything better,\n            # like '-m68040'.\n            lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4'\n        ;;\n      esac\n      ;;\n\n    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)\n      # PIC is the default for these OSes.\n      ;;\n    mingw* | cygwin* | os2* | pw32* | cegcc*)\n      # This hack is so that the source file can tell whether it is being\n      # built for inclusion in a dll (and should export symbols for example).\n      # Although the cygwin gcc ignores -fPIC, still need this for old-style\n      # (--disable-auto-import) libraries\n      lt_prog_compiler_pic_CXX='-DDLL_EXPORT'\n      case $host_os in\n      os2*)\n\tlt_prog_compiler_static_CXX='$wl-static'\n\t;;\n      esac\n      ;;\n    darwin* | rhapsody*)\n      # PIC is the default on this platform\n      # Common symbols not allowed in MH_DYLIB files\n      lt_prog_compiler_pic_CXX='-fno-common'\n      ;;\n    *djgpp*)\n      # DJGPP does not support shared libraries at all\n      lt_prog_compiler_pic_CXX=\n      ;;\n    haiku*)\n      # PIC is the default for Haiku.\n      # The \"-static\" flag exists, but is broken.\n      lt_prog_compiler_static_CXX=\n      ;;\n    interix[3-9]*)\n      # Interix 3.x gcc -fpic/-fPIC options generate broken code.\n      # Instead, we relocate shared libraries at runtime.\n      ;;\n    sysv4*MP*)\n      if test -d /usr/nec; then\n\tlt_prog_compiler_pic_CXX=-Kconform_pic\n      fi\n      ;;\n    hpux*)\n      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit\n      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag\n      # sets the default TLS model and affects inlining.\n      case $host_cpu in\n      hppa*64*)\n\t;;\n      *)\n\tlt_prog_compiler_pic_CXX='-fPIC'\n\t;;\n      esac\n      ;;\n    *qnx* | *nto*)\n      # QNX uses GNU C++, but need to define -shared option too, otherwise\n      # it will coredump.\n      lt_prog_compiler_pic_CXX='-fPIC -shared'\n      ;;\n    *)\n      lt_prog_compiler_pic_CXX='-fPIC'\n      ;;\n    esac\n  else\n    case $host_os in\n      aix[4-9]*)\n\t# All AIX code is PIC.\n\tif test ia64 = \"$host_cpu\"; then\n\t  # AIX 5 now supports IA64 processor\n\t  lt_prog_compiler_static_CXX='-Bstatic'\n\telse\n\t  lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp'\n\tfi\n\t;;\n      chorus*)\n\tcase $cc_basename in\n\tcxch68*)\n\t  # Green Hills C++ Compiler\n\t  # _LT_TAGVAR(lt_prog_compiler_static, CXX)=\"--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a\"\n\t  ;;\n\tesac\n\t;;\n      mingw* | cygwin* | os2* | pw32* | cegcc*)\n\t# This hack is so that the source file can tell whether it is being\n\t# built for inclusion in a dll (and should export symbols for example).\n\tlt_prog_compiler_pic_CXX='-DDLL_EXPORT'\n\t;;\n      dgux*)\n\tcase $cc_basename in\n\t  ec++*)\n\t    lt_prog_compiler_pic_CXX='-KPIC'\n\t    ;;\n\t  ghcx*)\n\t    # Green Hills C++ Compiler\n\t    lt_prog_compiler_pic_CXX='-pic'\n\t    ;;\n\t  *)\n\t    ;;\n\tesac\n\t;;\n      freebsd* | dragonfly*)\n\t# FreeBSD uses GNU C++\n\t;;\n      hpux9* | hpux10* | hpux11*)\n\tcase $cc_basename in\n\t  CC*)\n\t    lt_prog_compiler_wl_CXX='-Wl,'\n\t    lt_prog_compiler_static_CXX='$wl-a ${wl}archive'\n\t    if test ia64 != \"$host_cpu\"; then\n\t      lt_prog_compiler_pic_CXX='+Z'\n\t    fi\n\t    ;;\n\t  aCC*)\n\t    lt_prog_compiler_wl_CXX='-Wl,'\n\t    lt_prog_compiler_static_CXX='$wl-a ${wl}archive'\n\t    case $host_cpu in\n\t    hppa*64*|ia64*)\n\t      # +Z the default\n\t      ;;\n\t    *)\n\t      lt_prog_compiler_pic_CXX='+Z'\n\t      ;;\n\t    esac\n\t    ;;\n\t  *)\n\t    ;;\n\tesac\n\t;;\n      interix*)\n\t# This is c89, which is MS Visual C++ (no shared libs)\n\t# Anyone wants to do a port?\n\t;;\n      irix5* | irix6* | nonstopux*)\n\tcase $cc_basename in\n\t  CC*)\n\t    lt_prog_compiler_wl_CXX='-Wl,'\n\t    lt_prog_compiler_static_CXX='-non_shared'\n\t    # CC pic flag -KPIC is the default.\n\t    ;;\n\t  *)\n\t    ;;\n\tesac\n\t;;\n      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)\n\tcase $cc_basename in\n\t  KCC*)\n\t    # KAI C++ Compiler\n\t    lt_prog_compiler_wl_CXX='--backend -Wl,'\n\t    lt_prog_compiler_pic_CXX='-fPIC'\n\t    ;;\n\t  ecpc* )\n\t    # old Intel C++ for x86_64, which still supported -KPIC.\n\t    lt_prog_compiler_wl_CXX='-Wl,'\n\t    lt_prog_compiler_pic_CXX='-KPIC'\n\t    lt_prog_compiler_static_CXX='-static'\n\t    ;;\n\t  icpc* )\n\t    # Intel C++, used to be incompatible with GCC.\n\t    # ICC 10 doesn't accept -KPIC any more.\n\t    lt_prog_compiler_wl_CXX='-Wl,'\n\t    lt_prog_compiler_pic_CXX='-fPIC'\n\t    lt_prog_compiler_static_CXX='-static'\n\t    ;;\n\t  pgCC* | pgcpp*)\n\t    # Portland Group C++ compiler\n\t    lt_prog_compiler_wl_CXX='-Wl,'\n\t    lt_prog_compiler_pic_CXX='-fpic'\n\t    lt_prog_compiler_static_CXX='-Bstatic'\n\t    ;;\n\t  cxx*)\n\t    # Compaq C++\n\t    # Make sure the PIC flag is empty.  It appears that all Alpha\n\t    # Linux and Compaq Tru64 Unix objects are PIC.\n\t    lt_prog_compiler_pic_CXX=\n\t    lt_prog_compiler_static_CXX='-non_shared'\n\t    ;;\n\t  xlc* | xlC* | bgxl[cC]* | mpixl[cC]*)\n\t    # IBM XL 8.0, 9.0 on PPC and BlueGene\n\t    lt_prog_compiler_wl_CXX='-Wl,'\n\t    lt_prog_compiler_pic_CXX='-qpic'\n\t    lt_prog_compiler_static_CXX='-qstaticlink'\n\t    ;;\n\t  *)\n\t    case `$CC -V 2>&1 | sed 5q` in\n\t    *Sun\\ C*)\n\t      # Sun C++ 5.9\n\t      lt_prog_compiler_pic_CXX='-KPIC'\n\t      lt_prog_compiler_static_CXX='-Bstatic'\n\t      lt_prog_compiler_wl_CXX='-Qoption ld '\n\t      ;;\n\t    esac\n\t    ;;\n\tesac\n\t;;\n      lynxos*)\n\t;;\n      m88k*)\n\t;;\n      mvs*)\n\tcase $cc_basename in\n\t  cxx*)\n\t    lt_prog_compiler_pic_CXX='-W c,exportall'\n\t    ;;\n\t  *)\n\t    ;;\n\tesac\n\t;;\n      netbsd* | netbsdelf*-gnu)\n\t;;\n      *qnx* | *nto*)\n        # QNX uses GNU C++, but need to define -shared option too, otherwise\n        # it will coredump.\n        lt_prog_compiler_pic_CXX='-fPIC -shared'\n        ;;\n      osf3* | osf4* | osf5*)\n\tcase $cc_basename in\n\t  KCC*)\n\t    lt_prog_compiler_wl_CXX='--backend -Wl,'\n\t    ;;\n\t  RCC*)\n\t    # Rational C++ 2.4.1\n\t    lt_prog_compiler_pic_CXX='-pic'\n\t    ;;\n\t  cxx*)\n\t    # Digital/Compaq C++\n\t    lt_prog_compiler_wl_CXX='-Wl,'\n\t    # Make sure the PIC flag is empty.  It appears that all Alpha\n\t    # Linux and Compaq Tru64 Unix objects are PIC.\n\t    lt_prog_compiler_pic_CXX=\n\t    lt_prog_compiler_static_CXX='-non_shared'\n\t    ;;\n\t  *)\n\t    ;;\n\tesac\n\t;;\n      psos*)\n\t;;\n      solaris*)\n\tcase $cc_basename in\n\t  CC* | sunCC*)\n\t    # Sun C++ 4.2, 5.x and Centerline C++\n\t    lt_prog_compiler_pic_CXX='-KPIC'\n\t    lt_prog_compiler_static_CXX='-Bstatic'\n\t    lt_prog_compiler_wl_CXX='-Qoption ld '\n\t    ;;\n\t  gcx*)\n\t    # Green Hills C++ Compiler\n\t    lt_prog_compiler_pic_CXX='-PIC'\n\t    ;;\n\t  *)\n\t    ;;\n\tesac\n\t;;\n      sunos4*)\n\tcase $cc_basename in\n\t  CC*)\n\t    # Sun C++ 4.x\n\t    lt_prog_compiler_pic_CXX='-pic'\n\t    lt_prog_compiler_static_CXX='-Bstatic'\n\t    ;;\n\t  lcc*)\n\t    # Lucid\n\t    lt_prog_compiler_pic_CXX='-pic'\n\t    ;;\n\t  *)\n\t    ;;\n\tesac\n\t;;\n      sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)\n\tcase $cc_basename in\n\t  CC*)\n\t    lt_prog_compiler_wl_CXX='-Wl,'\n\t    lt_prog_compiler_pic_CXX='-KPIC'\n\t    lt_prog_compiler_static_CXX='-Bstatic'\n\t    ;;\n\tesac\n\t;;\n      tandem*)\n\tcase $cc_basename in\n\t  NCC*)\n\t    # NonStop-UX NCC 3.20\n\t    lt_prog_compiler_pic_CXX='-KPIC'\n\t    ;;\n\t  *)\n\t    ;;\n\tesac\n\t;;\n      vxworks*)\n\t;;\n      *)\n\tlt_prog_compiler_can_build_shared_CXX=no\n\t;;\n    esac\n  fi\n\ncase $host_os in\n  # For platforms that do not support PIC, -DPIC is meaningless:\n  *djgpp*)\n    lt_prog_compiler_pic_CXX=\n    ;;\n  *)\n    lt_prog_compiler_pic_CXX=\"$lt_prog_compiler_pic_CXX -DPIC\"\n    ;;\nesac\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC\" >&5\n$as_echo_n \"checking for $compiler option to produce PIC... \" >&6; }\nif ${lt_cv_prog_compiler_pic_CXX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX\" >&5\n$as_echo \"$lt_cv_prog_compiler_pic_CXX\" >&6; }\nlt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX\n\n#\n# Check to make sure the PIC flag actually works.\n#\nif test -n \"$lt_prog_compiler_pic_CXX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works\" >&5\n$as_echo_n \"checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... \" >&6; }\nif ${lt_cv_prog_compiler_pic_works_CXX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_pic_works_CXX=no\n   ac_outfile=conftest.$ac_objext\n   echo \"$lt_simple_compile_test_code\" > conftest.$ac_ext\n   lt_compiler_flag=\"$lt_prog_compiler_pic_CXX -DPIC\"  ## exclude from sc_useless_quotes_in_assignment\n   # Insert the option either (1) after the last *FLAGS variable, or\n   # (2) before a word containing \"conftest.\", or (3) at the end.\n   # Note that $ac_compile itself does not contain backslashes and begins\n   # with a dollar sign (not a hyphen), so the echo should work correctly.\n   # The option is referenced via a variable to avoid confusing sed.\n   lt_compile=`echo \"$ac_compile\" | $SED \\\n   -e 's:.*FLAGS}\\{0,1\\} :&$lt_compiler_flag :; t' \\\n   -e 's: [^ ]*conftest\\.: $lt_compiler_flag&:; t' \\\n   -e 's:$: $lt_compiler_flag:'`\n   (eval echo \"\\\"\\$as_me:$LINENO: $lt_compile\\\"\" >&5)\n   (eval \"$lt_compile\" 2>conftest.err)\n   ac_status=$?\n   cat conftest.err >&5\n   echo \"$as_me:$LINENO: \\$? = $ac_status\" >&5\n   if (exit $ac_status) && test -s \"$ac_outfile\"; then\n     # The compiler can only warn and ignore the option if not recognized\n     # So say no if there are warnings other than the usual output.\n     $ECHO \"$_lt_compiler_boilerplate\" | $SED '/^$/d' >conftest.exp\n     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2\n     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then\n       lt_cv_prog_compiler_pic_works_CXX=yes\n     fi\n   fi\n   $RM conftest*\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX\" >&5\n$as_echo \"$lt_cv_prog_compiler_pic_works_CXX\" >&6; }\n\nif test yes = \"$lt_cv_prog_compiler_pic_works_CXX\"; then\n    case $lt_prog_compiler_pic_CXX in\n     \"\" | \" \"*) ;;\n     *) lt_prog_compiler_pic_CXX=\" $lt_prog_compiler_pic_CXX\" ;;\n     esac\nelse\n    lt_prog_compiler_pic_CXX=\n     lt_prog_compiler_can_build_shared_CXX=no\nfi\n\nfi\n\n\n\n\n\n#\n# Check to make sure the static flag actually works.\n#\nwl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\\\"$lt_prog_compiler_static_CXX\\\"\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works\" >&5\n$as_echo_n \"checking if $compiler static flag $lt_tmp_static_flag works... \" >&6; }\nif ${lt_cv_prog_compiler_static_works_CXX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_static_works_CXX=no\n   save_LDFLAGS=$LDFLAGS\n   LDFLAGS=\"$LDFLAGS $lt_tmp_static_flag\"\n   echo \"$lt_simple_link_test_code\" > conftest.$ac_ext\n   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then\n     # The linker can only warn and ignore the option if not recognized\n     # So say no if there are warnings\n     if test -s conftest.err; then\n       # Append any errors to the config.log.\n       cat conftest.err 1>&5\n       $ECHO \"$_lt_linker_boilerplate\" | $SED '/^$/d' > conftest.exp\n       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2\n       if diff conftest.exp conftest.er2 >/dev/null; then\n         lt_cv_prog_compiler_static_works_CXX=yes\n       fi\n     else\n       lt_cv_prog_compiler_static_works_CXX=yes\n     fi\n   fi\n   $RM -r conftest*\n   LDFLAGS=$save_LDFLAGS\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX\" >&5\n$as_echo \"$lt_cv_prog_compiler_static_works_CXX\" >&6; }\n\nif test yes = \"$lt_cv_prog_compiler_static_works_CXX\"; then\n    :\nelse\n    lt_prog_compiler_static_CXX=\nfi\n\n\n\n\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext\" >&5\n$as_echo_n \"checking if $compiler supports -c -o file.$ac_objext... \" >&6; }\nif ${lt_cv_prog_compiler_c_o_CXX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_c_o_CXX=no\n   $RM -r conftest 2>/dev/null\n   mkdir conftest\n   cd conftest\n   mkdir out\n   echo \"$lt_simple_compile_test_code\" > conftest.$ac_ext\n\n   lt_compiler_flag=\"-o out/conftest2.$ac_objext\"\n   # Insert the option either (1) after the last *FLAGS variable, or\n   # (2) before a word containing \"conftest.\", or (3) at the end.\n   # Note that $ac_compile itself does not contain backslashes and begins\n   # with a dollar sign (not a hyphen), so the echo should work correctly.\n   lt_compile=`echo \"$ac_compile\" | $SED \\\n   -e 's:.*FLAGS}\\{0,1\\} :&$lt_compiler_flag :; t' \\\n   -e 's: [^ ]*conftest\\.: $lt_compiler_flag&:; t' \\\n   -e 's:$: $lt_compiler_flag:'`\n   (eval echo \"\\\"\\$as_me:$LINENO: $lt_compile\\\"\" >&5)\n   (eval \"$lt_compile\" 2>out/conftest.err)\n   ac_status=$?\n   cat out/conftest.err >&5\n   echo \"$as_me:$LINENO: \\$? = $ac_status\" >&5\n   if (exit $ac_status) && test -s out/conftest2.$ac_objext\n   then\n     # The compiler can only warn and ignore the option if not recognized\n     # So say no if there are warnings\n     $ECHO \"$_lt_compiler_boilerplate\" | $SED '/^$/d' > out/conftest.exp\n     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2\n     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then\n       lt_cv_prog_compiler_c_o_CXX=yes\n     fi\n   fi\n   chmod u+w . 2>&5\n   $RM conftest*\n   # SGI C++ compiler will create directory out/ii_files/ for\n   # template instantiation\n   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files\n   $RM out/* && rmdir out\n   cd ..\n   $RM -r conftest\n   $RM conftest*\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX\" >&5\n$as_echo \"$lt_cv_prog_compiler_c_o_CXX\" >&6; }\n\n\n\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext\" >&5\n$as_echo_n \"checking if $compiler supports -c -o file.$ac_objext... \" >&6; }\nif ${lt_cv_prog_compiler_c_o_CXX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_prog_compiler_c_o_CXX=no\n   $RM -r conftest 2>/dev/null\n   mkdir conftest\n   cd conftest\n   mkdir out\n   echo \"$lt_simple_compile_test_code\" > conftest.$ac_ext\n\n   lt_compiler_flag=\"-o out/conftest2.$ac_objext\"\n   # Insert the option either (1) after the last *FLAGS variable, or\n   # (2) before a word containing \"conftest.\", or (3) at the end.\n   # Note that $ac_compile itself does not contain backslashes and begins\n   # with a dollar sign (not a hyphen), so the echo should work correctly.\n   lt_compile=`echo \"$ac_compile\" | $SED \\\n   -e 's:.*FLAGS}\\{0,1\\} :&$lt_compiler_flag :; t' \\\n   -e 's: [^ ]*conftest\\.: $lt_compiler_flag&:; t' \\\n   -e 's:$: $lt_compiler_flag:'`\n   (eval echo \"\\\"\\$as_me:$LINENO: $lt_compile\\\"\" >&5)\n   (eval \"$lt_compile\" 2>out/conftest.err)\n   ac_status=$?\n   cat out/conftest.err >&5\n   echo \"$as_me:$LINENO: \\$? = $ac_status\" >&5\n   if (exit $ac_status) && test -s out/conftest2.$ac_objext\n   then\n     # The compiler can only warn and ignore the option if not recognized\n     # So say no if there are warnings\n     $ECHO \"$_lt_compiler_boilerplate\" | $SED '/^$/d' > out/conftest.exp\n     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2\n     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then\n       lt_cv_prog_compiler_c_o_CXX=yes\n     fi\n   fi\n   chmod u+w . 2>&5\n   $RM conftest*\n   # SGI C++ compiler will create directory out/ii_files/ for\n   # template instantiation\n   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files\n   $RM out/* && rmdir out\n   cd ..\n   $RM -r conftest\n   $RM conftest*\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX\" >&5\n$as_echo \"$lt_cv_prog_compiler_c_o_CXX\" >&6; }\n\n\n\n\nhard_links=nottested\nif test no = \"$lt_cv_prog_compiler_c_o_CXX\" && test no != \"$need_locks\"; then\n  # do not overwrite the value of need_locks provided by the user\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links\" >&5\n$as_echo_n \"checking if we can lock with hard links... \" >&6; }\n  hard_links=yes\n  $RM conftest*\n  ln conftest.a conftest.b 2>/dev/null && hard_links=no\n  touch conftest.a\n  ln conftest.a conftest.b 2>&5 || hard_links=no\n  ln conftest.a conftest.b 2>/dev/null && hard_links=no\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $hard_links\" >&5\n$as_echo \"$hard_links\" >&6; }\n  if test no = \"$hard_links\"; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe\" >&5\n$as_echo \"$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe\" >&2;}\n    need_locks=warn\n  fi\nelse\n  need_locks=no\nfi\n\n\n\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries\" >&5\n$as_echo_n \"checking whether the $compiler linker ($LD) supports shared libraries... \" >&6; }\n\n  export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\\''s/.* //'\\'' | sort | uniq > $export_symbols'\n  exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'\n  case $host_os in\n  aix[4-9]*)\n    # If we're using GNU nm, then we don't want the \"-C\" option.\n    # -C means demangle to GNU nm, but means don't demangle to AIX nm.\n    # Without the \"-l\" option, or with the \"-B\" option, AIX nm treats\n    # weak defined symbols like other global defined symbols, whereas\n    # GNU nm marks them as \"W\".\n    # While the 'weak' keyword is ignored in the Export File, we need\n    # it in the Import File for the 'aix-soname' feature, so we have\n    # to replace the \"-B\" option with \"-P\" for AIX nm.\n    if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then\n      export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\\''{ if (((\\$ 2 == \"T\") || (\\$ 2 == \"D\") || (\\$ 2 == \"B\") || (\\$ 2 == \"W\")) && (substr(\\$ 3,1,1) != \".\")) { if (\\$ 2 == \"W\") { print \\$ 3 \" weak\" } else { print \\$ 3 } } }'\\'' | sort -u > $export_symbols'\n    else\n      export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\\''s/B\\([^B]*\\)$/P\\1/'\\''` -PCpgl $libobjs $convenience | awk '\\''{ if (((\\$ 2 == \"T\") || (\\$ 2 == \"D\") || (\\$ 2 == \"B\") || (\\$ 2 == \"W\") || (\\$ 2 == \"V\") || (\\$ 2 == \"Z\")) && (substr(\\$ 1,1,1) != \".\")) { if ((\\$ 2 == \"W\") || (\\$ 2 == \"V\") || (\\$ 2 == \"Z\")) { print \\$ 1 \" weak\" } else { print \\$ 1 } } }'\\'' | sort -u > $export_symbols'\n    fi\n    ;;\n  pw32*)\n    export_symbols_cmds_CXX=$ltdll_cmds\n    ;;\n  cygwin* | mingw* | cegcc*)\n    case $cc_basename in\n    cl*)\n      exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'\n      ;;\n    *)\n      export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\\''/^[BCDGRS][ ]/s/.*[ ]\\([^ ]*\\)/\\1 DATA/;s/^.*[ ]__nm__\\([^ ]*\\)[ ][^ ]*/\\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\\'' | sort | uniq > $export_symbols'\n      exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'\n      ;;\n    esac\n    ;;\n  linux* | k*bsd*-gnu | gnu*)\n    link_all_deplibs_CXX=no\n    ;;\n  *)\n    export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\\''s/.* //'\\'' | sort | uniq > $export_symbols'\n    ;;\n  esac\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX\" >&5\n$as_echo \"$ld_shlibs_CXX\" >&6; }\ntest no = \"$ld_shlibs_CXX\" && can_build_shared=no\n\nwith_gnu_ld_CXX=$with_gnu_ld\n\n\n\n\n\n\n#\n# Do we need to explicitly link libc?\n#\ncase \"x$archive_cmds_need_lc_CXX\" in\nx|xyes)\n  # Assume -lc should be added\n  archive_cmds_need_lc_CXX=yes\n\n  if test yes,yes = \"$GCC,$enable_shared\"; then\n    case $archive_cmds_CXX in\n    *'~'*)\n      # FIXME: we may have to deal with multi-command sequences.\n      ;;\n    '$CC '*)\n      # Test whether the compiler implicitly links with -lc since on some\n      # systems, -lgcc has to come before -lc. If gcc already passes -lc\n      # to ld, don't add -lc before -lgcc.\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in\" >&5\n$as_echo_n \"checking whether -lc should be explicitly linked in... \" >&6; }\nif ${lt_cv_archive_cmds_need_lc_CXX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  $RM conftest*\n\techo \"$lt_simple_compile_test_code\" > conftest.$ac_ext\n\n\tif { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$ac_compile\\\"\"; } >&5\n  (eval $ac_compile) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } 2>conftest.err; then\n\t  soname=conftest\n\t  lib=conftest\n\t  libobjs=conftest.$ac_objext\n\t  deplibs=\n\t  wl=$lt_prog_compiler_wl_CXX\n\t  pic_flag=$lt_prog_compiler_pic_CXX\n\t  compiler_flags=-v\n\t  linker_flags=-v\n\t  verstring=\n\t  output_objdir=.\n\t  libname=conftest\n\t  lt_save_allow_undefined_flag=$allow_undefined_flag_CXX\n\t  allow_undefined_flag_CXX=\n\t  if { { eval echo \"\\\"\\$as_me\\\":${as_lineno-$LINENO}: \\\"$archive_cmds_CXX 2\\>\\&1 \\| $GREP \\\" -lc \\\" \\>/dev/null 2\\>\\&1\\\"\"; } >&5\n  (eval $archive_cmds_CXX 2\\>\\&1 \\| $GREP \\\" -lc \\\" \\>/dev/null 2\\>\\&1) 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\n\t  then\n\t    lt_cv_archive_cmds_need_lc_CXX=no\n\t  else\n\t    lt_cv_archive_cmds_need_lc_CXX=yes\n\t  fi\n\t  allow_undefined_flag_CXX=$lt_save_allow_undefined_flag\n\telse\n\t  cat conftest.err 1>&5\n\tfi\n\t$RM conftest*\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX\" >&5\n$as_echo \"$lt_cv_archive_cmds_need_lc_CXX\" >&6; }\n      archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX\n      ;;\n    esac\n  fi\n  ;;\nesac\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\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\n\n\n\n\n\n\n\n\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics\" >&5\n$as_echo_n \"checking dynamic linker characteristics... \" >&6; }\n\nlibrary_names_spec=\nlibname_spec='lib$name'\nsoname_spec=\nshrext_cmds=.so\npostinstall_cmds=\npostuninstall_cmds=\nfinish_cmds=\nfinish_eval=\nshlibpath_var=\nshlibpath_overrides_runpath=unknown\nversion_type=none\ndynamic_linker=\"$host_os ld.so\"\nsys_lib_dlsearch_path_spec=\"/lib /usr/lib\"\nneed_lib_prefix=unknown\nhardcode_into_libs=no\n\n# when you set need_version to no, make sure it does not cause -set_version\n# flags to be left without arguments\nneed_version=unknown\n\n\n\ncase $host_os in\naix3*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  library_names_spec='$libname$release$shared_ext$versuffix $libname.a'\n  shlibpath_var=LIBPATH\n\n  # AIX 3 has no versioning support, so we append a major version to the name.\n  soname_spec='$libname$release$shared_ext$major'\n  ;;\n\naix[4-9]*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  hardcode_into_libs=yes\n  if test ia64 = \"$host_cpu\"; then\n    # AIX 5 supports IA64\n    library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'\n    shlibpath_var=LD_LIBRARY_PATH\n  else\n    # With GCC up to 2.95.x, collect2 would create an import file\n    # for dependence libraries.  The import file would start with\n    # the line '#! .'.  This would cause the generated library to\n    # depend on '.', always an invalid library.  This was fixed in\n    # development snapshots of GCC prior to 3.0.\n    case $host_os in\n      aix4 | aix4.[01] | aix4.[01].*)\n      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'\n\t   echo ' yes '\n\t   echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then\n\t:\n      else\n\tcan_build_shared=no\n      fi\n      ;;\n    esac\n    # Using Import Files as archive members, it is possible to support\n    # filename-based versioning of shared library archives on AIX. While\n    # this would work for both with and without runtime linking, it will\n    # prevent static linking of such archives. So we do filename-based\n    # shared library versioning with .so extension only, which is used\n    # when both runtime linking and shared linking is enabled.\n    # Unfortunately, runtime linking may impact performance, so we do\n    # not want this to be the default eventually. Also, we use the\n    # versioned .so libs for executables only if there is the -brtl\n    # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.\n    # To allow for filename-based versioning support, we need to create\n    # libNAME.so.V as an archive file, containing:\n    # *) an Import File, referring to the versioned filename of the\n    #    archive as well as the shared archive member, telling the\n    #    bitwidth (32 or 64) of that shared object, and providing the\n    #    list of exported symbols of that shared object, eventually\n    #    decorated with the 'weak' keyword\n    # *) the shared object with the F_LOADONLY flag set, to really avoid\n    #    it being seen by the linker.\n    # At run time we better use the real file rather than another symlink,\n    # but for link time we create the symlink libNAME.so -> libNAME.so.V\n\n    case $with_aix_soname,$aix_use_runtimelinking in\n    # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct\n    # soname into executable. Probably we can add versioning support to\n    # collect2, so additional links can be useful in future.\n    aix,yes) # traditional libtool\n      dynamic_linker='AIX unversionable lib.so'\n      # If using run time linking (on AIX 4.2 or later) use lib<name>.so\n      # instead of lib<name>.a to let people know that these are not\n      # typical AIX shared libraries.\n      library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n      ;;\n    aix,no) # traditional AIX only\n      dynamic_linker='AIX lib.a(lib.so.V)'\n      # We preserve .a as extension for shared libraries through AIX4.2\n      # and later when we are not doing run time linking.\n      library_names_spec='$libname$release.a $libname.a'\n      soname_spec='$libname$release$shared_ext$major'\n      ;;\n    svr4,*) # full svr4 only\n      dynamic_linker=\"AIX lib.so.V($shared_archive_member_spec.o)\"\n      library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'\n      # We do not specify a path in Import Files, so LIBPATH fires.\n      shlibpath_overrides_runpath=yes\n      ;;\n    *,yes) # both, prefer svr4\n      dynamic_linker=\"AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)\"\n      library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'\n      # unpreferred sharedlib libNAME.a needs extra handling\n      postinstall_cmds='test -n \"$linkname\" || linkname=\"$realname\"~func_stripname \"\" \".so\" \"$linkname\"~$install_shared_prog \"$dir/$func_stripname_result.$libext\" \"$destdir/$func_stripname_result.$libext\"~test -z \"$tstripme\" || test -z \"$striplib\" || $striplib \"$destdir/$func_stripname_result.$libext\"'\n      postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname \"\" \".so\" \"$n\"~test \"$func_stripname_result\" = \"$n\" || func_append rmfiles \" $odir/$func_stripname_result.$libext\"'\n      # We do not specify a path in Import Files, so LIBPATH fires.\n      shlibpath_overrides_runpath=yes\n      ;;\n    *,no) # both, prefer aix\n      dynamic_linker=\"AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)\"\n      library_names_spec='$libname$release.a $libname.a'\n      soname_spec='$libname$release$shared_ext$major'\n      # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling\n      postinstall_cmds='test -z \"$dlname\" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z \"$tstripme\" || test -z \"$striplib\" || $striplib $destdir/$dlname~test -n \"$linkname\" || linkname=$realname~func_stripname \"\" \".a\" \"$linkname\"~(cd \"$destdir\" && $LN_S -f $dlname $func_stripname_result.so)'\n      postuninstall_cmds='test -z \"$dlname\" || func_append rmfiles \" $odir/$dlname\"~for n in $old_library $library_names; do :; done~func_stripname \"\" \".a\" \"$n\"~func_append rmfiles \" $odir/$func_stripname_result.so\"'\n      ;;\n    esac\n    shlibpath_var=LIBPATH\n  fi\n  ;;\n\namigaos*)\n  case $host_cpu in\n  powerpc)\n    # Since July 2007 AmigaOS4 officially supports .so libraries.\n    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n    ;;\n  m68k)\n    library_names_spec='$libname.ixlibrary $libname.a'\n    # Create ${libname}_ixlibrary.a entries in /sys/libs.\n    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all \"$lib\" | $SED '\\''s%^.*/\\([^/]*\\)\\.ixlibrary$%\\1%'\\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show \"cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a\"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'\n    ;;\n  esac\n  ;;\n\nbeos*)\n  library_names_spec='$libname$shared_ext'\n  dynamic_linker=\"$host_os ld.so\"\n  shlibpath_var=LIBRARY_PATH\n  ;;\n\nbsdi[45]*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  finish_cmds='PATH=\"\\$PATH:/sbin\" ldconfig $libdir'\n  shlibpath_var=LD_LIBRARY_PATH\n  sys_lib_search_path_spec=\"/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib\"\n  sys_lib_dlsearch_path_spec=\"/shlib /usr/lib /usr/local/lib\"\n  # the default ld.so.conf also contains /usr/contrib/lib and\n  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow\n  # libtool to hard-code these into programs\n  ;;\n\ncygwin* | mingw* | pw32* | cegcc*)\n  version_type=windows\n  shrext_cmds=.dll\n  need_version=no\n  need_lib_prefix=no\n\n  case $GCC,$cc_basename in\n  yes,*)\n    # gcc\n    library_names_spec='$libname.dll.a'\n    # DLL is installed to $(libdir)/../bin by postinstall_cmds\n    postinstall_cmds='base_file=`basename \\$file`~\n      dlpath=`$SHELL 2>&1 -c '\\''. $dir/'\\''\\$base_file'\\''i; echo \\$dlname'\\''`~\n      dldir=$destdir/`dirname \\$dlpath`~\n      test -d \\$dldir || mkdir -p \\$dldir~\n      $install_prog $dir/$dlname \\$dldir/$dlname~\n      chmod a+x \\$dldir/$dlname~\n      if test -n '\\''$stripme'\\'' && test -n '\\''$striplib'\\''; then\n        eval '\\''$striplib \\$dldir/$dlname'\\'' || exit \\$?;\n      fi'\n    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\\''. $file; echo \\$dlname'\\''`~\n      dlpath=$dir/\\$dldll~\n       $RM \\$dlpath'\n    shlibpath_overrides_runpath=yes\n\n    case $host_os in\n    cygwin*)\n      # Cygwin DLLs use 'cyg' prefix rather than 'lib'\n      soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'\n\n      ;;\n    mingw* | cegcc*)\n      # MinGW DLLs use traditional 'lib' prefix\n      soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'\n      ;;\n    pw32*)\n      # pw32 DLLs use 'pw' prefix rather than 'lib'\n      library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'\n      ;;\n    esac\n    dynamic_linker='Win32 ld.exe'\n    ;;\n\n  *,cl*)\n    # Native MSVC\n    libname_spec='$name'\n    soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'\n    library_names_spec='$libname.dll.lib'\n\n    case $build_os in\n    mingw*)\n      sys_lib_search_path_spec=\n      lt_save_ifs=$IFS\n      IFS=';'\n      for lt_path in $LIB\n      do\n        IFS=$lt_save_ifs\n        # Let DOS variable expansion print the short 8.3 style file name.\n        lt_path=`cd \"$lt_path\" 2>/dev/null && cmd //C \"for %i in (\".\") do @echo %~si\"`\n        sys_lib_search_path_spec=\"$sys_lib_search_path_spec $lt_path\"\n      done\n      IFS=$lt_save_ifs\n      # Convert to MSYS style.\n      sys_lib_search_path_spec=`$ECHO \"$sys_lib_search_path_spec\" | sed -e 's|\\\\\\\\|/|g' -e 's| \\\\([a-zA-Z]\\\\):| /\\\\1|g' -e 's|^ ||'`\n      ;;\n    cygwin*)\n      # Convert to unix form, then to dos form, then back to unix form\n      # but this time dos style (no spaces!) so that the unix form looks\n      # like /cygdrive/c/PROGRA~1:/cygdr...\n      sys_lib_search_path_spec=`cygpath --path --unix \"$LIB\"`\n      sys_lib_search_path_spec=`cygpath --path --dos \"$sys_lib_search_path_spec\" 2>/dev/null`\n      sys_lib_search_path_spec=`cygpath --path --unix \"$sys_lib_search_path_spec\" | $SED -e \"s/$PATH_SEPARATOR/ /g\"`\n      ;;\n    *)\n      sys_lib_search_path_spec=$LIB\n      if $ECHO \"$sys_lib_search_path_spec\" | $GREP ';[c-zC-Z]:/' >/dev/null; then\n        # It is most probably a Windows format PATH.\n        sys_lib_search_path_spec=`$ECHO \"$sys_lib_search_path_spec\" | $SED -e 's/;/ /g'`\n      else\n        sys_lib_search_path_spec=`$ECHO \"$sys_lib_search_path_spec\" | $SED -e \"s/$PATH_SEPARATOR/ /g\"`\n      fi\n      # FIXME: find the short name or the path components, as spaces are\n      # common. (e.g. \"Program Files\" -> \"PROGRA~1\")\n      ;;\n    esac\n\n    # DLL is installed to $(libdir)/../bin by postinstall_cmds\n    postinstall_cmds='base_file=`basename \\$file`~\n      dlpath=`$SHELL 2>&1 -c '\\''. $dir/'\\''\\$base_file'\\''i; echo \\$dlname'\\''`~\n      dldir=$destdir/`dirname \\$dlpath`~\n      test -d \\$dldir || mkdir -p \\$dldir~\n      $install_prog $dir/$dlname \\$dldir/$dlname'\n    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\\''. $file; echo \\$dlname'\\''`~\n      dlpath=$dir/\\$dldll~\n       $RM \\$dlpath'\n    shlibpath_overrides_runpath=yes\n    dynamic_linker='Win32 link.exe'\n    ;;\n\n  *)\n    # Assume MSVC wrapper\n    library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'\n    dynamic_linker='Win32 ld.exe'\n    ;;\n  esac\n  # FIXME: first we should search . and the directory the executable is in\n  shlibpath_var=PATH\n  ;;\n\ndarwin* | rhapsody*)\n  dynamic_linker=\"$host_os dyld\"\n  version_type=darwin\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'\n  soname_spec='$libname$release$major$shared_ext'\n  shlibpath_overrides_runpath=yes\n  shlibpath_var=DYLD_LIBRARY_PATH\n  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'\n\n  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'\n  ;;\n\ndgux*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  ;;\n\nfreebsd* | dragonfly*)\n  # DragonFly does not have aout.  When/if they implement a new\n  # versioning mechanism, adjust this.\n  if test -x /usr/bin/objformat; then\n    objformat=`/usr/bin/objformat`\n  else\n    case $host_os in\n    freebsd[23].*) objformat=aout ;;\n    *) objformat=elf ;;\n    esac\n  fi\n  version_type=freebsd-$objformat\n  case $version_type in\n    freebsd-elf*)\n      library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n      soname_spec='$libname$release$shared_ext$major'\n      need_version=no\n      need_lib_prefix=no\n      ;;\n    freebsd-*)\n      library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'\n      need_version=yes\n      ;;\n  esac\n  shlibpath_var=LD_LIBRARY_PATH\n  case $host_os in\n  freebsd2.*)\n    shlibpath_overrides_runpath=yes\n    ;;\n  freebsd3.[01]* | freebsdelf3.[01]*)\n    shlibpath_overrides_runpath=yes\n    hardcode_into_libs=yes\n    ;;\n  freebsd3.[2-9]* | freebsdelf3.[2-9]* | \\\n  freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)\n    shlibpath_overrides_runpath=no\n    hardcode_into_libs=yes\n    ;;\n  *) # from 4.6 on, and DragonFly\n    shlibpath_overrides_runpath=yes\n    hardcode_into_libs=yes\n    ;;\n  esac\n  ;;\n\nhaiku*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  dynamic_linker=\"$host_os runtime_loader\"\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'\n  hardcode_into_libs=yes\n  ;;\n\nhpux9* | hpux10* | hpux11*)\n  # Give a soname corresponding to the major version so that dld.sl refuses to\n  # link against other versions.\n  version_type=sunos\n  need_lib_prefix=no\n  need_version=no\n  case $host_cpu in\n  ia64*)\n    shrext_cmds='.so'\n    hardcode_into_libs=yes\n    dynamic_linker=\"$host_os dld.so\"\n    shlibpath_var=LD_LIBRARY_PATH\n    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n    soname_spec='$libname$release$shared_ext$major'\n    if test 32 = \"$HPUX_IA64_MODE\"; then\n      sys_lib_search_path_spec=\"/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib\"\n      sys_lib_dlsearch_path_spec=/usr/lib/hpux32\n    else\n      sys_lib_search_path_spec=\"/usr/lib/hpux64 /usr/local/lib/hpux64\"\n      sys_lib_dlsearch_path_spec=/usr/lib/hpux64\n    fi\n    ;;\n  hppa*64*)\n    shrext_cmds='.sl'\n    hardcode_into_libs=yes\n    dynamic_linker=\"$host_os dld.sl\"\n    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH\n    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n    soname_spec='$libname$release$shared_ext$major'\n    sys_lib_search_path_spec=\"/usr/lib/pa20_64 /usr/ccs/lib/pa20_64\"\n    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec\n    ;;\n  *)\n    shrext_cmds='.sl'\n    dynamic_linker=\"$host_os dld.sl\"\n    shlibpath_var=SHLIB_PATH\n    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n    soname_spec='$libname$release$shared_ext$major'\n    ;;\n  esac\n  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...\n  postinstall_cmds='chmod 555 $lib'\n  # or fails outright, so override atomically:\n  install_override_mode=555\n  ;;\n\ninterix[3-9]*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n  hardcode_into_libs=yes\n  ;;\n\nirix5* | irix6* | nonstopux*)\n  case $host_os in\n    nonstopux*) version_type=nonstopux ;;\n    *)\n\tif test yes = \"$lt_cv_prog_gnu_ld\"; then\n\t\tversion_type=linux # correct to gnu/linux during the next big refactor\n\telse\n\t\tversion_type=irix\n\tfi ;;\n  esac\n  need_lib_prefix=no\n  need_version=no\n  soname_spec='$libname$release$shared_ext$major'\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'\n  case $host_os in\n  irix5* | nonstopux*)\n    libsuff= shlibsuff=\n    ;;\n  *)\n    case $LD in # libtool.m4 will add one of these switches to LD\n    *-32|*\"-32 \"|*-melf32bsmip|*\"-melf32bsmip \")\n      libsuff= shlibsuff= libmagic=32-bit;;\n    *-n32|*\"-n32 \"|*-melf32bmipn32|*\"-melf32bmipn32 \")\n      libsuff=32 shlibsuff=N32 libmagic=N32;;\n    *-64|*\"-64 \"|*-melf64bmip|*\"-melf64bmip \")\n      libsuff=64 shlibsuff=64 libmagic=64-bit;;\n    *) libsuff= shlibsuff= libmagic=never-match;;\n    esac\n    ;;\n  esac\n  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH\n  shlibpath_overrides_runpath=no\n  sys_lib_search_path_spec=\"/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff\"\n  sys_lib_dlsearch_path_spec=\"/usr/lib$libsuff /lib$libsuff\"\n  hardcode_into_libs=yes\n  ;;\n\n# No shared lib support for Linux oldld, aout, or coff.\nlinux*oldld* | linux*aout* | linux*coff*)\n  dynamic_linker=no\n  ;;\n\nlinux*android*)\n  version_type=none # Android doesn't support versioned libraries.\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext'\n  soname_spec='$libname$release$shared_ext'\n  finish_cmds=\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n\n  # This implies no fast_install, which is unacceptable.\n  # Some rework will be needed to allow for fast_install\n  # before this can be enabled.\n  hardcode_into_libs=yes\n\n  dynamic_linker='Android linker'\n  # Don't embed -rpath directories since the linker doesn't support them.\n  hardcode_libdir_flag_spec_CXX='-L$libdir'\n  ;;\n\n# This must be glibc/ELF.\nlinux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  finish_cmds='PATH=\"\\$PATH:/sbin\" ldconfig -n $libdir'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n\n  # Some binutils ld are patched to set DT_RUNPATH\n  if ${lt_cv_shlibpath_overrides_runpath+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  lt_cv_shlibpath_overrides_runpath=no\n    save_LDFLAGS=$LDFLAGS\n    save_libdir=$libdir\n    eval \"libdir=/foo; wl=\\\"$lt_prog_compiler_wl_CXX\\\"; \\\n\t LDFLAGS=\\\"\\$LDFLAGS $hardcode_libdir_flag_spec_CXX\\\"\"\n    cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_cxx_try_link \"$LINENO\"; then :\n  if  ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep \"RUNPATH.*$libdir\" >/dev/null; then :\n  lt_cv_shlibpath_overrides_runpath=yes\nfi\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\n    LDFLAGS=$save_LDFLAGS\n    libdir=$save_libdir\n\nfi\n\n  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath\n\n  # This implies no fast_install, which is unacceptable.\n  # Some rework will be needed to allow for fast_install\n  # before this can be enabled.\n  hardcode_into_libs=yes\n\n  # Ideally, we could use ldconfig to report *all* directores which are\n  # searched for libraries, however this is still not possible.  Aside from not\n  # being certain /sbin/ldconfig is available, command\n  # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,\n  # even though it is searched at run-time.  Try to do the best guess by\n  # appending ld.so.conf contents (and includes) to the search path.\n  if test -f /etc/ld.so.conf; then\n    lt_ld_extra=`awk '/^include / { system(sprintf(\"cd /etc; cat %s 2>/dev/null\", \\$2)); skip = 1; } { if (!skip) print \\$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[\t ]*hwcap[\t ]/d;s/[:,\t]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/\"//g;/^$/d' | tr '\\n' ' '`\n    sys_lib_dlsearch_path_spec=\"/lib /usr/lib $lt_ld_extra\"\n  fi\n\n  # We used to test for /lib/ld.so.1 and disable shared libraries on\n  # powerpc, because MkLinux only supported shared libraries with the\n  # GNU dynamic linker.  Since this was broken with cross compilers,\n  # most powerpc-linux boxes support dynamic linking these days and\n  # people can always --disable-shared, the test was removed, and we\n  # assume the GNU/Linux dynamic linker is in use.\n  dynamic_linker='GNU/Linux ld.so'\n  ;;\n\nnetbsdelf*-gnu)\n  version_type=linux\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'\n  soname_spec='${libname}${release}${shared_ext}$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n  hardcode_into_libs=yes\n  dynamic_linker='NetBSD ld.elf_so'\n  ;;\n\nnetbsd*)\n  version_type=sunos\n  need_lib_prefix=no\n  need_version=no\n  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'\n    finish_cmds='PATH=\"\\$PATH:/sbin\" ldconfig -m $libdir'\n    dynamic_linker='NetBSD (a.out) ld.so'\n  else\n    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n    soname_spec='$libname$release$shared_ext$major'\n    dynamic_linker='NetBSD ld.elf_so'\n  fi\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  hardcode_into_libs=yes\n  ;;\n\nnewsos6)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  ;;\n\n*nto* | *qnx*)\n  version_type=qnx\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n  hardcode_into_libs=yes\n  dynamic_linker='ldqnx.so'\n  ;;\n\nopenbsd* | bitrig*)\n  version_type=sunos\n  sys_lib_dlsearch_path_spec=/usr/lib\n  need_lib_prefix=no\n  if test -z \"`echo __ELF__ | $CC -E - | $GREP __ELF__`\"; then\n    need_version=no\n  else\n    need_version=yes\n  fi\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'\n  finish_cmds='PATH=\"\\$PATH:/sbin\" ldconfig -m $libdir'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  ;;\n\nos2*)\n  libname_spec='$name'\n  version_type=windows\n  shrext_cmds=.dll\n  need_version=no\n  need_lib_prefix=no\n  # OS/2 can only load a DLL with a base name of 8 characters or less.\n  soname_spec='`test -n \"$os2dllname\" && libname=\"$os2dllname\";\n    v=$($ECHO $release$versuffix | tr -d .-);\n    n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);\n    $ECHO $n$v`$shared_ext'\n  library_names_spec='${libname}_dll.$libext'\n  dynamic_linker='OS/2 ld.exe'\n  shlibpath_var=BEGINLIBPATH\n  sys_lib_search_path_spec=\"/lib /usr/lib /usr/local/lib\"\n  sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec\n  postinstall_cmds='base_file=`basename \\$file`~\n    dlpath=`$SHELL 2>&1 -c '\\''. $dir/'\\''\\$base_file'\\''i; $ECHO \\$dlname'\\''`~\n    dldir=$destdir/`dirname \\$dlpath`~\n    test -d \\$dldir || mkdir -p \\$dldir~\n    $install_prog $dir/$dlname \\$dldir/$dlname~\n    chmod a+x \\$dldir/$dlname~\n    if test -n '\\''$stripme'\\'' && test -n '\\''$striplib'\\''; then\n      eval '\\''$striplib \\$dldir/$dlname'\\'' || exit \\$?;\n    fi'\n  postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\\''. $file; $ECHO \\$dlname'\\''`~\n    dlpath=$dir/\\$dldll~\n    $RM \\$dlpath'\n  ;;\n\nosf3* | osf4* | osf5*)\n  version_type=osf\n  need_lib_prefix=no\n  need_version=no\n  soname_spec='$libname$release$shared_ext$major'\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  shlibpath_var=LD_LIBRARY_PATH\n  sys_lib_search_path_spec=\"/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib\"\n  sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec\n  ;;\n\nrdos*)\n  dynamic_linker=no\n  ;;\n\nsolaris*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  hardcode_into_libs=yes\n  # ldd complains unless libraries are executable\n  postinstall_cmds='chmod +x $lib'\n  ;;\n\nsunos4*)\n  version_type=sunos\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'\n  finish_cmds='PATH=\"\\$PATH:/usr/etc\" ldconfig $libdir'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  if test yes = \"$with_gnu_ld\"; then\n    need_lib_prefix=no\n  fi\n  need_version=yes\n  ;;\n\nsysv4 | sysv4.3*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  case $host_vendor in\n    sni)\n      shlibpath_overrides_runpath=no\n      need_lib_prefix=no\n      runpath_var=LD_RUN_PATH\n      ;;\n    siemens)\n      need_lib_prefix=no\n      ;;\n    motorola)\n      need_lib_prefix=no\n      need_version=no\n      shlibpath_overrides_runpath=no\n      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'\n      ;;\n  esac\n  ;;\n\nsysv4*MP*)\n  if test -d /usr/nec; then\n    version_type=linux # correct to gnu/linux during the next big refactor\n    library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'\n    soname_spec='$libname$shared_ext.$major'\n    shlibpath_var=LD_LIBRARY_PATH\n  fi\n  ;;\n\nsysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)\n  version_type=sco\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=yes\n  hardcode_into_libs=yes\n  if test yes = \"$with_gnu_ld\"; then\n    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'\n  else\n    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'\n    case $host_os in\n      sco3.2v5*)\n        sys_lib_search_path_spec=\"$sys_lib_search_path_spec /lib\"\n\t;;\n    esac\n  fi\n  sys_lib_dlsearch_path_spec='/usr/lib'\n  ;;\n\ntpf*)\n  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.\n  version_type=linux # correct to gnu/linux during the next big refactor\n  need_lib_prefix=no\n  need_version=no\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  shlibpath_var=LD_LIBRARY_PATH\n  shlibpath_overrides_runpath=no\n  hardcode_into_libs=yes\n  ;;\n\nuts4*)\n  version_type=linux # correct to gnu/linux during the next big refactor\n  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'\n  soname_spec='$libname$release$shared_ext$major'\n  shlibpath_var=LD_LIBRARY_PATH\n  ;;\n\n*)\n  dynamic_linker=no\n  ;;\nesac\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $dynamic_linker\" >&5\n$as_echo \"$dynamic_linker\" >&6; }\ntest no = \"$dynamic_linker\" && can_build_shared=no\n\nvariables_saved_for_relink=\"PATH $shlibpath_var $runpath_var\"\nif test yes = \"$GCC\"; then\n  variables_saved_for_relink=\"$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH\"\nfi\n\nif test set = \"${lt_cv_sys_lib_search_path_spec+set}\"; then\n  sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec\nfi\n\nif test set = \"${lt_cv_sys_lib_dlsearch_path_spec+set}\"; then\n  sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec\nfi\n\n# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...\nconfigure_time_dlsearch_path=$sys_lib_dlsearch_path_spec\n\n# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code\nfunc_munge_path_list sys_lib_dlsearch_path_spec \"$LT_SYS_LIBRARY_PATH\"\n\n# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool\nconfigure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH\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\n\n\n\n\n\n\n\n\n\n\n\n\n\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs\" >&5\n$as_echo_n \"checking how to hardcode library paths into programs... \" >&6; }\nhardcode_action_CXX=\nif test -n \"$hardcode_libdir_flag_spec_CXX\" ||\n   test -n \"$runpath_var_CXX\" ||\n   test yes = \"$hardcode_automatic_CXX\"; then\n\n  # We can hardcode non-existent directories.\n  if test no != \"$hardcode_direct_CXX\" &&\n     # If the only mechanism to avoid hardcoding is shlibpath_var, we\n     # have to relink, otherwise we might link with an installed library\n     # when we should be linking with a yet-to-be-installed one\n     ## test no != \"$_LT_TAGVAR(hardcode_shlibpath_var, CXX)\" &&\n     test no != \"$hardcode_minus_L_CXX\"; then\n    # Linking always hardcodes the temporary library directory.\n    hardcode_action_CXX=relink\n  else\n    # We can link without hardcoding, and we can hardcode nonexisting dirs.\n    hardcode_action_CXX=immediate\n  fi\nelse\n  # We cannot hardcode anything, or else we can only hardcode existing\n  # directories.\n  hardcode_action_CXX=unsupported\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX\" >&5\n$as_echo \"$hardcode_action_CXX\" >&6; }\n\nif test relink = \"$hardcode_action_CXX\" ||\n   test yes = \"$inherit_rpath_CXX\"; then\n  # Fast installation is not supported\n  enable_fast_install=no\nelif test yes = \"$shlibpath_overrides_runpath\" ||\n     test no = \"$enable_shared\"; then\n  # Fast installation is not necessary\n  enable_fast_install=needless\nfi\n\n\n\n\n\n\n\n  fi # test -n \"$compiler\"\n\n  CC=$lt_save_CC\n  CFLAGS=$lt_save_CFLAGS\n  LDCXX=$LD\n  LD=$lt_save_LD\n  GCC=$lt_save_GCC\n  with_gnu_ld=$lt_save_with_gnu_ld\n  lt_cv_path_LDCXX=$lt_cv_path_LD\n  lt_cv_path_LD=$lt_save_path_LD\n  lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld\n  lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld\nfi # test yes != \"$_lt_caught_CXX_error\"\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n        ac_config_commands=\"$ac_config_commands libtool\"\n\n\n\n\n# Only expand once:\n\n\n\n#enable -D_GNU_SOURCE since the return code value of getaddrinfo\n#ifdefed with __USE_GNU\n#features.h header undef's __USE_GNU and defines it only if _GNU_SOURCE is defined\n#hence this define for gcc\n# Check whether --enable-debug was given.\nif test \"${enable_debug+set}\" = set; then :\n  enableval=$enable_debug;\nelse\n  enable_debug=no\nfi\n\n\nif test \"x$enable_debug\" = xyes; then\n    if test \"x$init_cflags\" = x; then\n        CFLAGS=\"\"\n    fi\n    CFLAGS=\"$CFLAGS -g -O0 -D_GNU_SOURCE\"\nelse\n    if test \"x$init_cflags\" = x; then\n        CFLAGS=\"-g -O2 -D_GNU_SOURCE\"\n    fi\nfi\n\nif test \"x$enable_debug\" = xyes; then\n    if test \"x$init_cxxflags\" = x; then\n        CXXFLAGS=\"\"\n    fi\n    CXXFLAGS=\"$CXXFLAGS -g -O0\"\nelse\n    if test \"x$init_cxxflags\" = x; then\n        CXXFLAGS=\"-g -O2\"\n    fi\nfi\n\n# Check whether to enable gcov (coverage test)\n# Check whether --enable-gcov was given.\nif test \"${enable_gcov+set}\" = set; then :\n  enableval=$enable_gcov;\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether to enable gcov\" >&5\n$as_echo_n \"checking whether to enable gcov... \" >&6; }\nif test \"x${enable_gcov}\" = \"xyes\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n if test \"x${enable_gcov}\" = \"xyes\"; then\n  ENABLEGCOV_TRUE=\n  ENABLEGCOV_FALSE='#'\nelse\n  ENABLEGCOV_TRUE='#'\n  ENABLEGCOV_FALSE=\nfi\n\n\n\nCXXFLAGS=\"$CXXFLAGS -std=c++11\"\n\n\n# Check whether --with-syncapi was given.\nif test \"${with_syncapi+set}\" = set; then :\n  withval=$with_syncapi;\nelse\n  with_syncapi=yes\nfi\n\n\n# Checks for libraries.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_lock in -lpthread\" >&5\n$as_echo_n \"checking for pthread_mutex_lock in -lpthread... \" >&6; }\nif ${ac_cv_lib_pthread_pthread_mutex_lock+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-lpthread  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar pthread_mutex_lock ();\nint\nmain ()\n{\nreturn pthread_mutex_lock ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_pthread_pthread_mutex_lock=yes\nelse\n  ac_cv_lib_pthread_pthread_mutex_lock=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_mutex_lock\" >&5\n$as_echo \"$ac_cv_lib_pthread_pthread_mutex_lock\" >&6; }\nif test \"x$ac_cv_lib_pthread_pthread_mutex_lock\" = xyes; then :\n  have_pthread=yes\nelse\n  have_pthread=no\nfi\n\n\nif test \"x$with_syncapi\" != xno && test \"x$have_pthread\" = xno; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: cannot build SyncAPI -- pthread not found\" >&5\n$as_echo \"$as_me: WARNING: cannot build SyncAPI -- pthread not found\" >&2;}\n    with_syncapi=no\nfi\nif test \"x$with_syncapi\" != xno; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: building with SyncAPI support\" >&5\n$as_echo \"$as_me: building with SyncAPI support\" >&6;}\nelse\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: building without SyncAPI support\" >&5\n$as_echo \"$as_me: building without SyncAPI support\" >&6;}\nfi\n\n if test \"x$with_syncapi\" != xno; then\n  WANT_SYNCAPI_TRUE=\n  WANT_SYNCAPI_FALSE='#'\nelse\n  WANT_SYNCAPI_TRUE='#'\n  WANT_SYNCAPI_FALSE=\nfi\n\n\n\n# Check whether --with-sasl was given.\nif test \"${with_sasl+set}\" = set; then :\n  withval=$with_sasl;\nelse\n  with_sasl=yes\nfi\n\nif test \"x$with_sasl\" != \"xno\"; then\n    saved_CPPFLAGS=\"$CPPFLAGS\"\n    saved_LDFLAGS=\"$LDFLAGS\"\n    if test \"x$with_sasl\" != \"xyes\" ; then\n        CPPFLAGS=\"$CPPFLAGS -I$with_sasl/include\"\n        LDFLAGS=\"$LDFLAGS -L$with_sasl/lib\"\n    fi\n    have_sasl=no\n    ac_fn_c_check_header_mongrel \"$LINENO\" \"sasl/sasl.h\" \"ac_cv_header_sasl_sasl_h\" \"$ac_includes_default\"\nif test \"x$ac_cv_header_sasl_sasl_h\" = xyes; then :\n\n     { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for sasl_client_init in -lsasl2\" >&5\n$as_echo_n \"checking for sasl_client_init in -lsasl2... \" >&6; }\nif ${ac_cv_lib_sasl2_sasl_client_init+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-lsasl2  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar sasl_client_init ();\nint\nmain ()\n{\nreturn sasl_client_init ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_sasl2_sasl_client_init=yes\nelse\n  ac_cv_lib_sasl2_sasl_client_init=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sasl2_sasl_client_init\" >&5\n$as_echo \"$ac_cv_lib_sasl2_sasl_client_init\" >&6; }\nif test \"x$ac_cv_lib_sasl2_sasl_client_init\" = xyes; then :\n  have_sasl=yes\nfi\n\nfi\n\n\n    if test \"x$have_sasl\" != \"xyes\"; then\n        CPPFLAGS=\"$saved_CPPFLAGS\"\n        LDFLAGS=\"$saved_LDFLAGS\"\n    fi\nfi\nif test \"x$with_sasl\" != xno && test \"x$have_sasl\" = xno; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: cannot build SASL support -- sasl2 not found\" >&5\n$as_echo \"$as_me: WARNING: cannot build SASL support -- sasl2 not found\" >&2;}\n    with_sasl=no\nfi\nif test \"x$with_sasl\" != xno; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: building with SASL support\" >&5\n$as_echo \"$as_me: building with SASL support\" >&6;}\nelse\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: building without SASL support\" >&5\n$as_echo \"$as_me: building without SASL support\" >&6;}\nfi\n if test \"x$with_sasl\" != xno; then\n  WANT_SASL_TRUE=\n  WANT_SASL_FALSE='#'\nelse\n  WANT_SASL_TRUE='#'\n  WANT_SASL_FALSE=\nfi\n\n\n# Checks for header files.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for ANSI C header files\" >&5\n$as_echo_n \"checking for ANSI C header files... \" >&6; }\nif ${ac_cv_header_stdc+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdlib.h>\n#include <stdarg.h>\n#include <string.h>\n#include <float.h>\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_header_stdc=yes\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\nif test $ac_cv_header_stdc = yes; then\n  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <string.h>\n\n_ACEOF\nif (eval \"$ac_cpp conftest.$ac_ext\") 2>&5 |\n  $EGREP \"memchr\" >/dev/null 2>&1; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f conftest*\n\nfi\n\nif test $ac_cv_header_stdc = yes; then\n  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdlib.h>\n\n_ACEOF\nif (eval \"$ac_cpp conftest.$ac_ext\") 2>&5 |\n  $EGREP \"free\" >/dev/null 2>&1; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f conftest*\n\nfi\n\nif test $ac_cv_header_stdc = yes; then\n  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.\n  if test \"$cross_compiling\" = yes; then :\n  :\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ctype.h>\n#include <stdlib.h>\n#if ((' ' & 0x0FF) == 0x020)\n# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')\n# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))\n#else\n# define ISLOWER(c) \\\n\t\t   (('a' <= (c) && (c) <= 'i') \\\n\t\t     || ('j' <= (c) && (c) <= 'r') \\\n\t\t     || ('s' <= (c) && (c) <= 'z'))\n# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))\n#endif\n\n#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))\nint\nmain ()\n{\n  int i;\n  for (i = 0; i < 256; i++)\n    if (XOR (islower (i), ISLOWER (i))\n\t|| toupper (i) != TOUPPER (i))\n      return 2;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_run \"$LINENO\"; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \\\n  conftest.$ac_objext conftest.beam conftest.$ac_ext\nfi\n\nfi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc\" >&5\n$as_echo \"$ac_cv_header_stdc\" >&6; }\nif test $ac_cv_header_stdc = yes; then\n\n$as_echo \"#define STDC_HEADERS 1\" >>confdefs.h\n\nfi\n\nfor ac_header in arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h sys/time.h unistd.h sys/utsname.h\ndo :\n  as_ac_Header=`$as_echo \"ac_cv_header_$ac_header\" | $as_tr_sh`\nac_fn_c_check_header_mongrel \"$LINENO\" \"$ac_header\" \"$as_ac_Header\" \"$ac_includes_default\"\nif eval test \\\"x\\$\"$as_ac_Header\"\\\" = x\"yes\"; then :\n  cat >>confdefs.h <<_ACEOF\n#define `$as_echo \"HAVE_$ac_header\" | $as_tr_cpp` 1\n_ACEOF\n\nfi\n\ndone\n\n\n# Checks for typedefs, structures, and compiler characteristics.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const\" >&5\n$as_echo_n \"checking for an ANSI C-conforming const... \" >&6; }\nif ${ac_cv_c_const+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n#ifndef __cplusplus\n  /* Ultrix mips cc rejects this sort of thing.  */\n  typedef int charset[2];\n  const charset cs = { 0, 0 };\n  /* SunOS 4.1.1 cc rejects this.  */\n  char const *const *pcpcc;\n  char **ppc;\n  /* NEC SVR4.0.2 mips cc rejects this.  */\n  struct point {int x, y;};\n  static struct point const zero = {0,0};\n  /* AIX XL C 1.02.0.0 rejects this.\n     It does not let you subtract one const X* pointer from another in\n     an arm of an if-expression whose if-part is not a constant\n     expression */\n  const char *g = \"string\";\n  pcpcc = &g + (g ? g-g : 0);\n  /* HPUX 7.0 cc rejects these. */\n  ++pcpcc;\n  ppc = (char**) pcpcc;\n  pcpcc = (char const *const *) ppc;\n  { /* SCO 3.2v4 cc rejects this sort of thing.  */\n    char tx;\n    char *t = &tx;\n    char const *s = 0 ? (char *) 0 : (char const *) 0;\n\n    *t++ = 0;\n    if (s) return 0;\n  }\n  { /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */\n    int x[] = {25, 17};\n    const int *foo = &x[0];\n    ++foo;\n  }\n  { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */\n    typedef const int *iptr;\n    iptr p = 0;\n    ++p;\n  }\n  { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying\n       \"k.c\", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */\n    struct s { int j; const int *ap[3]; } bx;\n    struct s *b = &bx; b->j = 5;\n  }\n  { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */\n    const int foo = 10;\n    if (!foo) return 0;\n  }\n  return !cs[0] && !zero.x;\n#endif\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_c_const=yes\nelse\n  ac_cv_c_const=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const\" >&5\n$as_echo \"$ac_cv_c_const\" >&6; }\nif test $ac_cv_c_const = no; then\n\n$as_echo \"#define const /**/\" >>confdefs.h\n\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for inline\" >&5\n$as_echo_n \"checking for inline... \" >&6; }\nif ${ac_cv_c_inline+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_cv_c_inline=no\nfor ac_kw in inline __inline__ __inline; do\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#ifndef __cplusplus\ntypedef int foo_t;\nstatic $ac_kw foo_t static_foo () {return 0; }\n$ac_kw foo_t foo () {return 0; }\n#endif\n\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_c_inline=$ac_kw\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n  test \"$ac_cv_c_inline\" != no && break\ndone\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline\" >&5\n$as_echo \"$ac_cv_c_inline\" >&6; }\n\ncase $ac_cv_c_inline in\n  inline | yes) ;;\n  *)\n    case $ac_cv_c_inline in\n      no) ac_val=;;\n      *) ac_val=$ac_cv_c_inline;;\n    esac\n    cat >>confdefs.h <<_ACEOF\n#ifndef __cplusplus\n#define inline $ac_val\n#endif\n_ACEOF\n    ;;\nesac\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included\" >&5\n$as_echo_n \"checking whether time.h and sys/time.h may both be included... \" >&6; }\nif ${ac_cv_header_time+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <sys/types.h>\n#include <sys/time.h>\n#include <time.h>\n\nint\nmain ()\n{\nif ((struct tm *) 0)\nreturn 0;\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_header_time=yes\nelse\n  ac_cv_header_time=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time\" >&5\n$as_echo \"$ac_cv_header_time\" >&6; }\nif test $ac_cv_header_time = yes; then\n\n$as_echo \"#define TIME_WITH_SYS_TIME 1\" >>confdefs.h\n\nfi\n\nac_fn_c_check_type \"$LINENO\" \"nfds_t\" \"ac_cv_type_nfds_t\" \"#include <poll.h>\n\"\nif test \"x$ac_cv_type_nfds_t\" = xyes; then :\n\n$as_echo \"#define POLL_NFDS_TYPE nfds_t\" >>confdefs.h\n\nelse\n\n$as_echo \"#define POLL_NFDS_TYPE unsigned int\" >>confdefs.h\n\nfi\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether to enable ipv6\" >&5\n$as_echo_n \"checking whether to enable ipv6... \" >&6; }\n\nif test \"$cross_compiling\" = yes; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n   ipv6=no\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n /* is AF_INET6 available? */\n#include <sys/types.h>\n#include <sys/socket.h>\nmain()\n{\n if (socket(AF_INET6, SOCK_STREAM, 0) < 0)\n   exit(1);\n else\n   exit(0);\n}\n\n_ACEOF\nif ac_fn_c_try_run \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\n   ipv6=yes\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n   ipv6=no\nfi\nrm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \\\n  conftest.$ac_objext conftest.beam conftest.$ac_ext\nfi\n\n\nif test x\"$ipv6\" = xyes; then\n  USEIPV6=\"-DZOO_IPV6_ENABLED\"\n\nfi\n\n# use SOCK_CLOEXEC if available and wanted\n\n# Check whether --with-sock_cloexec was given.\nif test \"${with_sock_cloexec+set}\" = set; then :\n  withval=$with_sock_cloexec;\nelse\n  with_sock_cloexec=no\nfi\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether SOCK_CLOEXEC is available\" >&5\n$as_echo_n \"checking whether SOCK_CLOEXEC is available... \" >&6; }\n\nif test \"$cross_compiling\" = yes; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n   has_sock_cloexec=no\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n /* is SOCK_CLOEXEC available ? */\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <stdlib.h>\nmain()\n{\n#ifdef SOCK_CLOEXEC\n  exit(0);\n#else\n  exit(1);\n#endif\n}\n\n_ACEOF\nif ac_fn_c_try_run \"$LINENO\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\n   has_sock_cloexec=yes\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n   has_sock_cloexec=no\nfi\nrm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \\\n  conftest.$ac_objext conftest.beam conftest.$ac_ext\nfi\n\n\nif test \"x$with_sock_cloexec\" != xno && test \"x$has_sock_cloexec\" = xno; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: cannot use SOCK_CLOEXEC -- SOCK_CLOEXEC undefined on this platform\" >&5\n$as_echo \"$as_me: WARNING: cannot use SOCK_CLOEXEC -- SOCK_CLOEXEC undefined on this platform\" >&2;}\n  with_sock_cloexec=no\nfi\n\nif test \"x$with_sock_cloexec\" != xno; then\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: building with SOCK_CLOEXEC\" >&5\n$as_echo \"$as_me: building with SOCK_CLOEXEC\" >&6;}\nelse\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: building without SOCK_CLOEXEC\" >&5\n$as_echo \"$as_me: building without SOCK_CLOEXEC\" >&6;}\nfi\n\nif test x\"$with_sock_cloexec\" != xno; then :\n\n$as_echo \"#define SOCK_CLOEXEC_ENABLED 1\" >>confdefs.h\n\nfi\n if test \"x$with_sock_cloexec\" != xno; then\n  SOCK_CLOEXEC_ENABLED_TRUE=\n  SOCK_CLOEXEC_ENABLED_FALSE='#'\nelse\n  SOCK_CLOEXEC_ENABLED_TRUE='#'\n  SOCK_CLOEXEC_ENABLED_FALSE=\nfi\n\n\n# Determine which libraries we need to use clock_gettime\nsaved_LIBS=\"$LIBS\"\nLIBS=\"\"\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt\" >&5\n$as_echo_n \"checking for clock_gettime in -lrt... \" >&6; }\nif ${ac_cv_lib_rt_clock_gettime+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-lrt  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar clock_gettime ();\nint\nmain ()\n{\nreturn clock_gettime ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_rt_clock_gettime=yes\nelse\n  ac_cv_lib_rt_clock_gettime=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime\" >&5\n$as_echo \"$ac_cv_lib_rt_clock_gettime\" >&6; }\nif test \"x$ac_cv_lib_rt_clock_gettime\" = xyes; then :\n  cat >>confdefs.h <<_ACEOF\n#define HAVE_LIBRT 1\n_ACEOF\n\n  LIBS=\"-lrt $LIBS\"\n\nfi\n\nCLOCK_GETTIME_LIBS=$LIBS\n\nLIBS=\"$saved_LIBS\"\n\n# Checks for library functions.\nfor ac_func in getcwd gethostbyname gethostname getlogin getpwuid_r gettimeofday getuid memmove memset poll socket strchr strdup strerror strtol\ndo :\n  as_ac_var=`$as_echo \"ac_cv_func_$ac_func\" | $as_tr_sh`\nac_fn_c_check_func \"$LINENO\" \"$ac_func\" \"$as_ac_var\"\nif eval test \\\"x\\$\"$as_ac_var\"\\\" = x\"yes\"; then :\n  cat >>confdefs.h <<_ACEOF\n#define `$as_echo \"HAVE_$ac_func\" | $as_tr_cpp` 1\n_ACEOF\n\nfi\ndone\n\n\nac_config_files=\"$ac_config_files Makefile\"\n\n\n if\n  case \"$host_os\" in\n  *solaris*)\n    true\n    ;;\n  *)\n    false\n    ;;\n  esac ; then\n  SOLARIS_TRUE=\n  SOLARIS_FALSE='#'\nelse\n  SOLARIS_TRUE='#'\n  SOLARIS_FALSE=\nfi\n\ncat >confcache <<\\_ACEOF\n# This file is a shell script that caches the results of configure\n# tests run on this system so they can be shared between configure\n# scripts and configure runs, see configure's option --config-cache.\n# It is not useful on other systems.  If it contains results you don't\n# want to keep, you may remove or edit it.\n#\n# config.status only pays attention to the cache file if you give it\n# the --recheck option to rerun configure.\n#\n# `ac_cv_env_foo' variables (set or unset) will be overridden when\n# loading this file, other *unset* `ac_cv_foo' will be assigned the\n# following values.\n\n_ACEOF\n\n# The following way of writing the cache mishandles newlines in values,\n# but we know of no workaround that is simple, portable, and efficient.\n# So, we kill variables containing newlines.\n# Ultrix sh set writes to stderr and can't be redirected directly,\n# and sets the high bit in the cache file unless we assign to the vars.\n(\n  for ac_var in `(set) 2>&1 | sed -n 's/^\\([a-zA-Z_][a-zA-Z0-9_]*\\)=.*/\\1/p'`; do\n    eval ac_val=\\$$ac_var\n    case $ac_val in #(\n    *${as_nl}*)\n      case $ac_var in #(\n      *_cv_*) { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline\" >&5\n$as_echo \"$as_me: WARNING: cache variable $ac_var contains a newline\" >&2;} ;;\n      esac\n      case $ac_var in #(\n      _ | IFS | as_nl) ;; #(\n      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(\n      *) { eval $ac_var=; unset $ac_var;} ;;\n      esac ;;\n    esac\n  done\n\n  (set) 2>&1 |\n    case $as_nl`(ac_space=' '; set) 2>&1` in #(\n    *${as_nl}ac_space=\\ *)\n      # `set' does not quote correctly, so add quotes: double-quote\n      # substitution turns \\\\\\\\ into \\\\, and sed turns \\\\ into \\.\n      sed -n \\\n\t\"s/'/'\\\\\\\\''/g;\n\t  s/^\\\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\\\)=\\\\(.*\\\\)/\\\\1='\\\\2'/p\"\n      ;; #(\n    *)\n      # `set' quotes correctly as required by POSIX, so do not add quotes.\n      sed -n \"/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p\"\n      ;;\n    esac |\n    sort\n) |\n  sed '\n     /^ac_cv_env_/b end\n     t clear\n     :clear\n     s/^\\([^=]*\\)=\\(.*[{}].*\\)$/test \"${\\1+set}\" = set || &/\n     t end\n     s/^\\([^=]*\\)=\\(.*\\)$/\\1=${\\1=\\2}/\n     :end' >>confcache\nif diff \"$cache_file\" confcache >/dev/null 2>&1; then :; else\n  if test -w \"$cache_file\"; then\n    if test \"x$cache_file\" != \"x/dev/null\"; then\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: updating cache $cache_file\" >&5\n$as_echo \"$as_me: updating cache $cache_file\" >&6;}\n      if test ! -f \"$cache_file\" || test -h \"$cache_file\"; then\n\tcat confcache >\"$cache_file\"\n      else\n        case $cache_file in #(\n        */* | ?:*)\n\t  mv -f confcache \"$cache_file\"$$ &&\n\t  mv -f \"$cache_file\"$$ \"$cache_file\" ;; #(\n        *)\n\t  mv -f confcache \"$cache_file\" ;;\n\tesac\n      fi\n    fi\n  else\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file\" >&5\n$as_echo \"$as_me: not updating unwritable cache $cache_file\" >&6;}\n  fi\nfi\nrm -f confcache\n\ntest \"x$prefix\" = xNONE && prefix=$ac_default_prefix\n# Let make expand exec_prefix.\ntest \"x$exec_prefix\" = xNONE && exec_prefix='${prefix}'\n\nDEFS=-DHAVE_CONFIG_H\n\nac_libobjs=\nac_ltlibobjs=\nU=\nfor ac_i in : $LIBOBJS; do test \"x$ac_i\" = x: && continue\n  # 1. Remove the extension, and $U if already installed.\n  ac_script='s/\\$U\\././;s/\\.o$//;s/\\.obj$//'\n  ac_i=`$as_echo \"$ac_i\" | sed \"$ac_script\"`\n  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR\n  #    will be set to the directory where LIBOBJS objects are built.\n  as_fn_append ac_libobjs \" \\${LIBOBJDIR}$ac_i\\$U.$ac_objext\"\n  as_fn_append ac_ltlibobjs \" \\${LIBOBJDIR}$ac_i\"'$U.lo'\ndone\nLIBOBJS=$ac_libobjs\n\nLTLIBOBJS=$ac_ltlibobjs\n\n\nif test -z \"${DX_COND_doc_TRUE}\" && test -z \"${DX_COND_doc_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_doc\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_doc_TRUE}\" && test -z \"${DX_COND_doc_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_doc\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_dot_TRUE}\" && test -z \"${DX_COND_dot_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_dot\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_dot_TRUE}\" && test -z \"${DX_COND_dot_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_dot\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_man_TRUE}\" && test -z \"${DX_COND_man_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_man\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_man_TRUE}\" && test -z \"${DX_COND_man_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_man\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_rtf_TRUE}\" && test -z \"${DX_COND_rtf_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_rtf\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_rtf_TRUE}\" && test -z \"${DX_COND_rtf_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_rtf\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_xml_TRUE}\" && test -z \"${DX_COND_xml_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_xml\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_xml_TRUE}\" && test -z \"${DX_COND_xml_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_xml\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_chm_TRUE}\" && test -z \"${DX_COND_chm_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_chm\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_chm_TRUE}\" && test -z \"${DX_COND_chm_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_chm\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_chi_TRUE}\" && test -z \"${DX_COND_chi_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_chi\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_chi_TRUE}\" && test -z \"${DX_COND_chi_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_chi\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_html_TRUE}\" && test -z \"${DX_COND_html_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_html\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_html_TRUE}\" && test -z \"${DX_COND_html_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_html\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_ps_TRUE}\" && test -z \"${DX_COND_ps_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_ps\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_ps_TRUE}\" && test -z \"${DX_COND_ps_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_ps\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_pdf_TRUE}\" && test -z \"${DX_COND_pdf_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_pdf\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_pdf_TRUE}\" && test -z \"${DX_COND_pdf_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_pdf\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_latex_TRUE}\" && test -z \"${DX_COND_latex_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_latex\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${DX_COND_latex_TRUE}\" && test -z \"${DX_COND_latex_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"DX_COND_latex\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure\" >&5\n$as_echo_n \"checking that generated files are newer than configure... \" >&6; }\n   if test -n \"$am_sleep_pid\"; then\n     # Hide warnings about reused PIDs.\n     wait $am_sleep_pid 2>/dev/null\n   fi\n   { $as_echo \"$as_me:${as_lineno-$LINENO}: result: done\" >&5\n$as_echo \"done\" >&6; }\n if test -n \"$EXEEXT\"; then\n  am__EXEEXT_TRUE=\n  am__EXEEXT_FALSE='#'\nelse\n  am__EXEEXT_TRUE='#'\n  am__EXEEXT_FALSE=\nfi\n\nif test -z \"${AMDEP_TRUE}\" && test -z \"${AMDEP_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"AMDEP\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${am__fastdepCC_TRUE}\" && test -z \"${am__fastdepCC_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"am__fastdepCC\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${WANT_OPENSSL_TRUE}\" && test -z \"${WANT_OPENSSL_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"WANT_OPENSSL\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${am__fastdepCC_TRUE}\" && test -z \"${am__fastdepCC_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"am__fastdepCC\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${am__fastdepCXX_TRUE}\" && test -z \"${am__fastdepCXX_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"am__fastdepCXX\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${ENABLEGCOV_TRUE}\" && test -z \"${ENABLEGCOV_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"ENABLEGCOV\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${WANT_SYNCAPI_TRUE}\" && test -z \"${WANT_SYNCAPI_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"WANT_SYNCAPI\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${WANT_SASL_TRUE}\" && test -z \"${WANT_SASL_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"WANT_SASL\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${SOCK_CLOEXEC_ENABLED_TRUE}\" && test -z \"${SOCK_CLOEXEC_ENABLED_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"SOCK_CLOEXEC_ENABLED\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\nif test -z \"${SOLARIS_TRUE}\" && test -z \"${SOLARIS_FALSE}\"; then\n  as_fn_error $? \"conditional \\\"SOLARIS\\\" was never defined.\nUsually this means the macro was only invoked conditionally.\" \"$LINENO\" 5\nfi\n\n: \"${CONFIG_STATUS=./config.status}\"\nac_write_fail=0\nac_clean_files_save=$ac_clean_files\nac_clean_files=\"$ac_clean_files $CONFIG_STATUS\"\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS\" >&5\n$as_echo \"$as_me: creating $CONFIG_STATUS\" >&6;}\nas_write_fail=0\ncat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1\n#! $SHELL\n# Generated by $as_me.\n# Run this file to recreate the current configuration.\n# Compiler output produced by configure, useful for debugging\n# configure, is in config.log if it exists.\n\ndebug=false\nac_cs_recheck=false\nac_cs_silent=false\n\nSHELL=\\${CONFIG_SHELL-$SHELL}\nexport SHELL\n_ASEOF\ncat >>$CONFIG_STATUS <<\\_ASEOF || as_write_fail=1\n## -------------------- ##\n## M4sh Initialization. ##\n## -------------------- ##\n\n# Be more Bourne compatible\nDUALCASE=1; export DUALCASE # for MKS sh\nif test -n \"${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :\n  emulate sh\n  NULLCMD=:\n  # Pre-4.2 versions of Zsh do word splitting on ${1+\"$@\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '${1+\"$@\"}'='\"$@\"'\n  setopt NO_GLOB_SUBST\nelse\n  case `(set -o) 2>/dev/null` in #(\n  *posix*) :\n    set -o posix ;; #(\n  *) :\n     ;;\nesac\nfi\n\n\nas_nl='\n'\nexport as_nl\n# Printing a long string crashes Solaris 7 /usr/bin/printf.\nas_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo\n# Prefer a ksh shell builtin over an external printf program on Solaris,\n# but without wasting forks for bash or zsh.\nif test -z \"$BASH_VERSION$ZSH_VERSION\" \\\n    && (test \"X`print -r -- $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='print -r --'\n  as_echo_n='print -rn --'\nelif (test \"X`printf %s $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='printf %s\\n'\n  as_echo_n='printf %s'\nelse\n  if test \"X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`\" = \"X-n $as_echo\"; then\n    as_echo_body='eval /usr/ucb/echo -n \"$1$as_nl\"'\n    as_echo_n='/usr/ucb/echo -n'\n  else\n    as_echo_body='eval expr \"X$1\" : \"X\\\\(.*\\\\)\"'\n    as_echo_n_body='eval\n      arg=$1;\n      case $arg in #(\n      *\"$as_nl\"*)\n\texpr \"X$arg\" : \"X\\\\(.*\\\\)$as_nl\";\n\targ=`expr \"X$arg\" : \".*$as_nl\\\\(.*\\\\)\"`;;\n      esac;\n      expr \"X$arg\" : \"X\\\\(.*\\\\)\" | tr -d \"$as_nl\"\n    '\n    export as_echo_n_body\n    as_echo_n='sh -c $as_echo_n_body as_echo'\n  fi\n  export as_echo_body\n  as_echo='sh -c $as_echo_body as_echo'\nfi\n\n# The user is always right.\nif test \"${PATH_SEPARATOR+set}\" != set; then\n  PATH_SEPARATOR=:\n  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {\n    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||\n      PATH_SEPARATOR=';'\n  }\nfi\n\n\n# IFS\n# We need space, tab and new line, in precisely that order.  Quoting is\n# there to prevent editors from complaining about space-tab.\n# (If _AS_PATH_WALK were called with IFS unset, it would disable word\n# splitting by setting IFS to empty value.)\nIFS=\" \"\"\t$as_nl\"\n\n# Find who we are.  Look in the path if we contain no directory separator.\nas_myself=\ncase $0 in #((\n  *[\\\\/]* ) as_myself=$0 ;;\n  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    test -r \"$as_dir/$0\" && as_myself=$as_dir/$0 && break\n  done\nIFS=$as_save_IFS\n\n     ;;\nesac\n# We did not find ourselves, most probably we were run as `sh COMMAND'\n# in which case we are not to be found in the path.\nif test \"x$as_myself\" = x; then\n  as_myself=$0\nfi\nif test ! -f \"$as_myself\"; then\n  $as_echo \"$as_myself: error: cannot find myself; rerun with an absolute file name\" >&2\n  exit 1\nfi\n\n# Unset variables that we do not need and which cause bugs (e.g. in\n# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the \"|| exit 1\"\n# suppresses any \"Segmentation fault\" message there.  '((' could\n# trigger a bug in pdksh 5.2.14.\nfor as_var in BASH_ENV ENV MAIL MAILPATH\ndo eval test x\\${$as_var+set} = xset \\\n  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :\ndone\nPS1='$ '\nPS2='> '\nPS4='+ '\n\n# NLS nuisances.\nLC_ALL=C\nexport LC_ALL\nLANGUAGE=C\nexport LANGUAGE\n\n# CDPATH.\n(unset CDPATH) >/dev/null 2>&1 && unset CDPATH\n\n\n# as_fn_error STATUS ERROR [LINENO LOG_FD]\n# ----------------------------------------\n# Output \"`basename $0`: error: ERROR\" to stderr. If LINENO and LOG_FD are\n# provided, also output the error to LOG_FD, referencing LINENO. Then exit the\n# script with STATUS, using 1 if that was 0.\nas_fn_error ()\n{\n  as_status=$1; test $as_status -eq 0 && as_status=1\n  if test \"$4\"; then\n    as_lineno=${as_lineno-\"$3\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n    $as_echo \"$as_me:${as_lineno-$LINENO}: error: $2\" >&$4\n  fi\n  $as_echo \"$as_me: error: $2\" >&2\n  as_fn_exit $as_status\n} # as_fn_error\n\n\n# as_fn_set_status STATUS\n# -----------------------\n# Set $? to STATUS, without forking.\nas_fn_set_status ()\n{\n  return $1\n} # as_fn_set_status\n\n# as_fn_exit STATUS\n# -----------------\n# Exit the shell with STATUS, even in a \"trap 0\" or \"set -e\" context.\nas_fn_exit ()\n{\n  set +e\n  as_fn_set_status $1\n  exit $1\n} # as_fn_exit\n\n# as_fn_unset VAR\n# ---------------\n# Portably unset VAR.\nas_fn_unset ()\n{\n  { eval $1=; unset $1;}\n}\nas_unset=as_fn_unset\n# as_fn_append VAR VALUE\n# ----------------------\n# Append the text in VALUE to the end of the definition contained in VAR. Take\n# advantage of any shell optimizations that allow amortized linear growth over\n# repeated appends, instead of the typical quadratic growth present in naive\n# implementations.\nif (eval \"as_var=1; as_var+=2; test x\\$as_var = x12\") 2>/dev/null; then :\n  eval 'as_fn_append ()\n  {\n    eval $1+=\\$2\n  }'\nelse\n  as_fn_append ()\n  {\n    eval $1=\\$$1\\$2\n  }\nfi # as_fn_append\n\n# as_fn_arith ARG...\n# ------------------\n# Perform arithmetic evaluation on the ARGs, and store the result in the\n# global $as_val. Take advantage of shells that can avoid forks. The arguments\n# must be portable across $(()) and expr.\nif (eval \"test \\$(( 1 + 1 )) = 2\") 2>/dev/null; then :\n  eval 'as_fn_arith ()\n  {\n    as_val=$(( $* ))\n  }'\nelse\n  as_fn_arith ()\n  {\n    as_val=`expr \"$@\" || test $? -eq 1`\n  }\nfi # as_fn_arith\n\n\nif expr a : '\\(a\\)' >/dev/null 2>&1 &&\n   test \"X`expr 00001 : '.*\\(...\\)'`\" = X001; then\n  as_expr=expr\nelse\n  as_expr=false\nfi\n\nif (basename -- /) >/dev/null 2>&1 && test \"X`basename -- / 2>&1`\" = \"X/\"; then\n  as_basename=basename\nelse\n  as_basename=false\nfi\n\nif (as_dir=`dirname -- /` && test \"X$as_dir\" = X/) >/dev/null 2>&1; then\n  as_dirname=dirname\nelse\n  as_dirname=false\nfi\n\nas_me=`$as_basename -- \"$0\" ||\n$as_expr X/\"$0\" : '.*/\\([^/][^/]*\\)/*$' \\| \\\n\t X\"$0\" : 'X\\(//\\)$' \\| \\\n\t X\"$0\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X/\"$0\" |\n    sed '/^.*\\/\\([^/][^/]*\\)\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n\n# Avoid depending upon Character Ranges.\nas_cr_letters='abcdefghijklmnopqrstuvwxyz'\nas_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'\nas_cr_Letters=$as_cr_letters$as_cr_LETTERS\nas_cr_digits='0123456789'\nas_cr_alnum=$as_cr_Letters$as_cr_digits\n\nECHO_C= ECHO_N= ECHO_T=\ncase `echo -n x` in #(((((\n-n*)\n  case `echo 'xy\\c'` in\n  *c*) ECHO_T='\t';;\t# ECHO_T is single tab character.\n  xy)  ECHO_C='\\c';;\n  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null\n       ECHO_T='\t';;\n  esac;;\n*)\n  ECHO_N='-n';;\nesac\n\nrm -f conf$$ conf$$.exe conf$$.file\nif test -d conf$$.dir; then\n  rm -f conf$$.dir/conf$$.file\nelse\n  rm -f conf$$.dir\n  mkdir conf$$.dir 2>/dev/null\nfi\nif (echo >conf$$.file) 2>/dev/null; then\n  if ln -s conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s='ln -s'\n    # ... but there are two gotchas:\n    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.\n    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.\n    # In both cases, we have to default to `cp -pR'.\n    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||\n      as_ln_s='cp -pR'\n  elif ln conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s=ln\n  else\n    as_ln_s='cp -pR'\n  fi\nelse\n  as_ln_s='cp -pR'\nfi\nrm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file\nrmdir conf$$.dir 2>/dev/null\n\n\n# as_fn_mkdir_p\n# -------------\n# Create \"$as_dir\" as a directory, including parents if necessary.\nas_fn_mkdir_p ()\n{\n\n  case $as_dir in #(\n  -*) as_dir=./$as_dir;;\n  esac\n  test -d \"$as_dir\" || eval $as_mkdir_p || {\n    as_dirs=\n    while :; do\n      case $as_dir in #(\n      *\\'*) as_qdir=`$as_echo \"$as_dir\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;; #'(\n      *) as_qdir=$as_dir;;\n      esac\n      as_dirs=\"'$as_qdir' $as_dirs\"\n      as_dir=`$as_dirname -- \"$as_dir\" ||\n$as_expr X\"$as_dir\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)$' \\| \\\n\t X\"$as_dir\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$as_dir\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n      test -d \"$as_dir\" && break\n    done\n    test -z \"$as_dirs\" || eval \"mkdir $as_dirs\"\n  } || test -d \"$as_dir\" || as_fn_error $? \"cannot create directory $as_dir\"\n\n\n} # as_fn_mkdir_p\nif mkdir -p . 2>/dev/null; then\n  as_mkdir_p='mkdir -p \"$as_dir\"'\nelse\n  test -d ./-p && rmdir ./-p\n  as_mkdir_p=false\nfi\n\n\n# as_fn_executable_p FILE\n# -----------------------\n# Test if FILE is an executable regular file.\nas_fn_executable_p ()\n{\n  test -f \"$1\" && test -x \"$1\"\n} # as_fn_executable_p\nas_test_x='test -x'\nas_executable_p=as_fn_executable_p\n\n# Sed expression to map a string onto a valid CPP name.\nas_tr_cpp=\"eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'\"\n\n# Sed expression to map a string onto a valid variable name.\nas_tr_sh=\"eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'\"\n\n\nexec 6>&1\n## ----------------------------------- ##\n## Main body of $CONFIG_STATUS script. ##\n## ----------------------------------- ##\n_ASEOF\ntest $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n# Save the log message, to keep $0 and so on meaningful, and to\n# report actual input values of CONFIG_FILES etc. instead of their\n# values after options handling.\nac_log=\"\nThis file was extended by zookeeper C client $as_me 3.7.0, which was\ngenerated by GNU Autoconf 2.69.  Invocation command line was\n\n  CONFIG_FILES    = $CONFIG_FILES\n  CONFIG_HEADERS  = $CONFIG_HEADERS\n  CONFIG_LINKS    = $CONFIG_LINKS\n  CONFIG_COMMANDS = $CONFIG_COMMANDS\n  $ $0 $@\n\non `(hostname || uname -n) 2>/dev/null | sed 1q`\n\"\n\n_ACEOF\n\ncase $ac_config_files in *\"\n\"*) set x $ac_config_files; shift; ac_config_files=$*;;\nesac\n\ncase $ac_config_headers in *\"\n\"*) set x $ac_config_headers; shift; ac_config_headers=$*;;\nesac\n\n\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n# Files that config.status was made for.\nconfig_files=\"$ac_config_files\"\nconfig_headers=\"$ac_config_headers\"\nconfig_commands=\"$ac_config_commands\"\n\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nac_cs_usage=\"\\\n\\`$as_me' instantiates files and other configuration actions\nfrom templates according to the current configuration.  Unless the files\nand actions are specified as TAGs, all are instantiated by default.\n\nUsage: $0 [OPTION]... [TAG]...\n\n  -h, --help       print this help, then exit\n  -V, --version    print version number and configuration settings, then exit\n      --config     print configuration, then exit\n  -q, --quiet, --silent\n                   do not print progress messages\n  -d, --debug      don't remove temporary files\n      --recheck    update $as_me by reconfiguring in the same conditions\n      --file=FILE[:TEMPLATE]\n                   instantiate the configuration file FILE\n      --header=FILE[:TEMPLATE]\n                   instantiate the configuration header FILE\n\nConfiguration files:\n$config_files\n\nConfiguration headers:\n$config_headers\n\nConfiguration commands:\n$config_commands\n\nReport bugs to <user@zookeeper.apache.org>.\"\n\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\nac_cs_config=\"`$as_echo \"$ac_configure_args\" | sed 's/^ //; s/[\\\\\"\"\\`\\$]/\\\\\\\\&/g'`\"\nac_cs_version=\"\\\\\nzookeeper C client config.status 3.7.0\nconfigured by $0, generated by GNU Autoconf 2.69,\n  with options \\\\\"\\$ac_cs_config\\\\\"\n\nCopyright (C) 2012 Free Software Foundation, Inc.\nThis config.status script is free software; the Free Software Foundation\ngives unlimited permission to copy, distribute and modify it.\"\n\nac_pwd='$ac_pwd'\nsrcdir='$srcdir'\nINSTALL='$INSTALL'\nMKDIR_P='$MKDIR_P'\nAWK='$AWK'\ntest -n \"\\$AWK\" || AWK=awk\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n# The default lists apply if the user does not specify any file.\nac_need_defaults=:\nwhile test $# != 0\ndo\n  case $1 in\n  --*=?*)\n    ac_option=`expr \"X$1\" : 'X\\([^=]*\\)='`\n    ac_optarg=`expr \"X$1\" : 'X[^=]*=\\(.*\\)'`\n    ac_shift=:\n    ;;\n  --*=)\n    ac_option=`expr \"X$1\" : 'X\\([^=]*\\)='`\n    ac_optarg=\n    ac_shift=:\n    ;;\n  *)\n    ac_option=$1\n    ac_optarg=$2\n    ac_shift=shift\n    ;;\n  esac\n\n  case $ac_option in\n  # Handling of the options.\n  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)\n    ac_cs_recheck=: ;;\n  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )\n    $as_echo \"$ac_cs_version\"; exit ;;\n  --config | --confi | --conf | --con | --co | --c )\n    $as_echo \"$ac_cs_config\"; exit ;;\n  --debug | --debu | --deb | --de | --d | -d )\n    debug=: ;;\n  --file | --fil | --fi | --f )\n    $ac_shift\n    case $ac_optarg in\n    *\\'*) ac_optarg=`$as_echo \"$ac_optarg\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    '') as_fn_error $? \"missing file argument\" ;;\n    esac\n    as_fn_append CONFIG_FILES \" '$ac_optarg'\"\n    ac_need_defaults=false;;\n  --header | --heade | --head | --hea )\n    $ac_shift\n    case $ac_optarg in\n    *\\'*) ac_optarg=`$as_echo \"$ac_optarg\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    esac\n    as_fn_append CONFIG_HEADERS \" '$ac_optarg'\"\n    ac_need_defaults=false;;\n  --he | --h)\n    # Conflict between --help and --header\n    as_fn_error $? \"ambiguous option: \\`$1'\nTry \\`$0 --help' for more information.\";;\n  --help | --hel | -h )\n    $as_echo \"$ac_cs_usage\"; exit ;;\n  -q | -quiet | --quiet | --quie | --qui | --qu | --q \\\n  | -silent | --silent | --silen | --sile | --sil | --si | --s)\n    ac_cs_silent=: ;;\n\n  # This is an error.\n  -*) as_fn_error $? \"unrecognized option: \\`$1'\nTry \\`$0 --help' for more information.\" ;;\n\n  *) as_fn_append ac_config_targets \" $1\"\n     ac_need_defaults=false ;;\n\n  esac\n  shift\ndone\n\nac_configure_extra_args=\n\nif $ac_cs_silent; then\n  exec 6>/dev/null\n  ac_configure_extra_args=\"$ac_configure_extra_args --silent\"\nfi\n\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\nif \\$ac_cs_recheck; then\n  set X $SHELL '$0' $ac_configure_args \\$ac_configure_extra_args --no-create --no-recursion\n  shift\n  \\$as_echo \"running CONFIG_SHELL=$SHELL \\$*\" >&6\n  CONFIG_SHELL='$SHELL'\n  export CONFIG_SHELL\n  exec \"\\$@\"\nfi\n\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nexec 5>>config.log\n{\n  echo\n  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX\n## Running $as_me. ##\n_ASBOX\n  $as_echo \"$ac_log\"\n} >&5\n\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n#\n# INIT-COMMANDS\n#\nAMDEP_TRUE=\"$AMDEP_TRUE\" ac_aux_dir=\"$ac_aux_dir\"\n\n\n# The HP-UX ksh and POSIX shell print the target directory to stdout\n# if CDPATH is set.\n(unset CDPATH) >/dev/null 2>&1 && unset CDPATH\n\nsed_quote_subst='$sed_quote_subst'\ndouble_quote_subst='$double_quote_subst'\ndelay_variable_subst='$delay_variable_subst'\nmacro_version='`$ECHO \"$macro_version\" | $SED \"$delay_single_quote_subst\"`'\nmacro_revision='`$ECHO \"$macro_revision\" | $SED \"$delay_single_quote_subst\"`'\nenable_shared='`$ECHO \"$enable_shared\" | $SED \"$delay_single_quote_subst\"`'\nenable_static='`$ECHO \"$enable_static\" | $SED \"$delay_single_quote_subst\"`'\npic_mode='`$ECHO \"$pic_mode\" | $SED \"$delay_single_quote_subst\"`'\nenable_fast_install='`$ECHO \"$enable_fast_install\" | $SED \"$delay_single_quote_subst\"`'\nshared_archive_member_spec='`$ECHO \"$shared_archive_member_spec\" | $SED \"$delay_single_quote_subst\"`'\nSHELL='`$ECHO \"$SHELL\" | $SED \"$delay_single_quote_subst\"`'\nECHO='`$ECHO \"$ECHO\" | $SED \"$delay_single_quote_subst\"`'\nPATH_SEPARATOR='`$ECHO \"$PATH_SEPARATOR\" | $SED \"$delay_single_quote_subst\"`'\nhost_alias='`$ECHO \"$host_alias\" | $SED \"$delay_single_quote_subst\"`'\nhost='`$ECHO \"$host\" | $SED \"$delay_single_quote_subst\"`'\nhost_os='`$ECHO \"$host_os\" | $SED \"$delay_single_quote_subst\"`'\nbuild_alias='`$ECHO \"$build_alias\" | $SED \"$delay_single_quote_subst\"`'\nbuild='`$ECHO \"$build\" | $SED \"$delay_single_quote_subst\"`'\nbuild_os='`$ECHO \"$build_os\" | $SED \"$delay_single_quote_subst\"`'\nSED='`$ECHO \"$SED\" | $SED \"$delay_single_quote_subst\"`'\nXsed='`$ECHO \"$Xsed\" | $SED \"$delay_single_quote_subst\"`'\nGREP='`$ECHO \"$GREP\" | $SED \"$delay_single_quote_subst\"`'\nEGREP='`$ECHO \"$EGREP\" | $SED \"$delay_single_quote_subst\"`'\nFGREP='`$ECHO \"$FGREP\" | $SED \"$delay_single_quote_subst\"`'\nLD='`$ECHO \"$LD\" | $SED \"$delay_single_quote_subst\"`'\nNM='`$ECHO \"$NM\" | $SED \"$delay_single_quote_subst\"`'\nLN_S='`$ECHO \"$LN_S\" | $SED \"$delay_single_quote_subst\"`'\nmax_cmd_len='`$ECHO \"$max_cmd_len\" | $SED \"$delay_single_quote_subst\"`'\nac_objext='`$ECHO \"$ac_objext\" | $SED \"$delay_single_quote_subst\"`'\nexeext='`$ECHO \"$exeext\" | $SED \"$delay_single_quote_subst\"`'\nlt_unset='`$ECHO \"$lt_unset\" | $SED \"$delay_single_quote_subst\"`'\nlt_SP2NL='`$ECHO \"$lt_SP2NL\" | $SED \"$delay_single_quote_subst\"`'\nlt_NL2SP='`$ECHO \"$lt_NL2SP\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_to_host_file_cmd='`$ECHO \"$lt_cv_to_host_file_cmd\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_to_tool_file_cmd='`$ECHO \"$lt_cv_to_tool_file_cmd\" | $SED \"$delay_single_quote_subst\"`'\nreload_flag='`$ECHO \"$reload_flag\" | $SED \"$delay_single_quote_subst\"`'\nreload_cmds='`$ECHO \"$reload_cmds\" | $SED \"$delay_single_quote_subst\"`'\nOBJDUMP='`$ECHO \"$OBJDUMP\" | $SED \"$delay_single_quote_subst\"`'\ndeplibs_check_method='`$ECHO \"$deplibs_check_method\" | $SED \"$delay_single_quote_subst\"`'\nfile_magic_cmd='`$ECHO \"$file_magic_cmd\" | $SED \"$delay_single_quote_subst\"`'\nfile_magic_glob='`$ECHO \"$file_magic_glob\" | $SED \"$delay_single_quote_subst\"`'\nwant_nocaseglob='`$ECHO \"$want_nocaseglob\" | $SED \"$delay_single_quote_subst\"`'\nDLLTOOL='`$ECHO \"$DLLTOOL\" | $SED \"$delay_single_quote_subst\"`'\nsharedlib_from_linklib_cmd='`$ECHO \"$sharedlib_from_linklib_cmd\" | $SED \"$delay_single_quote_subst\"`'\nAR='`$ECHO \"$AR\" | $SED \"$delay_single_quote_subst\"`'\nAR_FLAGS='`$ECHO \"$AR_FLAGS\" | $SED \"$delay_single_quote_subst\"`'\narchiver_list_spec='`$ECHO \"$archiver_list_spec\" | $SED \"$delay_single_quote_subst\"`'\nSTRIP='`$ECHO \"$STRIP\" | $SED \"$delay_single_quote_subst\"`'\nRANLIB='`$ECHO \"$RANLIB\" | $SED \"$delay_single_quote_subst\"`'\nold_postinstall_cmds='`$ECHO \"$old_postinstall_cmds\" | $SED \"$delay_single_quote_subst\"`'\nold_postuninstall_cmds='`$ECHO \"$old_postuninstall_cmds\" | $SED \"$delay_single_quote_subst\"`'\nold_archive_cmds='`$ECHO \"$old_archive_cmds\" | $SED \"$delay_single_quote_subst\"`'\nlock_old_archive_extraction='`$ECHO \"$lock_old_archive_extraction\" | $SED \"$delay_single_quote_subst\"`'\nCC='`$ECHO \"$CC\" | $SED \"$delay_single_quote_subst\"`'\nCFLAGS='`$ECHO \"$CFLAGS\" | $SED \"$delay_single_quote_subst\"`'\ncompiler='`$ECHO \"$compiler\" | $SED \"$delay_single_quote_subst\"`'\nGCC='`$ECHO \"$GCC\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_sys_global_symbol_pipe='`$ECHO \"$lt_cv_sys_global_symbol_pipe\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_sys_global_symbol_to_cdecl='`$ECHO \"$lt_cv_sys_global_symbol_to_cdecl\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_sys_global_symbol_to_import='`$ECHO \"$lt_cv_sys_global_symbol_to_import\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_sys_global_symbol_to_c_name_address='`$ECHO \"$lt_cv_sys_global_symbol_to_c_name_address\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO \"$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_nm_interface='`$ECHO \"$lt_cv_nm_interface\" | $SED \"$delay_single_quote_subst\"`'\nnm_file_list_spec='`$ECHO \"$nm_file_list_spec\" | $SED \"$delay_single_quote_subst\"`'\nlt_sysroot='`$ECHO \"$lt_sysroot\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_truncate_bin='`$ECHO \"$lt_cv_truncate_bin\" | $SED \"$delay_single_quote_subst\"`'\nobjdir='`$ECHO \"$objdir\" | $SED \"$delay_single_quote_subst\"`'\nMAGIC_CMD='`$ECHO \"$MAGIC_CMD\" | $SED \"$delay_single_quote_subst\"`'\nlt_prog_compiler_no_builtin_flag='`$ECHO \"$lt_prog_compiler_no_builtin_flag\" | $SED \"$delay_single_quote_subst\"`'\nlt_prog_compiler_pic='`$ECHO \"$lt_prog_compiler_pic\" | $SED \"$delay_single_quote_subst\"`'\nlt_prog_compiler_wl='`$ECHO \"$lt_prog_compiler_wl\" | $SED \"$delay_single_quote_subst\"`'\nlt_prog_compiler_static='`$ECHO \"$lt_prog_compiler_static\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_prog_compiler_c_o='`$ECHO \"$lt_cv_prog_compiler_c_o\" | $SED \"$delay_single_quote_subst\"`'\nneed_locks='`$ECHO \"$need_locks\" | $SED \"$delay_single_quote_subst\"`'\nMANIFEST_TOOL='`$ECHO \"$MANIFEST_TOOL\" | $SED \"$delay_single_quote_subst\"`'\nDSYMUTIL='`$ECHO \"$DSYMUTIL\" | $SED \"$delay_single_quote_subst\"`'\nNMEDIT='`$ECHO \"$NMEDIT\" | $SED \"$delay_single_quote_subst\"`'\nLIPO='`$ECHO \"$LIPO\" | $SED \"$delay_single_quote_subst\"`'\nOTOOL='`$ECHO \"$OTOOL\" | $SED \"$delay_single_quote_subst\"`'\nOTOOL64='`$ECHO \"$OTOOL64\" | $SED \"$delay_single_quote_subst\"`'\nlibext='`$ECHO \"$libext\" | $SED \"$delay_single_quote_subst\"`'\nshrext_cmds='`$ECHO \"$shrext_cmds\" | $SED \"$delay_single_quote_subst\"`'\nextract_expsyms_cmds='`$ECHO \"$extract_expsyms_cmds\" | $SED \"$delay_single_quote_subst\"`'\narchive_cmds_need_lc='`$ECHO \"$archive_cmds_need_lc\" | $SED \"$delay_single_quote_subst\"`'\nenable_shared_with_static_runtimes='`$ECHO \"$enable_shared_with_static_runtimes\" | $SED \"$delay_single_quote_subst\"`'\nexport_dynamic_flag_spec='`$ECHO \"$export_dynamic_flag_spec\" | $SED \"$delay_single_quote_subst\"`'\nwhole_archive_flag_spec='`$ECHO \"$whole_archive_flag_spec\" | $SED \"$delay_single_quote_subst\"`'\ncompiler_needs_object='`$ECHO \"$compiler_needs_object\" | $SED \"$delay_single_quote_subst\"`'\nold_archive_from_new_cmds='`$ECHO \"$old_archive_from_new_cmds\" | $SED \"$delay_single_quote_subst\"`'\nold_archive_from_expsyms_cmds='`$ECHO \"$old_archive_from_expsyms_cmds\" | $SED \"$delay_single_quote_subst\"`'\narchive_cmds='`$ECHO \"$archive_cmds\" | $SED \"$delay_single_quote_subst\"`'\narchive_expsym_cmds='`$ECHO \"$archive_expsym_cmds\" | $SED \"$delay_single_quote_subst\"`'\nmodule_cmds='`$ECHO \"$module_cmds\" | $SED \"$delay_single_quote_subst\"`'\nmodule_expsym_cmds='`$ECHO \"$module_expsym_cmds\" | $SED \"$delay_single_quote_subst\"`'\nwith_gnu_ld='`$ECHO \"$with_gnu_ld\" | $SED \"$delay_single_quote_subst\"`'\nallow_undefined_flag='`$ECHO \"$allow_undefined_flag\" | $SED \"$delay_single_quote_subst\"`'\nno_undefined_flag='`$ECHO \"$no_undefined_flag\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_libdir_flag_spec='`$ECHO \"$hardcode_libdir_flag_spec\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_libdir_separator='`$ECHO \"$hardcode_libdir_separator\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_direct='`$ECHO \"$hardcode_direct\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_direct_absolute='`$ECHO \"$hardcode_direct_absolute\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_minus_L='`$ECHO \"$hardcode_minus_L\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_shlibpath_var='`$ECHO \"$hardcode_shlibpath_var\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_automatic='`$ECHO \"$hardcode_automatic\" | $SED \"$delay_single_quote_subst\"`'\ninherit_rpath='`$ECHO \"$inherit_rpath\" | $SED \"$delay_single_quote_subst\"`'\nlink_all_deplibs='`$ECHO \"$link_all_deplibs\" | $SED \"$delay_single_quote_subst\"`'\nalways_export_symbols='`$ECHO \"$always_export_symbols\" | $SED \"$delay_single_quote_subst\"`'\nexport_symbols_cmds='`$ECHO \"$export_symbols_cmds\" | $SED \"$delay_single_quote_subst\"`'\nexclude_expsyms='`$ECHO \"$exclude_expsyms\" | $SED \"$delay_single_quote_subst\"`'\ninclude_expsyms='`$ECHO \"$include_expsyms\" | $SED \"$delay_single_quote_subst\"`'\nprelink_cmds='`$ECHO \"$prelink_cmds\" | $SED \"$delay_single_quote_subst\"`'\npostlink_cmds='`$ECHO \"$postlink_cmds\" | $SED \"$delay_single_quote_subst\"`'\nfile_list_spec='`$ECHO \"$file_list_spec\" | $SED \"$delay_single_quote_subst\"`'\nvariables_saved_for_relink='`$ECHO \"$variables_saved_for_relink\" | $SED \"$delay_single_quote_subst\"`'\nneed_lib_prefix='`$ECHO \"$need_lib_prefix\" | $SED \"$delay_single_quote_subst\"`'\nneed_version='`$ECHO \"$need_version\" | $SED \"$delay_single_quote_subst\"`'\nversion_type='`$ECHO \"$version_type\" | $SED \"$delay_single_quote_subst\"`'\nrunpath_var='`$ECHO \"$runpath_var\" | $SED \"$delay_single_quote_subst\"`'\nshlibpath_var='`$ECHO \"$shlibpath_var\" | $SED \"$delay_single_quote_subst\"`'\nshlibpath_overrides_runpath='`$ECHO \"$shlibpath_overrides_runpath\" | $SED \"$delay_single_quote_subst\"`'\nlibname_spec='`$ECHO \"$libname_spec\" | $SED \"$delay_single_quote_subst\"`'\nlibrary_names_spec='`$ECHO \"$library_names_spec\" | $SED \"$delay_single_quote_subst\"`'\nsoname_spec='`$ECHO \"$soname_spec\" | $SED \"$delay_single_quote_subst\"`'\ninstall_override_mode='`$ECHO \"$install_override_mode\" | $SED \"$delay_single_quote_subst\"`'\npostinstall_cmds='`$ECHO \"$postinstall_cmds\" | $SED \"$delay_single_quote_subst\"`'\npostuninstall_cmds='`$ECHO \"$postuninstall_cmds\" | $SED \"$delay_single_quote_subst\"`'\nfinish_cmds='`$ECHO \"$finish_cmds\" | $SED \"$delay_single_quote_subst\"`'\nfinish_eval='`$ECHO \"$finish_eval\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_into_libs='`$ECHO \"$hardcode_into_libs\" | $SED \"$delay_single_quote_subst\"`'\nsys_lib_search_path_spec='`$ECHO \"$sys_lib_search_path_spec\" | $SED \"$delay_single_quote_subst\"`'\nconfigure_time_dlsearch_path='`$ECHO \"$configure_time_dlsearch_path\" | $SED \"$delay_single_quote_subst\"`'\nconfigure_time_lt_sys_library_path='`$ECHO \"$configure_time_lt_sys_library_path\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_action='`$ECHO \"$hardcode_action\" | $SED \"$delay_single_quote_subst\"`'\nenable_dlopen='`$ECHO \"$enable_dlopen\" | $SED \"$delay_single_quote_subst\"`'\nenable_dlopen_self='`$ECHO \"$enable_dlopen_self\" | $SED \"$delay_single_quote_subst\"`'\nenable_dlopen_self_static='`$ECHO \"$enable_dlopen_self_static\" | $SED \"$delay_single_quote_subst\"`'\nold_striplib='`$ECHO \"$old_striplib\" | $SED \"$delay_single_quote_subst\"`'\nstriplib='`$ECHO \"$striplib\" | $SED \"$delay_single_quote_subst\"`'\ncompiler_lib_search_dirs='`$ECHO \"$compiler_lib_search_dirs\" | $SED \"$delay_single_quote_subst\"`'\npredep_objects='`$ECHO \"$predep_objects\" | $SED \"$delay_single_quote_subst\"`'\npostdep_objects='`$ECHO \"$postdep_objects\" | $SED \"$delay_single_quote_subst\"`'\npredeps='`$ECHO \"$predeps\" | $SED \"$delay_single_quote_subst\"`'\npostdeps='`$ECHO \"$postdeps\" | $SED \"$delay_single_quote_subst\"`'\ncompiler_lib_search_path='`$ECHO \"$compiler_lib_search_path\" | $SED \"$delay_single_quote_subst\"`'\nLD_CXX='`$ECHO \"$LD_CXX\" | $SED \"$delay_single_quote_subst\"`'\nreload_flag_CXX='`$ECHO \"$reload_flag_CXX\" | $SED \"$delay_single_quote_subst\"`'\nreload_cmds_CXX='`$ECHO \"$reload_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\nold_archive_cmds_CXX='`$ECHO \"$old_archive_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\ncompiler_CXX='`$ECHO \"$compiler_CXX\" | $SED \"$delay_single_quote_subst\"`'\nGCC_CXX='`$ECHO \"$GCC_CXX\" | $SED \"$delay_single_quote_subst\"`'\nlt_prog_compiler_no_builtin_flag_CXX='`$ECHO \"$lt_prog_compiler_no_builtin_flag_CXX\" | $SED \"$delay_single_quote_subst\"`'\nlt_prog_compiler_pic_CXX='`$ECHO \"$lt_prog_compiler_pic_CXX\" | $SED \"$delay_single_quote_subst\"`'\nlt_prog_compiler_wl_CXX='`$ECHO \"$lt_prog_compiler_wl_CXX\" | $SED \"$delay_single_quote_subst\"`'\nlt_prog_compiler_static_CXX='`$ECHO \"$lt_prog_compiler_static_CXX\" | $SED \"$delay_single_quote_subst\"`'\nlt_cv_prog_compiler_c_o_CXX='`$ECHO \"$lt_cv_prog_compiler_c_o_CXX\" | $SED \"$delay_single_quote_subst\"`'\narchive_cmds_need_lc_CXX='`$ECHO \"$archive_cmds_need_lc_CXX\" | $SED \"$delay_single_quote_subst\"`'\nenable_shared_with_static_runtimes_CXX='`$ECHO \"$enable_shared_with_static_runtimes_CXX\" | $SED \"$delay_single_quote_subst\"`'\nexport_dynamic_flag_spec_CXX='`$ECHO \"$export_dynamic_flag_spec_CXX\" | $SED \"$delay_single_quote_subst\"`'\nwhole_archive_flag_spec_CXX='`$ECHO \"$whole_archive_flag_spec_CXX\" | $SED \"$delay_single_quote_subst\"`'\ncompiler_needs_object_CXX='`$ECHO \"$compiler_needs_object_CXX\" | $SED \"$delay_single_quote_subst\"`'\nold_archive_from_new_cmds_CXX='`$ECHO \"$old_archive_from_new_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\nold_archive_from_expsyms_cmds_CXX='`$ECHO \"$old_archive_from_expsyms_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\narchive_cmds_CXX='`$ECHO \"$archive_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\narchive_expsym_cmds_CXX='`$ECHO \"$archive_expsym_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\nmodule_cmds_CXX='`$ECHO \"$module_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\nmodule_expsym_cmds_CXX='`$ECHO \"$module_expsym_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\nwith_gnu_ld_CXX='`$ECHO \"$with_gnu_ld_CXX\" | $SED \"$delay_single_quote_subst\"`'\nallow_undefined_flag_CXX='`$ECHO \"$allow_undefined_flag_CXX\" | $SED \"$delay_single_quote_subst\"`'\nno_undefined_flag_CXX='`$ECHO \"$no_undefined_flag_CXX\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_libdir_flag_spec_CXX='`$ECHO \"$hardcode_libdir_flag_spec_CXX\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_libdir_separator_CXX='`$ECHO \"$hardcode_libdir_separator_CXX\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_direct_CXX='`$ECHO \"$hardcode_direct_CXX\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_direct_absolute_CXX='`$ECHO \"$hardcode_direct_absolute_CXX\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_minus_L_CXX='`$ECHO \"$hardcode_minus_L_CXX\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_shlibpath_var_CXX='`$ECHO \"$hardcode_shlibpath_var_CXX\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_automatic_CXX='`$ECHO \"$hardcode_automatic_CXX\" | $SED \"$delay_single_quote_subst\"`'\ninherit_rpath_CXX='`$ECHO \"$inherit_rpath_CXX\" | $SED \"$delay_single_quote_subst\"`'\nlink_all_deplibs_CXX='`$ECHO \"$link_all_deplibs_CXX\" | $SED \"$delay_single_quote_subst\"`'\nalways_export_symbols_CXX='`$ECHO \"$always_export_symbols_CXX\" | $SED \"$delay_single_quote_subst\"`'\nexport_symbols_cmds_CXX='`$ECHO \"$export_symbols_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\nexclude_expsyms_CXX='`$ECHO \"$exclude_expsyms_CXX\" | $SED \"$delay_single_quote_subst\"`'\ninclude_expsyms_CXX='`$ECHO \"$include_expsyms_CXX\" | $SED \"$delay_single_quote_subst\"`'\nprelink_cmds_CXX='`$ECHO \"$prelink_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\npostlink_cmds_CXX='`$ECHO \"$postlink_cmds_CXX\" | $SED \"$delay_single_quote_subst\"`'\nfile_list_spec_CXX='`$ECHO \"$file_list_spec_CXX\" | $SED \"$delay_single_quote_subst\"`'\nhardcode_action_CXX='`$ECHO \"$hardcode_action_CXX\" | $SED \"$delay_single_quote_subst\"`'\ncompiler_lib_search_dirs_CXX='`$ECHO \"$compiler_lib_search_dirs_CXX\" | $SED \"$delay_single_quote_subst\"`'\npredep_objects_CXX='`$ECHO \"$predep_objects_CXX\" | $SED \"$delay_single_quote_subst\"`'\npostdep_objects_CXX='`$ECHO \"$postdep_objects_CXX\" | $SED \"$delay_single_quote_subst\"`'\npredeps_CXX='`$ECHO \"$predeps_CXX\" | $SED \"$delay_single_quote_subst\"`'\npostdeps_CXX='`$ECHO \"$postdeps_CXX\" | $SED \"$delay_single_quote_subst\"`'\ncompiler_lib_search_path_CXX='`$ECHO \"$compiler_lib_search_path_CXX\" | $SED \"$delay_single_quote_subst\"`'\n\nLTCC='$LTCC'\nLTCFLAGS='$LTCFLAGS'\ncompiler='$compiler_DEFAULT'\n\n# A function that is used when there is no print builtin or printf.\nfunc_fallback_echo ()\n{\n  eval 'cat <<_LTECHO_EOF\n\\$1\n_LTECHO_EOF'\n}\n\n# Quote evaled strings.\nfor var in SHELL \\\nECHO \\\nPATH_SEPARATOR \\\nSED \\\nGREP \\\nEGREP \\\nFGREP \\\nLD \\\nNM \\\nLN_S \\\nlt_SP2NL \\\nlt_NL2SP \\\nreload_flag \\\nOBJDUMP \\\ndeplibs_check_method \\\nfile_magic_cmd \\\nfile_magic_glob \\\nwant_nocaseglob \\\nDLLTOOL \\\nsharedlib_from_linklib_cmd \\\nAR \\\nAR_FLAGS \\\narchiver_list_spec \\\nSTRIP \\\nRANLIB \\\nCC \\\nCFLAGS \\\ncompiler \\\nlt_cv_sys_global_symbol_pipe \\\nlt_cv_sys_global_symbol_to_cdecl \\\nlt_cv_sys_global_symbol_to_import \\\nlt_cv_sys_global_symbol_to_c_name_address \\\nlt_cv_sys_global_symbol_to_c_name_address_lib_prefix \\\nlt_cv_nm_interface \\\nnm_file_list_spec \\\nlt_cv_truncate_bin \\\nlt_prog_compiler_no_builtin_flag \\\nlt_prog_compiler_pic \\\nlt_prog_compiler_wl \\\nlt_prog_compiler_static \\\nlt_cv_prog_compiler_c_o \\\nneed_locks \\\nMANIFEST_TOOL \\\nDSYMUTIL \\\nNMEDIT \\\nLIPO \\\nOTOOL \\\nOTOOL64 \\\nshrext_cmds \\\nexport_dynamic_flag_spec \\\nwhole_archive_flag_spec \\\ncompiler_needs_object \\\nwith_gnu_ld \\\nallow_undefined_flag \\\nno_undefined_flag \\\nhardcode_libdir_flag_spec \\\nhardcode_libdir_separator \\\nexclude_expsyms \\\ninclude_expsyms \\\nfile_list_spec \\\nvariables_saved_for_relink \\\nlibname_spec \\\nlibrary_names_spec \\\nsoname_spec \\\ninstall_override_mode \\\nfinish_eval \\\nold_striplib \\\nstriplib \\\ncompiler_lib_search_dirs \\\npredep_objects \\\npostdep_objects \\\npredeps \\\npostdeps \\\ncompiler_lib_search_path \\\nLD_CXX \\\nreload_flag_CXX \\\ncompiler_CXX \\\nlt_prog_compiler_no_builtin_flag_CXX \\\nlt_prog_compiler_pic_CXX \\\nlt_prog_compiler_wl_CXX \\\nlt_prog_compiler_static_CXX \\\nlt_cv_prog_compiler_c_o_CXX \\\nexport_dynamic_flag_spec_CXX \\\nwhole_archive_flag_spec_CXX \\\ncompiler_needs_object_CXX \\\nwith_gnu_ld_CXX \\\nallow_undefined_flag_CXX \\\nno_undefined_flag_CXX \\\nhardcode_libdir_flag_spec_CXX \\\nhardcode_libdir_separator_CXX \\\nexclude_expsyms_CXX \\\ninclude_expsyms_CXX \\\nfile_list_spec_CXX \\\ncompiler_lib_search_dirs_CXX \\\npredep_objects_CXX \\\npostdep_objects_CXX \\\npredeps_CXX \\\npostdeps_CXX \\\ncompiler_lib_search_path_CXX; do\n    case \\`eval \\\\\\\\\\$ECHO \\\\\\\\\"\"\\\\\\\\\\$\\$var\"\\\\\\\\\"\\` in\n    *[\\\\\\\\\\\\\\`\\\\\"\\\\\\$]*)\n      eval \"lt_\\$var=\\\\\\\\\\\\\"\\\\\\`\\\\\\$ECHO \\\\\"\\\\\\$\\$var\\\\\" | \\\\\\$SED \\\\\"\\\\\\$sed_quote_subst\\\\\"\\\\\\`\\\\\\\\\\\\\"\" ## exclude from sc_prohibit_nested_quotes\n      ;;\n    *)\n      eval \"lt_\\$var=\\\\\\\\\\\\\"\\\\\\$\\$var\\\\\\\\\\\\\"\"\n      ;;\n    esac\ndone\n\n# Double-quote double-evaled strings.\nfor var in reload_cmds \\\nold_postinstall_cmds \\\nold_postuninstall_cmds \\\nold_archive_cmds \\\nextract_expsyms_cmds \\\nold_archive_from_new_cmds \\\nold_archive_from_expsyms_cmds \\\narchive_cmds \\\narchive_expsym_cmds \\\nmodule_cmds \\\nmodule_expsym_cmds \\\nexport_symbols_cmds \\\nprelink_cmds \\\npostlink_cmds \\\npostinstall_cmds \\\npostuninstall_cmds \\\nfinish_cmds \\\nsys_lib_search_path_spec \\\nconfigure_time_dlsearch_path \\\nconfigure_time_lt_sys_library_path \\\nreload_cmds_CXX \\\nold_archive_cmds_CXX \\\nold_archive_from_new_cmds_CXX \\\nold_archive_from_expsyms_cmds_CXX \\\narchive_cmds_CXX \\\narchive_expsym_cmds_CXX \\\nmodule_cmds_CXX \\\nmodule_expsym_cmds_CXX \\\nexport_symbols_cmds_CXX \\\nprelink_cmds_CXX \\\npostlink_cmds_CXX; do\n    case \\`eval \\\\\\\\\\$ECHO \\\\\\\\\"\"\\\\\\\\\\$\\$var\"\\\\\\\\\"\\` in\n    *[\\\\\\\\\\\\\\`\\\\\"\\\\\\$]*)\n      eval \"lt_\\$var=\\\\\\\\\\\\\"\\\\\\`\\\\\\$ECHO \\\\\"\\\\\\$\\$var\\\\\" | \\\\\\$SED -e \\\\\"\\\\\\$double_quote_subst\\\\\" -e \\\\\"\\\\\\$sed_quote_subst\\\\\" -e \\\\\"\\\\\\$delay_variable_subst\\\\\"\\\\\\`\\\\\\\\\\\\\"\" ## exclude from sc_prohibit_nested_quotes\n      ;;\n    *)\n      eval \"lt_\\$var=\\\\\\\\\\\\\"\\\\\\$\\$var\\\\\\\\\\\\\"\"\n      ;;\n    esac\ndone\n\nac_aux_dir='$ac_aux_dir'\n\n# See if we are running on zsh, and set the options that allow our\n# commands through without removal of \\ escapes INIT.\nif test -n \"\\${ZSH_VERSION+set}\"; then\n   setopt NO_GLOB_SUBST\nfi\n\n\n    PACKAGE='$PACKAGE'\n    VERSION='$VERSION'\n    RM='$RM'\n    ofile='$ofile'\n\n\n\n\n\n\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n\n# Handling of arguments.\nfor ac_config_target in $ac_config_targets\ndo\n  case $ac_config_target in\n    \"config.h\") CONFIG_HEADERS=\"$CONFIG_HEADERS config.h\" ;;\n    \"depfiles\") CONFIG_COMMANDS=\"$CONFIG_COMMANDS depfiles\" ;;\n    \"libtool\") CONFIG_COMMANDS=\"$CONFIG_COMMANDS libtool\" ;;\n    \"Makefile\") CONFIG_FILES=\"$CONFIG_FILES Makefile\" ;;\n\n  *) as_fn_error $? \"invalid argument: \\`$ac_config_target'\" \"$LINENO\" 5;;\n  esac\ndone\n\n\n# If the user did not use the arguments to specify the items to instantiate,\n# then the envvar interface is used.  Set only those that are not.\n# We use the long form for the default assignment because of an extremely\n# bizarre bug on SunOS 4.1.3.\nif $ac_need_defaults; then\n  test \"${CONFIG_FILES+set}\" = set || CONFIG_FILES=$config_files\n  test \"${CONFIG_HEADERS+set}\" = set || CONFIG_HEADERS=$config_headers\n  test \"${CONFIG_COMMANDS+set}\" = set || CONFIG_COMMANDS=$config_commands\nfi\n\n# Have a temporary directory for convenience.  Make it in the build tree\n# simply because there is no reason against having it here, and in addition,\n# creating and moving files from /tmp can sometimes cause problems.\n# Hook for its removal unless debugging.\n# Note that there is a small window in which the directory will not be cleaned:\n# after its creation but before its name has been assigned to `$tmp'.\n$debug ||\n{\n  tmp= ac_tmp=\n  trap 'exit_status=$?\n  : \"${ac_tmp:=$tmp}\"\n  { test ! -d \"$ac_tmp\" || rm -fr \"$ac_tmp\"; } && exit $exit_status\n' 0\n  trap 'as_fn_exit 1' 1 2 13 15\n}\n# Create a (secure) tmp directory for tmp files.\n\n{\n  tmp=`(umask 077 && mktemp -d \"./confXXXXXX\") 2>/dev/null` &&\n  test -d \"$tmp\"\n}  ||\n{\n  tmp=./conf$$-$RANDOM\n  (umask 077 && mkdir \"$tmp\")\n} || as_fn_error $? \"cannot create a temporary directory in .\" \"$LINENO\" 5\nac_tmp=$tmp\n\n# Set up the scripts for CONFIG_FILES section.\n# No need to generate them if there are no CONFIG_FILES.\n# This happens for instance with `./config.status config.h'.\nif test -n \"$CONFIG_FILES\"; then\n\n\nac_cr=`echo X | tr X '\\015'`\n# On cygwin, bash can eat \\r inside `` if the user requested igncr.\n# But we know of no other shell where ac_cr would be empty at this\n# point, so we can use a bashism as a fallback.\nif test \"x$ac_cr\" = x; then\n  eval ac_cr=\\$\\'\\\\r\\'\nfi\nac_cs_awk_cr=`$AWK 'BEGIN { print \"a\\rb\" }' </dev/null 2>/dev/null`\nif test \"$ac_cs_awk_cr\" = \"a${ac_cr}b\"; then\n  ac_cs_awk_cr='\\\\r'\nelse\n  ac_cs_awk_cr=$ac_cr\nfi\n\necho 'BEGIN {' >\"$ac_tmp/subs1.awk\" &&\n_ACEOF\n\n\n{\n  echo \"cat >conf$$subs.awk <<_ACEOF\" &&\n  echo \"$ac_subst_vars\" | sed 's/.*/&!$&$ac_delim/' &&\n  echo \"_ACEOF\"\n} >conf$$subs.sh ||\n  as_fn_error $? \"could not make $CONFIG_STATUS\" \"$LINENO\" 5\nac_delim_num=`echo \"$ac_subst_vars\" | grep -c '^'`\nac_delim='%!_!# '\nfor ac_last_try in false false false false false :; do\n  . ./conf$$subs.sh ||\n    as_fn_error $? \"could not make $CONFIG_STATUS\" \"$LINENO\" 5\n\n  ac_delim_n=`sed -n \"s/.*$ac_delim\\$/X/p\" conf$$subs.awk | grep -c X`\n  if test $ac_delim_n = $ac_delim_num; then\n    break\n  elif $ac_last_try; then\n    as_fn_error $? \"could not make $CONFIG_STATUS\" \"$LINENO\" 5\n  else\n    ac_delim=\"$ac_delim!$ac_delim _$ac_delim!! \"\n  fi\ndone\nrm -f conf$$subs.sh\n\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\ncat >>\"\\$ac_tmp/subs1.awk\" <<\\\\_ACAWK &&\n_ACEOF\nsed -n '\nh\ns/^/S[\"/; s/!.*/\"]=/\np\ng\ns/^[^!]*!//\n:repl\nt repl\ns/'\"$ac_delim\"'$//\nt delim\n:nl\nh\ns/\\(.\\{148\\}\\)..*/\\1/\nt more1\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\\\\n\"\\\\/\np\nn\nb repl\n:more1\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"\\\\/\np\ng\ns/.\\{148\\}//\nt nl\n:delim\nh\ns/\\(.\\{148\\}\\)..*/\\1/\nt more2\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"/\np\nb\n:more2\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"\\\\/\np\ng\ns/.\\{148\\}//\nt delim\n' <conf$$subs.awk | sed '\n/^[^\"\"]/{\n  N\n  s/\\n//\n}\n' >>$CONFIG_STATUS || ac_write_fail=1\nrm -f conf$$subs.awk\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n_ACAWK\ncat >>\"\\$ac_tmp/subs1.awk\" <<_ACAWK &&\n  for (key in S) S_is_set[key] = 1\n  FS = \"\u0007\"\n\n}\n{\n  line = $ 0\n  nfields = split(line, field, \"@\")\n  substed = 0\n  len = length(field[1])\n  for (i = 2; i < nfields; i++) {\n    key = field[i]\n    keylen = length(key)\n    if (S_is_set[key]) {\n      value = S[key]\n      line = substr(line, 1, len) \"\" value \"\" substr(line, len + keylen + 3)\n      len += length(value) + length(field[++i])\n      substed = 1\n    } else\n      len += 1 + keylen\n  }\n\n  print line\n}\n\n_ACAWK\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nif sed \"s/$ac_cr//\" < /dev/null > /dev/null 2>&1; then\n  sed \"s/$ac_cr\\$//; s/$ac_cr/$ac_cs_awk_cr/g\"\nelse\n  cat\nfi < \"$ac_tmp/subs1.awk\" > \"$ac_tmp/subs.awk\" \\\n  || as_fn_error $? \"could not setup config files machinery\" \"$LINENO\" 5\n_ACEOF\n\n# VPATH may cause trouble with some makes, so we remove sole $(srcdir),\n# ${srcdir} and @srcdir@ entries from VPATH if srcdir is \".\", strip leading and\n# trailing colons and then remove the whole line if VPATH becomes empty\n# (actually we leave an empty line to preserve line numbers).\nif test \"x$srcdir\" = x.; then\n  ac_vpsub='/^[\t ]*VPATH[\t ]*=[\t ]*/{\nh\ns///\ns/^/:/\ns/[\t ]*$/:/\ns/:\\$(srcdir):/:/g\ns/:\\${srcdir}:/:/g\ns/:@srcdir@:/:/g\ns/^:*//\ns/:*$//\nx\ns/\\(=[\t ]*\\).*/\\1/\nG\ns/\\n//\ns/^[^=]*=[\t ]*$//\n}'\nfi\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nfi # test -n \"$CONFIG_FILES\"\n\n# Set up the scripts for CONFIG_HEADERS section.\n# No need to generate them if there are no CONFIG_HEADERS.\n# This happens for instance with `./config.status Makefile'.\nif test -n \"$CONFIG_HEADERS\"; then\ncat >\"$ac_tmp/defines.awk\" <<\\_ACAWK ||\nBEGIN {\n_ACEOF\n\n# Transform confdefs.h into an awk script `defines.awk', embedded as\n# here-document in config.status, that substitutes the proper values into\n# config.h.in to produce config.h.\n\n# Create a delimiter string that does not exist in confdefs.h, to ease\n# handling of long lines.\nac_delim='%!_!# '\nfor ac_last_try in false false :; do\n  ac_tt=`sed -n \"/$ac_delim/p\" confdefs.h`\n  if test -z \"$ac_tt\"; then\n    break\n  elif $ac_last_try; then\n    as_fn_error $? \"could not make $CONFIG_HEADERS\" \"$LINENO\" 5\n  else\n    ac_delim=\"$ac_delim!$ac_delim _$ac_delim!! \"\n  fi\ndone\n\n# For the awk script, D is an array of macro values keyed by name,\n# likewise P contains macro parameters if any.  Preserve backslash\n# newline sequences.\n\nac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*\nsed -n '\ns/.\\{148\\}/&'\"$ac_delim\"'/g\nt rset\n:rset\ns/^[\t ]*#[\t ]*define[\t ][\t ]*/ /\nt def\nd\n:def\ns/\\\\$//\nt bsnl\ns/[\"\\\\]/\\\\&/g\ns/^ \\('\"$ac_word_re\"'\\)\\(([^()]*)\\)[\t ]*\\(.*\\)/P[\"\\1\"]=\"\\2\"\\\nD[\"\\1\"]=\" \\3\"/p\ns/^ \\('\"$ac_word_re\"'\\)[\t ]*\\(.*\\)/D[\"\\1\"]=\" \\2\"/p\nd\n:bsnl\ns/[\"\\\\]/\\\\&/g\ns/^ \\('\"$ac_word_re\"'\\)\\(([^()]*)\\)[\t ]*\\(.*\\)/P[\"\\1\"]=\"\\2\"\\\nD[\"\\1\"]=\" \\3\\\\\\\\\\\\n\"\\\\/p\nt cont\ns/^ \\('\"$ac_word_re\"'\\)[\t ]*\\(.*\\)/D[\"\\1\"]=\" \\2\\\\\\\\\\\\n\"\\\\/p\nt cont\nd\n:cont\nn\ns/.\\{148\\}/&'\"$ac_delim\"'/g\nt clear\n:clear\ns/\\\\$//\nt bsnlc\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"/p\nd\n:bsnlc\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\\\\\\\\\\\\n\"\\\\/p\nb cont\n' <confdefs.h | sed '\ns/'\"$ac_delim\"'/\"\\\\\\\n\"/g' >>$CONFIG_STATUS || ac_write_fail=1\n\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n  for (key in D) D_is_set[key] = 1\n  FS = \"\u0007\"\n}\n/^[\\t ]*#[\\t ]*(define|undef)[\\t ]+$ac_word_re([\\t (]|\\$)/ {\n  line = \\$ 0\n  split(line, arg, \" \")\n  if (arg[1] == \"#\") {\n    defundef = arg[2]\n    mac1 = arg[3]\n  } else {\n    defundef = substr(arg[1], 2)\n    mac1 = arg[2]\n  }\n  split(mac1, mac2, \"(\") #)\n  macro = mac2[1]\n  prefix = substr(line, 1, index(line, defundef) - 1)\n  if (D_is_set[macro]) {\n    # Preserve the white space surrounding the \"#\".\n    print prefix \"define\", macro P[macro] D[macro]\n    next\n  } else {\n    # Replace #undef with comments.  This is necessary, for example,\n    # in the case of _POSIX_SOURCE, which is predefined and required\n    # on some systems where configure will not decide to define it.\n    if (defundef == \"undef\") {\n      print \"/*\", prefix defundef, macro, \"*/\"\n      next\n    }\n  }\n}\n{ print }\n_ACAWK\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n  as_fn_error $? \"could not setup config headers machinery\" \"$LINENO\" 5\nfi # test -n \"$CONFIG_HEADERS\"\n\n\neval set X \"  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS\"\nshift\nfor ac_tag\ndo\n  case $ac_tag in\n  :[FHLC]) ac_mode=$ac_tag; continue;;\n  esac\n  case $ac_mode$ac_tag in\n  :[FHL]*:*);;\n  :L* | :C*:*) as_fn_error $? \"invalid tag \\`$ac_tag'\" \"$LINENO\" 5;;\n  :[FH]-) ac_tag=-:-;;\n  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;\n  esac\n  ac_save_IFS=$IFS\n  IFS=:\n  set x $ac_tag\n  IFS=$ac_save_IFS\n  shift\n  ac_file=$1\n  shift\n\n  case $ac_mode in\n  :L) ac_source=$1;;\n  :[FH])\n    ac_file_inputs=\n    for ac_f\n    do\n      case $ac_f in\n      -) ac_f=\"$ac_tmp/stdin\";;\n      *) # Look for the file first in the build tree, then in the source tree\n\t # (if the path is not absolute).  The absolute path cannot be DOS-style,\n\t # because $ac_f cannot contain `:'.\n\t test -f \"$ac_f\" ||\n\t   case $ac_f in\n\t   [\\\\/$]*) false;;\n\t   *) test -f \"$srcdir/$ac_f\" && ac_f=\"$srcdir/$ac_f\";;\n\t   esac ||\n\t   as_fn_error 1 \"cannot find input file: \\`$ac_f'\" \"$LINENO\" 5;;\n      esac\n      case $ac_f in *\\'*) ac_f=`$as_echo \"$ac_f\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;; esac\n      as_fn_append ac_file_inputs \" '$ac_f'\"\n    done\n\n    # Let's still pretend it is `configure' which instantiates (i.e., don't\n    # use $as_me), people would be surprised to read:\n    #    /* config.h.  Generated by config.status.  */\n    configure_input='Generated from '`\n\t  $as_echo \"$*\" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'\n\t`' by configure.'\n    if test x\"$ac_file\" != x-; then\n      configure_input=\"$ac_file.  $configure_input\"\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: creating $ac_file\" >&5\n$as_echo \"$as_me: creating $ac_file\" >&6;}\n    fi\n    # Neutralize special characters interpreted by sed in replacement strings.\n    case $configure_input in #(\n    *\\&* | *\\|* | *\\\\* )\n       ac_sed_conf_input=`$as_echo \"$configure_input\" |\n       sed 's/[\\\\\\\\&|]/\\\\\\\\&/g'`;; #(\n    *) ac_sed_conf_input=$configure_input;;\n    esac\n\n    case $ac_tag in\n    *:-:* | *:-) cat >\"$ac_tmp/stdin\" \\\n      || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5 ;;\n    esac\n    ;;\n  esac\n\n  ac_dir=`$as_dirname -- \"$ac_file\" ||\n$as_expr X\"$ac_file\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$ac_file\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$ac_file\" : 'X\\(//\\)$' \\| \\\n\t X\"$ac_file\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$ac_file\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n  as_dir=\"$ac_dir\"; as_fn_mkdir_p\n  ac_builddir=.\n\ncase \"$ac_dir\" in\n.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;\n*)\n  ac_dir_suffix=/`$as_echo \"$ac_dir\" | sed 's|^\\.[\\\\/]||'`\n  # A \"..\" for each directory in $ac_dir_suffix.\n  ac_top_builddir_sub=`$as_echo \"$ac_dir_suffix\" | sed 's|/[^\\\\/]*|/..|g;s|/||'`\n  case $ac_top_builddir_sub in\n  \"\") ac_top_builddir_sub=. ac_top_build_prefix= ;;\n  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;\n  esac ;;\nesac\nac_abs_top_builddir=$ac_pwd\nac_abs_builddir=$ac_pwd$ac_dir_suffix\n# for backward compatibility:\nac_top_builddir=$ac_top_build_prefix\n\ncase $srcdir in\n  .)  # We are building in place.\n    ac_srcdir=.\n    ac_top_srcdir=$ac_top_builddir_sub\n    ac_abs_top_srcdir=$ac_pwd ;;\n  [\\\\/]* | ?:[\\\\/]* )  # Absolute name.\n    ac_srcdir=$srcdir$ac_dir_suffix;\n    ac_top_srcdir=$srcdir\n    ac_abs_top_srcdir=$srcdir ;;\n  *) # Relative name.\n    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix\n    ac_top_srcdir=$ac_top_build_prefix$srcdir\n    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;\nesac\nac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix\n\n\n  case $ac_mode in\n  :F)\n  #\n  # CONFIG_FILE\n  #\n\n  case $INSTALL in\n  [\\\\/$]* | ?:[\\\\/]* ) ac_INSTALL=$INSTALL ;;\n  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;\n  esac\n  ac_MKDIR_P=$MKDIR_P\n  case $MKDIR_P in\n  [\\\\/$]* | ?:[\\\\/]* ) ;;\n  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;\n  esac\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n# If the template does not know about datarootdir, expand it.\n# FIXME: This hack should be removed a few years after 2.60.\nac_datarootdir_hack=; ac_datarootdir_seen=\nac_sed_dataroot='\n/datarootdir/ {\n  p\n  q\n}\n/@datadir@/p\n/@docdir@/p\n/@infodir@/p\n/@localedir@/p\n/@mandir@/p'\ncase `eval \"sed -n \\\"\\$ac_sed_dataroot\\\" $ac_file_inputs\"` in\n*datarootdir*) ac_datarootdir_seen=yes;;\n*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting\" >&5\n$as_echo \"$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting\" >&2;}\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n  ac_datarootdir_hack='\n  s&@datadir@&$datadir&g\n  s&@docdir@&$docdir&g\n  s&@infodir@&$infodir&g\n  s&@localedir@&$localedir&g\n  s&@mandir@&$mandir&g\n  s&\\\\\\${datarootdir}&$datarootdir&g' ;;\nesac\n_ACEOF\n\n# Neutralize VPATH when `$srcdir' = `.'.\n# Shell code in configure.ac might set extrasub.\n# FIXME: do we really want to maintain this feature?\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\nac_sed_extra=\"$ac_vpsub\n$extrasub\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n:t\n/@[a-zA-Z_][a-zA-Z_0-9]*@/!b\ns|@configure_input@|$ac_sed_conf_input|;t t\ns&@top_builddir@&$ac_top_builddir_sub&;t t\ns&@top_build_prefix@&$ac_top_build_prefix&;t t\ns&@srcdir@&$ac_srcdir&;t t\ns&@abs_srcdir@&$ac_abs_srcdir&;t t\ns&@top_srcdir@&$ac_top_srcdir&;t t\ns&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t\ns&@builddir@&$ac_builddir&;t t\ns&@abs_builddir@&$ac_abs_builddir&;t t\ns&@abs_top_builddir@&$ac_abs_top_builddir&;t t\ns&@INSTALL@&$ac_INSTALL&;t t\ns&@MKDIR_P@&$ac_MKDIR_P&;t t\n$ac_datarootdir_hack\n\"\neval sed \\\"\\$ac_sed_extra\\\" \"$ac_file_inputs\" | $AWK -f \"$ac_tmp/subs.awk\" \\\n  >$ac_tmp/out || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n\ntest -z \"$ac_datarootdir_hack$ac_datarootdir_seen\" &&\n  { ac_out=`sed -n '/\\${datarootdir}/p' \"$ac_tmp/out\"`; test -n \"$ac_out\"; } &&\n  { ac_out=`sed -n '/^[\t ]*datarootdir[\t ]*:*=/p' \\\n      \"$ac_tmp/out\"`; test -z \"$ac_out\"; } &&\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \\`datarootdir'\nwhich seems to be undefined.  Please make sure it is defined\" >&5\n$as_echo \"$as_me: WARNING: $ac_file contains a reference to the variable \\`datarootdir'\nwhich seems to be undefined.  Please make sure it is defined\" >&2;}\n\n  rm -f \"$ac_tmp/stdin\"\n  case $ac_file in\n  -) cat \"$ac_tmp/out\" && rm -f \"$ac_tmp/out\";;\n  *) rm -f \"$ac_file\" && mv \"$ac_tmp/out\" \"$ac_file\";;\n  esac \\\n  || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n ;;\n  :H)\n  #\n  # CONFIG_HEADER\n  #\n  if test x\"$ac_file\" != x-; then\n    {\n      $as_echo \"/* $configure_input  */\" \\\n      && eval '$AWK -f \"$ac_tmp/defines.awk\"' \"$ac_file_inputs\"\n    } >\"$ac_tmp/config.h\" \\\n      || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n    if diff \"$ac_file\" \"$ac_tmp/config.h\" >/dev/null 2>&1; then\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: $ac_file is unchanged\" >&5\n$as_echo \"$as_me: $ac_file is unchanged\" >&6;}\n    else\n      rm -f \"$ac_file\"\n      mv \"$ac_tmp/config.h\" \"$ac_file\" \\\n\t|| as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n    fi\n  else\n    $as_echo \"/* $configure_input  */\" \\\n      && eval '$AWK -f \"$ac_tmp/defines.awk\"' \"$ac_file_inputs\" \\\n      || as_fn_error $? \"could not create -\" \"$LINENO\" 5\n  fi\n# Compute \"$ac_file\"'s index in $config_headers.\n_am_arg=\"$ac_file\"\n_am_stamp_count=1\nfor _am_header in $config_headers :; do\n  case $_am_header in\n    $_am_arg | $_am_arg:* )\n      break ;;\n    * )\n      _am_stamp_count=`expr $_am_stamp_count + 1` ;;\n  esac\ndone\necho \"timestamp for $_am_arg\" >`$as_dirname -- \"$_am_arg\" ||\n$as_expr X\"$_am_arg\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$_am_arg\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$_am_arg\" : 'X\\(//\\)$' \\| \\\n\t X\"$_am_arg\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$_am_arg\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`/stamp-h$_am_stamp_count\n ;;\n\n  :C)  { $as_echo \"$as_me:${as_lineno-$LINENO}: executing $ac_file commands\" >&5\n$as_echo \"$as_me: executing $ac_file commands\" >&6;}\n ;;\n  esac\n\n\n  case $ac_file$ac_mode in\n    \"depfiles\":C) test x\"$AMDEP_TRUE\" != x\"\" || {\n  # Older Autoconf quotes --file arguments for eval, but not when files\n  # are listed without --file.  Let's play safe and only enable the eval\n  # if we detect the quoting.\n  case $CONFIG_FILES in\n  *\\'*) eval set x \"$CONFIG_FILES\" ;;\n  *)   set x $CONFIG_FILES ;;\n  esac\n  shift\n  for mf\n  do\n    # Strip MF so we end up with the name of the file.\n    mf=`echo \"$mf\" | sed -e 's/:.*$//'`\n    # Check whether this is an Automake generated Makefile or not.\n    # We used to match only the files named 'Makefile.in', but\n    # some people rename them; so instead we look at the file content.\n    # Grep'ing the first line is not enough: some people post-process\n    # each Makefile.in and add a new line on top of each file to say so.\n    # Grep'ing the whole file is not good either: AIX grep has a line\n    # limit of 2048, but all sed's we know have understand at least 4000.\n    if sed -n 's,^#.*generated by automake.*,X,p' \"$mf\" | grep X >/dev/null 2>&1; then\n      dirpart=`$as_dirname -- \"$mf\" ||\n$as_expr X\"$mf\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$mf\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$mf\" : 'X\\(//\\)$' \\| \\\n\t X\"$mf\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$mf\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n    else\n      continue\n    fi\n    # Extract the definition of DEPDIR, am__include, and am__quote\n    # from the Makefile without running 'make'.\n    DEPDIR=`sed -n 's/^DEPDIR = //p' < \"$mf\"`\n    test -z \"$DEPDIR\" && continue\n    am__include=`sed -n 's/^am__include = //p' < \"$mf\"`\n    test -z \"$am__include\" && continue\n    am__quote=`sed -n 's/^am__quote = //p' < \"$mf\"`\n    # Find all dependency output files, they are included files with\n    # $(DEPDIR) in their names.  We invoke sed twice because it is the\n    # simplest approach to changing $(DEPDIR) to its actual value in the\n    # expansion.\n    for file in `sed -n \"\n      s/^$am__include $am__quote\\(.*(DEPDIR).*\\)$am__quote\"'$/\\1/p' <\"$mf\" | \\\n\t sed -e 's/\\$(DEPDIR)/'\"$DEPDIR\"'/g'`; do\n      # Make sure the directory exists.\n      test -f \"$dirpart/$file\" && continue\n      fdir=`$as_dirname -- \"$file\" ||\n$as_expr X\"$file\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$file\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$file\" : 'X\\(//\\)$' \\| \\\n\t X\"$file\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$file\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n      as_dir=$dirpart/$fdir; as_fn_mkdir_p\n      # echo \"creating $dirpart/$file\"\n      echo '# dummy' > \"$dirpart/$file\"\n    done\n  done\n}\n ;;\n    \"libtool\":C)\n\n    # See if we are running on zsh, and set the options that allow our\n    # commands through without removal of \\ escapes.\n    if test -n \"${ZSH_VERSION+set}\"; then\n      setopt NO_GLOB_SUBST\n    fi\n\n    cfgfile=${ofile}T\n    trap \"$RM \\\"$cfgfile\\\"; exit 1\" 1 2 15\n    $RM \"$cfgfile\"\n\n    cat <<_LT_EOF >> \"$cfgfile\"\n#! $SHELL\n# Generated automatically by $as_me ($PACKAGE) $VERSION\n# NOTE: Changes made to this file will be lost: look at ltmain.sh.\n\n# Provide generalized library-building support services.\n# Written by Gordon Matzigkeit, 1996\n\n# Copyright (C) 2014 Free Software Foundation, Inc.\n# This is free software; see the source for copying conditions.  There is NO\n# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n# GNU Libtool is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of of the License, or\n# (at your option) any later version.\n#\n# As a special exception to the GNU General Public License, if you\n# distribute this file as part of a program or library that is built\n# using GNU Libtool, you may include this file under the  same\n# distribution terms that you use for the rest of that program.\n#\n# GNU Libtool is distributed in the hope that it will be useful, but\n# WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\n# The names of the tagged configurations supported by this script.\navailable_tags='CXX '\n\n# Configured defaults for sys_lib_dlsearch_path munging.\n: \\${LT_SYS_LIBRARY_PATH=\"$configure_time_lt_sys_library_path\"}\n\n# ### BEGIN LIBTOOL CONFIG\n\n# Which release of libtool.m4 was used?\nmacro_version=$macro_version\nmacro_revision=$macro_revision\n\n# Whether or not to build shared libraries.\nbuild_libtool_libs=$enable_shared\n\n# Whether or not to build static libraries.\nbuild_old_libs=$enable_static\n\n# What type of objects to build.\npic_mode=$pic_mode\n\n# Whether or not to optimize for fast installation.\nfast_install=$enable_fast_install\n\n# Shared archive member basename,for filename based shared library versioning on AIX.\nshared_archive_member_spec=$shared_archive_member_spec\n\n# Shell to use when invoking shell scripts.\nSHELL=$lt_SHELL\n\n# An echo program that protects backslashes.\nECHO=$lt_ECHO\n\n# The PATH separator for the build system.\nPATH_SEPARATOR=$lt_PATH_SEPARATOR\n\n# The host system.\nhost_alias=$host_alias\nhost=$host\nhost_os=$host_os\n\n# The build system.\nbuild_alias=$build_alias\nbuild=$build\nbuild_os=$build_os\n\n# A sed program that does not truncate output.\nSED=$lt_SED\n\n# Sed that helps us avoid accidentally triggering echo(1) options like -n.\nXsed=\"\\$SED -e 1s/^X//\"\n\n# A grep program that handles long lines.\nGREP=$lt_GREP\n\n# An ERE matcher.\nEGREP=$lt_EGREP\n\n# A literal string matcher.\nFGREP=$lt_FGREP\n\n# A BSD- or MS-compatible name lister.\nNM=$lt_NM\n\n# Whether we need soft or hard links.\nLN_S=$lt_LN_S\n\n# What is the maximum length of a command?\nmax_cmd_len=$max_cmd_len\n\n# Object file suffix (normally \"o\").\nobjext=$ac_objext\n\n# Executable file suffix (normally \"\").\nexeext=$exeext\n\n# whether the shell understands \"unset\".\nlt_unset=$lt_unset\n\n# turn spaces into newlines.\nSP2NL=$lt_lt_SP2NL\n\n# turn newlines into spaces.\nNL2SP=$lt_lt_NL2SP\n\n# convert \\$build file names to \\$host format.\nto_host_file_cmd=$lt_cv_to_host_file_cmd\n\n# convert \\$build files to toolchain format.\nto_tool_file_cmd=$lt_cv_to_tool_file_cmd\n\n# An object symbol dumper.\nOBJDUMP=$lt_OBJDUMP\n\n# Method to check whether dependent libraries are shared objects.\ndeplibs_check_method=$lt_deplibs_check_method\n\n# Command to use when deplibs_check_method = \"file_magic\".\nfile_magic_cmd=$lt_file_magic_cmd\n\n# How to find potential files when deplibs_check_method = \"file_magic\".\nfile_magic_glob=$lt_file_magic_glob\n\n# Find potential files using nocaseglob when deplibs_check_method = \"file_magic\".\nwant_nocaseglob=$lt_want_nocaseglob\n\n# DLL creation program.\nDLLTOOL=$lt_DLLTOOL\n\n# Command to associate shared and link libraries.\nsharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd\n\n# The archiver.\nAR=$lt_AR\n\n# Flags to create an archive.\nAR_FLAGS=$lt_AR_FLAGS\n\n# How to feed a file listing to the archiver.\narchiver_list_spec=$lt_archiver_list_spec\n\n# A symbol stripping program.\nSTRIP=$lt_STRIP\n\n# Commands used to install an old-style archive.\nRANLIB=$lt_RANLIB\nold_postinstall_cmds=$lt_old_postinstall_cmds\nold_postuninstall_cmds=$lt_old_postuninstall_cmds\n\n# Whether to use a lock for old archive extraction.\nlock_old_archive_extraction=$lock_old_archive_extraction\n\n# A C compiler.\nLTCC=$lt_CC\n\n# LTCC compiler flags.\nLTCFLAGS=$lt_CFLAGS\n\n# Take the output of nm and produce a listing of raw symbols and C names.\nglobal_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe\n\n# Transform the output of nm in a proper C declaration.\nglobal_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl\n\n# Transform the output of nm into a list of symbols to manually relocate.\nglobal_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import\n\n# Transform the output of nm in a C name address pair.\nglobal_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address\n\n# Transform the output of nm in a C name address pair when lib prefix is needed.\nglobal_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix\n\n# The name lister interface.\nnm_interface=$lt_lt_cv_nm_interface\n\n# Specify filename containing input files for \\$NM.\nnm_file_list_spec=$lt_nm_file_list_spec\n\n# The root where to search for dependent libraries,and where our libraries should be installed.\nlt_sysroot=$lt_sysroot\n\n# Command to truncate a binary pipe.\nlt_truncate_bin=$lt_lt_cv_truncate_bin\n\n# The name of the directory that contains temporary libtool files.\nobjdir=$objdir\n\n# Used to examine libraries when file_magic_cmd begins with \"file\".\nMAGIC_CMD=$MAGIC_CMD\n\n# Must we lock files when doing compilation?\nneed_locks=$lt_need_locks\n\n# Manifest tool.\nMANIFEST_TOOL=$lt_MANIFEST_TOOL\n\n# Tool to manipulate archived DWARF debug symbol files on Mac OS X.\nDSYMUTIL=$lt_DSYMUTIL\n\n# Tool to change global to local symbols on Mac OS X.\nNMEDIT=$lt_NMEDIT\n\n# Tool to manipulate fat objects and archives on Mac OS X.\nLIPO=$lt_LIPO\n\n# ldd/readelf like tool for Mach-O binaries on Mac OS X.\nOTOOL=$lt_OTOOL\n\n# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.\nOTOOL64=$lt_OTOOL64\n\n# Old archive suffix (normally \"a\").\nlibext=$libext\n\n# Shared library suffix (normally \".so\").\nshrext_cmds=$lt_shrext_cmds\n\n# The commands to extract the exported symbol list from a shared archive.\nextract_expsyms_cmds=$lt_extract_expsyms_cmds\n\n# Variables whose values should be saved in libtool wrapper scripts and\n# restored at link time.\nvariables_saved_for_relink=$lt_variables_saved_for_relink\n\n# Do we need the \"lib\" prefix for modules?\nneed_lib_prefix=$need_lib_prefix\n\n# Do we need a version for libraries?\nneed_version=$need_version\n\n# Library versioning type.\nversion_type=$version_type\n\n# Shared library runtime path variable.\nrunpath_var=$runpath_var\n\n# Shared library path variable.\nshlibpath_var=$shlibpath_var\n\n# Is shlibpath searched before the hard-coded library search path?\nshlibpath_overrides_runpath=$shlibpath_overrides_runpath\n\n# Format of library name prefix.\nlibname_spec=$lt_libname_spec\n\n# List of archive names.  First name is the real one, the rest are links.\n# The last name is the one that the linker finds with -lNAME\nlibrary_names_spec=$lt_library_names_spec\n\n# The coded name of the library, if different from the real name.\nsoname_spec=$lt_soname_spec\n\n# Permission mode override for installation of shared libraries.\ninstall_override_mode=$lt_install_override_mode\n\n# Command to use after installation of a shared archive.\npostinstall_cmds=$lt_postinstall_cmds\n\n# Command to use after uninstallation of a shared archive.\npostuninstall_cmds=$lt_postuninstall_cmds\n\n# Commands used to finish a libtool library installation in a directory.\nfinish_cmds=$lt_finish_cmds\n\n# As \"finish_cmds\", except a single script fragment to be evaled but\n# not shown.\nfinish_eval=$lt_finish_eval\n\n# Whether we should hardcode library paths into libraries.\nhardcode_into_libs=$hardcode_into_libs\n\n# Compile-time system search path for libraries.\nsys_lib_search_path_spec=$lt_sys_lib_search_path_spec\n\n# Detected run-time system search path for libraries.\nsys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path\n\n# Explicit LT_SYS_LIBRARY_PATH set during ./configure time.\nconfigure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path\n\n# Whether dlopen is supported.\ndlopen_support=$enable_dlopen\n\n# Whether dlopen of programs is supported.\ndlopen_self=$enable_dlopen_self\n\n# Whether dlopen of statically linked programs is supported.\ndlopen_self_static=$enable_dlopen_self_static\n\n# Commands to strip libraries.\nold_striplib=$lt_old_striplib\nstriplib=$lt_striplib\n\n\n# The linker used to build libraries.\nLD=$lt_LD\n\n# How to create reloadable object files.\nreload_flag=$lt_reload_flag\nreload_cmds=$lt_reload_cmds\n\n# Commands used to build an old-style archive.\nold_archive_cmds=$lt_old_archive_cmds\n\n# A language specific compiler.\nCC=$lt_compiler\n\n# Is the compiler the GNU compiler?\nwith_gcc=$GCC\n\n# Compiler flag to turn off builtin functions.\nno_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag\n\n# Additional compiler flags for building library objects.\npic_flag=$lt_lt_prog_compiler_pic\n\n# How to pass a linker flag through the compiler.\nwl=$lt_lt_prog_compiler_wl\n\n# Compiler flag to prevent dynamic linking.\nlink_static_flag=$lt_lt_prog_compiler_static\n\n# Does compiler simultaneously support -c and -o options?\ncompiler_c_o=$lt_lt_cv_prog_compiler_c_o\n\n# Whether or not to add -lc for building shared libraries.\nbuild_libtool_need_lc=$archive_cmds_need_lc\n\n# Whether or not to disallow shared libs when runtime libs are static.\nallow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes\n\n# Compiler flag to allow reflexive dlopens.\nexport_dynamic_flag_spec=$lt_export_dynamic_flag_spec\n\n# Compiler flag to generate shared objects directly from archives.\nwhole_archive_flag_spec=$lt_whole_archive_flag_spec\n\n# Whether the compiler copes with passing no objects directly.\ncompiler_needs_object=$lt_compiler_needs_object\n\n# Create an old-style archive from a shared archive.\nold_archive_from_new_cmds=$lt_old_archive_from_new_cmds\n\n# Create a temporary old-style archive to link instead of a shared archive.\nold_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds\n\n# Commands used to build a shared archive.\narchive_cmds=$lt_archive_cmds\narchive_expsym_cmds=$lt_archive_expsym_cmds\n\n# Commands used to build a loadable module if different from building\n# a shared archive.\nmodule_cmds=$lt_module_cmds\nmodule_expsym_cmds=$lt_module_expsym_cmds\n\n# Whether we are building with GNU ld or not.\nwith_gnu_ld=$lt_with_gnu_ld\n\n# Flag that allows shared libraries with undefined symbols to be built.\nallow_undefined_flag=$lt_allow_undefined_flag\n\n# Flag that enforces no undefined symbols.\nno_undefined_flag=$lt_no_undefined_flag\n\n# Flag to hardcode \\$libdir into a binary during linking.\n# This must work even if \\$libdir does not exist\nhardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec\n\n# Whether we need a single \"-rpath\" flag with a separated argument.\nhardcode_libdir_separator=$lt_hardcode_libdir_separator\n\n# Set to \"yes\" if using DIR/libNAME\\$shared_ext during linking hardcodes\n# DIR into the resulting binary.\nhardcode_direct=$hardcode_direct\n\n# Set to \"yes\" if using DIR/libNAME\\$shared_ext during linking hardcodes\n# DIR into the resulting binary and the resulting library dependency is\n# \"absolute\",i.e impossible to change by setting \\$shlibpath_var if the\n# library is relocated.\nhardcode_direct_absolute=$hardcode_direct_absolute\n\n# Set to \"yes\" if using the -LDIR flag during linking hardcodes DIR\n# into the resulting binary.\nhardcode_minus_L=$hardcode_minus_L\n\n# Set to \"yes\" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR\n# into the resulting binary.\nhardcode_shlibpath_var=$hardcode_shlibpath_var\n\n# Set to \"yes\" if building a shared library automatically hardcodes DIR\n# into the library and all subsequent libraries and executables linked\n# against it.\nhardcode_automatic=$hardcode_automatic\n\n# Set to yes if linker adds runtime paths of dependent libraries\n# to runtime path list.\ninherit_rpath=$inherit_rpath\n\n# Whether libtool must link a program against all its dependency libraries.\nlink_all_deplibs=$link_all_deplibs\n\n# Set to \"yes\" if exported symbols are required.\nalways_export_symbols=$always_export_symbols\n\n# The commands to list exported symbols.\nexport_symbols_cmds=$lt_export_symbols_cmds\n\n# Symbols that should not be listed in the preloaded symbols.\nexclude_expsyms=$lt_exclude_expsyms\n\n# Symbols that must always be exported.\ninclude_expsyms=$lt_include_expsyms\n\n# Commands necessary for linking programs (against libraries) with templates.\nprelink_cmds=$lt_prelink_cmds\n\n# Commands necessary for finishing linking programs.\npostlink_cmds=$lt_postlink_cmds\n\n# Specify filename containing input files.\nfile_list_spec=$lt_file_list_spec\n\n# How to hardcode a shared library path into an executable.\nhardcode_action=$hardcode_action\n\n# The directories searched by this compiler when creating a shared library.\ncompiler_lib_search_dirs=$lt_compiler_lib_search_dirs\n\n# Dependencies to place before and after the objects being linked to\n# create a shared library.\npredep_objects=$lt_predep_objects\npostdep_objects=$lt_postdep_objects\npredeps=$lt_predeps\npostdeps=$lt_postdeps\n\n# The library search path used internally by the compiler when linking\n# a shared library.\ncompiler_lib_search_path=$lt_compiler_lib_search_path\n\n# ### END LIBTOOL CONFIG\n\n_LT_EOF\n\n    cat <<'_LT_EOF' >> \"$cfgfile\"\n\n# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE\n\n# func_munge_path_list VARIABLE PATH\n# -----------------------------------\n# VARIABLE is name of variable containing _space_ separated list of\n# directories to be munged by the contents of PATH, which is string\n# having a format:\n# \"DIR[:DIR]:\"\n#       string \"DIR[ DIR]\" will be prepended to VARIABLE\n# \":DIR[:DIR]\"\n#       string \"DIR[ DIR]\" will be appended to VARIABLE\n# \"DIRP[:DIRP]::[DIRA:]DIRA\"\n#       string \"DIRP[ DIRP]\" will be prepended to VARIABLE and string\n#       \"DIRA[ DIRA]\" will be appended to VARIABLE\n# \"DIR[:DIR]\"\n#       VARIABLE will be replaced by \"DIR[ DIR]\"\nfunc_munge_path_list ()\n{\n    case x$2 in\n    x)\n        ;;\n    *:)\n        eval $1=\\\"`$ECHO $2 | $SED 's/:/ /g'` \\$$1\\\"\n        ;;\n    x:*)\n        eval $1=\\\"\\$$1 `$ECHO $2 | $SED 's/:/ /g'`\\\"\n        ;;\n    *::*)\n        eval $1=\\\"\\$$1\\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\\\"\n        eval $1=\\\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\\ \\$$1\\\"\n        ;;\n    *)\n        eval $1=\\\"`$ECHO $2 | $SED 's/:/ /g'`\\\"\n        ;;\n    esac\n}\n\n\n# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.\nfunc_cc_basename ()\n{\n    for cc_temp in $*\"\"; do\n      case $cc_temp in\n        compile | *[\\\\/]compile | ccache | *[\\\\/]ccache ) ;;\n        distcc | *[\\\\/]distcc | purify | *[\\\\/]purify ) ;;\n        \\-*) ;;\n        *) break;;\n      esac\n    done\n    func_cc_basename_result=`$ECHO \"$cc_temp\" | $SED \"s%.*/%%; s%^$host_alias-%%\"`\n}\n\n\n# ### END FUNCTIONS SHARED WITH CONFIGURE\n\n_LT_EOF\n\n  case $host_os in\n  aix3*)\n    cat <<\\_LT_EOF >> \"$cfgfile\"\n# AIX sometimes has problems with the GCC collect2 program.  For some\n# reason, if we set the COLLECT_NAMES environment variable, the problems\n# vanish in a puff of smoke.\nif test set != \"${COLLECT_NAMES+set}\"; then\n  COLLECT_NAMES=\n  export COLLECT_NAMES\nfi\n_LT_EOF\n    ;;\n  esac\n\n\nltmain=$ac_aux_dir/ltmain.sh\n\n\n  # We use sed instead of cat because bash on DJGPP gets confused if\n  # if finds mixed CR/LF and LF-only lines.  Since sed operates in\n  # text mode, it properly converts lines to CR/LF.  This bash problem\n  # is reportedly fixed, but why not run on old versions too?\n  sed '$q' \"$ltmain\" >> \"$cfgfile\" \\\n     || (rm -f \"$cfgfile\"; exit 1)\n\n   mv -f \"$cfgfile\" \"$ofile\" ||\n    (rm -f \"$ofile\" && cp \"$cfgfile\" \"$ofile\" && rm -f \"$cfgfile\")\n  chmod +x \"$ofile\"\n\n\n    cat <<_LT_EOF >> \"$ofile\"\n\n# ### BEGIN LIBTOOL TAG CONFIG: CXX\n\n# The linker used to build libraries.\nLD=$lt_LD_CXX\n\n# How to create reloadable object files.\nreload_flag=$lt_reload_flag_CXX\nreload_cmds=$lt_reload_cmds_CXX\n\n# Commands used to build an old-style archive.\nold_archive_cmds=$lt_old_archive_cmds_CXX\n\n# A language specific compiler.\nCC=$lt_compiler_CXX\n\n# Is the compiler the GNU compiler?\nwith_gcc=$GCC_CXX\n\n# Compiler flag to turn off builtin functions.\nno_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX\n\n# Additional compiler flags for building library objects.\npic_flag=$lt_lt_prog_compiler_pic_CXX\n\n# How to pass a linker flag through the compiler.\nwl=$lt_lt_prog_compiler_wl_CXX\n\n# Compiler flag to prevent dynamic linking.\nlink_static_flag=$lt_lt_prog_compiler_static_CXX\n\n# Does compiler simultaneously support -c and -o options?\ncompiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX\n\n# Whether or not to add -lc for building shared libraries.\nbuild_libtool_need_lc=$archive_cmds_need_lc_CXX\n\n# Whether or not to disallow shared libs when runtime libs are static.\nallow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX\n\n# Compiler flag to allow reflexive dlopens.\nexport_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX\n\n# Compiler flag to generate shared objects directly from archives.\nwhole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX\n\n# Whether the compiler copes with passing no objects directly.\ncompiler_needs_object=$lt_compiler_needs_object_CXX\n\n# Create an old-style archive from a shared archive.\nold_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX\n\n# Create a temporary old-style archive to link instead of a shared archive.\nold_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX\n\n# Commands used to build a shared archive.\narchive_cmds=$lt_archive_cmds_CXX\narchive_expsym_cmds=$lt_archive_expsym_cmds_CXX\n\n# Commands used to build a loadable module if different from building\n# a shared archive.\nmodule_cmds=$lt_module_cmds_CXX\nmodule_expsym_cmds=$lt_module_expsym_cmds_CXX\n\n# Whether we are building with GNU ld or not.\nwith_gnu_ld=$lt_with_gnu_ld_CXX\n\n# Flag that allows shared libraries with undefined symbols to be built.\nallow_undefined_flag=$lt_allow_undefined_flag_CXX\n\n# Flag that enforces no undefined symbols.\nno_undefined_flag=$lt_no_undefined_flag_CXX\n\n# Flag to hardcode \\$libdir into a binary during linking.\n# This must work even if \\$libdir does not exist\nhardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX\n\n# Whether we need a single \"-rpath\" flag with a separated argument.\nhardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX\n\n# Set to \"yes\" if using DIR/libNAME\\$shared_ext during linking hardcodes\n# DIR into the resulting binary.\nhardcode_direct=$hardcode_direct_CXX\n\n# Set to \"yes\" if using DIR/libNAME\\$shared_ext during linking hardcodes\n# DIR into the resulting binary and the resulting library dependency is\n# \"absolute\",i.e impossible to change by setting \\$shlibpath_var if the\n# library is relocated.\nhardcode_direct_absolute=$hardcode_direct_absolute_CXX\n\n# Set to \"yes\" if using the -LDIR flag during linking hardcodes DIR\n# into the resulting binary.\nhardcode_minus_L=$hardcode_minus_L_CXX\n\n# Set to \"yes\" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR\n# into the resulting binary.\nhardcode_shlibpath_var=$hardcode_shlibpath_var_CXX\n\n# Set to \"yes\" if building a shared library automatically hardcodes DIR\n# into the library and all subsequent libraries and executables linked\n# against it.\nhardcode_automatic=$hardcode_automatic_CXX\n\n# Set to yes if linker adds runtime paths of dependent libraries\n# to runtime path list.\ninherit_rpath=$inherit_rpath_CXX\n\n# Whether libtool must link a program against all its dependency libraries.\nlink_all_deplibs=$link_all_deplibs_CXX\n\n# Set to \"yes\" if exported symbols are required.\nalways_export_symbols=$always_export_symbols_CXX\n\n# The commands to list exported symbols.\nexport_symbols_cmds=$lt_export_symbols_cmds_CXX\n\n# Symbols that should not be listed in the preloaded symbols.\nexclude_expsyms=$lt_exclude_expsyms_CXX\n\n# Symbols that must always be exported.\ninclude_expsyms=$lt_include_expsyms_CXX\n\n# Commands necessary for linking programs (against libraries) with templates.\nprelink_cmds=$lt_prelink_cmds_CXX\n\n# Commands necessary for finishing linking programs.\npostlink_cmds=$lt_postlink_cmds_CXX\n\n# Specify filename containing input files.\nfile_list_spec=$lt_file_list_spec_CXX\n\n# How to hardcode a shared library path into an executable.\nhardcode_action=$hardcode_action_CXX\n\n# The directories searched by this compiler when creating a shared library.\ncompiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX\n\n# Dependencies to place before and after the objects being linked to\n# create a shared library.\npredep_objects=$lt_predep_objects_CXX\npostdep_objects=$lt_postdep_objects_CXX\npredeps=$lt_predeps_CXX\npostdeps=$lt_postdeps_CXX\n\n# The library search path used internally by the compiler when linking\n# a shared library.\ncompiler_lib_search_path=$lt_compiler_lib_search_path_CXX\n\n# ### END LIBTOOL TAG CONFIG: CXX\n_LT_EOF\n\n ;;\n\n  esac\ndone # for ac_tag\n\n\nas_fn_exit 0\n_ACEOF\nac_clean_files=$ac_clean_files_save\n\ntest $ac_write_fail = 0 ||\n  as_fn_error $? \"write failure creating $CONFIG_STATUS\" \"$LINENO\" 5\n\n\n# configure is writing to config.log, and then calls config.status.\n# config.status does its own redirection, appending to config.log.\n# Unfortunately, on DOS this fails, as config.log is still kept open\n# by configure, so config.status won't be able to write to it; its\n# output is simply discarded.  So we exec the FD to /dev/null,\n# effectively closing config.log, so it can be properly (re)opened and\n# appended to by config.status.  When coming back to configure, we\n# need to make the FD available again.\nif test \"$no_create\" != yes; then\n  ac_cs_success=:\n  ac_config_status_args=\n  test \"$silent\" = yes &&\n    ac_config_status_args=\"$ac_config_status_args --quiet\"\n  exec 5>/dev/null\n  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false\n  exec 5>>config.log\n  # Use ||, not &&, to avoid exiting from the if with $? = 1, which\n  # would make configure fail if this is the last instruction.\n  $ac_cs_success || as_fn_exit 1\nfi\nif test -n \"$ac_unrecognized_opts\" && test \"$enable_option_checking\" != no; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts\" >&5\n$as_echo \"$as_me: WARNING: unrecognized options: $ac_unrecognized_opts\" >&2;}\nfi\n\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/generated/zookeeper.jute.c",
    "content": "/**\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  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*/\n\n#include <stdlib.h>\n#include \"zookeeper.jute.h\"\n\nint serialize_Id(struct oarchive *out, const char *tag, struct Id *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"scheme\", &v->scheme);\n    rc = rc ? rc : out->serialize_String(out, \"id\", &v->id);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_Id(struct iarchive *in, const char *tag, struct Id*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"scheme\", &v->scheme);\n    rc = rc ? rc : in->deserialize_String(in, \"id\", &v->id);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_Id(struct Id*v){\n    deallocate_String(&v->scheme);\n    deallocate_String(&v->id);\n}\nint serialize_ACL(struct oarchive *out, const char *tag, struct ACL *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"perms\", &v->perms);\n    rc = rc ? rc : serialize_Id(out, \"id\", &v->id);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ACL(struct iarchive *in, const char *tag, struct ACL*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"perms\", &v->perms);\n    rc = rc ? rc : deserialize_Id(in, \"id\", &v->id);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ACL(struct ACL*v){\n    deallocate_Id(&v->id);\n}\nint serialize_Stat(struct oarchive *out, const char *tag, struct Stat *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"czxid\", &v->czxid);\n    rc = rc ? rc : out->serialize_Long(out, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : out->serialize_Long(out, \"ctime\", &v->ctime);\n    rc = rc ? rc : out->serialize_Long(out, \"mtime\", &v->mtime);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Int(out, \"cversion\", &v->cversion);\n    rc = rc ? rc : out->serialize_Int(out, \"aversion\", &v->aversion);\n    rc = rc ? rc : out->serialize_Long(out, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : out->serialize_Int(out, \"dataLength\", &v->dataLength);\n    rc = rc ? rc : out->serialize_Int(out, \"numChildren\", &v->numChildren);\n    rc = rc ? rc : out->serialize_Long(out, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_Stat(struct iarchive *in, const char *tag, struct Stat*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"czxid\", &v->czxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"ctime\", &v->ctime);\n    rc = rc ? rc : in->deserialize_Long(in, \"mtime\", &v->mtime);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Int(in, \"cversion\", &v->cversion);\n    rc = rc ? rc : in->deserialize_Int(in, \"aversion\", &v->aversion);\n    rc = rc ? rc : in->deserialize_Long(in, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : in->deserialize_Int(in, \"dataLength\", &v->dataLength);\n    rc = rc ? rc : in->deserialize_Int(in, \"numChildren\", &v->numChildren);\n    rc = rc ? rc : in->deserialize_Long(in, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_Stat(struct Stat*v){\n}\nint serialize_StatPersisted(struct oarchive *out, const char *tag, struct StatPersisted *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"czxid\", &v->czxid);\n    rc = rc ? rc : out->serialize_Long(out, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : out->serialize_Long(out, \"ctime\", &v->ctime);\n    rc = rc ? rc : out->serialize_Long(out, \"mtime\", &v->mtime);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Int(out, \"cversion\", &v->cversion);\n    rc = rc ? rc : out->serialize_Int(out, \"aversion\", &v->aversion);\n    rc = rc ? rc : out->serialize_Long(out, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : out->serialize_Long(out, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_StatPersisted(struct iarchive *in, const char *tag, struct StatPersisted*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"czxid\", &v->czxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"mzxid\", &v->mzxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"ctime\", &v->ctime);\n    rc = rc ? rc : in->deserialize_Long(in, \"mtime\", &v->mtime);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Int(in, \"cversion\", &v->cversion);\n    rc = rc ? rc : in->deserialize_Int(in, \"aversion\", &v->aversion);\n    rc = rc ? rc : in->deserialize_Long(in, \"ephemeralOwner\", &v->ephemeralOwner);\n    rc = rc ? rc : in->deserialize_Long(in, \"pzxid\", &v->pzxid);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_StatPersisted(struct StatPersisted*v){\n}\nint serialize_ClientInfo(struct oarchive *out, const char *tag, struct ClientInfo *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"authScheme\", &v->authScheme);\n    rc = rc ? rc : out->serialize_String(out, \"user\", &v->user);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ClientInfo(struct iarchive *in, const char *tag, struct ClientInfo*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"authScheme\", &v->authScheme);\n    rc = rc ? rc : in->deserialize_String(in, \"user\", &v->user);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ClientInfo(struct ClientInfo*v){\n    deallocate_String(&v->authScheme);\n    deallocate_String(&v->user);\n}\nint serialize_ConnectRequest(struct oarchive *out, const char *tag, struct ConnectRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : out->serialize_Long(out, \"lastZxidSeen\", &v->lastZxidSeen);\n    rc = rc ? rc : out->serialize_Int(out, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : out->serialize_Long(out, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : out->serialize_Buffer(out, \"passwd\", &v->passwd);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ConnectRequest(struct iarchive *in, const char *tag, struct ConnectRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : in->deserialize_Long(in, \"lastZxidSeen\", &v->lastZxidSeen);\n    rc = rc ? rc : in->deserialize_Int(in, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : in->deserialize_Long(in, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"passwd\", &v->passwd);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ConnectRequest(struct ConnectRequest*v){\n    deallocate_Buffer(&v->passwd);\n}\nint serialize_ConnectResponse(struct oarchive *out, const char *tag, struct ConnectResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : out->serialize_Int(out, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : out->serialize_Long(out, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : out->serialize_Buffer(out, \"passwd\", &v->passwd);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ConnectResponse(struct iarchive *in, const char *tag, struct ConnectResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : in->deserialize_Int(in, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : in->deserialize_Long(in, \"sessionId\", &v->sessionId);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"passwd\", &v->passwd);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ConnectResponse(struct ConnectResponse*v){\n    deallocate_Buffer(&v->passwd);\n}\nint allocate_String_vector(struct String_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_String_vector(struct String_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_String(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_String_vector(struct oarchive *out, const char *tag, struct String_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : out->serialize_String(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_String_vector(struct iarchive *in, const char *tag, struct String_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : in->deserialize_String(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_SetWatches(struct oarchive *out, const char *tag, struct SetWatches *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"relativeZxid\", &v->relativeZxid);\n    rc = rc ? rc : serialize_String_vector(out, \"dataWatches\", &v->dataWatches);\n    rc = rc ? rc : serialize_String_vector(out, \"existWatches\", &v->existWatches);\n    rc = rc ? rc : serialize_String_vector(out, \"childWatches\", &v->childWatches);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetWatches(struct iarchive *in, const char *tag, struct SetWatches*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"relativeZxid\", &v->relativeZxid);\n    rc = rc ? rc : deserialize_String_vector(in, \"dataWatches\", &v->dataWatches);\n    rc = rc ? rc : deserialize_String_vector(in, \"existWatches\", &v->existWatches);\n    rc = rc ? rc : deserialize_String_vector(in, \"childWatches\", &v->childWatches);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetWatches(struct SetWatches*v){\n    deallocate_String_vector(&v->dataWatches);\n    deallocate_String_vector(&v->existWatches);\n    deallocate_String_vector(&v->childWatches);\n}\nint serialize_SetWatches2(struct oarchive *out, const char *tag, struct SetWatches2 *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"relativeZxid\", &v->relativeZxid);\n    rc = rc ? rc : serialize_String_vector(out, \"dataWatches\", &v->dataWatches);\n    rc = rc ? rc : serialize_String_vector(out, \"existWatches\", &v->existWatches);\n    rc = rc ? rc : serialize_String_vector(out, \"childWatches\", &v->childWatches);\n    rc = rc ? rc : serialize_String_vector(out, \"persistentWatches\", &v->persistentWatches);\n    rc = rc ? rc : serialize_String_vector(out, \"persistentRecursiveWatches\", &v->persistentRecursiveWatches);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetWatches2(struct iarchive *in, const char *tag, struct SetWatches2*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"relativeZxid\", &v->relativeZxid);\n    rc = rc ? rc : deserialize_String_vector(in, \"dataWatches\", &v->dataWatches);\n    rc = rc ? rc : deserialize_String_vector(in, \"existWatches\", &v->existWatches);\n    rc = rc ? rc : deserialize_String_vector(in, \"childWatches\", &v->childWatches);\n    rc = rc ? rc : deserialize_String_vector(in, \"persistentWatches\", &v->persistentWatches);\n    rc = rc ? rc : deserialize_String_vector(in, \"persistentRecursiveWatches\", &v->persistentRecursiveWatches);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetWatches2(struct SetWatches2*v){\n    deallocate_String_vector(&v->dataWatches);\n    deallocate_String_vector(&v->existWatches);\n    deallocate_String_vector(&v->childWatches);\n    deallocate_String_vector(&v->persistentWatches);\n    deallocate_String_vector(&v->persistentRecursiveWatches);\n}\nint serialize_RequestHeader(struct oarchive *out, const char *tag, struct RequestHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"xid\", &v->xid);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_RequestHeader(struct iarchive *in, const char *tag, struct RequestHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"xid\", &v->xid);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_RequestHeader(struct RequestHeader*v){\n}\nint serialize_MultiHeader(struct oarchive *out, const char *tag, struct MultiHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Bool(out, \"done\", &v->done);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_MultiHeader(struct iarchive *in, const char *tag, struct MultiHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Bool(in, \"done\", &v->done);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_MultiHeader(struct MultiHeader*v){\n}\nint serialize_AuthPacket(struct oarchive *out, const char *tag, struct AuthPacket *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_String(out, \"scheme\", &v->scheme);\n    rc = rc ? rc : out->serialize_Buffer(out, \"auth\", &v->auth);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_AuthPacket(struct iarchive *in, const char *tag, struct AuthPacket*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_String(in, \"scheme\", &v->scheme);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"auth\", &v->auth);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_AuthPacket(struct AuthPacket*v){\n    deallocate_String(&v->scheme);\n    deallocate_Buffer(&v->auth);\n}\nint serialize_ReplyHeader(struct oarchive *out, const char *tag, struct ReplyHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"xid\", &v->xid);\n    rc = rc ? rc : out->serialize_Long(out, \"zxid\", &v->zxid);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ReplyHeader(struct iarchive *in, const char *tag, struct ReplyHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"xid\", &v->xid);\n    rc = rc ? rc : in->deserialize_Long(in, \"zxid\", &v->zxid);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ReplyHeader(struct ReplyHeader*v){\n}\nint serialize_GetDataRequest(struct oarchive *out, const char *tag, struct GetDataRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetDataRequest(struct iarchive *in, const char *tag, struct GetDataRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetDataRequest(struct GetDataRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetDataRequest(struct oarchive *out, const char *tag, struct SetDataRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetDataRequest(struct iarchive *in, const char *tag, struct SetDataRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetDataRequest(struct SetDataRequest*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n}\nint serialize_ReconfigRequest(struct oarchive *out, const char *tag, struct ReconfigRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"joiningServers\", &v->joiningServers);\n    rc = rc ? rc : out->serialize_String(out, \"leavingServers\", &v->leavingServers);\n    rc = rc ? rc : out->serialize_String(out, \"newMembers\", &v->newMembers);\n    rc = rc ? rc : out->serialize_Long(out, \"curConfigId\", &v->curConfigId);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ReconfigRequest(struct iarchive *in, const char *tag, struct ReconfigRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"joiningServers\", &v->joiningServers);\n    rc = rc ? rc : in->deserialize_String(in, \"leavingServers\", &v->leavingServers);\n    rc = rc ? rc : in->deserialize_String(in, \"newMembers\", &v->newMembers);\n    rc = rc ? rc : in->deserialize_Long(in, \"curConfigId\", &v->curConfigId);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ReconfigRequest(struct ReconfigRequest*v){\n    deallocate_String(&v->joiningServers);\n    deallocate_String(&v->leavingServers);\n    deallocate_String(&v->newMembers);\n}\nint serialize_SetDataResponse(struct oarchive *out, const char *tag, struct SetDataResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetDataResponse(struct iarchive *in, const char *tag, struct SetDataResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetDataResponse(struct SetDataResponse*v){\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetSASLRequest(struct oarchive *out, const char *tag, struct GetSASLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"token\", &v->token);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetSASLRequest(struct iarchive *in, const char *tag, struct GetSASLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"token\", &v->token);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetSASLRequest(struct GetSASLRequest*v){\n    deallocate_Buffer(&v->token);\n}\nint serialize_SetSASLRequest(struct oarchive *out, const char *tag, struct SetSASLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"token\", &v->token);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetSASLRequest(struct iarchive *in, const char *tag, struct SetSASLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"token\", &v->token);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetSASLRequest(struct SetSASLRequest*v){\n    deallocate_Buffer(&v->token);\n}\nint serialize_SetSASLResponse(struct oarchive *out, const char *tag, struct SetSASLResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"token\", &v->token);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetSASLResponse(struct iarchive *in, const char *tag, struct SetSASLResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"token\", &v->token);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetSASLResponse(struct SetSASLResponse*v){\n    deallocate_Buffer(&v->token);\n}\nint allocate_ACL_vector(struct ACL_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_ACL_vector(struct ACL_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_ACL(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_ACL_vector(struct oarchive *out, const char *tag, struct ACL_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : serialize_ACL(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_ACL_vector(struct iarchive *in, const char *tag, struct ACL_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : deserialize_ACL(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_CreateRequest(struct oarchive *out, const char *tag, struct CreateRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"flags\", &v->flags);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateRequest(struct iarchive *in, const char *tag, struct CreateRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"flags\", &v->flags);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateRequest(struct CreateRequest*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_CreateTTLRequest(struct oarchive *out, const char *tag, struct CreateTTLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"flags\", &v->flags);\n    rc = rc ? rc : out->serialize_Long(out, \"ttl\", &v->ttl);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateTTLRequest(struct iarchive *in, const char *tag, struct CreateTTLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"flags\", &v->flags);\n    rc = rc ? rc : in->deserialize_Long(in, \"ttl\", &v->ttl);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateTTLRequest(struct CreateTTLRequest*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_DeleteRequest(struct oarchive *out, const char *tag, struct DeleteRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_DeleteRequest(struct iarchive *in, const char *tag, struct DeleteRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_DeleteRequest(struct DeleteRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetChildrenRequest(struct oarchive *out, const char *tag, struct GetChildrenRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildrenRequest(struct iarchive *in, const char *tag, struct GetChildrenRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildrenRequest(struct GetChildrenRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetAllChildrenNumberRequest(struct oarchive *out, const char *tag, struct GetAllChildrenNumberRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetAllChildrenNumberRequest(struct iarchive *in, const char *tag, struct GetAllChildrenNumberRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetAllChildrenNumberRequest(struct GetAllChildrenNumberRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetChildren2Request(struct oarchive *out, const char *tag, struct GetChildren2Request *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildren2Request(struct iarchive *in, const char *tag, struct GetChildren2Request*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildren2Request(struct GetChildren2Request*v){\n    deallocate_String(&v->path);\n}\nint serialize_CheckVersionRequest(struct oarchive *out, const char *tag, struct CheckVersionRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CheckVersionRequest(struct iarchive *in, const char *tag, struct CheckVersionRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CheckVersionRequest(struct CheckVersionRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetMaxChildrenRequest(struct oarchive *out, const char *tag, struct GetMaxChildrenRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetMaxChildrenRequest(struct iarchive *in, const char *tag, struct GetMaxChildrenRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetMaxChildrenRequest(struct GetMaxChildrenRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetMaxChildrenResponse(struct oarchive *out, const char *tag, struct GetMaxChildrenResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"max\", &v->max);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetMaxChildrenResponse(struct iarchive *in, const char *tag, struct GetMaxChildrenResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"max\", &v->max);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetMaxChildrenResponse(struct GetMaxChildrenResponse*v){\n}\nint serialize_SetMaxChildrenRequest(struct oarchive *out, const char *tag, struct SetMaxChildrenRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"max\", &v->max);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetMaxChildrenRequest(struct iarchive *in, const char *tag, struct SetMaxChildrenRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"max\", &v->max);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetMaxChildrenRequest(struct SetMaxChildrenRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SyncRequest(struct oarchive *out, const char *tag, struct SyncRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SyncRequest(struct iarchive *in, const char *tag, struct SyncRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SyncRequest(struct SyncRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SyncResponse(struct oarchive *out, const char *tag, struct SyncResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SyncResponse(struct iarchive *in, const char *tag, struct SyncResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SyncResponse(struct SyncResponse*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetACLRequest(struct oarchive *out, const char *tag, struct GetACLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetACLRequest(struct iarchive *in, const char *tag, struct GetACLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetACLRequest(struct GetACLRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetACLRequest(struct oarchive *out, const char *tag, struct SetACLRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetACLRequest(struct iarchive *in, const char *tag, struct SetACLRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetACLRequest(struct SetACLRequest*v){\n    deallocate_String(&v->path);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_SetACLResponse(struct oarchive *out, const char *tag, struct SetACLResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetACLResponse(struct iarchive *in, const char *tag, struct SetACLResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetACLResponse(struct SetACLResponse*v){\n    deallocate_Stat(&v->stat);\n}\nint serialize_AddWatchRequest(struct oarchive *out, const char *tag, struct AddWatchRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"mode\", &v->mode);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_AddWatchRequest(struct iarchive *in, const char *tag, struct AddWatchRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"mode\", &v->mode);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_AddWatchRequest(struct AddWatchRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_WatcherEvent(struct oarchive *out, const char *tag, struct WatcherEvent *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Int(out, \"state\", &v->state);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_WatcherEvent(struct iarchive *in, const char *tag, struct WatcherEvent*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Int(in, \"state\", &v->state);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_WatcherEvent(struct WatcherEvent*v){\n    deallocate_String(&v->path);\n}\nint serialize_ErrorResponse(struct oarchive *out, const char *tag, struct ErrorResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ErrorResponse(struct iarchive *in, const char *tag, struct ErrorResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ErrorResponse(struct ErrorResponse*v){\n}\nint serialize_CreateResponse(struct oarchive *out, const char *tag, struct CreateResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateResponse(struct iarchive *in, const char *tag, struct CreateResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateResponse(struct CreateResponse*v){\n    deallocate_String(&v->path);\n}\nint serialize_Create2Response(struct oarchive *out, const char *tag, struct Create2Response *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_Create2Response(struct iarchive *in, const char *tag, struct Create2Response*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_Create2Response(struct Create2Response*v){\n    deallocate_String(&v->path);\n    deallocate_Stat(&v->stat);\n}\nint serialize_ExistsRequest(struct oarchive *out, const char *tag, struct ExistsRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Bool(out, \"watch\", &v->watch);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ExistsRequest(struct iarchive *in, const char *tag, struct ExistsRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Bool(in, \"watch\", &v->watch);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ExistsRequest(struct ExistsRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_ExistsResponse(struct oarchive *out, const char *tag, struct ExistsResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ExistsResponse(struct iarchive *in, const char *tag, struct ExistsResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ExistsResponse(struct ExistsResponse*v){\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetDataResponse(struct oarchive *out, const char *tag, struct GetDataResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetDataResponse(struct iarchive *in, const char *tag, struct GetDataResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetDataResponse(struct GetDataResponse*v){\n    deallocate_Buffer(&v->data);\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetChildrenResponse(struct oarchive *out, const char *tag, struct GetChildrenResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_String_vector(out, \"children\", &v->children);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildrenResponse(struct iarchive *in, const char *tag, struct GetChildrenResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_String_vector(in, \"children\", &v->children);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildrenResponse(struct GetChildrenResponse*v){\n    deallocate_String_vector(&v->children);\n}\nint serialize_GetAllChildrenNumberResponse(struct oarchive *out, const char *tag, struct GetAllChildrenNumberResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"totalNumber\", &v->totalNumber);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetAllChildrenNumberResponse(struct iarchive *in, const char *tag, struct GetAllChildrenNumberResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"totalNumber\", &v->totalNumber);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetAllChildrenNumberResponse(struct GetAllChildrenNumberResponse*v){\n}\nint serialize_GetChildren2Response(struct oarchive *out, const char *tag, struct GetChildren2Response *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_String_vector(out, \"children\", &v->children);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetChildren2Response(struct iarchive *in, const char *tag, struct GetChildren2Response*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_String_vector(in, \"children\", &v->children);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetChildren2Response(struct GetChildren2Response*v){\n    deallocate_String_vector(&v->children);\n    deallocate_Stat(&v->stat);\n}\nint serialize_GetACLResponse(struct oarchive *out, const char *tag, struct GetACLResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : serialize_Stat(out, \"stat\", &v->stat);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetACLResponse(struct iarchive *in, const char *tag, struct GetACLResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : deserialize_Stat(in, \"stat\", &v->stat);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetACLResponse(struct GetACLResponse*v){\n    deallocate_ACL_vector(&v->acl);\n    deallocate_Stat(&v->stat);\n}\nint serialize_CheckWatchesRequest(struct oarchive *out, const char *tag, struct CheckWatchesRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CheckWatchesRequest(struct iarchive *in, const char *tag, struct CheckWatchesRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CheckWatchesRequest(struct CheckWatchesRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_RemoveWatchesRequest(struct oarchive *out, const char *tag, struct RemoveWatchesRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_RemoveWatchesRequest(struct iarchive *in, const char *tag, struct RemoveWatchesRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_RemoveWatchesRequest(struct RemoveWatchesRequest*v){\n    deallocate_String(&v->path);\n}\nint serialize_GetEphemeralsRequest(struct oarchive *out, const char *tag, struct GetEphemeralsRequest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"prefixPath\", &v->prefixPath);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetEphemeralsRequest(struct iarchive *in, const char *tag, struct GetEphemeralsRequest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"prefixPath\", &v->prefixPath);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetEphemeralsRequest(struct GetEphemeralsRequest*v){\n    deallocate_String(&v->prefixPath);\n}\nint serialize_GetEphemeralsResponse(struct oarchive *out, const char *tag, struct GetEphemeralsResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_String_vector(out, \"ephemerals\", &v->ephemerals);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_GetEphemeralsResponse(struct iarchive *in, const char *tag, struct GetEphemeralsResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_String_vector(in, \"ephemerals\", &v->ephemerals);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_GetEphemeralsResponse(struct GetEphemeralsResponse*v){\n    deallocate_String_vector(&v->ephemerals);\n}\nint allocate_ClientInfo_vector(struct ClientInfo_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_ClientInfo_vector(struct ClientInfo_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_ClientInfo(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_ClientInfo_vector(struct oarchive *out, const char *tag, struct ClientInfo_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : serialize_ClientInfo(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_ClientInfo_vector(struct iarchive *in, const char *tag, struct ClientInfo_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : deserialize_ClientInfo(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_WhoAmIResponse(struct oarchive *out, const char *tag, struct WhoAmIResponse *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_ClientInfo_vector(out, \"clientInfo\", &v->clientInfo);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_WhoAmIResponse(struct iarchive *in, const char *tag, struct WhoAmIResponse*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_ClientInfo_vector(in, \"clientInfo\", &v->clientInfo);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_WhoAmIResponse(struct WhoAmIResponse*v){\n    deallocate_ClientInfo_vector(&v->clientInfo);\n}\nint serialize_LearnerInfo(struct oarchive *out, const char *tag, struct LearnerInfo *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"serverid\", &v->serverid);\n    rc = rc ? rc : out->serialize_Int(out, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : out->serialize_Long(out, \"configVersion\", &v->configVersion);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_LearnerInfo(struct iarchive *in, const char *tag, struct LearnerInfo*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"serverid\", &v->serverid);\n    rc = rc ? rc : in->deserialize_Int(in, \"protocolVersion\", &v->protocolVersion);\n    rc = rc ? rc : in->deserialize_Long(in, \"configVersion\", &v->configVersion);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_LearnerInfo(struct LearnerInfo*v){\n}\nint allocate_Id_vector(struct Id_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_Id_vector(struct Id_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_Id(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_Id_vector(struct oarchive *out, const char *tag, struct Id_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : serialize_Id(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_Id_vector(struct iarchive *in, const char *tag, struct Id_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : deserialize_Id(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_QuorumPacket(struct oarchive *out, const char *tag, struct QuorumPacket *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Long(out, \"zxid\", &v->zxid);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_Id_vector(out, \"authinfo\", &v->authinfo);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_QuorumPacket(struct iarchive *in, const char *tag, struct QuorumPacket*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Long(in, \"zxid\", &v->zxid);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_Id_vector(in, \"authinfo\", &v->authinfo);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_QuorumPacket(struct QuorumPacket*v){\n    deallocate_Buffer(&v->data);\n    deallocate_Id_vector(&v->authinfo);\n}\nint serialize_QuorumAuthPacket(struct oarchive *out, const char *tag, struct QuorumAuthPacket *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"magic\", &v->magic);\n    rc = rc ? rc : out->serialize_Int(out, \"status\", &v->status);\n    rc = rc ? rc : out->serialize_Buffer(out, \"token\", &v->token);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_QuorumAuthPacket(struct iarchive *in, const char *tag, struct QuorumAuthPacket*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"magic\", &v->magic);\n    rc = rc ? rc : in->deserialize_Int(in, \"status\", &v->status);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"token\", &v->token);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_QuorumAuthPacket(struct QuorumAuthPacket*v){\n    deallocate_Buffer(&v->token);\n}\nint serialize_FileHeader(struct oarchive *out, const char *tag, struct FileHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"magic\", &v->magic);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Long(out, \"dbid\", &v->dbid);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_FileHeader(struct iarchive *in, const char *tag, struct FileHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"magic\", &v->magic);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Long(in, \"dbid\", &v->dbid);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_FileHeader(struct FileHeader*v){\n}\nint serialize_TxnDigest(struct oarchive *out, const char *tag, struct TxnDigest *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->serialize_Long(out, \"treeDigest\", &v->treeDigest);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_TxnDigest(struct iarchive *in, const char *tag, struct TxnDigest*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->deserialize_Long(in, \"treeDigest\", &v->treeDigest);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_TxnDigest(struct TxnDigest*v){\n}\nint serialize_TxnHeader(struct oarchive *out, const char *tag, struct TxnHeader *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Long(out, \"clientId\", &v->clientId);\n    rc = rc ? rc : out->serialize_Int(out, \"cxid\", &v->cxid);\n    rc = rc ? rc : out->serialize_Long(out, \"zxid\", &v->zxid);\n    rc = rc ? rc : out->serialize_Long(out, \"time\", &v->time);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_TxnHeader(struct iarchive *in, const char *tag, struct TxnHeader*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Long(in, \"clientId\", &v->clientId);\n    rc = rc ? rc : in->deserialize_Int(in, \"cxid\", &v->cxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"zxid\", &v->zxid);\n    rc = rc ? rc : in->deserialize_Long(in, \"time\", &v->time);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_TxnHeader(struct TxnHeader*v){\n}\nint serialize_CreateTxnV0(struct oarchive *out, const char *tag, struct CreateTxnV0 *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Bool(out, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateTxnV0(struct iarchive *in, const char *tag, struct CreateTxnV0*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Bool(in, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateTxnV0(struct CreateTxnV0*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_CreateTxn(struct oarchive *out, const char *tag, struct CreateTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Bool(out, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : out->serialize_Int(out, \"parentCVersion\", &v->parentCVersion);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateTxn(struct iarchive *in, const char *tag, struct CreateTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Bool(in, \"ephemeral\", &v->ephemeral);\n    rc = rc ? rc : in->deserialize_Int(in, \"parentCVersion\", &v->parentCVersion);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateTxn(struct CreateTxn*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_CreateTTLTxn(struct oarchive *out, const char *tag, struct CreateTTLTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"parentCVersion\", &v->parentCVersion);\n    rc = rc ? rc : out->serialize_Long(out, \"ttl\", &v->ttl);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateTTLTxn(struct iarchive *in, const char *tag, struct CreateTTLTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"parentCVersion\", &v->parentCVersion);\n    rc = rc ? rc : in->deserialize_Long(in, \"ttl\", &v->ttl);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateTTLTxn(struct CreateTTLTxn*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_CreateContainerTxn(struct oarchive *out, const char *tag, struct CreateContainerTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"parentCVersion\", &v->parentCVersion);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateContainerTxn(struct iarchive *in, const char *tag, struct CreateContainerTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"parentCVersion\", &v->parentCVersion);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateContainerTxn(struct CreateContainerTxn*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_DeleteTxn(struct oarchive *out, const char *tag, struct DeleteTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_DeleteTxn(struct iarchive *in, const char *tag, struct DeleteTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_DeleteTxn(struct DeleteTxn*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetDataTxn(struct oarchive *out, const char *tag, struct SetDataTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetDataTxn(struct iarchive *in, const char *tag, struct SetDataTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetDataTxn(struct SetDataTxn*v){\n    deallocate_String(&v->path);\n    deallocate_Buffer(&v->data);\n}\nint serialize_CheckVersionTxn(struct oarchive *out, const char *tag, struct CheckVersionTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CheckVersionTxn(struct iarchive *in, const char *tag, struct CheckVersionTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CheckVersionTxn(struct CheckVersionTxn*v){\n    deallocate_String(&v->path);\n}\nint serialize_SetACLTxn(struct oarchive *out, const char *tag, struct SetACLTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : serialize_ACL_vector(out, \"acl\", &v->acl);\n    rc = rc ? rc : out->serialize_Int(out, \"version\", &v->version);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetACLTxn(struct iarchive *in, const char *tag, struct SetACLTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : deserialize_ACL_vector(in, \"acl\", &v->acl);\n    rc = rc ? rc : in->deserialize_Int(in, \"version\", &v->version);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetACLTxn(struct SetACLTxn*v){\n    deallocate_String(&v->path);\n    deallocate_ACL_vector(&v->acl);\n}\nint serialize_SetMaxChildrenTxn(struct oarchive *out, const char *tag, struct SetMaxChildrenTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_String(out, \"path\", &v->path);\n    rc = rc ? rc : out->serialize_Int(out, \"max\", &v->max);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_SetMaxChildrenTxn(struct iarchive *in, const char *tag, struct SetMaxChildrenTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_String(in, \"path\", &v->path);\n    rc = rc ? rc : in->deserialize_Int(in, \"max\", &v->max);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_SetMaxChildrenTxn(struct SetMaxChildrenTxn*v){\n    deallocate_String(&v->path);\n}\nint serialize_CreateSessionTxn(struct oarchive *out, const char *tag, struct CreateSessionTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CreateSessionTxn(struct iarchive *in, const char *tag, struct CreateSessionTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"timeOut\", &v->timeOut);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CreateSessionTxn(struct CreateSessionTxn*v){\n}\nint serialize_CloseSessionTxn(struct oarchive *out, const char *tag, struct CloseSessionTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_String_vector(out, \"paths2Delete\", &v->paths2Delete);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_CloseSessionTxn(struct iarchive *in, const char *tag, struct CloseSessionTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_String_vector(in, \"paths2Delete\", &v->paths2Delete);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_CloseSessionTxn(struct CloseSessionTxn*v){\n    deallocate_String_vector(&v->paths2Delete);\n}\nint serialize_ErrorTxn(struct oarchive *out, const char *tag, struct ErrorTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"err\", &v->err);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_ErrorTxn(struct iarchive *in, const char *tag, struct ErrorTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"err\", &v->err);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_ErrorTxn(struct ErrorTxn*v){\n}\nint serialize_Txn(struct oarchive *out, const char *tag, struct Txn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : out->serialize_Int(out, \"type\", &v->type);\n    rc = rc ? rc : out->serialize_Buffer(out, \"data\", &v->data);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_Txn(struct iarchive *in, const char *tag, struct Txn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : in->deserialize_Int(in, \"type\", &v->type);\n    rc = rc ? rc : in->deserialize_Buffer(in, \"data\", &v->data);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_Txn(struct Txn*v){\n    deallocate_Buffer(&v->data);\n}\nint allocate_Txn_vector(struct Txn_vector *v, int32_t len) {\n    if (!len) {\n        v->count = 0;\n        v->data = 0;\n    } else {\n        v->count = len;\n        v->data = calloc(sizeof(*v->data), len);\n    }\n    return 0;\n}\nint deallocate_Txn_vector(struct Txn_vector *v) {\n    if (v->data) {\n        int32_t i;\n        for(i=0;i<v->count; i++) {\n            deallocate_Txn(&v->data[i]);\n        }\n        free(v->data);\n        v->data = 0;\n    }\n    return 0;\n}\nint serialize_Txn_vector(struct oarchive *out, const char *tag, struct Txn_vector *v)\n{\n    int32_t count = v->count;\n    int rc = 0;\n    int32_t i;\n    rc = out->start_vector(out, tag, &count);\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : serialize_Txn(out, \"data\", &v->data[i]);\n    }\n    rc = rc ? rc : out->end_vector(out, tag);\n    return rc;\n}\nint deserialize_Txn_vector(struct iarchive *in, const char *tag, struct Txn_vector *v)\n{\n    int rc = 0;\n    int32_t i;\n    rc = in->start_vector(in, tag, &v->count);\n    v->data = calloc(v->count, sizeof(*v->data));\n    for(i=0;i<v->count;i++) {\n    rc = rc ? rc : deserialize_Txn(in, \"value\", &v->data[i]);\n    }\n    rc = in->end_vector(in, tag);\n    return rc;\n}\nint serialize_MultiTxn(struct oarchive *out, const char *tag, struct MultiTxn *v){\n    int rc;\n    rc = out->start_record(out, tag);\n    rc = rc ? rc : serialize_Txn_vector(out, \"txns\", &v->txns);\n    rc = rc ? rc : out->end_record(out, tag);\n    return rc;\n}\nint deserialize_MultiTxn(struct iarchive *in, const char *tag, struct MultiTxn*v){\n    int rc;\n    rc = in->start_record(in, tag);\n    rc = rc ? rc : deserialize_Txn_vector(in, \"txns\", &v->txns);\n    rc = rc ? rc : in->end_record(in, tag);\n    return rc;\n}\nvoid deallocate_MultiTxn(struct MultiTxn*v){\n    deallocate_Txn_vector(&v->txns);\n}\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/generated/zookeeper.jute.h",
    "content": "// Copyright 2022 ByteDance and/or its affiliates.\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\n/**\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements.  See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership.  The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License.  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*/\n\n#ifndef __ZOOKEEPER_JUTE__\n#define __ZOOKEEPER_JUTE__\n#include \"recordio.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct Id {\n    char * scheme;\n    char * id;\n};\nint serialize_Id(struct oarchive *out, const char *tag, struct Id *v);\nint deserialize_Id(struct iarchive *in, const char *tag, struct Id*v);\nvoid deallocate_Id(struct Id*);\nstruct ACL {\n    int32_t perms;\n    struct Id id;\n};\nint serialize_ACL(struct oarchive *out, const char *tag, struct ACL *v);\nint deserialize_ACL(struct iarchive *in, const char *tag, struct ACL*v);\nvoid deallocate_ACL(struct ACL*);\nstruct Stat {\n    int64_t czxid;\n    int64_t mzxid;\n    int64_t ctime;\n    int64_t mtime;\n    int32_t version;\n    int32_t cversion;\n    int32_t aversion;\n    int64_t ephemeralOwner;\n    int32_t dataLength;\n    int32_t numChildren;\n    int64_t pzxid;\n};\nint serialize_Stat(struct oarchive *out, const char *tag, struct Stat *v);\nint deserialize_Stat(struct iarchive *in, const char *tag, struct Stat*v);\nvoid deallocate_Stat(struct Stat*);\nstruct StatPersisted {\n    int64_t czxid;\n    int64_t mzxid;\n    int64_t ctime;\n    int64_t mtime;\n    int32_t version;\n    int32_t cversion;\n    int32_t aversion;\n    int64_t ephemeralOwner;\n    int64_t pzxid;\n};\nint serialize_StatPersisted(struct oarchive *out, const char *tag, struct StatPersisted *v);\nint deserialize_StatPersisted(struct iarchive *in, const char *tag, struct StatPersisted*v);\nvoid deallocate_StatPersisted(struct StatPersisted*);\nstruct ClientInfo {\n    char * authScheme;\n    char * user;\n};\nint serialize_ClientInfo(struct oarchive *out, const char *tag, struct ClientInfo *v);\nint deserialize_ClientInfo(struct iarchive *in, const char *tag, struct ClientInfo*v);\nvoid deallocate_ClientInfo(struct ClientInfo*);\nstruct ConnectRequest {\n    int32_t protocolVersion;\n    int64_t lastZxidSeen;\n    int32_t timeOut;\n    int64_t sessionId;\n    struct buffer passwd;\n};\nint serialize_ConnectRequest(struct oarchive *out, const char *tag, struct ConnectRequest *v);\nint deserialize_ConnectRequest(struct iarchive *in, const char *tag, struct ConnectRequest*v);\nvoid deallocate_ConnectRequest(struct ConnectRequest*);\nstruct ConnectResponse {\n    int32_t protocolVersion;\n    int32_t timeOut;\n    int64_t sessionId;\n    struct buffer passwd;\n};\nint serialize_ConnectResponse(struct oarchive *out, const char *tag, struct ConnectResponse *v);\nint deserialize_ConnectResponse(struct iarchive *in, const char *tag, struct ConnectResponse*v);\nvoid deallocate_ConnectResponse(struct ConnectResponse*);\nstruct String_vector {\n    int32_t count;\n    char * *data;\n\n};\nint serialize_String_vector(struct oarchive *out, const char *tag, struct String_vector *v);\nint deserialize_String_vector(struct iarchive *in, const char *tag, struct String_vector *v);\nint allocate_String_vector(struct String_vector *v, int32_t len);\nint deallocate_String_vector(struct String_vector *v);\nstruct SetWatches {\n    int64_t relativeZxid;\n    struct String_vector dataWatches;\n    struct String_vector existWatches;\n    struct String_vector childWatches;\n};\nint serialize_SetWatches(struct oarchive *out, const char *tag, struct SetWatches *v);\nint deserialize_SetWatches(struct iarchive *in, const char *tag, struct SetWatches*v);\nvoid deallocate_SetWatches(struct SetWatches*);\nstruct SetWatches2 {\n    int64_t relativeZxid;\n    struct String_vector dataWatches;\n    struct String_vector existWatches;\n    struct String_vector childWatches;\n    struct String_vector persistentWatches;\n    struct String_vector persistentRecursiveWatches;\n};\nint serialize_SetWatches2(struct oarchive *out, const char *tag, struct SetWatches2 *v);\nint deserialize_SetWatches2(struct iarchive *in, const char *tag, struct SetWatches2*v);\nvoid deallocate_SetWatches2(struct SetWatches2*);\nstruct RequestHeader {\n    int32_t xid;\n    int32_t type;\n};\nint serialize_RequestHeader(struct oarchive *out, const char *tag, struct RequestHeader *v);\nint deserialize_RequestHeader(struct iarchive *in, const char *tag, struct RequestHeader*v);\nvoid deallocate_RequestHeader(struct RequestHeader*);\nstruct MultiHeader {\n    int32_t type;\n    int32_t done;\n    int32_t err;\n};\nint serialize_MultiHeader(struct oarchive *out, const char *tag, struct MultiHeader *v);\nint deserialize_MultiHeader(struct iarchive *in, const char *tag, struct MultiHeader*v);\nvoid deallocate_MultiHeader(struct MultiHeader*);\nstruct AuthPacket {\n    int32_t type;\n    char * scheme;\n    struct buffer auth;\n};\nint serialize_AuthPacket(struct oarchive *out, const char *tag, struct AuthPacket *v);\nint deserialize_AuthPacket(struct iarchive *in, const char *tag, struct AuthPacket*v);\nvoid deallocate_AuthPacket(struct AuthPacket*);\nstruct ReplyHeader {\n    int32_t xid;\n    int64_t zxid;\n    int32_t err;\n};\nint serialize_ReplyHeader(struct oarchive *out, const char *tag, struct ReplyHeader *v);\nint deserialize_ReplyHeader(struct iarchive *in, const char *tag, struct ReplyHeader*v);\nvoid deallocate_ReplyHeader(struct ReplyHeader*);\nstruct GetDataRequest {\n    char * path;\n    int32_t watch;\n};\nint serialize_GetDataRequest(struct oarchive *out, const char *tag, struct GetDataRequest *v);\nint deserialize_GetDataRequest(struct iarchive *in, const char *tag, struct GetDataRequest*v);\nvoid deallocate_GetDataRequest(struct GetDataRequest*);\nstruct SetDataRequest {\n    char * path;\n    struct buffer data;\n    int32_t version;\n};\nint serialize_SetDataRequest(struct oarchive *out, const char *tag, struct SetDataRequest *v);\nint deserialize_SetDataRequest(struct iarchive *in, const char *tag, struct SetDataRequest*v);\nvoid deallocate_SetDataRequest(struct SetDataRequest*);\nstruct ReconfigRequest {\n    char * joiningServers;\n    char * leavingServers;\n    char * newMembers;\n    int64_t curConfigId;\n};\nint serialize_ReconfigRequest(struct oarchive *out, const char *tag, struct ReconfigRequest *v);\nint deserialize_ReconfigRequest(struct iarchive *in, const char *tag, struct ReconfigRequest*v);\nvoid deallocate_ReconfigRequest(struct ReconfigRequest*);\nstruct SetDataResponse {\n    struct Stat stat;\n};\nint serialize_SetDataResponse(struct oarchive *out, const char *tag, struct SetDataResponse *v);\nint deserialize_SetDataResponse(struct iarchive *in, const char *tag, struct SetDataResponse*v);\nvoid deallocate_SetDataResponse(struct SetDataResponse*);\nstruct GetSASLRequest {\n    struct buffer token;\n};\nint serialize_GetSASLRequest(struct oarchive *out, const char *tag, struct GetSASLRequest *v);\nint deserialize_GetSASLRequest(struct iarchive *in, const char *tag, struct GetSASLRequest*v);\nvoid deallocate_GetSASLRequest(struct GetSASLRequest*);\nstruct SetSASLRequest {\n    struct buffer token;\n};\nint serialize_SetSASLRequest(struct oarchive *out, const char *tag, struct SetSASLRequest *v);\nint deserialize_SetSASLRequest(struct iarchive *in, const char *tag, struct SetSASLRequest*v);\nvoid deallocate_SetSASLRequest(struct SetSASLRequest*);\nstruct SetSASLResponse {\n    struct buffer token;\n};\nint serialize_SetSASLResponse(struct oarchive *out, const char *tag, struct SetSASLResponse *v);\nint deserialize_SetSASLResponse(struct iarchive *in, const char *tag, struct SetSASLResponse*v);\nvoid deallocate_SetSASLResponse(struct SetSASLResponse*);\nstruct ACL_vector {\n    int32_t count;\n    struct ACL *data;\n\n};\nint serialize_ACL_vector(struct oarchive *out, const char *tag, struct ACL_vector *v);\nint deserialize_ACL_vector(struct iarchive *in, const char *tag, struct ACL_vector *v);\nint allocate_ACL_vector(struct ACL_vector *v, int32_t len);\nint deallocate_ACL_vector(struct ACL_vector *v);\nstruct CreateRequest {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t flags;\n};\nint serialize_CreateRequest(struct oarchive *out, const char *tag, struct CreateRequest *v);\nint deserialize_CreateRequest(struct iarchive *in, const char *tag, struct CreateRequest*v);\nvoid deallocate_CreateRequest(struct CreateRequest*);\nstruct CreateTTLRequest {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t flags;\n    int64_t ttl;\n};\nint serialize_CreateTTLRequest(struct oarchive *out, const char *tag, struct CreateTTLRequest *v);\nint deserialize_CreateTTLRequest(struct iarchive *in, const char *tag, struct CreateTTLRequest*v);\nvoid deallocate_CreateTTLRequest(struct CreateTTLRequest*);\nstruct DeleteRequest {\n    char * path;\n    int32_t version;\n};\nint serialize_DeleteRequest(struct oarchive *out, const char *tag, struct DeleteRequest *v);\nint deserialize_DeleteRequest(struct iarchive *in, const char *tag, struct DeleteRequest*v);\nvoid deallocate_DeleteRequest(struct DeleteRequest*);\nstruct GetChildrenRequest {\n    char * path;\n    int32_t watch;\n};\nint serialize_GetChildrenRequest(struct oarchive *out, const char *tag, struct GetChildrenRequest *v);\nint deserialize_GetChildrenRequest(struct iarchive *in, const char *tag, struct GetChildrenRequest*v);\nvoid deallocate_GetChildrenRequest(struct GetChildrenRequest*);\nstruct GetAllChildrenNumberRequest {\n    char * path;\n};\nint serialize_GetAllChildrenNumberRequest(struct oarchive *out, const char *tag, struct GetAllChildrenNumberRequest *v);\nint deserialize_GetAllChildrenNumberRequest(struct iarchive *in, const char *tag, struct GetAllChildrenNumberRequest*v);\nvoid deallocate_GetAllChildrenNumberRequest(struct GetAllChildrenNumberRequest*);\nstruct GetChildren2Request {\n    char * path;\n    int32_t watch;\n};\nint serialize_GetChildren2Request(struct oarchive *out, const char *tag, struct GetChildren2Request *v);\nint deserialize_GetChildren2Request(struct iarchive *in, const char *tag, struct GetChildren2Request*v);\nvoid deallocate_GetChildren2Request(struct GetChildren2Request*);\nstruct CheckVersionRequest {\n    char * path;\n    int32_t version;\n};\nint serialize_CheckVersionRequest(struct oarchive *out, const char *tag, struct CheckVersionRequest *v);\nint deserialize_CheckVersionRequest(struct iarchive *in, const char *tag, struct CheckVersionRequest*v);\nvoid deallocate_CheckVersionRequest(struct CheckVersionRequest*);\nstruct GetMaxChildrenRequest {\n    char * path;\n};\nint serialize_GetMaxChildrenRequest(struct oarchive *out, const char *tag, struct GetMaxChildrenRequest *v);\nint deserialize_GetMaxChildrenRequest(struct iarchive *in, const char *tag, struct GetMaxChildrenRequest*v);\nvoid deallocate_GetMaxChildrenRequest(struct GetMaxChildrenRequest*);\nstruct GetMaxChildrenResponse {\n    int32_t max;\n};\nint serialize_GetMaxChildrenResponse(struct oarchive *out, const char *tag, struct GetMaxChildrenResponse *v);\nint deserialize_GetMaxChildrenResponse(struct iarchive *in, const char *tag, struct GetMaxChildrenResponse*v);\nvoid deallocate_GetMaxChildrenResponse(struct GetMaxChildrenResponse*);\nstruct SetMaxChildrenRequest {\n    char * path;\n    int32_t max;\n};\nint serialize_SetMaxChildrenRequest(struct oarchive *out, const char *tag, struct SetMaxChildrenRequest *v);\nint deserialize_SetMaxChildrenRequest(struct iarchive *in, const char *tag, struct SetMaxChildrenRequest*v);\nvoid deallocate_SetMaxChildrenRequest(struct SetMaxChildrenRequest*);\nstruct SyncRequest {\n    char * path;\n};\nint serialize_SyncRequest(struct oarchive *out, const char *tag, struct SyncRequest *v);\nint deserialize_SyncRequest(struct iarchive *in, const char *tag, struct SyncRequest*v);\nvoid deallocate_SyncRequest(struct SyncRequest*);\nstruct SyncResponse {\n    char * path;\n};\nint serialize_SyncResponse(struct oarchive *out, const char *tag, struct SyncResponse *v);\nint deserialize_SyncResponse(struct iarchive *in, const char *tag, struct SyncResponse*v);\nvoid deallocate_SyncResponse(struct SyncResponse*);\nstruct GetACLRequest {\n    char * path;\n};\nint serialize_GetACLRequest(struct oarchive *out, const char *tag, struct GetACLRequest *v);\nint deserialize_GetACLRequest(struct iarchive *in, const char *tag, struct GetACLRequest*v);\nvoid deallocate_GetACLRequest(struct GetACLRequest*);\nstruct SetACLRequest {\n    char * path;\n    struct ACL_vector acl;\n    int32_t version;\n};\nint serialize_SetACLRequest(struct oarchive *out, const char *tag, struct SetACLRequest *v);\nint deserialize_SetACLRequest(struct iarchive *in, const char *tag, struct SetACLRequest*v);\nvoid deallocate_SetACLRequest(struct SetACLRequest*);\nstruct SetACLResponse {\n    struct Stat stat;\n};\nint serialize_SetACLResponse(struct oarchive *out, const char *tag, struct SetACLResponse *v);\nint deserialize_SetACLResponse(struct iarchive *in, const char *tag, struct SetACLResponse*v);\nvoid deallocate_SetACLResponse(struct SetACLResponse*);\nstruct AddWatchRequest {\n    char * path;\n    int32_t mode;\n};\nint serialize_AddWatchRequest(struct oarchive *out, const char *tag, struct AddWatchRequest *v);\nint deserialize_AddWatchRequest(struct iarchive *in, const char *tag, struct AddWatchRequest*v);\nvoid deallocate_AddWatchRequest(struct AddWatchRequest*);\nstruct WatcherEvent {\n    int32_t type;\n    int32_t state;\n    char * path;\n};\nint serialize_WatcherEvent(struct oarchive *out, const char *tag, struct WatcherEvent *v);\nint deserialize_WatcherEvent(struct iarchive *in, const char *tag, struct WatcherEvent*v);\nvoid deallocate_WatcherEvent(struct WatcherEvent*);\nstruct ErrorResponse {\n    int32_t err;\n};\nint serialize_ErrorResponse(struct oarchive *out, const char *tag, struct ErrorResponse *v);\nint deserialize_ErrorResponse(struct iarchive *in, const char *tag, struct ErrorResponse*v);\nvoid deallocate_ErrorResponse(struct ErrorResponse*);\nstruct CreateResponse {\n    char * path;\n};\nint serialize_CreateResponse(struct oarchive *out, const char *tag, struct CreateResponse *v);\nint deserialize_CreateResponse(struct iarchive *in, const char *tag, struct CreateResponse*v);\nvoid deallocate_CreateResponse(struct CreateResponse*);\nstruct Create2Response {\n    char * path;\n    struct Stat stat;\n};\nint serialize_Create2Response(struct oarchive *out, const char *tag, struct Create2Response *v);\nint deserialize_Create2Response(struct iarchive *in, const char *tag, struct Create2Response*v);\nvoid deallocate_Create2Response(struct Create2Response*);\nstruct ExistsRequest {\n    char * path;\n    int32_t watch;\n};\nint serialize_ExistsRequest(struct oarchive *out, const char *tag, struct ExistsRequest *v);\nint deserialize_ExistsRequest(struct iarchive *in, const char *tag, struct ExistsRequest*v);\nvoid deallocate_ExistsRequest(struct ExistsRequest*);\nstruct ExistsResponse {\n    struct Stat stat;\n};\nint serialize_ExistsResponse(struct oarchive *out, const char *tag, struct ExistsResponse *v);\nint deserialize_ExistsResponse(struct iarchive *in, const char *tag, struct ExistsResponse*v);\nvoid deallocate_ExistsResponse(struct ExistsResponse*);\nstruct GetDataResponse {\n    struct buffer data;\n    struct Stat stat;\n};\nint serialize_GetDataResponse(struct oarchive *out, const char *tag, struct GetDataResponse *v);\nint deserialize_GetDataResponse(struct iarchive *in, const char *tag, struct GetDataResponse*v);\nvoid deallocate_GetDataResponse(struct GetDataResponse*);\nstruct GetChildrenResponse {\n    struct String_vector children;\n};\nint serialize_GetChildrenResponse(struct oarchive *out, const char *tag, struct GetChildrenResponse *v);\nint deserialize_GetChildrenResponse(struct iarchive *in, const char *tag, struct GetChildrenResponse*v);\nvoid deallocate_GetChildrenResponse(struct GetChildrenResponse*);\nstruct GetAllChildrenNumberResponse {\n    int32_t totalNumber;\n};\nint serialize_GetAllChildrenNumberResponse(struct oarchive *out, const char *tag, struct GetAllChildrenNumberResponse *v);\nint deserialize_GetAllChildrenNumberResponse(struct iarchive *in, const char *tag, struct GetAllChildrenNumberResponse*v);\nvoid deallocate_GetAllChildrenNumberResponse(struct GetAllChildrenNumberResponse*);\nstruct GetChildren2Response {\n    struct String_vector children;\n    struct Stat stat;\n};\nint serialize_GetChildren2Response(struct oarchive *out, const char *tag, struct GetChildren2Response *v);\nint deserialize_GetChildren2Response(struct iarchive *in, const char *tag, struct GetChildren2Response*v);\nvoid deallocate_GetChildren2Response(struct GetChildren2Response*);\nstruct GetACLResponse {\n    struct ACL_vector acl;\n    struct Stat stat;\n};\nint serialize_GetACLResponse(struct oarchive *out, const char *tag, struct GetACLResponse *v);\nint deserialize_GetACLResponse(struct iarchive *in, const char *tag, struct GetACLResponse*v);\nvoid deallocate_GetACLResponse(struct GetACLResponse*);\nstruct CheckWatchesRequest {\n    char * path;\n    int32_t type;\n};\nint serialize_CheckWatchesRequest(struct oarchive *out, const char *tag, struct CheckWatchesRequest *v);\nint deserialize_CheckWatchesRequest(struct iarchive *in, const char *tag, struct CheckWatchesRequest*v);\nvoid deallocate_CheckWatchesRequest(struct CheckWatchesRequest*);\nstruct RemoveWatchesRequest {\n    char * path;\n    int32_t type;\n};\nint serialize_RemoveWatchesRequest(struct oarchive *out, const char *tag, struct RemoveWatchesRequest *v);\nint deserialize_RemoveWatchesRequest(struct iarchive *in, const char *tag, struct RemoveWatchesRequest*v);\nvoid deallocate_RemoveWatchesRequest(struct RemoveWatchesRequest*);\nstruct GetEphemeralsRequest {\n    char * prefixPath;\n};\nint serialize_GetEphemeralsRequest(struct oarchive *out, const char *tag, struct GetEphemeralsRequest *v);\nint deserialize_GetEphemeralsRequest(struct iarchive *in, const char *tag, struct GetEphemeralsRequest*v);\nvoid deallocate_GetEphemeralsRequest(struct GetEphemeralsRequest*);\nstruct GetEphemeralsResponse {\n    struct String_vector ephemerals;\n};\nint serialize_GetEphemeralsResponse(struct oarchive *out, const char *tag, struct GetEphemeralsResponse *v);\nint deserialize_GetEphemeralsResponse(struct iarchive *in, const char *tag, struct GetEphemeralsResponse*v);\nvoid deallocate_GetEphemeralsResponse(struct GetEphemeralsResponse*);\nstruct ClientInfo_vector {\n    int32_t count;\n    struct ClientInfo *data;\n\n};\nint serialize_ClientInfo_vector(struct oarchive *out, const char *tag, struct ClientInfo_vector *v);\nint deserialize_ClientInfo_vector(struct iarchive *in, const char *tag, struct ClientInfo_vector *v);\nint allocate_ClientInfo_vector(struct ClientInfo_vector *v, int32_t len);\nint deallocate_ClientInfo_vector(struct ClientInfo_vector *v);\nstruct WhoAmIResponse {\n    struct ClientInfo_vector clientInfo;\n};\nint serialize_WhoAmIResponse(struct oarchive *out, const char *tag, struct WhoAmIResponse *v);\nint deserialize_WhoAmIResponse(struct iarchive *in, const char *tag, struct WhoAmIResponse*v);\nvoid deallocate_WhoAmIResponse(struct WhoAmIResponse*);\nstruct LearnerInfo {\n    int64_t serverid;\n    int32_t protocolVersion;\n    int64_t configVersion;\n};\nint serialize_LearnerInfo(struct oarchive *out, const char *tag, struct LearnerInfo *v);\nint deserialize_LearnerInfo(struct iarchive *in, const char *tag, struct LearnerInfo*v);\nvoid deallocate_LearnerInfo(struct LearnerInfo*);\nstruct Id_vector {\n    int32_t count;\n    struct Id *data;\n\n};\nint serialize_Id_vector(struct oarchive *out, const char *tag, struct Id_vector *v);\nint deserialize_Id_vector(struct iarchive *in, const char *tag, struct Id_vector *v);\nint allocate_Id_vector(struct Id_vector *v, int32_t len);\nint deallocate_Id_vector(struct Id_vector *v);\nstruct QuorumPacket {\n    int32_t type;\n    int64_t zxid;\n    struct buffer data;\n    struct Id_vector authinfo;\n};\nint serialize_QuorumPacket(struct oarchive *out, const char *tag, struct QuorumPacket *v);\nint deserialize_QuorumPacket(struct iarchive *in, const char *tag, struct QuorumPacket*v);\nvoid deallocate_QuorumPacket(struct QuorumPacket*);\nstruct QuorumAuthPacket {\n    int64_t magic;\n    int32_t status;\n    struct buffer token;\n};\nint serialize_QuorumAuthPacket(struct oarchive *out, const char *tag, struct QuorumAuthPacket *v);\nint deserialize_QuorumAuthPacket(struct iarchive *in, const char *tag, struct QuorumAuthPacket*v);\nvoid deallocate_QuorumAuthPacket(struct QuorumAuthPacket*);\nstruct FileHeader {\n    int32_t magic;\n    int32_t version;\n    int64_t dbid;\n};\nint serialize_FileHeader(struct oarchive *out, const char *tag, struct FileHeader *v);\nint deserialize_FileHeader(struct iarchive *in, const char *tag, struct FileHeader*v);\nvoid deallocate_FileHeader(struct FileHeader*);\nstruct TxnDigest {\n    int32_t version;\n    int64_t treeDigest;\n};\nint serialize_TxnDigest(struct oarchive *out, const char *tag, struct TxnDigest *v);\nint deserialize_TxnDigest(struct iarchive *in, const char *tag, struct TxnDigest*v);\nvoid deallocate_TxnDigest(struct TxnDigest*);\nstruct TxnHeader {\n    int64_t clientId;\n    int32_t cxid;\n    int64_t zxid;\n    int64_t time;\n    int32_t type;\n};\nint serialize_TxnHeader(struct oarchive *out, const char *tag, struct TxnHeader *v);\nint deserialize_TxnHeader(struct iarchive *in, const char *tag, struct TxnHeader*v);\nvoid deallocate_TxnHeader(struct TxnHeader*);\nstruct CreateTxnV0 {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t ephemeral;\n};\nint serialize_CreateTxnV0(struct oarchive *out, const char *tag, struct CreateTxnV0 *v);\nint deserialize_CreateTxnV0(struct iarchive *in, const char *tag, struct CreateTxnV0*v);\nvoid deallocate_CreateTxnV0(struct CreateTxnV0*);\nstruct CreateTxn {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t ephemeral;\n    int32_t parentCVersion;\n};\nint serialize_CreateTxn(struct oarchive *out, const char *tag, struct CreateTxn *v);\nint deserialize_CreateTxn(struct iarchive *in, const char *tag, struct CreateTxn*v);\nvoid deallocate_CreateTxn(struct CreateTxn*);\nstruct CreateTTLTxn {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t parentCVersion;\n    int64_t ttl;\n};\nint serialize_CreateTTLTxn(struct oarchive *out, const char *tag, struct CreateTTLTxn *v);\nint deserialize_CreateTTLTxn(struct iarchive *in, const char *tag, struct CreateTTLTxn*v);\nvoid deallocate_CreateTTLTxn(struct CreateTTLTxn*);\nstruct CreateContainerTxn {\n    char * path;\n    struct buffer data;\n    struct ACL_vector acl;\n    int32_t parentCVersion;\n};\nint serialize_CreateContainerTxn(struct oarchive *out, const char *tag, struct CreateContainerTxn *v);\nint deserialize_CreateContainerTxn(struct iarchive *in, const char *tag, struct CreateContainerTxn*v);\nvoid deallocate_CreateContainerTxn(struct CreateContainerTxn*);\nstruct DeleteTxn {\n    char * path;\n};\nint serialize_DeleteTxn(struct oarchive *out, const char *tag, struct DeleteTxn *v);\nint deserialize_DeleteTxn(struct iarchive *in, const char *tag, struct DeleteTxn*v);\nvoid deallocate_DeleteTxn(struct DeleteTxn*);\nstruct SetDataTxn {\n    char * path;\n    struct buffer data;\n    int32_t version;\n};\nint serialize_SetDataTxn(struct oarchive *out, const char *tag, struct SetDataTxn *v);\nint deserialize_SetDataTxn(struct iarchive *in, const char *tag, struct SetDataTxn*v);\nvoid deallocate_SetDataTxn(struct SetDataTxn*);\nstruct CheckVersionTxn {\n    char * path;\n    int32_t version;\n};\nint serialize_CheckVersionTxn(struct oarchive *out, const char *tag, struct CheckVersionTxn *v);\nint deserialize_CheckVersionTxn(struct iarchive *in, const char *tag, struct CheckVersionTxn*v);\nvoid deallocate_CheckVersionTxn(struct CheckVersionTxn*);\nstruct SetACLTxn {\n    char * path;\n    struct ACL_vector acl;\n    int32_t version;\n};\nint serialize_SetACLTxn(struct oarchive *out, const char *tag, struct SetACLTxn *v);\nint deserialize_SetACLTxn(struct iarchive *in, const char *tag, struct SetACLTxn*v);\nvoid deallocate_SetACLTxn(struct SetACLTxn*);\nstruct SetMaxChildrenTxn {\n    char * path;\n    int32_t max;\n};\nint serialize_SetMaxChildrenTxn(struct oarchive *out, const char *tag, struct SetMaxChildrenTxn *v);\nint deserialize_SetMaxChildrenTxn(struct iarchive *in, const char *tag, struct SetMaxChildrenTxn*v);\nvoid deallocate_SetMaxChildrenTxn(struct SetMaxChildrenTxn*);\nstruct CreateSessionTxn {\n    int32_t timeOut;\n};\nint serialize_CreateSessionTxn(struct oarchive *out, const char *tag, struct CreateSessionTxn *v);\nint deserialize_CreateSessionTxn(struct iarchive *in, const char *tag, struct CreateSessionTxn*v);\nvoid deallocate_CreateSessionTxn(struct CreateSessionTxn*);\nstruct CloseSessionTxn {\n    struct String_vector paths2Delete;\n};\nint serialize_CloseSessionTxn(struct oarchive *out, const char *tag, struct CloseSessionTxn *v);\nint deserialize_CloseSessionTxn(struct iarchive *in, const char *tag, struct CloseSessionTxn*v);\nvoid deallocate_CloseSessionTxn(struct CloseSessionTxn*);\nstruct ErrorTxn {\n    int32_t err;\n};\nint serialize_ErrorTxn(struct oarchive *out, const char *tag, struct ErrorTxn *v);\nint deserialize_ErrorTxn(struct iarchive *in, const char *tag, struct ErrorTxn*v);\nvoid deallocate_ErrorTxn(struct ErrorTxn*);\nstruct Txn {\n    int32_t type;\n    struct buffer data;\n};\nint serialize_Txn(struct oarchive *out, const char *tag, struct Txn *v);\nint deserialize_Txn(struct iarchive *in, const char *tag, struct Txn*v);\nvoid deallocate_Txn(struct Txn*);\nstruct Txn_vector {\n    int32_t count;\n    struct Txn *data;\n\n};\nint serialize_Txn_vector(struct oarchive *out, const char *tag, struct Txn_vector *v);\nint deserialize_Txn_vector(struct iarchive *in, const char *tag, struct Txn_vector *v);\nint allocate_Txn_vector(struct Txn_vector *v, int32_t len);\nint deallocate_Txn_vector(struct Txn_vector *v);\nstruct MultiTxn {\n    struct Txn_vector txns;\n};\nint serialize_MultiTxn(struct oarchive *out, const char *tag, struct MultiTxn *v);\nint deserialize_MultiTxn(struct iarchive *in, const char *tag, struct MultiTxn*v);\nvoid deallocate_MultiTxn(struct MultiTxn*);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif //ZOOKEEPER_JUTE__\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/install-sh",
    "content": "#!/bin/sh\n# install - install a program, script, or datafile\n\nscriptversion=2014-09-12.12; # UTC\n\n# This originates from X11R5 (mit/util/scripts/install.sh), which was\n# later released in X11R6 (xc/config/util/install.sh) with the\n# following copyright and license.\n#\n# Copyright (C) 1994 X Consortium\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to\n# deal in the Software without restriction, including without limitation the\n# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n# sell copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\n# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN\n# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-\n# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n# Except as contained in this notice, the name of the X Consortium shall not\n# be used in advertising or otherwise to promote the sale, use or other deal-\n# ings in this Software without prior written authorization from the X Consor-\n# tium.\n#\n#\n# FSF changes to this file are in the public domain.\n#\n# Calling this script install-sh is preferred over install.sh, to prevent\n# 'make' implicit rules from creating a file called install from it\n# when there is no Makefile.\n#\n# This script is compatible with the BSD install script, but was written\n# from scratch.\n\ntab='\t'\nnl='\n'\nIFS=\" $tab$nl\"\n\n# Set DOITPROG to \"echo\" to test this script.\n\ndoit=${DOITPROG-}\ndoit_exec=${doit:-exec}\n\n# Put in absolute file names if you don't have them in your path;\n# or use environment vars.\n\nchgrpprog=${CHGRPPROG-chgrp}\nchmodprog=${CHMODPROG-chmod}\nchownprog=${CHOWNPROG-chown}\ncmpprog=${CMPPROG-cmp}\ncpprog=${CPPROG-cp}\nmkdirprog=${MKDIRPROG-mkdir}\nmvprog=${MVPROG-mv}\nrmprog=${RMPROG-rm}\nstripprog=${STRIPPROG-strip}\n\nposix_mkdir=\n\n# Desired mode of installed file.\nmode=0755\n\nchgrpcmd=\nchmodcmd=$chmodprog\nchowncmd=\nmvcmd=$mvprog\nrmcmd=\"$rmprog -f\"\nstripcmd=\n\nsrc=\ndst=\ndir_arg=\ndst_arg=\n\ncopy_on_change=false\nis_target_a_directory=possibly\n\nusage=\"\\\nUsage: $0 [OPTION]... [-T] SRCFILE DSTFILE\n   or: $0 [OPTION]... SRCFILES... DIRECTORY\n   or: $0 [OPTION]... -t DIRECTORY SRCFILES...\n   or: $0 [OPTION]... -d DIRECTORIES...\n\nIn the 1st form, copy SRCFILE to DSTFILE.\nIn the 2nd and 3rd, copy all SRCFILES to DIRECTORY.\nIn the 4th, create DIRECTORIES.\n\nOptions:\n     --help     display this help and exit.\n     --version  display version info and exit.\n\n  -c            (ignored)\n  -C            install only if different (preserve the last data modification time)\n  -d            create directories instead of installing files.\n  -g GROUP      $chgrpprog installed files to GROUP.\n  -m MODE       $chmodprog installed files to MODE.\n  -o USER       $chownprog installed files to USER.\n  -s            $stripprog installed files.\n  -t DIRECTORY  install into DIRECTORY.\n  -T            report an error if DSTFILE is a directory.\n\nEnvironment variables override the default commands:\n  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG\n  RMPROG STRIPPROG\n\"\n\nwhile test $# -ne 0; do\n  case $1 in\n    -c) ;;\n\n    -C) copy_on_change=true;;\n\n    -d) dir_arg=true;;\n\n    -g) chgrpcmd=\"$chgrpprog $2\"\n        shift;;\n\n    --help) echo \"$usage\"; exit $?;;\n\n    -m) mode=$2\n        case $mode in\n          *' '* | *\"$tab\"* | *\"$nl\"* | *'*'* | *'?'* | *'['*)\n            echo \"$0: invalid mode: $mode\" >&2\n            exit 1;;\n        esac\n        shift;;\n\n    -o) chowncmd=\"$chownprog $2\"\n        shift;;\n\n    -s) stripcmd=$stripprog;;\n\n    -t)\n        is_target_a_directory=always\n        dst_arg=$2\n        # Protect names problematic for 'test' and other utilities.\n        case $dst_arg in\n          -* | [=\\(\\)!]) dst_arg=./$dst_arg;;\n        esac\n        shift;;\n\n    -T) is_target_a_directory=never;;\n\n    --version) echo \"$0 $scriptversion\"; exit $?;;\n\n    --) shift\n        break;;\n\n    -*) echo \"$0: invalid option: $1\" >&2\n        exit 1;;\n\n    *)  break;;\n  esac\n  shift\ndone\n\n# We allow the use of options -d and -T together, by making -d\n# take the precedence; this is for compatibility with GNU install.\n\nif test -n \"$dir_arg\"; then\n  if test -n \"$dst_arg\"; then\n    echo \"$0: target directory not allowed when installing a directory.\" >&2\n    exit 1\n  fi\nfi\n\nif test $# -ne 0 && test -z \"$dir_arg$dst_arg\"; then\n  # When -d is used, all remaining arguments are directories to create.\n  # When -t is used, the destination is already specified.\n  # Otherwise, the last argument is the destination.  Remove it from $@.\n  for arg\n  do\n    if test -n \"$dst_arg\"; then\n      # $@ is not empty: it contains at least $arg.\n      set fnord \"$@\" \"$dst_arg\"\n      shift # fnord\n    fi\n    shift # arg\n    dst_arg=$arg\n    # Protect names problematic for 'test' and other utilities.\n    case $dst_arg in\n      -* | [=\\(\\)!]) dst_arg=./$dst_arg;;\n    esac\n  done\nfi\n\nif test $# -eq 0; then\n  if test -z \"$dir_arg\"; then\n    echo \"$0: no input file specified.\" >&2\n    exit 1\n  fi\n  # It's OK to call 'install-sh -d' without argument.\n  # This can happen when creating conditional directories.\n  exit 0\nfi\n\nif test -z \"$dir_arg\"; then\n  if test $# -gt 1 || test \"$is_target_a_directory\" = always; then\n    if test ! -d \"$dst_arg\"; then\n      echo \"$0: $dst_arg: Is not a directory.\" >&2\n      exit 1\n    fi\n  fi\nfi\n\nif test -z \"$dir_arg\"; then\n  do_exit='(exit $ret); exit $ret'\n  trap \"ret=129; $do_exit\" 1\n  trap \"ret=130; $do_exit\" 2\n  trap \"ret=141; $do_exit\" 13\n  trap \"ret=143; $do_exit\" 15\n\n  # Set umask so as not to create temps with too-generous modes.\n  # However, 'strip' requires both read and write access to temps.\n  case $mode in\n    # Optimize common cases.\n    *644) cp_umask=133;;\n    *755) cp_umask=22;;\n\n    *[0-7])\n      if test -z \"$stripcmd\"; then\n        u_plus_rw=\n      else\n        u_plus_rw='% 200'\n      fi\n      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;\n    *)\n      if test -z \"$stripcmd\"; then\n        u_plus_rw=\n      else\n        u_plus_rw=,u+rw\n      fi\n      cp_umask=$mode$u_plus_rw;;\n  esac\nfi\n\nfor src\ndo\n  # Protect names problematic for 'test' and other utilities.\n  case $src in\n    -* | [=\\(\\)!]) src=./$src;;\n  esac\n\n  if test -n \"$dir_arg\"; then\n    dst=$src\n    dstdir=$dst\n    test -d \"$dstdir\"\n    dstdir_status=$?\n  else\n\n    # Waiting for this to be detected by the \"$cpprog $src $dsttmp\" command\n    # might cause directories to be created, which would be especially bad\n    # if $src (and thus $dsttmp) contains '*'.\n    if test ! -f \"$src\" && test ! -d \"$src\"; then\n      echo \"$0: $src does not exist.\" >&2\n      exit 1\n    fi\n\n    if test -z \"$dst_arg\"; then\n      echo \"$0: no destination specified.\" >&2\n      exit 1\n    fi\n    dst=$dst_arg\n\n    # If destination is a directory, append the input filename; won't work\n    # if double slashes aren't ignored.\n    if test -d \"$dst\"; then\n      if test \"$is_target_a_directory\" = never; then\n        echo \"$0: $dst_arg: Is a directory\" >&2\n        exit 1\n      fi\n      dstdir=$dst\n      dst=$dstdir/`basename \"$src\"`\n      dstdir_status=0\n    else\n      dstdir=`dirname \"$dst\"`\n      test -d \"$dstdir\"\n      dstdir_status=$?\n    fi\n  fi\n\n  obsolete_mkdir_used=false\n\n  if test $dstdir_status != 0; then\n    case $posix_mkdir in\n      '')\n        # Create intermediate dirs using mode 755 as modified by the umask.\n        # This is like FreeBSD 'install' as of 1997-10-28.\n        umask=`umask`\n        case $stripcmd.$umask in\n          # Optimize common cases.\n          *[2367][2367]) mkdir_umask=$umask;;\n          .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;\n\n          *[0-7])\n            mkdir_umask=`expr $umask + 22 \\\n              - $umask % 100 % 40 + $umask % 20 \\\n              - $umask % 10 % 4 + $umask % 2\n            `;;\n          *) mkdir_umask=$umask,go-w;;\n        esac\n\n        # With -d, create the new directory with the user-specified mode.\n        # Otherwise, rely on $mkdir_umask.\n        if test -n \"$dir_arg\"; then\n          mkdir_mode=-m$mode\n        else\n          mkdir_mode=\n        fi\n\n        posix_mkdir=false\n        case $umask in\n          *[123567][0-7][0-7])\n            # POSIX mkdir -p sets u+wx bits regardless of umask, which\n            # is incompatible with FreeBSD 'install' when (umask & 300) != 0.\n            ;;\n          *)\n            # $RANDOM is not portable (e.g. dash);  use it when possible to\n            # lower collision chance\n            tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$\n            trap 'ret=$?; rmdir \"$tmpdir/a/b\" \"$tmpdir/a\" \"$tmpdir\" 2>/dev/null; exit $ret' 0\n\n            # As \"mkdir -p\" follows symlinks and we work in /tmp possibly;  so\n            # create the $tmpdir first (and fail if unsuccessful) to make sure\n            # that nobody tries to guess the $tmpdir name.\n            if (umask $mkdir_umask &&\n                $mkdirprog $mkdir_mode \"$tmpdir\" &&\n                exec $mkdirprog $mkdir_mode -p -- \"$tmpdir/a/b\") >/dev/null 2>&1\n            then\n              if test -z \"$dir_arg\" || {\n                   # Check for POSIX incompatibilities with -m.\n                   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or\n                   # other-writable bit of parent directory when it shouldn't.\n                   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.\n                   test_tmpdir=\"$tmpdir/a\"\n                   ls_ld_tmpdir=`ls -ld \"$test_tmpdir\"`\n                   case $ls_ld_tmpdir in\n                     d????-?r-*) different_mode=700;;\n                     d????-?--*) different_mode=755;;\n                     *) false;;\n                   esac &&\n                   $mkdirprog -m$different_mode -p -- \"$test_tmpdir\" && {\n                     ls_ld_tmpdir_1=`ls -ld \"$test_tmpdir\"`\n                     test \"$ls_ld_tmpdir\" = \"$ls_ld_tmpdir_1\"\n                   }\n                 }\n              then posix_mkdir=:\n              fi\n              rmdir \"$tmpdir/a/b\" \"$tmpdir/a\" \"$tmpdir\"\n            else\n              # Remove any dirs left behind by ancient mkdir implementations.\n              rmdir ./$mkdir_mode ./-p ./-- \"$tmpdir\" 2>/dev/null\n            fi\n            trap '' 0;;\n        esac;;\n    esac\n\n    if\n      $posix_mkdir && (\n        umask $mkdir_umask &&\n        $doit_exec $mkdirprog $mkdir_mode -p -- \"$dstdir\"\n      )\n    then :\n    else\n\n      # The umask is ridiculous, or mkdir does not conform to POSIX,\n      # or it failed possibly due to a race condition.  Create the\n      # directory the slow way, step by step, checking for races as we go.\n\n      case $dstdir in\n        /*) prefix='/';;\n        [-=\\(\\)!]*) prefix='./';;\n        *)  prefix='';;\n      esac\n\n      oIFS=$IFS\n      IFS=/\n      set -f\n      set fnord $dstdir\n      shift\n      set +f\n      IFS=$oIFS\n\n      prefixes=\n\n      for d\n      do\n        test X\"$d\" = X && continue\n\n        prefix=$prefix$d\n        if test -d \"$prefix\"; then\n          prefixes=\n        else\n          if $posix_mkdir; then\n            (umask=$mkdir_umask &&\n             $doit_exec $mkdirprog $mkdir_mode -p -- \"$dstdir\") && break\n            # Don't fail if two instances are running concurrently.\n            test -d \"$prefix\" || exit 1\n          else\n            case $prefix in\n              *\\'*) qprefix=`echo \"$prefix\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;;\n              *) qprefix=$prefix;;\n            esac\n            prefixes=\"$prefixes '$qprefix'\"\n          fi\n        fi\n        prefix=$prefix/\n      done\n\n      if test -n \"$prefixes\"; then\n        # Don't fail if two instances are running concurrently.\n        (umask $mkdir_umask &&\n         eval \"\\$doit_exec \\$mkdirprog $prefixes\") ||\n          test -d \"$dstdir\" || exit 1\n        obsolete_mkdir_used=true\n      fi\n    fi\n  fi\n\n  if test -n \"$dir_arg\"; then\n    { test -z \"$chowncmd\" || $doit $chowncmd \"$dst\"; } &&\n    { test -z \"$chgrpcmd\" || $doit $chgrpcmd \"$dst\"; } &&\n    { test \"$obsolete_mkdir_used$chowncmd$chgrpcmd\" = false ||\n      test -z \"$chmodcmd\" || $doit $chmodcmd $mode \"$dst\"; } || exit 1\n  else\n\n    # Make a couple of temp file names in the proper directory.\n    dsttmp=$dstdir/_inst.$$_\n    rmtmp=$dstdir/_rm.$$_\n\n    # Trap to clean up those temp files at exit.\n    trap 'ret=$?; rm -f \"$dsttmp\" \"$rmtmp\" && exit $ret' 0\n\n    # Copy the file name to the temp name.\n    (umask $cp_umask && $doit_exec $cpprog \"$src\" \"$dsttmp\") &&\n\n    # and set any options; do chmod last to preserve setuid bits.\n    #\n    # If any of these fail, we abort the whole thing.  If we want to\n    # ignore errors from any of these, just make sure not to ignore\n    # errors from the above \"$doit $cpprog $src $dsttmp\" command.\n    #\n    { test -z \"$chowncmd\" || $doit $chowncmd \"$dsttmp\"; } &&\n    { test -z \"$chgrpcmd\" || $doit $chgrpcmd \"$dsttmp\"; } &&\n    { test -z \"$stripcmd\" || $doit $stripcmd \"$dsttmp\"; } &&\n    { test -z \"$chmodcmd\" || $doit $chmodcmd $mode \"$dsttmp\"; } &&\n\n    # If -C, don't bother to copy if it wouldn't change the file.\n    if $copy_on_change &&\n       old=`LC_ALL=C ls -dlL \"$dst\"     2>/dev/null` &&\n       new=`LC_ALL=C ls -dlL \"$dsttmp\"  2>/dev/null` &&\n       set -f &&\n       set X $old && old=:$2:$4:$5:$6 &&\n       set X $new && new=:$2:$4:$5:$6 &&\n       set +f &&\n       test \"$old\" = \"$new\" &&\n       $cmpprog \"$dst\" \"$dsttmp\" >/dev/null 2>&1\n    then\n      rm -f \"$dsttmp\"\n    else\n      # Rename the file to the real destination.\n      $doit $mvcmd -f \"$dsttmp\" \"$dst\" 2>/dev/null ||\n\n      # The rename failed, perhaps because mv can't rename something else\n      # to itself, or perhaps because mv is so ancient that it does not\n      # support -f.\n      {\n        # Now remove or move aside any old file at destination location.\n        # We try this two ways since rm can't unlink itself on some\n        # systems and the destination file might be busy for other\n        # reasons.  In this case, the final cleanup might fail but the new\n        # file should still install successfully.\n        {\n          test ! -f \"$dst\" ||\n          $doit $rmcmd -f \"$dst\" 2>/dev/null ||\n          { $doit $mvcmd -f \"$dst\" \"$rmtmp\" 2>/dev/null &&\n            { $doit $rmcmd -f \"$rmtmp\" 2>/dev/null; :; }\n          } ||\n          { echo \"$0: cannot unlink or rename $dst\" >&2\n            (exit 1); exit 1\n          }\n        } &&\n\n        # Now rename the file to the real destination.\n        $doit $mvcmd \"$dsttmp\" \"$dst\"\n      }\n    fi || exit 1\n\n    trap '' 0\n  fi\ndone\n\n# Local variables:\n# eval: (add-hook 'write-file-hooks 'time-stamp)\n# time-stamp-start: \"scriptversion=\"\n# time-stamp-format: \"%:y-%02m-%02d.%02H\"\n# time-stamp-time-zone: \"UTC\"\n# time-stamp-end: \"; # UTC\"\n# End:\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/ltmain.sh",
    "content": "#! /bin/sh\n## DO NOT EDIT - This file generated from ./build-aux/ltmain.in\n##               by inline-source v2014-01-03.01\n\n# libtool (GNU libtool) 2.4.6\n# Provide generalized library-building support services.\n# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996\n\n# Copyright (C) 1996-2015 Free Software Foundation, Inc.\n# This is free software; see the source for copying conditions.  There is NO\n# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n# GNU Libtool is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# As a special exception to the GNU General Public License,\n# if you distribute this file as part of a program or library that\n# is built using GNU Libtool, you may include this file under the\n# same distribution terms that you use for the rest of that program.\n#\n# GNU Libtool is distributed in the hope that it will be useful, but\n# WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n# General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n\nPROGRAM=libtool\nPACKAGE=libtool\nVERSION=\"2.4.6 Debian-2.4.6-2\"\npackage_revision=2.4.6\n\n\n## ------ ##\n## Usage. ##\n## ------ ##\n\n# Run './libtool --help' for help with using this script from the\n# command line.\n\n\n## ------------------------------- ##\n## User overridable command paths. ##\n## ------------------------------- ##\n\n# After configure completes, it has a better idea of some of the\n# shell tools we need than the defaults used by the functions shared\n# with bootstrap, so set those here where they can still be over-\n# ridden by the user, but otherwise take precedence.\n\n: ${AUTOCONF=\"autoconf\"}\n: ${AUTOMAKE=\"automake\"}\n\n\n## -------------------------- ##\n## Source external libraries. ##\n## -------------------------- ##\n\n# Much of our low-level functionality needs to be sourced from external\n# libraries, which are installed to $pkgauxdir.\n\n# Set a version string for this script.\nscriptversion=2015-01-20.17; # UTC\n\n# General shell script boiler plate, and helper functions.\n# Written by Gary V. Vaughan, 2004\n\n# Copyright (C) 2004-2015 Free Software Foundation, Inc.\n# This is free software; see the source for copying conditions.  There is NO\n# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 3 of the License, or\n# (at your option) any later version.\n\n# As a special exception to the GNU General Public License, if you distribute\n# this file as part of a program or library that is built using GNU Libtool,\n# you may include this file under the same distribution terms that you use\n# for the rest of that program.\n\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU\n# General Public License for more details.\n\n# You should have received a copy of the GNU General Public License\n# along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n# Please report bugs or propose patches to gary@gnu.org.\n\n\n## ------ ##\n## Usage. ##\n## ------ ##\n\n# Evaluate this file near the top of your script to gain access to\n# the functions and variables defined here:\n#\n#   . `echo \"$0\" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh\n#\n# If you need to override any of the default environment variable\n# settings, do that before evaluating this file.\n\n\n## -------------------- ##\n## Shell normalisation. ##\n## -------------------- ##\n\n# Some shells need a little help to be as Bourne compatible as possible.\n# Before doing anything else, make sure all that help has been provided!\n\nDUALCASE=1; export DUALCASE # for MKS sh\nif test -n \"${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :\n  emulate sh\n  NULLCMD=:\n  # Pre-4.2 versions of Zsh do word splitting on ${1+\"$@\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '${1+\"$@\"}'='\"$@\"'\n  setopt NO_GLOB_SUBST\nelse\n  case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac\nfi\n\n# NLS nuisances: We save the old values in case they are required later.\n_G_user_locale=\n_G_safe_locale=\nfor _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES\ndo\n  eval \"if test set = \\\"\\${$_G_var+set}\\\"; then\n          save_$_G_var=\\$$_G_var\n          $_G_var=C\n\t  export $_G_var\n\t  _G_user_locale=\\\"$_G_var=\\\\\\$save_\\$_G_var; \\$_G_user_locale\\\"\n\t  _G_safe_locale=\\\"$_G_var=C; \\$_G_safe_locale\\\"\n\tfi\"\ndone\n\n# CDPATH.\n(unset CDPATH) >/dev/null 2>&1 && unset CDPATH\n\n# Make sure IFS has a sensible default\nsp=' '\nnl='\n'\nIFS=\"$sp\t$nl\"\n\n# There are apparently some retarded systems that use ';' as a PATH separator!\nif test \"${PATH_SEPARATOR+set}\" != set; then\n  PATH_SEPARATOR=:\n  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {\n    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||\n      PATH_SEPARATOR=';'\n  }\nfi\n\n\n\n## ------------------------- ##\n## Locate command utilities. ##\n## ------------------------- ##\n\n\n# func_executable_p FILE\n# ----------------------\n# Check that FILE is an executable regular file.\nfunc_executable_p ()\n{\n    test -f \"$1\" && test -x \"$1\"\n}\n\n\n# func_path_progs PROGS_LIST CHECK_FUNC [PATH]\n# --------------------------------------------\n# Search for either a program that responds to --version with output\n# containing \"GNU\", or else returned by CHECK_FUNC otherwise, by\n# trying all the directories in PATH with each of the elements of\n# PROGS_LIST.\n#\n# CHECK_FUNC should accept the path to a candidate program, and\n# set $func_check_prog_result if it truncates its output less than\n# $_G_path_prog_max characters.\nfunc_path_progs ()\n{\n    _G_progs_list=$1\n    _G_check_func=$2\n    _G_PATH=${3-\"$PATH\"}\n\n    _G_path_prog_max=0\n    _G_path_prog_found=false\n    _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:}\n    for _G_dir in $_G_PATH; do\n      IFS=$_G_save_IFS\n      test -z \"$_G_dir\" && _G_dir=.\n      for _G_prog_name in $_G_progs_list; do\n        for _exeext in '' .EXE; do\n          _G_path_prog=$_G_dir/$_G_prog_name$_exeext\n          func_executable_p \"$_G_path_prog\" || continue\n          case `\"$_G_path_prog\" --version 2>&1` in\n            *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;;\n            *)     $_G_check_func $_G_path_prog\n\t\t   func_path_progs_result=$func_check_prog_result\n\t\t   ;;\n          esac\n          $_G_path_prog_found && break 3\n        done\n      done\n    done\n    IFS=$_G_save_IFS\n    test -z \"$func_path_progs_result\" && {\n      echo \"no acceptable sed could be found in \\$PATH\" >&2\n      exit 1\n    }\n}\n\n\n# We want to be able to use the functions in this file before configure\n# has figured out where the best binaries are kept, which means we have\n# to search for them ourselves - except when the results are already set\n# where we skip the searches.\n\n# Unless the user overrides by setting SED, search the path for either GNU\n# sed, or the sed that truncates its output the least.\ntest -z \"$SED\" && {\n  _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/\n  for _G_i in 1 2 3 4 5 6 7; do\n    _G_sed_script=$_G_sed_script$nl$_G_sed_script\n  done\n  echo \"$_G_sed_script\" 2>/dev/null | sed 99q >conftest.sed\n  _G_sed_script=\n\n  func_check_prog_sed ()\n  {\n    _G_path_prog=$1\n\n    _G_count=0\n    printf 0123456789 >conftest.in\n    while :\n    do\n      cat conftest.in conftest.in >conftest.tmp\n      mv conftest.tmp conftest.in\n      cp conftest.in conftest.nl\n      echo '' >> conftest.nl\n      \"$_G_path_prog\" -f conftest.sed <conftest.nl >conftest.out 2>/dev/null || break\n      diff conftest.out conftest.nl >/dev/null 2>&1 || break\n      _G_count=`expr $_G_count + 1`\n      if test \"$_G_count\" -gt \"$_G_path_prog_max\"; then\n        # Best one so far, save it but keep looking for a better one\n        func_check_prog_result=$_G_path_prog\n        _G_path_prog_max=$_G_count\n      fi\n      # 10*(2^10) chars as input seems more than enough\n      test 10 -lt \"$_G_count\" && break\n    done\n    rm -f conftest.in conftest.tmp conftest.nl conftest.out\n  }\n\n  func_path_progs \"sed gsed\" func_check_prog_sed $PATH:/usr/xpg4/bin\n  rm -f conftest.sed\n  SED=$func_path_progs_result\n}\n\n\n# Unless the user overrides by setting GREP, search the path for either GNU\n# grep, or the grep that truncates its output the least.\ntest -z \"$GREP\" && {\n  func_check_prog_grep ()\n  {\n    _G_path_prog=$1\n\n    _G_count=0\n    _G_path_prog_max=0\n    printf 0123456789 >conftest.in\n    while :\n    do\n      cat conftest.in conftest.in >conftest.tmp\n      mv conftest.tmp conftest.in\n      cp conftest.in conftest.nl\n      echo 'GREP' >> conftest.nl\n      \"$_G_path_prog\" -e 'GREP$' -e '-(cannot match)-' <conftest.nl >conftest.out 2>/dev/null || break\n      diff conftest.out conftest.nl >/dev/null 2>&1 || break\n      _G_count=`expr $_G_count + 1`\n      if test \"$_G_count\" -gt \"$_G_path_prog_max\"; then\n        # Best one so far, save it but keep looking for a better one\n        func_check_prog_result=$_G_path_prog\n        _G_path_prog_max=$_G_count\n      fi\n      # 10*(2^10) chars as input seems more than enough\n      test 10 -lt \"$_G_count\" && break\n    done\n    rm -f conftest.in conftest.tmp conftest.nl conftest.out\n  }\n\n  func_path_progs \"grep ggrep\" func_check_prog_grep $PATH:/usr/xpg4/bin\n  GREP=$func_path_progs_result\n}\n\n\n## ------------------------------- ##\n## User overridable command paths. ##\n## ------------------------------- ##\n\n# All uppercase variable names are used for environment variables.  These\n# variables can be overridden by the user before calling a script that\n# uses them if a suitable command of that name is not already available\n# in the command search PATH.\n\n: ${CP=\"cp -f\"}\n: ${ECHO=\"printf %s\\n\"}\n: ${EGREP=\"$GREP -E\"}\n: ${FGREP=\"$GREP -F\"}\n: ${LN_S=\"ln -s\"}\n: ${MAKE=\"make\"}\n: ${MKDIR=\"mkdir\"}\n: ${MV=\"mv -f\"}\n: ${RM=\"rm -f\"}\n: ${SHELL=\"${CONFIG_SHELL-/bin/sh}\"}\n\n\n## -------------------- ##\n## Useful sed snippets. ##\n## -------------------- ##\n\nsed_dirname='s|/[^/]*$||'\nsed_basename='s|^.*/||'\n\n# Sed substitution that helps us do robust quoting.  It backslashifies\n# metacharacters that are still active within double-quoted strings.\nsed_quote_subst='s|\\([`\"$\\\\]\\)|\\\\\\1|g'\n\n# Same as above, but do not quote variable references.\nsed_double_quote_subst='s/\\([\"`\\\\]\\)/\\\\\\1/g'\n\n# Sed substitution that turns a string into a regex matching for the\n# string literally.\nsed_make_literal_regex='s|[].[^$\\\\*\\/]|\\\\&|g'\n\n# Sed substitution that converts a w32 file name or path\n# that contains forward slashes, into one that contains\n# (escaped) backslashes.  A very naive implementation.\nsed_naive_backslashify='s|\\\\\\\\*|\\\\|g;s|/|\\\\|g;s|\\\\|\\\\\\\\|g'\n\n# Re-'\\' parameter expansions in output of sed_double_quote_subst that\n# were '\\'-ed in input to the same.  If an odd number of '\\' preceded a\n# '$' in input to sed_double_quote_subst, that '$' was protected from\n# expansion.  Since each input '\\' is now two '\\'s, look for any number\n# of runs of four '\\'s followed by two '\\'s and then a '$'.  '\\' that '$'.\n_G_bs='\\\\'\n_G_bs2='\\\\\\\\'\n_G_bs4='\\\\\\\\\\\\\\\\'\n_G_dollar='\\$'\nsed_double_backslash=\"\\\n  s/$_G_bs4/&\\\\\n/g\n  s/^$_G_bs2$_G_dollar/$_G_bs&/\n  s/\\\\([^$_G_bs]\\\\)$_G_bs2$_G_dollar/\\\\1$_G_bs2$_G_bs$_G_dollar/g\n  s/\\n//g\"\n\n\n## ----------------- ##\n## Global variables. ##\n## ----------------- ##\n\n# Except for the global variables explicitly listed below, the following\n# functions in the '^func_' namespace, and the '^require_' namespace\n# variables initialised in the 'Resource management' section, sourcing\n# this file will not pollute your global namespace with anything\n# else. There's no portable way to scope variables in Bourne shell\n# though, so actually running these functions will sometimes place\n# results into a variable named after the function, and often use\n# temporary variables in the '^_G_' namespace. If you are careful to\n# avoid using those namespaces casually in your sourcing script, things\n# should continue to work as you expect. And, of course, you can freely\n# overwrite any of the functions or variables defined here before\n# calling anything to customize them.\n\nEXIT_SUCCESS=0\nEXIT_FAILURE=1\nEXIT_MISMATCH=63  # $? = 63 is used to indicate version mismatch to missing.\nEXIT_SKIP=77\t  # $? = 77 is used to indicate a skipped test to automake.\n\n# Allow overriding, eg assuming that you follow the convention of\n# putting '$debug_cmd' at the start of all your functions, you can get\n# bash to show function call trace with:\n#\n#    debug_cmd='eval echo \"${FUNCNAME[0]} $*\" >&2' bash your-script-name\ndebug_cmd=${debug_cmd-\":\"}\nexit_cmd=:\n\n# By convention, finish your script with:\n#\n#    exit $exit_status\n#\n# so that you can set exit_status to non-zero if you want to indicate\n# something went wrong during execution without actually bailing out at\n# the point of failure.\nexit_status=$EXIT_SUCCESS\n\n# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh\n# is ksh but when the shell is invoked as \"sh\" and the current value of\n# the _XPG environment variable is not equal to 1 (one), the special\n# positional parameter $0, within a function call, is the name of the\n# function.\nprogpath=$0\n\n# The name of this program.\nprogname=`$ECHO \"$progpath\" |$SED \"$sed_basename\"`\n\n# Make sure we have an absolute progpath for reexecution:\ncase $progpath in\n  [\\\\/]*|[A-Za-z]:\\\\*) ;;\n  *[\\\\/]*)\n     progdir=`$ECHO \"$progpath\" |$SED \"$sed_dirname\"`\n     progdir=`cd \"$progdir\" && pwd`\n     progpath=$progdir/$progname\n     ;;\n  *)\n     _G_IFS=$IFS\n     IFS=${PATH_SEPARATOR-:}\n     for progdir in $PATH; do\n       IFS=$_G_IFS\n       test -x \"$progdir/$progname\" && break\n     done\n     IFS=$_G_IFS\n     test -n \"$progdir\" || progdir=`pwd`\n     progpath=$progdir/$progname\n     ;;\nesac\n\n\n## ----------------- ##\n## Standard options. ##\n## ----------------- ##\n\n# The following options affect the operation of the functions defined\n# below, and should be set appropriately depending on run-time para-\n# meters passed on the command line.\n\nopt_dry_run=false\nopt_quiet=false\nopt_verbose=false\n\n# Categories 'all' and 'none' are always available.  Append any others\n# you will pass as the first argument to func_warning from your own\n# code.\nwarning_categories=\n\n# By default, display warnings according to 'opt_warning_types'.  Set\n# 'warning_func'  to ':' to elide all warnings, or func_fatal_error to\n# treat the next displayed warning as a fatal error.\nwarning_func=func_warn_and_continue\n\n# Set to 'all' to display all warnings, 'none' to suppress all\n# warnings, or a space delimited list of some subset of\n# 'warning_categories' to display only the listed warnings.\nopt_warning_types=all\n\n\n## -------------------- ##\n## Resource management. ##\n## -------------------- ##\n\n# This section contains definitions for functions that each ensure a\n# particular resource (a file, or a non-empty configuration variable for\n# example) is available, and if appropriate to extract default values\n# from pertinent package files. Call them using their associated\n# 'require_*' variable to ensure that they are executed, at most, once.\n#\n# It's entirely deliberate that calling these functions can set\n# variables that don't obey the namespace limitations obeyed by the rest\n# of this file, in order that that they be as useful as possible to\n# callers.\n\n\n# require_term_colors\n# -------------------\n# Allow display of bold text on terminals that support it.\nrequire_term_colors=func_require_term_colors\nfunc_require_term_colors ()\n{\n    $debug_cmd\n\n    test -t 1 && {\n      # COLORTERM and USE_ANSI_COLORS environment variables take\n      # precedence, because most terminfo databases neglect to describe\n      # whether color sequences are supported.\n      test -n \"${COLORTERM+set}\" && : ${USE_ANSI_COLORS=\"1\"}\n\n      if test 1 = \"$USE_ANSI_COLORS\"; then\n        # Standard ANSI escape sequences\n        tc_reset='\u001b[0m'\n        tc_bold='\u001b[1m';   tc_standout='\u001b[7m'\n        tc_red='\u001b[31m';   tc_green='\u001b[32m'\n        tc_blue='\u001b[34m';  tc_cyan='\u001b[36m'\n      else\n        # Otherwise trust the terminfo database after all.\n        test -n \"`tput sgr0 2>/dev/null`\" && {\n          tc_reset=`tput sgr0`\n          test -n \"`tput bold 2>/dev/null`\" && tc_bold=`tput bold`\n          tc_standout=$tc_bold\n          test -n \"`tput smso 2>/dev/null`\" && tc_standout=`tput smso`\n          test -n \"`tput setaf 1 2>/dev/null`\" && tc_red=`tput setaf 1`\n          test -n \"`tput setaf 2 2>/dev/null`\" && tc_green=`tput setaf 2`\n          test -n \"`tput setaf 4 2>/dev/null`\" && tc_blue=`tput setaf 4`\n          test -n \"`tput setaf 5 2>/dev/null`\" && tc_cyan=`tput setaf 5`\n        }\n      fi\n    }\n\n    require_term_colors=:\n}\n\n\n## ----------------- ##\n## Function library. ##\n## ----------------- ##\n\n# This section contains a variety of useful functions to call in your\n# scripts. Take note of the portable wrappers for features provided by\n# some modern shells, which will fall back to slower equivalents on\n# less featureful shells.\n\n\n# func_append VAR VALUE\n# ---------------------\n# Append VALUE onto the existing contents of VAR.\n\n  # We should try to minimise forks, especially on Windows where they are\n  # unreasonably slow, so skip the feature probes when bash or zsh are\n  # being used:\n  if test set = \"${BASH_VERSION+set}${ZSH_VERSION+set}\"; then\n    : ${_G_HAVE_ARITH_OP=\"yes\"}\n    : ${_G_HAVE_XSI_OPS=\"yes\"}\n    # The += operator was introduced in bash 3.1\n    case $BASH_VERSION in\n      [12].* | 3.0 | 3.0*) ;;\n      *)\n        : ${_G_HAVE_PLUSEQ_OP=\"yes\"}\n        ;;\n    esac\n  fi\n\n  # _G_HAVE_PLUSEQ_OP\n  # Can be empty, in which case the shell is probed, \"yes\" if += is\n  # useable or anything else if it does not work.\n  test -z \"$_G_HAVE_PLUSEQ_OP\" \\\n    && (eval 'x=a; x+=\" b\"; test \"a b\" = \"$x\"') 2>/dev/null \\\n    && _G_HAVE_PLUSEQ_OP=yes\n\nif test yes = \"$_G_HAVE_PLUSEQ_OP\"\nthen\n  # This is an XSI compatible shell, allowing a faster implementation...\n  eval 'func_append ()\n  {\n    $debug_cmd\n\n    eval \"$1+=\\$2\"\n  }'\nelse\n  # ...otherwise fall back to using expr, which is often a shell builtin.\n  func_append ()\n  {\n    $debug_cmd\n\n    eval \"$1=\\$$1\\$2\"\n  }\nfi\n\n\n# func_append_quoted VAR VALUE\n# ----------------------------\n# Quote VALUE and append to the end of shell variable VAR, separated\n# by a space.\nif test yes = \"$_G_HAVE_PLUSEQ_OP\"; then\n  eval 'func_append_quoted ()\n  {\n    $debug_cmd\n\n    func_quote_for_eval \"$2\"\n    eval \"$1+=\\\\ \\$func_quote_for_eval_result\"\n  }'\nelse\n  func_append_quoted ()\n  {\n    $debug_cmd\n\n    func_quote_for_eval \"$2\"\n    eval \"$1=\\$$1\\\\ \\$func_quote_for_eval_result\"\n  }\nfi\n\n\n# func_append_uniq VAR VALUE\n# --------------------------\n# Append unique VALUE onto the existing contents of VAR, assuming\n# entries are delimited by the first character of VALUE.  For example:\n#\n#   func_append_uniq options \" --another-option option-argument\"\n#\n# will only append to $options if \" --another-option option-argument \"\n# is not already present somewhere in $options already (note spaces at\n# each end implied by leading space in second argument).\nfunc_append_uniq ()\n{\n    $debug_cmd\n\n    eval _G_current_value='`$ECHO $'$1'`'\n    _G_delim=`expr \"$2\" : '\\(.\\)'`\n\n    case $_G_delim$_G_current_value$_G_delim in\n      *\"$2$_G_delim\"*) ;;\n      *) func_append \"$@\" ;;\n    esac\n}\n\n\n# func_arith TERM...\n# ------------------\n# Set func_arith_result to the result of evaluating TERMs.\n  test -z \"$_G_HAVE_ARITH_OP\" \\\n    && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \\\n    && _G_HAVE_ARITH_OP=yes\n\nif test yes = \"$_G_HAVE_ARITH_OP\"; then\n  eval 'func_arith ()\n  {\n    $debug_cmd\n\n    func_arith_result=$(( $* ))\n  }'\nelse\n  func_arith ()\n  {\n    $debug_cmd\n\n    func_arith_result=`expr \"$@\"`\n  }\nfi\n\n\n# func_basename FILE\n# ------------------\n# Set func_basename_result to FILE with everything up to and including\n# the last / stripped.\nif test yes = \"$_G_HAVE_XSI_OPS\"; then\n  # If this shell supports suffix pattern removal, then use it to avoid\n  # forking. Hide the definitions single quotes in case the shell chokes\n  # on unsupported syntax...\n  _b='func_basename_result=${1##*/}'\n  _d='case $1 in\n        */*) func_dirname_result=${1%/*}$2 ;;\n        *  ) func_dirname_result=$3        ;;\n      esac'\n\nelse\n  # ...otherwise fall back to using sed.\n  _b='func_basename_result=`$ECHO \"$1\" |$SED \"$sed_basename\"`'\n  _d='func_dirname_result=`$ECHO \"$1\"  |$SED \"$sed_dirname\"`\n      if test \"X$func_dirname_result\" = \"X$1\"; then\n        func_dirname_result=$3\n      else\n        func_append func_dirname_result \"$2\"\n      fi'\nfi\n\neval 'func_basename ()\n{\n    $debug_cmd\n\n    '\"$_b\"'\n}'\n\n\n# func_dirname FILE APPEND NONDIR_REPLACEMENT\n# -------------------------------------------\n# Compute the dirname of FILE.  If nonempty, add APPEND to the result,\n# otherwise set result to NONDIR_REPLACEMENT.\neval 'func_dirname ()\n{\n    $debug_cmd\n\n    '\"$_d\"'\n}'\n\n\n# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT\n# --------------------------------------------------------\n# Perform func_basename and func_dirname in a single function\n# call:\n#   dirname:  Compute the dirname of FILE.  If nonempty,\n#             add APPEND to the result, otherwise set result\n#             to NONDIR_REPLACEMENT.\n#             value returned in \"$func_dirname_result\"\n#   basename: Compute filename of FILE.\n#             value retuned in \"$func_basename_result\"\n# For efficiency, we do not delegate to the functions above but instead\n# duplicate the functionality here.\neval 'func_dirname_and_basename ()\n{\n    $debug_cmd\n\n    '\"$_b\"'\n    '\"$_d\"'\n}'\n\n\n# func_echo ARG...\n# ----------------\n# Echo program name prefixed message.\nfunc_echo ()\n{\n    $debug_cmd\n\n    _G_message=$*\n\n    func_echo_IFS=$IFS\n    IFS=$nl\n    for _G_line in $_G_message; do\n      IFS=$func_echo_IFS\n      $ECHO \"$progname: $_G_line\"\n    done\n    IFS=$func_echo_IFS\n}\n\n\n# func_echo_all ARG...\n# --------------------\n# Invoke $ECHO with all args, space-separated.\nfunc_echo_all ()\n{\n    $ECHO \"$*\"\n}\n\n\n# func_echo_infix_1 INFIX ARG...\n# ------------------------------\n# Echo program name, followed by INFIX on the first line, with any\n# additional lines not showing INFIX.\nfunc_echo_infix_1 ()\n{\n    $debug_cmd\n\n    $require_term_colors\n\n    _G_infix=$1; shift\n    _G_indent=$_G_infix\n    _G_prefix=\"$progname: $_G_infix: \"\n    _G_message=$*\n\n    # Strip color escape sequences before counting printable length\n    for _G_tc in \"$tc_reset\" \"$tc_bold\" \"$tc_standout\" \"$tc_red\" \"$tc_green\" \"$tc_blue\" \"$tc_cyan\"\n    do\n      test -n \"$_G_tc\" && {\n        _G_esc_tc=`$ECHO \"$_G_tc\" | $SED \"$sed_make_literal_regex\"`\n        _G_indent=`$ECHO \"$_G_indent\" | $SED \"s|$_G_esc_tc||g\"`\n      }\n    done\n    _G_indent=\"$progname: \"`echo \"$_G_indent\" | $SED 's|.| |g'`\"  \" ## exclude from sc_prohibit_nested_quotes\n\n    func_echo_infix_1_IFS=$IFS\n    IFS=$nl\n    for _G_line in $_G_message; do\n      IFS=$func_echo_infix_1_IFS\n      $ECHO \"$_G_prefix$tc_bold$_G_line$tc_reset\" >&2\n      _G_prefix=$_G_indent\n    done\n    IFS=$func_echo_infix_1_IFS\n}\n\n\n# func_error ARG...\n# -----------------\n# Echo program name prefixed message to standard error.\nfunc_error ()\n{\n    $debug_cmd\n\n    $require_term_colors\n\n    func_echo_infix_1 \"  $tc_standout${tc_red}error$tc_reset\" \"$*\" >&2\n}\n\n\n# func_fatal_error ARG...\n# -----------------------\n# Echo program name prefixed message to standard error, and exit.\nfunc_fatal_error ()\n{\n    $debug_cmd\n\n    func_error \"$*\"\n    exit $EXIT_FAILURE\n}\n\n\n# func_grep EXPRESSION FILENAME\n# -----------------------------\n# Check whether EXPRESSION matches any line of FILENAME, without output.\nfunc_grep ()\n{\n    $debug_cmd\n\n    $GREP \"$1\" \"$2\" >/dev/null 2>&1\n}\n\n\n# func_len STRING\n# ---------------\n# Set func_len_result to the length of STRING. STRING may not\n# start with a hyphen.\n  test -z \"$_G_HAVE_XSI_OPS\" \\\n    && (eval 'x=a/b/c;\n      test 5aa/bb/cc = \"${#x}${x%%/*}${x%/*}${x#*/}${x##*/}\"') 2>/dev/null \\\n    && _G_HAVE_XSI_OPS=yes\n\nif test yes = \"$_G_HAVE_XSI_OPS\"; then\n  eval 'func_len ()\n  {\n    $debug_cmd\n\n    func_len_result=${#1}\n  }'\nelse\n  func_len ()\n  {\n    $debug_cmd\n\n    func_len_result=`expr \"$1\" : \".*\" 2>/dev/null || echo $max_cmd_len`\n  }\nfi\n\n\n# func_mkdir_p DIRECTORY-PATH\n# ---------------------------\n# Make sure the entire path to DIRECTORY-PATH is available.\nfunc_mkdir_p ()\n{\n    $debug_cmd\n\n    _G_directory_path=$1\n    _G_dir_list=\n\n    if test -n \"$_G_directory_path\" && test : != \"$opt_dry_run\"; then\n\n      # Protect directory names starting with '-'\n      case $_G_directory_path in\n        -*) _G_directory_path=./$_G_directory_path ;;\n      esac\n\n      # While some portion of DIR does not yet exist...\n      while test ! -d \"$_G_directory_path\"; do\n        # ...make a list in topmost first order.  Use a colon delimited\n\t# list incase some portion of path contains whitespace.\n        _G_dir_list=$_G_directory_path:$_G_dir_list\n\n        # If the last portion added has no slash in it, the list is done\n        case $_G_directory_path in */*) ;; *) break ;; esac\n\n        # ...otherwise throw away the child directory and loop\n        _G_directory_path=`$ECHO \"$_G_directory_path\" | $SED -e \"$sed_dirname\"`\n      done\n      _G_dir_list=`$ECHO \"$_G_dir_list\" | $SED 's|:*$||'`\n\n      func_mkdir_p_IFS=$IFS; IFS=:\n      for _G_dir in $_G_dir_list; do\n\tIFS=$func_mkdir_p_IFS\n        # mkdir can fail with a 'File exist' error if two processes\n        # try to create one of the directories concurrently.  Don't\n        # stop in that case!\n        $MKDIR \"$_G_dir\" 2>/dev/null || :\n      done\n      IFS=$func_mkdir_p_IFS\n\n      # Bail out if we (or some other process) failed to create a directory.\n      test -d \"$_G_directory_path\" || \\\n        func_fatal_error \"Failed to create '$1'\"\n    fi\n}\n\n\n# func_mktempdir [BASENAME]\n# -------------------------\n# Make a temporary directory that won't clash with other running\n# libtool processes, and avoids race conditions if possible.  If\n# given, BASENAME is the basename for that directory.\nfunc_mktempdir ()\n{\n    $debug_cmd\n\n    _G_template=${TMPDIR-/tmp}/${1-$progname}\n\n    if test : = \"$opt_dry_run\"; then\n      # Return a directory name, but don't create it in dry-run mode\n      _G_tmpdir=$_G_template-$$\n    else\n\n      # If mktemp works, use that first and foremost\n      _G_tmpdir=`mktemp -d \"$_G_template-XXXXXXXX\" 2>/dev/null`\n\n      if test ! -d \"$_G_tmpdir\"; then\n        # Failing that, at least try and use $RANDOM to avoid a race\n        _G_tmpdir=$_G_template-${RANDOM-0}$$\n\n        func_mktempdir_umask=`umask`\n        umask 0077\n        $MKDIR \"$_G_tmpdir\"\n        umask $func_mktempdir_umask\n      fi\n\n      # If we're not in dry-run mode, bomb out on failure\n      test -d \"$_G_tmpdir\" || \\\n        func_fatal_error \"cannot create temporary directory '$_G_tmpdir'\"\n    fi\n\n    $ECHO \"$_G_tmpdir\"\n}\n\n\n# func_normal_abspath PATH\n# ------------------------\n# Remove doubled-up and trailing slashes, \".\" path components,\n# and cancel out any \"..\" path components in PATH after making\n# it an absolute path.\nfunc_normal_abspath ()\n{\n    $debug_cmd\n\n    # These SED scripts presuppose an absolute path with a trailing slash.\n    _G_pathcar='s|^/\\([^/]*\\).*$|\\1|'\n    _G_pathcdr='s|^/[^/]*||'\n    _G_removedotparts=':dotsl\n\t\ts|/\\./|/|g\n\t\tt dotsl\n\t\ts|/\\.$|/|'\n    _G_collapseslashes='s|/\\{1,\\}|/|g'\n    _G_finalslash='s|/*$|/|'\n\n    # Start from root dir and reassemble the path.\n    func_normal_abspath_result=\n    func_normal_abspath_tpath=$1\n    func_normal_abspath_altnamespace=\n    case $func_normal_abspath_tpath in\n      \"\")\n        # Empty path, that just means $cwd.\n        func_stripname '' '/' \"`pwd`\"\n        func_normal_abspath_result=$func_stripname_result\n        return\n        ;;\n      # The next three entries are used to spot a run of precisely\n      # two leading slashes without using negated character classes;\n      # we take advantage of case's first-match behaviour.\n      ///*)\n        # Unusual form of absolute path, do nothing.\n        ;;\n      //*)\n        # Not necessarily an ordinary path; POSIX reserves leading '//'\n        # and for example Cygwin uses it to access remote file shares\n        # over CIFS/SMB, so we conserve a leading double slash if found.\n        func_normal_abspath_altnamespace=/\n        ;;\n      /*)\n        # Absolute path, do nothing.\n        ;;\n      *)\n        # Relative path, prepend $cwd.\n        func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath\n        ;;\n    esac\n\n    # Cancel out all the simple stuff to save iterations.  We also want\n    # the path to end with a slash for ease of parsing, so make sure\n    # there is one (and only one) here.\n    func_normal_abspath_tpath=`$ECHO \"$func_normal_abspath_tpath\" | $SED \\\n          -e \"$_G_removedotparts\" -e \"$_G_collapseslashes\" -e \"$_G_finalslash\"`\n    while :; do\n      # Processed it all yet?\n      if test / = \"$func_normal_abspath_tpath\"; then\n        # If we ascended to the root using \"..\" the result may be empty now.\n        if test -z \"$func_normal_abspath_result\"; then\n          func_normal_abspath_result=/\n        fi\n        break\n      fi\n      func_normal_abspath_tcomponent=`$ECHO \"$func_normal_abspath_tpath\" | $SED \\\n          -e \"$_G_pathcar\"`\n      func_normal_abspath_tpath=`$ECHO \"$func_normal_abspath_tpath\" | $SED \\\n          -e \"$_G_pathcdr\"`\n      # Figure out what to do with it\n      case $func_normal_abspath_tcomponent in\n        \"\")\n          # Trailing empty path component, ignore it.\n          ;;\n        ..)\n          # Parent dir; strip last assembled component from result.\n          func_dirname \"$func_normal_abspath_result\"\n          func_normal_abspath_result=$func_dirname_result\n          ;;\n        *)\n          # Actual path component, append it.\n          func_append func_normal_abspath_result \"/$func_normal_abspath_tcomponent\"\n          ;;\n      esac\n    done\n    # Restore leading double-slash if one was found on entry.\n    func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result\n}\n\n\n# func_notquiet ARG...\n# --------------------\n# Echo program name prefixed message only when not in quiet mode.\nfunc_notquiet ()\n{\n    $debug_cmd\n\n    $opt_quiet || func_echo ${1+\"$@\"}\n\n    # A bug in bash halts the script if the last line of a function\n    # fails when set -e is in force, so we need another command to\n    # work around that:\n    :\n}\n\n\n# func_relative_path SRCDIR DSTDIR\n# --------------------------------\n# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR.\nfunc_relative_path ()\n{\n    $debug_cmd\n\n    func_relative_path_result=\n    func_normal_abspath \"$1\"\n    func_relative_path_tlibdir=$func_normal_abspath_result\n    func_normal_abspath \"$2\"\n    func_relative_path_tbindir=$func_normal_abspath_result\n\n    # Ascend the tree starting from libdir\n    while :; do\n      # check if we have found a prefix of bindir\n      case $func_relative_path_tbindir in\n        $func_relative_path_tlibdir)\n          # found an exact match\n          func_relative_path_tcancelled=\n          break\n          ;;\n        $func_relative_path_tlibdir*)\n          # found a matching prefix\n          func_stripname \"$func_relative_path_tlibdir\" '' \"$func_relative_path_tbindir\"\n          func_relative_path_tcancelled=$func_stripname_result\n          if test -z \"$func_relative_path_result\"; then\n            func_relative_path_result=.\n          fi\n          break\n          ;;\n        *)\n          func_dirname $func_relative_path_tlibdir\n          func_relative_path_tlibdir=$func_dirname_result\n          if test -z \"$func_relative_path_tlibdir\"; then\n            # Have to descend all the way to the root!\n            func_relative_path_result=../$func_relative_path_result\n            func_relative_path_tcancelled=$func_relative_path_tbindir\n            break\n          fi\n          func_relative_path_result=../$func_relative_path_result\n          ;;\n      esac\n    done\n\n    # Now calculate path; take care to avoid doubling-up slashes.\n    func_stripname '' '/' \"$func_relative_path_result\"\n    func_relative_path_result=$func_stripname_result\n    func_stripname '/' '/' \"$func_relative_path_tcancelled\"\n    if test -n \"$func_stripname_result\"; then\n      func_append func_relative_path_result \"/$func_stripname_result\"\n    fi\n\n    # Normalisation. If bindir is libdir, return '.' else relative path.\n    if test -n \"$func_relative_path_result\"; then\n      func_stripname './' '' \"$func_relative_path_result\"\n      func_relative_path_result=$func_stripname_result\n    fi\n\n    test -n \"$func_relative_path_result\" || func_relative_path_result=.\n\n    :\n}\n\n\n# func_quote_for_eval ARG...\n# --------------------------\n# Aesthetically quote ARGs to be evaled later.\n# This function returns two values:\n#   i) func_quote_for_eval_result\n#      double-quoted, suitable for a subsequent eval\n#  ii) func_quote_for_eval_unquoted_result\n#      has all characters that are still active within double\n#      quotes backslashified.\nfunc_quote_for_eval ()\n{\n    $debug_cmd\n\n    func_quote_for_eval_unquoted_result=\n    func_quote_for_eval_result=\n    while test 0 -lt $#; do\n      case $1 in\n        *[\\\\\\`\\\"\\$]*)\n\t  _G_unquoted_arg=`printf '%s\\n' \"$1\" |$SED \"$sed_quote_subst\"` ;;\n        *)\n          _G_unquoted_arg=$1 ;;\n      esac\n      if test -n \"$func_quote_for_eval_unquoted_result\"; then\n\tfunc_append func_quote_for_eval_unquoted_result \" $_G_unquoted_arg\"\n      else\n        func_append func_quote_for_eval_unquoted_result \"$_G_unquoted_arg\"\n      fi\n\n      case $_G_unquoted_arg in\n        # Double-quote args containing shell metacharacters to delay\n        # word splitting, command substitution and variable expansion\n        # for a subsequent eval.\n        # Many Bourne shells cannot handle close brackets correctly\n        # in scan sets, so we specify it separately.\n        *[\\[\\~\\#\\^\\&\\*\\(\\)\\{\\}\\|\\;\\<\\>\\?\\'\\ \\\t]*|*]*|\"\")\n          _G_quoted_arg=\\\"$_G_unquoted_arg\\\"\n          ;;\n        *)\n          _G_quoted_arg=$_G_unquoted_arg\n\t  ;;\n      esac\n\n      if test -n \"$func_quote_for_eval_result\"; then\n\tfunc_append func_quote_for_eval_result \" $_G_quoted_arg\"\n      else\n        func_append func_quote_for_eval_result \"$_G_quoted_arg\"\n      fi\n      shift\n    done\n}\n\n\n# func_quote_for_expand ARG\n# -------------------------\n# Aesthetically quote ARG to be evaled later; same as above,\n# but do not quote variable references.\nfunc_quote_for_expand ()\n{\n    $debug_cmd\n\n    case $1 in\n      *[\\\\\\`\\\"]*)\n\t_G_arg=`$ECHO \"$1\" | $SED \\\n\t    -e \"$sed_double_quote_subst\" -e \"$sed_double_backslash\"` ;;\n      *)\n        _G_arg=$1 ;;\n    esac\n\n    case $_G_arg in\n      # Double-quote args containing shell metacharacters to delay\n      # word splitting and command substitution for a subsequent eval.\n      # Many Bourne shells cannot handle close brackets correctly\n      # in scan sets, so we specify it separately.\n      *[\\[\\~\\#\\^\\&\\*\\(\\)\\{\\}\\|\\;\\<\\>\\?\\'\\ \\\t]*|*]*|\"\")\n        _G_arg=\\\"$_G_arg\\\"\n        ;;\n    esac\n\n    func_quote_for_expand_result=$_G_arg\n}\n\n\n# func_stripname PREFIX SUFFIX NAME\n# ---------------------------------\n# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result.\n# PREFIX and SUFFIX must not contain globbing or regex special\n# characters, hashes, percent signs, but SUFFIX may contain a leading\n# dot (in which case that matches only a dot).\nif test yes = \"$_G_HAVE_XSI_OPS\"; then\n  eval 'func_stripname ()\n  {\n    $debug_cmd\n\n    # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\n    # positional parameters, so assign one to ordinary variable first.\n    func_stripname_result=$3\n    func_stripname_result=${func_stripname_result#\"$1\"}\n    func_stripname_result=${func_stripname_result%\"$2\"}\n  }'\nelse\n  func_stripname ()\n  {\n    $debug_cmd\n\n    case $2 in\n      .*) func_stripname_result=`$ECHO \"$3\" | $SED -e \"s%^$1%%\" -e \"s%\\\\\\\\$2\\$%%\"`;;\n      *)  func_stripname_result=`$ECHO \"$3\" | $SED -e \"s%^$1%%\" -e \"s%$2\\$%%\"`;;\n    esac\n  }\nfi\n\n\n# func_show_eval CMD [FAIL_EXP]\n# -----------------------------\n# Unless opt_quiet is true, then output CMD.  Then, if opt_dryrun is\n# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP\n# is given, then evaluate it.\nfunc_show_eval ()\n{\n    $debug_cmd\n\n    _G_cmd=$1\n    _G_fail_exp=${2-':'}\n\n    func_quote_for_expand \"$_G_cmd\"\n    eval \"func_notquiet $func_quote_for_expand_result\"\n\n    $opt_dry_run || {\n      eval \"$_G_cmd\"\n      _G_status=$?\n      if test 0 -ne \"$_G_status\"; then\n\teval \"(exit $_G_status); $_G_fail_exp\"\n      fi\n    }\n}\n\n\n# func_show_eval_locale CMD [FAIL_EXP]\n# ------------------------------------\n# Unless opt_quiet is true, then output CMD.  Then, if opt_dryrun is\n# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP\n# is given, then evaluate it.  Use the saved locale for evaluation.\nfunc_show_eval_locale ()\n{\n    $debug_cmd\n\n    _G_cmd=$1\n    _G_fail_exp=${2-':'}\n\n    $opt_quiet || {\n      func_quote_for_expand \"$_G_cmd\"\n      eval \"func_echo $func_quote_for_expand_result\"\n    }\n\n    $opt_dry_run || {\n      eval \"$_G_user_locale\n\t    $_G_cmd\"\n      _G_status=$?\n      eval \"$_G_safe_locale\"\n      if test 0 -ne \"$_G_status\"; then\n\teval \"(exit $_G_status); $_G_fail_exp\"\n      fi\n    }\n}\n\n\n# func_tr_sh\n# ----------\n# Turn $1 into a string suitable for a shell variable name.\n# Result is stored in $func_tr_sh_result.  All characters\n# not in the set a-zA-Z0-9_ are replaced with '_'. Further,\n# if $1 begins with a digit, a '_' is prepended as well.\nfunc_tr_sh ()\n{\n    $debug_cmd\n\n    case $1 in\n    [0-9]* | *[!a-zA-Z0-9_]*)\n      func_tr_sh_result=`$ECHO \"$1\" | $SED -e 's/^\\([0-9]\\)/_\\1/' -e 's/[^a-zA-Z0-9_]/_/g'`\n      ;;\n    * )\n      func_tr_sh_result=$1\n      ;;\n    esac\n}\n\n\n# func_verbose ARG...\n# -------------------\n# Echo program name prefixed message in verbose mode only.\nfunc_verbose ()\n{\n    $debug_cmd\n\n    $opt_verbose && func_echo \"$*\"\n\n    :\n}\n\n\n# func_warn_and_continue ARG...\n# -----------------------------\n# Echo program name prefixed warning message to standard error.\nfunc_warn_and_continue ()\n{\n    $debug_cmd\n\n    $require_term_colors\n\n    func_echo_infix_1 \"${tc_red}warning$tc_reset\" \"$*\" >&2\n}\n\n\n# func_warning CATEGORY ARG...\n# ----------------------------\n# Echo program name prefixed warning message to standard error. Warning\n# messages can be filtered according to CATEGORY, where this function\n# elides messages where CATEGORY is not listed in the global variable\n# 'opt_warning_types'.\nfunc_warning ()\n{\n    $debug_cmd\n\n    # CATEGORY must be in the warning_categories list!\n    case \" $warning_categories \" in\n      *\" $1 \"*) ;;\n      *) func_internal_error \"invalid warning category '$1'\" ;;\n    esac\n\n    _G_category=$1\n    shift\n\n    case \" $opt_warning_types \" in\n      *\" $_G_category \"*) $warning_func ${1+\"$@\"} ;;\n    esac\n}\n\n\n# func_sort_ver VER1 VER2\n# -----------------------\n# 'sort -V' is not generally available.\n# Note this deviates from the version comparison in automake\n# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a\n# but this should suffice as we won't be specifying old\n# version formats or redundant trailing .0 in bootstrap.conf.\n# If we did want full compatibility then we should probably\n# use m4_version_compare from autoconf.\nfunc_sort_ver ()\n{\n    $debug_cmd\n\n    printf '%s\\n%s\\n' \"$1\" \"$2\" \\\n      | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n\n}\n\n# func_lt_ver PREV CURR\n# ---------------------\n# Return true if PREV and CURR are in the correct order according to\n# func_sort_ver, otherwise false.  Use it like this:\n#\n#  func_lt_ver \"$prev_ver\" \"$proposed_ver\" || func_fatal_error \"...\"\nfunc_lt_ver ()\n{\n    $debug_cmd\n\n    test \"x$1\" = x`func_sort_ver \"$1\" \"$2\" | $SED 1q`\n}\n\n\n# Local variables:\n# mode: shell-script\n# sh-indentation: 2\n# eval: (add-hook 'before-save-hook 'time-stamp)\n# time-stamp-pattern: \"10/scriptversion=%:y-%02m-%02d.%02H; # UTC\"\n# time-stamp-time-zone: \"UTC\"\n# End:\n#! /bin/sh\n\n# Set a version string for this script.\nscriptversion=2014-01-07.03; # UTC\n\n# A portable, pluggable option parser for Bourne shell.\n# Written by Gary V. Vaughan, 2010\n\n# Copyright (C) 2010-2015 Free Software Foundation, Inc.\n# This is free software; see the source for copying conditions.  There is NO\n# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# Please report bugs or propose patches to gary@gnu.org.\n\n\n## ------ ##\n## Usage. ##\n## ------ ##\n\n# This file is a library for parsing options in your shell scripts along\n# with assorted other useful supporting features that you can make use\n# of too.\n#\n# For the simplest scripts you might need only:\n#\n#   #!/bin/sh\n#   . relative/path/to/funclib.sh\n#   . relative/path/to/options-parser\n#   scriptversion=1.0\n#   func_options ${1+\"$@\"}\n#   eval set dummy \"$func_options_result\"; shift\n#   ...rest of your script...\n#\n# In order for the '--version' option to work, you will need to have a\n# suitably formatted comment like the one at the top of this file\n# starting with '# Written by ' and ending with '# warranty; '.\n#\n# For '-h' and '--help' to work, you will also need a one line\n# description of your script's purpose in a comment directly above the\n# '# Written by ' line, like the one at the top of this file.\n#\n# The default options also support '--debug', which will turn on shell\n# execution tracing (see the comment above debug_cmd below for another\n# use), and '--verbose' and the func_verbose function to allow your script\n# to display verbose messages only when your user has specified\n# '--verbose'.\n#\n# After sourcing this file, you can plug processing for additional\n# options by amending the variables from the 'Configuration' section\n# below, and following the instructions in the 'Option parsing'\n# section further down.\n\n## -------------- ##\n## Configuration. ##\n## -------------- ##\n\n# You should override these variables in your script after sourcing this\n# file so that they reflect the customisations you have added to the\n# option parser.\n\n# The usage line for option parsing errors and the start of '-h' and\n# '--help' output messages. You can embed shell variables for delayed\n# expansion at the time the message is displayed, but you will need to\n# quote other shell meta-characters carefully to prevent them being\n# expanded when the contents are evaled.\nusage='$progpath [OPTION]...'\n\n# Short help message in response to '-h' and '--help'.  Add to this or\n# override it after sourcing this library to reflect the full set of\n# options your script accepts.\nusage_message=\"\\\n       --debug        enable verbose shell tracing\n   -W, --warnings=CATEGORY\n                      report the warnings falling in CATEGORY [all]\n   -v, --verbose      verbosely report processing\n       --version      print version information and exit\n   -h, --help         print short or long help message and exit\n\"\n\n# Additional text appended to 'usage_message' in response to '--help'.\nlong_help_message=\"\nWarning categories include:\n       'all'          show all warnings\n       'none'         turn off all the warnings\n       'error'        warnings are treated as fatal errors\"\n\n# Help message printed before fatal option parsing errors.\nfatal_help=\"Try '\\$progname --help' for more information.\"\n\n\n\n## ------------------------- ##\n## Hook function management. ##\n## ------------------------- ##\n\n# This section contains functions for adding, removing, and running hooks\n# to the main code.  A hook is just a named list of of function, that can\n# be run in order later on.\n\n# func_hookable FUNC_NAME\n# -----------------------\n# Declare that FUNC_NAME will run hooks added with\n# 'func_add_hook FUNC_NAME ...'.\nfunc_hookable ()\n{\n    $debug_cmd\n\n    func_append hookable_fns \" $1\"\n}\n\n\n# func_add_hook FUNC_NAME HOOK_FUNC\n# ---------------------------------\n# Request that FUNC_NAME call HOOK_FUNC before it returns.  FUNC_NAME must\n# first have been declared \"hookable\" by a call to 'func_hookable'.\nfunc_add_hook ()\n{\n    $debug_cmd\n\n    case \" $hookable_fns \" in\n      *\" $1 \"*) ;;\n      *) func_fatal_error \"'$1' does not accept hook functions.\" ;;\n    esac\n\n    eval func_append ${1}_hooks '\" $2\"'\n}\n\n\n# func_remove_hook FUNC_NAME HOOK_FUNC\n# ------------------------------------\n# Remove HOOK_FUNC from the list of functions called by FUNC_NAME.\nfunc_remove_hook ()\n{\n    $debug_cmd\n\n    eval ${1}_hooks='`$ECHO \"\\$'$1'_hooks\" |$SED \"s| '$2'||\"`'\n}\n\n\n# func_run_hooks FUNC_NAME [ARG]...\n# ---------------------------------\n# Run all hook functions registered to FUNC_NAME.\n# It is assumed that the list of hook functions contains nothing more\n# than a whitespace-delimited list of legal shell function names, and\n# no effort is wasted trying to catch shell meta-characters or preserve\n# whitespace.\nfunc_run_hooks ()\n{\n    $debug_cmd\n\n    case \" $hookable_fns \" in\n      *\" $1 \"*) ;;\n      *) func_fatal_error \"'$1' does not support hook funcions.n\" ;;\n    esac\n\n    eval _G_hook_fns=\\$$1_hooks; shift\n\n    for _G_hook in $_G_hook_fns; do\n      eval $_G_hook '\"$@\"'\n\n      # store returned options list back into positional\n      # parameters for next 'cmd' execution.\n      eval _G_hook_result=\\$${_G_hook}_result\n      eval set dummy \"$_G_hook_result\"; shift\n    done\n\n    func_quote_for_eval ${1+\"$@\"}\n    func_run_hooks_result=$func_quote_for_eval_result\n}\n\n\n\n## --------------- ##\n## Option parsing. ##\n## --------------- ##\n\n# In order to add your own option parsing hooks, you must accept the\n# full positional parameter list in your hook function, remove any\n# options that you action, and then pass back the remaining unprocessed\n# options in '<hooked_function_name>_result', escaped suitably for\n# 'eval'.  Like this:\n#\n#    my_options_prep ()\n#    {\n#        $debug_cmd\n#\n#        # Extend the existing usage message.\n#        usage_message=$usage_message'\n#      -s, --silent       don'\\''t print informational messages\n#    '\n#\n#        func_quote_for_eval ${1+\"$@\"}\n#        my_options_prep_result=$func_quote_for_eval_result\n#    }\n#    func_add_hook func_options_prep my_options_prep\n#\n#\n#    my_silent_option ()\n#    {\n#        $debug_cmd\n#\n#        # Note that for efficiency, we parse as many options as we can\n#        # recognise in a loop before passing the remainder back to the\n#        # caller on the first unrecognised argument we encounter.\n#        while test $# -gt 0; do\n#          opt=$1; shift\n#          case $opt in\n#            --silent|-s) opt_silent=: ;;\n#            # Separate non-argument short options:\n#            -s*)         func_split_short_opt \"$_G_opt\"\n#                         set dummy \"$func_split_short_opt_name\" \\\n#                             \"-$func_split_short_opt_arg\" ${1+\"$@\"}\n#                         shift\n#                         ;;\n#            *)            set dummy \"$_G_opt\" \"$*\"; shift; break ;;\n#          esac\n#        done\n#\n#        func_quote_for_eval ${1+\"$@\"}\n#        my_silent_option_result=$func_quote_for_eval_result\n#    }\n#    func_add_hook func_parse_options my_silent_option\n#\n#\n#    my_option_validation ()\n#    {\n#        $debug_cmd\n#\n#        $opt_silent && $opt_verbose && func_fatal_help \"\\\n#    '--silent' and '--verbose' options are mutually exclusive.\"\n#\n#        func_quote_for_eval ${1+\"$@\"}\n#        my_option_validation_result=$func_quote_for_eval_result\n#    }\n#    func_add_hook func_validate_options my_option_validation\n#\n# You'll alse need to manually amend $usage_message to reflect the extra\n# options you parse.  It's preferable to append if you can, so that\n# multiple option parsing hooks can be added safely.\n\n\n# func_options [ARG]...\n# ---------------------\n# All the functions called inside func_options are hookable. See the\n# individual implementations for details.\nfunc_hookable func_options\nfunc_options ()\n{\n    $debug_cmd\n\n    func_options_prep ${1+\"$@\"}\n    eval func_parse_options \\\n        ${func_options_prep_result+\"$func_options_prep_result\"}\n    eval func_validate_options \\\n        ${func_parse_options_result+\"$func_parse_options_result\"}\n\n    eval func_run_hooks func_options \\\n        ${func_validate_options_result+\"$func_validate_options_result\"}\n\n    # save modified positional parameters for caller\n    func_options_result=$func_run_hooks_result\n}\n\n\n# func_options_prep [ARG]...\n# --------------------------\n# All initialisations required before starting the option parse loop.\n# Note that when calling hook functions, we pass through the list of\n# positional parameters.  If a hook function modifies that list, and\n# needs to propogate that back to rest of this script, then the complete\n# modified list must be put in 'func_run_hooks_result' before\n# returning.\nfunc_hookable func_options_prep\nfunc_options_prep ()\n{\n    $debug_cmd\n\n    # Option defaults:\n    opt_verbose=false\n    opt_warning_types=\n\n    func_run_hooks func_options_prep ${1+\"$@\"}\n\n    # save modified positional parameters for caller\n    func_options_prep_result=$func_run_hooks_result\n}\n\n\n# func_parse_options [ARG]...\n# ---------------------------\n# The main option parsing loop.\nfunc_hookable func_parse_options\nfunc_parse_options ()\n{\n    $debug_cmd\n\n    func_parse_options_result=\n\n    # this just eases exit handling\n    while test $# -gt 0; do\n      # Defer to hook functions for initial option parsing, so they\n      # get priority in the event of reusing an option name.\n      func_run_hooks func_parse_options ${1+\"$@\"}\n\n      # Adjust func_parse_options positional parameters to match\n      eval set dummy \"$func_run_hooks_result\"; shift\n\n      # Break out of the loop if we already parsed every option.\n      test $# -gt 0 || break\n\n      _G_opt=$1\n      shift\n      case $_G_opt in\n        --debug|-x)   debug_cmd='set -x'\n                      func_echo \"enabling shell trace mode\"\n                      $debug_cmd\n                      ;;\n\n        --no-warnings|--no-warning|--no-warn)\n                      set dummy --warnings none ${1+\"$@\"}\n                      shift\n\t\t      ;;\n\n        --warnings|--warning|-W)\n                      test $# = 0 && func_missing_arg $_G_opt && break\n                      case \" $warning_categories $1\" in\n                        *\" $1 \"*)\n                          # trailing space prevents matching last $1 above\n                          func_append_uniq opt_warning_types \" $1\"\n                          ;;\n                        *all)\n                          opt_warning_types=$warning_categories\n                          ;;\n                        *none)\n                          opt_warning_types=none\n                          warning_func=:\n                          ;;\n                        *error)\n                          opt_warning_types=$warning_categories\n                          warning_func=func_fatal_error\n                          ;;\n                        *)\n                          func_fatal_error \\\n                             \"unsupported warning category: '$1'\"\n                          ;;\n                      esac\n                      shift\n                      ;;\n\n        --verbose|-v) opt_verbose=: ;;\n        --version)    func_version ;;\n        -\\?|-h)       func_usage ;;\n        --help)       func_help ;;\n\n\t# Separate optargs to long options (plugins may need this):\n\t--*=*)        func_split_equals \"$_G_opt\"\n\t              set dummy \"$func_split_equals_lhs\" \\\n                          \"$func_split_equals_rhs\" ${1+\"$@\"}\n                      shift\n                      ;;\n\n       # Separate optargs to short options:\n        -W*)\n                      func_split_short_opt \"$_G_opt\"\n                      set dummy \"$func_split_short_opt_name\" \\\n                          \"$func_split_short_opt_arg\" ${1+\"$@\"}\n                      shift\n                      ;;\n\n        # Separate non-argument short options:\n        -\\?*|-h*|-v*|-x*)\n                      func_split_short_opt \"$_G_opt\"\n                      set dummy \"$func_split_short_opt_name\" \\\n                          \"-$func_split_short_opt_arg\" ${1+\"$@\"}\n                      shift\n                      ;;\n\n        --)           break ;;\n        -*)           func_fatal_help \"unrecognised option: '$_G_opt'\" ;;\n        *)            set dummy \"$_G_opt\" ${1+\"$@\"}; shift; break ;;\n      esac\n    done\n\n    # save modified positional parameters for caller\n    func_quote_for_eval ${1+\"$@\"}\n    func_parse_options_result=$func_quote_for_eval_result\n}\n\n\n# func_validate_options [ARG]...\n# ------------------------------\n# Perform any sanity checks on option settings and/or unconsumed\n# arguments.\nfunc_hookable func_validate_options\nfunc_validate_options ()\n{\n    $debug_cmd\n\n    # Display all warnings if -W was not given.\n    test -n \"$opt_warning_types\" || opt_warning_types=\" $warning_categories\"\n\n    func_run_hooks func_validate_options ${1+\"$@\"}\n\n    # Bail if the options were screwed!\n    $exit_cmd $EXIT_FAILURE\n\n    # save modified positional parameters for caller\n    func_validate_options_result=$func_run_hooks_result\n}\n\n\n\n## ----------------- ##\n## Helper functions. ##\n## ----------------- ##\n\n# This section contains the helper functions used by the rest of the\n# hookable option parser framework in ascii-betical order.\n\n\n# func_fatal_help ARG...\n# ----------------------\n# Echo program name prefixed message to standard error, followed by\n# a help hint, and exit.\nfunc_fatal_help ()\n{\n    $debug_cmd\n\n    eval \\$ECHO \\\"\"Usage: $usage\"\\\"\n    eval \\$ECHO \\\"\"$fatal_help\"\\\"\n    func_error ${1+\"$@\"}\n    exit $EXIT_FAILURE\n}\n\n\n# func_help\n# ---------\n# Echo long help message to standard output and exit.\nfunc_help ()\n{\n    $debug_cmd\n\n    func_usage_message\n    $ECHO \"$long_help_message\"\n    exit 0\n}\n\n\n# func_missing_arg ARGNAME\n# ------------------------\n# Echo program name prefixed message to standard error and set global\n# exit_cmd.\nfunc_missing_arg ()\n{\n    $debug_cmd\n\n    func_error \"Missing argument for '$1'.\"\n    exit_cmd=exit\n}\n\n\n# func_split_equals STRING\n# ------------------------\n# Set func_split_equals_lhs and func_split_equals_rhs shell variables after\n# splitting STRING at the '=' sign.\ntest -z \"$_G_HAVE_XSI_OPS\" \\\n    && (eval 'x=a/b/c;\n      test 5aa/bb/cc = \"${#x}${x%%/*}${x%/*}${x#*/}${x##*/}\"') 2>/dev/null \\\n    && _G_HAVE_XSI_OPS=yes\n\nif test yes = \"$_G_HAVE_XSI_OPS\"\nthen\n  # This is an XSI compatible shell, allowing a faster implementation...\n  eval 'func_split_equals ()\n  {\n      $debug_cmd\n\n      func_split_equals_lhs=${1%%=*}\n      func_split_equals_rhs=${1#*=}\n      test \"x$func_split_equals_lhs\" = \"x$1\" \\\n        && func_split_equals_rhs=\n  }'\nelse\n  # ...otherwise fall back to using expr, which is often a shell builtin.\n  func_split_equals ()\n  {\n      $debug_cmd\n\n      func_split_equals_lhs=`expr \"x$1\" : 'x\\([^=]*\\)'`\n      func_split_equals_rhs=\n      test \"x$func_split_equals_lhs\" = \"x$1\" \\\n        || func_split_equals_rhs=`expr \"x$1\" : 'x[^=]*=\\(.*\\)$'`\n  }\nfi #func_split_equals\n\n\n# func_split_short_opt SHORTOPT\n# -----------------------------\n# Set func_split_short_opt_name and func_split_short_opt_arg shell\n# variables after splitting SHORTOPT after the 2nd character.\nif test yes = \"$_G_HAVE_XSI_OPS\"\nthen\n  # This is an XSI compatible shell, allowing a faster implementation...\n  eval 'func_split_short_opt ()\n  {\n      $debug_cmd\n\n      func_split_short_opt_arg=${1#??}\n      func_split_short_opt_name=${1%\"$func_split_short_opt_arg\"}\n  }'\nelse\n  # ...otherwise fall back to using expr, which is often a shell builtin.\n  func_split_short_opt ()\n  {\n      $debug_cmd\n\n      func_split_short_opt_name=`expr \"x$1\" : 'x-\\(.\\)'`\n      func_split_short_opt_arg=`expr \"x$1\" : 'x-.\\(.*\\)$'`\n  }\nfi #func_split_short_opt\n\n\n# func_usage\n# ----------\n# Echo short help message to standard output and exit.\nfunc_usage ()\n{\n    $debug_cmd\n\n    func_usage_message\n    $ECHO \"Run '$progname --help |${PAGER-more}' for full usage\"\n    exit 0\n}\n\n\n# func_usage_message\n# ------------------\n# Echo short help message to standard output.\nfunc_usage_message ()\n{\n    $debug_cmd\n\n    eval \\$ECHO \\\"\"Usage: $usage\"\\\"\n    echo\n    $SED -n 's|^# ||\n        /^Written by/{\n          x;p;x\n        }\n\th\n\t/^Written by/q' < \"$progpath\"\n    echo\n    eval \\$ECHO \\\"\"$usage_message\"\\\"\n}\n\n\n# func_version\n# ------------\n# Echo version message to standard output and exit.\nfunc_version ()\n{\n    $debug_cmd\n\n    printf '%s\\n' \"$progname $scriptversion\"\n    $SED -n '\n        /(C)/!b go\n        :more\n        /\\./!{\n          N\n          s|\\n# | |\n          b more\n        }\n        :go\n        /^# Written by /,/# warranty; / {\n          s|^# ||\n          s|^# *$||\n          s|\\((C)\\)[ 0-9,-]*[ ,-]\\([1-9][0-9]* \\)|\\1 \\2|\n          p\n        }\n        /^# Written by / {\n          s|^# ||\n          p\n        }\n        /^warranty; /q' < \"$progpath\"\n\n    exit $?\n}\n\n\n# Local variables:\n# mode: shell-script\n# sh-indentation: 2\n# eval: (add-hook 'before-save-hook 'time-stamp)\n# time-stamp-pattern: \"10/scriptversion=%:y-%02m-%02d.%02H; # UTC\"\n# time-stamp-time-zone: \"UTC\"\n# End:\n\n# Set a version string.\nscriptversion='(GNU libtool) 2.4.6'\n\n\n# func_echo ARG...\n# ----------------\n# Libtool also displays the current mode in messages, so override\n# funclib.sh func_echo with this custom definition.\nfunc_echo ()\n{\n    $debug_cmd\n\n    _G_message=$*\n\n    func_echo_IFS=$IFS\n    IFS=$nl\n    for _G_line in $_G_message; do\n      IFS=$func_echo_IFS\n      $ECHO \"$progname${opt_mode+: $opt_mode}: $_G_line\"\n    done\n    IFS=$func_echo_IFS\n}\n\n\n# func_warning ARG...\n# -------------------\n# Libtool warnings are not categorized, so override funclib.sh\n# func_warning with this simpler definition.\nfunc_warning ()\n{\n    $debug_cmd\n\n    $warning_func ${1+\"$@\"}\n}\n\n\n## ---------------- ##\n## Options parsing. ##\n## ---------------- ##\n\n# Hook in the functions to make sure our own options are parsed during\n# the option parsing loop.\n\nusage='$progpath [OPTION]... [MODE-ARG]...'\n\n# Short help message in response to '-h'.\nusage_message=\"Options:\n       --config             show all configuration variables\n       --debug              enable verbose shell tracing\n   -n, --dry-run            display commands without modifying any files\n       --features           display basic configuration information and exit\n       --mode=MODE          use operation mode MODE\n       --no-warnings        equivalent to '-Wnone'\n       --preserve-dup-deps  don't remove duplicate dependency libraries\n       --quiet, --silent    don't print informational messages\n       --tag=TAG            use configuration variables from tag TAG\n   -v, --verbose            print more informational messages than default\n       --version            print version information\n   -W, --warnings=CATEGORY  report the warnings falling in CATEGORY [all]\n   -h, --help, --help-all   print short, long, or detailed help message\n\"\n\n# Additional text appended to 'usage_message' in response to '--help'.\nfunc_help ()\n{\n    $debug_cmd\n\n    func_usage_message\n    $ECHO \"$long_help_message\n\nMODE must be one of the following:\n\n       clean           remove files from the build directory\n       compile         compile a source file into a libtool object\n       execute         automatically set library path, then run a program\n       finish          complete the installation of libtool libraries\n       install         install libraries or executables\n       link            create a library or an executable\n       uninstall       remove libraries from an installed directory\n\nMODE-ARGS vary depending on the MODE.  When passed as first option,\n'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that.\nTry '$progname --help --mode=MODE' for a more detailed description of MODE.\n\nWhen reporting a bug, please describe a test case to reproduce it and\ninclude the following information:\n\n       host-triplet:   $host\n       shell:          $SHELL\n       compiler:       $LTCC\n       compiler flags: $LTCFLAGS\n       linker:         $LD (gnu? $with_gnu_ld)\n       version:        $progname $scriptversion Debian-2.4.6-2\n       automake:       `($AUTOMAKE --version) 2>/dev/null |$SED 1q`\n       autoconf:       `($AUTOCONF --version) 2>/dev/null |$SED 1q`\n\nReport bugs to <bug-libtool@gnu.org>.\nGNU libtool home page: <http://www.gnu.org/s/libtool/>.\nGeneral help using GNU software: <http://www.gnu.org/gethelp/>.\"\n    exit 0\n}\n\n\n# func_lo2o OBJECT-NAME\n# ---------------------\n# Transform OBJECT-NAME from a '.lo' suffix to the platform specific\n# object suffix.\n\nlo2o=s/\\\\.lo\\$/.$objext/\no2lo=s/\\\\.$objext\\$/.lo/\n\nif test yes = \"$_G_HAVE_XSI_OPS\"; then\n  eval 'func_lo2o ()\n  {\n    case $1 in\n      *.lo) func_lo2o_result=${1%.lo}.$objext ;;\n      *   ) func_lo2o_result=$1               ;;\n    esac\n  }'\n\n  # func_xform LIBOBJ-OR-SOURCE\n  # ---------------------------\n  # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise)\n  # suffix to a '.lo' libtool-object suffix.\n  eval 'func_xform ()\n  {\n    func_xform_result=${1%.*}.lo\n  }'\nelse\n  # ...otherwise fall back to using sed.\n  func_lo2o ()\n  {\n    func_lo2o_result=`$ECHO \"$1\" | $SED \"$lo2o\"`\n  }\n\n  func_xform ()\n  {\n    func_xform_result=`$ECHO \"$1\" | $SED 's|\\.[^.]*$|.lo|'`\n  }\nfi\n\n\n# func_fatal_configuration ARG...\n# -------------------------------\n# Echo program name prefixed message to standard error, followed by\n# a configuration failure hint, and exit.\nfunc_fatal_configuration ()\n{\n    func__fatal_error ${1+\"$@\"} \\\n      \"See the $PACKAGE documentation for more information.\" \\\n      \"Fatal configuration error.\"\n}\n\n\n# func_config\n# -----------\n# Display the configuration for all the tags in this script.\nfunc_config ()\n{\n    re_begincf='^# ### BEGIN LIBTOOL'\n    re_endcf='^# ### END LIBTOOL'\n\n    # Default configuration.\n    $SED \"1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\\$d\" < \"$progpath\"\n\n    # Now print the configurations for the tags.\n    for tagname in $taglist; do\n      $SED -n \"/$re_begincf TAG CONFIG: $tagname\\$/,/$re_endcf TAG CONFIG: $tagname\\$/p\" < \"$progpath\"\n    done\n\n    exit $?\n}\n\n\n# func_features\n# -------------\n# Display the features supported by this script.\nfunc_features ()\n{\n    echo \"host: $host\"\n    if test yes = \"$build_libtool_libs\"; then\n      echo \"enable shared libraries\"\n    else\n      echo \"disable shared libraries\"\n    fi\n    if test yes = \"$build_old_libs\"; then\n      echo \"enable static libraries\"\n    else\n      echo \"disable static libraries\"\n    fi\n\n    exit $?\n}\n\n\n# func_enable_tag TAGNAME\n# -----------------------\n# Verify that TAGNAME is valid, and either flag an error and exit, or\n# enable the TAGNAME tag.  We also add TAGNAME to the global $taglist\n# variable here.\nfunc_enable_tag ()\n{\n    # Global variable:\n    tagname=$1\n\n    re_begincf=\"^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\\$\"\n    re_endcf=\"^# ### END LIBTOOL TAG CONFIG: $tagname\\$\"\n    sed_extractcf=/$re_begincf/,/$re_endcf/p\n\n    # Validate tagname.\n    case $tagname in\n      *[!-_A-Za-z0-9,/]*)\n        func_fatal_error \"invalid tag name: $tagname\"\n        ;;\n    esac\n\n    # Don't test for the \"default\" C tag, as we know it's\n    # there but not specially marked.\n    case $tagname in\n        CC) ;;\n    *)\n        if $GREP \"$re_begincf\" \"$progpath\" >/dev/null 2>&1; then\n\t  taglist=\"$taglist $tagname\"\n\n\t  # Evaluate the configuration.  Be careful to quote the path\n\t  # and the sed script, to avoid splitting on whitespace, but\n\t  # also don't use non-portable quotes within backquotes within\n\t  # quotes we have to do it in 2 steps:\n\t  extractedcf=`$SED -n -e \"$sed_extractcf\" < \"$progpath\"`\n\t  eval \"$extractedcf\"\n        else\n\t  func_error \"ignoring unknown tag $tagname\"\n        fi\n        ;;\n    esac\n}\n\n\n# func_check_version_match\n# ------------------------\n# Ensure that we are using m4 macros, and libtool script from the same\n# release of libtool.\nfunc_check_version_match ()\n{\n    if test \"$package_revision\" != \"$macro_revision\"; then\n      if test \"$VERSION\" != \"$macro_version\"; then\n        if test -z \"$macro_version\"; then\n          cat >&2 <<_LT_EOF\n$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the\n$progname: definition of this LT_INIT comes from an older release.\n$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION\n$progname: and run autoconf again.\n_LT_EOF\n        else\n          cat >&2 <<_LT_EOF\n$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the\n$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.\n$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION\n$progname: and run autoconf again.\n_LT_EOF\n        fi\n      else\n        cat >&2 <<_LT_EOF\n$progname: Version mismatch error.  This is $PACKAGE $VERSION, revision $package_revision,\n$progname: but the definition of this LT_INIT comes from revision $macro_revision.\n$progname: You should recreate aclocal.m4 with macros from revision $package_revision\n$progname: of $PACKAGE $VERSION and run autoconf again.\n_LT_EOF\n      fi\n\n      exit $EXIT_MISMATCH\n    fi\n}\n\n\n# libtool_options_prep [ARG]...\n# -----------------------------\n# Preparation for options parsed by libtool.\nlibtool_options_prep ()\n{\n    $debug_mode\n\n    # Option defaults:\n    opt_config=false\n    opt_dlopen=\n    opt_dry_run=false\n    opt_help=false\n    opt_mode=\n    opt_preserve_dup_deps=false\n    opt_quiet=false\n\n    nonopt=\n    preserve_args=\n\n    # Shorthand for --mode=foo, only valid as the first argument\n    case $1 in\n    clean|clea|cle|cl)\n      shift; set dummy --mode clean ${1+\"$@\"}; shift\n      ;;\n    compile|compil|compi|comp|com|co|c)\n      shift; set dummy --mode compile ${1+\"$@\"}; shift\n      ;;\n    execute|execut|execu|exec|exe|ex|e)\n      shift; set dummy --mode execute ${1+\"$@\"}; shift\n      ;;\n    finish|finis|fini|fin|fi|f)\n      shift; set dummy --mode finish ${1+\"$@\"}; shift\n      ;;\n    install|instal|insta|inst|ins|in|i)\n      shift; set dummy --mode install ${1+\"$@\"}; shift\n      ;;\n    link|lin|li|l)\n      shift; set dummy --mode link ${1+\"$@\"}; shift\n      ;;\n    uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)\n      shift; set dummy --mode uninstall ${1+\"$@\"}; shift\n      ;;\n    esac\n\n    # Pass back the list of options.\n    func_quote_for_eval ${1+\"$@\"}\n    libtool_options_prep_result=$func_quote_for_eval_result\n}\nfunc_add_hook func_options_prep libtool_options_prep\n\n\n# libtool_parse_options [ARG]...\n# ---------------------------------\n# Provide handling for libtool specific options.\nlibtool_parse_options ()\n{\n    $debug_cmd\n\n    # Perform our own loop to consume as many options as possible in\n    # each iteration.\n    while test $# -gt 0; do\n      _G_opt=$1\n      shift\n      case $_G_opt in\n        --dry-run|--dryrun|-n)\n                        opt_dry_run=:\n                        ;;\n\n        --config)       func_config ;;\n\n        --dlopen|-dlopen)\n                        opt_dlopen=\"${opt_dlopen+$opt_dlopen\n}$1\"\n                        shift\n                        ;;\n\n        --preserve-dup-deps)\n                        opt_preserve_dup_deps=: ;;\n\n        --features)     func_features ;;\n\n        --finish)       set dummy --mode finish ${1+\"$@\"}; shift ;;\n\n        --help)         opt_help=: ;;\n\n        --help-all)     opt_help=': help-all' ;;\n\n        --mode)         test $# = 0 && func_missing_arg $_G_opt && break\n                        opt_mode=$1\n                        case $1 in\n                          # Valid mode arguments:\n                          clean|compile|execute|finish|install|link|relink|uninstall) ;;\n\n                          # Catch anything else as an error\n                          *) func_error \"invalid argument for $_G_opt\"\n                             exit_cmd=exit\n                             break\n                             ;;\n                        esac\n                        shift\n                        ;;\n\n        --no-silent|--no-quiet)\n                        opt_quiet=false\n                        func_append preserve_args \" $_G_opt\"\n                        ;;\n\n        --no-warnings|--no-warning|--no-warn)\n                        opt_warning=false\n                        func_append preserve_args \" $_G_opt\"\n                        ;;\n\n        --no-verbose)\n                        opt_verbose=false\n                        func_append preserve_args \" $_G_opt\"\n                        ;;\n\n        --silent|--quiet)\n                        opt_quiet=:\n                        opt_verbose=false\n                        func_append preserve_args \" $_G_opt\"\n                        ;;\n\n        --tag)          test $# = 0 && func_missing_arg $_G_opt && break\n                        opt_tag=$1\n                        func_append preserve_args \" $_G_opt $1\"\n                        func_enable_tag \"$1\"\n                        shift\n                        ;;\n\n        --verbose|-v)   opt_quiet=false\n                        opt_verbose=:\n                        func_append preserve_args \" $_G_opt\"\n                        ;;\n\n\t# An option not handled by this hook function:\n        *)\t\tset dummy \"$_G_opt\" ${1+\"$@\"};\tshift; break  ;;\n      esac\n    done\n\n\n    # save modified positional parameters for caller\n    func_quote_for_eval ${1+\"$@\"}\n    libtool_parse_options_result=$func_quote_for_eval_result\n}\nfunc_add_hook func_parse_options libtool_parse_options\n\n\n\n# libtool_validate_options [ARG]...\n# ---------------------------------\n# Perform any sanity checks on option settings and/or unconsumed\n# arguments.\nlibtool_validate_options ()\n{\n    # save first non-option argument\n    if test 0 -lt $#; then\n      nonopt=$1\n      shift\n    fi\n\n    # preserve --debug\n    test : = \"$debug_cmd\" || func_append preserve_args \" --debug\"\n\n    case $host in\n      # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452\n      # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788\n      *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*)\n        # don't eliminate duplications in $postdeps and $predeps\n        opt_duplicate_compiler_generated_deps=:\n        ;;\n      *)\n        opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps\n        ;;\n    esac\n\n    $opt_help || {\n      # Sanity checks first:\n      func_check_version_match\n\n      test yes != \"$build_libtool_libs\" \\\n        && test yes != \"$build_old_libs\" \\\n        && func_fatal_configuration \"not configured to build any kind of library\"\n\n      # Darwin sucks\n      eval std_shrext=\\\"$shrext_cmds\\\"\n\n      # Only execute mode is allowed to have -dlopen flags.\n      if test -n \"$opt_dlopen\" && test execute != \"$opt_mode\"; then\n        func_error \"unrecognized option '-dlopen'\"\n        $ECHO \"$help\" 1>&2\n        exit $EXIT_FAILURE\n      fi\n\n      # Change the help message to a mode-specific one.\n      generic_help=$help\n      help=\"Try '$progname --help --mode=$opt_mode' for more information.\"\n    }\n\n    # Pass back the unparsed argument list\n    func_quote_for_eval ${1+\"$@\"}\n    libtool_validate_options_result=$func_quote_for_eval_result\n}\nfunc_add_hook func_validate_options libtool_validate_options\n\n\n# Process options as early as possible so that --help and --version\n# can return quickly.\nfunc_options ${1+\"$@\"}\neval set dummy \"$func_options_result\"; shift\n\n\n\n## ----------- ##\n##    Main.    ##\n## ----------- ##\n\nmagic='%%%MAGIC variable%%%'\nmagic_exe='%%%MAGIC EXE variable%%%'\n\n# Global variables.\nextracted_archives=\nextracted_serial=0\n\n# If this variable is set in any of the actions, the command in it\n# will be execed at the end.  This prevents here-documents from being\n# left over by shells.\nexec_cmd=\n\n\n# A function that is used when there is no print builtin or printf.\nfunc_fallback_echo ()\n{\n  eval 'cat <<_LTECHO_EOF\n$1\n_LTECHO_EOF'\n}\n\n# func_generated_by_libtool\n# True iff stdin has been generated by Libtool. This function is only\n# a basic sanity check; it will hardly flush out determined imposters.\nfunc_generated_by_libtool_p ()\n{\n  $GREP \"^# Generated by .*$PACKAGE\" > /dev/null 2>&1\n}\n\n# func_lalib_p file\n# True iff FILE is a libtool '.la' library or '.lo' object file.\n# This function is only a basic sanity check; it will hardly flush out\n# determined imposters.\nfunc_lalib_p ()\n{\n    test -f \"$1\" &&\n      $SED -e 4q \"$1\" 2>/dev/null | func_generated_by_libtool_p\n}\n\n# func_lalib_unsafe_p file\n# True iff FILE is a libtool '.la' library or '.lo' object file.\n# This function implements the same check as func_lalib_p without\n# resorting to external programs.  To this end, it redirects stdin and\n# closes it afterwards, without saving the original file descriptor.\n# As a safety measure, use it only where a negative result would be\n# fatal anyway.  Works if 'file' does not exist.\nfunc_lalib_unsafe_p ()\n{\n    lalib_p=no\n    if test -f \"$1\" && test -r \"$1\" && exec 5<&0 <\"$1\"; then\n\tfor lalib_p_l in 1 2 3 4\n\tdo\n\t    read lalib_p_line\n\t    case $lalib_p_line in\n\t\t\\#\\ Generated\\ by\\ *$PACKAGE* ) lalib_p=yes; break;;\n\t    esac\n\tdone\n\texec 0<&5 5<&-\n    fi\n    test yes = \"$lalib_p\"\n}\n\n# func_ltwrapper_script_p file\n# True iff FILE is a libtool wrapper script\n# This function is only a basic sanity check; it will hardly flush out\n# determined imposters.\nfunc_ltwrapper_script_p ()\n{\n    test -f \"$1\" &&\n      $lt_truncate_bin < \"$1\" 2>/dev/null | func_generated_by_libtool_p\n}\n\n# func_ltwrapper_executable_p file\n# True iff FILE is a libtool wrapper executable\n# This function is only a basic sanity check; it will hardly flush out\n# determined imposters.\nfunc_ltwrapper_executable_p ()\n{\n    func_ltwrapper_exec_suffix=\n    case $1 in\n    *.exe) ;;\n    *) func_ltwrapper_exec_suffix=.exe ;;\n    esac\n    $GREP \"$magic_exe\" \"$1$func_ltwrapper_exec_suffix\" >/dev/null 2>&1\n}\n\n# func_ltwrapper_scriptname file\n# Assumes file is an ltwrapper_executable\n# uses $file to determine the appropriate filename for a\n# temporary ltwrapper_script.\nfunc_ltwrapper_scriptname ()\n{\n    func_dirname_and_basename \"$1\" \"\" \".\"\n    func_stripname '' '.exe' \"$func_basename_result\"\n    func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper\n}\n\n# func_ltwrapper_p file\n# True iff FILE is a libtool wrapper script or wrapper executable\n# This function is only a basic sanity check; it will hardly flush out\n# determined imposters.\nfunc_ltwrapper_p ()\n{\n    func_ltwrapper_script_p \"$1\" || func_ltwrapper_executable_p \"$1\"\n}\n\n\n# func_execute_cmds commands fail_cmd\n# Execute tilde-delimited COMMANDS.\n# If FAIL_CMD is given, eval that upon failure.\n# FAIL_CMD may read-access the current command in variable CMD!\nfunc_execute_cmds ()\n{\n    $debug_cmd\n\n    save_ifs=$IFS; IFS='~'\n    for cmd in $1; do\n      IFS=$sp$nl\n      eval cmd=\\\"$cmd\\\"\n      IFS=$save_ifs\n      func_show_eval \"$cmd\" \"${2-:}\"\n    done\n    IFS=$save_ifs\n}\n\n\n# func_source file\n# Source FILE, adding directory component if necessary.\n# Note that it is not necessary on cygwin/mingw to append a dot to\n# FILE even if both FILE and FILE.exe exist: automatic-append-.exe\n# behavior happens only for exec(3), not for open(2)!  Also, sourcing\n# 'FILE.' does not work on cygwin managed mounts.\nfunc_source ()\n{\n    $debug_cmd\n\n    case $1 in\n    */* | *\\\\*)\t. \"$1\" ;;\n    *)\t\t. \"./$1\" ;;\n    esac\n}\n\n\n# func_resolve_sysroot PATH\n# Replace a leading = in PATH with a sysroot.  Store the result into\n# func_resolve_sysroot_result\nfunc_resolve_sysroot ()\n{\n  func_resolve_sysroot_result=$1\n  case $func_resolve_sysroot_result in\n  =*)\n    func_stripname '=' '' \"$func_resolve_sysroot_result\"\n    func_resolve_sysroot_result=$lt_sysroot$func_stripname_result\n    ;;\n  esac\n}\n\n# func_replace_sysroot PATH\n# If PATH begins with the sysroot, replace it with = and\n# store the result into func_replace_sysroot_result.\nfunc_replace_sysroot ()\n{\n  case $lt_sysroot:$1 in\n  ?*:\"$lt_sysroot\"*)\n    func_stripname \"$lt_sysroot\" '' \"$1\"\n    func_replace_sysroot_result='='$func_stripname_result\n    ;;\n  *)\n    # Including no sysroot.\n    func_replace_sysroot_result=$1\n    ;;\n  esac\n}\n\n# func_infer_tag arg\n# Infer tagged configuration to use if any are available and\n# if one wasn't chosen via the \"--tag\" command line option.\n# Only attempt this if the compiler in the base compile\n# command doesn't match the default compiler.\n# arg is usually of the form 'gcc ...'\nfunc_infer_tag ()\n{\n    $debug_cmd\n\n    if test -n \"$available_tags\" && test -z \"$tagname\"; then\n      CC_quoted=\n      for arg in $CC; do\n\tfunc_append_quoted CC_quoted \"$arg\"\n      done\n      CC_expanded=`func_echo_all $CC`\n      CC_quoted_expanded=`func_echo_all $CC_quoted`\n      case $@ in\n      # Blanks in the command may have been stripped by the calling shell,\n      # but not from the CC environment variable when configure was run.\n      \" $CC \"* | \"$CC \"* | \" $CC_expanded \"* | \"$CC_expanded \"* | \\\n      \" $CC_quoted\"* | \"$CC_quoted \"* | \" $CC_quoted_expanded \"* | \"$CC_quoted_expanded \"*) ;;\n      # Blanks at the start of $base_compile will cause this to fail\n      # if we don't check for them as well.\n      *)\n\tfor z in $available_tags; do\n\t  if $GREP \"^# ### BEGIN LIBTOOL TAG CONFIG: $z$\" < \"$progpath\" > /dev/null; then\n\t    # Evaluate the configuration.\n\t    eval \"`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`\"\n\t    CC_quoted=\n\t    for arg in $CC; do\n\t      # Double-quote args containing other shell metacharacters.\n\t      func_append_quoted CC_quoted \"$arg\"\n\t    done\n\t    CC_expanded=`func_echo_all $CC`\n\t    CC_quoted_expanded=`func_echo_all $CC_quoted`\n\t    case \"$@ \" in\n\t    \" $CC \"* | \"$CC \"* | \" $CC_expanded \"* | \"$CC_expanded \"* | \\\n\t    \" $CC_quoted\"* | \"$CC_quoted \"* | \" $CC_quoted_expanded \"* | \"$CC_quoted_expanded \"*)\n\t      # The compiler in the base compile command matches\n\t      # the one in the tagged configuration.\n\t      # Assume this is the tagged configuration we want.\n\t      tagname=$z\n\t      break\n\t      ;;\n\t    esac\n\t  fi\n\tdone\n\t# If $tagname still isn't set, then no tagged configuration\n\t# was found and let the user know that the \"--tag\" command\n\t# line option must be used.\n\tif test -z \"$tagname\"; then\n\t  func_echo \"unable to infer tagged configuration\"\n\t  func_fatal_error \"specify a tag with '--tag'\"\n#\telse\n#\t  func_verbose \"using $tagname tagged configuration\"\n\tfi\n\t;;\n      esac\n    fi\n}\n\n\n\n# func_write_libtool_object output_name pic_name nonpic_name\n# Create a libtool object file (analogous to a \".la\" file),\n# but don't create it if we're doing a dry run.\nfunc_write_libtool_object ()\n{\n    write_libobj=$1\n    if test yes = \"$build_libtool_libs\"; then\n      write_lobj=\\'$2\\'\n    else\n      write_lobj=none\n    fi\n\n    if test yes = \"$build_old_libs\"; then\n      write_oldobj=\\'$3\\'\n    else\n      write_oldobj=none\n    fi\n\n    $opt_dry_run || {\n      cat >${write_libobj}T <<EOF\n# $write_libobj - a libtool object file\n# Generated by $PROGRAM (GNU $PACKAGE) $VERSION\n#\n# Please DO NOT delete this file!\n# It is necessary for linking the library.\n\n# Name of the PIC object.\npic_object=$write_lobj\n\n# Name of the non-PIC object\nnon_pic_object=$write_oldobj\n\nEOF\n      $MV \"${write_libobj}T\" \"$write_libobj\"\n    }\n}\n\n\n##################################################\n# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS #\n##################################################\n\n# func_convert_core_file_wine_to_w32 ARG\n# Helper function used by file name conversion functions when $build is *nix,\n# and $host is mingw, cygwin, or some other w32 environment. Relies on a\n# correctly configured wine environment available, with the winepath program\n# in $build's $PATH.\n#\n# ARG is the $build file name to be converted to w32 format.\n# Result is available in $func_convert_core_file_wine_to_w32_result, and will\n# be empty on error (or when ARG is empty)\nfunc_convert_core_file_wine_to_w32 ()\n{\n  $debug_cmd\n\n  func_convert_core_file_wine_to_w32_result=$1\n  if test -n \"$1\"; then\n    # Unfortunately, winepath does not exit with a non-zero error code, so we\n    # are forced to check the contents of stdout. On the other hand, if the\n    # command is not found, the shell will set an exit code of 127 and print\n    # *an error message* to stdout. So we must check for both error code of\n    # zero AND non-empty stdout, which explains the odd construction:\n    func_convert_core_file_wine_to_w32_tmp=`winepath -w \"$1\" 2>/dev/null`\n    if test \"$?\" -eq 0 && test -n \"$func_convert_core_file_wine_to_w32_tmp\"; then\n      func_convert_core_file_wine_to_w32_result=`$ECHO \"$func_convert_core_file_wine_to_w32_tmp\" |\n        $SED -e \"$sed_naive_backslashify\"`\n    else\n      func_convert_core_file_wine_to_w32_result=\n    fi\n  fi\n}\n# end: func_convert_core_file_wine_to_w32\n\n\n# func_convert_core_path_wine_to_w32 ARG\n# Helper function used by path conversion functions when $build is *nix, and\n# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly\n# configured wine environment available, with the winepath program in $build's\n# $PATH. Assumes ARG has no leading or trailing path separator characters.\n#\n# ARG is path to be converted from $build format to win32.\n# Result is available in $func_convert_core_path_wine_to_w32_result.\n# Unconvertible file (directory) names in ARG are skipped; if no directory names\n# are convertible, then the result may be empty.\nfunc_convert_core_path_wine_to_w32 ()\n{\n  $debug_cmd\n\n  # unfortunately, winepath doesn't convert paths, only file names\n  func_convert_core_path_wine_to_w32_result=\n  if test -n \"$1\"; then\n    oldIFS=$IFS\n    IFS=:\n    for func_convert_core_path_wine_to_w32_f in $1; do\n      IFS=$oldIFS\n      func_convert_core_file_wine_to_w32 \"$func_convert_core_path_wine_to_w32_f\"\n      if test -n \"$func_convert_core_file_wine_to_w32_result\"; then\n        if test -z \"$func_convert_core_path_wine_to_w32_result\"; then\n          func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result\n        else\n          func_append func_convert_core_path_wine_to_w32_result \";$func_convert_core_file_wine_to_w32_result\"\n        fi\n      fi\n    done\n    IFS=$oldIFS\n  fi\n}\n# end: func_convert_core_path_wine_to_w32\n\n\n# func_cygpath ARGS...\n# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when\n# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2)\n# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or\n# (2), returns the Cygwin file name or path in func_cygpath_result (input\n# file name or path is assumed to be in w32 format, as previously converted\n# from $build's *nix or MSYS format). In case (3), returns the w32 file name\n# or path in func_cygpath_result (input file name or path is assumed to be in\n# Cygwin format). Returns an empty string on error.\n#\n# ARGS are passed to cygpath, with the last one being the file name or path to\n# be converted.\n#\n# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH\n# environment variable; do not put it in $PATH.\nfunc_cygpath ()\n{\n  $debug_cmd\n\n  if test -n \"$LT_CYGPATH\" && test -f \"$LT_CYGPATH\"; then\n    func_cygpath_result=`$LT_CYGPATH \"$@\" 2>/dev/null`\n    if test \"$?\" -ne 0; then\n      # on failure, ensure result is empty\n      func_cygpath_result=\n    fi\n  else\n    func_cygpath_result=\n    func_error \"LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'\"\n  fi\n}\n#end: func_cygpath\n\n\n# func_convert_core_msys_to_w32 ARG\n# Convert file name or path ARG from MSYS format to w32 format.  Return\n# result in func_convert_core_msys_to_w32_result.\nfunc_convert_core_msys_to_w32 ()\n{\n  $debug_cmd\n\n  # awkward: cmd appends spaces to result\n  func_convert_core_msys_to_w32_result=`( cmd //c echo \"$1\" ) 2>/dev/null |\n    $SED -e 's/[ ]*$//' -e \"$sed_naive_backslashify\"`\n}\n#end: func_convert_core_msys_to_w32\n\n\n# func_convert_file_check ARG1 ARG2\n# Verify that ARG1 (a file name in $build format) was converted to $host\n# format in ARG2. Otherwise, emit an error message, but continue (resetting\n# func_to_host_file_result to ARG1).\nfunc_convert_file_check ()\n{\n  $debug_cmd\n\n  if test -z \"$2\" && test -n \"$1\"; then\n    func_error \"Could not determine host file name corresponding to\"\n    func_error \"  '$1'\"\n    func_error \"Continuing, but uninstalled executables may not work.\"\n    # Fallback:\n    func_to_host_file_result=$1\n  fi\n}\n# end func_convert_file_check\n\n\n# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH\n# Verify that FROM_PATH (a path in $build format) was converted to $host\n# format in TO_PATH. Otherwise, emit an error message, but continue, resetting\n# func_to_host_file_result to a simplistic fallback value (see below).\nfunc_convert_path_check ()\n{\n  $debug_cmd\n\n  if test -z \"$4\" && test -n \"$3\"; then\n    func_error \"Could not determine the host path corresponding to\"\n    func_error \"  '$3'\"\n    func_error \"Continuing, but uninstalled executables may not work.\"\n    # Fallback.  This is a deliberately simplistic \"conversion\" and\n    # should not be \"improved\".  See libtool.info.\n    if test \"x$1\" != \"x$2\"; then\n      lt_replace_pathsep_chars=\"s|$1|$2|g\"\n      func_to_host_path_result=`echo \"$3\" |\n        $SED -e \"$lt_replace_pathsep_chars\"`\n    else\n      func_to_host_path_result=$3\n    fi\n  fi\n}\n# end func_convert_path_check\n\n\n# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG\n# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT\n# and appending REPL if ORIG matches BACKPAT.\nfunc_convert_path_front_back_pathsep ()\n{\n  $debug_cmd\n\n  case $4 in\n  $1 ) func_to_host_path_result=$3$func_to_host_path_result\n    ;;\n  esac\n  case $4 in\n  $2 ) func_append func_to_host_path_result \"$3\"\n    ;;\n  esac\n}\n# end func_convert_path_front_back_pathsep\n\n\n##################################################\n# $build to $host FILE NAME CONVERSION FUNCTIONS #\n##################################################\n# invoked via '$to_host_file_cmd ARG'\n#\n# In each case, ARG is the path to be converted from $build to $host format.\n# Result will be available in $func_to_host_file_result.\n\n\n# func_to_host_file ARG\n# Converts the file name ARG from $build format to $host format. Return result\n# in func_to_host_file_result.\nfunc_to_host_file ()\n{\n  $debug_cmd\n\n  $to_host_file_cmd \"$1\"\n}\n# end func_to_host_file\n\n\n# func_to_tool_file ARG LAZY\n# converts the file name ARG from $build format to toolchain format. Return\n# result in func_to_tool_file_result.  If the conversion in use is listed\n# in (the comma separated) LAZY, no conversion takes place.\nfunc_to_tool_file ()\n{\n  $debug_cmd\n\n  case ,$2, in\n    *,\"$to_tool_file_cmd\",*)\n      func_to_tool_file_result=$1\n      ;;\n    *)\n      $to_tool_file_cmd \"$1\"\n      func_to_tool_file_result=$func_to_host_file_result\n      ;;\n  esac\n}\n# end func_to_tool_file\n\n\n# func_convert_file_noop ARG\n# Copy ARG to func_to_host_file_result.\nfunc_convert_file_noop ()\n{\n  func_to_host_file_result=$1\n}\n# end func_convert_file_noop\n\n\n# func_convert_file_msys_to_w32 ARG\n# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic\n# conversion to w32 is not available inside the cwrapper.  Returns result in\n# func_to_host_file_result.\nfunc_convert_file_msys_to_w32 ()\n{\n  $debug_cmd\n\n  func_to_host_file_result=$1\n  if test -n \"$1\"; then\n    func_convert_core_msys_to_w32 \"$1\"\n    func_to_host_file_result=$func_convert_core_msys_to_w32_result\n  fi\n  func_convert_file_check \"$1\" \"$func_to_host_file_result\"\n}\n# end func_convert_file_msys_to_w32\n\n\n# func_convert_file_cygwin_to_w32 ARG\n# Convert file name ARG from Cygwin to w32 format.  Returns result in\n# func_to_host_file_result.\nfunc_convert_file_cygwin_to_w32 ()\n{\n  $debug_cmd\n\n  func_to_host_file_result=$1\n  if test -n \"$1\"; then\n    # because $build is cygwin, we call \"the\" cygpath in $PATH; no need to use\n    # LT_CYGPATH in this case.\n    func_to_host_file_result=`cygpath -m \"$1\"`\n  fi\n  func_convert_file_check \"$1\" \"$func_to_host_file_result\"\n}\n# end func_convert_file_cygwin_to_w32\n\n\n# func_convert_file_nix_to_w32 ARG\n# Convert file name ARG from *nix to w32 format.  Requires a wine environment\n# and a working winepath. Returns result in func_to_host_file_result.\nfunc_convert_file_nix_to_w32 ()\n{\n  $debug_cmd\n\n  func_to_host_file_result=$1\n  if test -n \"$1\"; then\n    func_convert_core_file_wine_to_w32 \"$1\"\n    func_to_host_file_result=$func_convert_core_file_wine_to_w32_result\n  fi\n  func_convert_file_check \"$1\" \"$func_to_host_file_result\"\n}\n# end func_convert_file_nix_to_w32\n\n\n# func_convert_file_msys_to_cygwin ARG\n# Convert file name ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.\n# Returns result in func_to_host_file_result.\nfunc_convert_file_msys_to_cygwin ()\n{\n  $debug_cmd\n\n  func_to_host_file_result=$1\n  if test -n \"$1\"; then\n    func_convert_core_msys_to_w32 \"$1\"\n    func_cygpath -u \"$func_convert_core_msys_to_w32_result\"\n    func_to_host_file_result=$func_cygpath_result\n  fi\n  func_convert_file_check \"$1\" \"$func_to_host_file_result\"\n}\n# end func_convert_file_msys_to_cygwin\n\n\n# func_convert_file_nix_to_cygwin ARG\n# Convert file name ARG from *nix to Cygwin format.  Requires Cygwin installed\n# in a wine environment, working winepath, and LT_CYGPATH set.  Returns result\n# in func_to_host_file_result.\nfunc_convert_file_nix_to_cygwin ()\n{\n  $debug_cmd\n\n  func_to_host_file_result=$1\n  if test -n \"$1\"; then\n    # convert from *nix to w32, then use cygpath to convert from w32 to cygwin.\n    func_convert_core_file_wine_to_w32 \"$1\"\n    func_cygpath -u \"$func_convert_core_file_wine_to_w32_result\"\n    func_to_host_file_result=$func_cygpath_result\n  fi\n  func_convert_file_check \"$1\" \"$func_to_host_file_result\"\n}\n# end func_convert_file_nix_to_cygwin\n\n\n#############################################\n# $build to $host PATH CONVERSION FUNCTIONS #\n#############################################\n# invoked via '$to_host_path_cmd ARG'\n#\n# In each case, ARG is the path to be converted from $build to $host format.\n# The result will be available in $func_to_host_path_result.\n#\n# Path separators are also converted from $build format to $host format.  If\n# ARG begins or ends with a path separator character, it is preserved (but\n# converted to $host format) on output.\n#\n# All path conversion functions are named using the following convention:\n#   file name conversion function    : func_convert_file_X_to_Y ()\n#   path conversion function         : func_convert_path_X_to_Y ()\n# where, for any given $build/$host combination the 'X_to_Y' value is the\n# same.  If conversion functions are added for new $build/$host combinations,\n# the two new functions must follow this pattern, or func_init_to_host_path_cmd\n# will break.\n\n\n# func_init_to_host_path_cmd\n# Ensures that function \"pointer\" variable $to_host_path_cmd is set to the\n# appropriate value, based on the value of $to_host_file_cmd.\nto_host_path_cmd=\nfunc_init_to_host_path_cmd ()\n{\n  $debug_cmd\n\n  if test -z \"$to_host_path_cmd\"; then\n    func_stripname 'func_convert_file_' '' \"$to_host_file_cmd\"\n    to_host_path_cmd=func_convert_path_$func_stripname_result\n  fi\n}\n\n\n# func_to_host_path ARG\n# Converts the path ARG from $build format to $host format. Return result\n# in func_to_host_path_result.\nfunc_to_host_path ()\n{\n  $debug_cmd\n\n  func_init_to_host_path_cmd\n  $to_host_path_cmd \"$1\"\n}\n# end func_to_host_path\n\n\n# func_convert_path_noop ARG\n# Copy ARG to func_to_host_path_result.\nfunc_convert_path_noop ()\n{\n  func_to_host_path_result=$1\n}\n# end func_convert_path_noop\n\n\n# func_convert_path_msys_to_w32 ARG\n# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic\n# conversion to w32 is not available inside the cwrapper.  Returns result in\n# func_to_host_path_result.\nfunc_convert_path_msys_to_w32 ()\n{\n  $debug_cmd\n\n  func_to_host_path_result=$1\n  if test -n \"$1\"; then\n    # Remove leading and trailing path separator characters from ARG.  MSYS\n    # behavior is inconsistent here; cygpath turns them into '.;' and ';.';\n    # and winepath ignores them completely.\n    func_stripname : : \"$1\"\n    func_to_host_path_tmp1=$func_stripname_result\n    func_convert_core_msys_to_w32 \"$func_to_host_path_tmp1\"\n    func_to_host_path_result=$func_convert_core_msys_to_w32_result\n    func_convert_path_check : \";\" \\\n      \"$func_to_host_path_tmp1\" \"$func_to_host_path_result\"\n    func_convert_path_front_back_pathsep \":*\" \"*:\" \";\" \"$1\"\n  fi\n}\n# end func_convert_path_msys_to_w32\n\n\n# func_convert_path_cygwin_to_w32 ARG\n# Convert path ARG from Cygwin to w32 format.  Returns result in\n# func_to_host_file_result.\nfunc_convert_path_cygwin_to_w32 ()\n{\n  $debug_cmd\n\n  func_to_host_path_result=$1\n  if test -n \"$1\"; then\n    # See func_convert_path_msys_to_w32:\n    func_stripname : : \"$1\"\n    func_to_host_path_tmp1=$func_stripname_result\n    func_to_host_path_result=`cygpath -m -p \"$func_to_host_path_tmp1\"`\n    func_convert_path_check : \";\" \\\n      \"$func_to_host_path_tmp1\" \"$func_to_host_path_result\"\n    func_convert_path_front_back_pathsep \":*\" \"*:\" \";\" \"$1\"\n  fi\n}\n# end func_convert_path_cygwin_to_w32\n\n\n# func_convert_path_nix_to_w32 ARG\n# Convert path ARG from *nix to w32 format.  Requires a wine environment and\n# a working winepath.  Returns result in func_to_host_file_result.\nfunc_convert_path_nix_to_w32 ()\n{\n  $debug_cmd\n\n  func_to_host_path_result=$1\n  if test -n \"$1\"; then\n    # See func_convert_path_msys_to_w32:\n    func_stripname : : \"$1\"\n    func_to_host_path_tmp1=$func_stripname_result\n    func_convert_core_path_wine_to_w32 \"$func_to_host_path_tmp1\"\n    func_to_host_path_result=$func_convert_core_path_wine_to_w32_result\n    func_convert_path_check : \";\" \\\n      \"$func_to_host_path_tmp1\" \"$func_to_host_path_result\"\n    func_convert_path_front_back_pathsep \":*\" \"*:\" \";\" \"$1\"\n  fi\n}\n# end func_convert_path_nix_to_w32\n\n\n# func_convert_path_msys_to_cygwin ARG\n# Convert path ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.\n# Returns result in func_to_host_file_result.\nfunc_convert_path_msys_to_cygwin ()\n{\n  $debug_cmd\n\n  func_to_host_path_result=$1\n  if test -n \"$1\"; then\n    # See func_convert_path_msys_to_w32:\n    func_stripname : : \"$1\"\n    func_to_host_path_tmp1=$func_stripname_result\n    func_convert_core_msys_to_w32 \"$func_to_host_path_tmp1\"\n    func_cygpath -u -p \"$func_convert_core_msys_to_w32_result\"\n    func_to_host_path_result=$func_cygpath_result\n    func_convert_path_check : : \\\n      \"$func_to_host_path_tmp1\" \"$func_to_host_path_result\"\n    func_convert_path_front_back_pathsep \":*\" \"*:\" : \"$1\"\n  fi\n}\n# end func_convert_path_msys_to_cygwin\n\n\n# func_convert_path_nix_to_cygwin ARG\n# Convert path ARG from *nix to Cygwin format.  Requires Cygwin installed in a\n# a wine environment, working winepath, and LT_CYGPATH set.  Returns result in\n# func_to_host_file_result.\nfunc_convert_path_nix_to_cygwin ()\n{\n  $debug_cmd\n\n  func_to_host_path_result=$1\n  if test -n \"$1\"; then\n    # Remove leading and trailing path separator characters from\n    # ARG. msys behavior is inconsistent here, cygpath turns them\n    # into '.;' and ';.', and winepath ignores them completely.\n    func_stripname : : \"$1\"\n    func_to_host_path_tmp1=$func_stripname_result\n    func_convert_core_path_wine_to_w32 \"$func_to_host_path_tmp1\"\n    func_cygpath -u -p \"$func_convert_core_path_wine_to_w32_result\"\n    func_to_host_path_result=$func_cygpath_result\n    func_convert_path_check : : \\\n      \"$func_to_host_path_tmp1\" \"$func_to_host_path_result\"\n    func_convert_path_front_back_pathsep \":*\" \"*:\" : \"$1\"\n  fi\n}\n# end func_convert_path_nix_to_cygwin\n\n\n# func_dll_def_p FILE\n# True iff FILE is a Windows DLL '.def' file.\n# Keep in sync with _LT_DLL_DEF_P in libtool.m4\nfunc_dll_def_p ()\n{\n  $debug_cmd\n\n  func_dll_def_p_tmp=`$SED -n \\\n    -e 's/^[\t ]*//' \\\n    -e '/^\\(;.*\\)*$/d' \\\n    -e 's/^\\(EXPORTS\\|LIBRARY\\)\\([\t ].*\\)*$/DEF/p' \\\n    -e q \\\n    \"$1\"`\n  test DEF = \"$func_dll_def_p_tmp\"\n}\n\n\n# func_mode_compile arg...\nfunc_mode_compile ()\n{\n    $debug_cmd\n\n    # Get the compilation command and the source file.\n    base_compile=\n    srcfile=$nonopt  #  always keep a non-empty value in \"srcfile\"\n    suppress_opt=yes\n    suppress_output=\n    arg_mode=normal\n    libobj=\n    later=\n    pie_flag=\n\n    for arg\n    do\n      case $arg_mode in\n      arg  )\n\t# do not \"continue\".  Instead, add this to base_compile\n\tlastarg=$arg\n\targ_mode=normal\n\t;;\n\n      target )\n\tlibobj=$arg\n\targ_mode=normal\n\tcontinue\n\t;;\n\n      normal )\n\t# Accept any command-line options.\n\tcase $arg in\n\t-o)\n\t  test -n \"$libobj\" && \\\n\t    func_fatal_error \"you cannot specify '-o' more than once\"\n\t  arg_mode=target\n\t  continue\n\t  ;;\n\n\t-pie | -fpie | -fPIE)\n          func_append pie_flag \" $arg\"\n\t  continue\n\t  ;;\n\n\t-shared | -static | -prefer-pic | -prefer-non-pic)\n\t  func_append later \" $arg\"\n\t  continue\n\t  ;;\n\n\t-no-suppress)\n\t  suppress_opt=no\n\t  continue\n\t  ;;\n\n\t-Xcompiler)\n\t  arg_mode=arg  #  the next one goes into the \"base_compile\" arg list\n\t  continue      #  The current \"srcfile\" will either be retained or\n\t  ;;            #  replaced later.  I would guess that would be a bug.\n\n\t-Wc,*)\n\t  func_stripname '-Wc,' '' \"$arg\"\n\t  args=$func_stripname_result\n\t  lastarg=\n\t  save_ifs=$IFS; IFS=,\n\t  for arg in $args; do\n\t    IFS=$save_ifs\n\t    func_append_quoted lastarg \"$arg\"\n\t  done\n\t  IFS=$save_ifs\n\t  func_stripname ' ' '' \"$lastarg\"\n\t  lastarg=$func_stripname_result\n\n\t  # Add the arguments to base_compile.\n\t  func_append base_compile \" $lastarg\"\n\t  continue\n\t  ;;\n\n\t*)\n\t  # Accept the current argument as the source file.\n\t  # The previous \"srcfile\" becomes the current argument.\n\t  #\n\t  lastarg=$srcfile\n\t  srcfile=$arg\n\t  ;;\n\tesac  #  case $arg\n\t;;\n      esac    #  case $arg_mode\n\n      # Aesthetically quote the previous argument.\n      func_append_quoted base_compile \"$lastarg\"\n    done # for arg\n\n    case $arg_mode in\n    arg)\n      func_fatal_error \"you must specify an argument for -Xcompile\"\n      ;;\n    target)\n      func_fatal_error \"you must specify a target with '-o'\"\n      ;;\n    *)\n      # Get the name of the library object.\n      test -z \"$libobj\" && {\n\tfunc_basename \"$srcfile\"\n\tlibobj=$func_basename_result\n      }\n      ;;\n    esac\n\n    # Recognize several different file suffixes.\n    # If the user specifies -o file.o, it is replaced with file.lo\n    case $libobj in\n    *.[cCFSifmso] | \\\n    *.ada | *.adb | *.ads | *.asm | \\\n    *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \\\n    *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup)\n      func_xform \"$libobj\"\n      libobj=$func_xform_result\n      ;;\n    esac\n\n    case $libobj in\n    *.lo) func_lo2o \"$libobj\"; obj=$func_lo2o_result ;;\n    *)\n      func_fatal_error \"cannot determine name of library object from '$libobj'\"\n      ;;\n    esac\n\n    func_infer_tag $base_compile\n\n    for arg in $later; do\n      case $arg in\n      -shared)\n\ttest yes = \"$build_libtool_libs\" \\\n\t  || func_fatal_configuration \"cannot build a shared library\"\n\tbuild_old_libs=no\n\tcontinue\n\t;;\n\n      -static)\n\tbuild_libtool_libs=no\n\tbuild_old_libs=yes\n\tcontinue\n\t;;\n\n      -prefer-pic)\n\tpic_mode=yes\n\tcontinue\n\t;;\n\n      -prefer-non-pic)\n\tpic_mode=no\n\tcontinue\n\t;;\n      esac\n    done\n\n    func_quote_for_eval \"$libobj\"\n    test \"X$libobj\" != \"X$func_quote_for_eval_result\" \\\n      && $ECHO \"X$libobj\" | $GREP '[]~#^*{};<>?\"'\"'\"'\t &()|`$[]' \\\n      && func_warning \"libobj name '$libobj' may not contain shell special characters.\"\n    func_dirname_and_basename \"$obj\" \"/\" \"\"\n    objname=$func_basename_result\n    xdir=$func_dirname_result\n    lobj=$xdir$objdir/$objname\n\n    test -z \"$base_compile\" && \\\n      func_fatal_help \"you must specify a compilation command\"\n\n    # Delete any leftover library objects.\n    if test yes = \"$build_old_libs\"; then\n      removelist=\"$obj $lobj $libobj ${libobj}T\"\n    else\n      removelist=\"$lobj $libobj ${libobj}T\"\n    fi\n\n    # On Cygwin there's no \"real\" PIC flag so we must build both object types\n    case $host_os in\n    cygwin* | mingw* | pw32* | os2* | cegcc*)\n      pic_mode=default\n      ;;\n    esac\n    if test no = \"$pic_mode\" && test pass_all != \"$deplibs_check_method\"; then\n      # non-PIC code in shared libraries is not supported\n      pic_mode=default\n    fi\n\n    # Calculate the filename of the output object if compiler does\n    # not support -o with -c\n    if test no = \"$compiler_c_o\"; then\n      output_obj=`$ECHO \"$srcfile\" | $SED 's%^.*/%%; s%\\.[^.]*$%%'`.$objext\n      lockfile=$output_obj.lock\n    else\n      output_obj=\n      need_locks=no\n      lockfile=\n    fi\n\n    # Lock this critical section if it is needed\n    # We use this script file to make the link, it avoids creating a new file\n    if test yes = \"$need_locks\"; then\n      until $opt_dry_run || ln \"$progpath\" \"$lockfile\" 2>/dev/null; do\n\tfunc_echo \"Waiting for $lockfile to be removed\"\n\tsleep 2\n      done\n    elif test warn = \"$need_locks\"; then\n      if test -f \"$lockfile\"; then\n\t$ECHO \"\\\n*** ERROR, $lockfile exists and contains:\n`cat $lockfile 2>/dev/null`\n\nThis indicates that another process is trying to use the same\ntemporary object file, and libtool could not work around it because\nyour compiler does not support '-c' and '-o' together.  If you\nrepeat this compilation, it may succeed, by chance, but you had better\navoid parallel builds (make -j) in this platform, or get a better\ncompiler.\"\n\n\t$opt_dry_run || $RM $removelist\n\texit $EXIT_FAILURE\n      fi\n      func_append removelist \" $output_obj\"\n      $ECHO \"$srcfile\" > \"$lockfile\"\n    fi\n\n    $opt_dry_run || $RM $removelist\n    func_append removelist \" $lockfile\"\n    trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15\n\n    func_to_tool_file \"$srcfile\" func_convert_file_msys_to_w32\n    srcfile=$func_to_tool_file_result\n    func_quote_for_eval \"$srcfile\"\n    qsrcfile=$func_quote_for_eval_result\n\n    # Only build a PIC object if we are building libtool libraries.\n    if test yes = \"$build_libtool_libs\"; then\n      # Without this assignment, base_compile gets emptied.\n      fbsd_hideous_sh_bug=$base_compile\n\n      if test no != \"$pic_mode\"; then\n\tcommand=\"$base_compile $qsrcfile $pic_flag\"\n      else\n\t# Don't build PIC code\n\tcommand=\"$base_compile $qsrcfile\"\n      fi\n\n      func_mkdir_p \"$xdir$objdir\"\n\n      if test -z \"$output_obj\"; then\n\t# Place PIC objects in $objdir\n\tfunc_append command \" -o $lobj\"\n      fi\n\n      func_show_eval_locale \"$command\"\t\\\n          'test -n \"$output_obj\" && $RM $removelist; exit $EXIT_FAILURE'\n\n      if test warn = \"$need_locks\" &&\n\t test \"X`cat $lockfile 2>/dev/null`\" != \"X$srcfile\"; then\n\t$ECHO \"\\\n*** ERROR, $lockfile contains:\n`cat $lockfile 2>/dev/null`\n\nbut it should contain:\n$srcfile\n\nThis indicates that another process is trying to use the same\ntemporary object file, and libtool could not work around it because\nyour compiler does not support '-c' and '-o' together.  If you\nrepeat this compilation, it may succeed, by chance, but you had better\navoid parallel builds (make -j) in this platform, or get a better\ncompiler.\"\n\n\t$opt_dry_run || $RM $removelist\n\texit $EXIT_FAILURE\n      fi\n\n      # Just move the object if needed, then go on to compile the next one\n      if test -n \"$output_obj\" && test \"X$output_obj\" != \"X$lobj\"; then\n\tfunc_show_eval '$MV \"$output_obj\" \"$lobj\"' \\\n\t  'error=$?; $opt_dry_run || $RM $removelist; exit $error'\n      fi\n\n      # Allow error messages only from the first compilation.\n      if test yes = \"$suppress_opt\"; then\n\tsuppress_output=' >/dev/null 2>&1'\n      fi\n    fi\n\n    # Only build a position-dependent object if we build old libraries.\n    if test yes = \"$build_old_libs\"; then\n      if test yes != \"$pic_mode\"; then\n\t# Don't build PIC code\n\tcommand=\"$base_compile $qsrcfile$pie_flag\"\n      else\n\tcommand=\"$base_compile $qsrcfile $pic_flag\"\n      fi\n      if test yes = \"$compiler_c_o\"; then\n\tfunc_append command \" -o $obj\"\n      fi\n\n      # Suppress compiler output if we already did a PIC compilation.\n      func_append command \"$suppress_output\"\n      func_show_eval_locale \"$command\" \\\n        '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'\n\n      if test warn = \"$need_locks\" &&\n\t test \"X`cat $lockfile 2>/dev/null`\" != \"X$srcfile\"; then\n\t$ECHO \"\\\n*** ERROR, $lockfile contains:\n`cat $lockfile 2>/dev/null`\n\nbut it should contain:\n$srcfile\n\nThis indicates that another process is trying to use the same\ntemporary object file, and libtool could not work around it because\nyour compiler does not support '-c' and '-o' together.  If you\nrepeat this compilation, it may succeed, by chance, but you had better\navoid parallel builds (make -j) in this platform, or get a better\ncompiler.\"\n\n\t$opt_dry_run || $RM $removelist\n\texit $EXIT_FAILURE\n      fi\n\n      # Just move the object if needed\n      if test -n \"$output_obj\" && test \"X$output_obj\" != \"X$obj\"; then\n\tfunc_show_eval '$MV \"$output_obj\" \"$obj\"' \\\n\t  'error=$?; $opt_dry_run || $RM $removelist; exit $error'\n      fi\n    fi\n\n    $opt_dry_run || {\n      func_write_libtool_object \"$libobj\" \"$objdir/$objname\" \"$objname\"\n\n      # Unlock the critical section if it was locked\n      if test no != \"$need_locks\"; then\n\tremovelist=$lockfile\n        $RM \"$lockfile\"\n      fi\n    }\n\n    exit $EXIT_SUCCESS\n}\n\n$opt_help || {\n  test compile = \"$opt_mode\" && func_mode_compile ${1+\"$@\"}\n}\n\nfunc_mode_help ()\n{\n    # We need to display help for each of the modes.\n    case $opt_mode in\n      \"\")\n        # Generic help is extracted from the usage comments\n        # at the start of this file.\n        func_help\n        ;;\n\n      clean)\n        $ECHO \\\n\"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...\n\nRemove files from the build directory.\n\nRM is the name of the program to use to delete files associated with each FILE\n(typically '/bin/rm').  RM-OPTIONS are options (such as '-f') to be passed\nto RM.\n\nIf FILE is a libtool library, object or program, all the files associated\nwith it are deleted. Otherwise, only FILE itself is deleted using RM.\"\n        ;;\n\n      compile)\n      $ECHO \\\n\"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE\n\nCompile a source file into a libtool library object.\n\nThis mode accepts the following additional options:\n\n  -o OUTPUT-FILE    set the output file name to OUTPUT-FILE\n  -no-suppress      do not suppress compiler output for multiple passes\n  -prefer-pic       try to build PIC objects only\n  -prefer-non-pic   try to build non-PIC objects only\n  -shared           do not build a '.o' file suitable for static linking\n  -static           only build a '.o' file suitable for static linking\n  -Wc,FLAG          pass FLAG directly to the compiler\n\nCOMPILE-COMMAND is a command to be used in creating a 'standard' object file\nfrom the given SOURCEFILE.\n\nThe output file name is determined by removing the directory component from\nSOURCEFILE, then substituting the C source code suffix '.c' with the\nlibrary object suffix, '.lo'.\"\n        ;;\n\n      execute)\n        $ECHO \\\n\"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...\n\nAutomatically set library path, then run a program.\n\nThis mode accepts the following additional options:\n\n  -dlopen FILE      add the directory containing FILE to the library path\n\nThis mode sets the library path environment variable according to '-dlopen'\nflags.\n\nIf any of the ARGS are libtool executable wrappers, then they are translated\ninto their corresponding uninstalled binary, and any of their required library\ndirectories are added to the library path.\n\nThen, COMMAND is executed, with ARGS as arguments.\"\n        ;;\n\n      finish)\n        $ECHO \\\n\"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...\n\nComplete the installation of libtool libraries.\n\nEach LIBDIR is a directory that contains libtool libraries.\n\nThe commands that this mode executes may require superuser privileges.  Use\nthe '--dry-run' option if you just want to see what would be executed.\"\n        ;;\n\n      install)\n        $ECHO \\\n\"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...\n\nInstall executables or libraries.\n\nINSTALL-COMMAND is the installation command.  The first component should be\neither the 'install' or 'cp' program.\n\nThe following components of INSTALL-COMMAND are treated specially:\n\n  -inst-prefix-dir PREFIX-DIR  Use PREFIX-DIR as a staging area for installation\n\nThe rest of the components are interpreted as arguments to that command (only\nBSD-compatible install options are recognized).\"\n        ;;\n\n      link)\n        $ECHO \\\n\"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...\n\nLink object files or libraries together to form another library, or to\ncreate an executable program.\n\nLINK-COMMAND is a command using the C compiler that you would use to create\na program from several object files.\n\nThe following components of LINK-COMMAND are treated specially:\n\n  -all-static       do not do any dynamic linking at all\n  -avoid-version    do not add a version suffix if possible\n  -bindir BINDIR    specify path to binaries directory (for systems where\n                    libraries must be found in the PATH setting at runtime)\n  -dlopen FILE      '-dlpreopen' FILE if it cannot be dlopened at runtime\n  -dlpreopen FILE   link in FILE and add its symbols to lt_preloaded_symbols\n  -export-dynamic   allow symbols from OUTPUT-FILE to be resolved with dlsym(3)\n  -export-symbols SYMFILE\n                    try to export only the symbols listed in SYMFILE\n  -export-symbols-regex REGEX\n                    try to export only the symbols matching REGEX\n  -LLIBDIR          search LIBDIR for required installed libraries\n  -lNAME            OUTPUT-FILE requires the installed library libNAME\n  -module           build a library that can dlopened\n  -no-fast-install  disable the fast-install mode\n  -no-install       link a not-installable executable\n  -no-undefined     declare that a library does not refer to external symbols\n  -o OUTPUT-FILE    create OUTPUT-FILE from the specified objects\n  -objectlist FILE  use a list of object files found in FILE to specify objects\n  -os2dllname NAME  force a short DLL name on OS/2 (no effect on other OSes)\n  -precious-files-regex REGEX\n                    don't remove output files matching REGEX\n  -release RELEASE  specify package release information\n  -rpath LIBDIR     the created library will eventually be installed in LIBDIR\n  -R[ ]LIBDIR       add LIBDIR to the runtime path of programs and libraries\n  -shared           only do dynamic linking of libtool libraries\n  -shrext SUFFIX    override the standard shared library file extension\n  -static           do not do any dynamic linking of uninstalled libtool libraries\n  -static-libtool-libs\n                    do not do any dynamic linking of libtool libraries\n  -version-info CURRENT[:REVISION[:AGE]]\n                    specify library version info [each variable defaults to 0]\n  -weak LIBNAME     declare that the target provides the LIBNAME interface\n  -Wc,FLAG\n  -Xcompiler FLAG   pass linker-specific FLAG directly to the compiler\n  -Wl,FLAG\n  -Xlinker FLAG     pass linker-specific FLAG directly to the linker\n  -XCClinker FLAG   pass link-specific FLAG to the compiler driver (CC)\n\nAll other options (arguments beginning with '-') are ignored.\n\nEvery other argument is treated as a filename.  Files ending in '.la' are\ntreated as uninstalled libtool libraries, other files are standard or library\nobject files.\n\nIf the OUTPUT-FILE ends in '.la', then a libtool library is created,\nonly library objects ('.lo' files) may be specified, and '-rpath' is\nrequired, except when creating a convenience library.\n\nIf OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created\nusing 'ar' and 'ranlib', or on Windows using 'lib'.\n\nIf OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file\nis created, otherwise an executable program is created.\"\n        ;;\n\n      uninstall)\n        $ECHO \\\n\"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...\n\nRemove libraries from an installation directory.\n\nRM is the name of the program to use to delete files associated with each FILE\n(typically '/bin/rm').  RM-OPTIONS are options (such as '-f') to be passed\nto RM.\n\nIf FILE is a libtool library, all the files associated with it are deleted.\nOtherwise, only FILE itself is deleted using RM.\"\n        ;;\n\n      *)\n        func_fatal_help \"invalid operation mode '$opt_mode'\"\n        ;;\n    esac\n\n    echo\n    $ECHO \"Try '$progname --help' for more information about other modes.\"\n}\n\n# Now that we've collected a possible --mode arg, show help if necessary\nif $opt_help; then\n  if test : = \"$opt_help\"; then\n    func_mode_help\n  else\n    {\n      func_help noexit\n      for opt_mode in compile link execute install finish uninstall clean; do\n\tfunc_mode_help\n      done\n    } | $SED -n '1p; 2,$s/^Usage:/  or: /p'\n    {\n      func_help noexit\n      for opt_mode in compile link execute install finish uninstall clean; do\n\techo\n\tfunc_mode_help\n      done\n    } |\n    $SED '1d\n      /^When reporting/,/^Report/{\n\tH\n\td\n      }\n      $x\n      /information about other modes/d\n      /more detailed .*MODE/d\n      s/^Usage:.*--mode=\\([^ ]*\\) .*/Description of \\1 mode:/'\n  fi\n  exit $?\nfi\n\n\n# func_mode_execute arg...\nfunc_mode_execute ()\n{\n    $debug_cmd\n\n    # The first argument is the command name.\n    cmd=$nonopt\n    test -z \"$cmd\" && \\\n      func_fatal_help \"you must specify a COMMAND\"\n\n    # Handle -dlopen flags immediately.\n    for file in $opt_dlopen; do\n      test -f \"$file\" \\\n\t|| func_fatal_help \"'$file' is not a file\"\n\n      dir=\n      case $file in\n      *.la)\n\tfunc_resolve_sysroot \"$file\"\n\tfile=$func_resolve_sysroot_result\n\n\t# Check to see that this really is a libtool archive.\n\tfunc_lalib_unsafe_p \"$file\" \\\n\t  || func_fatal_help \"'$lib' is not a valid libtool archive\"\n\n\t# Read the libtool library.\n\tdlname=\n\tlibrary_names=\n\tfunc_source \"$file\"\n\n\t# Skip this library if it cannot be dlopened.\n\tif test -z \"$dlname\"; then\n\t  # Warn if it was a shared library.\n\t  test -n \"$library_names\" && \\\n\t    func_warning \"'$file' was not linked with '-export-dynamic'\"\n\t  continue\n\tfi\n\n\tfunc_dirname \"$file\" \"\" \".\"\n\tdir=$func_dirname_result\n\n\tif test -f \"$dir/$objdir/$dlname\"; then\n\t  func_append dir \"/$objdir\"\n\telse\n\t  if test ! -f \"$dir/$dlname\"; then\n\t    func_fatal_error \"cannot find '$dlname' in '$dir' or '$dir/$objdir'\"\n\t  fi\n\tfi\n\t;;\n\n      *.lo)\n\t# Just add the directory containing the .lo file.\n\tfunc_dirname \"$file\" \"\" \".\"\n\tdir=$func_dirname_result\n\t;;\n\n      *)\n\tfunc_warning \"'-dlopen' is ignored for non-libtool libraries and objects\"\n\tcontinue\n\t;;\n      esac\n\n      # Get the absolute pathname.\n      absdir=`cd \"$dir\" && pwd`\n      test -n \"$absdir\" && dir=$absdir\n\n      # Now add the directory to shlibpath_var.\n      if eval \"test -z \\\"\\$$shlibpath_var\\\"\"; then\n\teval \"$shlibpath_var=\\\"\\$dir\\\"\"\n      else\n\teval \"$shlibpath_var=\\\"\\$dir:\\$$shlibpath_var\\\"\"\n      fi\n    done\n\n    # This variable tells wrapper scripts just to set shlibpath_var\n    # rather than running their programs.\n    libtool_execute_magic=$magic\n\n    # Check if any of the arguments is a wrapper script.\n    args=\n    for file\n    do\n      case $file in\n      -* | *.la | *.lo ) ;;\n      *)\n\t# Do a test to see if this is really a libtool program.\n\tif func_ltwrapper_script_p \"$file\"; then\n\t  func_source \"$file\"\n\t  # Transform arg to wrapped name.\n\t  file=$progdir/$program\n\telif func_ltwrapper_executable_p \"$file\"; then\n\t  func_ltwrapper_scriptname \"$file\"\n\t  func_source \"$func_ltwrapper_scriptname_result\"\n\t  # Transform arg to wrapped name.\n\t  file=$progdir/$program\n\tfi\n\t;;\n      esac\n      # Quote arguments (to preserve shell metacharacters).\n      func_append_quoted args \"$file\"\n    done\n\n    if $opt_dry_run; then\n      # Display what would be done.\n      if test -n \"$shlibpath_var\"; then\n\teval \"\\$ECHO \\\"\\$shlibpath_var=\\$$shlibpath_var\\\"\"\n\techo \"export $shlibpath_var\"\n      fi\n      $ECHO \"$cmd$args\"\n      exit $EXIT_SUCCESS\n    else\n      if test -n \"$shlibpath_var\"; then\n\t# Export the shlibpath_var.\n\teval \"export $shlibpath_var\"\n      fi\n\n      # Restore saved environment variables\n      for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES\n      do\n\teval \"if test \\\"\\${save_$lt_var+set}\\\" = set; then\n                $lt_var=\\$save_$lt_var; export $lt_var\n\t      else\n\t\t$lt_unset $lt_var\n\t      fi\"\n      done\n\n      # Now prepare to actually exec the command.\n      exec_cmd=\\$cmd$args\n    fi\n}\n\ntest execute = \"$opt_mode\" && func_mode_execute ${1+\"$@\"}\n\n\n# func_mode_finish arg...\nfunc_mode_finish ()\n{\n    $debug_cmd\n\n    libs=\n    libdirs=\n    admincmds=\n\n    for opt in \"$nonopt\" ${1+\"$@\"}\n    do\n      if test -d \"$opt\"; then\n\tfunc_append libdirs \" $opt\"\n\n      elif test -f \"$opt\"; then\n\tif func_lalib_unsafe_p \"$opt\"; then\n\t  func_append libs \" $opt\"\n\telse\n\t  func_warning \"'$opt' is not a valid libtool archive\"\n\tfi\n\n      else\n\tfunc_fatal_error \"invalid argument '$opt'\"\n      fi\n    done\n\n    if test -n \"$libs\"; then\n      if test -n \"$lt_sysroot\"; then\n        sysroot_regex=`$ECHO \"$lt_sysroot\" | $SED \"$sed_make_literal_regex\"`\n        sysroot_cmd=\"s/\\([ ']\\)$sysroot_regex/\\1/g;\"\n      else\n        sysroot_cmd=\n      fi\n\n      # Remove sysroot references\n      if $opt_dry_run; then\n        for lib in $libs; do\n          echo \"removing references to $lt_sysroot and '=' prefixes from $lib\"\n        done\n      else\n        tmpdir=`func_mktempdir`\n        for lib in $libs; do\n\t  $SED -e \"$sysroot_cmd s/\\([ ']-[LR]\\)=/\\1/g; s/\\([ ']\\)=/\\1/g\" $lib \\\n\t    > $tmpdir/tmp-la\n\t  mv -f $tmpdir/tmp-la $lib\n\tdone\n        ${RM}r \"$tmpdir\"\n      fi\n    fi\n\n    if test -n \"$finish_cmds$finish_eval\" && test -n \"$libdirs\"; then\n      for libdir in $libdirs; do\n\tif test -n \"$finish_cmds\"; then\n\t  # Do each command in the finish commands.\n\t  func_execute_cmds \"$finish_cmds\" 'admincmds=\"$admincmds\n'\"$cmd\"'\"'\n\tfi\n\tif test -n \"$finish_eval\"; then\n\t  # Do the single finish_eval.\n\t  eval cmds=\\\"$finish_eval\\\"\n\t  $opt_dry_run || eval \"$cmds\" || func_append admincmds \"\n       $cmds\"\n\tfi\n      done\n    fi\n\n    # Exit here if they wanted silent mode.\n    $opt_quiet && exit $EXIT_SUCCESS\n\n    if test -n \"$finish_cmds$finish_eval\" && test -n \"$libdirs\"; then\n      echo \"----------------------------------------------------------------------\"\n      echo \"Libraries have been installed in:\"\n      for libdir in $libdirs; do\n\t$ECHO \"   $libdir\"\n      done\n      echo\n      echo \"If you ever happen to want to link against installed libraries\"\n      echo \"in a given directory, LIBDIR, you must either use libtool, and\"\n      echo \"specify the full pathname of the library, or use the '-LLIBDIR'\"\n      echo \"flag during linking and do at least one of the following:\"\n      if test -n \"$shlibpath_var\"; then\n\techo \"   - add LIBDIR to the '$shlibpath_var' environment variable\"\n\techo \"     during execution\"\n      fi\n      if test -n \"$runpath_var\"; then\n\techo \"   - add LIBDIR to the '$runpath_var' environment variable\"\n\techo \"     during linking\"\n      fi\n      if test -n \"$hardcode_libdir_flag_spec\"; then\n\tlibdir=LIBDIR\n\teval flag=\\\"$hardcode_libdir_flag_spec\\\"\n\n\t$ECHO \"   - use the '$flag' linker flag\"\n      fi\n      if test -n \"$admincmds\"; then\n\t$ECHO \"   - have your system administrator run these commands:$admincmds\"\n      fi\n      if test -f /etc/ld.so.conf; then\n\techo \"   - have your system administrator add LIBDIR to '/etc/ld.so.conf'\"\n      fi\n      echo\n\n      echo \"See any operating system documentation about shared libraries for\"\n      case $host in\n\tsolaris2.[6789]|solaris2.1[0-9])\n\t  echo \"more information, such as the ld(1), crle(1) and ld.so(8) manual\"\n\t  echo \"pages.\"\n\t  ;;\n\t*)\n\t  echo \"more information, such as the ld(1) and ld.so(8) manual pages.\"\n\t  ;;\n      esac\n      echo \"----------------------------------------------------------------------\"\n    fi\n    exit $EXIT_SUCCESS\n}\n\ntest finish = \"$opt_mode\" && func_mode_finish ${1+\"$@\"}\n\n\n# func_mode_install arg...\nfunc_mode_install ()\n{\n    $debug_cmd\n\n    # There may be an optional sh(1) argument at the beginning of\n    # install_prog (especially on Windows NT).\n    if test \"$SHELL\" = \"$nonopt\" || test /bin/sh = \"$nonopt\" ||\n       # Allow the use of GNU shtool's install command.\n       case $nonopt in *shtool*) :;; *) false;; esac\n    then\n      # Aesthetically quote it.\n      func_quote_for_eval \"$nonopt\"\n      install_prog=\"$func_quote_for_eval_result \"\n      arg=$1\n      shift\n    else\n      install_prog=\n      arg=$nonopt\n    fi\n\n    # The real first argument should be the name of the installation program.\n    # Aesthetically quote it.\n    func_quote_for_eval \"$arg\"\n    func_append install_prog \"$func_quote_for_eval_result\"\n    install_shared_prog=$install_prog\n    case \" $install_prog \" in\n      *[\\\\\\ /]cp\\ *) install_cp=: ;;\n      *) install_cp=false ;;\n    esac\n\n    # We need to accept at least all the BSD install flags.\n    dest=\n    files=\n    opts=\n    prev=\n    install_type=\n    isdir=false\n    stripme=\n    no_mode=:\n    for arg\n    do\n      arg2=\n      if test -n \"$dest\"; then\n\tfunc_append files \" $dest\"\n\tdest=$arg\n\tcontinue\n      fi\n\n      case $arg in\n      -d) isdir=: ;;\n      -f)\n\tif $install_cp; then :; else\n\t  prev=$arg\n\tfi\n\t;;\n      -g | -m | -o)\n\tprev=$arg\n\t;;\n      -s)\n\tstripme=\" -s\"\n\tcontinue\n\t;;\n      -*)\n\t;;\n      *)\n\t# If the previous option needed an argument, then skip it.\n\tif test -n \"$prev\"; then\n\t  if test X-m = \"X$prev\" && test -n \"$install_override_mode\"; then\n\t    arg2=$install_override_mode\n\t    no_mode=false\n\t  fi\n\t  prev=\n\telse\n\t  dest=$arg\n\t  continue\n\tfi\n\t;;\n      esac\n\n      # Aesthetically quote the argument.\n      func_quote_for_eval \"$arg\"\n      func_append install_prog \" $func_quote_for_eval_result\"\n      if test -n \"$arg2\"; then\n\tfunc_quote_for_eval \"$arg2\"\n      fi\n      func_append install_shared_prog \" $func_quote_for_eval_result\"\n    done\n\n    test -z \"$install_prog\" && \\\n      func_fatal_help \"you must specify an install program\"\n\n    test -n \"$prev\" && \\\n      func_fatal_help \"the '$prev' option requires an argument\"\n\n    if test -n \"$install_override_mode\" && $no_mode; then\n      if $install_cp; then :; else\n\tfunc_quote_for_eval \"$install_override_mode\"\n\tfunc_append install_shared_prog \" -m $func_quote_for_eval_result\"\n      fi\n    fi\n\n    if test -z \"$files\"; then\n      if test -z \"$dest\"; then\n\tfunc_fatal_help \"no file or destination specified\"\n      else\n\tfunc_fatal_help \"you must specify a destination\"\n      fi\n    fi\n\n    # Strip any trailing slash from the destination.\n    func_stripname '' '/' \"$dest\"\n    dest=$func_stripname_result\n\n    # Check to see that the destination is a directory.\n    test -d \"$dest\" && isdir=:\n    if $isdir; then\n      destdir=$dest\n      destname=\n    else\n      func_dirname_and_basename \"$dest\" \"\" \".\"\n      destdir=$func_dirname_result\n      destname=$func_basename_result\n\n      # Not a directory, so check to see that there is only one file specified.\n      set dummy $files; shift\n      test \"$#\" -gt 1 && \\\n\tfunc_fatal_help \"'$dest' is not a directory\"\n    fi\n    case $destdir in\n    [\\\\/]* | [A-Za-z]:[\\\\/]*) ;;\n    *)\n      for file in $files; do\n\tcase $file in\n\t*.lo) ;;\n\t*)\n\t  func_fatal_help \"'$destdir' must be an absolute directory name\"\n\t  ;;\n\tesac\n      done\n      ;;\n    esac\n\n    # This variable tells wrapper scripts just to set variables rather\n    # than running their programs.\n    libtool_install_magic=$magic\n\n    staticlibs=\n    future_libdirs=\n    current_libdirs=\n    for file in $files; do\n\n      # Do each installation.\n      case $file in\n      *.$libext)\n\t# Do the static libraries later.\n\tfunc_append staticlibs \" $file\"\n\t;;\n\n      *.la)\n\tfunc_resolve_sysroot \"$file\"\n\tfile=$func_resolve_sysroot_result\n\n\t# Check to see that this really is a libtool archive.\n\tfunc_lalib_unsafe_p \"$file\" \\\n\t  || func_fatal_help \"'$file' is not a valid libtool archive\"\n\n\tlibrary_names=\n\told_library=\n\trelink_command=\n\tfunc_source \"$file\"\n\n\t# Add the libdir to current_libdirs if it is the destination.\n\tif test \"X$destdir\" = \"X$libdir\"; then\n\t  case \"$current_libdirs \" in\n\t  *\" $libdir \"*) ;;\n\t  *) func_append current_libdirs \" $libdir\" ;;\n\t  esac\n\telse\n\t  # Note the libdir as a future libdir.\n\t  case \"$future_libdirs \" in\n\t  *\" $libdir \"*) ;;\n\t  *) func_append future_libdirs \" $libdir\" ;;\n\t  esac\n\tfi\n\n\tfunc_dirname \"$file\" \"/\" \"\"\n\tdir=$func_dirname_result\n\tfunc_append dir \"$objdir\"\n\n\tif test -n \"$relink_command\"; then\n\t  # Determine the prefix the user has applied to our future dir.\n\t  inst_prefix_dir=`$ECHO \"$destdir\" | $SED -e \"s%$libdir\\$%%\"`\n\n\t  # Don't allow the user to place us outside of our expected\n\t  # location b/c this prevents finding dependent libraries that\n\t  # are installed to the same prefix.\n\t  # At present, this check doesn't affect windows .dll's that\n\t  # are installed into $libdir/../bin (currently, that works fine)\n\t  # but it's something to keep an eye on.\n\t  test \"$inst_prefix_dir\" = \"$destdir\" && \\\n\t    func_fatal_error \"error: cannot install '$file' to a directory not ending in $libdir\"\n\n\t  if test -n \"$inst_prefix_dir\"; then\n\t    # Stick the inst_prefix_dir data into the link command.\n\t    relink_command=`$ECHO \"$relink_command\" | $SED \"s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%\"`\n\t  else\n\t    relink_command=`$ECHO \"$relink_command\" | $SED \"s%@inst_prefix_dir@%%\"`\n\t  fi\n\n\t  func_warning \"relinking '$file'\"\n\t  func_show_eval \"$relink_command\" \\\n\t    'func_fatal_error \"error: relink '\\''$file'\\'' with the above command before installing it\"'\n\tfi\n\n\t# See the names of the shared library.\n\tset dummy $library_names; shift\n\tif test -n \"$1\"; then\n\t  realname=$1\n\t  shift\n\n\t  srcname=$realname\n\t  test -n \"$relink_command\" && srcname=${realname}T\n\n\t  # Install the shared library and build the symlinks.\n\t  func_show_eval \"$install_shared_prog $dir/$srcname $destdir/$realname\" \\\n\t      'exit $?'\n\t  tstripme=$stripme\n\t  case $host_os in\n\t  cygwin* | mingw* | pw32* | cegcc*)\n\t    case $realname in\n\t    *.dll.a)\n\t      tstripme=\n\t      ;;\n\t    esac\n\t    ;;\n\t  os2*)\n\t    case $realname in\n\t    *_dll.a)\n\t      tstripme=\n\t      ;;\n\t    esac\n\t    ;;\n\t  esac\n\t  if test -n \"$tstripme\" && test -n \"$striplib\"; then\n\t    func_show_eval \"$striplib $destdir/$realname\" 'exit $?'\n\t  fi\n\n\t  if test \"$#\" -gt 0; then\n\t    # Delete the old symlinks, and create new ones.\n\t    # Try 'ln -sf' first, because the 'ln' binary might depend on\n\t    # the symlink we replace!  Solaris /bin/ln does not understand -f,\n\t    # so we also need to try rm && ln -s.\n\t    for linkname\n\t    do\n\t      test \"$linkname\" != \"$realname\" \\\n\t\t&& func_show_eval \"(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })\"\n\t    done\n\t  fi\n\n\t  # Do each command in the postinstall commands.\n\t  lib=$destdir/$realname\n\t  func_execute_cmds \"$postinstall_cmds\" 'exit $?'\n\tfi\n\n\t# Install the pseudo-library for information purposes.\n\tfunc_basename \"$file\"\n\tname=$func_basename_result\n\tinstname=$dir/${name}i\n\tfunc_show_eval \"$install_prog $instname $destdir/$name\" 'exit $?'\n\n\t# Maybe install the static library, too.\n\ttest -n \"$old_library\" && func_append staticlibs \" $dir/$old_library\"\n\t;;\n\n      *.lo)\n\t# Install (i.e. copy) a libtool object.\n\n\t# Figure out destination file name, if it wasn't already specified.\n\tif test -n \"$destname\"; then\n\t  destfile=$destdir/$destname\n\telse\n\t  func_basename \"$file\"\n\t  destfile=$func_basename_result\n\t  destfile=$destdir/$destfile\n\tfi\n\n\t# Deduce the name of the destination old-style object file.\n\tcase $destfile in\n\t*.lo)\n\t  func_lo2o \"$destfile\"\n\t  staticdest=$func_lo2o_result\n\t  ;;\n\t*.$objext)\n\t  staticdest=$destfile\n\t  destfile=\n\t  ;;\n\t*)\n\t  func_fatal_help \"cannot copy a libtool object to '$destfile'\"\n\t  ;;\n\tesac\n\n\t# Install the libtool object if requested.\n\ttest -n \"$destfile\" && \\\n\t  func_show_eval \"$install_prog $file $destfile\" 'exit $?'\n\n\t# Install the old object if enabled.\n\tif test yes = \"$build_old_libs\"; then\n\t  # Deduce the name of the old-style object file.\n\t  func_lo2o \"$file\"\n\t  staticobj=$func_lo2o_result\n\t  func_show_eval \"$install_prog \\$staticobj \\$staticdest\" 'exit $?'\n\tfi\n\texit $EXIT_SUCCESS\n\t;;\n\n      *)\n\t# Figure out destination file name, if it wasn't already specified.\n\tif test -n \"$destname\"; then\n\t  destfile=$destdir/$destname\n\telse\n\t  func_basename \"$file\"\n\t  destfile=$func_basename_result\n\t  destfile=$destdir/$destfile\n\tfi\n\n\t# If the file is missing, and there is a .exe on the end, strip it\n\t# because it is most likely a libtool script we actually want to\n\t# install\n\tstripped_ext=\n\tcase $file in\n\t  *.exe)\n\t    if test ! -f \"$file\"; then\n\t      func_stripname '' '.exe' \"$file\"\n\t      file=$func_stripname_result\n\t      stripped_ext=.exe\n\t    fi\n\t    ;;\n\tesac\n\n\t# Do a test to see if this is really a libtool program.\n\tcase $host in\n\t*cygwin* | *mingw*)\n\t    if func_ltwrapper_executable_p \"$file\"; then\n\t      func_ltwrapper_scriptname \"$file\"\n\t      wrapper=$func_ltwrapper_scriptname_result\n\t    else\n\t      func_stripname '' '.exe' \"$file\"\n\t      wrapper=$func_stripname_result\n\t    fi\n\t    ;;\n\t*)\n\t    wrapper=$file\n\t    ;;\n\tesac\n\tif func_ltwrapper_script_p \"$wrapper\"; then\n\t  notinst_deplibs=\n\t  relink_command=\n\n\t  func_source \"$wrapper\"\n\n\t  # Check the variables that should have been set.\n\t  test -z \"$generated_by_libtool_version\" && \\\n\t    func_fatal_error \"invalid libtool wrapper script '$wrapper'\"\n\n\t  finalize=:\n\t  for lib in $notinst_deplibs; do\n\t    # Check to see that each library is installed.\n\t    libdir=\n\t    if test -f \"$lib\"; then\n\t      func_source \"$lib\"\n\t    fi\n\t    libfile=$libdir/`$ECHO \"$lib\" | $SED 's%^.*/%%g'`\n\t    if test -n \"$libdir\" && test ! -f \"$libfile\"; then\n\t      func_warning \"'$lib' has not been installed in '$libdir'\"\n\t      finalize=false\n\t    fi\n\t  done\n\n\t  relink_command=\n\t  func_source \"$wrapper\"\n\n\t  outputname=\n\t  if test no = \"$fast_install\" && test -n \"$relink_command\"; then\n\t    $opt_dry_run || {\n\t      if $finalize; then\n\t        tmpdir=`func_mktempdir`\n\t\tfunc_basename \"$file$stripped_ext\"\n\t\tfile=$func_basename_result\n\t        outputname=$tmpdir/$file\n\t        # Replace the output file specification.\n\t        relink_command=`$ECHO \"$relink_command\" | $SED 's%@OUTPUT@%'\"$outputname\"'%g'`\n\n\t        $opt_quiet || {\n\t          func_quote_for_expand \"$relink_command\"\n\t\t  eval \"func_echo $func_quote_for_expand_result\"\n\t        }\n\t        if eval \"$relink_command\"; then :\n\t          else\n\t\t  func_error \"error: relink '$file' with the above command before installing it\"\n\t\t  $opt_dry_run || ${RM}r \"$tmpdir\"\n\t\t  continue\n\t        fi\n\t        file=$outputname\n\t      else\n\t        func_warning \"cannot relink '$file'\"\n\t      fi\n\t    }\n\t  else\n\t    # Install the binary that we compiled earlier.\n\t    file=`$ECHO \"$file$stripped_ext\" | $SED \"s%\\([^/]*\\)$%$objdir/\\1%\"`\n\t  fi\n\tfi\n\n\t# remove .exe since cygwin /usr/bin/install will append another\n\t# one anyway\n\tcase $install_prog,$host in\n\t*/usr/bin/install*,*cygwin*)\n\t  case $file:$destfile in\n\t  *.exe:*.exe)\n\t    # this is ok\n\t    ;;\n\t  *.exe:*)\n\t    destfile=$destfile.exe\n\t    ;;\n\t  *:*.exe)\n\t    func_stripname '' '.exe' \"$destfile\"\n\t    destfile=$func_stripname_result\n\t    ;;\n\t  esac\n\t  ;;\n\tesac\n\tfunc_show_eval \"$install_prog\\$stripme \\$file \\$destfile\" 'exit $?'\n\t$opt_dry_run || if test -n \"$outputname\"; then\n\t  ${RM}r \"$tmpdir\"\n\tfi\n\t;;\n      esac\n    done\n\n    for file in $staticlibs; do\n      func_basename \"$file\"\n      name=$func_basename_result\n\n      # Set up the ranlib parameters.\n      oldlib=$destdir/$name\n      func_to_tool_file \"$oldlib\" func_convert_file_msys_to_w32\n      tool_oldlib=$func_to_tool_file_result\n\n      func_show_eval \"$install_prog \\$file \\$oldlib\" 'exit $?'\n\n      if test -n \"$stripme\" && test -n \"$old_striplib\"; then\n\tfunc_show_eval \"$old_striplib $tool_oldlib\" 'exit $?'\n      fi\n\n      # Do each command in the postinstall commands.\n      func_execute_cmds \"$old_postinstall_cmds\" 'exit $?'\n    done\n\n    test -n \"$future_libdirs\" && \\\n      func_warning \"remember to run '$progname --finish$future_libdirs'\"\n\n    if test -n \"$current_libdirs\"; then\n      # Maybe just do a dry run.\n      $opt_dry_run && current_libdirs=\" -n$current_libdirs\"\n      exec_cmd='$SHELL \"$progpath\" $preserve_args --finish$current_libdirs'\n    else\n      exit $EXIT_SUCCESS\n    fi\n}\n\ntest install = \"$opt_mode\" && func_mode_install ${1+\"$@\"}\n\n\n# func_generate_dlsyms outputname originator pic_p\n# Extract symbols from dlprefiles and create ${outputname}S.o with\n# a dlpreopen symbol table.\nfunc_generate_dlsyms ()\n{\n    $debug_cmd\n\n    my_outputname=$1\n    my_originator=$2\n    my_pic_p=${3-false}\n    my_prefix=`$ECHO \"$my_originator\" | $SED 's%[^a-zA-Z0-9]%_%g'`\n    my_dlsyms=\n\n    if test -n \"$dlfiles$dlprefiles\" || test no != \"$dlself\"; then\n      if test -n \"$NM\" && test -n \"$global_symbol_pipe\"; then\n\tmy_dlsyms=${my_outputname}S.c\n      else\n\tfunc_error \"not configured to extract global symbols from dlpreopened files\"\n      fi\n    fi\n\n    if test -n \"$my_dlsyms\"; then\n      case $my_dlsyms in\n      \"\") ;;\n      *.c)\n\t# Discover the nlist of each of the dlfiles.\n\tnlist=$output_objdir/$my_outputname.nm\n\n\tfunc_show_eval \"$RM $nlist ${nlist}S ${nlist}T\"\n\n\t# Parse the name list into a source file.\n\tfunc_verbose \"creating $output_objdir/$my_dlsyms\"\n\n\t$opt_dry_run || $ECHO > \"$output_objdir/$my_dlsyms\" \"\\\n/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */\n/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */\n\n#ifdef __cplusplus\nextern \\\"C\\\" {\n#endif\n\n#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))\n#pragma GCC diagnostic ignored \\\"-Wstrict-prototypes\\\"\n#endif\n\n/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */\n#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE\n/* DATA imports from DLLs on WIN32 can't be const, because runtime\n   relocations are performed -- see ld's documentation on pseudo-relocs.  */\n# define LT_DLSYM_CONST\n#elif defined __osf__\n/* This system does not cope well with relocations in const data.  */\n# define LT_DLSYM_CONST\n#else\n# define LT_DLSYM_CONST const\n#endif\n\n#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)\n\n/* External symbol declarations for the compiler. */\\\n\"\n\n\tif test yes = \"$dlself\"; then\n\t  func_verbose \"generating symbol list for '$output'\"\n\n\t  $opt_dry_run || echo ': @PROGRAM@ ' > \"$nlist\"\n\n\t  # Add our own program objects to the symbol list.\n\t  progfiles=`$ECHO \"$objs$old_deplibs\" | $SP2NL | $SED \"$lo2o\" | $NL2SP`\n\t  for progfile in $progfiles; do\n\t    func_to_tool_file \"$progfile\" func_convert_file_msys_to_w32\n\t    func_verbose \"extracting global C symbols from '$func_to_tool_file_result'\"\n\t    $opt_dry_run || eval \"$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'\"\n\t  done\n\n\t  if test -n \"$exclude_expsyms\"; then\n\t    $opt_dry_run || {\n\t      eval '$EGREP -v \" ($exclude_expsyms)$\" \"$nlist\" > \"$nlist\"T'\n\t      eval '$MV \"$nlist\"T \"$nlist\"'\n\t    }\n\t  fi\n\n\t  if test -n \"$export_symbols_regex\"; then\n\t    $opt_dry_run || {\n\t      eval '$EGREP -e \"$export_symbols_regex\" \"$nlist\" > \"$nlist\"T'\n\t      eval '$MV \"$nlist\"T \"$nlist\"'\n\t    }\n\t  fi\n\n\t  # Prepare the list of exported symbols\n\t  if test -z \"$export_symbols\"; then\n\t    export_symbols=$output_objdir/$outputname.exp\n\t    $opt_dry_run || {\n\t      $RM $export_symbols\n\t      eval \"$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \\(.*\\)$/\\1/p' \"'< \"$nlist\" > \"$export_symbols\"'\n\t      case $host in\n\t      *cygwin* | *mingw* | *cegcc* )\n                eval \"echo EXPORTS \"'> \"$output_objdir/$outputname.def\"'\n                eval 'cat \"$export_symbols\" >> \"$output_objdir/$outputname.def\"'\n\t        ;;\n\t      esac\n\t    }\n\t  else\n\t    $opt_dry_run || {\n\t      eval \"$SED -e 's/\\([].[*^$]\\)/\\\\\\\\\\1/g' -e 's/^/ /' -e 's/$/$/'\"' < \"$export_symbols\" > \"$output_objdir/$outputname.exp\"'\n\t      eval '$GREP -f \"$output_objdir/$outputname.exp\" < \"$nlist\" > \"$nlist\"T'\n\t      eval '$MV \"$nlist\"T \"$nlist\"'\n\t      case $host in\n\t        *cygwin* | *mingw* | *cegcc* )\n\t          eval \"echo EXPORTS \"'> \"$output_objdir/$outputname.def\"'\n\t          eval 'cat \"$nlist\" >> \"$output_objdir/$outputname.def\"'\n\t          ;;\n\t      esac\n\t    }\n\t  fi\n\tfi\n\n\tfor dlprefile in $dlprefiles; do\n\t  func_verbose \"extracting global C symbols from '$dlprefile'\"\n\t  func_basename \"$dlprefile\"\n\t  name=$func_basename_result\n          case $host in\n\t    *cygwin* | *mingw* | *cegcc* )\n\t      # if an import library, we need to obtain dlname\n\t      if func_win32_import_lib_p \"$dlprefile\"; then\n\t        func_tr_sh \"$dlprefile\"\n\t        eval \"curr_lafile=\\$libfile_$func_tr_sh_result\"\n\t        dlprefile_dlbasename=\n\t        if test -n \"$curr_lafile\" && func_lalib_p \"$curr_lafile\"; then\n\t          # Use subshell, to avoid clobbering current variable values\n\t          dlprefile_dlname=`source \"$curr_lafile\" && echo \"$dlname\"`\n\t          if test -n \"$dlprefile_dlname\"; then\n\t            func_basename \"$dlprefile_dlname\"\n\t            dlprefile_dlbasename=$func_basename_result\n\t          else\n\t            # no lafile. user explicitly requested -dlpreopen <import library>.\n\t            $sharedlib_from_linklib_cmd \"$dlprefile\"\n\t            dlprefile_dlbasename=$sharedlib_from_linklib_result\n\t          fi\n\t        fi\n\t        $opt_dry_run || {\n\t          if test -n \"$dlprefile_dlbasename\"; then\n\t            eval '$ECHO \": $dlprefile_dlbasename\" >> \"$nlist\"'\n\t          else\n\t            func_warning \"Could not compute DLL name from $name\"\n\t            eval '$ECHO \": $name \" >> \"$nlist\"'\n\t          fi\n\t          func_to_tool_file \"$dlprefile\" func_convert_file_msys_to_w32\n\t          eval \"$NM \\\"$func_to_tool_file_result\\\" 2>/dev/null | $global_symbol_pipe |\n\t            $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'\"\n\t        }\n\t      else # not an import lib\n\t        $opt_dry_run || {\n\t          eval '$ECHO \": $name \" >> \"$nlist\"'\n\t          func_to_tool_file \"$dlprefile\" func_convert_file_msys_to_w32\n\t          eval \"$NM \\\"$func_to_tool_file_result\\\" 2>/dev/null | $global_symbol_pipe >> '$nlist'\"\n\t        }\n\t      fi\n\t    ;;\n\t    *)\n\t      $opt_dry_run || {\n\t        eval '$ECHO \": $name \" >> \"$nlist\"'\n\t        func_to_tool_file \"$dlprefile\" func_convert_file_msys_to_w32\n\t        eval \"$NM \\\"$func_to_tool_file_result\\\" 2>/dev/null | $global_symbol_pipe >> '$nlist'\"\n\t      }\n\t    ;;\n          esac\n\tdone\n\n\t$opt_dry_run || {\n\t  # Make sure we have at least an empty file.\n\t  test -f \"$nlist\" || : > \"$nlist\"\n\n\t  if test -n \"$exclude_expsyms\"; then\n\t    $EGREP -v \" ($exclude_expsyms)$\" \"$nlist\" > \"$nlist\"T\n\t    $MV \"$nlist\"T \"$nlist\"\n\t  fi\n\n\t  # Try sorting and uniquifying the output.\n\t  if $GREP -v \"^: \" < \"$nlist\" |\n\t      if sort -k 3 </dev/null >/dev/null 2>&1; then\n\t\tsort -k 3\n\t      else\n\t\tsort +2\n\t      fi |\n\t      uniq > \"$nlist\"S; then\n\t    :\n\t  else\n\t    $GREP -v \"^: \" < \"$nlist\" > \"$nlist\"S\n\t  fi\n\n\t  if test -f \"$nlist\"S; then\n\t    eval \"$global_symbol_to_cdecl\"' < \"$nlist\"S >> \"$output_objdir/$my_dlsyms\"'\n\t  else\n\t    echo '/* NONE */' >> \"$output_objdir/$my_dlsyms\"\n\t  fi\n\n\t  func_show_eval '$RM \"${nlist}I\"'\n\t  if test -n \"$global_symbol_to_import\"; then\n\t    eval \"$global_symbol_to_import\"' < \"$nlist\"S > \"$nlist\"I'\n\t  fi\n\n\t  echo >> \"$output_objdir/$my_dlsyms\" \"\\\n\n/* The mapping between symbol names and symbols.  */\ntypedef struct {\n  const char *name;\n  void *address;\n} lt_dlsymlist;\nextern LT_DLSYM_CONST lt_dlsymlist\nlt_${my_prefix}_LTX_preloaded_symbols[];\\\n\"\n\n\t  if test -s \"$nlist\"I; then\n\t    echo >> \"$output_objdir/$my_dlsyms\" \"\\\nstatic void lt_syminit(void)\n{\n  LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols;\n  for (; symbol->name; ++symbol)\n    {\"\n\t    $SED 's/.*/      if (STREQ (symbol->name, \\\"&\\\")) symbol->address = (void *) \\&&;/' < \"$nlist\"I >> \"$output_objdir/$my_dlsyms\"\n\t    echo >> \"$output_objdir/$my_dlsyms\" \"\\\n    }\n}\"\n\t  fi\n\t  echo >> \"$output_objdir/$my_dlsyms\" \"\\\nLT_DLSYM_CONST lt_dlsymlist\nlt_${my_prefix}_LTX_preloaded_symbols[] =\n{ {\\\"$my_originator\\\", (void *) 0},\"\n\n\t  if test -s \"$nlist\"I; then\n\t    echo >> \"$output_objdir/$my_dlsyms\" \"\\\n  {\\\"@INIT@\\\", (void *) &lt_syminit},\"\n\t  fi\n\n\t  case $need_lib_prefix in\n\t  no)\n\t    eval \"$global_symbol_to_c_name_address\" < \"$nlist\" >> \"$output_objdir/$my_dlsyms\"\n\t    ;;\n\t  *)\n\t    eval \"$global_symbol_to_c_name_address_lib_prefix\" < \"$nlist\" >> \"$output_objdir/$my_dlsyms\"\n\t    ;;\n\t  esac\n\t  echo >> \"$output_objdir/$my_dlsyms\" \"\\\n  {0, (void *) 0}\n};\n\n/* This works around a problem in FreeBSD linker */\n#ifdef FREEBSD_WORKAROUND\nstatic const void *lt_preloaded_setup() {\n  return lt_${my_prefix}_LTX_preloaded_symbols;\n}\n#endif\n\n#ifdef __cplusplus\n}\n#endif\\\n\"\n\t} # !$opt_dry_run\n\n\tpic_flag_for_symtable=\n\tcase \"$compile_command \" in\n\t*\" -static \"*) ;;\n\t*)\n\t  case $host in\n\t  # compiling the symbol table file with pic_flag works around\n\t  # a FreeBSD bug that causes programs to crash when -lm is\n\t  # linked before any other PIC object.  But we must not use\n\t  # pic_flag when linking with -static.  The problem exists in\n\t  # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.\n\t  *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)\n\t    pic_flag_for_symtable=\" $pic_flag -DFREEBSD_WORKAROUND\" ;;\n\t  *-*-hpux*)\n\t    pic_flag_for_symtable=\" $pic_flag\"  ;;\n\t  *)\n\t    $my_pic_p && pic_flag_for_symtable=\" $pic_flag\"\n\t    ;;\n\t  esac\n\t  ;;\n\tesac\n\tsymtab_cflags=\n\tfor arg in $LTCFLAGS; do\n\t  case $arg in\n\t  -pie | -fpie | -fPIE) ;;\n\t  *) func_append symtab_cflags \" $arg\" ;;\n\t  esac\n\tdone\n\n\t# Now compile the dynamic symbol file.\n\tfunc_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable \"$my_dlsyms\")' 'exit $?'\n\n\t# Clean up the generated files.\n\tfunc_show_eval '$RM \"$output_objdir/$my_dlsyms\" \"$nlist\" \"${nlist}S\" \"${nlist}T\" \"${nlist}I\"'\n\n\t# Transform the symbol file into the correct name.\n\tsymfileobj=$output_objdir/${my_outputname}S.$objext\n\tcase $host in\n\t*cygwin* | *mingw* | *cegcc* )\n\t  if test -f \"$output_objdir/$my_outputname.def\"; then\n\t    compile_command=`$ECHO \"$compile_command\" | $SED \"s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%\"`\n\t    finalize_command=`$ECHO \"$finalize_command\" | $SED \"s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%\"`\n\t  else\n\t    compile_command=`$ECHO \"$compile_command\" | $SED \"s%@SYMFILE@%$symfileobj%\"`\n\t    finalize_command=`$ECHO \"$finalize_command\" | $SED \"s%@SYMFILE@%$symfileobj%\"`\n\t  fi\n\t  ;;\n\t*)\n\t  compile_command=`$ECHO \"$compile_command\" | $SED \"s%@SYMFILE@%$symfileobj%\"`\n\t  finalize_command=`$ECHO \"$finalize_command\" | $SED \"s%@SYMFILE@%$symfileobj%\"`\n\t  ;;\n\tesac\n\t;;\n      *)\n\tfunc_fatal_error \"unknown suffix for '$my_dlsyms'\"\n\t;;\n      esac\n    else\n      # We keep going just in case the user didn't refer to\n      # lt_preloaded_symbols.  The linker will fail if global_symbol_pipe\n      # really was required.\n\n      # Nullify the symbol file.\n      compile_command=`$ECHO \"$compile_command\" | $SED \"s% @SYMFILE@%%\"`\n      finalize_command=`$ECHO \"$finalize_command\" | $SED \"s% @SYMFILE@%%\"`\n    fi\n}\n\n# func_cygming_gnu_implib_p ARG\n# This predicate returns with zero status (TRUE) if\n# ARG is a GNU/binutils-style import library. Returns\n# with nonzero status (FALSE) otherwise.\nfunc_cygming_gnu_implib_p ()\n{\n  $debug_cmd\n\n  func_to_tool_file \"$1\" func_convert_file_msys_to_w32\n  func_cygming_gnu_implib_tmp=`$NM \"$func_to_tool_file_result\" | eval \"$global_symbol_pipe\" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`\n  test -n \"$func_cygming_gnu_implib_tmp\"\n}\n\n# func_cygming_ms_implib_p ARG\n# This predicate returns with zero status (TRUE) if\n# ARG is an MS-style import library. Returns\n# with nonzero status (FALSE) otherwise.\nfunc_cygming_ms_implib_p ()\n{\n  $debug_cmd\n\n  func_to_tool_file \"$1\" func_convert_file_msys_to_w32\n  func_cygming_ms_implib_tmp=`$NM \"$func_to_tool_file_result\" | eval \"$global_symbol_pipe\" | $GREP '_NULL_IMPORT_DESCRIPTOR'`\n  test -n \"$func_cygming_ms_implib_tmp\"\n}\n\n# func_win32_libid arg\n# return the library type of file 'arg'\n#\n# Need a lot of goo to handle *both* DLLs and import libs\n# Has to be a shell function in order to 'eat' the argument\n# that is supplied when $file_magic_command is called.\n# Despite the name, also deal with 64 bit binaries.\nfunc_win32_libid ()\n{\n  $debug_cmd\n\n  win32_libid_type=unknown\n  win32_fileres=`file -L $1 2>/dev/null`\n  case $win32_fileres in\n  *ar\\ archive\\ import\\ library*) # definitely import\n    win32_libid_type=\"x86 archive import\"\n    ;;\n  *ar\\ archive*) # could be an import, or static\n    # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.\n    if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |\n       $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then\n      case $nm_interface in\n      \"MS dumpbin\")\n\tif func_cygming_ms_implib_p \"$1\" ||\n\t   func_cygming_gnu_implib_p \"$1\"\n\tthen\n\t  win32_nmres=import\n\telse\n\t  win32_nmres=\n\tfi\n\t;;\n      *)\n\tfunc_to_tool_file \"$1\" func_convert_file_msys_to_w32\n\twin32_nmres=`eval $NM -f posix -A \\\"$func_to_tool_file_result\\\" |\n\t  $SED -n -e '\n\t    1,100{\n\t\t/ I /{\n\t\t    s|.*|import|\n\t\t    p\n\t\t    q\n\t\t}\n\t    }'`\n\t;;\n      esac\n      case $win32_nmres in\n      import*)  win32_libid_type=\"x86 archive import\";;\n      *)        win32_libid_type=\"x86 archive static\";;\n      esac\n    fi\n    ;;\n  *DLL*)\n    win32_libid_type=\"x86 DLL\"\n    ;;\n  *executable*) # but shell scripts are \"executable\" too...\n    case $win32_fileres in\n    *MS\\ Windows\\ PE\\ Intel*)\n      win32_libid_type=\"x86 DLL\"\n      ;;\n    esac\n    ;;\n  esac\n  $ECHO \"$win32_libid_type\"\n}\n\n# func_cygming_dll_for_implib ARG\n#\n# Platform-specific function to extract the\n# name of the DLL associated with the specified\n# import library ARG.\n# Invoked by eval'ing the libtool variable\n#    $sharedlib_from_linklib_cmd\n# Result is available in the variable\n#    $sharedlib_from_linklib_result\nfunc_cygming_dll_for_implib ()\n{\n  $debug_cmd\n\n  sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify \"$1\"`\n}\n\n# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs\n#\n# The is the core of a fallback implementation of a\n# platform-specific function to extract the name of the\n# DLL associated with the specified import library LIBNAME.\n#\n# SECTION_NAME is either .idata$6 or .idata$7, depending\n# on the platform and compiler that created the implib.\n#\n# Echos the name of the DLL associated with the\n# specified import library.\nfunc_cygming_dll_for_implib_fallback_core ()\n{\n  $debug_cmd\n\n  match_literal=`$ECHO \"$1\" | $SED \"$sed_make_literal_regex\"`\n  $OBJDUMP -s --section \"$1\" \"$2\" 2>/dev/null |\n    $SED '/^Contents of section '\"$match_literal\"':/{\n      # Place marker at beginning of archive member dllname section\n      s/.*/====MARK====/\n      p\n      d\n    }\n    # These lines can sometimes be longer than 43 characters, but\n    # are always uninteresting\n    /:[\t ]*file format pe[i]\\{,1\\}-/d\n    /^In archive [^:]*:/d\n    # Ensure marker is printed\n    /^====MARK====/p\n    # Remove all lines with less than 43 characters\n    /^.\\{43\\}/!d\n    # From remaining lines, remove first 43 characters\n    s/^.\\{43\\}//' |\n    $SED -n '\n      # Join marker and all lines until next marker into a single line\n      /^====MARK====/ b para\n      H\n      $ b para\n      b\n      :para\n      x\n      s/\\n//g\n      # Remove the marker\n      s/^====MARK====//\n      # Remove trailing dots and whitespace\n      s/[\\. \\t]*$//\n      # Print\n      /./p' |\n    # we now have a list, one entry per line, of the stringified\n    # contents of the appropriate section of all members of the\n    # archive that possess that section. Heuristic: eliminate\n    # all those that have a first or second character that is\n    # a '.' (that is, objdump's representation of an unprintable\n    # character.) This should work for all archives with less than\n    # 0x302f exports -- but will fail for DLLs whose name actually\n    # begins with a literal '.' or a single character followed by\n    # a '.'.\n    #\n    # Of those that remain, print the first one.\n    $SED -e '/^\\./d;/^.\\./d;q'\n}\n\n# func_cygming_dll_for_implib_fallback ARG\n# Platform-specific function to extract the\n# name of the DLL associated with the specified\n# import library ARG.\n#\n# This fallback implementation is for use when $DLLTOOL\n# does not support the --identify-strict option.\n# Invoked by eval'ing the libtool variable\n#    $sharedlib_from_linklib_cmd\n# Result is available in the variable\n#    $sharedlib_from_linklib_result\nfunc_cygming_dll_for_implib_fallback ()\n{\n  $debug_cmd\n\n  if func_cygming_gnu_implib_p \"$1\"; then\n    # binutils import library\n    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' \"$1\"`\n  elif func_cygming_ms_implib_p \"$1\"; then\n    # ms-generated import library\n    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' \"$1\"`\n  else\n    # unknown\n    sharedlib_from_linklib_result=\n  fi\n}\n\n\n# func_extract_an_archive dir oldlib\nfunc_extract_an_archive ()\n{\n    $debug_cmd\n\n    f_ex_an_ar_dir=$1; shift\n    f_ex_an_ar_oldlib=$1\n    if test yes = \"$lock_old_archive_extraction\"; then\n      lockfile=$f_ex_an_ar_oldlib.lock\n      until $opt_dry_run || ln \"$progpath\" \"$lockfile\" 2>/dev/null; do\n\tfunc_echo \"Waiting for $lockfile to be removed\"\n\tsleep 2\n      done\n    fi\n    func_show_eval \"(cd \\$f_ex_an_ar_dir && $AR x \\\"\\$f_ex_an_ar_oldlib\\\")\" \\\n\t\t   'stat=$?; rm -f \"$lockfile\"; exit $stat'\n    if test yes = \"$lock_old_archive_extraction\"; then\n      $opt_dry_run || rm -f \"$lockfile\"\n    fi\n    if ($AR t \"$f_ex_an_ar_oldlib\" | sort | sort -uc >/dev/null 2>&1); then\n     :\n    else\n      func_fatal_error \"object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib\"\n    fi\n}\n\n\n# func_extract_archives gentop oldlib ...\nfunc_extract_archives ()\n{\n    $debug_cmd\n\n    my_gentop=$1; shift\n    my_oldlibs=${1+\"$@\"}\n    my_oldobjs=\n    my_xlib=\n    my_xabs=\n    my_xdir=\n\n    for my_xlib in $my_oldlibs; do\n      # Extract the objects.\n      case $my_xlib in\n\t[\\\\/]* | [A-Za-z]:[\\\\/]*) my_xabs=$my_xlib ;;\n\t*) my_xabs=`pwd`\"/$my_xlib\" ;;\n      esac\n      func_basename \"$my_xlib\"\n      my_xlib=$func_basename_result\n      my_xlib_u=$my_xlib\n      while :; do\n        case \" $extracted_archives \" in\n\t*\" $my_xlib_u \"*)\n\t  func_arith $extracted_serial + 1\n\t  extracted_serial=$func_arith_result\n\t  my_xlib_u=lt$extracted_serial-$my_xlib ;;\n\t*) break ;;\n\tesac\n      done\n      extracted_archives=\"$extracted_archives $my_xlib_u\"\n      my_xdir=$my_gentop/$my_xlib_u\n\n      func_mkdir_p \"$my_xdir\"\n\n      case $host in\n      *-darwin*)\n\tfunc_verbose \"Extracting $my_xabs\"\n\t# Do not bother doing anything if just a dry run\n\t$opt_dry_run || {\n\t  darwin_orig_dir=`pwd`\n\t  cd $my_xdir || exit $?\n\t  darwin_archive=$my_xabs\n\t  darwin_curdir=`pwd`\n\t  func_basename \"$darwin_archive\"\n\t  darwin_base_archive=$func_basename_result\n\t  darwin_arches=`$LIPO -info \"$darwin_archive\" 2>/dev/null | $GREP Architectures 2>/dev/null || true`\n\t  if test -n \"$darwin_arches\"; then\n\t    darwin_arches=`$ECHO \"$darwin_arches\" | $SED -e 's/.*are://'`\n\t    darwin_arch=\n\t    func_verbose \"$darwin_base_archive has multiple architectures $darwin_arches\"\n\t    for darwin_arch in  $darwin_arches; do\n\t      func_mkdir_p \"unfat-$$/$darwin_base_archive-$darwin_arch\"\n\t      $LIPO -thin $darwin_arch -output \"unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive\" \"$darwin_archive\"\n\t      cd \"unfat-$$/$darwin_base_archive-$darwin_arch\"\n\t      func_extract_an_archive \"`pwd`\" \"$darwin_base_archive\"\n\t      cd \"$darwin_curdir\"\n\t      $RM \"unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive\"\n\t    done # $darwin_arches\n            ## Okay now we've a bunch of thin objects, gotta fatten them up :)\n\t    darwin_filelist=`find unfat-$$ -type f -name \\*.o -print -o -name \\*.lo -print | $SED -e \"$sed_basename\" | sort -u`\n\t    darwin_file=\n\t    darwin_files=\n\t    for darwin_file in $darwin_filelist; do\n\t      darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`\n\t      $LIPO -create -output \"$darwin_file\" $darwin_files\n\t    done # $darwin_filelist\n\t    $RM -rf unfat-$$\n\t    cd \"$darwin_orig_dir\"\n\t  else\n\t    cd $darwin_orig_dir\n\t    func_extract_an_archive \"$my_xdir\" \"$my_xabs\"\n\t  fi # $darwin_arches\n\t} # !$opt_dry_run\n\t;;\n      *)\n        func_extract_an_archive \"$my_xdir\" \"$my_xabs\"\n\t;;\n      esac\n      my_oldobjs=\"$my_oldobjs \"`find $my_xdir -name \\*.$objext -print -o -name \\*.lo -print | sort | $NL2SP`\n    done\n\n    func_extract_archives_result=$my_oldobjs\n}\n\n\n# func_emit_wrapper [arg=no]\n#\n# Emit a libtool wrapper script on stdout.\n# Don't directly open a file because we may want to\n# incorporate the script contents within a cygwin/mingw\n# wrapper executable.  Must ONLY be called from within\n# func_mode_link because it depends on a number of variables\n# set therein.\n#\n# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\n# variable will take.  If 'yes', then the emitted script\n# will assume that the directory where it is stored is\n# the $objdir directory.  This is a cygwin/mingw-specific\n# behavior.\nfunc_emit_wrapper ()\n{\n\tfunc_emit_wrapper_arg1=${1-no}\n\n\t$ECHO \"\\\n#! $SHELL\n\n# $output - temporary wrapper script for $objdir/$outputname\n# Generated by $PROGRAM (GNU $PACKAGE) $VERSION\n#\n# The $output program cannot be directly executed until all the libtool\n# libraries that it depends on are installed.\n#\n# This wrapper script should never be moved out of the build directory.\n# If it is, it will not operate correctly.\n\n# Sed substitution that helps us do robust quoting.  It backslashifies\n# metacharacters that are still active within double-quoted strings.\nsed_quote_subst='$sed_quote_subst'\n\n# Be Bourne compatible\nif test -n \\\"\\${ZSH_VERSION+set}\\\" && (emulate sh) >/dev/null 2>&1; then\n  emulate sh\n  NULLCMD=:\n  # Zsh 3.x and 4.x performs word splitting on \\${1+\\\"\\$@\\\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '\\${1+\\\"\\$@\\\"}'='\\\"\\$@\\\"'\n  setopt NO_GLOB_SUBST\nelse\n  case \\`(set -o) 2>/dev/null\\` in *posix*) set -o posix;; esac\nfi\nBIN_SH=xpg4; export BIN_SH # for Tru64\nDUALCASE=1; export DUALCASE # for MKS sh\n\n# The HP-UX ksh and POSIX shell print the target directory to stdout\n# if CDPATH is set.\n(unset CDPATH) >/dev/null 2>&1 && unset CDPATH\n\nrelink_command=\\\"$relink_command\\\"\n\n# This environment variable determines our operation mode.\nif test \\\"\\$libtool_install_magic\\\" = \\\"$magic\\\"; then\n  # install mode needs the following variables:\n  generated_by_libtool_version='$macro_version'\n  notinst_deplibs='$notinst_deplibs'\nelse\n  # When we are sourced in execute mode, \\$file and \\$ECHO are already set.\n  if test \\\"\\$libtool_execute_magic\\\" != \\\"$magic\\\"; then\n    file=\\\"\\$0\\\"\"\n\n    qECHO=`$ECHO \"$ECHO\" | $SED \"$sed_quote_subst\"`\n    $ECHO \"\\\n\n# A function that is used when there is no print builtin or printf.\nfunc_fallback_echo ()\n{\n  eval 'cat <<_LTECHO_EOF\n\\$1\n_LTECHO_EOF'\n}\n    ECHO=\\\"$qECHO\\\"\n  fi\n\n# Very basic option parsing. These options are (a) specific to\n# the libtool wrapper, (b) are identical between the wrapper\n# /script/ and the wrapper /executable/ that is used only on\n# windows platforms, and (c) all begin with the string \"--lt-\"\n# (application programs are unlikely to have options that match\n# this pattern).\n#\n# There are only two supported options: --lt-debug and\n# --lt-dump-script. There is, deliberately, no --lt-help.\n#\n# The first argument to this parsing function should be the\n# script's $0 value, followed by \"$@\".\nlt_option_debug=\nfunc_parse_lt_options ()\n{\n  lt_script_arg0=\\$0\n  shift\n  for lt_opt\n  do\n    case \\\"\\$lt_opt\\\" in\n    --lt-debug) lt_option_debug=1 ;;\n    --lt-dump-script)\n        lt_dump_D=\\`\\$ECHO \\\"X\\$lt_script_arg0\\\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\\`\n        test \\\"X\\$lt_dump_D\\\" = \\\"X\\$lt_script_arg0\\\" && lt_dump_D=.\n        lt_dump_F=\\`\\$ECHO \\\"X\\$lt_script_arg0\\\" | $SED -e 's/^X//' -e 's%^.*/%%'\\`\n        cat \\\"\\$lt_dump_D/\\$lt_dump_F\\\"\n        exit 0\n      ;;\n    --lt-*)\n        \\$ECHO \\\"Unrecognized --lt- option: '\\$lt_opt'\\\" 1>&2\n        exit 1\n      ;;\n    esac\n  done\n\n  # Print the debug banner immediately:\n  if test -n \\\"\\$lt_option_debug\\\"; then\n    echo \\\"$outputname:$output:\\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\\\" 1>&2\n  fi\n}\n\n# Used when --lt-debug. Prints its arguments to stdout\n# (redirection is the responsibility of the caller)\nfunc_lt_dump_args ()\n{\n  lt_dump_args_N=1;\n  for lt_arg\n  do\n    \\$ECHO \\\"$outputname:$output:\\$LINENO: newargv[\\$lt_dump_args_N]: \\$lt_arg\\\"\n    lt_dump_args_N=\\`expr \\$lt_dump_args_N + 1\\`\n  done\n}\n\n# Core function for launching the target application\nfunc_exec_program_core ()\n{\n\"\n  case $host in\n  # Backslashes separate directories on plain windows\n  *-*-mingw | *-*-os2* | *-cegcc*)\n    $ECHO \"\\\n      if test -n \\\"\\$lt_option_debug\\\"; then\n        \\$ECHO \\\"$outputname:$output:\\$LINENO: newargv[0]: \\$progdir\\\\\\\\\\$program\\\" 1>&2\n        func_lt_dump_args \\${1+\\\"\\$@\\\"} 1>&2\n      fi\n      exec \\\"\\$progdir\\\\\\\\\\$program\\\" \\${1+\\\"\\$@\\\"}\n\"\n    ;;\n\n  *)\n    $ECHO \"\\\n      if test -n \\\"\\$lt_option_debug\\\"; then\n        \\$ECHO \\\"$outputname:$output:\\$LINENO: newargv[0]: \\$progdir/\\$program\\\" 1>&2\n        func_lt_dump_args \\${1+\\\"\\$@\\\"} 1>&2\n      fi\n      exec \\\"\\$progdir/\\$program\\\" \\${1+\\\"\\$@\\\"}\n\"\n    ;;\n  esac\n  $ECHO \"\\\n      \\$ECHO \\\"\\$0: cannot exec \\$program \\$*\\\" 1>&2\n      exit 1\n}\n\n# A function to encapsulate launching the target application\n# Strips options in the --lt-* namespace from \\$@ and\n# launches target application with the remaining arguments.\nfunc_exec_program ()\n{\n  case \\\" \\$* \\\" in\n  *\\\\ --lt-*)\n    for lt_wr_arg\n    do\n      case \\$lt_wr_arg in\n      --lt-*) ;;\n      *) set x \\\"\\$@\\\" \\\"\\$lt_wr_arg\\\"; shift;;\n      esac\n      shift\n    done ;;\n  esac\n  func_exec_program_core \\${1+\\\"\\$@\\\"}\n}\n\n  # Parse options\n  func_parse_lt_options \\\"\\$0\\\" \\${1+\\\"\\$@\\\"}\n\n  # Find the directory that this script lives in.\n  thisdir=\\`\\$ECHO \\\"\\$file\\\" | $SED 's%/[^/]*$%%'\\`\n  test \\\"x\\$thisdir\\\" = \\\"x\\$file\\\" && thisdir=.\n\n  # Follow symbolic links until we get to the real thisdir.\n  file=\\`ls -ld \\\"\\$file\\\" | $SED -n 's/.*-> //p'\\`\n  while test -n \\\"\\$file\\\"; do\n    destdir=\\`\\$ECHO \\\"\\$file\\\" | $SED 's%/[^/]*\\$%%'\\`\n\n    # If there was a directory component, then change thisdir.\n    if test \\\"x\\$destdir\\\" != \\\"x\\$file\\\"; then\n      case \\\"\\$destdir\\\" in\n      [\\\\\\\\/]* | [A-Za-z]:[\\\\\\\\/]*) thisdir=\\\"\\$destdir\\\" ;;\n      *) thisdir=\\\"\\$thisdir/\\$destdir\\\" ;;\n      esac\n    fi\n\n    file=\\`\\$ECHO \\\"\\$file\\\" | $SED 's%^.*/%%'\\`\n    file=\\`ls -ld \\\"\\$thisdir/\\$file\\\" | $SED -n 's/.*-> //p'\\`\n  done\n\n  # Usually 'no', except on cygwin/mingw when embedded into\n  # the cwrapper.\n  WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1\n  if test \\\"\\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\\\" = \\\"yes\\\"; then\n    # special case for '.'\n    if test \\\"\\$thisdir\\\" = \\\".\\\"; then\n      thisdir=\\`pwd\\`\n    fi\n    # remove .libs from thisdir\n    case \\\"\\$thisdir\\\" in\n    *[\\\\\\\\/]$objdir ) thisdir=\\`\\$ECHO \\\"\\$thisdir\\\" | $SED 's%[\\\\\\\\/][^\\\\\\\\/]*$%%'\\` ;;\n    $objdir )   thisdir=. ;;\n    esac\n  fi\n\n  # Try to get the absolute directory name.\n  absdir=\\`cd \\\"\\$thisdir\\\" && pwd\\`\n  test -n \\\"\\$absdir\\\" && thisdir=\\\"\\$absdir\\\"\n\"\n\n\tif test yes = \"$fast_install\"; then\n\t  $ECHO \"\\\n  program=lt-'$outputname'$exeext\n  progdir=\\\"\\$thisdir/$objdir\\\"\n\n  if test ! -f \\\"\\$progdir/\\$program\\\" ||\n     { file=\\`ls -1dt \\\"\\$progdir/\\$program\\\" \\\"\\$progdir/../\\$program\\\" 2>/dev/null | $SED 1q\\`; \\\\\n       test \\\"X\\$file\\\" != \\\"X\\$progdir/\\$program\\\"; }; then\n\n    file=\\\"\\$\\$-\\$program\\\"\n\n    if test ! -d \\\"\\$progdir\\\"; then\n      $MKDIR \\\"\\$progdir\\\"\n    else\n      $RM \\\"\\$progdir/\\$file\\\"\n    fi\"\n\n\t  $ECHO \"\\\n\n    # relink executable if necessary\n    if test -n \\\"\\$relink_command\\\"; then\n      if relink_command_output=\\`eval \\$relink_command 2>&1\\`; then :\n      else\n\t\\$ECHO \\\"\\$relink_command_output\\\" >&2\n\t$RM \\\"\\$progdir/\\$file\\\"\n\texit 1\n      fi\n    fi\n\n    $MV \\\"\\$progdir/\\$file\\\" \\\"\\$progdir/\\$program\\\" 2>/dev/null ||\n    { $RM \\\"\\$progdir/\\$program\\\";\n      $MV \\\"\\$progdir/\\$file\\\" \\\"\\$progdir/\\$program\\\"; }\n    $RM \\\"\\$progdir/\\$file\\\"\n  fi\"\n\telse\n\t  $ECHO \"\\\n  program='$outputname'\n  progdir=\\\"\\$thisdir/$objdir\\\"\n\"\n\tfi\n\n\t$ECHO \"\\\n\n  if test -f \\\"\\$progdir/\\$program\\\"; then\"\n\n\t# fixup the dll searchpath if we need to.\n\t#\n\t# Fix the DLL searchpath if we need to.  Do this before prepending\n\t# to shlibpath, because on Windows, both are PATH and uninstalled\n\t# libraries must come first.\n\tif test -n \"$dllsearchpath\"; then\n\t  $ECHO \"\\\n    # Add the dll search path components to the executable PATH\n    PATH=$dllsearchpath:\\$PATH\n\"\n\tfi\n\n\t# Export our shlibpath_var if we have one.\n\tif test yes = \"$shlibpath_overrides_runpath\" && test -n \"$shlibpath_var\" && test -n \"$temp_rpath\"; then\n\t  $ECHO \"\\\n    # Add our own library path to $shlibpath_var\n    $shlibpath_var=\\\"$temp_rpath\\$$shlibpath_var\\\"\n\n    # Some systems cannot cope with colon-terminated $shlibpath_var\n    # The second colon is a workaround for a bug in BeOS R4 sed\n    $shlibpath_var=\\`\\$ECHO \\\"\\$$shlibpath_var\\\" | $SED 's/::*\\$//'\\`\n\n    export $shlibpath_var\n\"\n\tfi\n\n\t$ECHO \"\\\n    if test \\\"\\$libtool_execute_magic\\\" != \\\"$magic\\\"; then\n      # Run the actual program with our arguments.\n      func_exec_program \\${1+\\\"\\$@\\\"}\n    fi\n  else\n    # The program doesn't exist.\n    \\$ECHO \\\"\\$0: error: '\\$progdir/\\$program' does not exist\\\" 1>&2\n    \\$ECHO \\\"This script is just a wrapper for \\$program.\\\" 1>&2\n    \\$ECHO \\\"See the $PACKAGE documentation for more information.\\\" 1>&2\n    exit 1\n  fi\nfi\\\n\"\n}\n\n\n# func_emit_cwrapperexe_src\n# emit the source code for a wrapper executable on stdout\n# Must ONLY be called from within func_mode_link because\n# it depends on a number of variable set therein.\nfunc_emit_cwrapperexe_src ()\n{\n\tcat <<EOF\n\n/* $cwrappersource - temporary wrapper executable for $objdir/$outputname\n   Generated by $PROGRAM (GNU $PACKAGE) $VERSION\n\n   The $output program cannot be directly executed until all the libtool\n   libraries that it depends on are installed.\n\n   This wrapper executable should never be moved out of the build directory.\n   If it is, it will not operate correctly.\n*/\nEOF\n\t    cat <<\"EOF\"\n#ifdef _MSC_VER\n# define _CRT_SECURE_NO_DEPRECATE 1\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#ifdef _MSC_VER\n# include <direct.h>\n# include <process.h>\n# include <io.h>\n#else\n# include <unistd.h>\n# include <stdint.h>\n# ifdef __CYGWIN__\n#  include <io.h>\n# endif\n#endif\n#include <malloc.h>\n#include <stdarg.h>\n#include <assert.h>\n#include <string.h>\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n\n#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)\n\n/* declarations of non-ANSI functions */\n#if defined __MINGW32__\n# ifdef __STRICT_ANSI__\nint _putenv (const char *);\n# endif\n#elif defined __CYGWIN__\n# ifdef __STRICT_ANSI__\nchar *realpath (const char *, char *);\nint putenv (char *);\nint setenv (const char *, const char *, int);\n# endif\n/* #elif defined other_platform || defined ... */\n#endif\n\n/* portability defines, excluding path handling macros */\n#if defined _MSC_VER\n# define setmode _setmode\n# define stat    _stat\n# define chmod   _chmod\n# define getcwd  _getcwd\n# define putenv  _putenv\n# define S_IXUSR _S_IEXEC\n#elif defined __MINGW32__\n# define setmode _setmode\n# define stat    _stat\n# define chmod   _chmod\n# define getcwd  _getcwd\n# define putenv  _putenv\n#elif defined __CYGWIN__\n# define HAVE_SETENV\n# define FOPEN_WB \"wb\"\n/* #elif defined other platforms ... */\n#endif\n\n#if defined PATH_MAX\n# define LT_PATHMAX PATH_MAX\n#elif defined MAXPATHLEN\n# define LT_PATHMAX MAXPATHLEN\n#else\n# define LT_PATHMAX 1024\n#endif\n\n#ifndef S_IXOTH\n# define S_IXOTH 0\n#endif\n#ifndef S_IXGRP\n# define S_IXGRP 0\n#endif\n\n/* path handling portability macros */\n#ifndef DIR_SEPARATOR\n# define DIR_SEPARATOR '/'\n# define PATH_SEPARATOR ':'\n#endif\n\n#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \\\n  defined __OS2__\n# define HAVE_DOS_BASED_FILE_SYSTEM\n# define FOPEN_WB \"wb\"\n# ifndef DIR_SEPARATOR_2\n#  define DIR_SEPARATOR_2 '\\\\'\n# endif\n# ifndef PATH_SEPARATOR_2\n#  define PATH_SEPARATOR_2 ';'\n# endif\n#endif\n\n#ifndef DIR_SEPARATOR_2\n# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)\n#else /* DIR_SEPARATOR_2 */\n# define IS_DIR_SEPARATOR(ch) \\\n\t(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))\n#endif /* DIR_SEPARATOR_2 */\n\n#ifndef PATH_SEPARATOR_2\n# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)\n#else /* PATH_SEPARATOR_2 */\n# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)\n#endif /* PATH_SEPARATOR_2 */\n\n#ifndef FOPEN_WB\n# define FOPEN_WB \"w\"\n#endif\n#ifndef _O_BINARY\n# define _O_BINARY 0\n#endif\n\n#define XMALLOC(type, num)      ((type *) xmalloc ((num) * sizeof(type)))\n#define XFREE(stale) do { \\\n  if (stale) { free (stale); stale = 0; } \\\n} while (0)\n\n#if defined LT_DEBUGWRAPPER\nstatic int lt_debug = 1;\n#else\nstatic int lt_debug = 0;\n#endif\n\nconst char *program_name = \"libtool-wrapper\"; /* in case xstrdup fails */\n\nvoid *xmalloc (size_t num);\nchar *xstrdup (const char *string);\nconst char *base_name (const char *name);\nchar *find_executable (const char *wrapper);\nchar *chase_symlinks (const char *pathspec);\nint make_executable (const char *path);\nint check_executable (const char *path);\nchar *strendzap (char *str, const char *pat);\nvoid lt_debugprintf (const char *file, int line, const char *fmt, ...);\nvoid lt_fatal (const char *file, int line, const char *message, ...);\nstatic const char *nonnull (const char *s);\nstatic const char *nonempty (const char *s);\nvoid lt_setenv (const char *name, const char *value);\nchar *lt_extend_str (const char *orig_value, const char *add, int to_end);\nvoid lt_update_exe_path (const char *name, const char *value);\nvoid lt_update_lib_path (const char *name, const char *value);\nchar **prepare_spawn (char **argv);\nvoid lt_dump_script (FILE *f);\nEOF\n\n\t    cat <<EOF\n#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)\n# define externally_visible volatile\n#else\n# define externally_visible __attribute__((externally_visible)) volatile\n#endif\nexternally_visible const char * MAGIC_EXE = \"$magic_exe\";\nconst char * LIB_PATH_VARNAME = \"$shlibpath_var\";\nEOF\n\n\t    if test yes = \"$shlibpath_overrides_runpath\" && test -n \"$shlibpath_var\" && test -n \"$temp_rpath\"; then\n              func_to_host_path \"$temp_rpath\"\n\t      cat <<EOF\nconst char * LIB_PATH_VALUE   = \"$func_to_host_path_result\";\nEOF\n\t    else\n\t      cat <<\"EOF\"\nconst char * LIB_PATH_VALUE   = \"\";\nEOF\n\t    fi\n\n\t    if test -n \"$dllsearchpath\"; then\n              func_to_host_path \"$dllsearchpath:\"\n\t      cat <<EOF\nconst char * EXE_PATH_VARNAME = \"PATH\";\nconst char * EXE_PATH_VALUE   = \"$func_to_host_path_result\";\nEOF\n\t    else\n\t      cat <<\"EOF\"\nconst char * EXE_PATH_VARNAME = \"\";\nconst char * EXE_PATH_VALUE   = \"\";\nEOF\n\t    fi\n\n\t    if test yes = \"$fast_install\"; then\n\t      cat <<EOF\nconst char * TARGET_PROGRAM_NAME = \"lt-$outputname\"; /* hopefully, no .exe */\nEOF\n\t    else\n\t      cat <<EOF\nconst char * TARGET_PROGRAM_NAME = \"$outputname\"; /* hopefully, no .exe */\nEOF\n\t    fi\n\n\n\t    cat <<\"EOF\"\n\n#define LTWRAPPER_OPTION_PREFIX         \"--lt-\"\n\nstatic const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;\nstatic const char *dumpscript_opt       = LTWRAPPER_OPTION_PREFIX \"dump-script\";\nstatic const char *debug_opt            = LTWRAPPER_OPTION_PREFIX \"debug\";\n\nint\nmain (int argc, char *argv[])\n{\n  char **newargz;\n  int  newargc;\n  char *tmp_pathspec;\n  char *actual_cwrapper_path;\n  char *actual_cwrapper_name;\n  char *target_name;\n  char *lt_argv_zero;\n  int rval = 127;\n\n  int i;\n\n  program_name = (char *) xstrdup (base_name (argv[0]));\n  newargz = XMALLOC (char *, (size_t) argc + 1);\n\n  /* very simple arg parsing; don't want to rely on getopt\n   * also, copy all non cwrapper options to newargz, except\n   * argz[0], which is handled differently\n   */\n  newargc=0;\n  for (i = 1; i < argc; i++)\n    {\n      if (STREQ (argv[i], dumpscript_opt))\n\t{\nEOF\n\t    case $host in\n\t      *mingw* | *cygwin* )\n\t\t# make stdout use \"unix\" line endings\n\t\techo \"          setmode(1,_O_BINARY);\"\n\t\t;;\n\t      esac\n\n\t    cat <<\"EOF\"\n\t  lt_dump_script (stdout);\n\t  return 0;\n\t}\n      if (STREQ (argv[i], debug_opt))\n\t{\n          lt_debug = 1;\n          continue;\n\t}\n      if (STREQ (argv[i], ltwrapper_option_prefix))\n        {\n          /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX\n             namespace, but it is not one of the ones we know about and\n             have already dealt with, above (inluding dump-script), then\n             report an error. Otherwise, targets might begin to believe\n             they are allowed to use options in the LTWRAPPER_OPTION_PREFIX\n             namespace. The first time any user complains about this, we'll\n             need to make LTWRAPPER_OPTION_PREFIX a configure-time option\n             or a configure.ac-settable value.\n           */\n          lt_fatal (__FILE__, __LINE__,\n\t\t    \"unrecognized %s option: '%s'\",\n                    ltwrapper_option_prefix, argv[i]);\n        }\n      /* otherwise ... */\n      newargz[++newargc] = xstrdup (argv[i]);\n    }\n  newargz[++newargc] = NULL;\n\nEOF\n\t    cat <<EOF\n  /* The GNU banner must be the first non-error debug message */\n  lt_debugprintf (__FILE__, __LINE__, \"libtool wrapper (GNU $PACKAGE) $VERSION\\n\");\nEOF\n\t    cat <<\"EOF\"\n  lt_debugprintf (__FILE__, __LINE__, \"(main) argv[0]: %s\\n\", argv[0]);\n  lt_debugprintf (__FILE__, __LINE__, \"(main) program_name: %s\\n\", program_name);\n\n  tmp_pathspec = find_executable (argv[0]);\n  if (tmp_pathspec == NULL)\n    lt_fatal (__FILE__, __LINE__, \"couldn't find %s\", argv[0]);\n  lt_debugprintf (__FILE__, __LINE__,\n                  \"(main) found exe (before symlink chase) at: %s\\n\",\n\t\t  tmp_pathspec);\n\n  actual_cwrapper_path = chase_symlinks (tmp_pathspec);\n  lt_debugprintf (__FILE__, __LINE__,\n                  \"(main) found exe (after symlink chase) at: %s\\n\",\n\t\t  actual_cwrapper_path);\n  XFREE (tmp_pathspec);\n\n  actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path));\n  strendzap (actual_cwrapper_path, actual_cwrapper_name);\n\n  /* wrapper name transforms */\n  strendzap (actual_cwrapper_name, \".exe\");\n  tmp_pathspec = lt_extend_str (actual_cwrapper_name, \".exe\", 1);\n  XFREE (actual_cwrapper_name);\n  actual_cwrapper_name = tmp_pathspec;\n  tmp_pathspec = 0;\n\n  /* target_name transforms -- use actual target program name; might have lt- prefix */\n  target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));\n  strendzap (target_name, \".exe\");\n  tmp_pathspec = lt_extend_str (target_name, \".exe\", 1);\n  XFREE (target_name);\n  target_name = tmp_pathspec;\n  tmp_pathspec = 0;\n\n  lt_debugprintf (__FILE__, __LINE__,\n\t\t  \"(main) libtool target name: %s\\n\",\n\t\t  target_name);\nEOF\n\n\t    cat <<EOF\n  newargz[0] =\n    XMALLOC (char, (strlen (actual_cwrapper_path) +\n\t\t    strlen (\"$objdir\") + 1 + strlen (actual_cwrapper_name) + 1));\n  strcpy (newargz[0], actual_cwrapper_path);\n  strcat (newargz[0], \"$objdir\");\n  strcat (newargz[0], \"/\");\nEOF\n\n\t    cat <<\"EOF\"\n  /* stop here, and copy so we don't have to do this twice */\n  tmp_pathspec = xstrdup (newargz[0]);\n\n  /* do NOT want the lt- prefix here, so use actual_cwrapper_name */\n  strcat (newargz[0], actual_cwrapper_name);\n\n  /* DO want the lt- prefix here if it exists, so use target_name */\n  lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);\n  XFREE (tmp_pathspec);\n  tmp_pathspec = NULL;\nEOF\n\n\t    case $host_os in\n\t      mingw*)\n\t    cat <<\"EOF\"\n  {\n    char* p;\n    while ((p = strchr (newargz[0], '\\\\')) != NULL)\n      {\n\t*p = '/';\n      }\n    while ((p = strchr (lt_argv_zero, '\\\\')) != NULL)\n      {\n\t*p = '/';\n      }\n  }\nEOF\n\t    ;;\n\t    esac\n\n\t    cat <<\"EOF\"\n  XFREE (target_name);\n  XFREE (actual_cwrapper_path);\n  XFREE (actual_cwrapper_name);\n\n  lt_setenv (\"BIN_SH\", \"xpg4\"); /* for Tru64 */\n  lt_setenv (\"DUALCASE\", \"1\");  /* for MSK sh */\n  /* Update the DLL searchpath.  EXE_PATH_VALUE ($dllsearchpath) must\n     be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath)\n     because on Windows, both *_VARNAMEs are PATH but uninstalled\n     libraries must come first. */\n  lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);\n  lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);\n\n  lt_debugprintf (__FILE__, __LINE__, \"(main) lt_argv_zero: %s\\n\",\n\t\t  nonnull (lt_argv_zero));\n  for (i = 0; i < newargc; i++)\n    {\n      lt_debugprintf (__FILE__, __LINE__, \"(main) newargz[%d]: %s\\n\",\n\t\t      i, nonnull (newargz[i]));\n    }\n\nEOF\n\n\t    case $host_os in\n\t      mingw*)\n\t\tcat <<\"EOF\"\n  /* execv doesn't actually work on mingw as expected on unix */\n  newargz = prepare_spawn (newargz);\n  rval = (int) _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);\n  if (rval == -1)\n    {\n      /* failed to start process */\n      lt_debugprintf (__FILE__, __LINE__,\n\t\t      \"(main) failed to launch target \\\"%s\\\": %s\\n\",\n\t\t      lt_argv_zero, nonnull (strerror (errno)));\n      return 127;\n    }\n  return rval;\nEOF\n\t\t;;\n\t      *)\n\t\tcat <<\"EOF\"\n  execv (lt_argv_zero, newargz);\n  return rval; /* =127, but avoids unused variable warning */\nEOF\n\t\t;;\n\t    esac\n\n\t    cat <<\"EOF\"\n}\n\nvoid *\nxmalloc (size_t num)\n{\n  void *p = (void *) malloc (num);\n  if (!p)\n    lt_fatal (__FILE__, __LINE__, \"memory exhausted\");\n\n  return p;\n}\n\nchar *\nxstrdup (const char *string)\n{\n  return string ? strcpy ((char *) xmalloc (strlen (string) + 1),\n\t\t\t  string) : NULL;\n}\n\nconst char *\nbase_name (const char *name)\n{\n  const char *base;\n\n#if defined HAVE_DOS_BASED_FILE_SYSTEM\n  /* Skip over the disk name in MSDOS pathnames. */\n  if (isalpha ((unsigned char) name[0]) && name[1] == ':')\n    name += 2;\n#endif\n\n  for (base = name; *name; name++)\n    if (IS_DIR_SEPARATOR (*name))\n      base = name + 1;\n  return base;\n}\n\nint\ncheck_executable (const char *path)\n{\n  struct stat st;\n\n  lt_debugprintf (__FILE__, __LINE__, \"(check_executable): %s\\n\",\n                  nonempty (path));\n  if ((!path) || (!*path))\n    return 0;\n\n  if ((stat (path, &st) >= 0)\n      && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))\n    return 1;\n  else\n    return 0;\n}\n\nint\nmake_executable (const char *path)\n{\n  int rval = 0;\n  struct stat st;\n\n  lt_debugprintf (__FILE__, __LINE__, \"(make_executable): %s\\n\",\n                  nonempty (path));\n  if ((!path) || (!*path))\n    return 0;\n\n  if (stat (path, &st) >= 0)\n    {\n      rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);\n    }\n  return rval;\n}\n\n/* Searches for the full path of the wrapper.  Returns\n   newly allocated full path name if found, NULL otherwise\n   Does not chase symlinks, even on platforms that support them.\n*/\nchar *\nfind_executable (const char *wrapper)\n{\n  int has_slash = 0;\n  const char *p;\n  const char *p_next;\n  /* static buffer for getcwd */\n  char tmp[LT_PATHMAX + 1];\n  size_t tmp_len;\n  char *concat_name;\n\n  lt_debugprintf (__FILE__, __LINE__, \"(find_executable): %s\\n\",\n                  nonempty (wrapper));\n\n  if ((wrapper == NULL) || (*wrapper == '\\0'))\n    return NULL;\n\n  /* Absolute path? */\n#if defined HAVE_DOS_BASED_FILE_SYSTEM\n  if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')\n    {\n      concat_name = xstrdup (wrapper);\n      if (check_executable (concat_name))\n\treturn concat_name;\n      XFREE (concat_name);\n    }\n  else\n    {\n#endif\n      if (IS_DIR_SEPARATOR (wrapper[0]))\n\t{\n\t  concat_name = xstrdup (wrapper);\n\t  if (check_executable (concat_name))\n\t    return concat_name;\n\t  XFREE (concat_name);\n\t}\n#if defined HAVE_DOS_BASED_FILE_SYSTEM\n    }\n#endif\n\n  for (p = wrapper; *p; p++)\n    if (*p == '/')\n      {\n\thas_slash = 1;\n\tbreak;\n      }\n  if (!has_slash)\n    {\n      /* no slashes; search PATH */\n      const char *path = getenv (\"PATH\");\n      if (path != NULL)\n\t{\n\t  for (p = path; *p; p = p_next)\n\t    {\n\t      const char *q;\n\t      size_t p_len;\n\t      for (q = p; *q; q++)\n\t\tif (IS_PATH_SEPARATOR (*q))\n\t\t  break;\n\t      p_len = (size_t) (q - p);\n\t      p_next = (*q == '\\0' ? q : q + 1);\n\t      if (p_len == 0)\n\t\t{\n\t\t  /* empty path: current directory */\n\t\t  if (getcwd (tmp, LT_PATHMAX) == NULL)\n\t\t    lt_fatal (__FILE__, __LINE__, \"getcwd failed: %s\",\n                              nonnull (strerror (errno)));\n\t\t  tmp_len = strlen (tmp);\n\t\t  concat_name =\n\t\t    XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);\n\t\t  memcpy (concat_name, tmp, tmp_len);\n\t\t  concat_name[tmp_len] = '/';\n\t\t  strcpy (concat_name + tmp_len + 1, wrapper);\n\t\t}\n\t      else\n\t\t{\n\t\t  concat_name =\n\t\t    XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);\n\t\t  memcpy (concat_name, p, p_len);\n\t\t  concat_name[p_len] = '/';\n\t\t  strcpy (concat_name + p_len + 1, wrapper);\n\t\t}\n\t      if (check_executable (concat_name))\n\t\treturn concat_name;\n\t      XFREE (concat_name);\n\t    }\n\t}\n      /* not found in PATH; assume curdir */\n    }\n  /* Relative path | not found in path: prepend cwd */\n  if (getcwd (tmp, LT_PATHMAX) == NULL)\n    lt_fatal (__FILE__, __LINE__, \"getcwd failed: %s\",\n              nonnull (strerror (errno)));\n  tmp_len = strlen (tmp);\n  concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);\n  memcpy (concat_name, tmp, tmp_len);\n  concat_name[tmp_len] = '/';\n  strcpy (concat_name + tmp_len + 1, wrapper);\n\n  if (check_executable (concat_name))\n    return concat_name;\n  XFREE (concat_name);\n  return NULL;\n}\n\nchar *\nchase_symlinks (const char *pathspec)\n{\n#ifndef S_ISLNK\n  return xstrdup (pathspec);\n#else\n  char buf[LT_PATHMAX];\n  struct stat s;\n  char *tmp_pathspec = xstrdup (pathspec);\n  char *p;\n  int has_symlinks = 0;\n  while (strlen (tmp_pathspec) && !has_symlinks)\n    {\n      lt_debugprintf (__FILE__, __LINE__,\n\t\t      \"checking path component for symlinks: %s\\n\",\n\t\t      tmp_pathspec);\n      if (lstat (tmp_pathspec, &s) == 0)\n\t{\n\t  if (S_ISLNK (s.st_mode) != 0)\n\t    {\n\t      has_symlinks = 1;\n\t      break;\n\t    }\n\n\t  /* search backwards for last DIR_SEPARATOR */\n\t  p = tmp_pathspec + strlen (tmp_pathspec) - 1;\n\t  while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))\n\t    p--;\n\t  if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))\n\t    {\n\t      /* no more DIR_SEPARATORS left */\n\t      break;\n\t    }\n\t  *p = '\\0';\n\t}\n      else\n\t{\n\t  lt_fatal (__FILE__, __LINE__,\n\t\t    \"error accessing file \\\"%s\\\": %s\",\n\t\t    tmp_pathspec, nonnull (strerror (errno)));\n\t}\n    }\n  XFREE (tmp_pathspec);\n\n  if (!has_symlinks)\n    {\n      return xstrdup (pathspec);\n    }\n\n  tmp_pathspec = realpath (pathspec, buf);\n  if (tmp_pathspec == 0)\n    {\n      lt_fatal (__FILE__, __LINE__,\n\t\t\"could not follow symlinks for %s\", pathspec);\n    }\n  return xstrdup (tmp_pathspec);\n#endif\n}\n\nchar *\nstrendzap (char *str, const char *pat)\n{\n  size_t len, patlen;\n\n  assert (str != NULL);\n  assert (pat != NULL);\n\n  len = strlen (str);\n  patlen = strlen (pat);\n\n  if (patlen <= len)\n    {\n      str += len - patlen;\n      if (STREQ (str, pat))\n\t*str = '\\0';\n    }\n  return str;\n}\n\nvoid\nlt_debugprintf (const char *file, int line, const char *fmt, ...)\n{\n  va_list args;\n  if (lt_debug)\n    {\n      (void) fprintf (stderr, \"%s:%s:%d: \", program_name, file, line);\n      va_start (args, fmt);\n      (void) vfprintf (stderr, fmt, args);\n      va_end (args);\n    }\n}\n\nstatic void\nlt_error_core (int exit_status, const char *file,\n\t       int line, const char *mode,\n\t       const char *message, va_list ap)\n{\n  fprintf (stderr, \"%s:%s:%d: %s: \", program_name, file, line, mode);\n  vfprintf (stderr, message, ap);\n  fprintf (stderr, \".\\n\");\n\n  if (exit_status >= 0)\n    exit (exit_status);\n}\n\nvoid\nlt_fatal (const char *file, int line, const char *message, ...)\n{\n  va_list ap;\n  va_start (ap, message);\n  lt_error_core (EXIT_FAILURE, file, line, \"FATAL\", message, ap);\n  va_end (ap);\n}\n\nstatic const char *\nnonnull (const char *s)\n{\n  return s ? s : \"(null)\";\n}\n\nstatic const char *\nnonempty (const char *s)\n{\n  return (s && !*s) ? \"(empty)\" : nonnull (s);\n}\n\nvoid\nlt_setenv (const char *name, const char *value)\n{\n  lt_debugprintf (__FILE__, __LINE__,\n\t\t  \"(lt_setenv) setting '%s' to '%s'\\n\",\n                  nonnull (name), nonnull (value));\n  {\n#ifdef HAVE_SETENV\n    /* always make a copy, for consistency with !HAVE_SETENV */\n    char *str = xstrdup (value);\n    setenv (name, str, 1);\n#else\n    size_t len = strlen (name) + 1 + strlen (value) + 1;\n    char *str = XMALLOC (char, len);\n    sprintf (str, \"%s=%s\", name, value);\n    if (putenv (str) != EXIT_SUCCESS)\n      {\n        XFREE (str);\n      }\n#endif\n  }\n}\n\nchar *\nlt_extend_str (const char *orig_value, const char *add, int to_end)\n{\n  char *new_value;\n  if (orig_value && *orig_value)\n    {\n      size_t orig_value_len = strlen (orig_value);\n      size_t add_len = strlen (add);\n      new_value = XMALLOC (char, add_len + orig_value_len + 1);\n      if (to_end)\n        {\n          strcpy (new_value, orig_value);\n          strcpy (new_value + orig_value_len, add);\n        }\n      else\n        {\n          strcpy (new_value, add);\n          strcpy (new_value + add_len, orig_value);\n        }\n    }\n  else\n    {\n      new_value = xstrdup (add);\n    }\n  return new_value;\n}\n\nvoid\nlt_update_exe_path (const char *name, const char *value)\n{\n  lt_debugprintf (__FILE__, __LINE__,\n\t\t  \"(lt_update_exe_path) modifying '%s' by prepending '%s'\\n\",\n                  nonnull (name), nonnull (value));\n\n  if (name && *name && value && *value)\n    {\n      char *new_value = lt_extend_str (getenv (name), value, 0);\n      /* some systems can't cope with a ':'-terminated path #' */\n      size_t len = strlen (new_value);\n      while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1]))\n        {\n          new_value[--len] = '\\0';\n        }\n      lt_setenv (name, new_value);\n      XFREE (new_value);\n    }\n}\n\nvoid\nlt_update_lib_path (const char *name, const char *value)\n{\n  lt_debugprintf (__FILE__, __LINE__,\n\t\t  \"(lt_update_lib_path) modifying '%s' by prepending '%s'\\n\",\n                  nonnull (name), nonnull (value));\n\n  if (name && *name && value && *value)\n    {\n      char *new_value = lt_extend_str (getenv (name), value, 0);\n      lt_setenv (name, new_value);\n      XFREE (new_value);\n    }\n}\n\nEOF\n\t    case $host_os in\n\t      mingw*)\n\t\tcat <<\"EOF\"\n\n/* Prepares an argument vector before calling spawn().\n   Note that spawn() does not by itself call the command interpreter\n     (getenv (\"COMSPEC\") != NULL ? getenv (\"COMSPEC\") :\n      ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);\n         GetVersionEx(&v);\n         v.dwPlatformId == VER_PLATFORM_WIN32_NT;\n      }) ? \"cmd.exe\" : \"command.com\").\n   Instead it simply concatenates the arguments, separated by ' ', and calls\n   CreateProcess().  We must quote the arguments since Win32 CreateProcess()\n   interprets characters like ' ', '\\t', '\\\\', '\"' (but not '<' and '>') in a\n   special way:\n   - Space and tab are interpreted as delimiters. They are not treated as\n     delimiters if they are surrounded by double quotes: \"...\".\n   - Unescaped double quotes are removed from the input. Their only effect is\n     that within double quotes, space and tab are treated like normal\n     characters.\n   - Backslashes not followed by double quotes are not special.\n   - But 2*n+1 backslashes followed by a double quote become\n     n backslashes followed by a double quote (n >= 0):\n       \\\" -> \"\n       \\\\\\\" -> \\\"\n       \\\\\\\\\\\" -> \\\\\"\n */\n#define SHELL_SPECIAL_CHARS \"\\\"\\\\ \\001\\002\\003\\004\\005\\006\\007\\010\\011\\012\\013\\014\\015\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037\"\n#define SHELL_SPACE_CHARS \" \\001\\002\\003\\004\\005\\006\\007\\010\\011\\012\\013\\014\\015\\016\\017\\020\\021\\022\\023\\024\\025\\026\\027\\030\\031\\032\\033\\034\\035\\036\\037\"\nchar **\nprepare_spawn (char **argv)\n{\n  size_t argc;\n  char **new_argv;\n  size_t i;\n\n  /* Count number of arguments.  */\n  for (argc = 0; argv[argc] != NULL; argc++)\n    ;\n\n  /* Allocate new argument vector.  */\n  new_argv = XMALLOC (char *, argc + 1);\n\n  /* Put quoted arguments into the new argument vector.  */\n  for (i = 0; i < argc; i++)\n    {\n      const char *string = argv[i];\n\n      if (string[0] == '\\0')\n\tnew_argv[i] = xstrdup (\"\\\"\\\"\");\n      else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)\n\t{\n\t  int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);\n\t  size_t length;\n\t  unsigned int backslashes;\n\t  const char *s;\n\t  char *quoted_string;\n\t  char *p;\n\n\t  length = 0;\n\t  backslashes = 0;\n\t  if (quote_around)\n\t    length++;\n\t  for (s = string; *s != '\\0'; s++)\n\t    {\n\t      char c = *s;\n\t      if (c == '\"')\n\t\tlength += backslashes + 1;\n\t      length++;\n\t      if (c == '\\\\')\n\t\tbackslashes++;\n\t      else\n\t\tbackslashes = 0;\n\t    }\n\t  if (quote_around)\n\t    length += backslashes + 1;\n\n\t  quoted_string = XMALLOC (char, length + 1);\n\n\t  p = quoted_string;\n\t  backslashes = 0;\n\t  if (quote_around)\n\t    *p++ = '\"';\n\t  for (s = string; *s != '\\0'; s++)\n\t    {\n\t      char c = *s;\n\t      if (c == '\"')\n\t\t{\n\t\t  unsigned int j;\n\t\t  for (j = backslashes + 1; j > 0; j--)\n\t\t    *p++ = '\\\\';\n\t\t}\n\t      *p++ = c;\n\t      if (c == '\\\\')\n\t\tbackslashes++;\n\t      else\n\t\tbackslashes = 0;\n\t    }\n\t  if (quote_around)\n\t    {\n\t      unsigned int j;\n\t      for (j = backslashes; j > 0; j--)\n\t\t*p++ = '\\\\';\n\t      *p++ = '\"';\n\t    }\n\t  *p = '\\0';\n\n\t  new_argv[i] = quoted_string;\n\t}\n      else\n\tnew_argv[i] = (char *) string;\n    }\n  new_argv[argc] = NULL;\n\n  return new_argv;\n}\nEOF\n\t\t;;\n\t    esac\n\n            cat <<\"EOF\"\nvoid lt_dump_script (FILE* f)\n{\nEOF\n\t    func_emit_wrapper yes |\n\t      $SED -n -e '\ns/^\\(.\\{79\\}\\)\\(..*\\)/\\1\\\n\\2/\nh\ns/\\([\\\\\"]\\)/\\\\\\1/g\ns/$/\\\\n/\ns/\\([^\\n]*\\).*/  fputs (\"\\1\", f);/p\ng\nD'\n            cat <<\"EOF\"\n}\nEOF\n}\n# end: func_emit_cwrapperexe_src\n\n# func_win32_import_lib_p ARG\n# True if ARG is an import lib, as indicated by $file_magic_cmd\nfunc_win32_import_lib_p ()\n{\n    $debug_cmd\n\n    case `eval $file_magic_cmd \\\"\\$1\\\" 2>/dev/null | $SED -e 10q` in\n    *import*) : ;;\n    *) false ;;\n    esac\n}\n\n# func_suncc_cstd_abi\n# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!!\n# Several compiler flags select an ABI that is incompatible with the\n# Cstd library. Avoid specifying it if any are in CXXFLAGS.\nfunc_suncc_cstd_abi ()\n{\n    $debug_cmd\n\n    case \" $compile_command \" in\n    *\" -compat=g \"*|*\\ -std=c++[0-9][0-9]\\ *|*\" -library=stdcxx4 \"*|*\" -library=stlport4 \"*)\n      suncc_use_cstd_abi=no\n      ;;\n    *)\n      suncc_use_cstd_abi=yes\n      ;;\n    esac\n}\n\n# func_mode_link arg...\nfunc_mode_link ()\n{\n    $debug_cmd\n\n    case $host in\n    *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)\n      # It is impossible to link a dll without this setting, and\n      # we shouldn't force the makefile maintainer to figure out\n      # what system we are compiling for in order to pass an extra\n      # flag for every libtool invocation.\n      # allow_undefined=no\n\n      # FIXME: Unfortunately, there are problems with the above when trying\n      # to make a dll that has undefined symbols, in which case not\n      # even a static library is built.  For now, we need to specify\n      # -no-undefined on the libtool link line when we can be certain\n      # that all symbols are satisfied, otherwise we get a static library.\n      allow_undefined=yes\n      ;;\n    *)\n      allow_undefined=yes\n      ;;\n    esac\n    libtool_args=$nonopt\n    base_compile=\"$nonopt $@\"\n    compile_command=$nonopt\n    finalize_command=$nonopt\n\n    compile_rpath=\n    finalize_rpath=\n    compile_shlibpath=\n    finalize_shlibpath=\n    convenience=\n    old_convenience=\n    deplibs=\n    old_deplibs=\n    compiler_flags=\n    linker_flags=\n    dllsearchpath=\n    lib_search_path=`pwd`\n    inst_prefix_dir=\n    new_inherited_linker_flags=\n\n    avoid_version=no\n    bindir=\n    dlfiles=\n    dlprefiles=\n    dlself=no\n    export_dynamic=no\n    export_symbols=\n    export_symbols_regex=\n    generated=\n    libobjs=\n    ltlibs=\n    module=no\n    no_install=no\n    objs=\n    os2dllname=\n    non_pic_objects=\n    precious_files_regex=\n    prefer_static_libs=no\n    preload=false\n    prev=\n    prevarg=\n    release=\n    rpath=\n    xrpath=\n    perm_rpath=\n    temp_rpath=\n    thread_safe=no\n    vinfo=\n    vinfo_number=no\n    weak_libs=\n    single_module=$wl-single_module\n    func_infer_tag $base_compile\n\n    # We need to know -static, to get the right output filenames.\n    for arg\n    do\n      case $arg in\n      -shared)\n\ttest yes != \"$build_libtool_libs\" \\\n\t  && func_fatal_configuration \"cannot build a shared library\"\n\tbuild_old_libs=no\n\tbreak\n\t;;\n      -all-static | -static | -static-libtool-libs)\n\tcase $arg in\n\t-all-static)\n\t  if test yes = \"$build_libtool_libs\" && test -z \"$link_static_flag\"; then\n\t    func_warning \"complete static linking is impossible in this configuration\"\n\t  fi\n\t  if test -n \"$link_static_flag\"; then\n\t    dlopen_self=$dlopen_self_static\n\t  fi\n\t  prefer_static_libs=yes\n\t  ;;\n\t-static)\n\t  if test -z \"$pic_flag\" && test -n \"$link_static_flag\"; then\n\t    dlopen_self=$dlopen_self_static\n\t  fi\n\t  prefer_static_libs=built\n\t  ;;\n\t-static-libtool-libs)\n\t  if test -z \"$pic_flag\" && test -n \"$link_static_flag\"; then\n\t    dlopen_self=$dlopen_self_static\n\t  fi\n\t  prefer_static_libs=yes\n\t  ;;\n\tesac\n\tbuild_libtool_libs=no\n\tbuild_old_libs=yes\n\tbreak\n\t;;\n      esac\n    done\n\n    # See if our shared archives depend on static archives.\n    test -n \"$old_archive_from_new_cmds\" && build_old_libs=yes\n\n    # Go through the arguments, transforming them on the way.\n    while test \"$#\" -gt 0; do\n      arg=$1\n      shift\n      func_quote_for_eval \"$arg\"\n      qarg=$func_quote_for_eval_unquoted_result\n      func_append libtool_args \" $func_quote_for_eval_result\"\n\n      # If the previous option needs an argument, assign it.\n      if test -n \"$prev\"; then\n\tcase $prev in\n\toutput)\n\t  func_append compile_command \" @OUTPUT@\"\n\t  func_append finalize_command \" @OUTPUT@\"\n\t  ;;\n\tesac\n\n\tcase $prev in\n\tbindir)\n\t  bindir=$arg\n\t  prev=\n\t  continue\n\t  ;;\n\tdlfiles|dlprefiles)\n\t  $preload || {\n\t    # Add the symbol object into the linking commands.\n\t    func_append compile_command \" @SYMFILE@\"\n\t    func_append finalize_command \" @SYMFILE@\"\n\t    preload=:\n\t  }\n\t  case $arg in\n\t  *.la | *.lo) ;;  # We handle these cases below.\n\t  force)\n\t    if test no = \"$dlself\"; then\n\t      dlself=needless\n\t      export_dynamic=yes\n\t    fi\n\t    prev=\n\t    continue\n\t    ;;\n\t  self)\n\t    if test dlprefiles = \"$prev\"; then\n\t      dlself=yes\n\t    elif test dlfiles = \"$prev\" && test yes != \"$dlopen_self\"; then\n\t      dlself=yes\n\t    else\n\t      dlself=needless\n\t      export_dynamic=yes\n\t    fi\n\t    prev=\n\t    continue\n\t    ;;\n\t  *)\n\t    if test dlfiles = \"$prev\"; then\n\t      func_append dlfiles \" $arg\"\n\t    else\n\t      func_append dlprefiles \" $arg\"\n\t    fi\n\t    prev=\n\t    continue\n\t    ;;\n\t  esac\n\t  ;;\n\texpsyms)\n\t  export_symbols=$arg\n\t  test -f \"$arg\" \\\n\t    || func_fatal_error \"symbol file '$arg' does not exist\"\n\t  prev=\n\t  continue\n\t  ;;\n\texpsyms_regex)\n\t  export_symbols_regex=$arg\n\t  prev=\n\t  continue\n\t  ;;\n\tframework)\n\t  case $host in\n\t    *-*-darwin*)\n\t      case \"$deplibs \" in\n\t\t*\" $qarg.ltframework \"*) ;;\n\t\t*) func_append deplibs \" $qarg.ltframework\" # this is fixed later\n\t\t   ;;\n\t      esac\n\t      ;;\n\t  esac\n\t  prev=\n\t  continue\n\t  ;;\n\tinst_prefix)\n\t  inst_prefix_dir=$arg\n\t  prev=\n\t  continue\n\t  ;;\n\tmllvm)\n\t  # Clang does not use LLVM to link, so we can simply discard any\n\t  # '-mllvm $arg' options when doing the link step.\n\t  prev=\n\t  continue\n\t  ;;\n\tobjectlist)\n\t  if test -f \"$arg\"; then\n\t    save_arg=$arg\n\t    moreargs=\n\t    for fil in `cat \"$save_arg\"`\n\t    do\n#\t      func_append moreargs \" $fil\"\n\t      arg=$fil\n\t      # A libtool-controlled object.\n\n\t      # Check to see that this really is a libtool object.\n\t      if func_lalib_unsafe_p \"$arg\"; then\n\t\tpic_object=\n\t\tnon_pic_object=\n\n\t\t# Read the .lo file\n\t\tfunc_source \"$arg\"\n\n\t\tif test -z \"$pic_object\" ||\n\t\t   test -z \"$non_pic_object\" ||\n\t\t   test none = \"$pic_object\" &&\n\t\t   test none = \"$non_pic_object\"; then\n\t\t  func_fatal_error \"cannot find name of object for '$arg'\"\n\t\tfi\n\n\t\t# Extract subdirectory from the argument.\n\t\tfunc_dirname \"$arg\" \"/\" \"\"\n\t\txdir=$func_dirname_result\n\n\t\tif test none != \"$pic_object\"; then\n\t\t  # Prepend the subdirectory the object is found in.\n\t\t  pic_object=$xdir$pic_object\n\n\t\t  if test dlfiles = \"$prev\"; then\n\t\t    if test yes = \"$build_libtool_libs\" && test yes = \"$dlopen_support\"; then\n\t\t      func_append dlfiles \" $pic_object\"\n\t\t      prev=\n\t\t      continue\n\t\t    else\n\t\t      # If libtool objects are unsupported, then we need to preload.\n\t\t      prev=dlprefiles\n\t\t    fi\n\t\t  fi\n\n\t\t  # CHECK ME:  I think I busted this.  -Ossama\n\t\t  if test dlprefiles = \"$prev\"; then\n\t\t    # Preload the old-style object.\n\t\t    func_append dlprefiles \" $pic_object\"\n\t\t    prev=\n\t\t  fi\n\n\t\t  # A PIC object.\n\t\t  func_append libobjs \" $pic_object\"\n\t\t  arg=$pic_object\n\t\tfi\n\n\t\t# Non-PIC object.\n\t\tif test none != \"$non_pic_object\"; then\n\t\t  # Prepend the subdirectory the object is found in.\n\t\t  non_pic_object=$xdir$non_pic_object\n\n\t\t  # A standard non-PIC object\n\t\t  func_append non_pic_objects \" $non_pic_object\"\n\t\t  if test -z \"$pic_object\" || test none = \"$pic_object\"; then\n\t\t    arg=$non_pic_object\n\t\t  fi\n\t\telse\n\t\t  # If the PIC object exists, use it instead.\n\t\t  # $xdir was prepended to $pic_object above.\n\t\t  non_pic_object=$pic_object\n\t\t  func_append non_pic_objects \" $non_pic_object\"\n\t\tfi\n\t      else\n\t\t# Only an error if not doing a dry-run.\n\t\tif $opt_dry_run; then\n\t\t  # Extract subdirectory from the argument.\n\t\t  func_dirname \"$arg\" \"/\" \"\"\n\t\t  xdir=$func_dirname_result\n\n\t\t  func_lo2o \"$arg\"\n\t\t  pic_object=$xdir$objdir/$func_lo2o_result\n\t\t  non_pic_object=$xdir$func_lo2o_result\n\t\t  func_append libobjs \" $pic_object\"\n\t\t  func_append non_pic_objects \" $non_pic_object\"\n\t        else\n\t\t  func_fatal_error \"'$arg' is not a valid libtool object\"\n\t\tfi\n\t      fi\n\t    done\n\t  else\n\t    func_fatal_error \"link input file '$arg' does not exist\"\n\t  fi\n\t  arg=$save_arg\n\t  prev=\n\t  continue\n\t  ;;\n\tos2dllname)\n\t  os2dllname=$arg\n\t  prev=\n\t  continue\n\t  ;;\n\tprecious_regex)\n\t  precious_files_regex=$arg\n\t  prev=\n\t  continue\n\t  ;;\n\trelease)\n\t  release=-$arg\n\t  prev=\n\t  continue\n\t  ;;\n\trpath | xrpath)\n\t  # We need an absolute path.\n\t  case $arg in\n\t  [\\\\/]* | [A-Za-z]:[\\\\/]*) ;;\n\t  *)\n\t    func_fatal_error \"only absolute run-paths are allowed\"\n\t    ;;\n\t  esac\n\t  if test rpath = \"$prev\"; then\n\t    case \"$rpath \" in\n\t    *\" $arg \"*) ;;\n\t    *) func_append rpath \" $arg\" ;;\n\t    esac\n\t  else\n\t    case \"$xrpath \" in\n\t    *\" $arg \"*) ;;\n\t    *) func_append xrpath \" $arg\" ;;\n\t    esac\n\t  fi\n\t  prev=\n\t  continue\n\t  ;;\n\tshrext)\n\t  shrext_cmds=$arg\n\t  prev=\n\t  continue\n\t  ;;\n\tweak)\n\t  func_append weak_libs \" $arg\"\n\t  prev=\n\t  continue\n\t  ;;\n\txcclinker)\n\t  func_append linker_flags \" $qarg\"\n\t  func_append compiler_flags \" $qarg\"\n\t  prev=\n\t  func_append compile_command \" $qarg\"\n\t  func_append finalize_command \" $qarg\"\n\t  continue\n\t  ;;\n\txcompiler)\n\t  func_append compiler_flags \" $qarg\"\n\t  prev=\n\t  func_append compile_command \" $qarg\"\n\t  func_append finalize_command \" $qarg\"\n\t  continue\n\t  ;;\n\txlinker)\n\t  func_append linker_flags \" $qarg\"\n\t  func_append compiler_flags \" $wl$qarg\"\n\t  prev=\n\t  func_append compile_command \" $wl$qarg\"\n\t  func_append finalize_command \" $wl$qarg\"\n\t  continue\n\t  ;;\n\t*)\n\t  eval \"$prev=\\\"\\$arg\\\"\"\n\t  prev=\n\t  continue\n\t  ;;\n\tesac\n      fi # test -n \"$prev\"\n\n      prevarg=$arg\n\n      case $arg in\n      -all-static)\n\tif test -n \"$link_static_flag\"; then\n\t  # See comment for -static flag below, for more details.\n\t  func_append compile_command \" $link_static_flag\"\n\t  func_append finalize_command \" $link_static_flag\"\n\tfi\n\tcontinue\n\t;;\n\n      -allow-undefined)\n\t# FIXME: remove this flag sometime in the future.\n\tfunc_fatal_error \"'-allow-undefined' must not be used because it is the default\"\n\t;;\n\n      -avoid-version)\n\tavoid_version=yes\n\tcontinue\n\t;;\n\n      -bindir)\n\tprev=bindir\n\tcontinue\n\t;;\n\n      -dlopen)\n\tprev=dlfiles\n\tcontinue\n\t;;\n\n      -dlpreopen)\n\tprev=dlprefiles\n\tcontinue\n\t;;\n\n      -export-dynamic)\n\texport_dynamic=yes\n\tcontinue\n\t;;\n\n      -export-symbols | -export-symbols-regex)\n\tif test -n \"$export_symbols\" || test -n \"$export_symbols_regex\"; then\n\t  func_fatal_error \"more than one -exported-symbols argument is not allowed\"\n\tfi\n\tif test X-export-symbols = \"X$arg\"; then\n\t  prev=expsyms\n\telse\n\t  prev=expsyms_regex\n\tfi\n\tcontinue\n\t;;\n\n      -framework)\n\tprev=framework\n\tcontinue\n\t;;\n\n      -inst-prefix-dir)\n\tprev=inst_prefix\n\tcontinue\n\t;;\n\n      # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*\n      # so, if we see these flags be careful not to treat them like -L\n      -L[A-Z][A-Z]*:*)\n\tcase $with_gcc/$host in\n\tno/*-*-irix* | /*-*-irix*)\n\t  func_append compile_command \" $arg\"\n\t  func_append finalize_command \" $arg\"\n\t  ;;\n\tesac\n\tcontinue\n\t;;\n\n      -L*)\n\tfunc_stripname \"-L\" '' \"$arg\"\n\tif test -z \"$func_stripname_result\"; then\n\t  if test \"$#\" -gt 0; then\n\t    func_fatal_error \"require no space between '-L' and '$1'\"\n\t  else\n\t    func_fatal_error \"need path for '-L' option\"\n\t  fi\n\tfi\n\tfunc_resolve_sysroot \"$func_stripname_result\"\n\tdir=$func_resolve_sysroot_result\n\t# We need an absolute path.\n\tcase $dir in\n\t[\\\\/]* | [A-Za-z]:[\\\\/]*) ;;\n\t*)\n\t  absdir=`cd \"$dir\" && pwd`\n\t  test -z \"$absdir\" && \\\n\t    func_fatal_error \"cannot determine absolute directory name of '$dir'\"\n\t  dir=$absdir\n\t  ;;\n\tesac\n\tcase \"$deplibs \" in\n\t*\" -L$dir \"* | *\" $arg \"*)\n\t  # Will only happen for absolute or sysroot arguments\n\t  ;;\n\t*)\n\t  # Preserve sysroot, but never include relative directories\n\t  case $dir in\n\t    [\\\\/]* | [A-Za-z]:[\\\\/]* | =*) func_append deplibs \" $arg\" ;;\n\t    *) func_append deplibs \" -L$dir\" ;;\n\t  esac\n\t  func_append lib_search_path \" $dir\"\n\t  ;;\n\tesac\n\tcase $host in\n\t*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)\n\t  testbindir=`$ECHO \"$dir\" | $SED 's*/lib$*/bin*'`\n\t  case :$dllsearchpath: in\n\t  *\":$dir:\"*) ;;\n\t  ::) dllsearchpath=$dir;;\n\t  *) func_append dllsearchpath \":$dir\";;\n\t  esac\n\t  case :$dllsearchpath: in\n\t  *\":$testbindir:\"*) ;;\n\t  ::) dllsearchpath=$testbindir;;\n\t  *) func_append dllsearchpath \":$testbindir\";;\n\t  esac\n\t  ;;\n\tesac\n\tcontinue\n\t;;\n\n      -l*)\n\tif test X-lc = \"X$arg\" || test X-lm = \"X$arg\"; then\n\t  case $host in\n\t  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)\n\t    # These systems don't actually have a C or math library (as such)\n\t    continue\n\t    ;;\n\t  *-*-os2*)\n\t    # These systems don't actually have a C library (as such)\n\t    test X-lc = \"X$arg\" && continue\n\t    ;;\n\t  *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)\n\t    # Do not include libc due to us having libc/libc_r.\n\t    test X-lc = \"X$arg\" && continue\n\t    ;;\n\t  *-*-rhapsody* | *-*-darwin1.[012])\n\t    # Rhapsody C and math libraries are in the System framework\n\t    func_append deplibs \" System.ltframework\"\n\t    continue\n\t    ;;\n\t  *-*-sco3.2v5* | *-*-sco5v6*)\n\t    # Causes problems with __ctype\n\t    test X-lc = \"X$arg\" && continue\n\t    ;;\n\t  *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)\n\t    # Compiler inserts libc in the correct place for threads to work\n\t    test X-lc = \"X$arg\" && continue\n\t    ;;\n\t  esac\n\telif test X-lc_r = \"X$arg\"; then\n\t case $host in\n\t *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)\n\t   # Do not include libc_r directly, use -pthread flag.\n\t   continue\n\t   ;;\n\t esac\n\tfi\n\tfunc_append deplibs \" $arg\"\n\tcontinue\n\t;;\n\n      -mllvm)\n\tprev=mllvm\n\tcontinue\n\t;;\n\n      -module)\n\tmodule=yes\n\tcontinue\n\t;;\n\n      # Tru64 UNIX uses -model [arg] to determine the layout of C++\n      # classes, name mangling, and exception handling.\n      # Darwin uses the -arch flag to determine output architecture.\n      -model|-arch|-isysroot|--sysroot)\n\tfunc_append compiler_flags \" $arg\"\n\tfunc_append compile_command \" $arg\"\n\tfunc_append finalize_command \" $arg\"\n\tprev=xcompiler\n\tcontinue\n\t;;\n\n      -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \\\n      |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)\n\tfunc_append compiler_flags \" $arg\"\n\tfunc_append compile_command \" $arg\"\n\tfunc_append finalize_command \" $arg\"\n\tcase \"$new_inherited_linker_flags \" in\n\t    *\" $arg \"*) ;;\n\t    * ) func_append new_inherited_linker_flags \" $arg\" ;;\n\tesac\n\tcontinue\n\t;;\n\n      -multi_module)\n\tsingle_module=$wl-multi_module\n\tcontinue\n\t;;\n\n      -no-fast-install)\n\tfast_install=no\n\tcontinue\n\t;;\n\n      -no-install)\n\tcase $host in\n\t*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)\n\t  # The PATH hackery in wrapper scripts is required on Windows\n\t  # and Darwin in order for the loader to find any dlls it needs.\n\t  func_warning \"'-no-install' is ignored for $host\"\n\t  func_warning \"assuming '-no-fast-install' instead\"\n\t  fast_install=no\n\t  ;;\n\t*) no_install=yes ;;\n\tesac\n\tcontinue\n\t;;\n\n      -no-undefined)\n\tallow_undefined=no\n\tcontinue\n\t;;\n\n      -objectlist)\n\tprev=objectlist\n\tcontinue\n\t;;\n\n      -os2dllname)\n\tprev=os2dllname\n\tcontinue\n\t;;\n\n      -o) prev=output ;;\n\n      -precious-files-regex)\n\tprev=precious_regex\n\tcontinue\n\t;;\n\n      -release)\n\tprev=release\n\tcontinue\n\t;;\n\n      -rpath)\n\tprev=rpath\n\tcontinue\n\t;;\n\n      -R)\n\tprev=xrpath\n\tcontinue\n\t;;\n\n      -R*)\n\tfunc_stripname '-R' '' \"$arg\"\n\tdir=$func_stripname_result\n\t# We need an absolute path.\n\tcase $dir in\n\t[\\\\/]* | [A-Za-z]:[\\\\/]*) ;;\n\t=*)\n\t  func_stripname '=' '' \"$dir\"\n\t  dir=$lt_sysroot$func_stripname_result\n\t  ;;\n\t*)\n\t  func_fatal_error \"only absolute run-paths are allowed\"\n\t  ;;\n\tesac\n\tcase \"$xrpath \" in\n\t*\" $dir \"*) ;;\n\t*) func_append xrpath \" $dir\" ;;\n\tesac\n\tcontinue\n\t;;\n\n      -shared)\n\t# The effects of -shared are defined in a previous loop.\n\tcontinue\n\t;;\n\n      -shrext)\n\tprev=shrext\n\tcontinue\n\t;;\n\n      -static | -static-libtool-libs)\n\t# The effects of -static are defined in a previous loop.\n\t# We used to do the same as -all-static on platforms that\n\t# didn't have a PIC flag, but the assumption that the effects\n\t# would be equivalent was wrong.  It would break on at least\n\t# Digital Unix and AIX.\n\tcontinue\n\t;;\n\n      -thread-safe)\n\tthread_safe=yes\n\tcontinue\n\t;;\n\n      -version-info)\n\tprev=vinfo\n\tcontinue\n\t;;\n\n      -version-number)\n\tprev=vinfo\n\tvinfo_number=yes\n\tcontinue\n\t;;\n\n      -weak)\n        prev=weak\n\tcontinue\n\t;;\n\n      -Wc,*)\n\tfunc_stripname '-Wc,' '' \"$arg\"\n\targs=$func_stripname_result\n\targ=\n\tsave_ifs=$IFS; IFS=,\n\tfor flag in $args; do\n\t  IFS=$save_ifs\n          func_quote_for_eval \"$flag\"\n\t  func_append arg \" $func_quote_for_eval_result\"\n\t  func_append compiler_flags \" $func_quote_for_eval_result\"\n\tdone\n\tIFS=$save_ifs\n\tfunc_stripname ' ' '' \"$arg\"\n\targ=$func_stripname_result\n\t;;\n\n      -Wl,*)\n\tfunc_stripname '-Wl,' '' \"$arg\"\n\targs=$func_stripname_result\n\targ=\n\tsave_ifs=$IFS; IFS=,\n\tfor flag in $args; do\n\t  IFS=$save_ifs\n          func_quote_for_eval \"$flag\"\n\t  func_append arg \" $wl$func_quote_for_eval_result\"\n\t  func_append compiler_flags \" $wl$func_quote_for_eval_result\"\n\t  func_append linker_flags \" $func_quote_for_eval_result\"\n\tdone\n\tIFS=$save_ifs\n\tfunc_stripname ' ' '' \"$arg\"\n\targ=$func_stripname_result\n\t;;\n\n      -Xcompiler)\n\tprev=xcompiler\n\tcontinue\n\t;;\n\n      -Xlinker)\n\tprev=xlinker\n\tcontinue\n\t;;\n\n      -XCClinker)\n\tprev=xcclinker\n\tcontinue\n\t;;\n\n      # -msg_* for osf cc\n      -msg_*)\n\tfunc_quote_for_eval \"$arg\"\n\targ=$func_quote_for_eval_result\n\t;;\n\n      # Flags to be passed through unchanged, with rationale:\n      # -64, -mips[0-9]      enable 64-bit mode for the SGI compiler\n      # -r[0-9][0-9]*        specify processor for the SGI compiler\n      # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler\n      # +DA*, +DD*           enable 64-bit mode for the HP compiler\n      # -q*                  compiler args for the IBM compiler\n      # -m*, -t[45]*, -txscale* architecture-specific flags for GCC\n      # -F/path              path to uninstalled frameworks, gcc on darwin\n      # -p, -pg, --coverage, -fprofile-*  profiling flags for GCC\n      # -fstack-protector*   stack protector flags for GCC\n      # @file                GCC response files\n      # -tp=*                Portland pgcc target processor selection\n      # --sysroot=*          for sysroot support\n      # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization\n      # -specs=*             GCC specs files\n      # -stdlib=*            select c++ std lib with clang\n      # -fsanitize=*         Clang/GCC memory and address sanitizer\n      -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \\\n      -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \\\n      -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \\\n      -specs=*|-fsanitize=*)\n        func_quote_for_eval \"$arg\"\n\targ=$func_quote_for_eval_result\n        func_append compile_command \" $arg\"\n        func_append finalize_command \" $arg\"\n        func_append compiler_flags \" $arg\"\n        continue\n        ;;\n\n      -Z*)\n        if test os2 = \"`expr $host : '.*\\(os2\\)'`\"; then\n          # OS/2 uses -Zxxx to specify OS/2-specific options\n\t  compiler_flags=\"$compiler_flags $arg\"\n\t  func_append compile_command \" $arg\"\n\t  func_append finalize_command \" $arg\"\n\t  case $arg in\n\t  -Zlinker | -Zstack)\n\t    prev=xcompiler\n\t    ;;\n\t  esac\n\t  continue\n        else\n\t  # Otherwise treat like 'Some other compiler flag' below\n\t  func_quote_for_eval \"$arg\"\n\t  arg=$func_quote_for_eval_result\n        fi\n\t;;\n\n      # Some other compiler flag.\n      -* | +*)\n        func_quote_for_eval \"$arg\"\n\targ=$func_quote_for_eval_result\n\t;;\n\n      *.$objext)\n\t# A standard object.\n\tfunc_append objs \" $arg\"\n\t;;\n\n      *.lo)\n\t# A libtool-controlled object.\n\n\t# Check to see that this really is a libtool object.\n\tif func_lalib_unsafe_p \"$arg\"; then\n\t  pic_object=\n\t  non_pic_object=\n\n\t  # Read the .lo file\n\t  func_source \"$arg\"\n\n\t  if test -z \"$pic_object\" ||\n\t     test -z \"$non_pic_object\" ||\n\t     test none = \"$pic_object\" &&\n\t     test none = \"$non_pic_object\"; then\n\t    func_fatal_error \"cannot find name of object for '$arg'\"\n\t  fi\n\n\t  # Extract subdirectory from the argument.\n\t  func_dirname \"$arg\" \"/\" \"\"\n\t  xdir=$func_dirname_result\n\n\t  test none = \"$pic_object\" || {\n\t    # Prepend the subdirectory the object is found in.\n\t    pic_object=$xdir$pic_object\n\n\t    if test dlfiles = \"$prev\"; then\n\t      if test yes = \"$build_libtool_libs\" && test yes = \"$dlopen_support\"; then\n\t\tfunc_append dlfiles \" $pic_object\"\n\t\tprev=\n\t\tcontinue\n\t      else\n\t\t# If libtool objects are unsupported, then we need to preload.\n\t\tprev=dlprefiles\n\t      fi\n\t    fi\n\n\t    # CHECK ME:  I think I busted this.  -Ossama\n\t    if test dlprefiles = \"$prev\"; then\n\t      # Preload the old-style object.\n\t      func_append dlprefiles \" $pic_object\"\n\t      prev=\n\t    fi\n\n\t    # A PIC object.\n\t    func_append libobjs \" $pic_object\"\n\t    arg=$pic_object\n\t  }\n\n\t  # Non-PIC object.\n\t  if test none != \"$non_pic_object\"; then\n\t    # Prepend the subdirectory the object is found in.\n\t    non_pic_object=$xdir$non_pic_object\n\n\t    # A standard non-PIC object\n\t    func_append non_pic_objects \" $non_pic_object\"\n\t    if test -z \"$pic_object\" || test none = \"$pic_object\"; then\n\t      arg=$non_pic_object\n\t    fi\n\t  else\n\t    # If the PIC object exists, use it instead.\n\t    # $xdir was prepended to $pic_object above.\n\t    non_pic_object=$pic_object\n\t    func_append non_pic_objects \" $non_pic_object\"\n\t  fi\n\telse\n\t  # Only an error if not doing a dry-run.\n\t  if $opt_dry_run; then\n\t    # Extract subdirectory from the argument.\n\t    func_dirname \"$arg\" \"/\" \"\"\n\t    xdir=$func_dirname_result\n\n\t    func_lo2o \"$arg\"\n\t    pic_object=$xdir$objdir/$func_lo2o_result\n\t    non_pic_object=$xdir$func_lo2o_result\n\t    func_append libobjs \" $pic_object\"\n\t    func_append non_pic_objects \" $non_pic_object\"\n\t  else\n\t    func_fatal_error \"'$arg' is not a valid libtool object\"\n\t  fi\n\tfi\n\t;;\n\n      *.$libext)\n\t# An archive.\n\tfunc_append deplibs \" $arg\"\n\tfunc_append old_deplibs \" $arg\"\n\tcontinue\n\t;;\n\n      *.la)\n\t# A libtool-controlled library.\n\n\tfunc_resolve_sysroot \"$arg\"\n\tif test dlfiles = \"$prev\"; then\n\t  # This library was specified with -dlopen.\n\t  func_append dlfiles \" $func_resolve_sysroot_result\"\n\t  prev=\n\telif test dlprefiles = \"$prev\"; then\n\t  # The library was specified with -dlpreopen.\n\t  func_append dlprefiles \" $func_resolve_sysroot_result\"\n\t  prev=\n\telse\n\t  func_append deplibs \" $func_resolve_sysroot_result\"\n\tfi\n\tcontinue\n\t;;\n\n      # Some other compiler argument.\n      *)\n\t# Unknown arguments in both finalize_command and compile_command need\n\t# to be aesthetically quoted because they are evaled later.\n\tfunc_quote_for_eval \"$arg\"\n\targ=$func_quote_for_eval_result\n\t;;\n      esac # arg\n\n      # Now actually substitute the argument into the commands.\n      if test -n \"$arg\"; then\n\tfunc_append compile_command \" $arg\"\n\tfunc_append finalize_command \" $arg\"\n      fi\n    done # argument parsing loop\n\n    test -n \"$prev\" && \\\n      func_fatal_help \"the '$prevarg' option requires an argument\"\n\n    if test yes = \"$export_dynamic\" && test -n \"$export_dynamic_flag_spec\"; then\n      eval arg=\\\"$export_dynamic_flag_spec\\\"\n      func_append compile_command \" $arg\"\n      func_append finalize_command \" $arg\"\n    fi\n\n    oldlibs=\n    # calculate the name of the file, without its directory\n    func_basename \"$output\"\n    outputname=$func_basename_result\n    libobjs_save=$libobjs\n\n    if test -n \"$shlibpath_var\"; then\n      # get the directories listed in $shlibpath_var\n      eval shlib_search_path=\\`\\$ECHO \\\"\\$$shlibpath_var\\\" \\| \\$SED \\'s/:/ /g\\'\\`\n    else\n      shlib_search_path=\n    fi\n    eval sys_lib_search_path=\\\"$sys_lib_search_path_spec\\\"\n    eval sys_lib_dlsearch_path=\\\"$sys_lib_dlsearch_path_spec\\\"\n\n    # Definition is injected by LT_CONFIG during libtool generation.\n    func_munge_path_list sys_lib_dlsearch_path \"$LT_SYS_LIBRARY_PATH\"\n\n    func_dirname \"$output\" \"/\" \"\"\n    output_objdir=$func_dirname_result$objdir\n    func_to_tool_file \"$output_objdir/\"\n    tool_output_objdir=$func_to_tool_file_result\n    # Create the object directory.\n    func_mkdir_p \"$output_objdir\"\n\n    # Determine the type of output\n    case $output in\n    \"\")\n      func_fatal_help \"you must specify an output file\"\n      ;;\n    *.$libext) linkmode=oldlib ;;\n    *.lo | *.$objext) linkmode=obj ;;\n    *.la) linkmode=lib ;;\n    *) linkmode=prog ;; # Anything else should be a program.\n    esac\n\n    specialdeplibs=\n\n    libs=\n    # Find all interdependent deplibs by searching for libraries\n    # that are linked more than once (e.g. -la -lb -la)\n    for deplib in $deplibs; do\n      if $opt_preserve_dup_deps; then\n\tcase \"$libs \" in\n\t*\" $deplib \"*) func_append specialdeplibs \" $deplib\" ;;\n\tesac\n      fi\n      func_append libs \" $deplib\"\n    done\n\n    if test lib = \"$linkmode\"; then\n      libs=\"$predeps $libs $compiler_lib_search_path $postdeps\"\n\n      # Compute libraries that are listed more than once in $predeps\n      # $postdeps and mark them as special (i.e., whose duplicates are\n      # not to be eliminated).\n      pre_post_deps=\n      if $opt_duplicate_compiler_generated_deps; then\n\tfor pre_post_dep in $predeps $postdeps; do\n\t  case \"$pre_post_deps \" in\n\t  *\" $pre_post_dep \"*) func_append specialdeplibs \" $pre_post_deps\" ;;\n\t  esac\n\t  func_append pre_post_deps \" $pre_post_dep\"\n\tdone\n      fi\n      pre_post_deps=\n    fi\n\n    deplibs=\n    newdependency_libs=\n    newlib_search_path=\n    need_relink=no # whether we're linking any uninstalled libtool libraries\n    notinst_deplibs= # not-installed libtool libraries\n    notinst_path= # paths that contain not-installed libtool libraries\n\n    case $linkmode in\n    lib)\n\tpasses=\"conv dlpreopen link\"\n\tfor file in $dlfiles $dlprefiles; do\n\t  case $file in\n\t  *.la) ;;\n\t  *)\n\t    func_fatal_help \"libraries can '-dlopen' only libtool libraries: $file\"\n\t    ;;\n\t  esac\n\tdone\n\t;;\n    prog)\n\tcompile_deplibs=\n\tfinalize_deplibs=\n\talldeplibs=false\n\tnewdlfiles=\n\tnewdlprefiles=\n\tpasses=\"conv scan dlopen dlpreopen link\"\n\t;;\n    *)  passes=\"conv\"\n\t;;\n    esac\n\n    for pass in $passes; do\n      # The preopen pass in lib mode reverses $deplibs; put it back here\n      # so that -L comes before libs that need it for instance...\n      if test lib,link = \"$linkmode,$pass\"; then\n\t## FIXME: Find the place where the list is rebuilt in the wrong\n\t##        order, and fix it there properly\n        tmp_deplibs=\n\tfor deplib in $deplibs; do\n\t  tmp_deplibs=\"$deplib $tmp_deplibs\"\n\tdone\n\tdeplibs=$tmp_deplibs\n      fi\n\n      if test lib,link = \"$linkmode,$pass\" ||\n\t test prog,scan = \"$linkmode,$pass\"; then\n\tlibs=$deplibs\n\tdeplibs=\n      fi\n      if test prog = \"$linkmode\"; then\n\tcase $pass in\n\tdlopen) libs=$dlfiles ;;\n\tdlpreopen) libs=$dlprefiles ;;\n\tlink)\n\t  libs=\"$deplibs %DEPLIBS%\"\n\t  test \"X$link_all_deplibs\" != Xno && libs=\"$libs $dependency_libs\"\n\t  ;;\n\tesac\n      fi\n      if test lib,dlpreopen = \"$linkmode,$pass\"; then\n\t# Collect and forward deplibs of preopened libtool libs\n\tfor lib in $dlprefiles; do\n\t  # Ignore non-libtool-libs\n\t  dependency_libs=\n\t  func_resolve_sysroot \"$lib\"\n\t  case $lib in\n\t  *.la)\tfunc_source \"$func_resolve_sysroot_result\" ;;\n\t  esac\n\n\t  # Collect preopened libtool deplibs, except any this library\n\t  # has declared as weak libs\n\t  for deplib in $dependency_libs; do\n\t    func_basename \"$deplib\"\n            deplib_base=$func_basename_result\n\t    case \" $weak_libs \" in\n\t    *\" $deplib_base \"*) ;;\n\t    *) func_append deplibs \" $deplib\" ;;\n\t    esac\n\t  done\n\tdone\n\tlibs=$dlprefiles\n      fi\n      if test dlopen = \"$pass\"; then\n\t# Collect dlpreopened libraries\n\tsave_deplibs=$deplibs\n\tdeplibs=\n      fi\n\n      for deplib in $libs; do\n\tlib=\n\tfound=false\n\tcase $deplib in\n\t-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \\\n        |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)\n\t  if test prog,link = \"$linkmode,$pass\"; then\n\t    compile_deplibs=\"$deplib $compile_deplibs\"\n\t    finalize_deplibs=\"$deplib $finalize_deplibs\"\n\t  else\n\t    func_append compiler_flags \" $deplib\"\n\t    if test lib = \"$linkmode\"; then\n\t\tcase \"$new_inherited_linker_flags \" in\n\t\t    *\" $deplib \"*) ;;\n\t\t    * ) func_append new_inherited_linker_flags \" $deplib\" ;;\n\t\tesac\n\t    fi\n\t  fi\n\t  continue\n\t  ;;\n\t-l*)\n\t  if test lib != \"$linkmode\" && test prog != \"$linkmode\"; then\n\t    func_warning \"'-l' is ignored for archives/objects\"\n\t    continue\n\t  fi\n\t  func_stripname '-l' '' \"$deplib\"\n\t  name=$func_stripname_result\n\t  if test lib = \"$linkmode\"; then\n\t    searchdirs=\"$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path\"\n\t  else\n\t    searchdirs=\"$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path\"\n\t  fi\n\t  for searchdir in $searchdirs; do\n\t    for search_ext in .la $std_shrext .so .a; do\n\t      # Search the libtool library\n\t      lib=$searchdir/lib$name$search_ext\n\t      if test -f \"$lib\"; then\n\t\tif test .la = \"$search_ext\"; then\n\t\t  found=:\n\t\telse\n\t\t  found=false\n\t\tfi\n\t\tbreak 2\n\t      fi\n\t    done\n\t  done\n\t  if $found; then\n\t    # deplib is a libtool library\n\t    # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,\n\t    # We need to do some special things here, and not later.\n\t    if test yes = \"$allow_libtool_libs_with_static_runtimes\"; then\n\t      case \" $predeps $postdeps \" in\n\t      *\" $deplib \"*)\n\t\tif func_lalib_p \"$lib\"; then\n\t\t  library_names=\n\t\t  old_library=\n\t\t  func_source \"$lib\"\n\t\t  for l in $old_library $library_names; do\n\t\t    ll=$l\n\t\t  done\n\t\t  if test \"X$ll\" = \"X$old_library\"; then # only static version available\n\t\t    found=false\n\t\t    func_dirname \"$lib\" \"\" \".\"\n\t\t    ladir=$func_dirname_result\n\t\t    lib=$ladir/$old_library\n\t\t    if test prog,link = \"$linkmode,$pass\"; then\n\t\t      compile_deplibs=\"$deplib $compile_deplibs\"\n\t\t      finalize_deplibs=\"$deplib $finalize_deplibs\"\n\t\t    else\n\t\t      deplibs=\"$deplib $deplibs\"\n\t\t      test lib = \"$linkmode\" && newdependency_libs=\"$deplib $newdependency_libs\"\n\t\t    fi\n\t\t    continue\n\t\t  fi\n\t\tfi\n\t\t;;\n\t      *) ;;\n\t      esac\n\t    fi\n\t  else\n\t    # deplib doesn't seem to be a libtool library\n\t    if test prog,link = \"$linkmode,$pass\"; then\n\t      compile_deplibs=\"$deplib $compile_deplibs\"\n\t      finalize_deplibs=\"$deplib $finalize_deplibs\"\n\t    else\n\t      deplibs=\"$deplib $deplibs\"\n\t      test lib = \"$linkmode\" && newdependency_libs=\"$deplib $newdependency_libs\"\n\t    fi\n\t    continue\n\t  fi\n\t  ;; # -l\n\t*.ltframework)\n\t  if test prog,link = \"$linkmode,$pass\"; then\n\t    compile_deplibs=\"$deplib $compile_deplibs\"\n\t    finalize_deplibs=\"$deplib $finalize_deplibs\"\n\t  else\n\t    deplibs=\"$deplib $deplibs\"\n\t    if test lib = \"$linkmode\"; then\n\t\tcase \"$new_inherited_linker_flags \" in\n\t\t    *\" $deplib \"*) ;;\n\t\t    * ) func_append new_inherited_linker_flags \" $deplib\" ;;\n\t\tesac\n\t    fi\n\t  fi\n\t  continue\n\t  ;;\n\t-L*)\n\t  case $linkmode in\n\t  lib)\n\t    deplibs=\"$deplib $deplibs\"\n\t    test conv = \"$pass\" && continue\n\t    newdependency_libs=\"$deplib $newdependency_libs\"\n\t    func_stripname '-L' '' \"$deplib\"\n\t    func_resolve_sysroot \"$func_stripname_result\"\n\t    func_append newlib_search_path \" $func_resolve_sysroot_result\"\n\t    ;;\n\t  prog)\n\t    if test conv = \"$pass\"; then\n\t      deplibs=\"$deplib $deplibs\"\n\t      continue\n\t    fi\n\t    if test scan = \"$pass\"; then\n\t      deplibs=\"$deplib $deplibs\"\n\t    else\n\t      compile_deplibs=\"$deplib $compile_deplibs\"\n\t      finalize_deplibs=\"$deplib $finalize_deplibs\"\n\t    fi\n\t    func_stripname '-L' '' \"$deplib\"\n\t    func_resolve_sysroot \"$func_stripname_result\"\n\t    func_append newlib_search_path \" $func_resolve_sysroot_result\"\n\t    ;;\n\t  *)\n\t    func_warning \"'-L' is ignored for archives/objects\"\n\t    ;;\n\t  esac # linkmode\n\t  continue\n\t  ;; # -L\n\t-R*)\n\t  if test link = \"$pass\"; then\n\t    func_stripname '-R' '' \"$deplib\"\n\t    func_resolve_sysroot \"$func_stripname_result\"\n\t    dir=$func_resolve_sysroot_result\n\t    # Make sure the xrpath contains only unique directories.\n\t    case \"$xrpath \" in\n\t    *\" $dir \"*) ;;\n\t    *) func_append xrpath \" $dir\" ;;\n\t    esac\n\t  fi\n\t  deplibs=\"$deplib $deplibs\"\n\t  continue\n\t  ;;\n\t*.la)\n\t  func_resolve_sysroot \"$deplib\"\n\t  lib=$func_resolve_sysroot_result\n\t  ;;\n\t*.$libext)\n\t  if test conv = \"$pass\"; then\n\t    deplibs=\"$deplib $deplibs\"\n\t    continue\n\t  fi\n\t  case $linkmode in\n\t  lib)\n\t    # Linking convenience modules into shared libraries is allowed,\n\t    # but linking other static libraries is non-portable.\n\t    case \" $dlpreconveniencelibs \" in\n\t    *\" $deplib \"*) ;;\n\t    *)\n\t      valid_a_lib=false\n\t      case $deplibs_check_method in\n\t\tmatch_pattern*)\n\t\t  set dummy $deplibs_check_method; shift\n\t\t  match_pattern_regex=`expr \"$deplibs_check_method\" : \"$1 \\(.*\\)\"`\n\t\t  if eval \"\\$ECHO \\\"$deplib\\\"\" 2>/dev/null | $SED 10q \\\n\t\t    | $EGREP \"$match_pattern_regex\" > /dev/null; then\n\t\t    valid_a_lib=:\n\t\t  fi\n\t\t;;\n\t\tpass_all)\n\t\t  valid_a_lib=:\n\t\t;;\n\t      esac\n\t      if $valid_a_lib; then\n\t\techo\n\t\t$ECHO \"*** Warning: Linking the shared library $output against the\"\n\t\t$ECHO \"*** static library $deplib is not portable!\"\n\t\tdeplibs=\"$deplib $deplibs\"\n\t      else\n\t\techo\n\t\t$ECHO \"*** Warning: Trying to link with static lib archive $deplib.\"\n\t\techo \"*** I have the capability to make that library automatically link in when\"\n\t\techo \"*** you link to this library.  But I can only do this if you have a\"\n\t\techo \"*** shared version of the library, which you do not appear to have\"\n\t\techo \"*** because the file extensions .$libext of this argument makes me believe\"\n\t\techo \"*** that it is just a static archive that I should not use here.\"\n\t      fi\n\t      ;;\n\t    esac\n\t    continue\n\t    ;;\n\t  prog)\n\t    if test link != \"$pass\"; then\n\t      deplibs=\"$deplib $deplibs\"\n\t    else\n\t      compile_deplibs=\"$deplib $compile_deplibs\"\n\t      finalize_deplibs=\"$deplib $finalize_deplibs\"\n\t    fi\n\t    continue\n\t    ;;\n\t  esac # linkmode\n\t  ;; # *.$libext\n\t*.lo | *.$objext)\n\t  if test conv = \"$pass\"; then\n\t    deplibs=\"$deplib $deplibs\"\n\t  elif test prog = \"$linkmode\"; then\n\t    if test dlpreopen = \"$pass\" || test yes != \"$dlopen_support\" || test no = \"$build_libtool_libs\"; then\n\t      # If there is no dlopen support or we're linking statically,\n\t      # we need to preload.\n\t      func_append newdlprefiles \" $deplib\"\n\t      compile_deplibs=\"$deplib $compile_deplibs\"\n\t      finalize_deplibs=\"$deplib $finalize_deplibs\"\n\t    else\n\t      func_append newdlfiles \" $deplib\"\n\t    fi\n\t  fi\n\t  continue\n\t  ;;\n\t%DEPLIBS%)\n\t  alldeplibs=:\n\t  continue\n\t  ;;\n\tesac # case $deplib\n\n\t$found || test -f \"$lib\" \\\n\t  || func_fatal_error \"cannot find the library '$lib' or unhandled argument '$deplib'\"\n\n\t# Check to see that this really is a libtool archive.\n\tfunc_lalib_unsafe_p \"$lib\" \\\n\t  || func_fatal_error \"'$lib' is not a valid libtool archive\"\n\n\tfunc_dirname \"$lib\" \"\" \".\"\n\tladir=$func_dirname_result\n\n\tdlname=\n\tdlopen=\n\tdlpreopen=\n\tlibdir=\n\tlibrary_names=\n\told_library=\n\tinherited_linker_flags=\n\t# If the library was installed with an old release of libtool,\n\t# it will not redefine variables installed, or shouldnotlink\n\tinstalled=yes\n\tshouldnotlink=no\n\tavoidtemprpath=\n\n\n\t# Read the .la file\n\tfunc_source \"$lib\"\n\n\t# Convert \"-framework foo\" to \"foo.ltframework\"\n\tif test -n \"$inherited_linker_flags\"; then\n\t  tmp_inherited_linker_flags=`$ECHO \"$inherited_linker_flags\" | $SED 's/-framework \\([^ $]*\\)/\\1.ltframework/g'`\n\t  for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do\n\t    case \" $new_inherited_linker_flags \" in\n\t      *\" $tmp_inherited_linker_flag \"*) ;;\n\t      *) func_append new_inherited_linker_flags \" $tmp_inherited_linker_flag\";;\n\t    esac\n\t  done\n\tfi\n\tdependency_libs=`$ECHO \" $dependency_libs\" | $SED 's% \\([^ $]*\\).ltframework% -framework \\1%g'`\n\tif test lib,link = \"$linkmode,$pass\" ||\n\t   test prog,scan = \"$linkmode,$pass\" ||\n\t   { test prog != \"$linkmode\" && test lib != \"$linkmode\"; }; then\n\t  test -n \"$dlopen\" && func_append dlfiles \" $dlopen\"\n\t  test -n \"$dlpreopen\" && func_append dlprefiles \" $dlpreopen\"\n\tfi\n\n\tif test conv = \"$pass\"; then\n\t  # Only check for convenience libraries\n\t  deplibs=\"$lib $deplibs\"\n\t  if test -z \"$libdir\"; then\n\t    if test -z \"$old_library\"; then\n\t      func_fatal_error \"cannot find name of link library for '$lib'\"\n\t    fi\n\t    # It is a libtool convenience library, so add in its objects.\n\t    func_append convenience \" $ladir/$objdir/$old_library\"\n\t    func_append old_convenience \" $ladir/$objdir/$old_library\"\n\t    tmp_libs=\n\t    for deplib in $dependency_libs; do\n\t      deplibs=\"$deplib $deplibs\"\n\t      if $opt_preserve_dup_deps; then\n\t\tcase \"$tmp_libs \" in\n\t\t*\" $deplib \"*) func_append specialdeplibs \" $deplib\" ;;\n\t\tesac\n\t      fi\n\t      func_append tmp_libs \" $deplib\"\n\t    done\n\t  elif test prog != \"$linkmode\" && test lib != \"$linkmode\"; then\n\t    func_fatal_error \"'$lib' is not a convenience library\"\n\t  fi\n\t  continue\n\tfi # $pass = conv\n\n\n\t# Get the name of the library we link against.\n\tlinklib=\n\tif test -n \"$old_library\" &&\n\t   { test yes = \"$prefer_static_libs\" ||\n\t     test built,no = \"$prefer_static_libs,$installed\"; }; then\n\t  linklib=$old_library\n\telse\n\t  for l in $old_library $library_names; do\n\t    linklib=$l\n\t  done\n\tfi\n\tif test -z \"$linklib\"; then\n\t  func_fatal_error \"cannot find name of link library for '$lib'\"\n\tfi\n\n\t# This library was specified with -dlopen.\n\tif test dlopen = \"$pass\"; then\n\t  test -z \"$libdir\" \\\n\t    && func_fatal_error \"cannot -dlopen a convenience library: '$lib'\"\n\t  if test -z \"$dlname\" ||\n\t     test yes != \"$dlopen_support\" ||\n\t     test no = \"$build_libtool_libs\"\n\t  then\n\t    # If there is no dlname, no dlopen support or we're linking\n\t    # statically, we need to preload.  We also need to preload any\n\t    # dependent libraries so libltdl's deplib preloader doesn't\n\t    # bomb out in the load deplibs phase.\n\t    func_append dlprefiles \" $lib $dependency_libs\"\n\t  else\n\t    func_append newdlfiles \" $lib\"\n\t  fi\n\t  continue\n\tfi # $pass = dlopen\n\n\t# We need an absolute path.\n\tcase $ladir in\n\t[\\\\/]* | [A-Za-z]:[\\\\/]*) abs_ladir=$ladir ;;\n\t*)\n\t  abs_ladir=`cd \"$ladir\" && pwd`\n\t  if test -z \"$abs_ladir\"; then\n\t    func_warning \"cannot determine absolute directory name of '$ladir'\"\n\t    func_warning \"passing it literally to the linker, although it might fail\"\n\t    abs_ladir=$ladir\n\t  fi\n\t  ;;\n\tesac\n\tfunc_basename \"$lib\"\n\tlaname=$func_basename_result\n\n\t# Find the relevant object directory and library name.\n\tif test yes = \"$installed\"; then\n\t  if test ! -f \"$lt_sysroot$libdir/$linklib\" && test -f \"$abs_ladir/$linklib\"; then\n\t    func_warning \"library '$lib' was moved.\"\n\t    dir=$ladir\n\t    absdir=$abs_ladir\n\t    libdir=$abs_ladir\n\t  else\n\t    dir=$lt_sysroot$libdir\n\t    absdir=$lt_sysroot$libdir\n\t  fi\n\t  test yes = \"$hardcode_automatic\" && avoidtemprpath=yes\n\telse\n\t  if test ! -f \"$ladir/$objdir/$linklib\" && test -f \"$abs_ladir/$linklib\"; then\n\t    dir=$ladir\n\t    absdir=$abs_ladir\n\t    # Remove this search path later\n\t    func_append notinst_path \" $abs_ladir\"\n\t  else\n\t    dir=$ladir/$objdir\n\t    absdir=$abs_ladir/$objdir\n\t    # Remove this search path later\n\t    func_append notinst_path \" $abs_ladir\"\n\t  fi\n\tfi # $installed = yes\n\tfunc_stripname 'lib' '.la' \"$laname\"\n\tname=$func_stripname_result\n\n\t# This library was specified with -dlpreopen.\n\tif test dlpreopen = \"$pass\"; then\n\t  if test -z \"$libdir\" && test prog = \"$linkmode\"; then\n\t    func_fatal_error \"only libraries may -dlpreopen a convenience library: '$lib'\"\n\t  fi\n\t  case $host in\n\t    # special handling for platforms with PE-DLLs.\n\t    *cygwin* | *mingw* | *cegcc* )\n\t      # Linker will automatically link against shared library if both\n\t      # static and shared are present.  Therefore, ensure we extract\n\t      # symbols from the import library if a shared library is present\n\t      # (otherwise, the dlopen module name will be incorrect).  We do\n\t      # this by putting the import library name into $newdlprefiles.\n\t      # We recover the dlopen module name by 'saving' the la file\n\t      # name in a special purpose variable, and (later) extracting the\n\t      # dlname from the la file.\n\t      if test -n \"$dlname\"; then\n\t        func_tr_sh \"$dir/$linklib\"\n\t        eval \"libfile_$func_tr_sh_result=\\$abs_ladir/\\$laname\"\n\t        func_append newdlprefiles \" $dir/$linklib\"\n\t      else\n\t        func_append newdlprefiles \" $dir/$old_library\"\n\t        # Keep a list of preopened convenience libraries to check\n\t        # that they are being used correctly in the link pass.\n\t        test -z \"$libdir\" && \\\n\t          func_append dlpreconveniencelibs \" $dir/$old_library\"\n\t      fi\n\t    ;;\n\t    * )\n\t      # Prefer using a static library (so that no silly _DYNAMIC symbols\n\t      # are required to link).\n\t      if test -n \"$old_library\"; then\n\t        func_append newdlprefiles \" $dir/$old_library\"\n\t        # Keep a list of preopened convenience libraries to check\n\t        # that they are being used correctly in the link pass.\n\t        test -z \"$libdir\" && \\\n\t          func_append dlpreconveniencelibs \" $dir/$old_library\"\n\t      # Otherwise, use the dlname, so that lt_dlopen finds it.\n\t      elif test -n \"$dlname\"; then\n\t        func_append newdlprefiles \" $dir/$dlname\"\n\t      else\n\t        func_append newdlprefiles \" $dir/$linklib\"\n\t      fi\n\t    ;;\n\t  esac\n\tfi # $pass = dlpreopen\n\n\tif test -z \"$libdir\"; then\n\t  # Link the convenience library\n\t  if test lib = \"$linkmode\"; then\n\t    deplibs=\"$dir/$old_library $deplibs\"\n\t  elif test prog,link = \"$linkmode,$pass\"; then\n\t    compile_deplibs=\"$dir/$old_library $compile_deplibs\"\n\t    finalize_deplibs=\"$dir/$old_library $finalize_deplibs\"\n\t  else\n\t    deplibs=\"$lib $deplibs\" # used for prog,scan pass\n\t  fi\n\t  continue\n\tfi\n\n\n\tif test prog = \"$linkmode\" && test link != \"$pass\"; then\n\t  func_append newlib_search_path \" $ladir\"\n\t  deplibs=\"$lib $deplibs\"\n\n\t  linkalldeplibs=false\n\t  if test no != \"$link_all_deplibs\" || test -z \"$library_names\" ||\n\t     test no = \"$build_libtool_libs\"; then\n\t    linkalldeplibs=:\n\t  fi\n\n\t  tmp_libs=\n\t  for deplib in $dependency_libs; do\n\t    case $deplib in\n\t    -L*) func_stripname '-L' '' \"$deplib\"\n\t         func_resolve_sysroot \"$func_stripname_result\"\n\t         func_append newlib_search_path \" $func_resolve_sysroot_result\"\n\t\t ;;\n\t    esac\n\t    # Need to link against all dependency_libs?\n\t    if $linkalldeplibs; then\n\t      deplibs=\"$deplib $deplibs\"\n\t    else\n\t      # Need to hardcode shared library paths\n\t      # or/and link against static libraries\n\t      newdependency_libs=\"$deplib $newdependency_libs\"\n\t    fi\n\t    if $opt_preserve_dup_deps; then\n\t      case \"$tmp_libs \" in\n\t      *\" $deplib \"*) func_append specialdeplibs \" $deplib\" ;;\n\t      esac\n\t    fi\n\t    func_append tmp_libs \" $deplib\"\n\t  done # for deplib\n\t  continue\n\tfi # $linkmode = prog...\n\n\tif test prog,link = \"$linkmode,$pass\"; then\n\t  if test -n \"$library_names\" &&\n\t     { { test no = \"$prefer_static_libs\" ||\n\t         test built,yes = \"$prefer_static_libs,$installed\"; } ||\n\t       test -z \"$old_library\"; }; then\n\t    # We need to hardcode the library path\n\t    if test -n \"$shlibpath_var\" && test -z \"$avoidtemprpath\"; then\n\t      # Make sure the rpath contains only unique directories.\n\t      case $temp_rpath: in\n\t      *\"$absdir:\"*) ;;\n\t      *) func_append temp_rpath \"$absdir:\" ;;\n\t      esac\n\t    fi\n\n\t    # Hardcode the library path.\n\t    # Skip directories that are in the system default run-time\n\t    # search path.\n\t    case \" $sys_lib_dlsearch_path \" in\n\t    *\" $absdir \"*) ;;\n\t    *)\n\t      case \"$compile_rpath \" in\n\t      *\" $absdir \"*) ;;\n\t      *) func_append compile_rpath \" $absdir\" ;;\n\t      esac\n\t      ;;\n\t    esac\n\t    case \" $sys_lib_dlsearch_path \" in\n\t    *\" $libdir \"*) ;;\n\t    *)\n\t      case \"$finalize_rpath \" in\n\t      *\" $libdir \"*) ;;\n\t      *) func_append finalize_rpath \" $libdir\" ;;\n\t      esac\n\t      ;;\n\t    esac\n\t  fi # $linkmode,$pass = prog,link...\n\n\t  if $alldeplibs &&\n\t     { test pass_all = \"$deplibs_check_method\" ||\n\t       { test yes = \"$build_libtool_libs\" &&\n\t\t test -n \"$library_names\"; }; }; then\n\t    # We only need to search for static libraries\n\t    continue\n\t  fi\n\tfi\n\n\tlink_static=no # Whether the deplib will be linked statically\n\tuse_static_libs=$prefer_static_libs\n\tif test built = \"$use_static_libs\" && test yes = \"$installed\"; then\n\t  use_static_libs=no\n\tfi\n\tif test -n \"$library_names\" &&\n\t   { test no = \"$use_static_libs\" || test -z \"$old_library\"; }; then\n\t  case $host in\n\t  *cygwin* | *mingw* | *cegcc* | *os2*)\n\t      # No point in relinking DLLs because paths are not encoded\n\t      func_append notinst_deplibs \" $lib\"\n\t      need_relink=no\n\t    ;;\n\t  *)\n\t    if test no = \"$installed\"; then\n\t      func_append notinst_deplibs \" $lib\"\n\t      need_relink=yes\n\t    fi\n\t    ;;\n\t  esac\n\t  # This is a shared library\n\n\t  # Warn about portability, can't link against -module's on some\n\t  # systems (darwin).  Don't bleat about dlopened modules though!\n\t  dlopenmodule=\n\t  for dlpremoduletest in $dlprefiles; do\n\t    if test \"X$dlpremoduletest\" = \"X$lib\"; then\n\t      dlopenmodule=$dlpremoduletest\n\t      break\n\t    fi\n\t  done\n\t  if test -z \"$dlopenmodule\" && test yes = \"$shouldnotlink\" && test link = \"$pass\"; then\n\t    echo\n\t    if test prog = \"$linkmode\"; then\n\t      $ECHO \"*** Warning: Linking the executable $output against the loadable module\"\n\t    else\n\t      $ECHO \"*** Warning: Linking the shared library $output against the loadable module\"\n\t    fi\n\t    $ECHO \"*** $linklib is not portable!\"\n\t  fi\n\t  if test lib = \"$linkmode\" &&\n\t     test yes = \"$hardcode_into_libs\"; then\n\t    # Hardcode the library path.\n\t    # Skip directories that are in the system default run-time\n\t    # search path.\n\t    case \" $sys_lib_dlsearch_path \" in\n\t    *\" $absdir \"*) ;;\n\t    *)\n\t      case \"$compile_rpath \" in\n\t      *\" $absdir \"*) ;;\n\t      *) func_append compile_rpath \" $absdir\" ;;\n\t      esac\n\t      ;;\n\t    esac\n\t    case \" $sys_lib_dlsearch_path \" in\n\t    *\" $libdir \"*) ;;\n\t    *)\n\t      case \"$finalize_rpath \" in\n\t      *\" $libdir \"*) ;;\n\t      *) func_append finalize_rpath \" $libdir\" ;;\n\t      esac\n\t      ;;\n\t    esac\n\t  fi\n\n\t  if test -n \"$old_archive_from_expsyms_cmds\"; then\n\t    # figure out the soname\n\t    set dummy $library_names\n\t    shift\n\t    realname=$1\n\t    shift\n\t    libname=`eval \"\\\\$ECHO \\\"$libname_spec\\\"\"`\n\t    # use dlname if we got it. it's perfectly good, no?\n\t    if test -n \"$dlname\"; then\n\t      soname=$dlname\n\t    elif test -n \"$soname_spec\"; then\n\t      # bleh windows\n\t      case $host in\n\t      *cygwin* | mingw* | *cegcc* | *os2*)\n\t        func_arith $current - $age\n\t\tmajor=$func_arith_result\n\t\tversuffix=-$major\n\t\t;;\n\t      esac\n\t      eval soname=\\\"$soname_spec\\\"\n\t    else\n\t      soname=$realname\n\t    fi\n\n\t    # Make a new name for the extract_expsyms_cmds to use\n\t    soroot=$soname\n\t    func_basename \"$soroot\"\n\t    soname=$func_basename_result\n\t    func_stripname 'lib' '.dll' \"$soname\"\n\t    newlib=libimp-$func_stripname_result.a\n\n\t    # If the library has no export list, then create one now\n\t    if test -f \"$output_objdir/$soname-def\"; then :\n\t    else\n\t      func_verbose \"extracting exported symbol list from '$soname'\"\n\t      func_execute_cmds \"$extract_expsyms_cmds\" 'exit $?'\n\t    fi\n\n\t    # Create $newlib\n\t    if test -f \"$output_objdir/$newlib\"; then :; else\n\t      func_verbose \"generating import library for '$soname'\"\n\t      func_execute_cmds \"$old_archive_from_expsyms_cmds\" 'exit $?'\n\t    fi\n\t    # make sure the library variables are pointing to the new library\n\t    dir=$output_objdir\n\t    linklib=$newlib\n\t  fi # test -n \"$old_archive_from_expsyms_cmds\"\n\n\t  if test prog = \"$linkmode\" || test relink != \"$opt_mode\"; then\n\t    add_shlibpath=\n\t    add_dir=\n\t    add=\n\t    lib_linked=yes\n\t    case $hardcode_action in\n\t    immediate | unsupported)\n\t      if test no = \"$hardcode_direct\"; then\n\t\tadd=$dir/$linklib\n\t\tcase $host in\n\t\t  *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;;\n\t\t  *-*-sysv4*uw2*) add_dir=-L$dir ;;\n\t\t  *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \\\n\t\t    *-*-unixware7*) add_dir=-L$dir ;;\n\t\t  *-*-darwin* )\n\t\t    # if the lib is a (non-dlopened) module then we cannot\n\t\t    # link against it, someone is ignoring the earlier warnings\n\t\t    if /usr/bin/file -L $add 2> /dev/null |\n\t\t\t $GREP \": [^:]* bundle\" >/dev/null; then\n\t\t      if test \"X$dlopenmodule\" != \"X$lib\"; then\n\t\t\t$ECHO \"*** Warning: lib $linklib is a module, not a shared library\"\n\t\t\tif test -z \"$old_library\"; then\n\t\t\t  echo\n\t\t\t  echo \"*** And there doesn't seem to be a static archive available\"\n\t\t\t  echo \"*** The link will probably fail, sorry\"\n\t\t\telse\n\t\t\t  add=$dir/$old_library\n\t\t\tfi\n\t\t      elif test -n \"$old_library\"; then\n\t\t\tadd=$dir/$old_library\n\t\t      fi\n\t\t    fi\n\t\tesac\n\t      elif test no = \"$hardcode_minus_L\"; then\n\t\tcase $host in\n\t\t*-*-sunos*) add_shlibpath=$dir ;;\n\t\tesac\n\t\tadd_dir=-L$dir\n\t\tadd=-l$name\n\t      elif test no = \"$hardcode_shlibpath_var\"; then\n\t\tadd_shlibpath=$dir\n\t\tadd=-l$name\n\t      else\n\t\tlib_linked=no\n\t      fi\n\t      ;;\n\t    relink)\n\t      if test yes = \"$hardcode_direct\" &&\n\t         test no = \"$hardcode_direct_absolute\"; then\n\t\tadd=$dir/$linklib\n\t      elif test yes = \"$hardcode_minus_L\"; then\n\t\tadd_dir=-L$absdir\n\t\t# Try looking first in the location we're being installed to.\n\t\tif test -n \"$inst_prefix_dir\"; then\n\t\t  case $libdir in\n\t\t    [\\\\/]*)\n\t\t      func_append add_dir \" -L$inst_prefix_dir$libdir\"\n\t\t      ;;\n\t\t  esac\n\t\tfi\n\t\tadd=-l$name\n\t      elif test yes = \"$hardcode_shlibpath_var\"; then\n\t\tadd_shlibpath=$dir\n\t\tadd=-l$name\n\t      else\n\t\tlib_linked=no\n\t      fi\n\t      ;;\n\t    *) lib_linked=no ;;\n\t    esac\n\n\t    if test yes != \"$lib_linked\"; then\n\t      func_fatal_configuration \"unsupported hardcode properties\"\n\t    fi\n\n\t    if test -n \"$add_shlibpath\"; then\n\t      case :$compile_shlibpath: in\n\t      *\":$add_shlibpath:\"*) ;;\n\t      *) func_append compile_shlibpath \"$add_shlibpath:\" ;;\n\t      esac\n\t    fi\n\t    if test prog = \"$linkmode\"; then\n\t      test -n \"$add_dir\" && compile_deplibs=\"$add_dir $compile_deplibs\"\n\t      test -n \"$add\" && compile_deplibs=\"$add $compile_deplibs\"\n\t    else\n\t      test -n \"$add_dir\" && deplibs=\"$add_dir $deplibs\"\n\t      test -n \"$add\" && deplibs=\"$add $deplibs\"\n\t      if test yes != \"$hardcode_direct\" &&\n\t\t test yes != \"$hardcode_minus_L\" &&\n\t\t test yes = \"$hardcode_shlibpath_var\"; then\n\t\tcase :$finalize_shlibpath: in\n\t\t*\":$libdir:\"*) ;;\n\t\t*) func_append finalize_shlibpath \"$libdir:\" ;;\n\t\tesac\n\t      fi\n\t    fi\n\t  fi\n\n\t  if test prog = \"$linkmode\" || test relink = \"$opt_mode\"; then\n\t    add_shlibpath=\n\t    add_dir=\n\t    add=\n\t    # Finalize command for both is simple: just hardcode it.\n\t    if test yes = \"$hardcode_direct\" &&\n\t       test no = \"$hardcode_direct_absolute\"; then\n\t      add=$libdir/$linklib\n\t    elif test yes = \"$hardcode_minus_L\"; then\n\t      add_dir=-L$libdir\n\t      add=-l$name\n\t    elif test yes = \"$hardcode_shlibpath_var\"; then\n\t      case :$finalize_shlibpath: in\n\t      *\":$libdir:\"*) ;;\n\t      *) func_append finalize_shlibpath \"$libdir:\" ;;\n\t      esac\n\t      add=-l$name\n\t    elif test yes = \"$hardcode_automatic\"; then\n\t      if test -n \"$inst_prefix_dir\" &&\n\t\t test -f \"$inst_prefix_dir$libdir/$linklib\"; then\n\t\tadd=$inst_prefix_dir$libdir/$linklib\n\t      else\n\t\tadd=$libdir/$linklib\n\t      fi\n\t    else\n\t      # We cannot seem to hardcode it, guess we'll fake it.\n\t      add_dir=-L$libdir\n\t      # Try looking first in the location we're being installed to.\n\t      if test -n \"$inst_prefix_dir\"; then\n\t\tcase $libdir in\n\t\t  [\\\\/]*)\n\t\t    func_append add_dir \" -L$inst_prefix_dir$libdir\"\n\t\t    ;;\n\t\tesac\n\t      fi\n\t      add=-l$name\n\t    fi\n\n\t    if test prog = \"$linkmode\"; then\n\t      test -n \"$add_dir\" && finalize_deplibs=\"$add_dir $finalize_deplibs\"\n\t      test -n \"$add\" && finalize_deplibs=\"$add $finalize_deplibs\"\n\t    else\n\t      test -n \"$add_dir\" && deplibs=\"$add_dir $deplibs\"\n\t      test -n \"$add\" && deplibs=\"$add $deplibs\"\n\t    fi\n\t  fi\n\telif test prog = \"$linkmode\"; then\n\t  # Here we assume that one of hardcode_direct or hardcode_minus_L\n\t  # is not unsupported.  This is valid on all known static and\n\t  # shared platforms.\n\t  if test unsupported != \"$hardcode_direct\"; then\n\t    test -n \"$old_library\" && linklib=$old_library\n\t    compile_deplibs=\"$dir/$linklib $compile_deplibs\"\n\t    finalize_deplibs=\"$dir/$linklib $finalize_deplibs\"\n\t  else\n\t    compile_deplibs=\"-l$name -L$dir $compile_deplibs\"\n\t    finalize_deplibs=\"-l$name -L$dir $finalize_deplibs\"\n\t  fi\n\telif test yes = \"$build_libtool_libs\"; then\n\t  # Not a shared library\n\t  if test pass_all != \"$deplibs_check_method\"; then\n\t    # We're trying link a shared library against a static one\n\t    # but the system doesn't support it.\n\n\t    # Just print a warning and add the library to dependency_libs so\n\t    # that the program can be linked against the static library.\n\t    echo\n\t    $ECHO \"*** Warning: This system cannot link to static lib archive $lib.\"\n\t    echo \"*** I have the capability to make that library automatically link in when\"\n\t    echo \"*** you link to this library.  But I can only do this if you have a\"\n\t    echo \"*** shared version of the library, which you do not appear to have.\"\n\t    if test yes = \"$module\"; then\n\t      echo \"*** But as you try to build a module library, libtool will still create \"\n\t      echo \"*** a static module, that should work as long as the dlopening application\"\n\t      echo \"*** is linked with the -dlopen flag to resolve symbols at runtime.\"\n\t      if test -z \"$global_symbol_pipe\"; then\n\t\techo\n\t\techo \"*** However, this would only work if libtool was able to extract symbol\"\n\t\techo \"*** lists from a program, using 'nm' or equivalent, but libtool could\"\n\t\techo \"*** not find such a program.  So, this module is probably useless.\"\n\t\techo \"*** 'nm' from GNU binutils and a full rebuild may help.\"\n\t      fi\n\t      if test no = \"$build_old_libs\"; then\n\t\tbuild_libtool_libs=module\n\t\tbuild_old_libs=yes\n\t      else\n\t\tbuild_libtool_libs=no\n\t      fi\n\t    fi\n\t  else\n\t    deplibs=\"$dir/$old_library $deplibs\"\n\t    link_static=yes\n\t  fi\n\tfi # link shared/static library?\n\n\tif test lib = \"$linkmode\"; then\n\t  if test -n \"$dependency_libs\" &&\n\t     { test yes != \"$hardcode_into_libs\" ||\n\t       test yes = \"$build_old_libs\" ||\n\t       test yes = \"$link_static\"; }; then\n\t    # Extract -R from dependency_libs\n\t    temp_deplibs=\n\t    for libdir in $dependency_libs; do\n\t      case $libdir in\n\t      -R*) func_stripname '-R' '' \"$libdir\"\n\t           temp_xrpath=$func_stripname_result\n\t\t   case \" $xrpath \" in\n\t\t   *\" $temp_xrpath \"*) ;;\n\t\t   *) func_append xrpath \" $temp_xrpath\";;\n\t\t   esac;;\n\t      *) func_append temp_deplibs \" $libdir\";;\n\t      esac\n\t    done\n\t    dependency_libs=$temp_deplibs\n\t  fi\n\n\t  func_append newlib_search_path \" $absdir\"\n\t  # Link against this library\n\t  test no = \"$link_static\" && newdependency_libs=\"$abs_ladir/$laname $newdependency_libs\"\n\t  # ... and its dependency_libs\n\t  tmp_libs=\n\t  for deplib in $dependency_libs; do\n\t    newdependency_libs=\"$deplib $newdependency_libs\"\n\t    case $deplib in\n              -L*) func_stripname '-L' '' \"$deplib\"\n                   func_resolve_sysroot \"$func_stripname_result\";;\n              *) func_resolve_sysroot \"$deplib\" ;;\n            esac\n\t    if $opt_preserve_dup_deps; then\n\t      case \"$tmp_libs \" in\n\t      *\" $func_resolve_sysroot_result \"*)\n                func_append specialdeplibs \" $func_resolve_sysroot_result\" ;;\n\t      esac\n\t    fi\n\t    func_append tmp_libs \" $func_resolve_sysroot_result\"\n\t  done\n\n\t  if test no != \"$link_all_deplibs\"; then\n\t    # Add the search paths of all dependency libraries\n\t    for deplib in $dependency_libs; do\n\t      path=\n\t      case $deplib in\n\t      -L*) path=$deplib ;;\n\t      *.la)\n\t        func_resolve_sysroot \"$deplib\"\n\t        deplib=$func_resolve_sysroot_result\n\t        func_dirname \"$deplib\" \"\" \".\"\n\t\tdir=$func_dirname_result\n\t\t# We need an absolute path.\n\t\tcase $dir in\n\t\t[\\\\/]* | [A-Za-z]:[\\\\/]*) absdir=$dir ;;\n\t\t*)\n\t\t  absdir=`cd \"$dir\" && pwd`\n\t\t  if test -z \"$absdir\"; then\n\t\t    func_warning \"cannot determine absolute directory name of '$dir'\"\n\t\t    absdir=$dir\n\t\t  fi\n\t\t  ;;\n\t\tesac\n\t\tif $GREP \"^installed=no\" $deplib > /dev/null; then\n\t\tcase $host in\n\t\t*-*-darwin*)\n\t\t  depdepl=\n\t\t  eval deplibrary_names=`$SED -n -e 's/^library_names=\\(.*\\)$/\\1/p' $deplib`\n\t\t  if test -n \"$deplibrary_names\"; then\n\t\t    for tmp in $deplibrary_names; do\n\t\t      depdepl=$tmp\n\t\t    done\n\t\t    if test -f \"$absdir/$objdir/$depdepl\"; then\n\t\t      depdepl=$absdir/$objdir/$depdepl\n\t\t      darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`\n                      if test -z \"$darwin_install_name\"; then\n                          darwin_install_name=`$OTOOL64 -L $depdepl  | awk '{if (NR == 2) {print $1;exit}}'`\n                      fi\n\t\t      func_append compiler_flags \" $wl-dylib_file $wl$darwin_install_name:$depdepl\"\n\t\t      func_append linker_flags \" -dylib_file $darwin_install_name:$depdepl\"\n\t\t      path=\n\t\t    fi\n\t\t  fi\n\t\t  ;;\n\t\t*)\n\t\t  path=-L$absdir/$objdir\n\t\t  ;;\n\t\tesac\n\t\telse\n\t\t  eval libdir=`$SED -n -e 's/^libdir=\\(.*\\)$/\\1/p' $deplib`\n\t\t  test -z \"$libdir\" && \\\n\t\t    func_fatal_error \"'$deplib' is not a valid libtool archive\"\n\t\t  test \"$absdir\" != \"$libdir\" && \\\n\t\t    func_warning \"'$deplib' seems to be moved\"\n\n\t\t  path=-L$absdir\n\t\tfi\n\t\t;;\n\t      esac\n\t      case \" $deplibs \" in\n\t      *\" $path \"*) ;;\n\t      *) deplibs=\"$path $deplibs\" ;;\n\t      esac\n\t    done\n\t  fi # link_all_deplibs != no\n\tfi # linkmode = lib\n      done # for deplib in $libs\n      if test link = \"$pass\"; then\n\tif test prog = \"$linkmode\"; then\n\t  compile_deplibs=\"$new_inherited_linker_flags $compile_deplibs\"\n\t  finalize_deplibs=\"$new_inherited_linker_flags $finalize_deplibs\"\n\telse\n\t  compiler_flags=\"$compiler_flags \"`$ECHO \" $new_inherited_linker_flags\" | $SED 's% \\([^ $]*\\).ltframework% -framework \\1%g'`\n\tfi\n      fi\n      dependency_libs=$newdependency_libs\n      if test dlpreopen = \"$pass\"; then\n\t# Link the dlpreopened libraries before other libraries\n\tfor deplib in $save_deplibs; do\n\t  deplibs=\"$deplib $deplibs\"\n\tdone\n      fi\n      if test dlopen != \"$pass\"; then\n\ttest conv = \"$pass\" || {\n\t  # Make sure lib_search_path contains only unique directories.\n\t  lib_search_path=\n\t  for dir in $newlib_search_path; do\n\t    case \"$lib_search_path \" in\n\t    *\" $dir \"*) ;;\n\t    *) func_append lib_search_path \" $dir\" ;;\n\t    esac\n\t  done\n\t  newlib_search_path=\n\t}\n\n\tif test prog,link = \"$linkmode,$pass\"; then\n\t  vars=\"compile_deplibs finalize_deplibs\"\n\telse\n\t  vars=deplibs\n\tfi\n\tfor var in $vars dependency_libs; do\n\t  # Add libraries to $var in reverse order\n\t  eval tmp_libs=\\\"\\$$var\\\"\n\t  new_libs=\n\t  for deplib in $tmp_libs; do\n\t    # FIXME: Pedantically, this is the right thing to do, so\n\t    #        that some nasty dependency loop isn't accidentally\n\t    #        broken:\n\t    #new_libs=\"$deplib $new_libs\"\n\t    # Pragmatically, this seems to cause very few problems in\n\t    # practice:\n\t    case $deplib in\n\t    -L*) new_libs=\"$deplib $new_libs\" ;;\n\t    -R*) ;;\n\t    *)\n\t      # And here is the reason: when a library appears more\n\t      # than once as an explicit dependence of a library, or\n\t      # is implicitly linked in more than once by the\n\t      # compiler, it is considered special, and multiple\n\t      # occurrences thereof are not removed.  Compare this\n\t      # with having the same library being listed as a\n\t      # dependency of multiple other libraries: in this case,\n\t      # we know (pedantically, we assume) the library does not\n\t      # need to be listed more than once, so we keep only the\n\t      # last copy.  This is not always right, but it is rare\n\t      # enough that we require users that really mean to play\n\t      # such unportable linking tricks to link the library\n\t      # using -Wl,-lname, so that libtool does not consider it\n\t      # for duplicate removal.\n\t      case \" $specialdeplibs \" in\n\t      *\" $deplib \"*) new_libs=\"$deplib $new_libs\" ;;\n\t      *)\n\t\tcase \" $new_libs \" in\n\t\t*\" $deplib \"*) ;;\n\t\t*) new_libs=\"$deplib $new_libs\" ;;\n\t\tesac\n\t\t;;\n\t      esac\n\t      ;;\n\t    esac\n\t  done\n\t  tmp_libs=\n\t  for deplib in $new_libs; do\n\t    case $deplib in\n\t    -L*)\n\t      case \" $tmp_libs \" in\n\t      *\" $deplib \"*) ;;\n\t      *) func_append tmp_libs \" $deplib\" ;;\n\t      esac\n\t      ;;\n\t    *) func_append tmp_libs \" $deplib\" ;;\n\t    esac\n\t  done\n\t  eval $var=\\\"$tmp_libs\\\"\n\tdone # for var\n      fi\n\n      # Add Sun CC postdeps if required:\n      test CXX = \"$tagname\" && {\n        case $host_os in\n        linux*)\n          case `$CC -V 2>&1 | sed 5q` in\n          *Sun\\ C*) # Sun C++ 5.9\n            func_suncc_cstd_abi\n\n            if test no != \"$suncc_use_cstd_abi\"; then\n              func_append postdeps ' -library=Cstd -library=Crun'\n            fi\n            ;;\n          esac\n          ;;\n\n        solaris*)\n          func_cc_basename \"$CC\"\n          case $func_cc_basename_result in\n          CC* | sunCC*)\n            func_suncc_cstd_abi\n\n            if test no != \"$suncc_use_cstd_abi\"; then\n              func_append postdeps ' -library=Cstd -library=Crun'\n            fi\n            ;;\n          esac\n          ;;\n        esac\n      }\n\n      # Last step: remove runtime libs from dependency_libs\n      # (they stay in deplibs)\n      tmp_libs=\n      for i in $dependency_libs; do\n\tcase \" $predeps $postdeps $compiler_lib_search_path \" in\n\t*\" $i \"*)\n\t  i=\n\t  ;;\n\tesac\n\tif test -n \"$i\"; then\n\t  func_append tmp_libs \" $i\"\n\tfi\n      done\n      dependency_libs=$tmp_libs\n    done # for pass\n    if test prog = \"$linkmode\"; then\n      dlfiles=$newdlfiles\n    fi\n    if test prog = \"$linkmode\" || test lib = \"$linkmode\"; then\n      dlprefiles=$newdlprefiles\n    fi\n\n    case $linkmode in\n    oldlib)\n      if test -n \"$dlfiles$dlprefiles\" || test no != \"$dlself\"; then\n\tfunc_warning \"'-dlopen' is ignored for archives\"\n      fi\n\n      case \" $deplibs\" in\n      *\\ -l* | *\\ -L*)\n\tfunc_warning \"'-l' and '-L' are ignored for archives\" ;;\n      esac\n\n      test -n \"$rpath\" && \\\n\tfunc_warning \"'-rpath' is ignored for archives\"\n\n      test -n \"$xrpath\" && \\\n\tfunc_warning \"'-R' is ignored for archives\"\n\n      test -n \"$vinfo\" && \\\n\tfunc_warning \"'-version-info/-version-number' is ignored for archives\"\n\n      test -n \"$release\" && \\\n\tfunc_warning \"'-release' is ignored for archives\"\n\n      test -n \"$export_symbols$export_symbols_regex\" && \\\n\tfunc_warning \"'-export-symbols' is ignored for archives\"\n\n      # Now set the variables for building old libraries.\n      build_libtool_libs=no\n      oldlibs=$output\n      func_append objs \"$old_deplibs\"\n      ;;\n\n    lib)\n      # Make sure we only generate libraries of the form 'libNAME.la'.\n      case $outputname in\n      lib*)\n\tfunc_stripname 'lib' '.la' \"$outputname\"\n\tname=$func_stripname_result\n\teval shared_ext=\\\"$shrext_cmds\\\"\n\teval libname=\\\"$libname_spec\\\"\n\t;;\n      *)\n\ttest no = \"$module\" \\\n\t  && func_fatal_help \"libtool library '$output' must begin with 'lib'\"\n\n\tif test no != \"$need_lib_prefix\"; then\n\t  # Add the \"lib\" prefix for modules if required\n\t  func_stripname '' '.la' \"$outputname\"\n\t  name=$func_stripname_result\n\t  eval shared_ext=\\\"$shrext_cmds\\\"\n\t  eval libname=\\\"$libname_spec\\\"\n\telse\n\t  func_stripname '' '.la' \"$outputname\"\n\t  libname=$func_stripname_result\n\tfi\n\t;;\n      esac\n\n      if test -n \"$objs\"; then\n\tif test pass_all != \"$deplibs_check_method\"; then\n\t  func_fatal_error \"cannot build libtool library '$output' from non-libtool objects on this host:$objs\"\n\telse\n\t  echo\n\t  $ECHO \"*** Warning: Linking the shared library $output against the non-libtool\"\n\t  $ECHO \"*** objects $objs is not portable!\"\n\t  func_append libobjs \" $objs\"\n\tfi\n      fi\n\n      test no = \"$dlself\" \\\n\t|| func_warning \"'-dlopen self' is ignored for libtool libraries\"\n\n      set dummy $rpath\n      shift\n      test 1 -lt \"$#\" \\\n\t&& func_warning \"ignoring multiple '-rpath's for a libtool library\"\n\n      install_libdir=$1\n\n      oldlibs=\n      if test -z \"$rpath\"; then\n\tif test yes = \"$build_libtool_libs\"; then\n\t  # Building a libtool convenience library.\n\t  # Some compilers have problems with a '.al' extension so\n\t  # convenience libraries should have the same extension an\n\t  # archive normally would.\n\t  oldlibs=\"$output_objdir/$libname.$libext $oldlibs\"\n\t  build_libtool_libs=convenience\n\t  build_old_libs=yes\n\tfi\n\n\ttest -n \"$vinfo\" && \\\n\t  func_warning \"'-version-info/-version-number' is ignored for convenience libraries\"\n\n\ttest -n \"$release\" && \\\n\t  func_warning \"'-release' is ignored for convenience libraries\"\n      else\n\n\t# Parse the version information argument.\n\tsave_ifs=$IFS; IFS=:\n\tset dummy $vinfo 0 0 0\n\tshift\n\tIFS=$save_ifs\n\n\ttest -n \"$7\" && \\\n\t  func_fatal_help \"too many parameters to '-version-info'\"\n\n\t# convert absolute version numbers to libtool ages\n\t# this retains compatibility with .la files and attempts\n\t# to make the code below a bit more comprehensible\n\n\tcase $vinfo_number in\n\tyes)\n\t  number_major=$1\n\t  number_minor=$2\n\t  number_revision=$3\n\t  #\n\t  # There are really only two kinds -- those that\n\t  # use the current revision as the major version\n\t  # and those that subtract age and use age as\n\t  # a minor version.  But, then there is irix\n\t  # that has an extra 1 added just for fun\n\t  #\n\t  case $version_type in\n\t  # correct linux to gnu/linux during the next big refactor\n\t  darwin|freebsd-elf|linux|osf|windows|none)\n\t    func_arith $number_major + $number_minor\n\t    current=$func_arith_result\n\t    age=$number_minor\n\t    revision=$number_revision\n\t    ;;\n\t  freebsd-aout|qnx|sunos)\n\t    current=$number_major\n\t    revision=$number_minor\n\t    age=0\n\t    ;;\n\t  irix|nonstopux)\n\t    func_arith $number_major + $number_minor\n\t    current=$func_arith_result\n\t    age=$number_minor\n\t    revision=$number_minor\n\t    lt_irix_increment=no\n\t    ;;\n\t  *)\n\t    func_fatal_configuration \"$modename: unknown library version type '$version_type'\"\n\t    ;;\n\t  esac\n\t  ;;\n\tno)\n\t  current=$1\n\t  revision=$2\n\t  age=$3\n\t  ;;\n\tesac\n\n\t# Check that each of the things are valid numbers.\n\tcase $current in\n\t0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;\n\t*)\n\t  func_error \"CURRENT '$current' must be a nonnegative integer\"\n\t  func_fatal_error \"'$vinfo' is not valid version information\"\n\t  ;;\n\tesac\n\n\tcase $revision in\n\t0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;\n\t*)\n\t  func_error \"REVISION '$revision' must be a nonnegative integer\"\n\t  func_fatal_error \"'$vinfo' is not valid version information\"\n\t  ;;\n\tesac\n\n\tcase $age in\n\t0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;\n\t*)\n\t  func_error \"AGE '$age' must be a nonnegative integer\"\n\t  func_fatal_error \"'$vinfo' is not valid version information\"\n\t  ;;\n\tesac\n\n\tif test \"$age\" -gt \"$current\"; then\n\t  func_error \"AGE '$age' is greater than the current interface number '$current'\"\n\t  func_fatal_error \"'$vinfo' is not valid version information\"\n\tfi\n\n\t# Calculate the version variables.\n\tmajor=\n\tversuffix=\n\tverstring=\n\tcase $version_type in\n\tnone) ;;\n\n\tdarwin)\n\t  # Like Linux, but with the current version available in\n\t  # verstring for coding it into the library header\n\t  func_arith $current - $age\n\t  major=.$func_arith_result\n\t  versuffix=$major.$age.$revision\n\t  # Darwin ld doesn't like 0 for these options...\n\t  func_arith $current + 1\n\t  minor_current=$func_arith_result\n\t  xlcverstring=\"$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision\"\n\t  verstring=\"-compatibility_version $minor_current -current_version $minor_current.$revision\"\n          # On Darwin other compilers\n          case $CC in\n              nagfor*)\n                  verstring=\"$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision\"\n                  ;;\n              *)\n                  verstring=\"-compatibility_version $minor_current -current_version $minor_current.$revision\"\n                  ;;\n          esac\n\t  ;;\n\n\tfreebsd-aout)\n\t  major=.$current\n\t  versuffix=.$current.$revision\n\t  ;;\n\n\tfreebsd-elf)\n\t  func_arith $current - $age\n\t  major=.$func_arith_result\n\t  versuffix=$major.$age.$revision\n\t  ;;\n\n\tirix | nonstopux)\n\t  if test no = \"$lt_irix_increment\"; then\n\t    func_arith $current - $age\n\t  else\n\t    func_arith $current - $age + 1\n\t  fi\n\t  major=$func_arith_result\n\n\t  case $version_type in\n\t    nonstopux) verstring_prefix=nonstopux ;;\n\t    *)         verstring_prefix=sgi ;;\n\t  esac\n\t  verstring=$verstring_prefix$major.$revision\n\n\t  # Add in all the interfaces that we are compatible with.\n\t  loop=$revision\n\t  while test 0 -ne \"$loop\"; do\n\t    func_arith $revision - $loop\n\t    iface=$func_arith_result\n\t    func_arith $loop - 1\n\t    loop=$func_arith_result\n\t    verstring=$verstring_prefix$major.$iface:$verstring\n\t  done\n\n\t  # Before this point, $major must not contain '.'.\n\t  major=.$major\n\t  versuffix=$major.$revision\n\t  ;;\n\n\tlinux) # correct to gnu/linux during the next big refactor\n\t  func_arith $current - $age\n\t  major=.$func_arith_result\n\t  versuffix=$major.$age.$revision\n\t  ;;\n\n\tosf)\n\t  func_arith $current - $age\n\t  major=.$func_arith_result\n\t  versuffix=.$current.$age.$revision\n\t  verstring=$current.$age.$revision\n\n\t  # Add in all the interfaces that we are compatible with.\n\t  loop=$age\n\t  while test 0 -ne \"$loop\"; do\n\t    func_arith $current - $loop\n\t    iface=$func_arith_result\n\t    func_arith $loop - 1\n\t    loop=$func_arith_result\n\t    verstring=$verstring:$iface.0\n\t  done\n\n\t  # Make executables depend on our current version.\n\t  func_append verstring \":$current.0\"\n\t  ;;\n\n\tqnx)\n\t  major=.$current\n\t  versuffix=.$current\n\t  ;;\n\n\tsco)\n\t  major=.$current\n\t  versuffix=.$current\n\t  ;;\n\n\tsunos)\n\t  major=.$current\n\t  versuffix=.$current.$revision\n\t  ;;\n\n\twindows)\n\t  # Use '-' rather than '.', since we only want one\n\t  # extension on DOS 8.3 file systems.\n\t  func_arith $current - $age\n\t  major=$func_arith_result\n\t  versuffix=-$major\n\t  ;;\n\n\t*)\n\t  func_fatal_configuration \"unknown library version type '$version_type'\"\n\t  ;;\n\tesac\n\n\t# Clear the version info if we defaulted, and they specified a release.\n\tif test -z \"$vinfo\" && test -n \"$release\"; then\n\t  major=\n\t  case $version_type in\n\t  darwin)\n\t    # we can't check for \"0.0\" in archive_cmds due to quoting\n\t    # problems, so we reset it completely\n\t    verstring=\n\t    ;;\n\t  *)\n\t    verstring=0.0\n\t    ;;\n\t  esac\n\t  if test no = \"$need_version\"; then\n\t    versuffix=\n\t  else\n\t    versuffix=.0.0\n\t  fi\n\tfi\n\n\t# Remove version info from name if versioning should be avoided\n\tif test yes,no = \"$avoid_version,$need_version\"; then\n\t  major=\n\t  versuffix=\n\t  verstring=\n\tfi\n\n\t# Check to see if the archive will have undefined symbols.\n\tif test yes = \"$allow_undefined\"; then\n\t  if test unsupported = \"$allow_undefined_flag\"; then\n\t    if test yes = \"$build_old_libs\"; then\n\t      func_warning \"undefined symbols not allowed in $host shared libraries; building static only\"\n\t      build_libtool_libs=no\n\t    else\n\t      func_fatal_error \"can't build $host shared library unless -no-undefined is specified\"\n\t    fi\n\t  fi\n\telse\n\t  # Don't allow undefined symbols.\n\t  allow_undefined_flag=$no_undefined_flag\n\tfi\n\n      fi\n\n      func_generate_dlsyms \"$libname\" \"$libname\" :\n      func_append libobjs \" $symfileobj\"\n      test \" \" = \"$libobjs\" && libobjs=\n\n      if test relink != \"$opt_mode\"; then\n\t# Remove our outputs, but don't remove object files since they\n\t# may have been created when compiling PIC objects.\n\tremovelist=\n\ttempremovelist=`$ECHO \"$output_objdir/*\"`\n\tfor p in $tempremovelist; do\n\t  case $p in\n\t    *.$objext | *.gcno)\n\t       ;;\n\t    $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*)\n\t       if test -n \"$precious_files_regex\"; then\n\t\t if $ECHO \"$p\" | $EGREP -e \"$precious_files_regex\" >/dev/null 2>&1\n\t\t then\n\t\t   continue\n\t\t fi\n\t       fi\n\t       func_append removelist \" $p\"\n\t       ;;\n\t    *) ;;\n\t  esac\n\tdone\n\ttest -n \"$removelist\" && \\\n\t  func_show_eval \"${RM}r \\$removelist\"\n      fi\n\n      # Now set the variables for building old libraries.\n      if test yes = \"$build_old_libs\" && test convenience != \"$build_libtool_libs\"; then\n\tfunc_append oldlibs \" $output_objdir/$libname.$libext\"\n\n\t# Transform .lo files to .o files.\n\toldobjs=\"$objs \"`$ECHO \"$libobjs\" | $SP2NL | $SED \"/\\.$libext$/d; $lo2o\" | $NL2SP`\n      fi\n\n      # Eliminate all temporary directories.\n      #for path in $notinst_path; do\n      #\tlib_search_path=`$ECHO \"$lib_search_path \" | $SED \"s% $path % %g\"`\n      #\tdeplibs=`$ECHO \"$deplibs \" | $SED \"s% -L$path % %g\"`\n      #\tdependency_libs=`$ECHO \"$dependency_libs \" | $SED \"s% -L$path % %g\"`\n      #done\n\n      if test -n \"$xrpath\"; then\n\t# If the user specified any rpath flags, then add them.\n\ttemp_xrpath=\n\tfor libdir in $xrpath; do\n\t  func_replace_sysroot \"$libdir\"\n\t  func_append temp_xrpath \" -R$func_replace_sysroot_result\"\n\t  case \"$finalize_rpath \" in\n\t  *\" $libdir \"*) ;;\n\t  *) func_append finalize_rpath \" $libdir\" ;;\n\t  esac\n\tdone\n\tif test yes != \"$hardcode_into_libs\" || test yes = \"$build_old_libs\"; then\n\t  dependency_libs=\"$temp_xrpath $dependency_libs\"\n\tfi\n      fi\n\n      # Make sure dlfiles contains only unique files that won't be dlpreopened\n      old_dlfiles=$dlfiles\n      dlfiles=\n      for lib in $old_dlfiles; do\n\tcase \" $dlprefiles $dlfiles \" in\n\t*\" $lib \"*) ;;\n\t*) func_append dlfiles \" $lib\" ;;\n\tesac\n      done\n\n      # Make sure dlprefiles contains only unique files\n      old_dlprefiles=$dlprefiles\n      dlprefiles=\n      for lib in $old_dlprefiles; do\n\tcase \"$dlprefiles \" in\n\t*\" $lib \"*) ;;\n\t*) func_append dlprefiles \" $lib\" ;;\n\tesac\n      done\n\n      if test yes = \"$build_libtool_libs\"; then\n\tif test -n \"$rpath\"; then\n\t  case $host in\n\t  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)\n\t    # these systems don't actually have a c library (as such)!\n\t    ;;\n\t  *-*-rhapsody* | *-*-darwin1.[012])\n\t    # Rhapsody C library is in the System framework\n\t    func_append deplibs \" System.ltframework\"\n\t    ;;\n\t  *-*-netbsd*)\n\t    # Don't link with libc until the a.out ld.so is fixed.\n\t    ;;\n\t  *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)\n\t    # Do not include libc due to us having libc/libc_r.\n\t    ;;\n\t  *-*-sco3.2v5* | *-*-sco5v6*)\n\t    # Causes problems with __ctype\n\t    ;;\n\t  *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)\n\t    # Compiler inserts libc in the correct place for threads to work\n\t    ;;\n\t  *)\n\t    # Add libc to deplibs on all other systems if necessary.\n\t    if test yes = \"$build_libtool_need_lc\"; then\n\t      func_append deplibs \" -lc\"\n\t    fi\n\t    ;;\n\t  esac\n\tfi\n\n\t# Transform deplibs into only deplibs that can be linked in shared.\n\tname_save=$name\n\tlibname_save=$libname\n\trelease_save=$release\n\tversuffix_save=$versuffix\n\tmajor_save=$major\n\t# I'm not sure if I'm treating the release correctly.  I think\n\t# release should show up in the -l (ie -lgmp5) so we don't want to\n\t# add it in twice.  Is that correct?\n\trelease=\n\tversuffix=\n\tmajor=\n\tnewdeplibs=\n\tdroppeddeps=no\n\tcase $deplibs_check_method in\n\tpass_all)\n\t  # Don't check for shared/static.  Everything works.\n\t  # This might be a little naive.  We might want to check\n\t  # whether the library exists or not.  But this is on\n\t  # osf3 & osf4 and I'm not really sure... Just\n\t  # implementing what was already the behavior.\n\t  newdeplibs=$deplibs\n\t  ;;\n\ttest_compile)\n\t  # This code stresses the \"libraries are programs\" paradigm to its\n\t  # limits. Maybe even breaks it.  We compile a program, linking it\n\t  # against the deplibs as a proxy for the library.  Then we can check\n\t  # whether they linked in statically or dynamically with ldd.\n\t  $opt_dry_run || $RM conftest.c\n\t  cat > conftest.c <<EOF\n\t  int main() { return 0; }\nEOF\n\t  $opt_dry_run || $RM conftest\n\t  if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then\n\t    ldd_output=`ldd conftest`\n\t    for i in $deplibs; do\n\t      case $i in\n\t      -l*)\n\t\tfunc_stripname -l '' \"$i\"\n\t\tname=$func_stripname_result\n\t\tif test yes = \"$allow_libtool_libs_with_static_runtimes\"; then\n\t\t  case \" $predeps $postdeps \" in\n\t\t  *\" $i \"*)\n\t\t    func_append newdeplibs \" $i\"\n\t\t    i=\n\t\t    ;;\n\t\t  esac\n\t\tfi\n\t\tif test -n \"$i\"; then\n\t\t  libname=`eval \"\\\\$ECHO \\\"$libname_spec\\\"\"`\n\t\t  deplib_matches=`eval \"\\\\$ECHO \\\"$library_names_spec\\\"\"`\n\t\t  set dummy $deplib_matches; shift\n\t\t  deplib_match=$1\n\t\t  if test `expr \"$ldd_output\" : \".*$deplib_match\"` -ne 0; then\n\t\t    func_append newdeplibs \" $i\"\n\t\t  else\n\t\t    droppeddeps=yes\n\t\t    echo\n\t\t    $ECHO \"*** Warning: dynamic linker does not accept needed library $i.\"\n\t\t    echo \"*** I have the capability to make that library automatically link in when\"\n\t\t    echo \"*** you link to this library.  But I can only do this if you have a\"\n\t\t    echo \"*** shared version of the library, which I believe you do not have\"\n\t\t    echo \"*** because a test_compile did reveal that the linker did not use it for\"\n\t\t    echo \"*** its dynamic dependency list that programs get resolved with at runtime.\"\n\t\t  fi\n\t\tfi\n\t\t;;\n\t      *)\n\t\tfunc_append newdeplibs \" $i\"\n\t\t;;\n\t      esac\n\t    done\n\t  else\n\t    # Error occurred in the first compile.  Let's try to salvage\n\t    # the situation: Compile a separate program for each library.\n\t    for i in $deplibs; do\n\t      case $i in\n\t      -l*)\n\t\tfunc_stripname -l '' \"$i\"\n\t\tname=$func_stripname_result\n\t\t$opt_dry_run || $RM conftest\n\t\tif $LTCC $LTCFLAGS -o conftest conftest.c $i; then\n\t\t  ldd_output=`ldd conftest`\n\t\t  if test yes = \"$allow_libtool_libs_with_static_runtimes\"; then\n\t\t    case \" $predeps $postdeps \" in\n\t\t    *\" $i \"*)\n\t\t      func_append newdeplibs \" $i\"\n\t\t      i=\n\t\t      ;;\n\t\t    esac\n\t\t  fi\n\t\t  if test -n \"$i\"; then\n\t\t    libname=`eval \"\\\\$ECHO \\\"$libname_spec\\\"\"`\n\t\t    deplib_matches=`eval \"\\\\$ECHO \\\"$library_names_spec\\\"\"`\n\t\t    set dummy $deplib_matches; shift\n\t\t    deplib_match=$1\n\t\t    if test `expr \"$ldd_output\" : \".*$deplib_match\"` -ne 0; then\n\t\t      func_append newdeplibs \" $i\"\n\t\t    else\n\t\t      droppeddeps=yes\n\t\t      echo\n\t\t      $ECHO \"*** Warning: dynamic linker does not accept needed library $i.\"\n\t\t      echo \"*** I have the capability to make that library automatically link in when\"\n\t\t      echo \"*** you link to this library.  But I can only do this if you have a\"\n\t\t      echo \"*** shared version of the library, which you do not appear to have\"\n\t\t      echo \"*** because a test_compile did reveal that the linker did not use this one\"\n\t\t      echo \"*** as a dynamic dependency that programs can get resolved with at runtime.\"\n\t\t    fi\n\t\t  fi\n\t\telse\n\t\t  droppeddeps=yes\n\t\t  echo\n\t\t  $ECHO \"*** Warning!  Library $i is needed by this library but I was not able to\"\n\t\t  echo \"*** make it link in!  You will probably need to install it or some\"\n\t\t  echo \"*** library that it depends on before this library will be fully\"\n\t\t  echo \"*** functional.  Installing it before continuing would be even better.\"\n\t\tfi\n\t\t;;\n\t      *)\n\t\tfunc_append newdeplibs \" $i\"\n\t\t;;\n\t      esac\n\t    done\n\t  fi\n\t  ;;\n\tfile_magic*)\n\t  set dummy $deplibs_check_method; shift\n\t  file_magic_regex=`expr \"$deplibs_check_method\" : \"$1 \\(.*\\)\"`\n\t  for a_deplib in $deplibs; do\n\t    case $a_deplib in\n\t    -l*)\n\t      func_stripname -l '' \"$a_deplib\"\n\t      name=$func_stripname_result\n\t      if test yes = \"$allow_libtool_libs_with_static_runtimes\"; then\n\t\tcase \" $predeps $postdeps \" in\n\t\t*\" $a_deplib \"*)\n\t\t  func_append newdeplibs \" $a_deplib\"\n\t\t  a_deplib=\n\t\t  ;;\n\t\tesac\n\t      fi\n\t      if test -n \"$a_deplib\"; then\n\t\tlibname=`eval \"\\\\$ECHO \\\"$libname_spec\\\"\"`\n\t\tif test -n \"$file_magic_glob\"; then\n\t\t  libnameglob=`func_echo_all \"$libname\" | $SED -e $file_magic_glob`\n\t\telse\n\t\t  libnameglob=$libname\n\t\tfi\n\t\ttest yes = \"$want_nocaseglob\" && nocaseglob=`shopt -p nocaseglob`\n\t\tfor i in $lib_search_path $sys_lib_search_path $shlib_search_path; do\n\t\t  if test yes = \"$want_nocaseglob\"; then\n\t\t    shopt -s nocaseglob\n\t\t    potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`\n\t\t    $nocaseglob\n\t\t  else\n\t\t    potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`\n\t\t  fi\n\t\t  for potent_lib in $potential_libs; do\n\t\t      # Follow soft links.\n\t\t      if ls -lLd \"$potent_lib\" 2>/dev/null |\n\t\t\t $GREP \" -> \" >/dev/null; then\n\t\t\tcontinue\n\t\t      fi\n\t\t      # The statement above tries to avoid entering an\n\t\t      # endless loop below, in case of cyclic links.\n\t\t      # We might still enter an endless loop, since a link\n\t\t      # loop can be closed while we follow links,\n\t\t      # but so what?\n\t\t      potlib=$potent_lib\n\t\t      while test -h \"$potlib\" 2>/dev/null; do\n\t\t\tpotliblink=`ls -ld $potlib | $SED 's/.* -> //'`\n\t\t\tcase $potliblink in\n\t\t\t[\\\\/]* | [A-Za-z]:[\\\\/]*) potlib=$potliblink;;\n\t\t\t*) potlib=`$ECHO \"$potlib\" | $SED 's|[^/]*$||'`\"$potliblink\";;\n\t\t\tesac\n\t\t      done\n\t\t      if eval $file_magic_cmd \\\"\\$potlib\\\" 2>/dev/null |\n\t\t\t $SED -e 10q |\n\t\t\t $EGREP \"$file_magic_regex\" > /dev/null; then\n\t\t\tfunc_append newdeplibs \" $a_deplib\"\n\t\t\ta_deplib=\n\t\t\tbreak 2\n\t\t      fi\n\t\t  done\n\t\tdone\n\t      fi\n\t      if test -n \"$a_deplib\"; then\n\t\tdroppeddeps=yes\n\t\techo\n\t\t$ECHO \"*** Warning: linker path does not have real file for library $a_deplib.\"\n\t\techo \"*** I have the capability to make that library automatically link in when\"\n\t\techo \"*** you link to this library.  But I can only do this if you have a\"\n\t\techo \"*** shared version of the library, which you do not appear to have\"\n\t\techo \"*** because I did check the linker path looking for a file starting\"\n\t\tif test -z \"$potlib\"; then\n\t\t  $ECHO \"*** with $libname but no candidates were found. (...for file magic test)\"\n\t\telse\n\t\t  $ECHO \"*** with $libname and none of the candidates passed a file format test\"\n\t\t  $ECHO \"*** using a file magic. Last file checked: $potlib\"\n\t\tfi\n\t      fi\n\t      ;;\n\t    *)\n\t      # Add a -L argument.\n\t      func_append newdeplibs \" $a_deplib\"\n\t      ;;\n\t    esac\n\t  done # Gone through all deplibs.\n\t  ;;\n\tmatch_pattern*)\n\t  set dummy $deplibs_check_method; shift\n\t  match_pattern_regex=`expr \"$deplibs_check_method\" : \"$1 \\(.*\\)\"`\n\t  for a_deplib in $deplibs; do\n\t    case $a_deplib in\n\t    -l*)\n\t      func_stripname -l '' \"$a_deplib\"\n\t      name=$func_stripname_result\n\t      if test yes = \"$allow_libtool_libs_with_static_runtimes\"; then\n\t\tcase \" $predeps $postdeps \" in\n\t\t*\" $a_deplib \"*)\n\t\t  func_append newdeplibs \" $a_deplib\"\n\t\t  a_deplib=\n\t\t  ;;\n\t\tesac\n\t      fi\n\t      if test -n \"$a_deplib\"; then\n\t\tlibname=`eval \"\\\\$ECHO \\\"$libname_spec\\\"\"`\n\t\tfor i in $lib_search_path $sys_lib_search_path $shlib_search_path; do\n\t\t  potential_libs=`ls $i/$libname[.-]* 2>/dev/null`\n\t\t  for potent_lib in $potential_libs; do\n\t\t    potlib=$potent_lib # see symlink-check above in file_magic test\n\t\t    if eval \"\\$ECHO \\\"$potent_lib\\\"\" 2>/dev/null | $SED 10q | \\\n\t\t       $EGREP \"$match_pattern_regex\" > /dev/null; then\n\t\t      func_append newdeplibs \" $a_deplib\"\n\t\t      a_deplib=\n\t\t      break 2\n\t\t    fi\n\t\t  done\n\t\tdone\n\t      fi\n\t      if test -n \"$a_deplib\"; then\n\t\tdroppeddeps=yes\n\t\techo\n\t\t$ECHO \"*** Warning: linker path does not have real file for library $a_deplib.\"\n\t\techo \"*** I have the capability to make that library automatically link in when\"\n\t\techo \"*** you link to this library.  But I can only do this if you have a\"\n\t\techo \"*** shared version of the library, which you do not appear to have\"\n\t\techo \"*** because I did check the linker path looking for a file starting\"\n\t\tif test -z \"$potlib\"; then\n\t\t  $ECHO \"*** with $libname but no candidates were found. (...for regex pattern test)\"\n\t\telse\n\t\t  $ECHO \"*** with $libname and none of the candidates passed a file format test\"\n\t\t  $ECHO \"*** using a regex pattern. Last file checked: $potlib\"\n\t\tfi\n\t      fi\n\t      ;;\n\t    *)\n\t      # Add a -L argument.\n\t      func_append newdeplibs \" $a_deplib\"\n\t      ;;\n\t    esac\n\t  done # Gone through all deplibs.\n\t  ;;\n\tnone | unknown | *)\n\t  newdeplibs=\n\t  tmp_deplibs=`$ECHO \" $deplibs\" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'`\n\t  if test yes = \"$allow_libtool_libs_with_static_runtimes\"; then\n\t    for i in $predeps $postdeps; do\n\t      # can't use Xsed below, because $i might contain '/'\n\t      tmp_deplibs=`$ECHO \" $tmp_deplibs\" | $SED \"s|$i||\"`\n\t    done\n\t  fi\n\t  case $tmp_deplibs in\n\t  *[!\\\t\\ ]*)\n\t    echo\n\t    if test none = \"$deplibs_check_method\"; then\n\t      echo \"*** Warning: inter-library dependencies are not supported in this platform.\"\n\t    else\n\t      echo \"*** Warning: inter-library dependencies are not known to be supported.\"\n\t    fi\n\t    echo \"*** All declared inter-library dependencies are being dropped.\"\n\t    droppeddeps=yes\n\t    ;;\n\t  esac\n\t  ;;\n\tesac\n\tversuffix=$versuffix_save\n\tmajor=$major_save\n\trelease=$release_save\n\tlibname=$libname_save\n\tname=$name_save\n\n\tcase $host in\n\t*-*-rhapsody* | *-*-darwin1.[012])\n\t  # On Rhapsody replace the C library with the System framework\n\t  newdeplibs=`$ECHO \" $newdeplibs\" | $SED 's/ -lc / System.ltframework /'`\n\t  ;;\n\tesac\n\n\tif test yes = \"$droppeddeps\"; then\n\t  if test yes = \"$module\"; then\n\t    echo\n\t    echo \"*** Warning: libtool could not satisfy all declared inter-library\"\n\t    $ECHO \"*** dependencies of module $libname.  Therefore, libtool will create\"\n\t    echo \"*** a static module, that should work as long as the dlopening\"\n\t    echo \"*** application is linked with the -dlopen flag.\"\n\t    if test -z \"$global_symbol_pipe\"; then\n\t      echo\n\t      echo \"*** However, this would only work if libtool was able to extract symbol\"\n\t      echo \"*** lists from a program, using 'nm' or equivalent, but libtool could\"\n\t      echo \"*** not find such a program.  So, this module is probably useless.\"\n\t      echo \"*** 'nm' from GNU binutils and a full rebuild may help.\"\n\t    fi\n\t    if test no = \"$build_old_libs\"; then\n\t      oldlibs=$output_objdir/$libname.$libext\n\t      build_libtool_libs=module\n\t      build_old_libs=yes\n\t    else\n\t      build_libtool_libs=no\n\t    fi\n\t  else\n\t    echo \"*** The inter-library dependencies that have been dropped here will be\"\n\t    echo \"*** automatically added whenever a program is linked with this library\"\n\t    echo \"*** or is declared to -dlopen it.\"\n\n\t    if test no = \"$allow_undefined\"; then\n\t      echo\n\t      echo \"*** Since this library must not contain undefined symbols,\"\n\t      echo \"*** because either the platform does not support them or\"\n\t      echo \"*** it was explicitly requested with -no-undefined,\"\n\t      echo \"*** libtool will only create a static version of it.\"\n\t      if test no = \"$build_old_libs\"; then\n\t\toldlibs=$output_objdir/$libname.$libext\n\t\tbuild_libtool_libs=module\n\t\tbuild_old_libs=yes\n\t      else\n\t\tbuild_libtool_libs=no\n\t      fi\n\t    fi\n\t  fi\n\tfi\n\t# Done checking deplibs!\n\tdeplibs=$newdeplibs\n      fi\n      # Time to change all our \"foo.ltframework\" stuff back to \"-framework foo\"\n      case $host in\n\t*-*-darwin*)\n\t  newdeplibs=`$ECHO \" $newdeplibs\" | $SED 's% \\([^ $]*\\).ltframework% -framework \\1%g'`\n\t  new_inherited_linker_flags=`$ECHO \" $new_inherited_linker_flags\" | $SED 's% \\([^ $]*\\).ltframework% -framework \\1%g'`\n\t  deplibs=`$ECHO \" $deplibs\" | $SED 's% \\([^ $]*\\).ltframework% -framework \\1%g'`\n\t  ;;\n      esac\n\n      # move library search paths that coincide with paths to not yet\n      # installed libraries to the beginning of the library search list\n      new_libs=\n      for path in $notinst_path; do\n\tcase \" $new_libs \" in\n\t*\" -L$path/$objdir \"*) ;;\n\t*)\n\t  case \" $deplibs \" in\n\t  *\" -L$path/$objdir \"*)\n\t    func_append new_libs \" -L$path/$objdir\" ;;\n\t  esac\n\t  ;;\n\tesac\n      done\n      for deplib in $deplibs; do\n\tcase $deplib in\n\t-L*)\n\t  case \" $new_libs \" in\n\t  *\" $deplib \"*) ;;\n\t  *) func_append new_libs \" $deplib\" ;;\n\t  esac\n\t  ;;\n\t*) func_append new_libs \" $deplib\" ;;\n\tesac\n      done\n      deplibs=$new_libs\n\n      # All the library-specific variables (install_libdir is set above).\n      library_names=\n      old_library=\n      dlname=\n\n      # Test again, we may have decided not to build it any more\n      if test yes = \"$build_libtool_libs\"; then\n\t# Remove $wl instances when linking with ld.\n\t# FIXME: should test the right _cmds variable.\n\tcase $archive_cmds in\n\t  *\\$LD\\ *) wl= ;;\n        esac\n\tif test yes = \"$hardcode_into_libs\"; then\n\t  # Hardcode the library paths\n\t  hardcode_libdirs=\n\t  dep_rpath=\n\t  rpath=$finalize_rpath\n\t  test relink = \"$opt_mode\" || rpath=$compile_rpath$rpath\n\t  for libdir in $rpath; do\n\t    if test -n \"$hardcode_libdir_flag_spec\"; then\n\t      if test -n \"$hardcode_libdir_separator\"; then\n\t\tfunc_replace_sysroot \"$libdir\"\n\t\tlibdir=$func_replace_sysroot_result\n\t\tif test -z \"$hardcode_libdirs\"; then\n\t\t  hardcode_libdirs=$libdir\n\t\telse\n\t\t  # Just accumulate the unique libdirs.\n\t\t  case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in\n\t\t  *\"$hardcode_libdir_separator$libdir$hardcode_libdir_separator\"*)\n\t\t    ;;\n\t\t  *)\n\t\t    func_append hardcode_libdirs \"$hardcode_libdir_separator$libdir\"\n\t\t    ;;\n\t\t  esac\n\t\tfi\n\t      else\n\t\teval flag=\\\"$hardcode_libdir_flag_spec\\\"\n\t\tfunc_append dep_rpath \" $flag\"\n\t      fi\n\t    elif test -n \"$runpath_var\"; then\n\t      case \"$perm_rpath \" in\n\t      *\" $libdir \"*) ;;\n\t      *) func_append perm_rpath \" $libdir\" ;;\n\t      esac\n\t    fi\n\t  done\n\t  # Substitute the hardcoded libdirs into the rpath.\n\t  if test -n \"$hardcode_libdir_separator\" &&\n\t     test -n \"$hardcode_libdirs\"; then\n\t    libdir=$hardcode_libdirs\n\t    eval \"dep_rpath=\\\"$hardcode_libdir_flag_spec\\\"\"\n\t  fi\n\t  if test -n \"$runpath_var\" && test -n \"$perm_rpath\"; then\n\t    # We should set the runpath_var.\n\t    rpath=\n\t    for dir in $perm_rpath; do\n\t      func_append rpath \"$dir:\"\n\t    done\n\t    eval \"$runpath_var='$rpath\\$$runpath_var'; export $runpath_var\"\n\t  fi\n\t  test -n \"$dep_rpath\" && deplibs=\"$dep_rpath $deplibs\"\n\tfi\n\n\tshlibpath=$finalize_shlibpath\n\ttest relink = \"$opt_mode\" || shlibpath=$compile_shlibpath$shlibpath\n\tif test -n \"$shlibpath\"; then\n\t  eval \"$shlibpath_var='$shlibpath\\$$shlibpath_var'; export $shlibpath_var\"\n\tfi\n\n\t# Get the real and link names of the library.\n\teval shared_ext=\\\"$shrext_cmds\\\"\n\teval library_names=\\\"$library_names_spec\\\"\n\tset dummy $library_names\n\tshift\n\trealname=$1\n\tshift\n\n\tif test -n \"$soname_spec\"; then\n\t  eval soname=\\\"$soname_spec\\\"\n\telse\n\t  soname=$realname\n\tfi\n\tif test -z \"$dlname\"; then\n\t  dlname=$soname\n\tfi\n\n\tlib=$output_objdir/$realname\n\tlinknames=\n\tfor link\n\tdo\n\t  func_append linknames \" $link\"\n\tdone\n\n\t# Use standard objects if they are pic\n\ttest -z \"$pic_flag\" && libobjs=`$ECHO \"$libobjs\" | $SP2NL | $SED \"$lo2o\" | $NL2SP`\n\ttest \"X$libobjs\" = \"X \" && libobjs=\n\n\tdelfiles=\n\tif test -n \"$export_symbols\" && test -n \"$include_expsyms\"; then\n\t  $opt_dry_run || cp \"$export_symbols\" \"$output_objdir/$libname.uexp\"\n\t  export_symbols=$output_objdir/$libname.uexp\n\t  func_append delfiles \" $export_symbols\"\n\tfi\n\n\torig_export_symbols=\n\tcase $host_os in\n\tcygwin* | mingw* | cegcc*)\n\t  if test -n \"$export_symbols\" && test -z \"$export_symbols_regex\"; then\n\t    # exporting using user supplied symfile\n\t    func_dll_def_p \"$export_symbols\" || {\n\t      # and it's NOT already a .def file. Must figure out\n\t      # which of the given symbols are data symbols and tag\n\t      # them as such. So, trigger use of export_symbols_cmds.\n\t      # export_symbols gets reassigned inside the \"prepare\n\t      # the list of exported symbols\" if statement, so the\n\t      # include_expsyms logic still works.\n\t      orig_export_symbols=$export_symbols\n\t      export_symbols=\n\t      always_export_symbols=yes\n\t    }\n\t  fi\n\t  ;;\n\tesac\n\n\t# Prepare the list of exported symbols\n\tif test -z \"$export_symbols\"; then\n\t  if test yes = \"$always_export_symbols\" || test -n \"$export_symbols_regex\"; then\n\t    func_verbose \"generating symbol list for '$libname.la'\"\n\t    export_symbols=$output_objdir/$libname.exp\n\t    $opt_dry_run || $RM $export_symbols\n\t    cmds=$export_symbols_cmds\n\t    save_ifs=$IFS; IFS='~'\n\t    for cmd1 in $cmds; do\n\t      IFS=$save_ifs\n\t      # Take the normal branch if the nm_file_list_spec branch\n\t      # doesn't work or if tool conversion is not needed.\n\t      case $nm_file_list_spec~$to_tool_file_cmd in\n\t\t*~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*)\n\t\t  try_normal_branch=yes\n\t\t  eval cmd=\\\"$cmd1\\\"\n\t\t  func_len \" $cmd\"\n\t\t  len=$func_len_result\n\t\t  ;;\n\t\t*)\n\t\t  try_normal_branch=no\n\t\t  ;;\n\t      esac\n\t      if test yes = \"$try_normal_branch\" \\\n\t\t && { test \"$len\" -lt \"$max_cmd_len\" \\\n\t\t      || test \"$max_cmd_len\" -le -1; }\n\t      then\n\t\tfunc_show_eval \"$cmd\" 'exit $?'\n\t\tskipped_export=false\n\t      elif test -n \"$nm_file_list_spec\"; then\n\t\tfunc_basename \"$output\"\n\t\toutput_la=$func_basename_result\n\t\tsave_libobjs=$libobjs\n\t\tsave_output=$output\n\t\toutput=$output_objdir/$output_la.nm\n\t\tfunc_to_tool_file \"$output\"\n\t\tlibobjs=$nm_file_list_spec$func_to_tool_file_result\n\t\tfunc_append delfiles \" $output\"\n\t\tfunc_verbose \"creating $NM input file list: $output\"\n\t\tfor obj in $save_libobjs; do\n\t\t  func_to_tool_file \"$obj\"\n\t\t  $ECHO \"$func_to_tool_file_result\"\n\t\tdone > \"$output\"\n\t\teval cmd=\\\"$cmd1\\\"\n\t\tfunc_show_eval \"$cmd\" 'exit $?'\n\t\toutput=$save_output\n\t\tlibobjs=$save_libobjs\n\t\tskipped_export=false\n\t      else\n\t\t# The command line is too long to execute in one step.\n\t\tfunc_verbose \"using reloadable object file for export list...\"\n\t\tskipped_export=:\n\t\t# Break out early, otherwise skipped_export may be\n\t\t# set to false by a later but shorter cmd.\n\t\tbreak\n\t      fi\n\t    done\n\t    IFS=$save_ifs\n\t    if test -n \"$export_symbols_regex\" && test : != \"$skipped_export\"; then\n\t      func_show_eval '$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"'\n\t      func_show_eval '$MV \"${export_symbols}T\" \"$export_symbols\"'\n\t    fi\n\t  fi\n\tfi\n\n\tif test -n \"$export_symbols\" && test -n \"$include_expsyms\"; then\n\t  tmp_export_symbols=$export_symbols\n\t  test -n \"$orig_export_symbols\" && tmp_export_symbols=$orig_export_symbols\n\t  $opt_dry_run || eval '$ECHO \"$include_expsyms\" | $SP2NL >> \"$tmp_export_symbols\"'\n\tfi\n\n\tif test : != \"$skipped_export\" && test -n \"$orig_export_symbols\"; then\n\t  # The given exports_symbols file has to be filtered, so filter it.\n\t  func_verbose \"filter symbol list for '$libname.la' to tag DATA exports\"\n\t  # FIXME: $output_objdir/$libname.filter potentially contains lots of\n\t  # 's' commands, which not all seds can handle. GNU sed should be fine\n\t  # though. Also, the filter scales superlinearly with the number of\n\t  # global variables. join(1) would be nice here, but unfortunately\n\t  # isn't a blessed tool.\n\t  $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\\(.*\\)\\([ \\,].*\\),s|^\\1$|\\1\\2|,' < $export_symbols > $output_objdir/$libname.filter\n\t  func_append delfiles \" $export_symbols $output_objdir/$libname.filter\"\n\t  export_symbols=$output_objdir/$libname.def\n\t  $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols\n\tfi\n\n\ttmp_deplibs=\n\tfor test_deplib in $deplibs; do\n\t  case \" $convenience \" in\n\t  *\" $test_deplib \"*) ;;\n\t  *)\n\t    func_append tmp_deplibs \" $test_deplib\"\n\t    ;;\n\t  esac\n\tdone\n\tdeplibs=$tmp_deplibs\n\n\tif test -n \"$convenience\"; then\n\t  if test -n \"$whole_archive_flag_spec\" &&\n\t    test yes = \"$compiler_needs_object\" &&\n\t    test -z \"$libobjs\"; then\n\t    # extract the archives, so we have objects to list.\n\t    # TODO: could optimize this to just extract one archive.\n\t    whole_archive_flag_spec=\n\t  fi\n\t  if test -n \"$whole_archive_flag_spec\"; then\n\t    save_libobjs=$libobjs\n\t    eval libobjs=\\\"\\$libobjs $whole_archive_flag_spec\\\"\n\t    test \"X$libobjs\" = \"X \" && libobjs=\n\t  else\n\t    gentop=$output_objdir/${outputname}x\n\t    func_append generated \" $gentop\"\n\n\t    func_extract_archives $gentop $convenience\n\t    func_append libobjs \" $func_extract_archives_result\"\n\t    test \"X$libobjs\" = \"X \" && libobjs=\n\t  fi\n\tfi\n\n\tif test yes = \"$thread_safe\" && test -n \"$thread_safe_flag_spec\"; then\n\t  eval flag=\\\"$thread_safe_flag_spec\\\"\n\t  func_append linker_flags \" $flag\"\n\tfi\n\n\t# Make a backup of the uninstalled library when relinking\n\tif test relink = \"$opt_mode\"; then\n\t  $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?\n\tfi\n\n\t# Do each of the archive commands.\n\tif test yes = \"$module\" && test -n \"$module_cmds\"; then\n\t  if test -n \"$export_symbols\" && test -n \"$module_expsym_cmds\"; then\n\t    eval test_cmds=\\\"$module_expsym_cmds\\\"\n\t    cmds=$module_expsym_cmds\n\t  else\n\t    eval test_cmds=\\\"$module_cmds\\\"\n\t    cmds=$module_cmds\n\t  fi\n\telse\n\t  if test -n \"$export_symbols\" && test -n \"$archive_expsym_cmds\"; then\n\t    eval test_cmds=\\\"$archive_expsym_cmds\\\"\n\t    cmds=$archive_expsym_cmds\n\t  else\n\t    eval test_cmds=\\\"$archive_cmds\\\"\n\t    cmds=$archive_cmds\n\t  fi\n\tfi\n\n\tif test : != \"$skipped_export\" &&\n\t   func_len \" $test_cmds\" &&\n\t   len=$func_len_result &&\n\t   test \"$len\" -lt \"$max_cmd_len\" || test \"$max_cmd_len\" -le -1; then\n\t  :\n\telse\n\t  # The command line is too long to link in one step, link piecewise\n\t  # or, if using GNU ld and skipped_export is not :, use a linker\n\t  # script.\n\n\t  # Save the value of $output and $libobjs because we want to\n\t  # use them later.  If we have whole_archive_flag_spec, we\n\t  # want to use save_libobjs as it was before\n\t  # whole_archive_flag_spec was expanded, because we can't\n\t  # assume the linker understands whole_archive_flag_spec.\n\t  # This may have to be revisited, in case too many\n\t  # convenience libraries get linked in and end up exceeding\n\t  # the spec.\n\t  if test -z \"$convenience\" || test -z \"$whole_archive_flag_spec\"; then\n\t    save_libobjs=$libobjs\n\t  fi\n\t  save_output=$output\n\t  func_basename \"$output\"\n\t  output_la=$func_basename_result\n\n\t  # Clear the reloadable object creation command queue and\n\t  # initialize k to one.\n\t  test_cmds=\n\t  concat_cmds=\n\t  objlist=\n\t  last_robj=\n\t  k=1\n\n\t  if test -n \"$save_libobjs\" && test : != \"$skipped_export\" && test yes = \"$with_gnu_ld\"; then\n\t    output=$output_objdir/$output_la.lnkscript\n\t    func_verbose \"creating GNU ld script: $output\"\n\t    echo 'INPUT (' > $output\n\t    for obj in $save_libobjs\n\t    do\n\t      func_to_tool_file \"$obj\"\n\t      $ECHO \"$func_to_tool_file_result\" >> $output\n\t    done\n\t    echo ')' >> $output\n\t    func_append delfiles \" $output\"\n\t    func_to_tool_file \"$output\"\n\t    output=$func_to_tool_file_result\n\t  elif test -n \"$save_libobjs\" && test : != \"$skipped_export\" && test -n \"$file_list_spec\"; then\n\t    output=$output_objdir/$output_la.lnk\n\t    func_verbose \"creating linker input file list: $output\"\n\t    : > $output\n\t    set x $save_libobjs\n\t    shift\n\t    firstobj=\n\t    if test yes = \"$compiler_needs_object\"; then\n\t      firstobj=\"$1 \"\n\t      shift\n\t    fi\n\t    for obj\n\t    do\n\t      func_to_tool_file \"$obj\"\n\t      $ECHO \"$func_to_tool_file_result\" >> $output\n\t    done\n\t    func_append delfiles \" $output\"\n\t    func_to_tool_file \"$output\"\n\t    output=$firstobj\\\"$file_list_spec$func_to_tool_file_result\\\"\n\t  else\n\t    if test -n \"$save_libobjs\"; then\n\t      func_verbose \"creating reloadable object files...\"\n\t      output=$output_objdir/$output_la-$k.$objext\n\t      eval test_cmds=\\\"$reload_cmds\\\"\n\t      func_len \" $test_cmds\"\n\t      len0=$func_len_result\n\t      len=$len0\n\n\t      # Loop over the list of objects to be linked.\n\t      for obj in $save_libobjs\n\t      do\n\t\tfunc_len \" $obj\"\n\t\tfunc_arith $len + $func_len_result\n\t\tlen=$func_arith_result\n\t\tif test -z \"$objlist\" ||\n\t\t   test \"$len\" -lt \"$max_cmd_len\"; then\n\t\t  func_append objlist \" $obj\"\n\t\telse\n\t\t  # The command $test_cmds is almost too long, add a\n\t\t  # command to the queue.\n\t\t  if test 1 -eq \"$k\"; then\n\t\t    # The first file doesn't have a previous command to add.\n\t\t    reload_objs=$objlist\n\t\t    eval concat_cmds=\\\"$reload_cmds\\\"\n\t\t  else\n\t\t    # All subsequent reloadable object files will link in\n\t\t    # the last one created.\n\t\t    reload_objs=\"$objlist $last_robj\"\n\t\t    eval concat_cmds=\\\"\\$concat_cmds~$reload_cmds~\\$RM $last_robj\\\"\n\t\t  fi\n\t\t  last_robj=$output_objdir/$output_la-$k.$objext\n\t\t  func_arith $k + 1\n\t\t  k=$func_arith_result\n\t\t  output=$output_objdir/$output_la-$k.$objext\n\t\t  objlist=\" $obj\"\n\t\t  func_len \" $last_robj\"\n\t\t  func_arith $len0 + $func_len_result\n\t\t  len=$func_arith_result\n\t\tfi\n\t      done\n\t      # Handle the remaining objects by creating one last\n\t      # reloadable object file.  All subsequent reloadable object\n\t      # files will link in the last one created.\n\t      test -z \"$concat_cmds\" || concat_cmds=$concat_cmds~\n\t      reload_objs=\"$objlist $last_robj\"\n\t      eval concat_cmds=\\\"\\$concat_cmds$reload_cmds\\\"\n\t      if test -n \"$last_robj\"; then\n\t        eval concat_cmds=\\\"\\$concat_cmds~\\$RM $last_robj\\\"\n\t      fi\n\t      func_append delfiles \" $output\"\n\n\t    else\n\t      output=\n\t    fi\n\n\t    ${skipped_export-false} && {\n\t      func_verbose \"generating symbol list for '$libname.la'\"\n\t      export_symbols=$output_objdir/$libname.exp\n\t      $opt_dry_run || $RM $export_symbols\n\t      libobjs=$output\n\t      # Append the command to create the export file.\n\t      test -z \"$concat_cmds\" || concat_cmds=$concat_cmds~\n\t      eval concat_cmds=\\\"\\$concat_cmds$export_symbols_cmds\\\"\n\t      if test -n \"$last_robj\"; then\n\t\teval concat_cmds=\\\"\\$concat_cmds~\\$RM $last_robj\\\"\n\t      fi\n\t    }\n\n\t    test -n \"$save_libobjs\" &&\n\t      func_verbose \"creating a temporary reloadable object file: $output\"\n\n\t    # Loop through the commands generated above and execute them.\n\t    save_ifs=$IFS; IFS='~'\n\t    for cmd in $concat_cmds; do\n\t      IFS=$save_ifs\n\t      $opt_quiet || {\n\t\t  func_quote_for_expand \"$cmd\"\n\t\t  eval \"func_echo $func_quote_for_expand_result\"\n\t      }\n\t      $opt_dry_run || eval \"$cmd\" || {\n\t\tlt_exit=$?\n\n\t\t# Restore the uninstalled library and exit\n\t\tif test relink = \"$opt_mode\"; then\n\t\t  ( cd \"$output_objdir\" && \\\n\t\t    $RM \"${realname}T\" && \\\n\t\t    $MV \"${realname}U\" \"$realname\" )\n\t\tfi\n\n\t\texit $lt_exit\n\t      }\n\t    done\n\t    IFS=$save_ifs\n\n\t    if test -n \"$export_symbols_regex\" && ${skipped_export-false}; then\n\t      func_show_eval '$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"'\n\t      func_show_eval '$MV \"${export_symbols}T\" \"$export_symbols\"'\n\t    fi\n\t  fi\n\n          ${skipped_export-false} && {\n\t    if test -n \"$export_symbols\" && test -n \"$include_expsyms\"; then\n\t      tmp_export_symbols=$export_symbols\n\t      test -n \"$orig_export_symbols\" && tmp_export_symbols=$orig_export_symbols\n\t      $opt_dry_run || eval '$ECHO \"$include_expsyms\" | $SP2NL >> \"$tmp_export_symbols\"'\n\t    fi\n\n\t    if test -n \"$orig_export_symbols\"; then\n\t      # The given exports_symbols file has to be filtered, so filter it.\n\t      func_verbose \"filter symbol list for '$libname.la' to tag DATA exports\"\n\t      # FIXME: $output_objdir/$libname.filter potentially contains lots of\n\t      # 's' commands, which not all seds can handle. GNU sed should be fine\n\t      # though. Also, the filter scales superlinearly with the number of\n\t      # global variables. join(1) would be nice here, but unfortunately\n\t      # isn't a blessed tool.\n\t      $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\\(.*\\)\\([ \\,].*\\),s|^\\1$|\\1\\2|,' < $export_symbols > $output_objdir/$libname.filter\n\t      func_append delfiles \" $export_symbols $output_objdir/$libname.filter\"\n\t      export_symbols=$output_objdir/$libname.def\n\t      $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols\n\t    fi\n\t  }\n\n\t  libobjs=$output\n\t  # Restore the value of output.\n\t  output=$save_output\n\n\t  if test -n \"$convenience\" && test -n \"$whole_archive_flag_spec\"; then\n\t    eval libobjs=\\\"\\$libobjs $whole_archive_flag_spec\\\"\n\t    test \"X$libobjs\" = \"X \" && libobjs=\n\t  fi\n\t  # Expand the library linking commands again to reset the\n\t  # value of $libobjs for piecewise linking.\n\n\t  # Do each of the archive commands.\n\t  if test yes = \"$module\" && test -n \"$module_cmds\"; then\n\t    if test -n \"$export_symbols\" && test -n \"$module_expsym_cmds\"; then\n\t      cmds=$module_expsym_cmds\n\t    else\n\t      cmds=$module_cmds\n\t    fi\n\t  else\n\t    if test -n \"$export_symbols\" && test -n \"$archive_expsym_cmds\"; then\n\t      cmds=$archive_expsym_cmds\n\t    else\n\t      cmds=$archive_cmds\n\t    fi\n\t  fi\n\tfi\n\n\tif test -n \"$delfiles\"; then\n\t  # Append the command to remove temporary files to $cmds.\n\t  eval cmds=\\\"\\$cmds~\\$RM $delfiles\\\"\n\tfi\n\n\t# Add any objects from preloaded convenience libraries\n\tif test -n \"$dlprefiles\"; then\n\t  gentop=$output_objdir/${outputname}x\n\t  func_append generated \" $gentop\"\n\n\t  func_extract_archives $gentop $dlprefiles\n\t  func_append libobjs \" $func_extract_archives_result\"\n\t  test \"X$libobjs\" = \"X \" && libobjs=\n\tfi\n\n\tsave_ifs=$IFS; IFS='~'\n\tfor cmd in $cmds; do\n\t  IFS=$sp$nl\n\t  eval cmd=\\\"$cmd\\\"\n\t  IFS=$save_ifs\n\t  $opt_quiet || {\n\t    func_quote_for_expand \"$cmd\"\n\t    eval \"func_echo $func_quote_for_expand_result\"\n\t  }\n\t  $opt_dry_run || eval \"$cmd\" || {\n\t    lt_exit=$?\n\n\t    # Restore the uninstalled library and exit\n\t    if test relink = \"$opt_mode\"; then\n\t      ( cd \"$output_objdir\" && \\\n\t        $RM \"${realname}T\" && \\\n\t\t$MV \"${realname}U\" \"$realname\" )\n\t    fi\n\n\t    exit $lt_exit\n\t  }\n\tdone\n\tIFS=$save_ifs\n\n\t# Restore the uninstalled library and exit\n\tif test relink = \"$opt_mode\"; then\n\t  $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?\n\n\t  if test -n \"$convenience\"; then\n\t    if test -z \"$whole_archive_flag_spec\"; then\n\t      func_show_eval '${RM}r \"$gentop\"'\n\t    fi\n\t  fi\n\n\t  exit $EXIT_SUCCESS\n\tfi\n\n\t# Create links to the real library.\n\tfor linkname in $linknames; do\n\t  if test \"$realname\" != \"$linkname\"; then\n\t    func_show_eval '(cd \"$output_objdir\" && $RM \"$linkname\" && $LN_S \"$realname\" \"$linkname\")' 'exit $?'\n\t  fi\n\tdone\n\n\t# If -module or -export-dynamic was specified, set the dlname.\n\tif test yes = \"$module\" || test yes = \"$export_dynamic\"; then\n\t  # On all known operating systems, these are identical.\n\t  dlname=$soname\n\tfi\n      fi\n      ;;\n\n    obj)\n      if test -n \"$dlfiles$dlprefiles\" || test no != \"$dlself\"; then\n\tfunc_warning \"'-dlopen' is ignored for objects\"\n      fi\n\n      case \" $deplibs\" in\n      *\\ -l* | *\\ -L*)\n\tfunc_warning \"'-l' and '-L' are ignored for objects\" ;;\n      esac\n\n      test -n \"$rpath\" && \\\n\tfunc_warning \"'-rpath' is ignored for objects\"\n\n      test -n \"$xrpath\" && \\\n\tfunc_warning \"'-R' is ignored for objects\"\n\n      test -n \"$vinfo\" && \\\n\tfunc_warning \"'-version-info' is ignored for objects\"\n\n      test -n \"$release\" && \\\n\tfunc_warning \"'-release' is ignored for objects\"\n\n      case $output in\n      *.lo)\n\ttest -n \"$objs$old_deplibs\" && \\\n\t  func_fatal_error \"cannot build library object '$output' from non-libtool objects\"\n\n\tlibobj=$output\n\tfunc_lo2o \"$libobj\"\n\tobj=$func_lo2o_result\n\t;;\n      *)\n\tlibobj=\n\tobj=$output\n\t;;\n      esac\n\n      # Delete the old objects.\n      $opt_dry_run || $RM $obj $libobj\n\n      # Objects from convenience libraries.  This assumes\n      # single-version convenience libraries.  Whenever we create\n      # different ones for PIC/non-PIC, this we'll have to duplicate\n      # the extraction.\n      reload_conv_objs=\n      gentop=\n      # if reload_cmds runs $LD directly, get rid of -Wl from\n      # whole_archive_flag_spec and hope we can get by with turning comma\n      # into space.\n      case $reload_cmds in\n        *\\$LD[\\ \\$]*) wl= ;;\n      esac\n      if test -n \"$convenience\"; then\n\tif test -n \"$whole_archive_flag_spec\"; then\n\t  eval tmp_whole_archive_flags=\\\"$whole_archive_flag_spec\\\"\n\t  test -n \"$wl\" || tmp_whole_archive_flags=`$ECHO \"$tmp_whole_archive_flags\" | $SED 's|,| |g'`\n\t  reload_conv_objs=$reload_objs\\ $tmp_whole_archive_flags\n\telse\n\t  gentop=$output_objdir/${obj}x\n\t  func_append generated \" $gentop\"\n\n\t  func_extract_archives $gentop $convenience\n\t  reload_conv_objs=\"$reload_objs $func_extract_archives_result\"\n\tfi\n      fi\n\n      # If we're not building shared, we need to use non_pic_objs\n      test yes = \"$build_libtool_libs\" || libobjs=$non_pic_objects\n\n      # Create the old-style object.\n      reload_objs=$objs$old_deplibs' '`$ECHO \"$libobjs\" | $SP2NL | $SED \"/\\.$libext$/d; /\\.lib$/d; $lo2o\" | $NL2SP`' '$reload_conv_objs\n\n      output=$obj\n      func_execute_cmds \"$reload_cmds\" 'exit $?'\n\n      # Exit if we aren't doing a library object file.\n      if test -z \"$libobj\"; then\n\tif test -n \"$gentop\"; then\n\t  func_show_eval '${RM}r \"$gentop\"'\n\tfi\n\n\texit $EXIT_SUCCESS\n      fi\n\n      test yes = \"$build_libtool_libs\" || {\n\tif test -n \"$gentop\"; then\n\t  func_show_eval '${RM}r \"$gentop\"'\n\tfi\n\n\t# Create an invalid libtool object if no PIC, so that we don't\n\t# accidentally link it into a program.\n\t# $show \"echo timestamp > $libobj\"\n\t# $opt_dry_run || eval \"echo timestamp > $libobj\" || exit $?\n\texit $EXIT_SUCCESS\n      }\n\n      if test -n \"$pic_flag\" || test default != \"$pic_mode\"; then\n\t# Only do commands if we really have different PIC objects.\n\treload_objs=\"$libobjs $reload_conv_objs\"\n\toutput=$libobj\n\tfunc_execute_cmds \"$reload_cmds\" 'exit $?'\n      fi\n\n      if test -n \"$gentop\"; then\n\tfunc_show_eval '${RM}r \"$gentop\"'\n      fi\n\n      exit $EXIT_SUCCESS\n      ;;\n\n    prog)\n      case $host in\n\t*cygwin*) func_stripname '' '.exe' \"$output\"\n\t          output=$func_stripname_result.exe;;\n      esac\n      test -n \"$vinfo\" && \\\n\tfunc_warning \"'-version-info' is ignored for programs\"\n\n      test -n \"$release\" && \\\n\tfunc_warning \"'-release' is ignored for programs\"\n\n      $preload \\\n\t&& test unknown,unknown,unknown = \"$dlopen_support,$dlopen_self,$dlopen_self_static\" \\\n\t&& func_warning \"'LT_INIT([dlopen])' not used. Assuming no dlopen support.\"\n\n      case $host in\n      *-*-rhapsody* | *-*-darwin1.[012])\n\t# On Rhapsody replace the C library is the System framework\n\tcompile_deplibs=`$ECHO \" $compile_deplibs\" | $SED 's/ -lc / System.ltframework /'`\n\tfinalize_deplibs=`$ECHO \" $finalize_deplibs\" | $SED 's/ -lc / System.ltframework /'`\n\t;;\n      esac\n\n      case $host in\n      *-*-darwin*)\n\t# Don't allow lazy linking, it breaks C++ global constructors\n\t# But is supposedly fixed on 10.4 or later (yay!).\n\tif test CXX = \"$tagname\"; then\n\t  case ${MACOSX_DEPLOYMENT_TARGET-10.0} in\n\t    10.[0123])\n\t      func_append compile_command \" $wl-bind_at_load\"\n\t      func_append finalize_command \" $wl-bind_at_load\"\n\t    ;;\n\t  esac\n\tfi\n\t# Time to change all our \"foo.ltframework\" stuff back to \"-framework foo\"\n\tcompile_deplibs=`$ECHO \" $compile_deplibs\" | $SED 's% \\([^ $]*\\).ltframework% -framework \\1%g'`\n\tfinalize_deplibs=`$ECHO \" $finalize_deplibs\" | $SED 's% \\([^ $]*\\).ltframework% -framework \\1%g'`\n\t;;\n      esac\n\n\n      # move library search paths that coincide with paths to not yet\n      # installed libraries to the beginning of the library search list\n      new_libs=\n      for path in $notinst_path; do\n\tcase \" $new_libs \" in\n\t*\" -L$path/$objdir \"*) ;;\n\t*)\n\t  case \" $compile_deplibs \" in\n\t  *\" -L$path/$objdir \"*)\n\t    func_append new_libs \" -L$path/$objdir\" ;;\n\t  esac\n\t  ;;\n\tesac\n      done\n      for deplib in $compile_deplibs; do\n\tcase $deplib in\n\t-L*)\n\t  case \" $new_libs \" in\n\t  *\" $deplib \"*) ;;\n\t  *) func_append new_libs \" $deplib\" ;;\n\t  esac\n\t  ;;\n\t*) func_append new_libs \" $deplib\" ;;\n\tesac\n      done\n      compile_deplibs=$new_libs\n\n\n      func_append compile_command \" $compile_deplibs\"\n      func_append finalize_command \" $finalize_deplibs\"\n\n      if test -n \"$rpath$xrpath\"; then\n\t# If the user specified any rpath flags, then add them.\n\tfor libdir in $rpath $xrpath; do\n\t  # This is the magic to use -rpath.\n\t  case \"$finalize_rpath \" in\n\t  *\" $libdir \"*) ;;\n\t  *) func_append finalize_rpath \" $libdir\" ;;\n\t  esac\n\tdone\n      fi\n\n      # Now hardcode the library paths\n      rpath=\n      hardcode_libdirs=\n      for libdir in $compile_rpath $finalize_rpath; do\n\tif test -n \"$hardcode_libdir_flag_spec\"; then\n\t  if test -n \"$hardcode_libdir_separator\"; then\n\t    if test -z \"$hardcode_libdirs\"; then\n\t      hardcode_libdirs=$libdir\n\t    else\n\t      # Just accumulate the unique libdirs.\n\t      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in\n\t      *\"$hardcode_libdir_separator$libdir$hardcode_libdir_separator\"*)\n\t\t;;\n\t      *)\n\t\tfunc_append hardcode_libdirs \"$hardcode_libdir_separator$libdir\"\n\t\t;;\n\t      esac\n\t    fi\n\t  else\n\t    eval flag=\\\"$hardcode_libdir_flag_spec\\\"\n\t    func_append rpath \" $flag\"\n\t  fi\n\telif test -n \"$runpath_var\"; then\n\t  case \"$perm_rpath \" in\n\t  *\" $libdir \"*) ;;\n\t  *) func_append perm_rpath \" $libdir\" ;;\n\t  esac\n\tfi\n\tcase $host in\n\t*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)\n\t  testbindir=`$ECHO \"$libdir\" | $SED -e 's*/lib$*/bin*'`\n\t  case :$dllsearchpath: in\n\t  *\":$libdir:\"*) ;;\n\t  ::) dllsearchpath=$libdir;;\n\t  *) func_append dllsearchpath \":$libdir\";;\n\t  esac\n\t  case :$dllsearchpath: in\n\t  *\":$testbindir:\"*) ;;\n\t  ::) dllsearchpath=$testbindir;;\n\t  *) func_append dllsearchpath \":$testbindir\";;\n\t  esac\n\t  ;;\n\tesac\n      done\n      # Substitute the hardcoded libdirs into the rpath.\n      if test -n \"$hardcode_libdir_separator\" &&\n\t test -n \"$hardcode_libdirs\"; then\n\tlibdir=$hardcode_libdirs\n\teval rpath=\\\" $hardcode_libdir_flag_spec\\\"\n      fi\n      compile_rpath=$rpath\n\n      rpath=\n      hardcode_libdirs=\n      for libdir in $finalize_rpath; do\n\tif test -n \"$hardcode_libdir_flag_spec\"; then\n\t  if test -n \"$hardcode_libdir_separator\"; then\n\t    if test -z \"$hardcode_libdirs\"; then\n\t      hardcode_libdirs=$libdir\n\t    else\n\t      # Just accumulate the unique libdirs.\n\t      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in\n\t      *\"$hardcode_libdir_separator$libdir$hardcode_libdir_separator\"*)\n\t\t;;\n\t      *)\n\t\tfunc_append hardcode_libdirs \"$hardcode_libdir_separator$libdir\"\n\t\t;;\n\t      esac\n\t    fi\n\t  else\n\t    eval flag=\\\"$hardcode_libdir_flag_spec\\\"\n\t    func_append rpath \" $flag\"\n\t  fi\n\telif test -n \"$runpath_var\"; then\n\t  case \"$finalize_perm_rpath \" in\n\t  *\" $libdir \"*) ;;\n\t  *) func_append finalize_perm_rpath \" $libdir\" ;;\n\t  esac\n\tfi\n      done\n      # Substitute the hardcoded libdirs into the rpath.\n      if test -n \"$hardcode_libdir_separator\" &&\n\t test -n \"$hardcode_libdirs\"; then\n\tlibdir=$hardcode_libdirs\n\teval rpath=\\\" $hardcode_libdir_flag_spec\\\"\n      fi\n      finalize_rpath=$rpath\n\n      if test -n \"$libobjs\" && test yes = \"$build_old_libs\"; then\n\t# Transform all the library objects into standard objects.\n\tcompile_command=`$ECHO \"$compile_command\" | $SP2NL | $SED \"$lo2o\" | $NL2SP`\n\tfinalize_command=`$ECHO \"$finalize_command\" | $SP2NL | $SED \"$lo2o\" | $NL2SP`\n      fi\n\n      func_generate_dlsyms \"$outputname\" \"@PROGRAM@\" false\n\n      # template prelinking step\n      if test -n \"$prelink_cmds\"; then\n\tfunc_execute_cmds \"$prelink_cmds\" 'exit $?'\n      fi\n\n      wrappers_required=:\n      case $host in\n      *cegcc* | *mingw32ce*)\n        # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.\n        wrappers_required=false\n        ;;\n      *cygwin* | *mingw* )\n        test yes = \"$build_libtool_libs\" || wrappers_required=false\n        ;;\n      *)\n        if test no = \"$need_relink\" || test yes != \"$build_libtool_libs\"; then\n          wrappers_required=false\n        fi\n        ;;\n      esac\n      $wrappers_required || {\n\t# Replace the output file specification.\n\tcompile_command=`$ECHO \"$compile_command\" | $SED 's%@OUTPUT@%'\"$output\"'%g'`\n\tlink_command=$compile_command$compile_rpath\n\n\t# We have no uninstalled library dependencies, so finalize right now.\n\texit_status=0\n\tfunc_show_eval \"$link_command\" 'exit_status=$?'\n\n\tif test -n \"$postlink_cmds\"; then\n\t  func_to_tool_file \"$output\"\n\t  postlink_cmds=`func_echo_all \"$postlink_cmds\" | $SED -e 's%@OUTPUT@%'\"$output\"'%g' -e 's%@TOOL_OUTPUT@%'\"$func_to_tool_file_result\"'%g'`\n\t  func_execute_cmds \"$postlink_cmds\" 'exit $?'\n\tfi\n\n\t# Delete the generated files.\n\tif test -f \"$output_objdir/${outputname}S.$objext\"; then\n\t  func_show_eval '$RM \"$output_objdir/${outputname}S.$objext\"'\n\tfi\n\n\texit $exit_status\n      }\n\n      if test -n \"$compile_shlibpath$finalize_shlibpath\"; then\n\tcompile_command=\"$shlibpath_var=\\\"$compile_shlibpath$finalize_shlibpath\\$$shlibpath_var\\\" $compile_command\"\n      fi\n      if test -n \"$finalize_shlibpath\"; then\n\tfinalize_command=\"$shlibpath_var=\\\"$finalize_shlibpath\\$$shlibpath_var\\\" $finalize_command\"\n      fi\n\n      compile_var=\n      finalize_var=\n      if test -n \"$runpath_var\"; then\n\tif test -n \"$perm_rpath\"; then\n\t  # We should set the runpath_var.\n\t  rpath=\n\t  for dir in $perm_rpath; do\n\t    func_append rpath \"$dir:\"\n\t  done\n\t  compile_var=\"$runpath_var=\\\"$rpath\\$$runpath_var\\\" \"\n\tfi\n\tif test -n \"$finalize_perm_rpath\"; then\n\t  # We should set the runpath_var.\n\t  rpath=\n\t  for dir in $finalize_perm_rpath; do\n\t    func_append rpath \"$dir:\"\n\t  done\n\t  finalize_var=\"$runpath_var=\\\"$rpath\\$$runpath_var\\\" \"\n\tfi\n      fi\n\n      if test yes = \"$no_install\"; then\n\t# We don't need to create a wrapper script.\n\tlink_command=$compile_var$compile_command$compile_rpath\n\t# Replace the output file specification.\n\tlink_command=`$ECHO \"$link_command\" | $SED 's%@OUTPUT@%'\"$output\"'%g'`\n\t# Delete the old output file.\n\t$opt_dry_run || $RM $output\n\t# Link the executable and exit\n\tfunc_show_eval \"$link_command\" 'exit $?'\n\n\tif test -n \"$postlink_cmds\"; then\n\t  func_to_tool_file \"$output\"\n\t  postlink_cmds=`func_echo_all \"$postlink_cmds\" | $SED -e 's%@OUTPUT@%'\"$output\"'%g' -e 's%@TOOL_OUTPUT@%'\"$func_to_tool_file_result\"'%g'`\n\t  func_execute_cmds \"$postlink_cmds\" 'exit $?'\n\tfi\n\n\texit $EXIT_SUCCESS\n      fi\n\n      case $hardcode_action,$fast_install in\n        relink,*)\n\t  # Fast installation is not supported\n\t  link_command=$compile_var$compile_command$compile_rpath\n\t  relink_command=$finalize_var$finalize_command$finalize_rpath\n\n\t  func_warning \"this platform does not like uninstalled shared libraries\"\n\t  func_warning \"'$output' will be relinked during installation\"\n\t  ;;\n        *,yes)\n\t  link_command=$finalize_var$compile_command$finalize_rpath\n\t  relink_command=`$ECHO \"$compile_var$compile_command$compile_rpath\" | $SED 's%@OUTPUT@%\\$progdir/\\$file%g'`\n          ;;\n\t*,no)\n\t  link_command=$compile_var$compile_command$compile_rpath\n\t  relink_command=$finalize_var$finalize_command$finalize_rpath\n          ;;\n\t*,needless)\n\t  link_command=$finalize_var$compile_command$finalize_rpath\n\t  relink_command=\n          ;;\n      esac\n\n      # Replace the output file specification.\n      link_command=`$ECHO \"$link_command\" | $SED 's%@OUTPUT@%'\"$output_objdir/$outputname\"'%g'`\n\n      # Delete the old output files.\n      $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname\n\n      func_show_eval \"$link_command\" 'exit $?'\n\n      if test -n \"$postlink_cmds\"; then\n\tfunc_to_tool_file \"$output_objdir/$outputname\"\n\tpostlink_cmds=`func_echo_all \"$postlink_cmds\" | $SED -e 's%@OUTPUT@%'\"$output_objdir/$outputname\"'%g' -e 's%@TOOL_OUTPUT@%'\"$func_to_tool_file_result\"'%g'`\n\tfunc_execute_cmds \"$postlink_cmds\" 'exit $?'\n      fi\n\n      # Now create the wrapper script.\n      func_verbose \"creating $output\"\n\n      # Quote the relink command for shipping.\n      if test -n \"$relink_command\"; then\n\t# Preserve any variables that may affect compiler behavior\n\tfor var in $variables_saved_for_relink; do\n\t  if eval test -z \\\"\\${$var+set}\\\"; then\n\t    relink_command=\"{ test -z \\\"\\${$var+set}\\\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command\"\n\t  elif eval var_value=\\$$var; test -z \"$var_value\"; then\n\t    relink_command=\"$var=; export $var; $relink_command\"\n\t  else\n\t    func_quote_for_eval \"$var_value\"\n\t    relink_command=\"$var=$func_quote_for_eval_result; export $var; $relink_command\"\n\t  fi\n\tdone\n\trelink_command=\"(cd `pwd`; $relink_command)\"\n\trelink_command=`$ECHO \"$relink_command\" | $SED \"$sed_quote_subst\"`\n      fi\n\n      # Only actually do things if not in dry run mode.\n      $opt_dry_run || {\n\t# win32 will think the script is a binary if it has\n\t# a .exe suffix, so we strip it off here.\n\tcase $output in\n\t  *.exe) func_stripname '' '.exe' \"$output\"\n\t         output=$func_stripname_result ;;\n\tesac\n\t# test for cygwin because mv fails w/o .exe extensions\n\tcase $host in\n\t  *cygwin*)\n\t    exeext=.exe\n\t    func_stripname '' '.exe' \"$outputname\"\n\t    outputname=$func_stripname_result ;;\n\t  *) exeext= ;;\n\tesac\n\tcase $host in\n\t  *cygwin* | *mingw* )\n\t    func_dirname_and_basename \"$output\" \"\" \".\"\n\t    output_name=$func_basename_result\n\t    output_path=$func_dirname_result\n\t    cwrappersource=$output_path/$objdir/lt-$output_name.c\n\t    cwrapper=$output_path/$output_name.exe\n\t    $RM $cwrappersource $cwrapper\n\t    trap \"$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE\" 1 2 15\n\n\t    func_emit_cwrapperexe_src > $cwrappersource\n\n\t    # The wrapper executable is built using the $host compiler,\n\t    # because it contains $host paths and files. If cross-\n\t    # compiling, it, like the target executable, must be\n\t    # executed on the $host or under an emulation environment.\n\t    $opt_dry_run || {\n\t      $LTCC $LTCFLAGS -o $cwrapper $cwrappersource\n\t      $STRIP $cwrapper\n\t    }\n\n\t    # Now, create the wrapper script for func_source use:\n\t    func_ltwrapper_scriptname $cwrapper\n\t    $RM $func_ltwrapper_scriptname_result\n\t    trap \"$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE\" 1 2 15\n\t    $opt_dry_run || {\n\t      # note: this script will not be executed, so do not chmod.\n\t      if test \"x$build\" = \"x$host\"; then\n\t\t$cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result\n\t      else\n\t\tfunc_emit_wrapper no > $func_ltwrapper_scriptname_result\n\t      fi\n\t    }\n\t  ;;\n\t  * )\n\t    $RM $output\n\t    trap \"$RM $output; exit $EXIT_FAILURE\" 1 2 15\n\n\t    func_emit_wrapper no > $output\n\t    chmod +x $output\n\t  ;;\n\tesac\n      }\n      exit $EXIT_SUCCESS\n      ;;\n    esac\n\n    # See if we need to build an old-fashioned archive.\n    for oldlib in $oldlibs; do\n\n      case $build_libtool_libs in\n        convenience)\n\t  oldobjs=\"$libobjs_save $symfileobj\"\n\t  addlibs=$convenience\n\t  build_libtool_libs=no\n\t  ;;\n\tmodule)\n\t  oldobjs=$libobjs_save\n\t  addlibs=$old_convenience\n\t  build_libtool_libs=no\n          ;;\n\t*)\n\t  oldobjs=\"$old_deplibs $non_pic_objects\"\n\t  $preload && test -f \"$symfileobj\" \\\n\t    && func_append oldobjs \" $symfileobj\"\n\t  addlibs=$old_convenience\n\t  ;;\n      esac\n\n      if test -n \"$addlibs\"; then\n\tgentop=$output_objdir/${outputname}x\n\tfunc_append generated \" $gentop\"\n\n\tfunc_extract_archives $gentop $addlibs\n\tfunc_append oldobjs \" $func_extract_archives_result\"\n      fi\n\n      # Do each command in the archive commands.\n      if test -n \"$old_archive_from_new_cmds\" && test yes = \"$build_libtool_libs\"; then\n\tcmds=$old_archive_from_new_cmds\n      else\n\n\t# Add any objects from preloaded convenience libraries\n\tif test -n \"$dlprefiles\"; then\n\t  gentop=$output_objdir/${outputname}x\n\t  func_append generated \" $gentop\"\n\n\t  func_extract_archives $gentop $dlprefiles\n\t  func_append oldobjs \" $func_extract_archives_result\"\n\tfi\n\n\t# POSIX demands no paths to be encoded in archives.  We have\n\t# to avoid creating archives with duplicate basenames if we\n\t# might have to extract them afterwards, e.g., when creating a\n\t# static archive out of a convenience library, or when linking\n\t# the entirety of a libtool archive into another (currently\n\t# not supported by libtool).\n\tif (for obj in $oldobjs\n\t    do\n\t      func_basename \"$obj\"\n\t      $ECHO \"$func_basename_result\"\n\t    done | sort | sort -uc >/dev/null 2>&1); then\n\t  :\n\telse\n\t  echo \"copying selected object files to avoid basename conflicts...\"\n\t  gentop=$output_objdir/${outputname}x\n\t  func_append generated \" $gentop\"\n\t  func_mkdir_p \"$gentop\"\n\t  save_oldobjs=$oldobjs\n\t  oldobjs=\n\t  counter=1\n\t  for obj in $save_oldobjs\n\t  do\n\t    func_basename \"$obj\"\n\t    objbase=$func_basename_result\n\t    case \" $oldobjs \" in\n\t    \" \") oldobjs=$obj ;;\n\t    *[\\ /]\"$objbase \"*)\n\t      while :; do\n\t\t# Make sure we don't pick an alternate name that also\n\t\t# overlaps.\n\t\tnewobj=lt$counter-$objbase\n\t\tfunc_arith $counter + 1\n\t\tcounter=$func_arith_result\n\t\tcase \" $oldobjs \" in\n\t\t*[\\ /]\"$newobj \"*) ;;\n\t\t*) if test ! -f \"$gentop/$newobj\"; then break; fi ;;\n\t\tesac\n\t      done\n\t      func_show_eval \"ln $obj $gentop/$newobj || cp $obj $gentop/$newobj\"\n\t      func_append oldobjs \" $gentop/$newobj\"\n\t      ;;\n\t    *) func_append oldobjs \" $obj\" ;;\n\t    esac\n\t  done\n\tfi\n\tfunc_to_tool_file \"$oldlib\" func_convert_file_msys_to_w32\n\ttool_oldlib=$func_to_tool_file_result\n\teval cmds=\\\"$old_archive_cmds\\\"\n\n\tfunc_len \" $cmds\"\n\tlen=$func_len_result\n\tif test \"$len\" -lt \"$max_cmd_len\" || test \"$max_cmd_len\" -le -1; then\n\t  cmds=$old_archive_cmds\n\telif test -n \"$archiver_list_spec\"; then\n\t  func_verbose \"using command file archive linking...\"\n\t  for obj in $oldobjs\n\t  do\n\t    func_to_tool_file \"$obj\"\n\t    $ECHO \"$func_to_tool_file_result\"\n\t  done > $output_objdir/$libname.libcmd\n\t  func_to_tool_file \"$output_objdir/$libname.libcmd\"\n\t  oldobjs=\" $archiver_list_spec$func_to_tool_file_result\"\n\t  cmds=$old_archive_cmds\n\telse\n\t  # the command line is too long to link in one step, link in parts\n\t  func_verbose \"using piecewise archive linking...\"\n\t  save_RANLIB=$RANLIB\n\t  RANLIB=:\n\t  objlist=\n\t  concat_cmds=\n\t  save_oldobjs=$oldobjs\n\t  oldobjs=\n\t  # Is there a better way of finding the last object in the list?\n\t  for obj in $save_oldobjs\n\t  do\n\t    last_oldobj=$obj\n\t  done\n\t  eval test_cmds=\\\"$old_archive_cmds\\\"\n\t  func_len \" $test_cmds\"\n\t  len0=$func_len_result\n\t  len=$len0\n\t  for obj in $save_oldobjs\n\t  do\n\t    func_len \" $obj\"\n\t    func_arith $len + $func_len_result\n\t    len=$func_arith_result\n\t    func_append objlist \" $obj\"\n\t    if test \"$len\" -lt \"$max_cmd_len\"; then\n\t      :\n\t    else\n\t      # the above command should be used before it gets too long\n\t      oldobjs=$objlist\n\t      if test \"$obj\" = \"$last_oldobj\"; then\n\t\tRANLIB=$save_RANLIB\n\t      fi\n\t      test -z \"$concat_cmds\" || concat_cmds=$concat_cmds~\n\t      eval concat_cmds=\\\"\\$concat_cmds$old_archive_cmds\\\"\n\t      objlist=\n\t      len=$len0\n\t    fi\n\t  done\n\t  RANLIB=$save_RANLIB\n\t  oldobjs=$objlist\n\t  if test -z \"$oldobjs\"; then\n\t    eval cmds=\\\"\\$concat_cmds\\\"\n\t  else\n\t    eval cmds=\\\"\\$concat_cmds~\\$old_archive_cmds\\\"\n\t  fi\n\tfi\n      fi\n      func_execute_cmds \"$cmds\" 'exit $?'\n    done\n\n    test -n \"$generated\" && \\\n      func_show_eval \"${RM}r$generated\"\n\n    # Now create the libtool archive.\n    case $output in\n    *.la)\n      old_library=\n      test yes = \"$build_old_libs\" && old_library=$libname.$libext\n      func_verbose \"creating $output\"\n\n      # Preserve any variables that may affect compiler behavior\n      for var in $variables_saved_for_relink; do\n\tif eval test -z \\\"\\${$var+set}\\\"; then\n\t  relink_command=\"{ test -z \\\"\\${$var+set}\\\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command\"\n\telif eval var_value=\\$$var; test -z \"$var_value\"; then\n\t  relink_command=\"$var=; export $var; $relink_command\"\n\telse\n\t  func_quote_for_eval \"$var_value\"\n\t  relink_command=\"$var=$func_quote_for_eval_result; export $var; $relink_command\"\n\tfi\n      done\n      # Quote the link command for shipping.\n      relink_command=\"(cd `pwd`; $SHELL \\\"$progpath\\\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)\"\n      relink_command=`$ECHO \"$relink_command\" | $SED \"$sed_quote_subst\"`\n      if test yes = \"$hardcode_automatic\"; then\n\trelink_command=\n      fi\n\n      # Only create the output if not a dry run.\n      $opt_dry_run || {\n\tfor installed in no yes; do\n\t  if test yes = \"$installed\"; then\n\t    if test -z \"$install_libdir\"; then\n\t      break\n\t    fi\n\t    output=$output_objdir/${outputname}i\n\t    # Replace all uninstalled libtool libraries with the installed ones\n\t    newdependency_libs=\n\t    for deplib in $dependency_libs; do\n\t      case $deplib in\n\t      *.la)\n\t\tfunc_basename \"$deplib\"\n\t\tname=$func_basename_result\n\t\tfunc_resolve_sysroot \"$deplib\"\n\t\teval libdir=`$SED -n -e 's/^libdir=\\(.*\\)$/\\1/p' $func_resolve_sysroot_result`\n\t\ttest -z \"$libdir\" && \\\n\t\t  func_fatal_error \"'$deplib' is not a valid libtool archive\"\n\t\tfunc_append newdependency_libs \" ${lt_sysroot:+=}$libdir/$name\"\n\t\t;;\n\t      -L*)\n\t\tfunc_stripname -L '' \"$deplib\"\n\t\tfunc_replace_sysroot \"$func_stripname_result\"\n\t\tfunc_append newdependency_libs \" -L$func_replace_sysroot_result\"\n\t\t;;\n\t      -R*)\n\t\tfunc_stripname -R '' \"$deplib\"\n\t\tfunc_replace_sysroot \"$func_stripname_result\"\n\t\tfunc_append newdependency_libs \" -R$func_replace_sysroot_result\"\n\t\t;;\n\t      *) func_append newdependency_libs \" $deplib\" ;;\n\t      esac\n\t    done\n\t    dependency_libs=$newdependency_libs\n\t    newdlfiles=\n\n\t    for lib in $dlfiles; do\n\t      case $lib in\n\t      *.la)\n\t        func_basename \"$lib\"\n\t\tname=$func_basename_result\n\t\teval libdir=`$SED -n -e 's/^libdir=\\(.*\\)$/\\1/p' $lib`\n\t\ttest -z \"$libdir\" && \\\n\t\t  func_fatal_error \"'$lib' is not a valid libtool archive\"\n\t\tfunc_append newdlfiles \" ${lt_sysroot:+=}$libdir/$name\"\n\t\t;;\n\t      *) func_append newdlfiles \" $lib\" ;;\n\t      esac\n\t    done\n\t    dlfiles=$newdlfiles\n\t    newdlprefiles=\n\t    for lib in $dlprefiles; do\n\t      case $lib in\n\t      *.la)\n\t\t# Only pass preopened files to the pseudo-archive (for\n\t\t# eventual linking with the app. that links it) if we\n\t\t# didn't already link the preopened objects directly into\n\t\t# the library:\n\t\tfunc_basename \"$lib\"\n\t\tname=$func_basename_result\n\t\teval libdir=`$SED -n -e 's/^libdir=\\(.*\\)$/\\1/p' $lib`\n\t\ttest -z \"$libdir\" && \\\n\t\t  func_fatal_error \"'$lib' is not a valid libtool archive\"\n\t\tfunc_append newdlprefiles \" ${lt_sysroot:+=}$libdir/$name\"\n\t\t;;\n\t      esac\n\t    done\n\t    dlprefiles=$newdlprefiles\n\t  else\n\t    newdlfiles=\n\t    for lib in $dlfiles; do\n\t      case $lib in\n\t\t[\\\\/]* | [A-Za-z]:[\\\\/]*) abs=$lib ;;\n\t\t*) abs=`pwd`\"/$lib\" ;;\n\t      esac\n\t      func_append newdlfiles \" $abs\"\n\t    done\n\t    dlfiles=$newdlfiles\n\t    newdlprefiles=\n\t    for lib in $dlprefiles; do\n\t      case $lib in\n\t\t[\\\\/]* | [A-Za-z]:[\\\\/]*) abs=$lib ;;\n\t\t*) abs=`pwd`\"/$lib\" ;;\n\t      esac\n\t      func_append newdlprefiles \" $abs\"\n\t    done\n\t    dlprefiles=$newdlprefiles\n\t  fi\n\t  $RM $output\n\t  # place dlname in correct position for cygwin\n\t  # In fact, it would be nice if we could use this code for all target\n\t  # systems that can't hard-code library paths into their executables\n\t  # and that have no shared library path variable independent of PATH,\n\t  # but it turns out we can't easily determine that from inspecting\n\t  # libtool variables, so we have to hard-code the OSs to which it\n\t  # applies here; at the moment, that means platforms that use the PE\n\t  # object format with DLL files.  See the long comment at the top of\n\t  # tests/bindir.at for full details.\n\t  tdlname=$dlname\n\t  case $host,$output,$installed,$module,$dlname in\n\t    *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)\n\t      # If a -bindir argument was supplied, place the dll there.\n\t      if test -n \"$bindir\"; then\n\t\tfunc_relative_path \"$install_libdir\" \"$bindir\"\n\t\ttdlname=$func_relative_path_result/$dlname\n\t      else\n\t\t# Otherwise fall back on heuristic.\n\t\ttdlname=../bin/$dlname\n\t      fi\n\t      ;;\n\t  esac\n\t  $ECHO > $output \"\\\n# $outputname - a libtool library file\n# Generated by $PROGRAM (GNU $PACKAGE) $VERSION\n#\n# Please DO NOT delete this file!\n# It is necessary for linking the library.\n\n# The name that we can dlopen(3).\ndlname='$tdlname'\n\n# Names of this library.\nlibrary_names='$library_names'\n\n# The name of the static archive.\nold_library='$old_library'\n\n# Linker flags that cannot go in dependency_libs.\ninherited_linker_flags='$new_inherited_linker_flags'\n\n# Libraries that this one depends upon.\ndependency_libs='$dependency_libs'\n\n# Names of additional weak libraries provided by this library\nweak_library_names='$weak_libs'\n\n# Version information for $libname.\ncurrent=$current\nage=$age\nrevision=$revision\n\n# Is this an already installed library?\ninstalled=$installed\n\n# Should we warn about portability when linking against -modules?\nshouldnotlink=$module\n\n# Files to dlopen/dlpreopen\ndlopen='$dlfiles'\ndlpreopen='$dlprefiles'\n\n# Directory that this library needs to be installed in:\nlibdir='$install_libdir'\"\n\t  if test no,yes = \"$installed,$need_relink\"; then\n\t    $ECHO >> $output \"\\\nrelink_command=\\\"$relink_command\\\"\"\n\t  fi\n\tdone\n      }\n\n      # Do a symbolic link so that the libtool archive can be found in\n      # LD_LIBRARY_PATH before the program is installed.\n      func_show_eval '( cd \"$output_objdir\" && $RM \"$outputname\" && $LN_S \"../$outputname\" \"$outputname\" )' 'exit $?'\n      ;;\n    esac\n    exit $EXIT_SUCCESS\n}\n\nif test link = \"$opt_mode\" || test relink = \"$opt_mode\"; then\n  func_mode_link ${1+\"$@\"}\nfi\n\n\n# func_mode_uninstall arg...\nfunc_mode_uninstall ()\n{\n    $debug_cmd\n\n    RM=$nonopt\n    files=\n    rmforce=false\n    exit_status=0\n\n    # This variable tells wrapper scripts just to set variables rather\n    # than running their programs.\n    libtool_install_magic=$magic\n\n    for arg\n    do\n      case $arg in\n      -f) func_append RM \" $arg\"; rmforce=: ;;\n      -*) func_append RM \" $arg\" ;;\n      *) func_append files \" $arg\" ;;\n      esac\n    done\n\n    test -z \"$RM\" && \\\n      func_fatal_help \"you must specify an RM program\"\n\n    rmdirs=\n\n    for file in $files; do\n      func_dirname \"$file\" \"\" \".\"\n      dir=$func_dirname_result\n      if test . = \"$dir\"; then\n\todir=$objdir\n      else\n\todir=$dir/$objdir\n      fi\n      func_basename \"$file\"\n      name=$func_basename_result\n      test uninstall = \"$opt_mode\" && odir=$dir\n\n      # Remember odir for removal later, being careful to avoid duplicates\n      if test clean = \"$opt_mode\"; then\n\tcase \" $rmdirs \" in\n\t  *\" $odir \"*) ;;\n\t  *) func_append rmdirs \" $odir\" ;;\n\tesac\n      fi\n\n      # Don't error if the file doesn't exist and rm -f was used.\n      if { test -L \"$file\"; } >/dev/null 2>&1 ||\n\t { test -h \"$file\"; } >/dev/null 2>&1 ||\n\t test -f \"$file\"; then\n\t:\n      elif test -d \"$file\"; then\n\texit_status=1\n\tcontinue\n      elif $rmforce; then\n\tcontinue\n      fi\n\n      rmfiles=$file\n\n      case $name in\n      *.la)\n\t# Possibly a libtool archive, so verify it.\n\tif func_lalib_p \"$file\"; then\n\t  func_source $dir/$name\n\n\t  # Delete the libtool libraries and symlinks.\n\t  for n in $library_names; do\n\t    func_append rmfiles \" $odir/$n\"\n\t  done\n\t  test -n \"$old_library\" && func_append rmfiles \" $odir/$old_library\"\n\n\t  case $opt_mode in\n\t  clean)\n\t    case \" $library_names \" in\n\t    *\" $dlname \"*) ;;\n\t    *) test -n \"$dlname\" && func_append rmfiles \" $odir/$dlname\" ;;\n\t    esac\n\t    test -n \"$libdir\" && func_append rmfiles \" $odir/$name $odir/${name}i\"\n\t    ;;\n\t  uninstall)\n\t    if test -n \"$library_names\"; then\n\t      # Do each command in the postuninstall commands.\n\t      func_execute_cmds \"$postuninstall_cmds\" '$rmforce || exit_status=1'\n\t    fi\n\n\t    if test -n \"$old_library\"; then\n\t      # Do each command in the old_postuninstall commands.\n\t      func_execute_cmds \"$old_postuninstall_cmds\" '$rmforce || exit_status=1'\n\t    fi\n\t    # FIXME: should reinstall the best remaining shared library.\n\t    ;;\n\t  esac\n\tfi\n\t;;\n\n      *.lo)\n\t# Possibly a libtool object, so verify it.\n\tif func_lalib_p \"$file\"; then\n\n\t  # Read the .lo file\n\t  func_source $dir/$name\n\n\t  # Add PIC object to the list of files to remove.\n\t  if test -n \"$pic_object\" && test none != \"$pic_object\"; then\n\t    func_append rmfiles \" $dir/$pic_object\"\n\t  fi\n\n\t  # Add non-PIC object to the list of files to remove.\n\t  if test -n \"$non_pic_object\" && test none != \"$non_pic_object\"; then\n\t    func_append rmfiles \" $dir/$non_pic_object\"\n\t  fi\n\tfi\n\t;;\n\n      *)\n\tif test clean = \"$opt_mode\"; then\n\t  noexename=$name\n\t  case $file in\n\t  *.exe)\n\t    func_stripname '' '.exe' \"$file\"\n\t    file=$func_stripname_result\n\t    func_stripname '' '.exe' \"$name\"\n\t    noexename=$func_stripname_result\n\t    # $file with .exe has already been added to rmfiles,\n\t    # add $file without .exe\n\t    func_append rmfiles \" $file\"\n\t    ;;\n\t  esac\n\t  # Do a test to see if this is a libtool program.\n\t  if func_ltwrapper_p \"$file\"; then\n\t    if func_ltwrapper_executable_p \"$file\"; then\n\t      func_ltwrapper_scriptname \"$file\"\n\t      relink_command=\n\t      func_source $func_ltwrapper_scriptname_result\n\t      func_append rmfiles \" $func_ltwrapper_scriptname_result\"\n\t    else\n\t      relink_command=\n\t      func_source $dir/$noexename\n\t    fi\n\n\t    # note $name still contains .exe if it was in $file originally\n\t    # as does the version of $file that was added into $rmfiles\n\t    func_append rmfiles \" $odir/$name $odir/${name}S.$objext\"\n\t    if test yes = \"$fast_install\" && test -n \"$relink_command\"; then\n\t      func_append rmfiles \" $odir/lt-$name\"\n\t    fi\n\t    if test \"X$noexename\" != \"X$name\"; then\n\t      func_append rmfiles \" $odir/lt-$noexename.c\"\n\t    fi\n\t  fi\n\tfi\n\t;;\n      esac\n      func_show_eval \"$RM $rmfiles\" 'exit_status=1'\n    done\n\n    # Try to remove the $objdir's in the directories where we deleted files\n    for dir in $rmdirs; do\n      if test -d \"$dir\"; then\n\tfunc_show_eval \"rmdir $dir >/dev/null 2>&1\"\n      fi\n    done\n\n    exit $exit_status\n}\n\nif test uninstall = \"$opt_mode\" || test clean = \"$opt_mode\"; then\n  func_mode_uninstall ${1+\"$@\"}\nfi\n\ntest -z \"$opt_mode\" && {\n  help=$generic_help\n  func_fatal_help \"you must specify a MODE\"\n}\n\ntest -z \"$exec_cmd\" && \\\n  func_fatal_help \"invalid operation mode '$opt_mode'\"\n\nif test -n \"$exec_cmd\"; then\n  eval exec \"$exec_cmd\"\n  exit $EXIT_FAILURE\nfi\n\nexit $exit_status\n\n\n# The TAGs below are defined such that we never get into a situation\n# where we disable both kinds of libraries.  Given conflicting\n# choices, we go for a static library, that is the most portable,\n# since we can't tell whether shared libraries were disabled because\n# the user asked for that or because the platform doesn't support\n# them.  This is particularly important on AIX, because we don't\n# support having both static and shared libraries enabled at the same\n# time on that platform, so we default to a shared-only configuration.\n# If a disable-shared tag is given, we'll fallback to a static-only\n# configuration.  But we'll never go from static-only to shared-only.\n\n# ### BEGIN LIBTOOL TAG CONFIG: disable-shared\nbuild_libtool_libs=no\nbuild_old_libs=yes\n# ### END LIBTOOL TAG CONFIG: disable-shared\n\n# ### BEGIN LIBTOOL TAG CONFIG: disable-static\nbuild_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`\n# ### END LIBTOOL TAG CONFIG: disable-static\n\n# Local Variables:\n# mode:shell-script\n# sh-indentation:2\n# End:\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/missing",
    "content": "#! /bin/sh\n# Common wrapper for a few potentially missing GNU programs.\n\nscriptversion=2013-10-28.13; # UTC\n\n# Copyright (C) 1996-2014 Free Software Foundation, Inc.\n# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.\n\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2, or (at your option)\n# any later version.\n\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# As a special exception to the GNU General Public License, if you\n# distribute this file as part of a program that contains a\n# configuration script generated by Autoconf, you may include it under\n# the same distribution terms that you use for the rest of that program.\n\nif test $# -eq 0; then\n  echo 1>&2 \"Try '$0 --help' for more information\"\n  exit 1\nfi\n\ncase $1 in\n\n  --is-lightweight)\n    # Used by our autoconf macros to check whether the available missing\n    # script is modern enough.\n    exit 0\n    ;;\n\n  --run)\n    # Back-compat with the calling convention used by older automake.\n    shift\n    ;;\n\n  -h|--h|--he|--hel|--help)\n    echo \"\\\n$0 [OPTION]... PROGRAM [ARGUMENT]...\n\nRun 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due\nto PROGRAM being missing or too old.\n\nOptions:\n  -h, --help      display this help and exit\n  -v, --version   output version information and exit\n\nSupported PROGRAM values:\n  aclocal   autoconf  autoheader   autom4te  automake  makeinfo\n  bison     yacc      flex         lex       help2man\n\nVersion suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and\n'g' are ignored when checking the name.\n\nSend bug reports to <bug-automake@gnu.org>.\"\n    exit $?\n    ;;\n\n  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)\n    echo \"missing $scriptversion (GNU Automake)\"\n    exit $?\n    ;;\n\n  -*)\n    echo 1>&2 \"$0: unknown '$1' option\"\n    echo 1>&2 \"Try '$0 --help' for more information\"\n    exit 1\n    ;;\n\nesac\n\n# Run the given program, remember its exit status.\n\"$@\"; st=$?\n\n# If it succeeded, we are done.\ntest $st -eq 0 && exit 0\n\n# Also exit now if we it failed (or wasn't found), and '--version' was\n# passed; such an option is passed most likely to detect whether the\n# program is present and works.\ncase $2 in --version|--help) exit $st;; esac\n\n# Exit code 63 means version mismatch.  This often happens when the user\n# tries to use an ancient version of a tool on a file that requires a\n# minimum version.\nif test $st -eq 63; then\n  msg=\"probably too old\"\nelif test $st -eq 127; then\n  # Program was missing.\n  msg=\"missing on your system\"\nelse\n  # Program was found and executed, but failed.  Give up.\n  exit $st\nfi\n\nperl_URL=http://www.perl.org/\nflex_URL=http://flex.sourceforge.net/\ngnu_software_URL=http://www.gnu.org/software\n\nprogram_details ()\n{\n  case $1 in\n    aclocal|automake)\n      echo \"The '$1' program is part of the GNU Automake package:\"\n      echo \"<$gnu_software_URL/automake>\"\n      echo \"It also requires GNU Autoconf, GNU m4 and Perl in order to run:\"\n      echo \"<$gnu_software_URL/autoconf>\"\n      echo \"<$gnu_software_URL/m4/>\"\n      echo \"<$perl_URL>\"\n      ;;\n    autoconf|autom4te|autoheader)\n      echo \"The '$1' program is part of the GNU Autoconf package:\"\n      echo \"<$gnu_software_URL/autoconf/>\"\n      echo \"It also requires GNU m4 and Perl in order to run:\"\n      echo \"<$gnu_software_URL/m4/>\"\n      echo \"<$perl_URL>\"\n      ;;\n  esac\n}\n\ngive_advice ()\n{\n  # Normalize program name to check for.\n  normalized_program=`echo \"$1\" | sed '\n    s/^gnu-//; t\n    s/^gnu//; t\n    s/^g//; t'`\n\n  printf '%s\\n' \"'$1' is $msg.\"\n\n  configure_deps=\"'configure.ac' or m4 files included by 'configure.ac'\"\n  case $normalized_program in\n    autoconf*)\n      echo \"You should only need it if you modified 'configure.ac',\"\n      echo \"or m4 files included by it.\"\n      program_details 'autoconf'\n      ;;\n    autoheader*)\n      echo \"You should only need it if you modified 'acconfig.h' or\"\n      echo \"$configure_deps.\"\n      program_details 'autoheader'\n      ;;\n    automake*)\n      echo \"You should only need it if you modified 'Makefile.am' or\"\n      echo \"$configure_deps.\"\n      program_details 'automake'\n      ;;\n    aclocal*)\n      echo \"You should only need it if you modified 'acinclude.m4' or\"\n      echo \"$configure_deps.\"\n      program_details 'aclocal'\n      ;;\n   autom4te*)\n      echo \"You might have modified some maintainer files that require\"\n      echo \"the 'autom4te' program to be rebuilt.\"\n      program_details 'autom4te'\n      ;;\n    bison*|yacc*)\n      echo \"You should only need it if you modified a '.y' file.\"\n      echo \"You may want to install the GNU Bison package:\"\n      echo \"<$gnu_software_URL/bison/>\"\n      ;;\n    lex*|flex*)\n      echo \"You should only need it if you modified a '.l' file.\"\n      echo \"You may want to install the Fast Lexical Analyzer package:\"\n      echo \"<$flex_URL>\"\n      ;;\n    help2man*)\n      echo \"You should only need it if you modified a dependency\" \\\n           \"of a man page.\"\n      echo \"You may want to install the GNU Help2man package:\"\n      echo \"<$gnu_software_URL/help2man/>\"\n    ;;\n    makeinfo*)\n      echo \"You should only need it if you modified a '.texi' file, or\"\n      echo \"any other file indirectly affecting the aspect of the manual.\"\n      echo \"You might want to install the Texinfo package:\"\n      echo \"<$gnu_software_URL/texinfo/>\"\n      echo \"The spurious makeinfo call might also be the consequence of\"\n      echo \"using a buggy 'make' (AIX, DU, IRIX), in which case you might\"\n      echo \"want to install GNU make:\"\n      echo \"<$gnu_software_URL/make/>\"\n      ;;\n    *)\n      echo \"You might have modified some files without having the proper\"\n      echo \"tools for further handling them.  Check the 'README' file, it\"\n      echo \"often tells you about the needed prerequisites for installing\"\n      echo \"this package.  You may also peek at any GNU archive site, in\"\n      echo \"case some other package contains this missing '$1' program.\"\n      ;;\n  esac\n}\n\ngive_advice \"$1\" | sed -e '1s/^/WARNING: /' \\\n                       -e '2,$s/^/         /' >&2\n\n# Propagate the correct exit status (expected to be 127 for a program\n# not found, 63 for a program that failed due to version mismatch).\nexit $st\n\n# Local variables:\n# eval: (add-hook 'write-file-hooks 'time-stamp)\n# time-stamp-start: \"scriptversion=\"\n# time-stamp-format: \"%:y-%02m-%02d.%02H\"\n# time-stamp-time-zone: \"UTC\"\n# time-stamp-end: \"; # UTC\"\n# End:\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/zookeeper-client-c.BUILD",
    "content": "load(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\nload(\"@//third_party/org_apache_zookeeper:zookeeper.bzl\", \"genrule2\")\n\ngenrule2(\n    name = \"config_h\",\n    srcs = [\n        \"@//third_party/org_apache_zookeeper:genfiles\",\n    ] + glob([\n        \"src/**\",\n    ]),\n    outs = [\n        \"config.h\",\n        \"include/zookeeper.jute.c\",\n        \"include/zookeeper.jute.h\",\n    ],\n    cmd = \" && \".join([\n        \"cp -r -L external/org_apache_zookeeper $$TMP\",\n        \"cp third_party/org_apache_zookeeper/configure $$TMP/org_apache_zookeeper/configure \",\n        \"cp third_party/org_apache_zookeeper/install-sh $$TMP/org_apache_zookeeper/install-sh \",\n        \"cp third_party/org_apache_zookeeper/missing $$TMP/org_apache_zookeeper/missing \",\n        \"cp third_party/org_apache_zookeeper/config.guess $$TMP/org_apache_zookeeper/config.guess \",\n        \"cp third_party/org_apache_zookeeper/config.sub $$TMP/org_apache_zookeeper/config.sub \",\n        \"cp third_party/org_apache_zookeeper/Makefile.in $$TMP/org_apache_zookeeper/Makefile.in \",\n        \"cp third_party/org_apache_zookeeper/config.h.in $$TMP/org_apache_zookeeper/config.h.in \",\n        \"cp third_party/org_apache_zookeeper/ltmain.sh $$TMP/org_apache_zookeeper/ltmain.sh \",\n        \"cp -rf third_party/org_apache_zookeeper/generated $$TMP/org_apache_zookeeper/generated \",\n        \"cd $$TMP/org_apache_zookeeper \",\n        \"./configure --without-cppunit \",\n        \"cd $$ROOT\",\n        \"cp $$TMP/org_apache_zookeeper/config.h $(location config.h)\",\n        \"cp $$TMP/org_apache_zookeeper/generated/zookeeper.jute.c $(location include/zookeeper.jute.c)\",\n        \"cp $$TMP/org_apache_zookeeper/generated/zookeeper.jute.h $(location include/zookeeper.jute.h)\",\n    ]),\n)\n\ncc_library(\n    name = \"config\",\n    hdrs = [\n        \":config.h\",\n    ],\n)\n\ncc_library(\n    name = \"libzookeeper_mt\",\n    srcs = [\n        \"include/zookeeper.jute.c\",\n        \"src/addrvec.c\",\n        \"src/mt_adaptor.c\",\n        \"src/recordio.c\",\n        \"src/zk_hashtable.c\",\n        \"src/zk_log.c\",\n        \"src/zookeeper.c\",\n        \"src/hashtable/hashtable.c\",\n        \"src/hashtable/hashtable_itr.c\",\n        \"src/addrvec.h\",\n        \"src/zk_adaptor.h\",\n        \"src/zk_hashtable.h\",\n        \"src/hashtable/hashtable.h\",\n        \"src/hashtable/hashtable_itr.h\",\n        \"src/hashtable/hashtable_private.h\",\n    ] + select({\n        \"@bazel_tools//src/conditions:windows\": [\n            \"src/winport.h\",\n            \"src/winport.c\",\n        ],\n        \"//conditions:default\": [],\n    }),\n    hdrs = [\n        \"include/zookeeper.jute.h\",\n        \"include/proto.h\",\n        \"include/recordio.h\",\n        \"include/zookeeper.h\",\n        \"include/zookeeper_log.h\",\n        \"include/zookeeper_version.h\",\n    ] + select({\n        \"@bazel_tools//src/conditions:windows\": [\n            \"include/winconfig.h\",\n        ],\n        \"//conditions:default\": [],\n    }),\n    data = [\n        \"@//third_party/org_apache_zookeeper:genfiles\",\n    ],\n    defines = [\n        \"THREADED\",\n        \"USE_STATIC_LIB\",\n    ] + select({\n        \"@bazel_tools//src/conditions:windows\": [\n            \"_WINDOWS\",\n            \"WIN32\",\n        ],\n        \"//conditions:default\": [],\n    }),\n    includes = [\n        \".\",\n        # \"generated\",\n        \"include\",\n        \"src\",\n        \"src/hashtable\",\n    ],\n    linkopts = select({\n        \"@bazel_tools//src/conditions:windows\": [\"-DEFAULTLIB:ws2_32.lib\"],\n        \"//conditions:default\": [\"-lpthread\"],\n    }),\n    linkstatic = True,\n    visibility = [\"//visibility:public\"],\n    deps = select({\n        \"@bazel_tools//src/conditions:windows\": [\n            \"@//third_party/zookeeper-client-c:config\",\n        ],\n        \"//conditions:default\": [\n            \":config\",\n        ],\n    }),\n)\n"
  },
  {
    "path": "third_party/org_apache_zookeeper/zookeeper.bzl",
    "content": "# Copyright (C) 2016 The Android Open Source Project\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# Syntactic sugar for native genrule() rule:\n#   expose ROOT shell variable\n#   expose TMP shell variable\n\ndef genrule2(cmd, **kwargs):\n    cmd = \" && \".join([\n        \"ROOT=$$PWD\",\n        \"TMP=$$(mktemp -d || mktemp -d -t bazel-tmp)\",\n        \"(\" + cmd + \")\",\n        \"rm -rf $$TMP\",\n    ])\n    native.genrule(\n        cmd = cmd,\n        **kwargs\n    )\n"
  },
  {
    "path": "third_party/org_tensorflow/README.md",
    "content": "# Diff 1\nSince we use shared library build version of TF, it is important to\nuse libtensorflow_framework.so.\n\nHowever, by default it is not public, so we change the visibility here.\n\n# Diff 2\nTo do the failure handling, we will check fail when we can't find the\nremote device.\n\n# Diff 3\nSince in our training, we create multiple masters, this log info is too messy.\n\n# Diff 4\nAdds the ability to tune the default handler number.\n\n# Diff 5\nAdd a macro protected Read over Pread to the HDFS random file accessor.\nRead is preferred over Pread since it works better with caching and block\nreader. Read improves the read performance for about 30%.\n\n# Diff: GPU Support\nIn `tensorflow/core/kernels/BUILD`:\n`gpu_device_array` is for array-of-tensors types used by our custom ops.\nThis patch exports `gpu_device_array` libs as target in a lighter manner. However if we builds on this original `gpu_device_array` [cc_lib](https://github.com/tensorflow/tensorflow/blob/v2.4.0/tensorflow/core/kernels/BUILD#L725) by changing its visibility directly to public, it will fail to build because it duplicated-dependency on `//tensorflow/core:lib` which'd lead to the multiple definitions while building cuda.\n\n# Diff 8:\nThere is no default timeout for RPCs like CleanGraph, which causes RPC hangs forever when the remote is down even if\ngrpc_fail_fast is true.\n\n# Diff 9:\nTemporarily solution for tensorflow deps.\n\n# Diff 11:\nAdd tf_gpu_kernel_library_allow_except to compile cpu/gpu version hash table\n# Diff 11 & 12:\nAdd allow_except rules to compile cpu/gpu version hash table\n\n# Diff executor.cc\nThis patch provides more detailed profiling\n\n# Diff regarding split ops\nThis patch fixes the issue of split ops still using CPU execution path when it's explicitly placed on the GPU for int32 value type. "
  },
  {
    "path": "third_party/org_tensorflow/tf.patch",
    "content": "diff --git a/tensorflow/BUILD b/tensorflow/BUILD\nindex 56b33a493fc..c14d267a353 100644\n--- a/tensorflow/BUILD\n+++ b/tensorflow/BUILD\n@@ -34,7 +34,7 @@ load(\n load(\"@bazel_skylib//:bzl_library.bzl\", \"bzl_library\")\n \n package(\n-    default_visibility = [\":internal\"],\n+    default_visibility = [\"//visibility:public\"],\n     licenses = [\"notice\"],  # Apache 2.0\n )\n \ndiff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD\nindex 5f307a62f62..198b211d35d 100644\n--- a/tensorflow/core/BUILD\n+++ b/tensorflow/core/BUILD\n@@ -572,7 +572,7 @@ cc_library(\n     ]) + if_libtpu(\n         if_false = [\"//tensorflow/compiler/mlir/tensorflow:mlir_passthrough_op\"],\n         if_true = [],\n-    ),\n+    ) + [\"@//monolith/native_training/runtime/ops:monolith_ops_for_tf\"],\n )\n \n alias(\ndiff --git a/tensorflow/core/data/compression_utils.cc b/tensorflow/core/data/compression_utils.cc\nindex bbff3a96667..ea0683cb236 100644\n--- a/tensorflow/core/data/compression_utils.cc\n+++ b/tensorflow/core/data/compression_utils.cc\n@@ -31,7 +31,10 @@ Status CompressElement(const std::vector<Tensor>& element,\n     if (DataTypeCanUseMemcpy(component.dtype())) {\n       // Some datatypes can be memcopied, allowing us to save two copies\n       // (AsProtoTensorContent and SerializeToArray).\n-      total_size += DMAHelper::buffer(&component)->size();\n+      auto buffer = DMAHelper::buffer(&component);\n+      if (buffer) {\n+        total_size += buffer->size();\n+      }\n     } else {\n       non_memcpy_components.emplace_back();\n       component.AsProtoTensorContent(&non_memcpy_components.back());\n@@ -53,8 +56,10 @@ Status CompressElement(const std::vector<Tensor>& element,\n     component.shape().AsProto(metadata->mutable_tensor_shape());\n     if (DataTypeCanUseMemcpy(component.dtype())) {\n       const TensorBuffer* buffer = DMAHelper::buffer(&component);\n-      memcpy(position, buffer->data(), buffer->size());\n-      metadata->set_tensor_size_bytes(buffer->size());\n+      if (buffer) {\n+        memcpy(position, buffer->data(), buffer->size());\n+        metadata->set_tensor_size_bytes(buffer->size());\n+      }\n     } else {\n       TensorProto& proto = non_memcpy_components[non_memcpy_component_index++];\n       proto.SerializeToArray(position, proto.ByteSizeLong());\n@@ -94,8 +99,13 @@ Status UncompressElement(const CompressedElement& compressed,\n     if (DataTypeCanUseMemcpy(metadata.dtype())) {\n       out->emplace_back(metadata.dtype(), metadata.tensor_shape());\n       TensorBuffer* buffer = DMAHelper::buffer(&out->back());\n-      iov[i].iov_base = buffer->data();\n-      iov[i].iov_len = buffer->size();\n+      if (buffer) {\n+        iov[i].iov_base = buffer->data();\n+        iov[i].iov_len = buffer->size();\n+      } else {\n+        iov[i].iov_base = nullptr;\n+        iov[i].iov_len = 0;\n+      }\n     } else {\n       // Allocate an empty Tensor. We will fill it out later after\n       // uncompressing into the tensor_proto_str.\ndiff --git a/tensorflow/core/distributed_runtime/master.cc b/tensorflow/core/distributed_runtime/master.cc\nindex adaecf861e6..979fcff54a9 100644\n--- a/tensorflow/core/distributed_runtime/master.cc\n+++ b/tensorflow/core/distributed_runtime/master.cc\n@@ -260,6 +260,7 @@ class DeviceFinder {\n     mutex_lock l(mu_);\n     // TODO(mrry): Propagate a timeout here, since `num_pending_` may\n     // never become zero.\n+    int times = 0;\n     while (num_pending_ != 0) {\n       pending_zero_.wait_for(l, std::chrono::milliseconds(kLoggingPeriodMs));\n       if (num_pending_ != 0) {\n@@ -271,6 +272,16 @@ class DeviceFinder {\n           }\n         }\n       }\n+      if (++times >= 6) {\n+        std::string unseen_workers;\n+        for (size_t i = 0; i < targets_.size(); ++i) {\n+          if (!seen_targets_[i]) {\n+            unseen_workers += \" \" + targets_[i];\n+          }\n+        }\n+        return errors::DeadlineExceeded(\n+            \"Unable to get responses from workers: \", unseen_workers);\n+      }\n     }\n     return status_;\n   }\ndiff --git a/tensorflow/core/distributed_runtime/rpc/grpc_channel.cc b/tensorflow/core/distributed_runtime/rpc/grpc_channel.cc\nindex 985b0454837..e4b6c48e80e 100644\n--- a/tensorflow/core/distributed_runtime/rpc/grpc_channel.cc\n+++ b/tensorflow/core/distributed_runtime/rpc/grpc_channel.cc\n@@ -20,9 +20,9 @@ limitations under the License.\n #include <map>\n #include <unordered_map>\n \n-#include \"grpcpp/create_channel.h\"\n #include \"absl/strings/escaping.h\"\n #include \"absl/strings/str_split.h\"\n+#include \"grpcpp/create_channel.h\"\n #include \"tensorflow/core/lib/core/errors.h\"\n #include \"tensorflow/core/lib/core/status.h\"\n #include \"tensorflow/core/lib/gtl/map_util.h\"\n@@ -298,7 +298,7 @@ class SparseGrpcChannelCache : public CachingGrpcChannelCache {\n       : job_id_(job_id),\n         host_ports_(host_ports),\n         channel_func_(std::move(channel_func)) {\n-    LOG(INFO) << \"Initialize GrpcChannelCache for job \" << ToString();\n+    // LOG(INFO) << \"Initialize GrpcChannelCache for job \" << ToString();\n   }\n   ~SparseGrpcChannelCache() override {}\n \ndiff --git a/tensorflow/core/distributed_runtime/rpc/grpc_state.h b/tensorflow/core/distributed_runtime/rpc/grpc_state.h\nindex d0e67cdcd57..6528ad1d455 100644\n--- a/tensorflow/core/distributed_runtime/rpc/grpc_state.h\n+++ b/tensorflow/core/distributed_runtime/rpc/grpc_state.h\n@@ -84,7 +84,7 @@ class RPCState : public GrpcClientCQTag {\n                 return false;\n               }\n             }(),\n-            (call_opts != nullptr ? call_opts->GetTimeout() : 0), max_retries,\n+            (call_opts != nullptr ? call_opts->GetTimeout() : 600000), max_retries,\n             target) {\n   }\n \ndiff --git a/tensorflow/core/distributed_runtime/rpc/grpc_worker_service.cc b/tensorflow/core/distributed_runtime/rpc/grpc_worker_service.cc\nindex 723a5130161..9ef663f1711 100644\n--- a/tensorflow/core/distributed_runtime/rpc/grpc_worker_service.cc\n+++ b/tensorflow/core/distributed_runtime/rpc/grpc_worker_service.cc\n@@ -55,6 +55,7 @@ limitations under the License.\n #include \"tensorflow/core/platform/tracing.h\"\n #include \"tensorflow/core/protobuf/transport_options.pb.h\"\n #include \"tensorflow/core/protobuf/worker.pb.h\"\n+#include \"tensorflow/core/util/env_var.h\"\n \n namespace tensorflow {\n \n@@ -132,6 +133,9 @@ class GrpcWorkerServiceThread {\n     // TODO(ncteisen): This may require performance engineering. We can\n     // change the number of threads, the number of handlers per thread,\n     // or even decide to specialize certain threads to certain methods.\n+    float ratio;\n+    ReadFloatFromEnvVar(\"MONOLITH_GRPC_WORKER_SERVICE_HANDLER_MULTIPLIER\", 1.0, &ratio);\n+    auto get_default_value = [ratio](int x) { return int(x * ratio); };\n     SETUP_FOR_REQUEST(GetStatus, 1, false);\n     SETUP_FOR_REQUEST(CreateWorkerSession, 1, false);\n     SETUP_FOR_REQUEST(DeleteWorkerSession, 1, false);\n@@ -140,20 +144,20 @@ class GrpcWorkerServiceThread {\n     SETUP_FOR_REQUEST(DeregisterGraph, 1, false);\n     SETUP_FOR_REQUEST(Logging, 1, false);\n     SETUP_FOR_REQUEST(Tracing, 1, false);\n-    SETUP_FOR_REQUEST(CompleteGroup, 10, true);\n-    SETUP_FOR_REQUEST(CompleteInstance, 10, true);\n-    SETUP_FOR_REQUEST(GetStepSequence, 10, true);\n-    SETUP_FOR_REQUEST(RecvBuf, 500, true);\n-    SETUP_FOR_REQUEST(RunGraph, 100, true);\n-    SETUP_FOR_REQUEST(CleanupGraph, 100, false);\n-    SETUP_FOR_REQUEST(MarkRecvFinished, 10, false);\n+    SETUP_FOR_REQUEST(CompleteGroup, get_default_value(10), true);\n+    SETUP_FOR_REQUEST(CompleteInstance, get_default_value(10), true);\n+    SETUP_FOR_REQUEST(GetStepSequence, get_default_value(10), true);\n+    SETUP_FOR_REQUEST(RecvBuf, get_default_value(500), true);\n+    SETUP_FOR_REQUEST(RunGraph, get_default_value(100), true);\n+    SETUP_FOR_REQUEST(CleanupGraph, get_default_value(100), false);\n+    SETUP_FOR_REQUEST(MarkRecvFinished, get_default_value(10), false);\n \n     // TODO(ncteisen): Determine a better policy for enqueuing the\n     // appropriate number of each request type.\n     for (int i = 0;\n          i < gtl::FindWithDefault(\n                  queue_depth_, static_cast<int>(GrpcWorkerMethod::kRecvTensor),\n-                 1000);\n+                 get_default_value(1000));\n          ++i) {\n       EnqueueRecvTensorRequestRaw();\n     }\ndiff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD\nindex 53c53ac3ff6..b1ae727ffbe 100644\n--- a/tensorflow/core/kernels/BUILD\n+++ b/tensorflow/core/kernels/BUILD\n@@ -730,6 +730,18 @@ cc_library(\n     ],\n )\n \n+\n+cc_library(\n+    name = \"gpu_device_array_for_custom_op\",\n+    hdrs = [\n+        \"gpu_device_array.h\",\n+        \"gpu_device_array_gpu.h\",\n+    ],\n+    deps = [\n+        \"//tensorflow/core:gpu_headers_lib\",\n+    ],\n+)\n+\n # Depending on a build configuration this target provides custom kernel for Eigen\n # tensor contractions (small matrix multiplication kernel used to multiple together\n # blocks of the original tensors).\ndiff --git a/tensorflow/core/kernels/dynamic_partition_op_gpu.cu.cc b/tensorflow/core/kernels/dynamic_partition_op_gpu.cu.cc\nindex 2bcc2b6ec65..9062b568aeb 100644\n--- a/tensorflow/core/kernels/dynamic_partition_op_gpu.cu.cc\n+++ b/tensorflow/core/kernels/dynamic_partition_op_gpu.cu.cc\n@@ -479,6 +479,8 @@ class DynamicPartitionOpGPU : public AsyncOpKernel {\n \n TF_CALL_GPU_NUMBER_TYPES(REGISTER_DYNAMIC_PARTITION_GPU);\n TF_CALL_COMPLEX_TYPES(REGISTER_DYNAMIC_PARTITION_GPU);\n+TF_CALL_int32(REGISTER_DYNAMIC_PARTITION_GPU);\n+TF_CALL_int64(REGISTER_DYNAMIC_PARTITION_GPU);\n #undef REGISTER_DYNAMIC_PARTITION_GPU\n \n }  // namespace tensorflow\ndiff --git a/tensorflow/core/kernels/padding_fifo_queue_op.cc b/tensorflow/core/kernels/padding_fifo_queue_op.cc\nindex c92cd732d5b..d574f6a02d8 100644\n--- a/tensorflow/core/kernels/padding_fifo_queue_op.cc\n+++ b/tensorflow/core/kernels/padding_fifo_queue_op.cc\n@@ -70,4 +70,9 @@ REGISTER_KERNEL_BUILDER(Name(\"PaddingFIFOQueue\").Device(DEVICE_CPU),\n REGISTER_KERNEL_BUILDER(Name(\"PaddingFIFOQueueV2\").Device(DEVICE_CPU),\n                         PaddingFIFOQueueOp);\n \n+REGISTER_KERNEL_BUILDER(\n+    Name(\"PaddingFIFOQueueV2\").Device(DEVICE_DEFAULT).HostMemory(\"handle\"),\n+    PaddingFIFOQueueOp);\n+\n+\n }  // namespace tensorflow\ndiff --git a/tensorflow/core/kernels/split_lib_gpu.cu.cc b/tensorflow/core/kernels/split_lib_gpu.cu.cc\nindex b4379a01ce1..99cdfa79fa1 100644\n--- a/tensorflow/core/kernels/split_lib_gpu.cu.cc\n+++ b/tensorflow/core/kernels/split_lib_gpu.cu.cc\n@@ -55,6 +55,7 @@ TF_CALL_int64(DEFINE_GPU_KERNELS);\n TF_CALL_bfloat16(DEFINE_GPU_KERNELS);\n TF_CALL_uint8(DEFINE_GPU_KERNELS);\n TF_CALL_GPU_ALL_TYPES(DEFINE_GPU_KERNELS);\n+TF_CALL_int32(DEFINE_GPU_KERNELS);\n \n #undef DEFINE_GPU_KERNELS\n #define DEFINE_GPU_KERNELS(T) template struct SplitCustom<Eigen::GpuDevice, T>;\n@@ -62,6 +63,7 @@ TF_CALL_GPU_ALL_TYPES(DEFINE_GPU_KERNELS);\n TF_CALL_GPU_NUMBER_TYPES(DEFINE_GPU_KERNELS);\n TF_CALL_COMPLEX_TYPES(DEFINE_GPU_KERNELS);\n TF_CALL_bfloat16(DEFINE_GPU_KERNELS);\n+TF_CALL_int32(DEFINE_GPU_KERNELS);\n \n #undef DEFINE_GPU_KERNELS\n \n@@ -246,6 +248,7 @@ void SplitVOpGPULaunch<T, IntType>::Run(\n TF_CALL_GPU_NUMBER_TYPES(REGISTER_GPU_KERNEL);\n TF_CALL_COMPLEX_TYPES(REGISTER_GPU_KERNEL);\n TF_CALL_bfloat16(REGISTER_GPU_KERNEL);\n+TF_CALL_int32(REGISTER_GPU_KERNEL);\n #undef REGISTER_GPU_KERNEL\n #define REGISTER_GPU_KERNEL(T)                 \\\n   template struct SplitVOpGPULaunch<T, int32>; \\\n@@ -254,6 +257,7 @@ TF_CALL_bfloat16(REGISTER_GPU_KERNEL);\n TF_CALL_GPU_NUMBER_TYPES(REGISTER_GPU_KERNEL);\n TF_CALL_COMPLEX_TYPES(REGISTER_GPU_KERNEL);\n TF_CALL_bfloat16(REGISTER_GPU_KERNEL);\n+TF_CALL_int32(REGISTER_GPU_KERNEL);\n #undef REGISTER_GPU_KERNEL\n \n }  // namespace tensorflow\ndiff --git a/tensorflow/core/kernels/split_op.cc b/tensorflow/core/kernels/split_op.cc\nindex 6f2cd965e7a..641ee45e537 100644\n--- a/tensorflow/core/kernels/split_op.cc\n+++ b/tensorflow/core/kernels/split_op.cc\n@@ -347,6 +347,7 @@ REGISTER_SPLIT(quint8);\n TF_CALL_bfloat16(REGISTER_GPU);\n TF_CALL_GPU_NUMBER_TYPES(REGISTER_GPU);\n TF_CALL_COMPLEX_TYPES(REGISTER_GPU);\n+TF_CALL_int32(REGISTER_GPU);\n #undef REGISTER_GPU\n \n #endif  // GOOGLE_CUDA || TENSORFLOW_USE_ROCM\ndiff --git a/tensorflow/core/kernels/split_v_op.cc b/tensorflow/core/kernels/split_v_op.cc\nindex fc070610877..24a5f869d19 100644\n--- a/tensorflow/core/kernels/split_v_op.cc\n+++ b/tensorflow/core/kernels/split_v_op.cc\n@@ -474,27 +474,12 @@ TF_CALL_ALL_TYPES(REGISTER_SPLIT_LEN);\n TF_CALL_bfloat16(REGISTER_GPU_LEN);\n TF_CALL_GPU_NUMBER_TYPES(REGISTER_GPU_LEN);\n TF_CALL_COMPLEX_TYPES(REGISTER_GPU_LEN);\n+// see https://github.com/tensorflow/tensorflow/pull/28051\n+TF_CALL_int32(REGISTER_GPU_LEN);\n+\n #undef REGISTER_GPU_LEN\n #undef REGISTER_GPU\n \n-// special GPU kernel for int32\n-\n-#define REGISTER_GPU_int32(len_type)                            \\\n-  REGISTER_KERNEL_BUILDER(Name(\"SplitV\")                        \\\n-                              .Device(DEVICE_GPU)               \\\n-                              .TypeConstraint<int32>(\"T\")       \\\n-                              .TypeConstraint<len_type>(\"Tlen\") \\\n-                              .HostMemory(\"size_splits\")        \\\n-                              .HostMemory(\"split_dim\")          \\\n-                              .HostMemory(\"value\")              \\\n-                              .HostMemory(\"output\"),            \\\n-                          SplitVOpCPU<int32, len_type>);\n-\n-REGISTER_GPU_int32(int32);\n-REGISTER_GPU_int32(int64);\n-\n-#undef REGISTER_GPU_int32\n-\n #endif  // GOOGLE_CUDA || TENSORFLOW_USE_ROCM\n \n }  // end namespace tensorflow\ndiff --git a/tensorflow/core/platform/hadoop/hadoop_file_system.cc b/tensorflow/core/platform/hadoop/hadoop_file_system.cc\nindex 74195db7730..edea461813f 100644\n--- a/tensorflow/core/platform/hadoop/hadoop_file_system.cc\n+++ b/tensorflow/core/platform/hadoop/hadoop_file_system.cc\n@@ -16,6 +16,7 @@ limitations under the License.\n #include \"tensorflow/core/platform/hadoop/hadoop_file_system.h\"\n \n #include <errno.h>\n+#include <sys/time.h>\n \n #include \"tensorflow/core/platform/env.h\"\n #include \"tensorflow/core/platform/error.h\"\n@@ -52,6 +53,7 @@ class LibHDFS {\n   std::function<void(hdfsBuilder*, const char*)> hdfsBuilderSetNameNode;\n   std::function<int(const char*, char**)> hdfsConfGetStr;\n   std::function<int(hdfsFS, hdfsFile)> hdfsCloseFile;\n+  std::function<tSize(hdfsFS, hdfsFile, void*, tSize)> hdfsRead;\n   std::function<tSize(hdfsFS, hdfsFile, tOffset, void*, tSize)> hdfsPread;\n   std::function<tSize(hdfsFS, hdfsFile, const void*, tSize)> hdfsWrite;\n   std::function<int(hdfsFS, hdfsFile)> hdfsHFlush;\n@@ -79,6 +81,7 @@ class LibHDFS {\n       BIND_HDFS_FUNC(hdfsBuilderSetNameNode);\n       BIND_HDFS_FUNC(hdfsConfGetStr);\n       BIND_HDFS_FUNC(hdfsCloseFile);\n+      BIND_HDFS_FUNC(hdfsRead);\n       BIND_HDFS_FUNC(hdfsPread);\n       BIND_HDFS_FUNC(hdfsWrite);\n       BIND_HDFS_FUNC(hdfsHFlush);\n@@ -225,6 +228,13 @@ class HDFSRandomAccessFile : public RandomAccessFile {\n     } else {\n       disable_eof_retried_ = false;\n     }\n+    const char* hdfs_optimize_read = getenv(\"HDFS_OPTIMIZE_READ\");\n+    if (hdfs_optimize_read && hdfs_optimize_read[0] == '1') {\n+      disable_eof_retried_ = true;\n+      optimize_read_ = true;\n+    } else {\n+      optimize_read_ = false;\n+    }\n   }\n \n   ~HDFSRandomAccessFile() override {\n@@ -256,8 +266,17 @@ class HDFSRandomAccessFile : public RandomAccessFile {\n       // of int32. -2 offset can avoid JVM OutOfMemoryError.\n       size_t read_n =\n           std::min(n, static_cast<size_t>(std::numeric_limits<int>::max() - 2));\n-      tSize r = libhdfs()->hdfsPread(fs_, file_, static_cast<tOffset>(offset),\n-                                     dst, static_cast<tSize>(read_n));\n+\n+      tSize r = 0;\n+      if (optimize_read_) {\n+        // offset is ignored, we simply rely on file_ to track the progress.\n+        // Always reads from the beginning of the file.\n+        r = libhdfs()->hdfsRead(fs_, file_, dst,\n+                                static_cast<tSize>(read_n));\n+      } else {\n+        r = libhdfs()->hdfsPread(fs_, file_, static_cast<tOffset>(offset),\n+                                 dst, static_cast<tSize>(read_n));\n+      }\n       if (r > 0) {\n         dst += r;\n         n -= r;\n@@ -269,6 +288,8 @@ class HDFSRandomAccessFile : public RandomAccessFile {\n         // contents.\n         //\n         // Fixes #5438\n+        struct timeval t0, t1;\n+        gettimeofday(&t0, NULL);\n         if (file_ != nullptr && libhdfs()->hdfsCloseFile(fs_, file_) != 0) {\n           return IOError(filename_, errno);\n         }\n@@ -277,6 +298,10 @@ class HDFSRandomAccessFile : public RandomAccessFile {\n         if (file_ == nullptr) {\n           return IOError(filename_, errno);\n         }\n+        gettimeofday(&t1, NULL);\n+        long elapsed = (t1.tv_sec-t0.tv_sec)*1000000 + t1.tv_usec-t0.tv_usec;\n+        LOG_EVERY_N(WARNING, 50) << \"****************************Re-Open time: \"\n+                                 << elapsed/1000.0 << \" ms. ******************\";\n         eof_retried = true;\n       } else if (eof_retried && r == 0) {\n         s = Status(error::OUT_OF_RANGE, \"Read less bytes than requested\");\n@@ -295,6 +320,7 @@ class HDFSRandomAccessFile : public RandomAccessFile {\n   string hdfs_filename_;\n   hdfsFS fs_;\n   bool disable_eof_retried_;\n+  bool optimize_read_;\n \n   mutable mutex mu_;\n   mutable hdfsFile file_ TF_GUARDED_BY(mu_);\n@@ -304,13 +330,24 @@ Status HadoopFileSystem::NewRandomAccessFile(\n     const string& fname, TransactionToken* token,\n     std::unique_ptr<RandomAccessFile>* result) {\n   hdfsFS fs = nullptr;\n+  struct timeval t0, t1, t2;\n+  gettimeofday(&t0, NULL);\n   TF_RETURN_IF_ERROR(Connect(fname, &fs));\n-\n+  gettimeofday(&t1, NULL);\n   hdfsFile file = libhdfs()->hdfsOpenFile(fs, TranslateName(fname).c_str(),\n                                           O_RDONLY, 0, 0, 0);\n+  gettimeofday(&t2, NULL);\n   if (file == nullptr) {\n     return IOError(fname, errno);\n   }\n+  long elapsed1 = (t1.tv_sec-t0.tv_sec)*1000000 + t1.tv_usec-t0.tv_usec;\n+  long elapsed2 = (t2.tv_sec-t1.tv_sec)*1000000 + t2.tv_usec-t1.tv_usec;\n+  LOG_EVERY_N(WARNING, 50) << \"****************************\"\n+                            <<\"NewRandomAccessFile the connect time is: \"\n+                            << elapsed1/1000.0\n+                            << \" ms, and the open time is: \"\n+                            << elapsed2/1000.0\n+                            << \" ms. ***************************\";\n   result->reset(\n       new HDFSRandomAccessFile(fname, TranslateName(fname), fs, file));\n   return Status::OK();\ndiff --git a/tensorflow/tensorflow.bzl b/tensorflow/tensorflow.bzl\nindex 096cdd17dcb..71b03b159fe 100644\n--- a/tensorflow/tensorflow.bzl\n+++ b/tensorflow/tensorflow.bzl\n@@ -1343,6 +1343,7 @@ def _cuda_copts(opts = []):\n         \"//conditions:default\": [],\n         \"@local_config_cuda//cuda:using_nvcc\": ([\n             \"-nvcc_options=relaxed-constexpr\",\n+            \"-nvcc_options=generate-line-info\",\n             \"-nvcc_options=ftz=true\",\n         ]),\n         \"@local_config_cuda//cuda:using_clang\": ([\n@@ -1383,6 +1384,42 @@ def tf_gpu_kernel_library(\n         **kwargs\n     )\n \n+def tf_gpu_kernel_library_allow_except(\n+        srcs,\n+        copts = [],\n+        cuda_copts = [],\n+        deps = [],\n+        hdrs = [],\n+        **kwargs):\n+    copts = copts + tf_copts(allow_exceptions = True) + _cuda_copts(opts = cuda_copts) + rocm_copts(opts = cuda_copts)\n+    kwargs[\"features\"] = kwargs.get(\"features\", []) + [\"-use_header_modules\"]\n+\n+    # tf_custom_op\n+\n+    cuda_deps = [\n+            #clean_dep(\"//tensorflow/core:stream_executor_headers_lib\"),\n+            \"@local_config_cuda//cuda:cuda_headers\",\n+            #\"@local_config_cuda//cuda:cudart_static\",\n+        ]\n+    rocm_deps = [\n+        #clean_dep(\"//tensorflow/core:stream_executor_headers_lib\"),\n+    ]\n+    # deps = deps + tf_custom_op_library_additional_deps()\n+\n+    # Override EIGEN_STRONG_INLINE to inline when\n+    # --define=override_eigen_strong_inline=true to avoid long compiling time.\n+    # See https://github.com/tensorflow/tensorflow/issues/10521\n+    copts = copts + if_override_eigen_strong_inline([\"/DEIGEN_STRONG_INLINE=inline\"])\n+\n+    cuda_library(\n+        srcs = srcs,\n+        hdrs = hdrs,\n+        copts = copts + if_tensorrt([\"-DGOOGLE_TENSORRT=1\"]),\n+        deps = deps + if_cuda_is_configured_compat(cuda_deps) + if_rocm_is_configured(rocm_deps),\n+        alwayslink = 1,\n+        **kwargs\n+    )\n+\n def tf_gpu_library(deps = None, cuda_deps = None, copts = tf_copts(), **kwargs):\n     \"\"\"Generate a cc_library with a conditional set of CUDA dependencies.\n \ndiff --git a/third_party/com_google_absl_fix_mac_and_nvcc_build.patch b/third_party/com_google_absl_fix_mac_and_nvcc_build.patch\nindex 6301119ab2c..f915a950b97 100644\n--- a/third_party/com_google_absl_fix_mac_and_nvcc_build.patch\n+++ b/third_party/com_google_absl_fix_mac_and_nvcc_build.patch\n@@ -308,3 +308,23 @@ index 7a53c81..159b0f0 100644\n      visibility = [\"//visibility:public\"],\n      deps = [\n          \":civil_time\",\n+diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl\n+index 9dd6bd0a..84292c8b 100644\n+--- a/absl/copts/configure_copts.bzl\n++++ b/absl/copts/configure_copts.bzl\n+@@ -50,6 +50,7 @@ ABSL_RANDOM_RANDEN_COPTS = select({\n+     \":cpu_x64_windows\": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS,\n+     \":cpu_haswell\": ABSL_RANDOM_HWAES_X64_FLAGS,\n+     \":cpu_ppc\": [\"-mcrypto\"],\n++    \":cpu_aarch64\": ABSL_RANDOM_HWAES_ARM64_FLAGS,\n+ \n+     # Supported by default or unsupported.\n+     \"//conditions:default\": [],\n+@@ -70,6 +71,7 @@ def absl_random_randen_copts_init():\n+         \"darwin\",\n+         \"x64_windows_msvc\",\n+         \"x64_windows\",\n++        \"aarch64\",\n+     ]\n+     for cpu in cpu_configs:\n+         native.config_setting(\ndiff --git a/third_party/snappy.BUILD b/third_party/snappy.BUILD\nindex a2ab4924f29..77cb7ea0dff 100644\n--- a/third_party/snappy.BUILD\n+++ b/third_party/snappy.BUILD\n@@ -27,6 +27,7 @@ cc_library(\n             \"-Wno-implicit-function-declaration\",\n         ],\n     }),\n+    includes = [\".\"],\n     defines = select({\n         \"@org_tensorflow//tensorflow:windows\": [],\n         \"//conditions:default\": [\"HAVE_SYS_UIO_H\"],\n"
  },
  {
    "path": "third_party/org_tensorflow_serving/public_tf_serving.patch",
    "content": "diff --git a/tensorflow_serving/model_servers/BUILD b/tensorflow_serving/model_servers/BUILD\nindex 6c812c12..f214f76a 100644\n--- a/tensorflow_serving/model_servers/BUILD\n+++ b/tensorflow_serving/model_servers/BUILD\n@@ -235,7 +235,9 @@ cc_library(\n         \"//tensorflow_serving/servables/tensorflow:predict_impl\",\n         \"//tensorflow_serving/servables/tensorflow:regression_service\",\n         \"//tensorflow_serving/servables/tensorflow:thread_pool_factory\",\n+        \"@//monolith/native_training/runtime/common:metrics\",\n         \"@com_github_grpc_grpc//:grpc++\",\n+        \"@com_google_absl//absl/time\",\n         \"@org_tensorflow//tensorflow/core:lib\",\n         \"@org_tensorflow//tensorflow/core:protos_all_cc\",\n     ],\n@@ -319,6 +321,7 @@ SUPPORTED_TENSORFLOW_OPS = if_v2([]) + if_not_v2([\n     \"@org_tensorflow//tensorflow/contrib:contrib_ops_op_lib\",\n ]) + [\n     \"@org_tensorflow_text//tensorflow_text:ops_lib\",\n+    \"@//monolith/native_training/runtime/ops:serving_ops_cc\",\n ]\n \n cc_library(\ndiff --git a/tensorflow_serving/model_servers/version.cc b/tensorflow_serving/model_servers/version.cc\nindex 96230a6c..4b1fb8e5 100644\n--- a/tensorflow_serving/model_servers/version.cc\n+++ b/tensorflow_serving/model_servers/version.cc\n@@ -13,9 +13,7 @@ See the License for the specific language governing permissions and\n limitations under the License.\n ==============================================================================*/\n \n-#include \"tensorflow_serving/model_servers/version.h\"\n-\n-const char kTFS_SCM_Revision[] = TF_MODELSERVER_VERSION_STRING;\n+const char kTFS_SCM_Revision[] = \"2.4.0\";\n \n extern \"C\" {\n const char* TF_Serving_Version() { return kTFS_SCM_Revision; }\n\ndiff --git a/tensorflow_serving/config/BUILD b/tensorflow_serving/config/BUILD\nindex 516bf55..b32b2ad 100644\n--- a/tensorflow_serving/config/BUILD\n+++ b/tensorflow_serving/config/BUILD\n@@ -51,6 +51,18 @@ serving_proto_library(\n     ],\n )\n \n+serving_proto_library_py(\n+    name = \"platform_config_py_pb2\",\n+    srcs = [\"platform_config.proto\"],\n+    proto_library = \"platform_config_proto\",\n+    visibility = [\n+        \"//visibility:public\",\n+    ],\n+    deps = [\n+        \"@org_tensorflow//tensorflow/core:protos_all_py\",\n+    ],\n+)\n+\n serving_proto_library(\n     name = \"log_collector_config_proto\",\n     srcs = [\"log_collector_config.proto\"],\ndiff --git a/tensorflow_serving/servables/tensorflow/BUILD b/tensorflow_serving/servables/tensorflow/BUILD\nindex ef4c54c..9fc97c3 100644\n--- a/tensorflow_serving/servables/tensorflow/BUILD\n+++ b/tensorflow_serving/servables/tensorflow/BUILD\n@@ -2,6 +2,7 @@\n \n load(\"//tensorflow_serving:oss_or_google.bzl\", \"if_google\", \"if_oss\")\n load(\"//tensorflow_serving:serving.bzl\", \"serving_proto_library\", \"serving_tensorflow_proto_dep\")\n+load(\"//tensorflow_serving:serving.bzl\", \"serving_proto_library_py\")\n \n package(\n     default_visibility = [\n@@ -38,6 +39,18 @@ serving_proto_library(\n     ],\n )\n \n+serving_proto_library_py(\n+    name = \"session_bundle_config_py_pb2\",\n+    srcs = [\"session_bundle_config.proto\"],\n+    proto_library = \"session_bundle_config_proto\",\n+    visibility = [\n+        \"//visibility:public\",\n+    ],\n+    deps = [\n+        \"@org_tensorflow//tensorflow/core:protos_all_py\",\n+    ],\n+)\n+\n cc_library(\n     name = \"resource_estimator\",\n     hdrs = [\"resource_estimator.h\"],\n@@ -253,6 +266,19 @@ serving_proto_library(\n     ],\n )\n \n+serving_proto_library_py(\n+    name = \"saved_model_bundle_source_adapter_py_pb2\",\n+    srcs = [\"saved_model_bundle_source_adapter.proto\"],\n+    proto_library = \"saved_model_bundle_source_adapter_proto\",\n+    visibility = [\n+        \"//visibility:public\",\n+    ],\n+    deps = [\n+        \":session_bundle_config_py_pb2\",\n+        \"@org_tensorflow//tensorflow/core:protos_all_py\",\n+    ],\n+)\n+\n cc_library(\n     name = \"simple_servers\",\n     srcs = [\"simple_servers.cc\"],\n"
  },
  {
    "path": "third_party/org_tensorflow_serving/support_diff_dim_size_inputs.patch",
    "content": "diff --git a/tensorflow_serving/batching/batching_options.h b/tensorflow_serving/batching/batching_options.h\nindex 6b0a2838..989ecd46 100644\n--- a/tensorflow_serving/batching/batching_options.h\n+++ b/tensorflow_serving/batching/batching_options.h\n@@ -75,6 +75,8 @@ struct BatchingOptions {\n   // (modulo zeroth dimension) and this option is set to false,\n   // then error Status will be returned.\n   bool pad_variable_length_inputs = false;\n+\n+  bool support_diff_dim_size_inputs = false;\n };\n \n }  // namespace serving\ndiff --git a/tensorflow_serving/batching/batching_session.cc b/tensorflow_serving/batching/batching_session.cc\nindex 8d68c130..eba81b7a 100644\n--- a/tensorflow_serving/batching/batching_session.cc\n+++ b/tensorflow_serving/batching/batching_session.cc\n@@ -438,19 +438,42 @@ BatchingSession::BatchingSession(const BatchingSessionOptions& options,\n \n Status BatchingSession::ComputeInputSize(\n     const std::vector<std::pair<string, Tensor>>& inputs, size_t* size) const {\n-  TF_RETURN_IF_ERROR(::tensorflow::serving::ComputeTensorBatchSize(\n+  auto status = ::tensorflow::serving::ComputeTensorBatchSize(\n       inputs, size,\n       [](const std::pair<std::string, Tensor>& tensor) {\n         return tensor.second.shape().dims();\n       },\n       [](const std::pair<std::string, Tensor>& tensor, size_t dim) {\n         return tensor.second.shape().dim_size(dim);\n-      }));\n-  for (const auto& entry : inputs) {\n-    const Tensor& tensor = entry.second;\n-    RecordInputBatchSize(tensor.shape().dim_size(0));\n+      });\n+  if (status.ok()) {\n+    for (const auto& entry : inputs) {\n+      const Tensor& tensor = entry.second;\n+      RecordInputBatchSize(tensor.shape().dim_size(0));\n+    }\n+    return status;\n+  }\n+\n+  if (options_.support_diff_dim_size_inputs) {\n+    bool has_bs_tensor = false;\n+    for (const auto& entry : inputs) {\n+      const Tensor& tensor = entry.second;\n+      // adhoc, must identity tensor with batch_size\n+      if (entry.first.find(\"batch_size\") != std::string::npos &&\n+          tensor.shape().dims() == 1 && tensor.shape().dim_size(0) == 1) {\n+        *size = tensor.flat<int32>()(0);\n+        has_bs_tensor = true;\n+        RecordInputBatchSize(*size);\n+        break;\n+      }\n+    }\n+    if (!has_bs_tensor) {\n+      return errors::InvalidArgument(\"Batching Run() not find batch_size tensor\");\n+    }\n+    return Status::OK();\n+  } else {\n+    return status;\n   }\n-  return Status::OK();\n }\n \n Status BatchingSession::MergeInputTensors(\ndiff --git a/tensorflow_serving/servables/tensorflow/bundle_factory_util.cc b/tensorflow_serving/servables/tensorflow/bundle_factory_util.cc\nindex ec2a66c4..f6b3ca2a 100644\n--- a/tensorflow_serving/servables/tensorflow/bundle_factory_util.cc\n+++ b/tensorflow_serving/servables/tensorflow/bundle_factory_util.cc\n@@ -142,6 +142,9 @@ Status WrapSessionForBatching(const BatchingParameters& batching_config,\n   batching_session_options.pad_variable_length_inputs =\n       batching_config.pad_variable_length_inputs();\n \n+  batching_session_options.support_diff_dim_size_inputs =\n+      batching_config.support_diff_dim_size_inputs();\n+\n   auto create_queue = [batch_scheduler, queue_options](\n       std::function<void(std::unique_ptr<Batch<BatchingSessionTask>>)>\n           process_batch_callback,\ndiff --git a/tensorflow_serving/servables/tensorflow/session_bundle_config.proto b/tensorflow_serving/servables/tensorflow/session_bundle_config.proto\nindex 2ebf4830..c7d52700 100644\n--- a/tensorflow_serving/servables/tensorflow/session_bundle_config.proto\n+++ b/tensorflow_serving/servables/tensorflow/session_bundle_config.proto\n@@ -152,4 +152,7 @@ message BatchingParameters {\n \n   // Whether to pad variable-length inputs when a batch is formed.\n   bool pad_variable_length_inputs = 7;\n+\n+  // should has batch_size input tensor.\n+  bool support_diff_dim_size_inputs = 10;\n }\n"
  },
  {
    "path": "third_party/pip_deps/BUILD",
    "content": "load(\"@pip_deps//:requirements.bzl\", \"requirement\")\nload(\"@rules_python//python:defs.bzl\", \"py_library\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files(glob([\"**\"]))\n"
  },
  {
    "path": "third_party/pip_deps/requirements.txt",
    "content": "\ncityhash==0.4.1\ncroniter==1.0.15\ncryptography==39.0.1\ndataclasses_json==0.5.2\nflask_api==2.0\nflask==1.1.2\nfreezegun==1.1.0\ngoogle-cloud-storage\ngrpcio==1.32.0\n# Flask currently lists wrong deps.\nitsdangerous==1.1.0\nkazoo==2.8.0\nkubernetes==19.15.0\nnetifaces==0.11.0\nnltk==3.6.7\nportpicker==1.3.1\npsutil==5.9.4\npytz==2021.3\nrequests==2.26.0\nurllib3==1.26.15\nSQLAlchemy==1.4.13\n# This is needed since Werkzeug 2.0.0 breaks flask_api\nWerkzeug==1.0.1\njinja2==3.0.3\nkafka-python==2.0.2\n\nnumpy==1.19.4\npyarrow==9.0.0\npandas==1.4.4\ntensorboard==2.8.0\n#mpi4py==3.0.3\n"
  },
  {
    "path": "third_party/rapidjson.BUILD",
    "content": "package(default_visibility = [\"//visibility:public\"])\n\nlicenses([\"notice\"])  # MIT/JSON license\n\ncc_library(\n    name = \"rapidjson\",\n    srcs = glob([\n        \"include/**/*.h\",\n    ]),\n    copts = [],\n    includes = [\n        \"include\",\n    ],\n)"
  },
  {
    "path": "third_party/repo.bzl",
    "content": "# Copyright 2017 The TensorFlow Authors. All rights reserved.\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\n\"\"\"Utilities for defining TensorFlow Bazel dependencies.\"\"\"\n\n_SINGLE_URL_WHITELIST = depset([\n    \"arm_compiler\",\n])\n\ndef _is_windows(ctx):\n    return ctx.os.name.lower().find(\"windows\") != -1\n\ndef _wrap_bash_cmd(ctx, cmd):\n    if _is_windows(ctx):\n        bazel_sh = _get_env_var(ctx, \"BAZEL_SH\")\n        if not bazel_sh:\n            fail(\"BAZEL_SH environment variable is not set\")\n        cmd = [bazel_sh, \"-l\", \"-c\", \" \".join([\"\\\"%s\\\"\" % s for s in cmd])]\n    return cmd\n\ndef _get_env_var(ctx, name):\n    if name in ctx.os.environ:\n        return ctx.os.environ[name]\n    else:\n        return None\n\n# Checks if we should use the system lib instead of the bundled one\ndef _use_system_lib(ctx, name):\n    syslibenv = _get_env_var(ctx, \"TF_SYSTEM_LIBS\")\n    if syslibenv:\n        for n in syslibenv.strip().split(\",\"):\n            if n.strip() == name:\n                return True\n    return False\n\n# Executes specified command with arguments and calls 'fail' if it exited with\n# non-zero code\ndef _execute_and_check_ret_code(repo_ctx, cmd_and_args):\n    result = repo_ctx.execute(cmd_and_args, timeout = 60)\n    if result.return_code != 0:\n        fail((\"Non-zero return code({1}) when executing '{0}':\\n\" + \"Stdout: {2}\\n\" +\n              \"Stderr: {3}\").format(\n            \" \".join(cmd_and_args),\n            result.return_code,\n            result.stdout,\n            result.stderr,\n        ))\n\ndef _repos_are_siblings():\n    return Label(\"@foo//bar\").workspace_root.startswith(\"../\")\n\n# Apply a patch_file to the repository root directory\n# Runs 'patch -p1' on both Windows and Unix.\ndef _apply_patch(ctx, patch_file):\n    patch_command = [\"patch\", \"-p1\", \"-d\", ctx.path(\".\"), \"-i\", ctx.path(patch_file)]\n    cmd = _wrap_bash_cmd(ctx, patch_command)\n    _execute_and_check_ret_code(ctx, cmd)\n\ndef _apply_delete(ctx, paths):\n    for path in paths:\n        if path.startswith(\"/\"):\n            fail(\"refusing to rm -rf path starting with '/': \" + path)\n        if \"..\" in path:\n            fail(\"refusing to rm -rf path containing '..': \" + path)\n    cmd = _wrap_bash_cmd(ctx, [\"rm\", \"-rf\"] + [ctx.path(path) for path in paths])\n    _execute_and_check_ret_code(ctx, cmd)\n\ndef _tf_http_archive(ctx):\n    if (\"mirror.tensorflow.org\" not in ctx.attr.urls[0] and\n        (len(ctx.attr.urls) < 2 and\n         ctx.attr.name not in _SINGLE_URL_WHITELIST.to_list())):\n        fail(\"tf_http_archive(urls) must have redundant URLs. The \" +\n             \"mirror.tensorflow.org URL must be present and it must come first. \" +\n             \"Even if you don't have permission to mirror the file, please \" +\n             \"put the correctly formatted mirror URL there anyway, because \" +\n             \"someone will come along shortly thereafter and mirror the file.\")\n\n    urls = []\n    for url in ctx.attr.urls:\n        if \"PWD\" in url:\n            url = url.replace(\"PWD\", _get_env_var(ctx, \"PWD\"))\n        urls.append(url)\n    use_syslib = _use_system_lib(ctx, ctx.attr.name)\n\n    # Work around the bazel bug that redownloads the whole library.\n    # Remove this after https://github.com/bazelbuild/bazel/issues/10515 is fixed.\n    if ctx.attr.additional_build_files:\n        for internal_src in ctx.attr.additional_build_files:\n            _ = ctx.path(Label(internal_src))\n\n    # End of workaround.\n\n    if not use_syslib:\n        ctx.download_and_extract(\n            urls,\n            \"\",\n            ctx.attr.sha256,\n            ctx.attr.type,\n            ctx.attr.strip_prefix,\n        )\n        if ctx.attr.delete:\n            _apply_delete(ctx, ctx.attr.delete)\n        if ctx.attr.patch_file != None:\n            _apply_patch(ctx, ctx.attr.patch_file)\n\n    if use_syslib and ctx.attr.system_build_file != None:\n        # Use BUILD.bazel to avoid conflict with third party projects with\n        # BUILD or build (directory) underneath.\n        ctx.template(\"BUILD.bazel\", ctx.attr.system_build_file, {\n            \"%prefix%\": \"..\" if _repos_are_siblings() else \"external\",\n        }, False)\n\n    elif ctx.attr.build_file != None:\n        # Use BUILD.bazel to avoid conflict with third party projects with\n        # BUILD or build (directory) underneath.\n        ctx.template(\"BUILD.bazel\", ctx.attr.build_file, {\n            \"%prefix%\": \"..\" if _repos_are_siblings() else \"external\",\n        }, False)\n\n    if use_syslib:\n        for internal_src, external_dest in ctx.attr.system_link_files.items():\n            ctx.symlink(Label(internal_src), ctx.path(external_dest))\n\n    if ctx.attr.additional_build_files:\n        for internal_src, external_dest in ctx.attr.additional_build_files.items():\n            ctx.symlink(Label(internal_src), ctx.path(external_dest))\n\ntf_http_archive = repository_rule(\n    attrs = {\n        \"sha256\": attr.string(mandatory = True),\n        \"urls\": attr.string_list(mandatory = True, allow_empty = False),\n        \"strip_prefix\": attr.string(),\n        \"type\": attr.string(),\n        \"delete\": attr.string_list(),\n        \"patch_file\": attr.label(),\n        \"build_file\": attr.label(),\n        \"system_build_file\": attr.label(),\n        \"system_link_files\": attr.string_dict(),\n        \"additional_build_files\": attr.string_dict(),\n    },\n    environ = [\n        \"TF_SYSTEM_LIBS\",\n    ],\n    implementation = _tf_http_archive,\n)\n\n\"\"\"Downloads and creates Bazel repos for dependencies.\n\nThis is a swappable replacement for both http_archive() and\nnew_http_archive() that offers some additional features. It also helps\nensure best practices are followed.\n\"\"\"\n\ndef _third_party_http_archive(ctx):\n    if (\"mirror.tensorflow.org\" not in ctx.attr.urls[0] and\n        (len(ctx.attr.urls) < 2 and\n         ctx.attr.name not in _SINGLE_URL_WHITELIST.to_list())):\n        fail(\"tf_http_archive(urls) must have redundant URLs. The \" +\n             \"mirror.tensorflow.org URL must be present and it must come first. \" +\n             \"Even if you don't have permission to mirror the file, please \" +\n             \"put the correctly formatted mirror URL there anyway, because \" +\n             \"someone will come along shortly thereafter and mirror the file.\")\n\n    use_syslib = _use_system_lib(ctx, ctx.attr.name)\n\n    # Use \"BUILD.bazel\" to avoid conflict with third party projects that contain a\n    # file or directory called \"BUILD\"\n    buildfile_path = ctx.path(\"BUILD.bazel\")\n\n    if use_syslib:\n        if ctx.attr.system_build_file == None:\n            fail(\"Bazel was configured with TF_SYSTEM_LIBS to use a system \" +\n                 \"library for %s, but no system build file for %s was configured. \" +\n                 \"Please add a system_build_file attribute to the repository rule\" +\n                 \"for %s.\" % (ctx.attr.name, ctx.attr.name, ctx.attr.name))\n        ctx.symlink(Label(ctx.attr.system_build_file), buildfile_path)\n\n    else:\n        ctx.download_and_extract(\n            ctx.attr.urls,\n            \"\",\n            ctx.attr.sha256,\n            ctx.attr.type,\n            ctx.attr.strip_prefix,\n        )\n        if ctx.attr.delete:\n            _apply_delete(ctx, ctx.attr.delete)\n        if ctx.attr.patch_file != None:\n            _apply_patch(ctx, ctx.attr.patch_file)\n        ctx.symlink(Label(ctx.attr.build_file), buildfile_path)\n\n    link_dict = {}\n    if use_syslib:\n        link_dict.update(ctx.attr.system_link_files)\n\n    for internal_src, external_dest in ctx.attr.link_files.items():\n        # if syslib and link exists in both, use the system one\n        if external_dest not in link_dict.values():\n            link_dict[internal_src] = external_dest\n\n    for internal_src, external_dest in link_dict.items():\n        ctx.symlink(Label(internal_src), ctx.path(external_dest))\n\n# Downloads and creates Bazel repos for dependencies.\n#\n# This is an upgrade for tf_http_archive that works with go/tfbr-thirdparty.\n#\n# For link_files, specify each dict entry as:\n# \"//path/to/source:file\": \"localfile\"\nthird_party_http_archive = repository_rule(\n    attrs = {\n        \"sha256\": attr.string(mandatory = True),\n        \"urls\": attr.string_list(\n            mandatory = True,\n            allow_empty = False,\n        ),\n        \"strip_prefix\": attr.string(),\n        \"type\": attr.string(),\n        \"delete\": attr.string_list(),\n        \"build_file\": attr.string(mandatory = True),\n        \"system_build_file\": attr.string(mandatory = False),\n        \"patch_file\": attr.label(),\n        \"link_files\": attr.string_dict(),\n        \"system_link_files\": attr.string_dict(),\n    },\n    environ = [\n        \"TF_SYSTEM_LIBS\",\n    ],\n    implementation = _third_party_http_archive,\n)\n"
  },
  {
    "path": "third_party/thrift.BUILD",
    "content": "# Description:\n#   Apache Thrift library\n\npackage(default_visibility = [\"//visibility:public\"])\n\nlicenses([\"notice\"])  # Apache 2.0\n\nexports_files([\"LICENSE\"])\n\ncc_library(\n    name = \"thrift\",\n    srcs = glob([\n        \"lib/cpp/src/thrift/**/*.h\",\n    ]) + [\n        \"lib/cpp/src/thrift/protocol/TProtocol.cpp\",\n        \"lib/cpp/src/thrift/transport/TBufferTransports.cpp\",\n        \"lib/cpp/src/thrift/transport/TTransportException.cpp\",\n    ],\n    hdrs = [\n        \"compiler/cpp/src/thrift/version.h\",\n        \"lib/cpp/src/thrift/config.h\",\n    ],\n    includes = [\n        \"lib/cpp/src\",\n    ],\n    textual_hdrs = [\n        \"lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc\",\n        \"lib/cpp/src/thrift/protocol/TCompactProtocol.tcc\",\n    ],\n    deps = [\n        \"@boost//:any\",\n        \"@boost//:algorithm\",\n        \"@boost//:assert\",\n        \"@boost//:bind\",\n        \"@boost//:call_traits\",\n        \"@boost//:config\",\n        \"@boost//:core\",\n        \"@boost//:current_function\",\n        \"@boost//:detail\",\n        \"@boost//:exception\",\n        \"@boost//:function\",\n        \"@boost//:io\",\n        \"@boost//:iterator\",\n        \"@boost//:limits\",\n        \"@boost//:mpl\",\n        \"@boost//:numeric_conversion\",\n        \"@boost//:optional\",\n        \"@boost//:preprocessor\",\n        \"@boost//:smart_ptr\",\n        \"@boost//:static_assert\",\n        \"@boost//:timer\",\n        \"@boost//:type\",\n        \"@boost//:type_traits\",\n        \"@boost//:utility\",\n        \"@boost//:version\",\n        \"@boost//:multiprecision\"\n    ],\n)\n\ngenrule(\n    name = \"version_h\",\n    srcs = [\n        \"compiler/cpp/src/thrift/version.h.in\",\n    ],\n    outs = [\n        \"compiler/cpp/src/thrift/version.h\",\n    ],\n    cmd = \"sed 's/@PACKAGE_VERSION@/0.12.0/g' $< > $@\",\n)\n\ngenrule(\n    name = \"config_h\",\n    srcs = [\"build/cmake/config.h.in\"],\n    outs = [\"lib/cpp/src/thrift/config.h\"],\n    cmd = (\"sed \" +\n           \"-e 's/cmakedefine/define/g' \" +\n           \"-e 's/$${PACKAGE}/thrift/g' \" +\n           \"-e 's/$${PACKAGE_BUGREPORT}//g' \" +\n           \"-e 's/$${PACKAGE_NAME}/thrift/g' \" +\n           \"-e 's/$${PACKAGE_TARNAME}/thrift/g' \" +\n           \"-e 's/$${PACKAGE_URL}//g' \" +\n           \"-e 's/$${PACKAGE_VERSION}/0.12.0/g' \" +\n           \"-e 's/$${PACKAGE_STRING}/thrift 0.12.0/g' \" +\n           \"$< >$@\"),\n)"
  },
  {
    "path": "third_party/tlearner_arch.BUILD",
    "content": "\npy_library(\n    name = \"tlearner_arch\",\n    deps = [\n        \":ream\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n\npy_library(\n    name = \"ream\",\n    deps = [\n        \":client\",\n        \":common\",\n    ],\n)\n\npy_library(\n    name = \"client\",\n    srcs = [\"ream/client/ream_client.py\"]\n)\n\npy_library(\n    name = \"common\",\n    deps = [\n        \":constant\",\n    ],\n)\n\npy_library(\n    name = \"constant\",\n    srcs = [\"ream/common/constant.py\"]\n)"
  },
  {
    "path": "third_party/upb.patch",
    "content": "diff --git a/upb/table.c b/upb/table.c\nindex 8896d21..d30286d 100644\n--- a/upb/table.c\n+++ b/upb/table.c\n@@ -576,18 +576,6 @@ bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val) {\n   return success;\n }\n \n-bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a) {\n-  upb_check_alloc(&t->t, a);\n-  return upb_inttable_insert2(t, upb_inttable_count(t), val, a);\n-}\n-\n-upb_value upb_inttable_pop(upb_inttable *t) {\n-  upb_value val;\n-  bool ok = upb_inttable_remove(t, upb_inttable_count(t) - 1, &val);\n-  UPB_ASSERT(ok);\n-  return val;\n-}\n-\n bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,\n                              upb_alloc *a) {\n   upb_check_alloc(&t->t, a);\ndiff --git a/upb/table.int.h b/upb/table.int.h\nindex 23b0b2f..d09fee5 100644\n--- a/upb/table.int.h\n+++ b/upb/table.int.h\n@@ -361,15 +361,6 @@ UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key,\n  * invalidate iterators. */\n bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val);\n \n-/* Handy routines for treating an inttable like a stack.  May not be mixed with\n- * other insert/remove calls. */\n-bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a);\n-upb_value upb_inttable_pop(upb_inttable *t);\n-\n-UPB_INLINE bool upb_inttable_push(upb_inttable *t, upb_value val) {\n-  return upb_inttable_push2(t, val, &upb_alloc_global);\n-}\n-\n /* Convenience routines for inttables with pointer keys. */\n bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,\n                              upb_alloc *a);\n"
  },
  {
    "path": "third_party/xsimd.BUILD",
    "content": "package(default_visibility = [\"//visibility:public\"])\n\nlicenses([\"notice\"])  # BSD 3-Clause\n\nexports_files([\"LICENSE\"])\n\ncc_library(\n    name = \"xsimd\",\n    srcs = [],\n    hdrs = glob(\n        [\n            \"include/xsimd/*.hpp\",\n            \"include/xsimd/arch/*.hpp\",\n            \"include/xsimd/arch/generic/*.hpp\",\n            \"include/xsimd/config/*.hpp\",\n            \"include/xsimd/math/*.hpp\",\n            \"include/xsimd/memory/*.hpp\",\n            \"include/xsimd/stl/*.hpp\",\n            \"include/xsimd/types/*.hpp\",\n        ],\n        exclude = [\n        ],\n    ),\n    copts = [],\n    defines = [],\n    includes = [\n        \"include\",\n    ],\n    linkopts = [],\n    visibility = [\"//visibility:public\"],\n    deps = [\n    ],\n)"
  },
  {
    "path": "third_party/zstd.BUILD",
    "content": "# Description:\n#   Zstandard library\n\nlicenses([\"notice\"])  # BSD license\n\nexports_files([\"LICENSE\"])\n\ncc_library(\n    name = \"zstd\",\n    srcs = glob(\n        [\n            \"lib/common/*.h\",\n            \"lib/common/*.c\",\n            \"lib/compress/*.c\",\n            \"lib/compress/*.h\",\n            \"lib/decompress/*.c\",\n            \"lib/decompress/*.h\",\n        ],\n        exclude = [\n            \"lib/common/xxhash.c\",\n        ],\n    ),\n    hdrs = [\n        \"lib/zstd.h\",\n    ],\n    defines = [\n        \"XXH_PRIVATE_API\",\n    ],\n    includes = [\n        \"lib\",\n        \"lib/common\",\n    ],\n    linkopts = [],\n    textual_hdrs = [\n        \"lib/common/xxhash.c\",\n    ],\n    visibility = [\"//visibility:public\"],\n)\n"
  }
]